Module: Gitlab::Auth::AuthFinders

Includes:
ActionController::HttpAuthentication::Basic, ActionController::HttpAuthentication::Token, RackLoadBalancingHelpers, Utils::StrongMemoize
Included in:
API::APIGuard::HelperMethods, ApplicationCable::Channel, ApplicationCable::Connection, RequestAuthenticator, GraphqlController
Defined in:
lib/gitlab/auth/auth_finders.rb

Constant Summary collapse

PRIVATE_TOKEN_HEADER =
'HTTP_PRIVATE_TOKEN'
PRIVATE_TOKEN_PARAM =
:private_token
JOB_TOKEN_HEADER =
'HTTP_JOB_TOKEN'
JOB_TOKEN_PARAM =
:job_token
DEPLOY_TOKEN_HEADER =
'HTTP_DEPLOY_TOKEN'
RUNNER_TOKEN_PARAM =
:token
RUNNER_JOB_TOKEN_PARAM =
:token
PARAM_TOKEN_KEYS =
[
  PRIVATE_TOKEN_PARAM,
  JOB_TOKEN_PARAM,
  RUNNER_JOB_TOKEN_PARAM
].map(&:to_s).freeze
HEADER_TOKEN_KEYS =
[
  PRIVATE_TOKEN_HEADER,
  JOB_TOKEN_HEADER,
  DEPLOY_TOKEN_HEADER
].freeze
MAX_JOB_TOKEN_SIZE_BYTES =
8192

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RackLoadBalancingHelpers

#load_balancer_stick_request

Instance Attribute Details

#current_tokenObject

Returns the value of attribute current_token.



62
63
64
# File 'lib/gitlab/auth/auth_finders.rb', line 62

def current_token
  @current_token
end

Class Method Details

.path_dependent_feed_token_regexObject



249
250
251
# File 'lib/gitlab/auth/auth_finders.rb', line 249

def self.path_dependent_feed_token_regex
  /\A(#{User::FEED_TOKEN_PREFIX}|#{User.prefix_for_feed_token})(\h{64})-(\d+)\z/
end

Instance Method Details

#authentication_token_present?Boolean

Returns:

  • (Boolean)


243
244
245
246
247
# File 'lib/gitlab/auth/auth_finders.rb', line 243

def authentication_token_present?
  PARAM_TOKEN_KEYS.intersection(current_request.params.keys).any? ||
    HEADER_TOKEN_KEYS.intersection(current_request.env.keys).any? ||
    parsed_oauth_token.present?
end

#cluster_agent_token_from_authorization_tokenObject



177
178
179
180
181
182
183
184
185
# File 'lib/gitlab/auth/auth_finders.rb', line 177

def cluster_agent_token_from_authorization_token
  return unless route_authentication_setting[:cluster_agent_token_allowed]

  self.current_token = current_request.headers[Gitlab::Kas::INTERNAL_API_AGENT_REQUEST_HEADER]

  return unless current_token.present?

  ::Clusters::AgentToken.active.find_by_token(current_token.to_s)
end

#deploy_token_from_requestObject

This returns a deploy token, not a user since a deploy token does not belong to a user.

deploy tokens are accepted with deploy token headers and basic auth headers



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/gitlab/auth/auth_finders.rb', line 161

def deploy_token_from_request
  return unless route_authentication_setting[:deploy_token_allowed]
  return unless Gitlab::ExternalAuthorization.allow_deploy_tokens_and_deploy_keys?

  self.current_token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token

  if has_basic_credentials?(current_request)
    _, self.current_token = user_name_and_password(current_request)
  end

  deploy_token = DeployToken.active.find_by_token(current_token.to_s)
  @current_authenticated_deploy_token = deploy_token # rubocop:disable Gitlab/ModuleWithInstanceVariables

  deploy_token
end

#find_job_from_job_tokenObject



196
197
198
199
200
201
202
203
204
205
# File 'lib/gitlab/auth/auth_finders.rb', line 196

def find_job_from_job_token
  return unless api_request?

  self.current_token = get_job_token_from_query_param_or_header
  return unless current_token

  validate_job_token_size!(current_token)

  find_valid_running_job_by_token!(current_token)
end

#find_runner_from_tokenObject



187
188
189
190
191
192
193
194
# File 'lib/gitlab/auth/auth_finders.rb', line 187

def find_runner_from_token
  return unless api_request?

  token = current_request.params[RUNNER_TOKEN_PARAM].presence
  return unless token

  ::Ci::Runner.find_by_token(token.to_s) || raise(UnauthorizedError)
end

#find_user_from_access_tokenObject



146
147
148
149
150
151
152
153
154
155
# File 'lib/gitlab/auth/auth_finders.rb', line 146

def find_user_from_access_token
  return unless access_token

  validate_and_save_access_token!

  ::PersonalAccessTokens::LastUsedService.new(access_token).execute

  user = access_token.user || raise(UnauthorizedError)
  resolve_composite_identity_user(user)
end

#find_user_from_basic_auth_passwordObject



103
104
105
106
107
108
109
110
# File 'lib/gitlab/auth/auth_finders.rb', line 103

def find_user_from_basic_auth_password
  return unless has_basic_credentials?(current_request)

  , password = user_name_and_password(current_request)
  return if ::Gitlab::Auth::CI_JOB_USER == 

  Gitlab::Auth.find_with_user_password(.to_s, password.to_s)
end

#find_user_from_bearer_tokenObject



90
91
92
# File 'lib/gitlab/auth/auth_finders.rb', line 90

def find_user_from_bearer_token
  find_user_from_job_bearer_token || find_user_from_access_token
end

#find_user_from_feed_token(request_format) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/gitlab/auth/auth_finders.rb', line 78

def find_user_from_feed_token(request_format)
  return unless valid_rss_format?(request_format)
  return if Gitlab::CurrentSettings.disable_feed_token

  # NOTE: feed_token was renamed from rss_token but both needs to be supported because
  #       users might have already added the feed to their RSS reader before the rename
  token = current_request.params[:feed_token].presence || current_request.params[:rss_token].presence
  return unless token

  find_feed_token_user(token) || raise(UnauthorizedError)
end

#find_user_from_job_tokenObject



94
95
96
97
98
99
100
101
# File 'lib/gitlab/auth/auth_finders.rb', line 94

def find_user_from_job_token
  return unless route_authentication_setting[:job_token_allowed]

  user = find_user_from_job_token_basic_auth if can_authenticate_job_token_basic_auth?
  return user if user

  find_user_from_job_token_query_params_or_header if can_authenticate_job_token_request?
end

#find_user_from_lfs_tokenObject



112
113
114
115
116
117
118
119
# File 'lib/gitlab/auth/auth_finders.rb', line 112

def find_user_from_lfs_token
  return unless has_basic_credentials?(current_request)

  , token = user_name_and_password(current_request)
  user = User.(.to_s)

  user if user && Gitlab::LfsToken.new(user, nil).token_valid?(token.to_s)
end

#find_user_from_personal_access_tokenObject



121
122
123
124
125
126
127
# File 'lib/gitlab/auth/auth_finders.rb', line 121

def find_user_from_personal_access_token
  return unless access_token

  validate_and_save_access_token!

  access_token&.user || raise(UnauthorizedError)
end

#find_user_from_static_object_token(request_format) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/gitlab/auth/auth_finders.rb', line 69

def find_user_from_static_object_token(request_format)
  return unless valid_static_objects_format?(request_format)

  token = current_request.params[:token].presence || current_request.headers['X-Gitlab-Static-Object-Token'].presence
  return unless token

  User.find_by_static_object_token(token.to_s) || raise(UnauthorizedError)
end

#find_user_from_wardenObject

Check the Rails session for valid authentication details



65
66
67
# File 'lib/gitlab/auth/auth_finders.rb', line 65

def find_user_from_warden
  current_request.env['warden']&.authenticate if verified_request?
end

#find_user_from_web_access_token(request_format, scopes: [:api]) ⇒ Object

We allow private access tokens with api scope to be used by web requests on RSS feeds or ICS files for backwards compatibility. It is also used by GraphQL/API requests. And to allow accessing /archive programatically as it was a big pain point for users gitlab.com/gitlab-org/gitlab/-/issues/28978. Used for release downloading as well



135
136
137
138
139
140
141
142
143
144
# File 'lib/gitlab/auth/auth_finders.rb', line 135

def find_user_from_web_access_token(request_format, scopes: [:api])
  return unless access_token && valid_web_access_format?(request_format)

  validate_and_save_access_token!(scopes: scopes)

  ::PersonalAccessTokens::LastUsedService.new(access_token).execute

  user = access_token.user || raise(UnauthorizedError)
  resolve_composite_identity_user(user)
end

#validate_and_save_access_token!(scopes: [], save_auth_context: true, reset_token: false) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/gitlab/auth/auth_finders.rb', line 207

def validate_and_save_access_token!(scopes: [], save_auth_context: true, reset_token: false)
  # return early if we've already authenticated via a job token
  return if @current_authenticated_job.present? # rubocop:disable Gitlab/ModuleWithInstanceVariables

  # return early if we've already authenticated via a deploy token
  return if @current_authenticated_deploy_token.present? # rubocop:disable Gitlab/ModuleWithInstanceVariables

  return unless access_token

  # Originally, we tried to use `reset` here to follow the rubocop rule introduced by
  # gitlab-org/gitlab-foss#60218, but this caused a NoMethodError for OAuth tokens,
  # leading to incident 18980 (see gitlab-com/gl-infra/production#18988).
  # We're using reload instead and disabling the rubocop rule to prevent similar incidents.
  access_token.reload if reset_token # rubocop:disable Cop/ActiveRecordAssociationReload

  case AccessTokenValidationService.new(access_token, request: request).validate(scopes: scopes)
  when AccessTokenValidationService::INSUFFICIENT_SCOPE
    save_auth_failure_in_application_context(access_token, :insufficient_scope, scopes) if save_auth_context
    raise InsufficientScopeError, scopes
  when AccessTokenValidationService::EXPIRED
    save_auth_failure_in_application_context(access_token, :token_expired, scopes) if save_auth_context
    raise ExpiredError
  when AccessTokenValidationService::REVOKED
    save_auth_failure_in_application_context(access_token, :token_revoked, scopes) if save_auth_context
    revoke_token_family(access_token)

    raise RevokedError
  when AccessTokenValidationService::IMPERSONATION_DISABLED
    save_auth_failure_in_application_context(access_token, :impersonation_disabled, scopes) if save_auth_context
    raise ImpersonationDisabled
  end

  save_current_token_in_env
  access_token
end