diff --git a/app/assets/javascripts/app/controllers/_profile/out_of_office.coffee b/app/assets/javascripts/app/controllers/_profile/out_of_office.coffee index 79c6c37dc..06a632e9d 100644 --- a/app/assets/javascripts/app/controllers/_profile/out_of_office.coffee +++ b/app/assets/javascripts/app/controllers/_profile/out_of_office.coffee @@ -148,8 +148,8 @@ class ProfileOutOfOffice extends App.ControllerSubContent data = JSON.parse(xhr.responseText) # show error message - if xhr.status is 401 || error is 'Unauthorized' - message = '» ' + App.i18n.translateInline('Unauthorized') + ' «' + if xhr.status is 403 || error is 'Not authorized' + message = '» ' + App.i18n.translateInline('Not authorized') + ' «' else if xhr.status is 404 || error is 'Not Found' message = '» ' + App.i18n.translateInline('Not Found') + ' «' else if data.error diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee index 878c9c660..548961b40 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee @@ -115,8 +115,8 @@ class App.TicketZoom extends App.Controller return # show error message - if status is 401 || statusText is 'Unauthorized' - @taskHead = '» ' + App.i18n.translateInline('Unauthorized') + ' «' + if status is 403 || statusText is 'Not authorized' + @taskHead = '» ' + App.i18n.translateInline('Not authorized') + ' «' @taskIconClass = 'diagonal-cross' @renderScreenUnauthorized(objectName: 'Ticket') else if status is 404 || statusText is 'Not Found' diff --git a/app/assets/javascripts/app/lib/app_post/ajax.coffee b/app/assets/javascripts/app/lib/app_post/ajax.coffee index 37de6e528..9ee86e2f6 100644 --- a/app/assets/javascripts/app/lib/app_post/ajax.coffee +++ b/app/assets/javascripts/app/lib/app_post/ajax.coffee @@ -93,8 +93,9 @@ class _ajaxSingleton # 200, all is fine return if status is 200 - # do not show any error message with code 401/404 (handled by controllers) + # do not show any error message for various 4** codes (handled by controllers) return if status is 401 + return if status is 403 return if status is 404 return if status is 422 diff --git a/app/controllers/application_controller/authenticates.rb b/app/controllers/application_controller/authenticates.rb index 3e1ae4378..2f36874b1 100644 --- a/app/controllers/application_controller/authenticates.rb +++ b/app/controllers/application_controller/authenticates.rb @@ -14,12 +14,12 @@ module ApplicationController::Authenticates ) return false if user - raise Exceptions::NotAuthorized, 'Not authorized (token)!' + raise Exceptions::Forbidden, 'Not authorized (token)!' end return false if current_user&.permissions?(key) - raise Exceptions::NotAuthorized, 'Not authorized (user)!' + raise Exceptions::Forbidden, 'Not authorized (user)!' end def authentication_check(auth_param = {}) @@ -33,7 +33,7 @@ module ApplicationController::Authenticates # return auth not ok if !user - raise Exceptions::NotAuthorized, 'authentication failed' + raise Exceptions::Forbidden, 'Authentication required' end # return auth ok @@ -45,12 +45,15 @@ module ApplicationController::Authenticates #logger.debug params.inspect #logger.debug session.inspect #logger.debug cookies.inspect + authentication_errors = [] # already logged in, early exit if session.id && session[:user_id] logger.debug { 'session based auth check' } user = User.lookup(id: session[:user_id]) return authentication_check_prerequesits(user, 'session', auth_param) if user + + authentication_errors.push("Can't find User with ID #{session[:user_id]} from Session") end # check http basic based authentication @@ -58,11 +61,13 @@ module ApplicationController::Authenticates request.session_options[:skip] = true # do not send a session cookie logger.debug { "http basic auth check '#{username}'" } if Setting.get('api_password_access') == false - raise Exceptions::NotAuthorized, 'API password access disabled!' + raise Exceptions::Forbidden, 'API password access disabled!' end user = User.authenticate(username, password) return authentication_check_prerequesits(user, 'basic_auth', auth_param) if user + + authentication_errors.push('Invalid BasicAuth credentials') end # check http token based authentication @@ -70,7 +75,7 @@ module ApplicationController::Authenticates logger.debug { "http token auth check '#{token_string}'" } request.session_options[:skip] = true # do not send a session cookie if Setting.get('api_token_access') == false - raise Exceptions::NotAuthorized, 'API token access disabled!' + raise Exceptions::Forbidden, 'API token access disabled!' end user = Token.check( @@ -106,13 +111,15 @@ module ApplicationController::Authenticates @_token_auth = token_string # remember for permission_check return authentication_check_prerequesits(user, 'token_auth', auth_param) if user + + authentication_errors.push("Can't find User for Token") end # check oauth2 token based authentication token = Doorkeeper::OAuth::Token.from_bearer_authorization(request) if token request.session_options[:skip] = true # do not send a session cookie - logger.debug { "oauth2 token auth check '#{token}'" } + logger.debug { "OAuth2 token auth check '#{token}'" } access_token = Doorkeeper::AccessToken.by_token(token) raise Exceptions::NotAuthorized, 'Invalid token!' if !access_token @@ -128,9 +135,13 @@ module ApplicationController::Authenticates user = User.find(access_token.resource_owner_id) return authentication_check_prerequesits(user, 'token_auth', auth_param) if user + + authentication_errors.push("Can't find User with ID #{access_token.resource_owner_id} for OAuth2 token") end - false + return false if authentication_errors.blank? + + raise Exceptions::NotAuthorized, authentication_errors.join(', ') end def authenticate_with_password @@ -142,7 +153,7 @@ module ApplicationController::Authenticates end def authentication_check_prerequesits(user, auth_type, auth_param) - raise Exceptions::NotAuthorized, 'Maintenance mode enabled!' if in_maintenance_mode?(user) + raise Exceptions::Forbidden, 'Maintenance mode enabled!' if in_maintenance_mode?(user) raise_unified_login_error if !user.active @@ -150,7 +161,7 @@ module ApplicationController::Authenticates ActiveSupport::Deprecation.warn("Parameter ':permission' is deprecated. Use Pundit policy and `authorize!` instead.") if !user.permissions?(auth_param[:permission]) - raise Exceptions::NotAuthorized, 'Not authorized (user)!' + raise Exceptions::Forbidden, 'Not authorized (user)!' end end diff --git a/app/controllers/application_controller/authorizes.rb b/app/controllers/application_controller/authorizes.rb index 3485d66ee..7e37fa3b4 100644 --- a/app/controllers/application_controller/authorizes.rb +++ b/app/controllers/application_controller/authorizes.rb @@ -11,7 +11,7 @@ module ApplicationController::Authorizes def authorized?(record = policy_record, query = nil) authorize!(record, query) true - rescue Exceptions::NotAuthorized, Pundit::NotAuthorizedError + rescue Exceptions::Forbidden, Pundit::NotAuthorizedError false end diff --git a/app/controllers/application_controller/handles_errors.rb b/app/controllers/application_controller/handles_errors.rb index fd558bdc7..651fe996a 100644 --- a/app/controllers/application_controller/handles_errors.rb +++ b/app/controllers/application_controller/handles_errors.rb @@ -11,6 +11,7 @@ module ApplicationController::HandlesErrors rescue_from ArgumentError, with: :unprocessable_entity rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity rescue_from Exceptions::NotAuthorized, with: :unauthorized + rescue_from Exceptions::Forbidden, with: :forbidden rescue_from Pundit::NotAuthorizedError, with: :pundit_not_authorized_error end @@ -40,13 +41,21 @@ module ApplicationController::HandlesErrors http_log end + def forbidden(e) + logger.info { e } + error = humanize_error(e) + response.headers['X-Failure'] = error.fetch(:error_human, error[:error]) + respond_to_exception(e, :forbidden) + http_log + end + def pundit_not_authorized_error(e) logger.info { e } # check if a special authorization_error should be shown in the result payload # which was raised in one of the policies. Fall back to a simple "Not authorized" # error to hide actual cause for security reasons. - exeption = e.policy.custom_exception || Exceptions::NotAuthorized.new - unauthorized(exeption) + exeption = e.policy&.custom_exception || Exceptions::Forbidden.new('Not authorized') + forbidden(exeption) end private @@ -79,10 +88,13 @@ module ApplicationController::HandlesErrors data[:error_human] = 'Object already exists!' elsif e.message =~ /null value in column "(.+?)" violates not-null constraint/i || e.message =~ /Field '(.+?)' doesn't have a default value/i data[:error_human] = "Attribute '#{$1}' required!" - elsif e.message == 'Exceptions::NotAuthorized' + elsif e.message == 'Exceptions::Forbidden' data[:error] = 'Not authorized' data[:error_human] = data[:error] - elsif [ActionController::RoutingError, ActiveRecord::RecordNotFound, Exceptions::UnprocessableEntity, Exceptions::NotAuthorized].include?(e.class) + elsif e.message == 'Exceptions::NotAuthorized' + data[:error] = 'Authorization failed' + data[:error_human] = data[:error] + elsif [ActionController::RoutingError, ActiveRecord::RecordNotFound, Exceptions::UnprocessableEntity, Exceptions::NotAuthorized, Exceptions::Forbidden].include?(e.class) data[:error_human] = data[:error] end diff --git a/app/controllers/application_controller/has_user.rb b/app/controllers/application_controller/has_user.rb index f86394e14..fc5067e09 100644 --- a/app/controllers/application_controller/has_user.rb +++ b/app/controllers/application_controller/has_user.rb @@ -43,7 +43,7 @@ module ApplicationController::HasUser 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') + raise Exceptions::Forbidden, "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| @@ -55,7 +55,7 @@ module ApplicationController::HasUser end # no behalf of user found - raise Exceptions::NotAuthorized, "No such user '#{request.headers['X-On-Behalf-Of']}'" + raise Exceptions::Forbidden, "No such user '#{request.headers['X-On-Behalf-Of']}'" end def search_attributes(field) diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index d5672c03c..2f1660858 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -75,7 +75,7 @@ class AttachmentsController < ApplicationController valid_disposition = %w[inline attachment] return disposition if valid_disposition.include?(disposition) - raise Exceptions::NotAuthorized, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid." + raise Exceptions::Forbidden, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid." end def verify_object_permissions diff --git a/app/controllers/calendar_subscriptions_controller.rb b/app/controllers/calendar_subscriptions_controller.rb index 43fcd808b..07642fe33 100644 --- a/app/controllers/calendar_subscriptions_controller.rb +++ b/app/controllers/calendar_subscriptions_controller.rb @@ -7,7 +7,8 @@ class CalendarSubscriptionsController < ApplicationController # @summary Returns an iCal file with all objects matching the calendar subscriptions preferences of the current user as events. # # @response_message 200 [String] iCal file ready to import in calendar applications. - # @response_message 401 Permission denied. + # @response_message 403 Forbidden / Invalid session. + # @response_message 422 Unprocessable Entity. def all calendar_subscriptions = CalendarSubscriptions.new(current_user) ical = calendar_subscriptions.all @@ -29,7 +30,8 @@ class CalendarSubscriptionsController < ApplicationController # @summary Returns an iCal file of the given object (and method) matching the calendar subscriptions preferences of the current user as events. # # @response_message 200 [String] iCal file ready to import in calendar applications. - # @response_message 401 Permission denied. + # @response_message 403 Forbidden / Invalid session. + # @response_message 422 Unprocessable Entity. def object calendar_subscriptions = CalendarSubscriptions.new(current_user) ical = calendar_subscriptions.generic(params[:object], params[:method]) diff --git a/app/controllers/channels_email_controller.rb b/app/controllers/channels_email_controller.rb index d963e881a..eff163f8d 100644 --- a/app/controllers/channels_email_controller.rb +++ b/app/controllers/channels_email_controller.rb @@ -267,7 +267,7 @@ class ChannelsEmailController < ApplicationController def check_online_service return true if !Setting.get('system_online_service') - raise Exceptions::NotAuthorized + raise Exceptions::Forbidden end def check_access(id = nil) @@ -279,6 +279,6 @@ class ChannelsEmailController < ApplicationController channel = Channel.find(id) return true if channel.preferences && !channel.preferences[:online_service_disable] - raise Exceptions::NotAuthorized + raise Exceptions::Forbidden end end diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 260fdbc02..e2a3cc334 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -149,7 +149,7 @@ class FormController < ApplicationController def authorize!(*) super rescue Pundit::NotAuthorizedError - raise Exceptions::NotAuthorized + raise Exceptions::Forbidden end def token_gen(fingerprint) @@ -161,7 +161,7 @@ class FormController < ApplicationController def token_valid?(token, fingerprint) if token.blank? Rails.logger.info 'No token for form!' - raise Exceptions::NotAuthorized + raise Exceptions::Forbidden end begin crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32]) @@ -205,15 +205,15 @@ class FormController < ApplicationController # in elasticsearch7 "created_at:>now-1h" is not working. Needed to catch -2h form_limit_by_ip_per_hour = Setting.get('form_ticket_create_by_ip_per_hour') || 20 result = SearchIndexBackend.search("preferences.form.remote_ip:'#{remote_ip}' AND created_at:>now-2h", 'Ticket', limit: form_limit_by_ip_per_hour) - raise Exceptions::NotAuthorized if result.count >= form_limit_by_ip_per_hour.to_i + raise Exceptions::Forbidden if result.count >= form_limit_by_ip_per_hour.to_i form_limit_by_ip_per_day = Setting.get('form_ticket_create_by_ip_per_day') || 240 result = SearchIndexBackend.search("preferences.form.remote_ip:'#{remote_ip}' AND created_at:>now-1d", 'Ticket', limit: form_limit_by_ip_per_day) - raise Exceptions::NotAuthorized if result.count >= form_limit_by_ip_per_day.to_i + raise Exceptions::Forbidden if result.count >= form_limit_by_ip_per_day.to_i form_limit_per_day = Setting.get('form_ticket_create_per_day') || 5000 result = SearchIndexBackend.search('preferences.form.remote_ip:* AND created_at:>now-1d', 'Ticket', limit: form_limit_per_day) - raise Exceptions::NotAuthorized if result.count >= form_limit_per_day.to_i + raise Exceptions::Forbidden if result.count >= form_limit_per_day.to_i false end @@ -222,6 +222,6 @@ class FormController < ApplicationController return true if params[:fingerprint].present? && params[:fingerprint].length > 30 Rails.logger.info 'No fingerprint given!' - raise Exceptions::NotAuthorized + raise Exceptions::Forbidden end end diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb index d94687946..6199bce9c 100644 --- a/app/controllers/organizations_controller.rb +++ b/app/controllers/organizations_controller.rb @@ -265,7 +265,7 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/organizations/import_example # # @response_message 200 File download. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_example send_data( Organization.csv_example, @@ -283,7 +283,7 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/organizations.csv' 'https://your.zammad/api/v1/organizations/import' # # @response_message 201 Import started. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_start string = params[:data] if string.blank? && params[:file].present? diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 31afaf089..473393af4 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -16,7 +16,7 @@ class SessionsController < ApplicationController end def create_sso - raise Exceptions::NotAuthorized, 'SSO authentication disabled!' if !Setting.get('auth_sso') + raise Exceptions::Forbidden, 'SSO authentication disabled!' if !Setting.get('auth_sso') user = begin login = request.env['REMOTE_USER'] || @@ -150,7 +150,7 @@ class SessionsController < ApplicationController def switch_back_to_user # check if it's a switch back - raise Exceptions::NotAuthorized if !session[:switched_from_user_id] + raise Exceptions::Forbidden if !session[:switched_from_user_id] user = User.lookup(id: session[:switched_from_user_id]) if !user diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index fdb09dd87..5368bb221 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -21,7 +21,7 @@ class SettingsController < ApplicationController # POST /settings def create - raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' + raise Exceptions::Forbidden, 'Not authorized (feature not possible)' end # PUT /settings/1 @@ -84,7 +84,7 @@ class SettingsController < ApplicationController # DELETE /settings/1 def destroy - raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' + raise Exceptions::Forbidden, 'Not authorized (feature not possible)' end private diff --git a/app/controllers/text_modules_controller.rb b/app/controllers/text_modules_controller.rb index 9748a76ae..bc6d2e03e 100644 --- a/app/controllers/text_modules_controller.rb +++ b/app/controllers/text_modules_controller.rb @@ -155,7 +155,7 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/text_modules/import_example # # @response_message 200 File download. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_example csv_string = TextModule.csv_example( col_sep: params[:col_sep] || ',', @@ -177,7 +177,7 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/Textbausteine_final2.csv' 'https://your.zammad/api/v1/text_modules/import' # # @response_message 201 Import started. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_start string = params[:data] if string.blank? && params[:file].present? diff --git a/app/controllers/ticket_articles_controller.rb b/app/controllers/ticket_articles_controller.rb index ad2a16536..1328b1a94 100644 --- a/app/controllers/ticket_articles_controller.rb +++ b/app/controllers/ticket_articles_controller.rb @@ -159,7 +159,7 @@ class TicketArticlesController < ApplicationController # check if requested ticket got merged if ticket.state.state_type.name != 'merged' - raise Exceptions::NotAuthorized, 'No access, article_id/ticket_id is not matching.' + raise Exceptions::Forbidden, 'No access, article_id/ticket_id is not matching.' end ticket = article.ticket @@ -173,7 +173,7 @@ class TicketArticlesController < ApplicationController access = true end end - raise Exceptions::NotAuthorized, 'Requested file id is not linked with article_id.' if !access + raise Exceptions::Forbidden, 'Requested file id is not linked with article_id.' if !access # find file file = Store.find(params[:id]) @@ -226,7 +226,7 @@ class TicketArticlesController < ApplicationController # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/ticket_articles/import_example # # @response_message 200 File download. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_example csv_string = Ticket::Article.csv_example( col_sep: ',', @@ -248,7 +248,7 @@ class TicketArticlesController < ApplicationController # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/ticket_articles.csv' 'https://your.zammad/api/v1/ticket_articles/import' # # @response_message 201 Import started. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_start if Setting.get('import_mode') != true raise 'Only can import tickets if system is in import mode.' @@ -286,6 +286,6 @@ class TicketArticlesController < ApplicationController valid_disposition = %w[inline attachment] return disposition if valid_disposition.include?(disposition) - raise Exceptions::NotAuthorized, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid." + raise Exceptions::Forbidden, "Invalid disposition #{disposition} requested. Only #{valid_disposition.join(', ')} are valid." end end diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 98c9a20df..d3daa7e93 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -624,7 +624,7 @@ class TicketsController < ApplicationController # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/tickets/import_example # # @response_message 200 File download. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_example csv_string = Ticket.csv_example( col_sep: ',', @@ -646,7 +646,7 @@ class TicketsController < ApplicationController # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/tickets.csv' 'https://your.zammad/api/v1/tickets/import' # # @response_message 201 Import started. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_start if Setting.get('import_mode') != true raise 'Only can import tickets if system is in import mode.' diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4eea0d051..6945078fd 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -15,7 +15,7 @@ class UsersController < ApplicationController # role 'Customer' only just the own User record will be returned. # # @response_message 200 [Array] List of matching User records. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def index offset = 0 per_page = 500 @@ -71,7 +71,7 @@ class UsersController < ApplicationController # @parameter full [Bool] If set a Asset structure with all connected Assets gets returned. # # @response_message 200 [User] User record matching the requested identifier. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def show user = User.find(params[:id]) authorize!(user) @@ -120,7 +120,7 @@ class UsersController < ApplicationController # @parameter User(required,body) [User] The attribute value structure needed to update a User record. # # @response_message 200 [User] Updated User record. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def update user = User.find(params[:id]) authorize!(user) @@ -169,7 +169,7 @@ class UsersController < ApplicationController # @parameter id(required) [User] The identifier matching the requested User record. # # @response_message 200 User successfully deleted. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def destroy user = User.find(params[:id]) authorize!(user) @@ -186,7 +186,7 @@ class UsersController < ApplicationController # @parameter full [Bool] If set a Asset structure with all connected Assets gets returned. # # @response_message 200 [User] User record matching the requested identifier. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def me if response_expand? @@ -225,7 +225,7 @@ class UsersController < ApplicationController # or false: [{:id => user.id, :label => "firstname lastname ", :value => "firstname lastname "},...]. # # @response_message 200 [Array] A list of User records matching the search term. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def search per_page = params[:per_page] || params[:limit] || 100 per_page = per_page.to_i @@ -328,7 +328,7 @@ class UsersController < ApplicationController # @parameter id(required) [Integer] The identifier matching the requested User record. # # @response_message 200 [History] The History records of the requested User record. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def history # get user data user = User.find(params[:id]) @@ -847,7 +847,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/users/import_example # # @response_message 200 File download. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_example send_data( User.csv_example, @@ -865,7 +865,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @example curl -u 'me@example.com:test' -F 'file=@/path/to/file/users.csv' 'https://your.zammad/api/v1/users/import' # # @response_message 201 Import started. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def import_start string = params[:data] if string.blank? && params[:file].present? @@ -896,7 +896,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @parameter User(required,body) [User] The attribute value structure needed to create a User record. # # @response_message 200 [User] Created User record. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def create_internal # permission check check_attributes_by_current_user_permission(params) @@ -947,7 +947,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @parameter User(required,body) [User] The attribute value structure needed to create a User record. # # @response_message 200 [User] Created User record. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def create_signup # check if feature is enabled if !Setting.get('user_create_account') @@ -1013,7 +1013,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content # @parameter User(required,body) [User] The attribute value structure needed to create a User record. # # @response_message 200 [User] Created User record. - # @response_message 401 Invalid session. + # @response_message 403 Forbidden / Invalid session. def create_admin if User.count > 2 # system and example users raise Exceptions::UnprocessableEntity, 'Administrator account already created' diff --git a/app/policies/pundit_policy.rb b/app/policies/pundit_policy.rb index 7ec65ce91..36f0d5a45 100644 --- a/app/policies/pundit_policy.rb +++ b/app/policies/pundit_policy.rb @@ -16,7 +16,7 @@ module PunditPolicy def user_required! return if user - raise Exceptions::NotAuthorized, 'authentication failed' + raise Exceptions::Forbidden, 'Authentication required' end private @@ -25,7 +25,7 @@ module PunditPolicy if details details = "Not authorized (#{details})!" end - @custom_exception = Exceptions::NotAuthorized.new(details) + @custom_exception = Exceptions::Forbidden.new(details) false end diff --git a/lib/auth/internal.rb b/lib/auth/internal.rb index 60ccb771b..2f54c2f99 100644 --- a/lib/auth/internal.rb +++ b/lib/auth/internal.rb @@ -14,7 +14,7 @@ class Auth password_verified = PasswordHash.verified?(user.password, password) - raise Exceptions::NotAuthorized, 'Please verify your account before you can login!' if !user.verified && user.source == 'signup' && password_verified + raise Exceptions::Forbidden, 'Please verify your account before you can login!' if !user.verified && user.source == 'signup' && password_verified password_verified end diff --git a/lib/exceptions.rb b/lib/exceptions.rb index f9114b6e4..3113af39b 100644 --- a/lib/exceptions.rb +++ b/lib/exceptions.rb @@ -2,6 +2,8 @@ module Exceptions class NotAuthorized < StandardError; end + class Forbidden < StandardError; end + class UnprocessableEntity < StandardError; end end diff --git a/lib/user_context.rb b/lib/user_context.rb index 9fede8f8d..49ab58a89 100644 --- a/lib/user_context.rb +++ b/lib/user_context.rb @@ -15,18 +15,18 @@ class UserContext < Delegator end def permissions!(permissions) - raise Exceptions::NotAuthorized, 'authentication failed' if !@user - raise Exceptions::NotAuthorized, 'Not authorized (user)!' if !@user.permissions?(permissions) + raise Exceptions::Forbidden, 'Authentication required' if !@user + raise Exceptions::Forbidden, 'Not authorized (user)!' if !@user.permissions?(permissions) return if !@token return if @token.with_context(user: @user) { permissions?(permissions) } - raise Exceptions::NotAuthorized, 'Not authorized (token)!' + raise Exceptions::Forbidden, 'Not authorized (token)!' end def permissions?(permissions) permissions!(permissions) true - rescue Exceptions::NotAuthorized + rescue Exceptions::Forbidden false end end diff --git a/public/401.html b/public/401.html index c70b0ca3f..ec114cb45 100644 --- a/public/401.html +++ b/public/401.html @@ -10,7 +10,7 @@ <% end %> <% if !@traceback %>
-

Sorry, but you're not allowed to access this page. If you're registered please log in and refresh this page.

+

Sorry, but your authentication failed. Double check your credentials and try again.

<% else %>
<%= @exception.message %>
<% if @exception.backtrace %> @@ -21,4 +21,4 @@ <% end %> <% end %> - \ No newline at end of file + diff --git a/public/403.html b/public/403.html new file mode 100644 index 000000000..414486781 --- /dev/null +++ b/public/403.html @@ -0,0 +1,24 @@ + + + +403: Forbidden + +class="error-message"<% end %>> +

403: Forbidden

+<% if @message.present? %> +
<%= @message %>
+<% end %> +<% if !@traceback %> +
+

Sorry, but you're not allowed to access this page. If you're registered please log in and refresh this page.

+<% else %> +
<%= @exception.message %>
+ <% if @exception.backtrace %> +

+<% @exception.backtrace.each {|row| %>
+  <%= row %>
+<% } %>
+ <% end %> +<% end %> + + diff --git a/public/assets/form/form.js b/public/assets/form/form.js index df238b7df..961d76179 100644 --- a/public/assets/form/form.js +++ b/public/assets/form/form.js @@ -251,7 +251,10 @@ $(function() { _this._config = data }).fail(function(jqXHR, textStatus, errorThrown) { if (jqXHR.status == 401) { - _this.log('error', 'Faild to load form config, feature is disabled!') + _this.log('error', 'Faild to load form config, wrong authentication data!') + } + else if (jqXHR.status == 403) { + _this.log('error', 'Faild to load form config, feature is disabled or request is wrong!') } else { _this.log('error', 'Faild to load form config!') diff --git a/spec/requests/api_auth_on_behalf_of_spec.rb b/spec/requests/api_auth_on_behalf_of_spec.rb index f038178d3..53d08a3e4 100644 --- a/spec/requests/api_auth_on_behalf_of_spec.rb +++ b/spec/requests/api_auth_on_behalf_of_spec.rb @@ -137,7 +137,7 @@ RSpec.describe 'Api Auth On Behalf Of', type: :request do } authenticated_as(admin, on_behalf_of: 99_449_494_949) post '/api/v1/tickets', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(@response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq("No such user '99449494949'") @@ -156,7 +156,7 @@ RSpec.describe 'Api Auth On Behalf Of', type: :request do } authenticated_as(customer, on_behalf_of: admin.email) post '/api/v1/tickets', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(@response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq("Current user has no permission to use 'X-On-Behalf-Of'!") diff --git a/spec/requests/api_auth_spec.rb b/spec/requests/api_auth_spec.rb index 743ff7b72..e924b151a 100644 --- a/spec/requests/api_auth_spec.rb +++ b/spec/requests/api_auth_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_password_access', false) authenticated_as(admin) get '/api/v1/sessions', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API password access disabled!') @@ -51,7 +51,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_password_access', false) authenticated_as(agent) get '/api/v1/tickets', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API password access disabled!') @@ -72,7 +72,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_password_access', false) authenticated_as(customer) get '/api/v1/tickets', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API password access disabled!') @@ -104,7 +104,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_token_access', false) get '/api/v1/sessions', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API token access disabled!') @@ -124,7 +124,7 @@ RSpec.describe 'Api Auth', type: :request do admin_token.save! get '/api/v1/sessions', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (token)!') @@ -132,7 +132,7 @@ RSpec.describe 'Api Auth', type: :request do admin_token.save! get '/api/v1/sessions', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (token)!') @@ -161,7 +161,7 @@ RSpec.describe 'Api Auth', type: :request do expect(json_response).to be_truthy get '/api/v1/roles', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (token)!') @@ -254,7 +254,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_token_access', false) get '/api/v1/tickets', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API token access disabled!') @@ -276,7 +276,7 @@ RSpec.describe 'Api Auth', type: :request do name = "some org name #{rand(999_999_999)}" post '/api/v1/organizations', params: { name: name }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end @@ -293,7 +293,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_token_access', false) get '/api/v1/tickets', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API token access disabled!') @@ -315,7 +315,7 @@ RSpec.describe 'Api Auth', type: :request do name = "some org name #{rand(999_999_999)}" post '/api/v1/organizations', params: { name: name }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does token auth - invalid user - admin', last_admin_check: false do @@ -334,7 +334,7 @@ RSpec.describe 'Api Auth', type: :request do Setting.set('api_token_access', false) get '/api/v1/sessions', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(response.header).not_to be_key('Access-Control-Allow-Origin') expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('API token access disabled!') diff --git a/spec/requests/calendar_spec.rb b/spec/requests/calendar_spec.rb index 8446acf5d..934a0c13d 100644 --- a/spec/requests/calendar_spec.rb +++ b/spec/requests/calendar_spec.rb @@ -10,22 +10,22 @@ RSpec.describe 'Calendars', type: :request do it 'does calendar index with nobody' do get '/api/v1/calendars', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') get '/api/v1/calendars_init', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') get '/api/v1/calendars/timezones', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end it 'does calendar index with admin' do diff --git a/spec/requests/error_spec.rb b/spec/requests/error_spec.rb index 75f849a79..0ab467236 100644 --- a/spec/requests/error_spec.rb +++ b/spec/requests/error_spec.rb @@ -104,10 +104,11 @@ RSpec.describe 'Error handling', type: :request do context 'request is not authenticated' do before do + authenticated_as(create(:agent), password: 'wrongpw') get '/api/v1/organizations', as: as end - let(:message) { 'authentication failed' } + let(:message) { 'Invalid BasicAuth credentials' } let(:http_status) { :unauthorized } context 'requesting JSON' do @@ -122,6 +123,27 @@ RSpec.describe 'Error handling', type: :request do end end + context 'request is forbidden' do + + before do + get '/api/v1/organizations', as: as + end + + let(:message) { 'Authentication required' } + let(:http_status) { :forbidden } + + context 'requesting JSON' do + include_examples 'JSON response format' + end + + context 'requesting HTML' do + let(:title) { '403: Forbidden' } + let(:headline) { '403: Forbidden' } + + include_examples 'HTML response format' + end + end + context 'exception is raised' do before do @@ -148,8 +170,8 @@ RSpec.describe 'Error handling', type: :request do end end - shared_examples 'handles exception' do |exception, http_status, title, headline| - include_examples 'exception check', 'some error message', exception, http_status, title, headline + shared_examples 'handles exception' do |exception, http_status, title, headline, message = 'some error message'| + include_examples 'exception check', message, exception, http_status, title, headline end shared_examples 'masks exception' do |exception, http_status, title, headline| @@ -157,6 +179,8 @@ RSpec.describe 'Error handling', type: :request do end include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized' + include_examples 'handles exception', Exceptions::Forbidden, :forbidden, '403: Forbidden', '403: Forbidden' + include_examples 'handles exception', Pundit::NotAuthorizedError, :forbidden, '403: Forbidden', '403: Forbidden', 'Not authorized' include_examples 'handles exception', ActiveRecord::RecordNotFound, :not_found, '404: Not Found', '404: Requested resource was not found' include_examples 'handles exception', Exceptions::UnprocessableEntity, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.' include_examples 'masks exception', ArgumentError, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.' diff --git a/spec/requests/external_credentials_spec.rb b/spec/requests/external_credentials_spec.rb index 600fbaace..ceaf4ae3b 100644 --- a/spec/requests/external_credentials_spec.rb +++ b/spec/requests/external_credentials_spec.rb @@ -5,38 +5,38 @@ RSpec.describe 'External Credentials', type: :request do context 'without authentication' do describe '#index' do - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/api/v1/external_credentials', as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response).to include('error' => 'authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response).to include('error' => 'Authentication required') end end describe '#app_verify' do - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do post '/api/v1/external_credentials/facebook/app_verify', as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response).to include('error' => 'authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response).to include('error' => 'Authentication required') end end describe '#link_account' do - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/api/v1/external_credentials/facebook/link_account', as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response).to include('error' => 'authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response).to include('error' => 'Authentication required') end end describe '#callback' do - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/api/v1/external_credentials/facebook/callback', as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response).to include('error' => 'authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response).to include('error' => 'Authentication required') end end end @@ -72,9 +72,9 @@ RSpec.describe 'External Credentials', type: :request do context 'when permission for Facebook channel is deactivated' do before { Permission.find_by(name: 'admin.channel_facebook').update(active: false) } - it 'returns 401 unauthorized with internal (Zammad) error' do + it 'returns 403 Forbidden with internal (Zammad) error' do post '/api/v1/external_credentials/facebook/app_verify', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to include('error' => 'Not authorized (user)!') end end @@ -207,7 +207,7 @@ RSpec.describe 'External Credentials', type: :request do before { Permission.find_by(name: 'admin.channel_twitter').update(active: false) } include_examples 'for failure cases' do - let(:status) { :unauthorized } + let(:status) { :forbidden } let(:error_message) { 'Not authorized (user)!' } end end diff --git a/spec/requests/form_spec.rb b/spec/requests/form_spec.rb index 821b1e741..8ea5a6ac9 100644 --- a/spec/requests/form_spec.rb +++ b/spec/requests/form_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do it 'does get config call' do post '/api/v1/form_config', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') end @@ -19,7 +19,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do it 'does get config call' do Setting.set('form_ticket_create', true) post '/api/v1/form_config', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') @@ -40,7 +40,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: 'invalid' }, as: :json expect(response).to have_http_status(:unauthorized) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('Not authorized') + expect(json_response['error']).to eq('Authorization failed') post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token }, as: :json expect(response).to have_http_status(:ok) @@ -105,7 +105,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: 'invalid' }, as: :json expect(response).to have_http_status(:unauthorized) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('Not authorized') + expect(json_response['error']).to eq('Authorization failed') post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token }, as: :json expect(response).to have_http_status(:ok) @@ -164,7 +164,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do sleep 10 # wait until elasticsearch is index post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test-last', body: 'hello' }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to be_truthy @@ -184,7 +184,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do sleep 10 # wait until elasticsearch is index post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test-2-last', body: 'hello' }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to be_truthy @@ -204,7 +204,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do sleep 10 # wait until elasticsearch is index post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token, name: 'Bob Smith', email: 'discard@znuny.com', title: 'test-2-last', body: 'hello' }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to be_truthy end @@ -229,7 +229,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do post '/api/v1/form_submit', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end end end diff --git a/spec/requests/integration/cti_spec.rb b/spec/requests/integration/cti_spec.rb index 15a034300..e46ac5b04 100644 --- a/spec/requests/integration/cti_spec.rb +++ b/spec/requests/integration/cti_spec.rb @@ -612,7 +612,7 @@ RSpec.describe 'Integration CTI', type: :request do expect(log.duration_talking_time).to be_nil get '/api/v1/cti/log' - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) # get caller list authenticated_as(agent) diff --git a/spec/requests/integration/idoit_spec.rb b/spec/requests/integration/idoit_spec.rb index 050582bf7..e99db1555 100644 --- a/spec/requests/integration/idoit_spec.rb +++ b/spec/requests/integration/idoit_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'Idoit', type: :request do } authenticated_as(agent) post '/api/v1/integration/idoit/verify', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response).not_to be_blank expect(json_response['error']).to eq('Not authorized (user)!') diff --git a/spec/requests/integration/monitoring_spec.rb b/spec/requests/integration/monitoring_spec.rb index e8c934506..2e710857f 100644 --- a/spec/requests/integration/monitoring_spec.rb +++ b/spec/requests/integration/monitoring_spec.rb @@ -48,7 +48,7 @@ RSpec.describe 'Monitoring', type: :request do # health_check get '/api/v1/monitoring/health_check', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['healthy']).to be_falsey @@ -56,7 +56,7 @@ RSpec.describe 'Monitoring', type: :request do # status get '/api/v1/monitoring/status', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['agents']).to be_falsey @@ -67,11 +67,11 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end @@ -79,7 +79,7 @@ RSpec.describe 'Monitoring', type: :request do # health_check get '/api/v1/monitoring/health_check?token=abc', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['healthy']).to be_falsey @@ -87,7 +87,7 @@ RSpec.describe 'Monitoring', type: :request do # status get '/api/v1/monitoring/status?token=abc', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['agents']).to be_falsey @@ -98,11 +98,11 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: { token: 'abc' }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end @@ -221,11 +221,11 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: { token: token }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end @@ -267,7 +267,7 @@ RSpec.describe 'Monitoring', type: :request do # health_check authenticated_as(agent) get '/api/v1/monitoring/health_check', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['healthy']).to be_falsey @@ -275,7 +275,7 @@ RSpec.describe 'Monitoring', type: :request do # status get '/api/v1/monitoring/status', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['agents']).to be_falsey @@ -286,7 +286,7 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: { token: token }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey @@ -303,7 +303,7 @@ RSpec.describe 'Monitoring', type: :request do # health_check authenticated_as(admin) get '/api/v1/monitoring/health_check', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['healthy']).to be_falsey @@ -311,7 +311,7 @@ RSpec.describe 'Monitoring', type: :request do # status get '/api/v1/monitoring/status', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['agents']).to be_falsey @@ -322,7 +322,7 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: { token: token }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey @@ -360,11 +360,11 @@ RSpec.describe 'Monitoring', type: :request do # token post '/api/v1/monitoring/token', params: { token: token }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['token']).to be_falsey - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') permission.active = true permission.save! diff --git a/spec/requests/integration/placetel_spec.rb b/spec/requests/integration/placetel_spec.rb index 9f9a2f14e..96ba2a033 100644 --- a/spec/requests/integration/placetel_spec.rb +++ b/spec/requests/integration/placetel_spec.rb @@ -512,7 +512,7 @@ RSpec.describe 'Integration Placetel', type: :request do # get caller list get '/api/v1/cti/log' - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) authenticated_as(agent) get '/api/v1/cti/log', as: :json diff --git a/spec/requests/integration/sipgate_spec.rb b/spec/requests/integration/sipgate_spec.rb index 6a8f9cb52..ed91084dd 100644 --- a/spec/requests/integration/sipgate_spec.rb +++ b/spec/requests/integration/sipgate_spec.rb @@ -442,7 +442,7 @@ RSpec.describe 'Integration Sipgate', type: :request do # get caller list get '/api/v1/cti/log' - expect(@response).to have_http_status(:unauthorized) + expect(@response).to have_http_status(:forbidden) authenticated_as(agent) get '/api/v1/cti/log', as: :json diff --git a/spec/requests/knowledge_base/loading_initial_data_spec.rb b/spec/requests/knowledge_base/loading_initial_data_spec.rb index d71940931..bb3ca9ee0 100644 --- a/spec/requests/knowledge_base/loading_initial_data_spec.rb +++ b/spec/requests/knowledge_base/loading_initial_data_spec.rb @@ -55,6 +55,6 @@ RSpec.describe 'KnowledgeBase loading initial data', type: :request, searchindex end describe 'for guests without authorization' do - it { expect(response).to have_http_status(:unauthorized) } + it { expect(response).to have_http_status(:forbidden) } end end diff --git a/spec/requests/knowledge_base/translation_update_spec.rb b/spec/requests/knowledge_base/translation_update_spec.rb index ef8462c96..893f980fb 100644 --- a/spec/requests/knowledge_base/translation_update_spec.rb +++ b/spec/requests/knowledge_base/translation_update_spec.rb @@ -67,17 +67,17 @@ RSpec.describe 'KnowledgeBase translation update', type: :request, authenticated describe 'as reader' do let(:user_identifier) { :agent } - it { expect(response).to have_http_status(:unauthorized) } + it { expect(response).to have_http_status(:forbidden) } end describe 'as non-KB user' do let(:user_identifier) { :customer } - it { expect(response).to have_http_status(:unauthorized) } + it { expect(response).to have_http_status(:forbidden) } end describe 'as a guest' do - it { expect(response).to have_http_status(:unauthorized) } + it { expect(response).to have_http_status(:forbidden) } end end end diff --git a/spec/requests/organization_spec.rb b/spec/requests/organization_spec.rb index c91c2f8ed..b65b0bfce 100644 --- a/spec/requests/organization_spec.rb +++ b/spec/requests/organization_spec.rb @@ -152,7 +152,7 @@ RSpec.describe 'Organization', type: :request, searchindex: true do # search Scheduler.worker(true) get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does index with customer2' do @@ -171,14 +171,14 @@ RSpec.describe 'Organization', type: :request, searchindex: true do expect('Rest Org #1').to eq(json_response['name']) get "/api/v1/organizations/#{organization2.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['name']).to be_nil # search Scheduler.worker(true) get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does organization search sortable' do @@ -474,7 +474,7 @@ RSpec.describe 'Organization', type: :request, searchindex: true do it 'does csv example - customer no access' do authenticated_as(customer) get '/api/v1/organizations/import_example', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') end diff --git a/spec/requests/overview_spec.rb b/spec/requests/overview_spec.rb index f77d8bb62..ed4d8ac74 100644 --- a/spec/requests/overview_spec.rb +++ b/spec/requests/overview_spec.rb @@ -37,7 +37,7 @@ RSpec.describe 'Overviews', type: :request do post '/api/v1/overviews', params: params, as: :json expect(response).to have_http_status(:unauthorized) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does create overviews' do diff --git a/spec/requests/package_spec.rb b/spec/requests/package_spec.rb index bb8ff25b7..fe174c123 100644 --- a/spec/requests/package_spec.rb +++ b/spec/requests/package_spec.rb @@ -16,11 +16,11 @@ RSpec.describe 'Packages', type: :request do it 'does packages index with nobody' do get '/api/v1/packages', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['packages']).to be_falsey - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end it 'does packages index with admin' do @@ -38,7 +38,7 @@ RSpec.describe 'Packages', type: :request do expect(response).to have_http_status(:unauthorized) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does packages index with inactive admin' do @@ -49,14 +49,14 @@ RSpec.describe 'Packages', type: :request do expect(response).to have_http_status(:unauthorized) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does packages index with agent' do authenticated_as(agent) get '/api/v1/packages', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['packages']).to be_falsey expect(json_response['error']).to eq('Not authorized (user)!') @@ -66,7 +66,7 @@ RSpec.describe 'Packages', type: :request do authenticated_as(customer) get '/api/v1/packages', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['packages']).to be_falsey expect(json_response['error']).to eq('Not authorized (user)!') diff --git a/spec/requests/search_spec.rb b/spec/requests/search_spec.rb index 397f51a4d..71e615ebf 100644 --- a/spec/requests/search_spec.rb +++ b/spec/requests/search_spec.rb @@ -98,22 +98,22 @@ RSpec.describe 'Search', type: :request, searchindex: true do } post '/api/v1/search/ticket', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response).not_to be_blank - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') post '/api/v1/search/user', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response).not_to be_blank - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') post '/api/v1/search', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response).not_to be_blank - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end it 'does settings index with admin' do diff --git a/spec/requests/session_spec.rb b/spec/requests/session_spec.rb index c91958e0a..8a0db15dd 100644 --- a/spec/requests/session_spec.rb +++ b/spec/requests/session_spec.rb @@ -47,7 +47,7 @@ RSpec.describe 'Sessions endpoints', type: :request do it 'returns a new user-session response' do get '/auth/sso', as: :json, headers: headers - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end end @@ -86,7 +86,7 @@ RSpec.describe 'Sessions endpoints', type: :request do end context 'with valid user login' do - let(:user) { User.last } + let(:user) { create(:agent) } let(:login) { user.login } context 'in Maintenance Mode' do @@ -95,10 +95,10 @@ RSpec.describe 'Sessions endpoints', type: :request do context 'in "REMOTE_USER" request env var' do let(:env) { { 'REMOTE_USER' => login } } - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/auth/sso', as: :json, env: env - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to include('error' => 'Maintenance mode enabled!') end end @@ -106,10 +106,10 @@ RSpec.describe 'Sessions endpoints', type: :request do context 'in "HTTP_REMOTE_USER" request env var' do let(:env) { { 'HTTP_REMOTE_USER' => login } } - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/auth/sso', as: :json, env: env - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to include('error' => 'Maintenance mode enabled!') end end @@ -117,10 +117,10 @@ RSpec.describe 'Sessions endpoints', type: :request do context 'in "X-Forwarded-User" request header' do let(:headers) { { 'X-Forwarded-User' => login } } - it 'returns 401 unauthorized' do + it 'returns 403 Forbidden' do get '/auth/sso', as: :json, headers: headers - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to include('error' => 'Maintenance mode enabled!') end end diff --git a/spec/requests/settings_spec.rb b/spec/requests/settings_spec.rb index 4031474e5..4047d2d78 100644 --- a/spec/requests/settings_spec.rb +++ b/spec/requests/settings_spec.rb @@ -24,15 +24,15 @@ RSpec.describe 'Settings', type: :request do # index get '/api/v1/settings', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['settings']).to be_falsey # show setting = Setting.find_by(name: 'product_name') get "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response['error']).to eq('Authentication required') end it 'does settings index with admin' do @@ -108,7 +108,7 @@ RSpec.describe 'Settings', type: :request do # delete setting = Setting.find_by(name: 'product_name') delete "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (feature not possible)') end @@ -136,7 +136,7 @@ RSpec.describe 'Settings', type: :request do # show setting = Setting.find_by(name: 'product_name') get "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (required ["admin.branding"])!') setting = Setting.find_by(name: 'api_token_access') @@ -156,7 +156,7 @@ RSpec.describe 'Settings', type: :request do } } put "/api/v1/settings/#{setting.id}", params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (required ["admin.branding"])!') # update @@ -180,7 +180,7 @@ RSpec.describe 'Settings', type: :request do # delete setting = Setting.find_by(name: 'product_name') delete "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (feature not possible)') end @@ -189,7 +189,7 @@ RSpec.describe 'Settings', type: :request do # index authenticated_as(agent) get '/api/v1/settings', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['settings']).to be_falsey expect(json_response['error']).to eq('Not authorized (user)!') @@ -197,7 +197,7 @@ RSpec.describe 'Settings', type: :request do # show setting = Setting.find_by(name: 'product_name') get "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') end @@ -206,7 +206,7 @@ RSpec.describe 'Settings', type: :request do # index authenticated_as(customer) get '/api/v1/settings', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['settings']).to be_falsey expect(json_response['error']).to eq('Not authorized (user)!') @@ -214,13 +214,13 @@ RSpec.describe 'Settings', type: :request do # show setting = Setting.find_by(name: 'product_name') get "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') # delete setting = Setting.find_by(name: 'product_name') delete "/api/v1/settings/#{setting.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') end end diff --git a/spec/requests/sla_spec.rb b/spec/requests/sla_spec.rb index 142dbe3b5..db5fb9287 100644 --- a/spec/requests/sla_spec.rb +++ b/spec/requests/sla_spec.rb @@ -10,10 +10,10 @@ RSpec.describe 'SLAs', type: :request do it 'does index sla with nobody' do get '/api/v1/slas', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Authentication required') end it 'does index sla with admin' do diff --git a/spec/requests/text_module_spec.rb b/spec/requests/text_module_spec.rb index 9e40fe45b..cc4000001 100644 --- a/spec/requests/text_module_spec.rb +++ b/spec/requests/text_module_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Text Module', type: :request do it 'does csv example - customer no access' do authenticated_as(customer) get '/api/v1/text_modules/import_example', as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') end diff --git a/spec/requests/ticket/article_attachments_spec.rb b/spec/requests/ticket/article_attachments_spec.rb index ff951371f..b4a964dc9 100644 --- a/spec/requests/ticket/article_attachments_spec.rb +++ b/spec/requests/ticket/article_attachments_spec.rb @@ -33,8 +33,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do authenticated_as(agent) get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {} - expect(response).to have_http_status(:unauthorized) - expect(@response.body).to match(/401: Unauthorized/) + expect(response).to have_http_status(:forbidden) + expect(@response.body).to match(/403: Forbidden/) ticket2 = create(:ticket, group: group) ticket1.merge_to( @@ -49,8 +49,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do authenticated_as(agent) get "/api/v1/ticket_attachment/#{ticket2.id}/#{article2.id}/#{store1.id}", params: {} - expect(response).to have_http_status(:unauthorized) - expect(@response.body).to match(/401: Unauthorized/) + expect(response).to have_http_status(:forbidden) + expect(@response.body).to match(/403: Forbidden/) # allow access via merged ticket id also authenticated_as(agent) @@ -60,8 +60,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do authenticated_as(agent) get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {} - expect(response).to have_http_status(:unauthorized) - expect(@response.body).to match(/401: Unauthorized/) + expect(response).to have_http_status(:forbidden) + expect(@response.body).to match(/403: Forbidden/) end diff --git a/spec/requests/ticket/article_spec.rb b/spec/requests/ticket/article_spec.rb index c9b6a113a..f6e468f4d 100644 --- a/spec/requests/ticket/article_spec.rb +++ b/spec/requests/ticket/article_spec.rb @@ -262,12 +262,12 @@ AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO expect(ticket.articles[4].attachments.count).to eq(0) get "/api/v1/ticket_articles/#{article.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') put "/api/v1/ticket_articles/#{article.id}", params: { internal: false }, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') diff --git a/spec/requests/ticket_spec.rb b/spec/requests/ticket_spec.rb index 209ce996f..dfb2f2ec4 100644 --- a/spec/requests/ticket_spec.rb +++ b/spec/requests/ticket_spec.rb @@ -84,7 +84,7 @@ RSpec.describe 'Ticket', type: :request do } authenticated_as(agent_change_only) post '/api/v1/tickets', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') end @@ -772,7 +772,7 @@ RSpec.describe 'Ticket', type: :request do ) authenticated_as(agent) get "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') @@ -780,12 +780,12 @@ RSpec.describe 'Ticket', type: :request do title: 'ticket with wrong ticket id - 2', } put "/api/v1/tickets/#{ticket.id}", params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') end @@ -923,12 +923,12 @@ RSpec.describe 'Ticket', type: :request do expect(json_response['internal']).to eq(false) delete "/api/v1/ticket_articles/#{json_response['id']}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (communication articles cannot be deleted)!') delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (admin permission required)!') end @@ -1022,7 +1022,7 @@ RSpec.describe 'Ticket', type: :request do expect(json_response['type_id']).to eq(Ticket::Article::Type.lookup(name: 'email').id) delete "/api/v1/ticket_articles/#{json_response['id']}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json expect(response).to have_http_status(:ok) @@ -1177,7 +1177,7 @@ RSpec.describe 'Ticket', type: :request do ) authenticated_as(customer) get "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') @@ -1185,12 +1185,12 @@ RSpec.describe 'Ticket', type: :request do title: 'ticket with wrong ticket id - 2', } put "/api/v1/tickets/#{ticket.id}", params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') end @@ -1266,7 +1266,7 @@ RSpec.describe 'Ticket', type: :request do expect(json_response['tickets_count']).to eq(1) delete "/api/v1/ticket_articles/#{article_json_response['id']}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (agent permission required)!') @@ -1290,7 +1290,7 @@ RSpec.describe 'Ticket', type: :request do expect(json_response['type_id']).to eq(Ticket::Article::Type.lookup(name: 'note').id) delete "/api/v1/ticket_articles/#{json_response['id']}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (agent permission required)!') @@ -1321,12 +1321,12 @@ RSpec.describe 'Ticket', type: :request do subject: 'new subject', } put "/api/v1/ticket_articles/#{json_response['id']}", params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (ticket.agent or admin permission required)!') delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized (admin permission required)!') end @@ -1826,7 +1826,7 @@ RSpec.describe 'Ticket', type: :request do authenticated_as(customer) get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) authenticated_as(agent) get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json @@ -1955,7 +1955,7 @@ RSpec.describe 'Ticket', type: :request do authenticated_as(customer) put "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) authenticated_as(agent) put "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json @@ -1965,13 +1965,13 @@ RSpec.describe 'Ticket', type: :request do expect(json_response['message']).to eq('No such master ticket number!') put "/api/v1/ticket_merge/#{ticket3.id}/#{ticket1.number}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') expect(json_response['error_human']).to eq('Not authorized') put "/api/v1/ticket_merge/#{ticket1.id}/#{ticket3.number}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['error']).to eq('Not authorized') expect(json_response['error_human']).to eq('Not authorized') @@ -2110,7 +2110,7 @@ RSpec.describe 'Ticket', type: :request do authenticated_as(customer) get "/api/v1/ticket_history/#{ticket1.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does ticket related' do @@ -2127,7 +2127,7 @@ RSpec.describe 'Ticket', type: :request do authenticated_as(customer) get "/api/v1/ticket_related/#{ticket1.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does ticket recent' do @@ -2137,7 +2137,7 @@ RSpec.describe 'Ticket', type: :request do authenticated_as(customer) get '/api/v1/ticket_recent', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end end diff --git a/spec/requests/user/organization_spec.rb b/spec/requests/user/organization_spec.rb index 01d5ef49b..f95e8a49e 100644 --- a/spec/requests/user/organization_spec.rb +++ b/spec/requests/user/organization_spec.rb @@ -133,7 +133,7 @@ RSpec.describe 'User Organization', type: :request, searchindex: true do # search Scheduler.worker(true) get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does organization index with customer2' do @@ -150,14 +150,14 @@ RSpec.describe 'User Organization', type: :request, searchindex: true do expect('Rest Org').to eq(json_response['name']) get "/api/v1/organizations/#{organization2.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response).to be_a_kind_of(Hash) expect(json_response['name']).to be_nil # search Scheduler.worker(true) get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end end end diff --git a/spec/requests/user/permission_spec.rb b/spec/requests/user/permission_spec.rb index 63ea2e047..5525a4691 100644 --- a/spec/requests/user/permission_spec.rb +++ b/spec/requests/user/permission_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'User endpoint', type: :request do let(:attributes) { attributes_params_for(:user) } - it 'responds unauthorized for customer' do + it 'responds forbidden for customer' do requester = create(:customer) authenticated_as(requester) @@ -30,7 +30,7 @@ RSpec.describe 'User endpoint', type: :request do User.count } - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end context 'privileged attributes' do @@ -58,7 +58,7 @@ RSpec.describe 'User endpoint', type: :request do expect(User.last.send(map_method_id)).to eq(send(map_method_id)) end - it 'responds unauthorized for sub admin without admin.user' do + it 'responds forbidden for sub admin without admin.user' do authenticated_as(admin_without_admin_user_permissions) expect do @@ -67,7 +67,7 @@ RSpec.describe 'User endpoint', type: :request do User.count } - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'responds successful for agent but removes assignment' do @@ -131,7 +131,7 @@ RSpec.describe 'User endpoint', type: :request do expect(User.last.roles).to eq(privileged) end - it 'responds unauthorized for sub admin without admin.user' do + it 'responds forbidden for sub admin without admin.user' do authenticated_as(admin_without_admin_user_permissions) expect do @@ -140,7 +140,7 @@ RSpec.describe 'User endpoint', type: :request do User.count } - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'responds successful for agent but removes assignment' do @@ -193,7 +193,7 @@ RSpec.describe 'User endpoint', type: :request do expect(response).to have_http_status(:success) end - def unauthorized_update_request(requester:, requested:) + def forbidden_update_request(requester:, requested:) authenticated_as(requester) expect do @@ -211,7 +211,7 @@ RSpec.describe 'User endpoint', type: :request do end } - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end context 'request by admin.user' do @@ -251,29 +251,29 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { admin_without_admin_user_permissions } - it 'is unauthorized for same admin' do - unauthorized_update_request( + it 'is forbidden for same admin' do + forbidden_update_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other admin' do - unauthorized_update_request( + it 'is forbidden for other admin' do + forbidden_update_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized for agent' do - unauthorized_update_request( + it 'is forbidden for agent' do + forbidden_update_request( requester: requester, requested: create(:agent), ) end - it 'is unauthorized for customer' do - unauthorized_update_request( + it 'is forbidden for customer' do + forbidden_update_request( requester: requester, requested: create(:customer), ) @@ -284,22 +284,22 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { create(:agent) } - it 'is unauthorized for admin' do - unauthorized_update_request( + it 'is forbidden for admin' do + forbidden_update_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized same agent' do - unauthorized_update_request( + it 'is forbidden same agent' do + forbidden_update_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other agent' do - unauthorized_update_request( + it 'is forbidden for other agent' do + forbidden_update_request( requester: requester, requested: create(:agent), ) @@ -317,40 +317,40 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { create(:customer) } - it 'is unauthorized for admin' do - unauthorized_update_request( + it 'is forbidden for admin' do + forbidden_update_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized for agent' do - unauthorized_update_request( + it 'is forbidden for agent' do + forbidden_update_request( requester: requester, requested: create(:agent), ) end - it 'is unauthorized for same customer' do - unauthorized_update_request( + it 'is forbidden for same customer' do + forbidden_update_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other customer' do - unauthorized_update_request( + it 'is forbidden for other customer' do + forbidden_update_request( requester: requester, requested: create(:customer), ) end - it 'is unauthorized for same organization' do + it 'is forbidden for same organization' do same_organization = create(:organization) requester.update!(organization: same_organization) - unauthorized_update_request( + forbidden_update_request( requester: requester, requested: create(:customer, organization: same_organization), ) @@ -383,7 +383,7 @@ RSpec.describe 'User endpoint', type: :request do expect(response).to have_http_status(:success) end - it 'responds unauthorized for sub admin without admin.user' do + it 'responds forbidden for sub admin without admin.user' do authenticated_as(admin_without_admin_user_permissions) expect do @@ -392,7 +392,7 @@ RSpec.describe 'User endpoint', type: :request do value_of_attribute } - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end end @@ -522,12 +522,12 @@ RSpec.describe 'User endpoint', type: :request do expect(requested).not_to exist_in_database end - def unauthorized_destroy_request(requester:, requested:) + def forbidden_destroy_request(requester:, requested:) authenticated_as(requester) delete api_v1_delete_user_path(requested) - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(requested).to exist_in_database end @@ -568,29 +568,29 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { admin_without_admin_user_permissions } - it 'is unauthorized for same admin' do - unauthorized_destroy_request( + it 'is forbidden for same admin' do + forbidden_destroy_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other admin' do - unauthorized_destroy_request( + it 'is forbidden for other admin' do + forbidden_destroy_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized for agent' do - unauthorized_destroy_request( + it 'is forbidden for agent' do + forbidden_destroy_request( requester: requester, requested: create(:agent), ) end - it 'is unauthorized for customer' do - unauthorized_destroy_request( + it 'is forbidden for customer' do + forbidden_destroy_request( requester: requester, requested: create(:customer), ) @@ -601,29 +601,29 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { create(:agent) } - it 'is unauthorized for admin' do - unauthorized_destroy_request( + it 'is forbidden for admin' do + forbidden_destroy_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized same agent' do - unauthorized_destroy_request( + it 'is forbidden same agent' do + forbidden_destroy_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other agent' do - unauthorized_destroy_request( + it 'is forbidden for other agent' do + forbidden_destroy_request( requester: requester, requested: create(:agent), ) end - it 'is unauthorized for customer' do - unauthorized_destroy_request( + it 'is forbidden for customer' do + forbidden_destroy_request( requester: requester, requested: create(:customer), ) @@ -634,40 +634,40 @@ RSpec.describe 'User endpoint', type: :request do let(:requester) { create(:customer) } - it 'is unauthorized for admin' do - unauthorized_destroy_request( + it 'is forbidden for admin' do + forbidden_destroy_request( requester: requester, requested: create(:admin), ) end - it 'is unauthorized for agent' do - unauthorized_destroy_request( + it 'is forbidden for agent' do + forbidden_destroy_request( requester: requester, requested: create(:agent), ) end - it 'is unauthorized for same customer' do - unauthorized_destroy_request( + it 'is forbidden for same customer' do + forbidden_destroy_request( requester: requester, requested: requester, ) end - it 'is unauthorized for other customer' do - unauthorized_destroy_request( + it 'is forbidden for other customer' do + forbidden_destroy_request( requester: requester, requested: create(:customer), ) end - it 'is unauthorized for same organization' do + it 'is forbidden for same organization' do same_organization = create(:organization) requester.update!(organization: same_organization) - unauthorized_destroy_request( + forbidden_destroy_request( requester: requester, requested: create(:customer, organization: same_organization), ) diff --git a/spec/requests/user_spec.rb b/spec/requests/user_spec.rb index 0e96ffcf5..5a76215cb 100644 --- a/spec/requests/user_spec.rb +++ b/spec/requests/user_spec.rb @@ -154,13 +154,13 @@ RSpec.describe 'User', type: :request do # no user (because of no session) get '/api/v1/users', params: {}, headers: headers, as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response['error']).to eq('Authentication required') # me get '/api/v1/users/me', params: {}, headers: headers, as: :json - expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(response).to have_http_status(:forbidden) + expect(json_response['error']).to eq('Authentication required') end context 'password security' do @@ -182,25 +182,25 @@ RSpec.describe 'User', type: :request do authenticated_as(nil, login: 'not_existing@example.com', password: 'adminpw') get '/api/v1/users/me', params: {}, as: :json expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') get '/api/v1/users', params: {}, as: :json expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does auth tests - username auth, wrong pw' do authenticated_as(admin, password: 'not_existing') get '/api/v1/users', params: {}, as: :json expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does auth tests - email auth, wrong pw' do authenticated_as(nil, login: 'rest-admin@example.com', password: 'not_existing') get '/api/v1/users', params: {}, as: :json expect(response).to have_http_status(:unauthorized) - expect(json_response['error']).to eq('authentication failed') + expect(json_response['error']).to eq('Invalid BasicAuth credentials') end it 'does auth tests - username auth' do @@ -507,7 +507,7 @@ RSpec.describe 'User', type: :request do expect('rest-customer1@example.com').to eq(json_response['email']) get "/api/v1/users/#{customer2.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(Hash).to eq(json_response.class) expect(json_response['error']).to be_truthy @@ -515,18 +515,18 @@ RSpec.describe 'User', type: :request do role = Role.lookup(name: 'Admin') params = { firstname: 'Admin First', lastname: 'Admin Last', email: 'new_admin_by_customer1@example.com', role_ids: [ role.id ] } post '/api/v1/users', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) # create user with agent role role = Role.lookup(name: 'Agent') params = { firstname: 'Agent First', lastname: 'Agent Last', email: 'new_agent_by_customer1@example.com', role_ids: [ role.id ] } post '/api/v1/users', params: params, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) # search Scheduler.worker(true) get "/api/v1/users/search?query=#{CGI.escape('First')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does user index with customer2' do @@ -549,14 +549,14 @@ RSpec.describe 'User', type: :request do expect('rest-customer2@example.com').to eq(json_response['email']) get "/api/v1/users/#{customer.id}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(Hash).to eq(json_response.class) expect(json_response['error']).to be_truthy # search Scheduler.worker(true) get "/api/v1/users/search?query=#{CGI.escape('First')}", params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'does users show and response format (04.01)' do @@ -847,7 +847,7 @@ RSpec.describe 'User', type: :request do authenticated_as(customer) get '/api/v1/users/import_example', params: {}, as: :json - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) expect(json_response['error']).to eq('Not authorized (user)!') end @@ -1172,7 +1172,7 @@ RSpec.describe 'User', type: :request do it 'does not accept requests from customers', authenticated_as: -> { create(:customer) } do make_request successful_params - expect(response).to have_http_status(:unauthorized) + expect(response).to have_http_status(:forbidden) end it 'admins can give any role', authenticated_as: -> { create(:admin) } do