From 6daaecb5fd81a3489d8b3d2ae65a465c6a6ca30d Mon Sep 17 00:00:00 2001 From: Rolf Schmidt Date: Tue, 13 Feb 2018 17:19:22 +0100 Subject: [PATCH] Fixed issue #1805 - API: Ticket creation - wrong user in notifcations, activity stream. --- .../application_controller/has_user.rb | 41 +++- test/controllers/api_auth_controller_test.rb | 1 - .../api_auth_on_behalf_of_controller_test.rb | 208 ++++++++++++++++++ 3 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 test/controllers/api_auth_on_behalf_of_controller_test.rb diff --git a/app/controllers/application_controller/has_user.rb b/app/controllers/application_controller/has_user.rb index 8658e5ec5..86ed5bb73 100644 --- a/app/controllers/application_controller/has_user.rb +++ b/app/controllers/application_controller/has_user.rb @@ -7,16 +7,55 @@ module ApplicationController::HasUser private + def current_user + user_on_behalf = current_user_on_behalf + return user_on_behalf if user_on_behalf + current_user_real + end + # Finds the User with the ID stored in the session with the key # :current_user_id This is a common way to handle user login in # a Rails application; logging in sets the session value and # logging out removes it. - def current_user + def current_user_real return @_current_user if @_current_user return if !session[:user_id] @_current_user = User.lookup(id: session[:user_id]) end + # Finds the user based on the id, login or email which is given + # in the headers. If it is found then all api activities are done + # with the behalf of user. With this functionality it is possible + # to do changes with a user which is different from the admin user. + # E.g. create a ticket as a customer user based on a user with admin rights. + def current_user_on_behalf + + # check header + return if request.headers['X-On-Behalf-Of'].blank? + + # return user if set + return @_user_on_behalf if @_user_on_behalf + + # get current user + user_real = current_user_real + return if !user_real + + # check if the user has admin rights + raise Exceptions::NotAuthorized, "Current user has no permission to use 'X-On-Behalf-Of'!" if !user_real.permissions?('admin.user') + + # find user for execution based on the header + %i[id login email].each do |field| + search_attributes = {} + search_attributes[field] = request.headers['X-On-Behalf-Of'] + @_user_on_behalf = User.find_by(search_attributes) + next if !@_user_on_behalf + return @_user_on_behalf + end + + # no behalf of user found + raise Exceptions::NotAuthorized, "No such user '#{request.headers['X-On-Behalf-Of']}'" + end + def current_user_set(user, auth_type = 'session') session[:user_id] = user.id @_auth_type = auth_type diff --git a/test/controllers/api_auth_controller_test.rb b/test/controllers/api_auth_controller_test.rb index 3bd3f35aa..a6d9b5835 100644 --- a/test/controllers/api_auth_controller_test.rb +++ b/test/controllers/api_auth_controller_test.rb @@ -440,5 +440,4 @@ class ApiAuthControllerTest < ActionDispatch::IntegrationTest assert_equal(Hash, result.class) assert(result) end - end diff --git a/test/controllers/api_auth_on_behalf_of_controller_test.rb b/test/controllers/api_auth_on_behalf_of_controller_test.rb new file mode 100644 index 000000000..0863759c1 --- /dev/null +++ b/test/controllers/api_auth_on_behalf_of_controller_test.rb @@ -0,0 +1,208 @@ + +require 'test_helper' + +class ApiAuthControllerTest < ActionDispatch::IntegrationTest + setup do + + # set accept header + @headers = { 'ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' } + + # create agent + roles = Role.where(name: %w[Admin Agent]) + groups = Group.all + + UserInfo.current_user_id = 1 + @admin = User.create_or_update( + login: 'api-admin-auth-behalf', + firstname: 'API', + lastname: 'Admin', + email: 'api-admin-auth-behalf@example.com', + password: 'adminpw', + active: true, + roles: roles, + groups: groups, + ) + + # create customer without org + roles = Role.where(name: 'Customer') + @customer = User.create_or_update( + login: 'api-customer1-auth-behalf@example.com', + firstname: 'API', + lastname: 'Customer1', + email: 'api-customer1-auth-behalf@example.com', + password: 'customer1pw', + active: true, + roles: roles, + ) + + end + + test 'X-On-Behalf-Of auth - ticket create admin for customer by id' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-admin-auth-behalf@example.com', 'adminpw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => @customer.id, + ) + + params = { + title: 'a new ticket #3', + group: 'Users', + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(result['created_by_id'], @customer.id) + end + + test 'X-On-Behalf-Of auth - ticket create admin for customer by login' do + ActivityStream.cleanup(1.year) + + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-admin-auth-behalf@example.com', 'adminpw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => @customer.login, + ) + admin_headers = @headers.merge( + 'Authorization' => credentials, + ) + + params = { + title: 'a new ticket #3', + group: 'Users', + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(201) + result_ticket_create = JSON.parse(@response.body) + assert_equal(Hash, result_ticket_create.class) + assert_equal(result_ticket_create['created_by_id'], @customer.id) + + get '/api/v1/activity_stream', params: {}, headers: admin_headers + assert_response(200) + result_activity_stream = JSON.parse(@response.body) + assert_equal(Hash, result_activity_stream.class) + + ticket_created = result_activity_stream['activity_stream'].find { |activity| activity['object'] == 'Ticket' && activity['o_id'] == result_ticket_create['id'] } + + assert_equal(Hash, ticket_created.class) + assert_equal(ticket_created['created_by_id'], @customer.id) + end + + test 'X-On-Behalf-Of auth - ticket create admin for customer by email' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-admin-auth-behalf@example.com', 'adminpw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => @customer.email, + ) + + params = { + title: 'a new ticket #3', + group: 'Users', + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(result['created_by_id'], @customer.id) + end + + test 'X-On-Behalf-Of auth - ticket create admin for unknown' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-admin-auth-behalf@example.com', 'adminpw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => 99_449_494_949, + ) + + params = { + title: 'a new ticket #3', + group: 'Users', + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(401) + assert_not(@response.header.key?('Access-Control-Allow-Origin')) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal("No such user '99449494949'", result['error']) + end + + test 'X-On-Behalf-Of auth - ticket create customer for admin' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-customer1-auth-behalf@example.com', 'customer1pw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => @admin.email, + ) + + params = { + title: 'a new ticket #3', + group: 'Users', + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(401) + assert_not(@response.header.key?('Access-Control-Allow-Origin')) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal("Current user has no permission to use 'X-On-Behalf-Of'!", result['error']) + end + + test 'X-On-Behalf-Of auth - ticket create admin for customer by email but no permitted action' do + group_secret = Group.new(name: 'secret1234') + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('api-admin-auth-behalf@example.com', 'adminpw') + + ticket_create_headers = @headers.merge( + 'Authorization' => credentials, + 'X-On-Behalf-Of' => @customer.email, + ) + + params = { + title: 'a new ticket #3', + group: group_secret.name, + priority: '2 normal', + state: 'new', + customer_id: @customer.id, + article: { + body: 'some test 123', + }, + } + post '/api/v1/tickets', params: params.to_json, headers: ticket_create_headers + assert_response(422) + assert_not(@response.header.key?('Access-Control-Allow-Origin')) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('No lookup value found for \'group\': "secret1234"', result['error']) + end +end