2018-09-14 05:42:05 +00:00
# frozen_string_literal: true
2016-05-02 11:29:17 +00:00
class JwtController < ApplicationController
2019-07-20 13:30:26 +00:00
skip_around_action :set_session_storage
2016-05-02 11:29:17 +00:00
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
2020-05-15 18:07:52 +00:00
# Add this before other actions, since we want to have the user or project
prepend_before_action :auth_user , :authenticate_project_or_user
2016-05-02 11:29:17 +00:00
2020-10-05 12:08:47 +00:00
feature_category :authentication_and_authorization
2022-04-19 15:08:32 +00:00
# https://gitlab.com/gitlab-org/gitlab/-/issues/357037
urgency :low
2020-10-05 12:08:47 +00:00
2016-05-02 12:32:16 +00:00
SERVICES = {
2020-12-03 18:10:10 +00:00
:: Auth :: ContainerRegistryAuthenticationService :: AUDIENCE = > :: Auth :: ContainerRegistryAuthenticationService ,
:: Auth :: DependencyProxyAuthenticationService :: AUDIENCE = > :: Auth :: DependencyProxyAuthenticationService
2017-02-21 23:32:18 +00:00
} . freeze
2016-05-02 12:32:16 +00:00
2016-05-02 11:29:17 +00:00
def auth
2016-05-02 12:32:16 +00:00
service = SERVICES [ params [ :service ] ]
2016-05-15 00:45:33 +00:00
return head :not_found unless service
2016-05-02 11:29:17 +00:00
2021-07-27 12:10:54 +00:00
result = service . new ( @authentication_result . project , auth_user , auth_params )
2017-06-21 13:48:12 +00:00
. execute ( authentication_abilities : @authentication_result . authentication_abilities )
2016-05-02 11:29:17 +00:00
2016-05-14 19:04:04 +00:00
render json : result , status : result [ :http_status ]
2016-05-02 11:29:17 +00:00
end
2016-05-02 12:32:16 +00:00
private
2016-05-02 11:29:17 +00:00
2016-05-13 21:23:02 +00:00
def authenticate_project_or_user
2019-04-15 13:05:55 +00:00
@authentication_result = Gitlab :: Auth :: Result . new ( nil , nil , :none , Gitlab :: Auth . read_only_authentication_abilities )
2016-09-20 15:07:34 +00:00
2016-05-13 21:23:02 +00:00
authenticate_with_http_basic do | login , password |
2018-04-03 21:34:56 +00:00
@authentication_result = Gitlab :: Auth . find_for_git_client ( login , password , project : nil , ip : request . ip )
2016-05-13 21:23:02 +00:00
2018-04-06 19:48:17 +00:00
if @authentication_result . failed?
2022-08-17 21:09:50 +00:00
log_authentication_failed ( login , @authentication_result )
2022-08-30 18:09:50 +00:00
render_access_denied
2017-05-31 13:55:12 +00:00
end
2016-05-13 21:23:02 +00:00
end
2020-12-16 09:10:26 +00:00
rescue Gitlab :: Auth :: MissingPersonalAccessTokenError
2022-08-30 18:09:50 +00:00
render_access_denied
2016-09-26 10:18:21 +00:00
end
2022-08-17 21:09:50 +00:00
def log_authentication_failed ( login , result )
log_info = {
message : 'JWT authentication failed' ,
http_user : login ,
remote_ip : request . ip ,
auth_service : params [ :service ] ,
'auth_result.type' : result . type ,
'auth_result.actor_type' : result . actor & . class
} . merge ( :: Gitlab :: ApplicationContext . current )
Gitlab :: AuthLogger . warn ( log_info )
end
2022-08-30 18:09:50 +00:00
def render_access_denied
help_page = help_page_url (
'user/profile/account/two_factor_authentication' ,
anchor : 'troubleshooting'
)
render (
json : { errors : [ {
code : 'UNAUTHORIZED' ,
message : format ( _ ( " HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url} " ) , help_page_url : help_page )
} ] } ,
status : :unauthorized
)
2016-05-13 21:23:02 +00:00
end
2016-05-02 12:32:16 +00:00
def auth_params
2018-07-13 17:45:07 +00:00
params . permit ( :service , :account , :client_id )
. merge ( additional_params )
end
def additional_params
2022-09-15 00:14:10 +00:00
{
scopes : scopes_param ,
deploy_token : @authentication_result . deploy_token ,
auth_type : @authentication_result . type
} . compact
2018-07-13 17:45:07 +00:00
end
# We have to parse scope here, because Docker Client does not send an array of scopes,
# but rather a flat list and we loose second scope when being processed by Rails:
# scope=scopeA&scope=scopeB
#
# This method makes to always return an array of scopes
def scopes_param
return unless params [ :scope ] . present?
Array ( Rack :: Utils . parse_query ( request . query_string ) [ 'scope' ] )
2016-05-02 11:29:17 +00:00
end
2020-05-11 15:09:37 +00:00
def auth_user
2020-05-15 18:07:52 +00:00
strong_memoize ( :auth_user ) do
2021-07-27 12:10:54 +00:00
@authentication_result . auth_user
2020-05-15 18:07:52 +00:00
end
2020-05-11 15:09:37 +00:00
end
2016-05-02 11:29:17 +00:00
end