Fixes issue #2983 - HTTP 401 responses causing issues with Basic Authentication.

This commit is contained in:
Thorsten Eckel 2021-02-04 09:28:41 +01:00
parent 5a65e5bfcf
commit 876c0b18fd
52 changed files with 370 additions and 291 deletions

View file

@ -148,8 +148,8 @@ class ProfileOutOfOffice extends App.ControllerSubContent
data = JSON.parse(xhr.responseText) data = JSON.parse(xhr.responseText)
# show error message # show error message
if xhr.status is 401 || error is 'Unauthorized' if xhr.status is 403 || error is 'Not authorized'
message = '» ' + App.i18n.translateInline('Unauthorized') + ' «' message = '» ' + App.i18n.translateInline('Not authorized') + ' «'
else if xhr.status is 404 || error is 'Not Found' else if xhr.status is 404 || error is 'Not Found'
message = '» ' + App.i18n.translateInline('Not Found') + ' «' message = '» ' + App.i18n.translateInline('Not Found') + ' «'
else if data.error else if data.error

View file

@ -115,8 +115,8 @@ class App.TicketZoom extends App.Controller
return return
# show error message # show error message
if status is 401 || statusText is 'Unauthorized' if status is 403 || statusText is 'Not authorized'
@taskHead = '» ' + App.i18n.translateInline('Unauthorized') + ' «' @taskHead = '» ' + App.i18n.translateInline('Not authorized') + ' «'
@taskIconClass = 'diagonal-cross' @taskIconClass = 'diagonal-cross'
@renderScreenUnauthorized(objectName: 'Ticket') @renderScreenUnauthorized(objectName: 'Ticket')
else if status is 404 || statusText is 'Not Found' else if status is 404 || statusText is 'Not Found'

View file

@ -93,8 +93,9 @@ class _ajaxSingleton
# 200, all is fine # 200, all is fine
return if status is 200 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 401
return if status is 403
return if status is 404 return if status is 404
return if status is 422 return if status is 422

View file

@ -14,12 +14,12 @@ module ApplicationController::Authenticates
) )
return false if user return false if user
raise Exceptions::NotAuthorized, 'Not authorized (token)!' raise Exceptions::Forbidden, 'Not authorized (token)!'
end end
return false if current_user&.permissions?(key) return false if current_user&.permissions?(key)
raise Exceptions::NotAuthorized, 'Not authorized (user)!' raise Exceptions::Forbidden, 'Not authorized (user)!'
end end
def authentication_check(auth_param = {}) def authentication_check(auth_param = {})
@ -33,7 +33,7 @@ module ApplicationController::Authenticates
# return auth not ok # return auth not ok
if !user if !user
raise Exceptions::NotAuthorized, 'authentication failed' raise Exceptions::Forbidden, 'Authentication required'
end end
# return auth ok # return auth ok
@ -45,12 +45,15 @@ module ApplicationController::Authenticates
#logger.debug params.inspect #logger.debug params.inspect
#logger.debug session.inspect #logger.debug session.inspect
#logger.debug cookies.inspect #logger.debug cookies.inspect
authentication_errors = []
# already logged in, early exit # already logged in, early exit
if session.id && session[:user_id] if session.id && session[:user_id]
logger.debug { 'session based auth check' } logger.debug { 'session based auth check' }
user = User.lookup(id: session[:user_id]) user = User.lookup(id: session[:user_id])
return authentication_check_prerequesits(user, 'session', auth_param) if user 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 end
# check http basic based authentication # check http basic based authentication
@ -58,11 +61,13 @@ module ApplicationController::Authenticates
request.session_options[:skip] = true # do not send a session cookie request.session_options[:skip] = true # do not send a session cookie
logger.debug { "http basic auth check '#{username}'" } logger.debug { "http basic auth check '#{username}'" }
if Setting.get('api_password_access') == false if Setting.get('api_password_access') == false
raise Exceptions::NotAuthorized, 'API password access disabled!' raise Exceptions::Forbidden, 'API password access disabled!'
end end
user = User.authenticate(username, password) user = User.authenticate(username, password)
return authentication_check_prerequesits(user, 'basic_auth', auth_param) if user return authentication_check_prerequesits(user, 'basic_auth', auth_param) if user
authentication_errors.push('Invalid BasicAuth credentials')
end end
# check http token based authentication # check http token based authentication
@ -70,7 +75,7 @@ module ApplicationController::Authenticates
logger.debug { "http token auth check '#{token_string}'" } logger.debug { "http token auth check '#{token_string}'" }
request.session_options[:skip] = true # do not send a session cookie request.session_options[:skip] = true # do not send a session cookie
if Setting.get('api_token_access') == false if Setting.get('api_token_access') == false
raise Exceptions::NotAuthorized, 'API token access disabled!' raise Exceptions::Forbidden, 'API token access disabled!'
end end
user = Token.check( user = Token.check(
@ -106,13 +111,15 @@ module ApplicationController::Authenticates
@_token_auth = token_string # remember for permission_check @_token_auth = token_string # remember for permission_check
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
authentication_errors.push("Can't find User for Token")
end end
# check oauth2 token based authentication # check oauth2 token based authentication
token = Doorkeeper::OAuth::Token.from_bearer_authorization(request) token = Doorkeeper::OAuth::Token.from_bearer_authorization(request)
if token if token
request.session_options[:skip] = true # do not send a session cookie 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) access_token = Doorkeeper::AccessToken.by_token(token)
raise Exceptions::NotAuthorized, 'Invalid token!' if !access_token raise Exceptions::NotAuthorized, 'Invalid token!' if !access_token
@ -128,9 +135,13 @@ module ApplicationController::Authenticates
user = User.find(access_token.resource_owner_id) user = User.find(access_token.resource_owner_id)
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user 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 end
false return false if authentication_errors.blank?
raise Exceptions::NotAuthorized, authentication_errors.join(', ')
end end
def authenticate_with_password def authenticate_with_password
@ -142,7 +153,7 @@ module ApplicationController::Authenticates
end end
def authentication_check_prerequesits(user, auth_type, auth_param) 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 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.") ActiveSupport::Deprecation.warn("Parameter ':permission' is deprecated. Use Pundit policy and `authorize!` instead.")
if !user.permissions?(auth_param[:permission]) if !user.permissions?(auth_param[:permission])
raise Exceptions::NotAuthorized, 'Not authorized (user)!' raise Exceptions::Forbidden, 'Not authorized (user)!'
end end
end end

View file

@ -11,7 +11,7 @@ module ApplicationController::Authorizes
def authorized?(record = policy_record, query = nil) def authorized?(record = policy_record, query = nil)
authorize!(record, query) authorize!(record, query)
true true
rescue Exceptions::NotAuthorized, Pundit::NotAuthorizedError rescue Exceptions::Forbidden, Pundit::NotAuthorizedError
false false
end end

View file

@ -11,6 +11,7 @@ module ApplicationController::HandlesErrors
rescue_from ArgumentError, with: :unprocessable_entity rescue_from ArgumentError, with: :unprocessable_entity
rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity rescue_from Exceptions::UnprocessableEntity, with: :unprocessable_entity
rescue_from Exceptions::NotAuthorized, with: :unauthorized rescue_from Exceptions::NotAuthorized, with: :unauthorized
rescue_from Exceptions::Forbidden, with: :forbidden
rescue_from Pundit::NotAuthorizedError, with: :pundit_not_authorized_error rescue_from Pundit::NotAuthorizedError, with: :pundit_not_authorized_error
end end
@ -40,13 +41,21 @@ module ApplicationController::HandlesErrors
http_log http_log
end 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) def pundit_not_authorized_error(e)
logger.info { e } logger.info { e }
# check if a special authorization_error should be shown in the result payload # 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" # which was raised in one of the policies. Fall back to a simple "Not authorized"
# error to hide actual cause for security reasons. # error to hide actual cause for security reasons.
exeption = e.policy.custom_exception || Exceptions::NotAuthorized.new exeption = e.policy&.custom_exception || Exceptions::Forbidden.new('Not authorized')
unauthorized(exeption) forbidden(exeption)
end end
private private
@ -79,10 +88,13 @@ module ApplicationController::HandlesErrors
data[:error_human] = 'Object already exists!' 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 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!" data[:error_human] = "Attribute '#{$1}' required!"
elsif e.message == 'Exceptions::NotAuthorized' elsif e.message == 'Exceptions::Forbidden'
data[:error] = 'Not authorized' data[:error] = 'Not authorized'
data[:error_human] = data[:error] 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] data[:error_human] = data[:error]
end end

View file

@ -43,7 +43,7 @@ module ApplicationController::HasUser
return if !user_real return if !user_real
# check if the user has admin rights # 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 # find user for execution based on the header
%i[id login email].each do |field| %i[id login email].each do |field|
@ -55,7 +55,7 @@ module ApplicationController::HasUser
end end
# no behalf of user found # 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 end
def search_attributes(field) def search_attributes(field)

View file

@ -75,7 +75,7 @@ class AttachmentsController < ApplicationController
valid_disposition = %w[inline attachment] valid_disposition = %w[inline attachment]
return disposition if valid_disposition.include?(disposition) 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
def verify_object_permissions def verify_object_permissions

View file

@ -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. # @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 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 def all
calendar_subscriptions = CalendarSubscriptions.new(current_user) calendar_subscriptions = CalendarSubscriptions.new(current_user)
ical = calendar_subscriptions.all 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. # @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 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 def object
calendar_subscriptions = CalendarSubscriptions.new(current_user) calendar_subscriptions = CalendarSubscriptions.new(current_user)
ical = calendar_subscriptions.generic(params[:object], params[:method]) ical = calendar_subscriptions.generic(params[:object], params[:method])

View file

@ -267,7 +267,7 @@ class ChannelsEmailController < ApplicationController
def check_online_service def check_online_service
return true if !Setting.get('system_online_service') return true if !Setting.get('system_online_service')
raise Exceptions::NotAuthorized raise Exceptions::Forbidden
end end
def check_access(id = nil) def check_access(id = nil)
@ -279,6 +279,6 @@ class ChannelsEmailController < ApplicationController
channel = Channel.find(id) channel = Channel.find(id)
return true if channel.preferences && !channel.preferences[:online_service_disable] return true if channel.preferences && !channel.preferences[:online_service_disable]
raise Exceptions::NotAuthorized raise Exceptions::Forbidden
end end
end end

View file

@ -149,7 +149,7 @@ class FormController < ApplicationController
def authorize!(*) def authorize!(*)
super super
rescue Pundit::NotAuthorizedError rescue Pundit::NotAuthorizedError
raise Exceptions::NotAuthorized raise Exceptions::Forbidden
end end
def token_gen(fingerprint) def token_gen(fingerprint)
@ -161,7 +161,7 @@ class FormController < ApplicationController
def token_valid?(token, fingerprint) def token_valid?(token, fingerprint)
if token.blank? if token.blank?
Rails.logger.info 'No token for form!' Rails.logger.info 'No token for form!'
raise Exceptions::NotAuthorized raise Exceptions::Forbidden
end end
begin begin
crypt = ActiveSupport::MessageEncryptor.new(Setting.get('application_secret')[0, 32]) 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 # 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 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) 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 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) 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 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) 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 false
end end
@ -222,6 +222,6 @@ class FormController < ApplicationController
return true if params[:fingerprint].present? && params[:fingerprint].length > 30 return true if params[:fingerprint].present? && params[:fingerprint].length > 30
Rails.logger.info 'No fingerprint given!' Rails.logger.info 'No fingerprint given!'
raise Exceptions::NotAuthorized raise Exceptions::Forbidden
end end
end end

View file

@ -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 # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/organizations/import_example
# #
# @response_message 200 File download. # @response_message 200 File download.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_example def import_example
send_data( send_data(
Organization.csv_example, 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' # @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 201 Import started.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_start def import_start
string = params[:data] string = params[:data]
if string.blank? && params[:file].present? if string.blank? && params[:file].present?

View file

@ -16,7 +16,7 @@ class SessionsController < ApplicationController
end end
def create_sso 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 user = begin
login = request.env['REMOTE_USER'] || login = request.env['REMOTE_USER'] ||
@ -150,7 +150,7 @@ class SessionsController < ApplicationController
def switch_back_to_user def switch_back_to_user
# check if it's a switch back # 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]) user = User.lookup(id: session[:switched_from_user_id])
if !user if !user

View file

@ -21,7 +21,7 @@ class SettingsController < ApplicationController
# POST /settings # POST /settings
def create def create
raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' raise Exceptions::Forbidden, 'Not authorized (feature not possible)'
end end
# PUT /settings/1 # PUT /settings/1
@ -84,7 +84,7 @@ class SettingsController < ApplicationController
# DELETE /settings/1 # DELETE /settings/1
def destroy def destroy
raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' raise Exceptions::Forbidden, 'Not authorized (feature not possible)'
end end
private private

View file

@ -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 # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/text_modules/import_example
# #
# @response_message 200 File download. # @response_message 200 File download.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_example def import_example
csv_string = TextModule.csv_example( csv_string = TextModule.csv_example(
col_sep: params[:col_sep] || ',', 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' # @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 201 Import started.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_start def import_start
string = params[:data] string = params[:data]
if string.blank? && params[:file].present? if string.blank? && params[:file].present?

View file

@ -159,7 +159,7 @@ class TicketArticlesController < ApplicationController
# check if requested ticket got merged # check if requested ticket got merged
if ticket.state.state_type.name != '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 end
ticket = article.ticket ticket = article.ticket
@ -173,7 +173,7 @@ class TicketArticlesController < ApplicationController
access = true access = true
end end
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 # find file
file = Store.find(params[:id]) 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 # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/ticket_articles/import_example
# #
# @response_message 200 File download. # @response_message 200 File download.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_example def import_example
csv_string = Ticket::Article.csv_example( csv_string = Ticket::Article.csv_example(
col_sep: ',', 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' # @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 201 Import started.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_start def import_start
if Setting.get('import_mode') != true if Setting.get('import_mode') != true
raise 'Only can import tickets if system is in import mode.' raise 'Only can import tickets if system is in import mode.'
@ -286,6 +286,6 @@ class TicketArticlesController < ApplicationController
valid_disposition = %w[inline attachment] valid_disposition = %w[inline attachment]
return disposition if valid_disposition.include?(disposition) 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
end end

View file

@ -624,7 +624,7 @@ class TicketsController < ApplicationController
# @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/tickets/import_example # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/tickets/import_example
# #
# @response_message 200 File download. # @response_message 200 File download.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_example def import_example
csv_string = Ticket.csv_example( csv_string = Ticket.csv_example(
col_sep: ',', 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' # @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 201 Import started.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_start def import_start
if Setting.get('import_mode') != true if Setting.get('import_mode') != true
raise 'Only can import tickets if system is in import mode.' raise 'Only can import tickets if system is in import mode.'

View file

@ -15,7 +15,7 @@ class UsersController < ApplicationController
# role 'Customer' only just the own User record will be returned. # role 'Customer' only just the own User record will be returned.
# #
# @response_message 200 [Array<User>] List of matching User records. # @response_message 200 [Array<User>] List of matching User records.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def index def index
offset = 0 offset = 0
per_page = 500 per_page = 500
@ -71,7 +71,7 @@ class UsersController < ApplicationController
# @parameter full [Bool] If set a Asset structure with all connected Assets gets returned. # @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 200 [User] User record matching the requested identifier.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def show def show
user = User.find(params[:id]) user = User.find(params[:id])
authorize!(user) authorize!(user)
@ -120,7 +120,7 @@ class UsersController < ApplicationController
# @parameter User(required,body) [User] The attribute value structure needed to update a User record. # @parameter User(required,body) [User] The attribute value structure needed to update a User record.
# #
# @response_message 200 [User] Updated User record. # @response_message 200 [User] Updated User record.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def update def update
user = User.find(params[:id]) user = User.find(params[:id])
authorize!(user) authorize!(user)
@ -169,7 +169,7 @@ class UsersController < ApplicationController
# @parameter id(required) [User] The identifier matching the requested User record. # @parameter id(required) [User] The identifier matching the requested User record.
# #
# @response_message 200 User successfully deleted. # @response_message 200 User successfully deleted.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def destroy def destroy
user = User.find(params[:id]) user = User.find(params[:id])
authorize!(user) authorize!(user)
@ -186,7 +186,7 @@ class UsersController < ApplicationController
# @parameter full [Bool] If set a Asset structure with all connected Assets gets returned. # @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 200 [User] User record matching the requested identifier.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def me def me
if response_expand? if response_expand?
@ -225,7 +225,7 @@ class UsersController < ApplicationController
# or false: [{:id => user.id, :label => "firstname lastname <email>", :value => "firstname lastname <email>"},...]. # or false: [{:id => user.id, :label => "firstname lastname <email>", :value => "firstname lastname <email>"},...].
# #
# @response_message 200 [Array<User>] A list of User records matching the search term. # @response_message 200 [Array<User>] A list of User records matching the search term.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def search def search
per_page = params[:per_page] || params[:limit] || 100 per_page = params[:per_page] || params[:limit] || 100
per_page = per_page.to_i per_page = per_page.to_i
@ -328,7 +328,7 @@ class UsersController < ApplicationController
# @parameter id(required) [Integer] The identifier matching the requested User record. # @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 200 [History] The History records of the requested User record.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def history def history
# get user data # get user data
user = User.find(params[:id]) 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 # @example curl -u 'me@example.com:test' http://localhost:3000/api/v1/users/import_example
# #
# @response_message 200 File download. # @response_message 200 File download.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_example def import_example
send_data( send_data(
User.csv_example, 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' # @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 201 Import started.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def import_start def import_start
string = params[:data] string = params[:data]
if string.blank? && params[:file].present? 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. # @parameter User(required,body) [User] The attribute value structure needed to create a User record.
# #
# @response_message 200 [User] Created User record. # @response_message 200 [User] Created User record.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def create_internal def create_internal
# permission check # permission check
check_attributes_by_current_user_permission(params) 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. # @parameter User(required,body) [User] The attribute value structure needed to create a User record.
# #
# @response_message 200 [User] Created User record. # @response_message 200 [User] Created User record.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def create_signup def create_signup
# check if feature is enabled # check if feature is enabled
if !Setting.get('user_create_account') 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. # @parameter User(required,body) [User] The attribute value structure needed to create a User record.
# #
# @response_message 200 [User] Created User record. # @response_message 200 [User] Created User record.
# @response_message 401 Invalid session. # @response_message 403 Forbidden / Invalid session.
def create_admin def create_admin
if User.count > 2 # system and example users if User.count > 2 # system and example users
raise Exceptions::UnprocessableEntity, 'Administrator account already created' raise Exceptions::UnprocessableEntity, 'Administrator account already created'

View file

@ -16,7 +16,7 @@ module PunditPolicy
def user_required! def user_required!
return if user return if user
raise Exceptions::NotAuthorized, 'authentication failed' raise Exceptions::Forbidden, 'Authentication required'
end end
private private
@ -25,7 +25,7 @@ module PunditPolicy
if details if details
details = "Not authorized (#{details})!" details = "Not authorized (#{details})!"
end end
@custom_exception = Exceptions::NotAuthorized.new(details) @custom_exception = Exceptions::Forbidden.new(details)
false false
end end

View file

@ -14,7 +14,7 @@ class Auth
password_verified = PasswordHash.verified?(user.password, password) 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 password_verified
end end

View file

@ -2,6 +2,8 @@ module Exceptions
class NotAuthorized < StandardError; end class NotAuthorized < StandardError; end
class Forbidden < StandardError; end
class UnprocessableEntity < StandardError; end class UnprocessableEntity < StandardError; end
end end

View file

@ -15,18 +15,18 @@ class UserContext < Delegator
end end
def permissions!(permissions) def permissions!(permissions)
raise Exceptions::NotAuthorized, 'authentication failed' if !@user raise Exceptions::Forbidden, 'Authentication required' if !@user
raise Exceptions::NotAuthorized, 'Not authorized (user)!' if !@user.permissions?(permissions) raise Exceptions::Forbidden, 'Not authorized (user)!' if !@user.permissions?(permissions)
return if !@token return if !@token
return if @token.with_context(user: @user) { permissions?(permissions) } return if @token.with_context(user: @user) { permissions?(permissions) }
raise Exceptions::NotAuthorized, 'Not authorized (token)!' raise Exceptions::Forbidden, 'Not authorized (token)!'
end end
def permissions?(permissions) def permissions?(permissions)
permissions!(permissions) permissions!(permissions)
true true
rescue Exceptions::NotAuthorized rescue Exceptions::Forbidden
false false
end end
end end

View file

@ -10,7 +10,7 @@
<% end %> <% end %>
<% if !@traceback %> <% if !@traceback %>
<div class="error-image" style="background-image: url(/assets/error/error-1.svg)"></div> <div class="error-image" style="background-image: url(/assets/error/error-1.svg)"></div>
<p>Sorry, but you're not allowed to access this page. If you're registered please log in and refresh this page.</p> <p>Sorry, but your authentication failed. Double check your credentials and try again.</p>
<% else %> <% else %>
<div><%= @exception.message %></div> <div><%= @exception.message %></div>
<% if @exception.backtrace %> <% if @exception.backtrace %>
@ -21,4 +21,4 @@
<% end %> <% end %>
<% end %> <% end %>
</body> </body>
</html> </html>

24
public/403.html Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html class="dark">
<meta charset="utf-8">
<title>403: Forbidden</title>
<link rel="stylesheet" href="/assets/error/style.css">
<body <% if @traceback %>class="error-message"<% end %>>
<h1>403: Forbidden</h1>
<% if @message.present? %>
<div><%= @message %></div>
<% end %>
<% if !@traceback %>
<div class="error-image" style="background-image: url(/assets/error/error-1.svg)"></div>
<p>Sorry, but you're not allowed to access this page. If you're registered please log in and refresh this page.</p>
<% else %>
<div><%= @exception.message %></div>
<% if @exception.backtrace %>
<pre><code>
<% @exception.backtrace.each {|row| %>
<%= row %>
<% } %></code></pre>
<% end %>
<% end %>
</body>
</html>

View file

@ -251,7 +251,10 @@ $(function() {
_this._config = data _this._config = data
}).fail(function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status == 401) { 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 { else {
_this.log('error', 'Faild to load form config!') _this.log('error', 'Faild to load form config!')

View file

@ -137,7 +137,7 @@ RSpec.describe 'Api Auth On Behalf Of', type: :request do
} }
authenticated_as(admin, on_behalf_of: 99_449_494_949) authenticated_as(admin, on_behalf_of: 99_449_494_949)
post '/api/v1/tickets', params: params, as: :json 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(@response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq("No such user '99449494949'") 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) authenticated_as(customer, on_behalf_of: admin.email)
post '/api/v1/tickets', params: params, as: :json 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(@response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) 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'!") expect(json_response['error']).to eq("Current user has no permission to use 'X-On-Behalf-Of'!")

View file

@ -30,7 +30,7 @@ RSpec.describe 'Api Auth', type: :request do
Setting.set('api_password_access', false) Setting.set('api_password_access', false)
authenticated_as(admin) authenticated_as(admin)
get '/api/v1/sessions', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API password access disabled!') 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) Setting.set('api_password_access', false)
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/tickets', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API password access disabled!') 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) Setting.set('api_password_access', false)
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/tickets', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API password access disabled!') 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) Setting.set('api_token_access', false)
get '/api/v1/sessions', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API token access disabled!') expect(json_response['error']).to eq('API token access disabled!')
@ -124,7 +124,7 @@ RSpec.describe 'Api Auth', type: :request do
admin_token.save! admin_token.save!
get '/api/v1/sessions', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (token)!') expect(json_response['error']).to eq('Not authorized (token)!')
@ -132,7 +132,7 @@ RSpec.describe 'Api Auth', type: :request do
admin_token.save! admin_token.save!
get '/api/v1/sessions', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (token)!') 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 expect(json_response).to be_truthy
get '/api/v1/roles', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (token)!') 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) Setting.set('api_token_access', false)
get '/api/v1/tickets', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API token access disabled!') 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)}" name = "some org name #{rand(999_999_999)}"
post '/api/v1/organizations', params: { name: name }, as: :json post '/api/v1/organizations', params: { name: name }, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
@ -293,7 +293,7 @@ RSpec.describe 'Api Auth', type: :request do
Setting.set('api_token_access', false) Setting.set('api_token_access', false)
get '/api/v1/tickets', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API token access disabled!') 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)}" name = "some org name #{rand(999_999_999)}"
post '/api/v1/organizations', params: { name: name }, as: :json post '/api/v1/organizations', params: { name: name }, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
it 'does token auth - invalid user - admin', last_admin_check: false do 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) Setting.set('api_token_access', false)
get '/api/v1/sessions', params: {}, as: :json 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(response.header).not_to be_key('Access-Control-Allow-Origin')
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('API token access disabled!') expect(json_response['error']).to eq('API token access disabled!')

View file

@ -10,22 +10,22 @@ RSpec.describe 'Calendars', type: :request do
it 'does calendar index with nobody' do it 'does calendar index with nobody' do
get '/api/v1/calendars', as: :json 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).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 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).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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
it 'does calendar index with admin' do it 'does calendar index with admin' do

View file

@ -104,10 +104,11 @@ RSpec.describe 'Error handling', type: :request do
context 'request is not authenticated' do context 'request is not authenticated' do
before do before do
authenticated_as(create(:agent), password: 'wrongpw')
get '/api/v1/organizations', as: as get '/api/v1/organizations', as: as
end end
let(:message) { 'authentication failed' } let(:message) { 'Invalid BasicAuth credentials' }
let(:http_status) { :unauthorized } let(:http_status) { :unauthorized }
context 'requesting JSON' do context 'requesting JSON' do
@ -122,6 +123,27 @@ RSpec.describe 'Error handling', type: :request do
end end
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 context 'exception is raised' do
before do before do
@ -148,8 +170,8 @@ RSpec.describe 'Error handling', type: :request do
end end
end end
shared_examples 'handles exception' do |exception, http_status, title, headline| shared_examples 'handles exception' do |exception, http_status, title, headline, message = 'some error message'|
include_examples 'exception check', 'some error message', exception, http_status, title, headline include_examples 'exception check', message, exception, http_status, title, headline
end end
shared_examples 'masks exception' do |exception, http_status, title, headline| shared_examples 'masks exception' do |exception, http_status, title, headline|
@ -157,6 +179,8 @@ RSpec.describe 'Error handling', type: :request do
end end
include_examples 'handles exception', Exceptions::NotAuthorized, :unauthorized, '401: Unauthorized', '401: Unauthorized' 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', 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 '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.' include_examples 'masks exception', ArgumentError, :unprocessable_entity, '422: Unprocessable Entity', '422: The change you wanted was rejected.'

View file

@ -5,38 +5,38 @@ RSpec.describe 'External Credentials', type: :request do
context 'without authentication' do context 'without authentication' do
describe '#index' do describe '#index' do
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/api/v1/external_credentials', as: :json get '/api/v1/external_credentials', as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(json_response).to include('error' => 'authentication failed') expect(json_response).to include('error' => 'Authentication required')
end end
end end
describe '#app_verify' do describe '#app_verify' do
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
post '/api/v1/external_credentials/facebook/app_verify', as: :json 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' => 'authentication failed') expect(json_response).to include('error' => 'Authentication required')
end end
end end
describe '#link_account' do describe '#link_account' do
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/api/v1/external_credentials/facebook/link_account', as: :json get '/api/v1/external_credentials/facebook/link_account', as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(json_response).to include('error' => 'authentication failed') expect(json_response).to include('error' => 'Authentication required')
end end
end end
describe '#callback' do describe '#callback' do
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/api/v1/external_credentials/facebook/callback', as: :json get '/api/v1/external_credentials/facebook/callback', as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(json_response).to include('error' => 'authentication failed') expect(json_response).to include('error' => 'Authentication required')
end end
end end
end end
@ -72,9 +72,9 @@ RSpec.describe 'External Credentials', type: :request do
context 'when permission for Facebook channel is deactivated' do context 'when permission for Facebook channel is deactivated' do
before { Permission.find_by(name: 'admin.channel_facebook').update(active: false) } 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 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)!') expect(json_response).to include('error' => 'Not authorized (user)!')
end end
end end
@ -207,7 +207,7 @@ RSpec.describe 'External Credentials', type: :request do
before { Permission.find_by(name: 'admin.channel_twitter').update(active: false) } before { Permission.find_by(name: 'admin.channel_twitter').update(active: false) }
include_examples 'for failure cases' do include_examples 'for failure cases' do
let(:status) { :unauthorized } let(:status) { :forbidden }
let(:error_message) { 'Not authorized (user)!' } let(:error_message) { 'Not authorized (user)!' }
end end
end end

View file

@ -11,7 +11,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do
it 'does get config call' do it 'does get config call' do
post '/api/v1/form_config', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
end end
@ -19,7 +19,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do
it 'does get config call' do it 'does get config call' do
Setting.set('form_ticket_create', true) Setting.set('form_ticket_create', true)
post '/api/v1/form_config', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') 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 post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: 'invalid' }, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
expect(json_response).to be_a_kind_of(Hash) 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 post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token }, as: :json
expect(response).to have_http_status(:ok) 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 post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: 'invalid' }, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
expect(json_response).to be_a_kind_of(Hash) 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 post '/api/v1/form_submit', params: { fingerprint: fingerprint, token: token }, as: :json
expect(response).to have_http_status(:ok) 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 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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to be_truthy 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 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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to be_truthy 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 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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to be_truthy expect(json_response['error']).to be_truthy
end end
@ -229,7 +229,7 @@ RSpec.describe 'Form', type: :request, searchindex: true do
post '/api/v1/form_submit', params: params, as: :json 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 end
end end

View file

@ -612,7 +612,7 @@ RSpec.describe 'Integration CTI', type: :request do
expect(log.duration_talking_time).to be_nil expect(log.duration_talking_time).to be_nil
get '/api/v1/cti/log' get '/api/v1/cti/log'
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
# get caller list # get caller list
authenticated_as(agent) authenticated_as(agent)

View file

@ -38,7 +38,7 @@ RSpec.describe 'Idoit', type: :request do
} }
authenticated_as(agent) authenticated_as(agent)
post '/api/v1/integration/idoit/verify', params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank expect(json_response).not_to be_blank
expect(json_response['error']).to eq('Not authorized (user)!') expect(json_response['error']).to eq('Not authorized (user)!')

View file

@ -48,7 +48,7 @@ RSpec.describe 'Monitoring', type: :request do
# health_check # health_check
get '/api/v1/monitoring/health_check', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['healthy']).to be_falsey expect(json_response['healthy']).to be_falsey
@ -56,7 +56,7 @@ RSpec.describe 'Monitoring', type: :request do
# status # status
get '/api/v1/monitoring/status', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['agents']).to be_falsey expect(json_response['agents']).to be_falsey
@ -67,11 +67,11 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey expect(json_response['token']).to be_falsey
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
@ -79,7 +79,7 @@ RSpec.describe 'Monitoring', type: :request do
# health_check # health_check
get '/api/v1/monitoring/health_check?token=abc', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['healthy']).to be_falsey expect(json_response['healthy']).to be_falsey
@ -87,7 +87,7 @@ RSpec.describe 'Monitoring', type: :request do
# status # status
get '/api/v1/monitoring/status?token=abc', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['agents']).to be_falsey expect(json_response['agents']).to be_falsey
@ -98,11 +98,11 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: { token: 'abc' }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey expect(json_response['token']).to be_falsey
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
@ -221,11 +221,11 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: { token: token }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey expect(json_response['token']).to be_falsey
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
@ -267,7 +267,7 @@ RSpec.describe 'Monitoring', type: :request do
# health_check # health_check
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/monitoring/health_check', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['healthy']).to be_falsey expect(json_response['healthy']).to be_falsey
@ -275,7 +275,7 @@ RSpec.describe 'Monitoring', type: :request do
# status # status
get '/api/v1/monitoring/status', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['agents']).to be_falsey expect(json_response['agents']).to be_falsey
@ -286,7 +286,7 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: { token: token }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey expect(json_response['token']).to be_falsey
@ -303,7 +303,7 @@ RSpec.describe 'Monitoring', type: :request do
# health_check # health_check
authenticated_as(admin) authenticated_as(admin)
get '/api/v1/monitoring/health_check', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['healthy']).to be_falsey expect(json_response['healthy']).to be_falsey
@ -311,7 +311,7 @@ RSpec.describe 'Monitoring', type: :request do
# status # status
get '/api/v1/monitoring/status', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['agents']).to be_falsey expect(json_response['agents']).to be_falsey
@ -322,7 +322,7 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: { token: token }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey expect(json_response['token']).to be_falsey
@ -360,11 +360,11 @@ RSpec.describe 'Monitoring', type: :request do
# token # token
post '/api/v1/monitoring/token', params: { token: token }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['token']).to be_falsey 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.active = true
permission.save! permission.save!

View file

@ -512,7 +512,7 @@ RSpec.describe 'Integration Placetel', type: :request do
# get caller list # get caller list
get '/api/v1/cti/log' get '/api/v1/cti/log'
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/cti/log', as: :json get '/api/v1/cti/log', as: :json

View file

@ -442,7 +442,7 @@ RSpec.describe 'Integration Sipgate', type: :request do
# get caller list # get caller list
get '/api/v1/cti/log' get '/api/v1/cti/log'
expect(@response).to have_http_status(:unauthorized) expect(@response).to have_http_status(:forbidden)
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/cti/log', as: :json get '/api/v1/cti/log', as: :json

View file

@ -55,6 +55,6 @@ RSpec.describe 'KnowledgeBase loading initial data', type: :request, searchindex
end end
describe 'for guests without authorization' do describe 'for guests without authorization' do
it { expect(response).to have_http_status(:unauthorized) } it { expect(response).to have_http_status(:forbidden) }
end end
end end

View file

@ -67,17 +67,17 @@ RSpec.describe 'KnowledgeBase translation update', type: :request, authenticated
describe 'as reader' do describe 'as reader' do
let(:user_identifier) { :agent } let(:user_identifier) { :agent }
it { expect(response).to have_http_status(:unauthorized) } it { expect(response).to have_http_status(:forbidden) }
end end
describe 'as non-KB user' do describe 'as non-KB user' do
let(:user_identifier) { :customer } let(:user_identifier) { :customer }
it { expect(response).to have_http_status(:unauthorized) } it { expect(response).to have_http_status(:forbidden) }
end end
describe 'as a guest' do describe 'as a guest' do
it { expect(response).to have_http_status(:unauthorized) } it { expect(response).to have_http_status(:forbidden) }
end end
end end
end end

View file

@ -152,7 +152,7 @@ RSpec.describe 'Organization', type: :request, searchindex: true do
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json 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
it 'does index with customer2' do 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']) expect('Rest Org #1').to eq(json_response['name'])
get "/api/v1/organizations/#{organization2.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['name']).to be_nil expect(json_response['name']).to be_nil
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json 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
it 'does organization search sortable' do 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 it 'does csv example - customer no access' do
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/organizations/import_example', params: {}, as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
end end

View file

@ -37,7 +37,7 @@ RSpec.describe 'Overviews', type: :request do
post '/api/v1/overviews', params: params, as: :json post '/api/v1/overviews', params: params, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:unauthorized)
expect(json_response).to be_a_kind_of(Hash) 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 end
it 'does create overviews' do it 'does create overviews' do

View file

@ -16,11 +16,11 @@ RSpec.describe 'Packages', type: :request do
it 'does packages index with nobody' do it 'does packages index with nobody' do
get '/api/v1/packages', as: :json 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).to be_a_kind_of(Hash)
expect(json_response['packages']).to be_falsey expect(json_response['packages']).to be_falsey
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
it 'does packages index with admin' do 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(response).to have_http_status(:unauthorized)
expect(json_response).to be_a_kind_of(Hash) 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 end
it 'does packages index with inactive admin' do 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(response).to have_http_status(:unauthorized)
expect(json_response).to be_a_kind_of(Hash) 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 end
it 'does packages index with agent' do it 'does packages index with agent' do
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/packages', as: :json 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).to be_a_kind_of(Hash)
expect(json_response['packages']).to be_falsey expect(json_response['packages']).to be_falsey
expect(json_response['error']).to eq('Not authorized (user)!') expect(json_response['error']).to eq('Not authorized (user)!')
@ -66,7 +66,7 @@ RSpec.describe 'Packages', type: :request do
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/packages', as: :json 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).to be_a_kind_of(Hash)
expect(json_response['packages']).to be_falsey expect(json_response['packages']).to be_falsey
expect(json_response['error']).to eq('Not authorized (user)!') expect(json_response['error']).to eq('Not authorized (user)!')

View file

@ -98,22 +98,22 @@ RSpec.describe 'Search', type: :request, searchindex: true do
} }
post '/api/v1/search/ticket', params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank 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 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).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank 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 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).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank expect(json_response).not_to be_blank
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
it 'does settings index with admin' do it 'does settings index with admin' do

View file

@ -47,7 +47,7 @@ RSpec.describe 'Sessions endpoints', type: :request do
it 'returns a new user-session response' do it 'returns a new user-session response' do
get '/auth/sso', as: :json, headers: headers get '/auth/sso', as: :json, headers: headers
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
end end
@ -86,7 +86,7 @@ RSpec.describe 'Sessions endpoints', type: :request do
end end
context 'with valid user login' do context 'with valid user login' do
let(:user) { User.last } let(:user) { create(:agent) }
let(:login) { user.login } let(:login) { user.login }
context 'in Maintenance Mode' do context 'in Maintenance Mode' do
@ -95,10 +95,10 @@ RSpec.describe 'Sessions endpoints', type: :request do
context 'in "REMOTE_USER" request env var' do context 'in "REMOTE_USER" request env var' do
let(:env) { { 'REMOTE_USER' => login } } let(:env) { { 'REMOTE_USER' => login } }
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/auth/sso', as: :json, env: env 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!') expect(json_response).to include('error' => 'Maintenance mode enabled!')
end end
end end
@ -106,10 +106,10 @@ RSpec.describe 'Sessions endpoints', type: :request do
context 'in "HTTP_REMOTE_USER" request env var' do context 'in "HTTP_REMOTE_USER" request env var' do
let(:env) { { 'HTTP_REMOTE_USER' => login } } let(:env) { { 'HTTP_REMOTE_USER' => login } }
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/auth/sso', as: :json, env: env 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!') expect(json_response).to include('error' => 'Maintenance mode enabled!')
end end
end end
@ -117,10 +117,10 @@ RSpec.describe 'Sessions endpoints', type: :request do
context 'in "X-Forwarded-User" request header' do context 'in "X-Forwarded-User" request header' do
let(:headers) { { 'X-Forwarded-User' => login } } let(:headers) { { 'X-Forwarded-User' => login } }
it 'returns 401 unauthorized' do it 'returns 403 Forbidden' do
get '/auth/sso', as: :json, headers: headers 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!') expect(json_response).to include('error' => 'Maintenance mode enabled!')
end end
end end

View file

@ -24,15 +24,15 @@ RSpec.describe 'Settings', type: :request do
# index # index
get '/api/v1/settings', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['settings']).to be_falsey expect(json_response['settings']).to be_falsey
# show # show
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
get "/api/v1/settings/#{setting.id}", params: {}, as: :json 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('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
it 'does settings index with admin' do it 'does settings index with admin' do
@ -108,7 +108,7 @@ RSpec.describe 'Settings', type: :request do
# delete # delete
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
delete "/api/v1/settings/#{setting.id}", params: {}, as: :json 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)') expect(json_response['error']).to eq('Not authorized (feature not possible)')
end end
@ -136,7 +136,7 @@ RSpec.describe 'Settings', type: :request do
# show # show
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
get "/api/v1/settings/#{setting.id}", params: {}, as: :json 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"])!') expect(json_response['error']).to eq('Not authorized (required ["admin.branding"])!')
setting = Setting.find_by(name: 'api_token_access') 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 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"])!') expect(json_response['error']).to eq('Not authorized (required ["admin.branding"])!')
# update # update
@ -180,7 +180,7 @@ RSpec.describe 'Settings', type: :request do
# delete # delete
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
delete "/api/v1/settings/#{setting.id}", params: {}, as: :json 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)') expect(json_response['error']).to eq('Not authorized (feature not possible)')
end end
@ -189,7 +189,7 @@ RSpec.describe 'Settings', type: :request do
# index # index
authenticated_as(agent) authenticated_as(agent)
get '/api/v1/settings', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['settings']).to be_falsey expect(json_response['settings']).to be_falsey
expect(json_response['error']).to eq('Not authorized (user)!') expect(json_response['error']).to eq('Not authorized (user)!')
@ -197,7 +197,7 @@ RSpec.describe 'Settings', type: :request do
# show # show
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
get "/api/v1/settings/#{setting.id}", params: {}, as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
end end
@ -206,7 +206,7 @@ RSpec.describe 'Settings', type: :request do
# index # index
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/settings', params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['settings']).to be_falsey expect(json_response['settings']).to be_falsey
expect(json_response['error']).to eq('Not authorized (user)!') expect(json_response['error']).to eq('Not authorized (user)!')
@ -214,13 +214,13 @@ RSpec.describe 'Settings', type: :request do
# show # show
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
get "/api/v1/settings/#{setting.id}", params: {}, as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
# delete # delete
setting = Setting.find_by(name: 'product_name') setting = Setting.find_by(name: 'product_name')
delete "/api/v1/settings/#{setting.id}", params: {}, as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
end end
end end

View file

@ -10,10 +10,10 @@ RSpec.describe 'SLAs', type: :request do
it 'does index sla with nobody' do it 'does index sla with nobody' do
get '/api/v1/slas', as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
it 'does index sla with admin' do it 'does index sla with admin' do

View file

@ -18,7 +18,7 @@ RSpec.describe 'Text Module', type: :request do
it 'does csv example - customer no access' do it 'does csv example - customer no access' do
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/text_modules/import_example', as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
end end

View file

@ -33,8 +33,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do
authenticated_as(agent) authenticated_as(agent)
get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {} get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {}
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(@response.body).to match(/401: Unauthorized/) expect(@response.body).to match(/403: Forbidden/)
ticket2 = create(:ticket, group: group) ticket2 = create(:ticket, group: group)
ticket1.merge_to( ticket1.merge_to(
@ -49,8 +49,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do
authenticated_as(agent) authenticated_as(agent)
get "/api/v1/ticket_attachment/#{ticket2.id}/#{article2.id}/#{store1.id}", params: {} get "/api/v1/ticket_attachment/#{ticket2.id}/#{article2.id}/#{store1.id}", params: {}
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(@response.body).to match(/401: Unauthorized/) expect(@response.body).to match(/403: Forbidden/)
# allow access via merged ticket id also # allow access via merged ticket id also
authenticated_as(agent) authenticated_as(agent)
@ -60,8 +60,8 @@ RSpec.describe 'Ticket Article Attachments', type: :request do
authenticated_as(agent) authenticated_as(agent)
get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {} get "/api/v1/ticket_attachment/#{ticket1.id}/#{article2.id}/#{store1.id}", params: {}
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(@response.body).to match(/401: Unauthorized/) expect(@response.body).to match(/403: Forbidden/)
end end

View file

@ -262,12 +262,12 @@ AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
expect(ticket.articles[4].attachments.count).to eq(0) expect(ticket.articles[4].attachments.count).to eq(0)
get "/api/v1/ticket_articles/#{article.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
put "/api/v1/ticket_articles/#{article.id}", params: { internal: false }, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')

View file

@ -84,7 +84,7 @@ RSpec.describe 'Ticket', type: :request do
} }
authenticated_as(agent_change_only) authenticated_as(agent_change_only)
post '/api/v1/tickets', params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
end end
@ -772,7 +772,7 @@ RSpec.describe 'Ticket', type: :request do
) )
authenticated_as(agent) authenticated_as(agent)
get "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') 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', title: 'ticket with wrong ticket id - 2',
} }
put "/api/v1/tickets/#{ticket.id}", params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
end end
@ -923,12 +923,12 @@ RSpec.describe 'Ticket', type: :request do
expect(json_response['internal']).to eq(false) expect(json_response['internal']).to eq(false)
delete "/api/v1/ticket_articles/#{json_response['id']}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (communication articles cannot be deleted)!') expect(json_response['error']).to eq('Not authorized (communication articles cannot be deleted)!')
delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (admin permission required)!') expect(json_response['error']).to eq('Not authorized (admin permission required)!')
end 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) 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 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 delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
@ -1177,7 +1177,7 @@ RSpec.describe 'Ticket', type: :request do
) )
authenticated_as(customer) authenticated_as(customer)
get "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') 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', title: 'ticket with wrong ticket id - 2',
} }
put "/api/v1/tickets/#{ticket.id}", params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
end end
@ -1266,7 +1266,7 @@ RSpec.describe 'Ticket', type: :request do
expect(json_response['tickets_count']).to eq(1) expect(json_response['tickets_count']).to eq(1)
delete "/api/v1/ticket_articles/#{article_json_response['id']}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (agent permission required)!') 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) 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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (agent permission required)!') expect(json_response['error']).to eq('Not authorized (agent permission required)!')
@ -1321,12 +1321,12 @@ RSpec.describe 'Ticket', type: :request do
subject: 'new subject', subject: 'new subject',
} }
put "/api/v1/ticket_articles/#{json_response['id']}", params: params, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (ticket.agent or admin permission required)!') expect(json_response['error']).to eq('Not authorized (ticket.agent or admin permission required)!')
delete "/api/v1/tickets/#{ticket.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized (admin permission required)!') expect(json_response['error']).to eq('Not authorized (admin permission required)!')
end end
@ -1826,7 +1826,7 @@ RSpec.describe 'Ticket', type: :request do
authenticated_as(customer) authenticated_as(customer)
get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json 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) authenticated_as(agent)
get "/api/v1/ticket_split?ticket_id=#{ticket.id}&article_id=#{article.id}&form_id=new_form_id123", params: {}, as: :json 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) authenticated_as(customer)
put "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json 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) authenticated_as(agent)
put "/api/v1/ticket_merge/#{ticket2.id}/#{ticket1.id}", params: {}, as: :json 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!') expect(json_response['message']).to eq('No such master ticket number!')
put "/api/v1/ticket_merge/#{ticket3.id}/#{ticket1.number}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
expect(json_response['error_human']).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 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).to be_a_kind_of(Hash)
expect(json_response['error']).to eq('Not authorized') expect(json_response['error']).to eq('Not authorized')
expect(json_response['error_human']).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) authenticated_as(customer)
get "/api/v1/ticket_history/#{ticket1.id}", params: {}, as: :json 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 end
it 'does ticket related' do it 'does ticket related' do
@ -2127,7 +2127,7 @@ RSpec.describe 'Ticket', type: :request do
authenticated_as(customer) authenticated_as(customer)
get "/api/v1/ticket_related/#{ticket1.id}", params: {}, as: :json 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 end
it 'does ticket recent' do it 'does ticket recent' do
@ -2137,7 +2137,7 @@ RSpec.describe 'Ticket', type: :request do
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/ticket_recent', params: {}, as: :json get '/api/v1/ticket_recent', params: {}, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
end end

View file

@ -133,7 +133,7 @@ RSpec.describe 'User Organization', type: :request, searchindex: true do
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json 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
it 'does organization index with customer2' do 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']) expect('Rest Org').to eq(json_response['name'])
get "/api/v1/organizations/#{organization2.id}", params: {}, as: :json 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).to be_a_kind_of(Hash)
expect(json_response['name']).to be_nil expect(json_response['name']).to be_nil
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/organizations/search?query=#{CGI.escape('Zammad')}", params: {}, as: :json 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 end
end end

View file

@ -20,7 +20,7 @@ RSpec.describe 'User endpoint', type: :request do
let(:attributes) { attributes_params_for(:user) } let(:attributes) { attributes_params_for(:user) }
it 'responds unauthorized for customer' do it 'responds forbidden for customer' do
requester = create(:customer) requester = create(:customer)
authenticated_as(requester) authenticated_as(requester)
@ -30,7 +30,7 @@ RSpec.describe 'User endpoint', type: :request do
User.count User.count
} }
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
context 'privileged attributes' do 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)) expect(User.last.send(map_method_id)).to eq(send(map_method_id))
end 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) authenticated_as(admin_without_admin_user_permissions)
expect do expect do
@ -67,7 +67,7 @@ RSpec.describe 'User endpoint', type: :request do
User.count User.count
} }
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
it 'responds successful for agent but removes assignment' do 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) expect(User.last.roles).to eq(privileged)
end 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) authenticated_as(admin_without_admin_user_permissions)
expect do expect do
@ -140,7 +140,7 @@ RSpec.describe 'User endpoint', type: :request do
User.count User.count
} }
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
it 'responds successful for agent but removes assignment' do 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) expect(response).to have_http_status(:success)
end end
def unauthorized_update_request(requester:, requested:) def forbidden_update_request(requester:, requested:)
authenticated_as(requester) authenticated_as(requester)
expect do expect do
@ -211,7 +211,7 @@ RSpec.describe 'User endpoint', type: :request do
end end
} }
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
context 'request by admin.user' do context 'request by admin.user' do
@ -251,29 +251,29 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { admin_without_admin_user_permissions } let(:requester) { admin_without_admin_user_permissions }
it 'is unauthorized for same admin' do it 'is forbidden for same admin' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other admin' do it 'is forbidden for other admin' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized for agent' do it 'is forbidden for agent' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
end end
it 'is unauthorized for customer' do it 'is forbidden for customer' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:customer), requested: create(:customer),
) )
@ -284,22 +284,22 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { create(:agent) } let(:requester) { create(:agent) }
it 'is unauthorized for admin' do it 'is forbidden for admin' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized same agent' do it 'is forbidden same agent' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other agent' do it 'is forbidden for other agent' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
@ -317,40 +317,40 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { create(:customer) } let(:requester) { create(:customer) }
it 'is unauthorized for admin' do it 'is forbidden for admin' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized for agent' do it 'is forbidden for agent' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
end end
it 'is unauthorized for same customer' do it 'is forbidden for same customer' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other customer' do it 'is forbidden for other customer' do
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:customer), requested: create(:customer),
) )
end end
it 'is unauthorized for same organization' do it 'is forbidden for same organization' do
same_organization = create(:organization) same_organization = create(:organization)
requester.update!(organization: same_organization) requester.update!(organization: same_organization)
unauthorized_update_request( forbidden_update_request(
requester: requester, requester: requester,
requested: create(:customer, organization: same_organization), requested: create(:customer, organization: same_organization),
) )
@ -383,7 +383,7 @@ RSpec.describe 'User endpoint', type: :request do
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
end 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) authenticated_as(admin_without_admin_user_permissions)
expect do expect do
@ -392,7 +392,7 @@ RSpec.describe 'User endpoint', type: :request do
value_of_attribute value_of_attribute
} }
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
end end
@ -522,12 +522,12 @@ RSpec.describe 'User endpoint', type: :request do
expect(requested).not_to exist_in_database expect(requested).not_to exist_in_database
end end
def unauthorized_destroy_request(requester:, requested:) def forbidden_destroy_request(requester:, requested:)
authenticated_as(requester) authenticated_as(requester)
delete api_v1_delete_user_path(requested) 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 expect(requested).to exist_in_database
end end
@ -568,29 +568,29 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { admin_without_admin_user_permissions } let(:requester) { admin_without_admin_user_permissions }
it 'is unauthorized for same admin' do it 'is forbidden for same admin' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other admin' do it 'is forbidden for other admin' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized for agent' do it 'is forbidden for agent' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
end end
it 'is unauthorized for customer' do it 'is forbidden for customer' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:customer), requested: create(:customer),
) )
@ -601,29 +601,29 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { create(:agent) } let(:requester) { create(:agent) }
it 'is unauthorized for admin' do it 'is forbidden for admin' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized same agent' do it 'is forbidden same agent' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other agent' do it 'is forbidden for other agent' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
end end
it 'is unauthorized for customer' do it 'is forbidden for customer' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:customer), requested: create(:customer),
) )
@ -634,40 +634,40 @@ RSpec.describe 'User endpoint', type: :request do
let(:requester) { create(:customer) } let(:requester) { create(:customer) }
it 'is unauthorized for admin' do it 'is forbidden for admin' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:admin), requested: create(:admin),
) )
end end
it 'is unauthorized for agent' do it 'is forbidden for agent' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:agent), requested: create(:agent),
) )
end end
it 'is unauthorized for same customer' do it 'is forbidden for same customer' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: requester, requested: requester,
) )
end end
it 'is unauthorized for other customer' do it 'is forbidden for other customer' do
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:customer), requested: create(:customer),
) )
end end
it 'is unauthorized for same organization' do it 'is forbidden for same organization' do
same_organization = create(:organization) same_organization = create(:organization)
requester.update!(organization: same_organization) requester.update!(organization: same_organization)
unauthorized_destroy_request( forbidden_destroy_request(
requester: requester, requester: requester,
requested: create(:customer, organization: same_organization), requested: create(:customer, organization: same_organization),
) )

View file

@ -154,13 +154,13 @@ RSpec.describe 'User', type: :request do
# no user (because of no session) # no user (because of no session)
get '/api/v1/users', params: {}, headers: headers, as: :json get '/api/v1/users', params: {}, headers: headers, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
# me # me
get '/api/v1/users/me', params: {}, headers: headers, as: :json get '/api/v1/users/me', params: {}, headers: headers, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
expect(json_response['error']).to eq('authentication failed') expect(json_response['error']).to eq('Authentication required')
end end
context 'password security' do context 'password security' do
@ -182,25 +182,25 @@ RSpec.describe 'User', type: :request do
authenticated_as(nil, login: 'not_existing@example.com', password: 'adminpw') authenticated_as(nil, login: 'not_existing@example.com', password: 'adminpw')
get '/api/v1/users/me', params: {}, as: :json get '/api/v1/users/me', params: {}, as: :json
expect(response).to have_http_status(:unauthorized) 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 get '/api/v1/users', params: {}, as: :json
expect(response).to have_http_status(:unauthorized) 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 end
it 'does auth tests - username auth, wrong pw' do it 'does auth tests - username auth, wrong pw' do
authenticated_as(admin, password: 'not_existing') authenticated_as(admin, password: 'not_existing')
get '/api/v1/users', params: {}, as: :json get '/api/v1/users', params: {}, as: :json
expect(response).to have_http_status(:unauthorized) 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 end
it 'does auth tests - email auth, wrong pw' do it 'does auth tests - email auth, wrong pw' do
authenticated_as(nil, login: 'rest-admin@example.com', password: 'not_existing') authenticated_as(nil, login: 'rest-admin@example.com', password: 'not_existing')
get '/api/v1/users', params: {}, as: :json get '/api/v1/users', params: {}, as: :json
expect(response).to have_http_status(:unauthorized) 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 end
it 'does auth tests - username auth' do 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']) expect('rest-customer1@example.com').to eq(json_response['email'])
get "/api/v1/users/#{customer2.id}", params: {}, as: :json 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(Hash).to eq(json_response.class)
expect(json_response['error']).to be_truthy expect(json_response['error']).to be_truthy
@ -515,18 +515,18 @@ RSpec.describe 'User', type: :request do
role = Role.lookup(name: 'Admin') role = Role.lookup(name: 'Admin')
params = { firstname: 'Admin First', lastname: 'Admin Last', email: 'new_admin_by_customer1@example.com', role_ids: [ role.id ] } 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 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 # create user with agent role
role = Role.lookup(name: 'Agent') role = Role.lookup(name: 'Agent')
params = { firstname: 'Agent First', lastname: 'Agent Last', email: 'new_agent_by_customer1@example.com', role_ids: [ role.id ] } 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 post '/api/v1/users', params: params, as: :json
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/users/search?query=#{CGI.escape('First')}", params: {}, as: :json 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 end
it 'does user index with customer2' do 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']) expect('rest-customer2@example.com').to eq(json_response['email'])
get "/api/v1/users/#{customer.id}", params: {}, as: :json 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(Hash).to eq(json_response.class)
expect(json_response['error']).to be_truthy expect(json_response['error']).to be_truthy
# search # search
Scheduler.worker(true) Scheduler.worker(true)
get "/api/v1/users/search?query=#{CGI.escape('First')}", params: {}, as: :json 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 end
it 'does users show and response format (04.01)' do it 'does users show and response format (04.01)' do
@ -847,7 +847,7 @@ RSpec.describe 'User', type: :request do
authenticated_as(customer) authenticated_as(customer)
get '/api/v1/users/import_example', params: {}, as: :json 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)!') expect(json_response['error']).to eq('Not authorized (user)!')
end end
@ -1172,7 +1172,7 @@ RSpec.describe 'User', type: :request do
it 'does not accept requests from customers', authenticated_as: -> { create(:customer) } do it 'does not accept requests from customers', authenticated_as: -> { create(:customer) } do
make_request successful_params make_request successful_params
expect(response).to have_http_status(:unauthorized) expect(response).to have_http_status(:forbidden)
end end
it 'admins can give any role', authenticated_as: -> { create(:admin) } do it 'admins can give any role', authenticated_as: -> { create(:admin) } do