2026-01-02 13:41:09 +00:00
|
|
|
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2017-03-09 11:44:51 +00:00
|
|
|
module ApplicationController::Authenticates
|
|
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
2023-04-11 08:36:34 +00:00
|
|
|
def authentication_check(basic_auth_prompt: nil)
|
|
|
|
|
user = authentication_check_only
|
2017-03-09 11:44:51 +00:00
|
|
|
|
|
|
|
|
# check if basic_auth fallback is possible
|
2023-04-11 08:36:34 +00:00
|
|
|
if basic_auth_prompt && !user
|
2020-05-25 16:26:06 +00:00
|
|
|
request_http_basic_authentication
|
|
|
|
|
return false
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# return auth not ok
|
|
|
|
|
if !user
|
2021-11-15 15:58:19 +00:00
|
|
|
raise Exceptions::Forbidden, __('Authentication required')
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# return auth ok
|
|
|
|
|
true
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-11 08:36:34 +00:00
|
|
|
def authentication_check_only
|
2022-06-08 06:16:28 +00:00
|
|
|
if %w[test development].include?(Rails.env) && ENV['FAKE_SELENIUM_LOGIN_USER_ID'].present? && session[:user_id].blank?
|
2021-11-04 13:40:58 +00:00
|
|
|
session[:user_id] = ENV['FAKE_SELENIUM_LOGIN_USER_ID'].to_i
|
2022-03-10 14:33:05 +00:00
|
|
|
session[:user_device_updated_at] = Time.zone.now
|
2023-05-19 14:29:47 +00:00
|
|
|
session[:authentication_type] = 'password'
|
2021-11-04 13:40:58 +00:00
|
|
|
end
|
|
|
|
|
|
2021-07-16 13:44:10 +00:00
|
|
|
# logger.debug 'authentication_check'
|
|
|
|
|
# logger.debug params.inspect
|
|
|
|
|
# logger.debug session.inspect
|
|
|
|
|
# logger.debug cookies.inspect
|
2021-02-04 08:28:41 +00:00
|
|
|
authentication_errors = []
|
2017-03-09 11:44:51 +00:00
|
|
|
|
|
|
|
|
# already logged in, early exit
|
|
|
|
|
if session.id && session[:user_id]
|
2018-03-20 17:47:49 +00:00
|
|
|
logger.debug { 'session based auth check' }
|
2017-03-09 11:44:51 +00:00
|
|
|
user = User.lookup(id: session[:user_id])
|
2023-04-11 08:36:34 +00:00
|
|
|
return authentication_check_prerequesits(user, 'session') if user
|
2021-02-04 08:28:41 +00:00
|
|
|
|
|
|
|
|
authentication_errors.push("Can't find User with ID #{session[:user_id]} from Session")
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# check http basic based authentication
|
|
|
|
|
authenticate_with_http_basic do |username, password|
|
|
|
|
|
request.session_options[:skip] = true # do not send a session cookie
|
2018-03-20 17:47:49 +00:00
|
|
|
logger.debug { "http basic auth check '#{username}'" }
|
2017-03-09 11:44:51 +00:00
|
|
|
if Setting.get('api_password_access') == false
|
2021-02-04 08:28:41 +00:00
|
|
|
raise Exceptions::Forbidden, 'API password access disabled!'
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2025-03-04 11:27:46 +00:00
|
|
|
# Disable 2FA for iCal and calendar subscriptions
|
|
|
|
|
only_verify_password = %w[/ical /calendar_subscriptions].any? { |path| request.path.start_with?(path) }
|
|
|
|
|
auth = Auth.new(username, password, only_verify_password:)
|
2021-02-04 08:28:41 +00:00
|
|
|
|
2023-05-19 14:29:47 +00:00
|
|
|
begin
|
|
|
|
|
auth.valid!
|
|
|
|
|
return authentication_check_prerequesits(auth.user, 'basic_auth')
|
|
|
|
|
rescue Auth::Error::AuthenticationFailed
|
|
|
|
|
authentication_errors.push(__('Invalid BasicAuth credentials'))
|
|
|
|
|
rescue Auth::Error::TwoFactorRequired
|
|
|
|
|
authentication_errors.push(__('Two-factor authentication is not supported with HTTP BasicAuth.'))
|
|
|
|
|
end
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# check http token based authentication
|
|
|
|
|
authenticate_with_http_token do |token_string, _options|
|
2018-03-20 17:47:49 +00:00
|
|
|
logger.debug { "http token auth check '#{token_string}'" }
|
2017-03-09 11:44:51 +00:00
|
|
|
request.session_options[:skip] = true # do not send a session cookie
|
|
|
|
|
if Setting.get('api_token_access') == false
|
2021-02-04 08:28:41 +00:00
|
|
|
raise Exceptions::Forbidden, 'API token access disabled!'
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-03-09 11:44:51 +00:00
|
|
|
user = Token.check(
|
2018-12-19 17:31:51 +00:00
|
|
|
action: 'api',
|
2023-04-11 10:50:56 +00:00
|
|
|
token: token_string,
|
2017-03-09 11:44:51 +00:00
|
|
|
inactive_user: true,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if user
|
2023-04-11 10:50:56 +00:00
|
|
|
token = Token.find_by(token: token_string)
|
2017-03-09 11:44:51 +00:00
|
|
|
|
|
|
|
|
token.last_used_at = Time.zone.now
|
|
|
|
|
token.save!
|
|
|
|
|
|
2025-02-14 08:36:02 +00:00
|
|
|
raise Exceptions::NotAuthorized, __('Not authorized (token expired)!') if token.expired?
|
2020-03-19 09:39:51 +00:00
|
|
|
|
|
|
|
|
@_token = token # remember for Pundit authorization / permit!
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@_token_auth = token_string # remember for permission_check
|
2023-04-11 08:36:34 +00:00
|
|
|
return authentication_check_prerequesits(user, 'token_auth') if user
|
2021-02-04 08:28:41 +00:00
|
|
|
|
2021-11-15 15:58:19 +00:00
|
|
|
authentication_errors.push(__("Can't find User for Token"))
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# check oauth2 token based authentication
|
|
|
|
|
token = Doorkeeper::OAuth::Token.from_bearer_authorization(request)
|
|
|
|
|
if token
|
|
|
|
|
request.session_options[:skip] = true # do not send a session cookie
|
2021-02-04 08:28:41 +00:00
|
|
|
logger.debug { "OAuth2 token auth check '#{token}'" }
|
2017-03-09 11:44:51 +00:00
|
|
|
access_token = Doorkeeper::AccessToken.by_token(token)
|
|
|
|
|
|
2022-04-25 13:37:05 +00:00
|
|
|
raise Exceptions::NotAuthorized, __('The provided token is invalid.') if !access_token
|
2017-03-09 11:44:51 +00:00
|
|
|
|
|
|
|
|
# check expire
|
|
|
|
|
if access_token.expires_in && (access_token.created_at + access_token.expires_in) < Time.zone.now
|
2021-11-15 15:58:19 +00:00
|
|
|
raise Exceptions::NotAuthorized, __('OAuth2 token is expired!')
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# if access_token.scopes.empty?
|
|
|
|
|
# raise Exceptions::NotAuthorized, 'OAuth2 scope missing for token!'
|
|
|
|
|
# end
|
|
|
|
|
|
|
|
|
|
user = User.find(access_token.resource_owner_id)
|
2023-04-11 08:36:34 +00:00
|
|
|
return authentication_check_prerequesits(user, 'token_auth') if user
|
2021-02-04 08:28:41 +00:00
|
|
|
|
|
|
|
|
authentication_errors.push("Can't find User with ID #{access_token.resource_owner_id} for OAuth2 token")
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
2021-02-04 08:28:41 +00:00
|
|
|
return false if authentication_errors.blank?
|
|
|
|
|
|
|
|
|
|
raise Exceptions::NotAuthorized, authentication_errors.join(', ')
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
|
|
|
|
|
2023-04-11 08:36:34 +00:00
|
|
|
def authentication_check_prerequesits(user, auth_type)
|
2021-11-15 15:58:19 +00:00
|
|
|
raise Exceptions::Forbidden, __('Maintenance mode enabled!') if in_maintenance_mode?(user)
|
2020-08-14 12:52:20 +00:00
|
|
|
|
2023-05-19 14:29:47 +00:00
|
|
|
raise Exceptions::NotAuthorized, Auth::Error::AuthenticationFailed::MESSAGE if !user.active
|
2020-03-19 09:39:51 +00:00
|
|
|
|
2017-03-09 11:44:51 +00:00
|
|
|
current_user_set(user, auth_type)
|
|
|
|
|
user_device_log(user, auth_type)
|
2018-03-20 17:47:49 +00:00
|
|
|
logger.debug { "#{auth_type} for '#{user.login}'" }
|
2019-09-05 14:02:31 +00:00
|
|
|
user
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|
2020-08-14 12:52:20 +00:00
|
|
|
|
2023-03-19 20:43:36 +00:00
|
|
|
def authenticate_and_authorize!
|
|
|
|
|
authentication_check && authorize!
|
|
|
|
|
end
|
2017-03-09 11:44:51 +00:00
|
|
|
end
|