spec: Make Auth-User header specify a user by name, not ID
This removes the Auth-User-Id header and changes it with Auth-User, which should contain the name of a user. A pseudonym is created for that user, since users can't make API requests without a pseudonym. The pact proxy will now raise an error if a user name is provided and there is no user matching that name. Also added some tests for the proxy. Change-Id: I29b2dd2ff34f8250bdbd0dc92e7d023dc337e057 Reviewed-on: https://gerrit.instructure.com/155375 Reviewed-by: Robert Lamb <rlamb@instructure.com> Product-Review: Robert Lamb <rlamb@instructure.com> QA-Review: Robert Lamb <rlamb@instructure.com> Tested-by: Jenkins
This commit is contained in:
parent
98aa24d0cd
commit
f93a0149ed
|
@ -30,7 +30,7 @@ describe 'Assignments', :pact do
|
|||
method: :get,
|
||||
headers: {
|
||||
'Authorization': 'some_token',
|
||||
'Auth-User-Id': '2',
|
||||
'Auth-User': 'Student',
|
||||
'Connection': 'close',
|
||||
'Host': PactConfig.mock_provider_service_base_uri,
|
||||
'Version': 'HTTP/1.1'
|
||||
|
@ -43,7 +43,7 @@ describe 'Assignments', :pact do
|
|||
body: Pact.each_like('id': 1, 'name': 'Assignment1')
|
||||
)
|
||||
|
||||
assignments_api.authenticate_as_user(2)
|
||||
assignments_api.authenticate_as_user('Student')
|
||||
response = assignments_api.list_assignments(1, 2)
|
||||
expect(response[0]['id']).to eq 1
|
||||
expect(response[0]['name']).to eq 'Assignment1'
|
||||
|
@ -58,7 +58,7 @@ describe 'Assignments', :pact do
|
|||
method: :post,
|
||||
headers: {
|
||||
'Authorization': 'some_token',
|
||||
'Auth-User-Id': '2',
|
||||
'Auth-User': 'Teacher',
|
||||
'Connection': 'close',
|
||||
'Host': PactConfig.mock_provider_service_base_uri,
|
||||
'Version': 'HTTP/1.1',
|
||||
|
@ -80,7 +80,7 @@ describe 'Assignments', :pact do
|
|||
body: Pact.like('id': 1, 'name': 'New Assignment')
|
||||
)
|
||||
|
||||
assignments_api.authenticate_as_user(2)
|
||||
assignments_api.authenticate_as_user('Teacher')
|
||||
response = assignments_api.post_assignments(1, 'New Assignment')
|
||||
expect(response['id']).to eq 1
|
||||
expect(response['name']).to eq 'New Assignment'
|
||||
|
|
|
@ -20,15 +20,10 @@ require 'byebug'
|
|||
class ApiClientBase
|
||||
include HTTParty
|
||||
|
||||
AUTH_HEADER = 'Auth-User-Id'.freeze
|
||||
AUTH_HEADER = 'Auth-User'.freeze
|
||||
|
||||
def initialize
|
||||
# default to user 1, optionally override later
|
||||
authenticate_as_user(1)
|
||||
end
|
||||
|
||||
def authenticate_as_user(user_id)
|
||||
self.class.headers AUTH_HEADER => user_id.to_s
|
||||
def authenticate_as_user(name)
|
||||
self.class.headers AUTH_HEADER => name
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -22,9 +22,8 @@ PactConfig::Consumers::ALL.each do |consumer|
|
|||
Pact.provider_states_for consumer do
|
||||
provider_state 'a student in a course with an assignment' do
|
||||
set_up do
|
||||
course_with_student(active_all: true)
|
||||
course_with_student(active_all: true, name: 'Student')
|
||||
Assignment.create!(context: @course, title: "Assignment1")
|
||||
Pseudonym.create!(user: @student, unique_id: 'testuser@instructure.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -54,7 +54,7 @@ PactConfig::Consumers::ALL.each do |consumer|
|
|||
|
||||
provider_state 'a teacher in a course' do
|
||||
set_up do
|
||||
course_with_teacher(active_all: true)
|
||||
course_with_teacher(active_all: true, name: 'Teacher')
|
||||
Pseudonym.create!(user: @teacher, unique_id: 'testuser@instructure.com')
|
||||
token = @teacher.access_tokens.create!().full_token
|
||||
|
||||
|
|
|
@ -18,31 +18,43 @@
|
|||
class PactApiConsumerProxy
|
||||
|
||||
AUTH_HEADER = 'HTTP_AUTHORIZATION'.freeze
|
||||
USER_ID_HEADER = 'HTTP_AUTH_USER_ID'.freeze
|
||||
USER_HEADER = 'HTTP_AUTH_USER'.freeze
|
||||
|
||||
def call(env)
|
||||
# Users calling the API will know the user ID of the
|
||||
# user that they want to identify as. These are given
|
||||
# in the provider state descriptions.
|
||||
if expects_auth_header(env)
|
||||
user = User.find(requesting_user_id(env))
|
||||
if expects_auth_header?(env)
|
||||
user = find_requesting_user(env)
|
||||
# You can create an access token without having a pseudonym;
|
||||
# however, when Canvas receives a request and looks up the user
|
||||
# for that access token, it expects that user to have a pseudonym.
|
||||
Pseudonym.create!(user: user, unique_id: "#{user.name}@instructure.com")
|
||||
token = user.access_tokens.create!.full_token
|
||||
env[AUTH_HEADER] = "Bearer #{token}"
|
||||
# Unset the 'AUTH_USER_ID' header -- that's only for this proxy,
|
||||
# don't pass it along to Canvas.
|
||||
env.delete(USER_ID_HEADER)
|
||||
end
|
||||
# Unset the 'AUTH_USER' header -- that's only for this proxy,
|
||||
# don't pass it along to Canvas.
|
||||
env.delete(USER_HEADER)
|
||||
|
||||
CanvasRails::Application.call(env)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expects_auth_header(env)
|
||||
def expects_auth_header?(env)
|
||||
env[AUTH_HEADER]
|
||||
end
|
||||
|
||||
def requesting_user_id(env)
|
||||
env[USER_ID_HEADER] || '1'
|
||||
def find_requesting_user(env)
|
||||
user_name = env[USER_HEADER]
|
||||
if user_name
|
||||
user = User.where(name: user_name).first
|
||||
raise "There is no user with name #{user_name}." unless user
|
||||
else
|
||||
user = User.first
|
||||
end
|
||||
|
||||
user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
#
|
||||
# Copyright (C) 2011 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
require_relative '../spec_helper'
|
||||
require_relative '../contracts/service_consumers/api/proxy_app'
|
||||
|
||||
describe PactApiConsumerProxy do
|
||||
|
||||
context 'Authorization header' do
|
||||
|
||||
subject(:proxy) { PactApiConsumerProxy.new }
|
||||
|
||||
before :each do
|
||||
# This happens when our Pact tests run -- we need to make it happen
|
||||
# here, too.
|
||||
ActiveRecord::Base.connection.tables.each do |t|
|
||||
ActiveRecord::Base.connection.reset_pk_sequence!(t)
|
||||
end
|
||||
|
||||
@student1 = User.create!(name: 'Student1')
|
||||
@student2 = User.create!(name: 'Student2')
|
||||
|
||||
student1_token = double(full_token: '1_TOKEN')
|
||||
student1_tokens = double(create!: student1_token)
|
||||
allow_any_instantiation_of(@student1).to receive(:access_tokens).and_return(student1_tokens)
|
||||
|
||||
student2_token = double(full_token: '2_TOKEN')
|
||||
student2_tokens = double(create!: student2_token)
|
||||
allow_any_instantiation_of(@student2).to receive(:access_tokens).and_return(student2_tokens)
|
||||
end
|
||||
|
||||
it 'sets header for the specified user' do
|
||||
expected_env = {
|
||||
'HTTP_AUTHORIZATION' => 'Bearer 2_TOKEN'
|
||||
}
|
||||
expect(CanvasRails::Application).to receive(:call).with(expected_env)
|
||||
|
||||
proxy_env = {
|
||||
'HTTP_AUTHORIZATION' => 'some_token',
|
||||
'HTTP_AUTH_USER' => 'Student2'
|
||||
}
|
||||
proxy.call(proxy_env)
|
||||
end
|
||||
|
||||
it 'sets header for the first user if one is not specified' do
|
||||
expected_env = {
|
||||
'HTTP_AUTHORIZATION' => 'Bearer 1_TOKEN'
|
||||
}
|
||||
expect(CanvasRails::Application).to receive(:call).with(expected_env)
|
||||
|
||||
proxy_env = {
|
||||
'HTTP_AUTHORIZATION' => 'some_token',
|
||||
}
|
||||
proxy.call(proxy_env)
|
||||
end
|
||||
|
||||
it 'does not add Authorization header if it is not sent to proxy' do
|
||||
expect(CanvasRails::Application)
|
||||
.to receive(:call).with(hash_excluding('HTTP_AUTHORIZATION')).twice
|
||||
|
||||
user_but_no_auth_header = {
|
||||
'HTTP_AUTH_USER' => 'Some User'
|
||||
}
|
||||
proxy.call(user_but_no_auth_header)
|
||||
proxy.call({})
|
||||
end
|
||||
|
||||
it 'removes the HTTP_AUTH_USER header' do
|
||||
expect(CanvasRails::Application).to receive(:call).with(hash_excluding('HTTP_AUTH_USER')).twice
|
||||
|
||||
env_with_auth = {
|
||||
'HTTP_AUTHORIZATION' => 'some_token',
|
||||
'HTTP_AUTH_USER' => 'Student1'
|
||||
}
|
||||
env_without_auth = {
|
||||
'HTTP_AUTH_USER' => 'Student1'
|
||||
}
|
||||
proxy.call(env_with_auth)
|
||||
proxy.call(env_without_auth)
|
||||
end
|
||||
|
||||
it 'throws an error if the specified user does not exist' do
|
||||
proxy_env = {
|
||||
'HTTP_AUTHORIZATION' => 'some_token',
|
||||
'HTTP_AUTH_USER' => 'NotAStudent'
|
||||
}
|
||||
|
||||
expect { proxy.call(proxy_env) }.to raise_error('There is no user with name NotAStudent.')
|
||||
end
|
||||
|
||||
it 'creates a pseudonym for the requested user' do
|
||||
allow(CanvasRails::Application).to receive(:call).and_return nil
|
||||
|
||||
proxy_env = {
|
||||
'HTTP_AUTHORIZATION' => 'some_token',
|
||||
'HTTP_AUTH_USER' => 'Student1'
|
||||
}
|
||||
proxy.call(proxy_env)
|
||||
pseudonym = Pseudonym.last
|
||||
expect(pseudonym.unique_id).to eq('Student1@instructure.com')
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue