diff options
Diffstat (limited to 'lib/gitlab/auth')
-rw-r--r-- | lib/gitlab/auth/auth_finders.rb | 7 | ||||
-rw-r--r-- | lib/gitlab/auth/crowd/authentication.rb | 35 | ||||
-rw-r--r-- | lib/gitlab/auth/ldap/config.rb | 34 | ||||
-rw-r--r-- | lib/gitlab/auth/ldap/user.rb | 14 | ||||
-rw-r--r-- | lib/gitlab/auth/o_auth/provider.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/auth/o_auth/user.rb | 19 | ||||
-rw-r--r-- | lib/gitlab/auth/otp/fortinet.rb | 20 | ||||
-rw-r--r-- | lib/gitlab/auth/otp/session_enforcer.rb | 36 | ||||
-rw-r--r-- | lib/gitlab/auth/otp/strategies/base.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/auth/otp/strategies/forti_authenticator.rb | 7 | ||||
-rw-r--r-- | lib/gitlab/auth/otp/strategies/forti_token_cloud.rb | 72 | ||||
-rw-r--r-- | lib/gitlab/auth/request_authenticator.rb | 9 |
12 files changed, 230 insertions, 29 deletions
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb index f3975fe219a..caa881eeeab 100644 --- a/lib/gitlab/auth/auth_finders.rb +++ b/lib/gitlab/auth/auth_finders.rb @@ -46,6 +46,7 @@ module Gitlab 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 @@ -193,6 +194,10 @@ module Gitlab def access_token strong_memoize(:access_token) do + # The token can be a PAT or an OAuth (doorkeeper) token + # It is also possible that a PAT is encapsulated in a `Bearer` OAuth token + # (e.g. NPM client registry auth), this case will be properly handled + # by find_personal_access_token find_oauth_access_token || find_personal_access_token end end @@ -236,7 +241,7 @@ module Gitlab end def matches_personal_access_token_length?(token) - token.length == PersonalAccessToken::TOKEN_LENGTH + PersonalAccessToken::TOKEN_LENGTH_RANGE.include?(token.length) end # Check if the request is GET/HEAD, or if CSRF token is valid. diff --git a/lib/gitlab/auth/crowd/authentication.rb b/lib/gitlab/auth/crowd/authentication.rb new file mode 100644 index 00000000000..7f3e980034e --- /dev/null +++ b/lib/gitlab/auth/crowd/authentication.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Gitlab + module Auth + module Crowd + class Authentication < Gitlab::Auth::OAuth::Authentication + def login(login, password) + return unless Gitlab::Auth::OAuth::Provider.enabled?(@provider) + return unless login.present? && password.present? + + user_info = user_info_from_authentication(login, password) + return unless user_info&.key?(:user) + + Gitlab::Auth::OAuth::User.find_by_uid_and_provider(user_info[:user], provider) + end + + private + + def config + gitlab_crowd_config = Gitlab::Auth::OAuth::Provider.config_for(@provider) + raise "OmniAuth Crowd is not configured." unless gitlab_crowd_config && gitlab_crowd_config[:args] + + OmniAuth::Strategies::Crowd::Configuration.new( + gitlab_crowd_config[:args].symbolize_keys) + end + + def user_info_from_authentication(login, password) + validator = OmniAuth::Strategies::Crowd::CrowdValidator.new( + config, login, password, RequestContext.instance.client_ip, nil) + validator&.user_info&.symbolize_keys + end + end + end + end +end diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb index 88cc840c395..f5931a1d5eb 100644 --- a/lib/gitlab/auth/ldap/config.rb +++ b/lib/gitlab/auth/ldap/config.rb @@ -53,6 +53,10 @@ module Gitlab raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}") end + def self.encrypted_secrets + Settings.encrypted(Gitlab.config.ldap.secret_file) + end + def initialize(provider) if self.class.valid_provider?(provider) @provider = provider @@ -89,8 +93,8 @@ module Gitlab if has_auth? opts.merge!( - bind_dn: options['bind_dn'], - password: options['password'] + bind_dn: auth_username, + password: auth_password ) end @@ -155,7 +159,7 @@ module Gitlab end def has_auth? - options['password'] || options['bind_dn'] + auth_password || auth_username end def allow_username_or_email_login @@ -267,12 +271,32 @@ module Gitlab { auth: { method: :simple, - username: options['bind_dn'], - password: options['password'] + username: auth_username, + password: auth_password } } end + def secrets + @secrets ||= self.class.encrypted_secrets[@provider.delete_prefix('ldap').to_sym] + rescue => e + Gitlab::AppLogger.error "LDAP encrypted secrets are invalid: #{e.inspect}" + + nil + end + + def auth_password + return options['password'] if options['password'] + + secrets&.fetch(:password, nil)&.chomp + end + + def auth_username + return options['bind_dn'] if options['bind_dn'] + + secrets&.fetch(:bind_dn, nil)&.chomp + end + def omniauth_user_filter uid_filter = Net::LDAP::Filter.eq(uid, '%{username}') diff --git a/lib/gitlab/auth/ldap/user.rb b/lib/gitlab/auth/ldap/user.rb index 1405fb4ab95..814c17b7e44 100644 --- a/lib/gitlab/auth/ldap/user.rb +++ b/lib/gitlab/auth/ldap/user.rb @@ -11,16 +11,6 @@ module Gitlab module Ldap class User < Gitlab::Auth::OAuth::User extend ::Gitlab::Utils::Override - class << self - # rubocop: disable CodeReuse/ActiveRecord - def find_by_uid_and_provider(uid, provider) - identity = ::Identity.with_extern_uid(provider, uid).take - - identity && identity.user - end - # rubocop: enable CodeReuse/ActiveRecord - end - def save super('LDAP') end @@ -30,10 +20,6 @@ module Gitlab find_by_uid_and_provider || find_by_email || build_new_user end - def find_by_uid_and_provider - self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider) - end - override :should_save? def should_save? gl_user.changed? || gl_user.identities.any?(&:changed?) diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb index 1eae7af442d..57ff3fcd1f0 100644 --- a/lib/gitlab/auth/o_auth/provider.rb +++ b/lib/gitlab/auth/o_auth/provider.rb @@ -18,6 +18,8 @@ module Gitlab authenticator = case provider + when /crowd/ + Gitlab::Auth::Crowd::Authentication when /^ldap/ Gitlab::Auth::Ldap::Authentication when 'database' diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb index 3211d2ffaea..f556a7f40e9 100644 --- a/lib/gitlab/auth/o_auth/user.rb +++ b/lib/gitlab/auth/o_auth/user.rb @@ -9,6 +9,16 @@ module Gitlab module Auth module OAuth class User + class << self + # rubocop: disable CodeReuse/ActiveRecord + def find_by_uid_and_provider(uid, provider) + identity = ::Identity.with_extern_uid(provider, uid).take + + identity && identity.user + end + # rubocop: enable CodeReuse/ActiveRecord + end + SignupDisabledError = Class.new(StandardError) SigninDisabledForProviderError = Class.new(StandardError) @@ -190,15 +200,12 @@ module Gitlab @auth_hash = AuthHash.new(auth_hash) end - # rubocop: disable CodeReuse/ActiveRecord def find_by_uid_and_provider - identity = Identity.with_extern_uid(auth_hash.provider, auth_hash.uid).take - identity&.user + self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider) end - # rubocop: enable CodeReuse/ActiveRecord - def build_new_user - user_params = user_attributes.merge(skip_confirmation: true) + def build_new_user(skip_confirmation: true) + user_params = user_attributes.merge(skip_confirmation: skip_confirmation) Users::BuildService.new(nil, user_params).execute(skip_authorization: true) end diff --git a/lib/gitlab/auth/otp/fortinet.rb b/lib/gitlab/auth/otp/fortinet.rb new file mode 100644 index 00000000000..a561e97dfcd --- /dev/null +++ b/lib/gitlab/auth/otp/fortinet.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +module Gitlab + module Auth + module Otp + module Fortinet + private + + def forti_authenticator_enabled?(user) + ::Gitlab.config.forti_authenticator.enabled && + Feature.enabled?(:forti_authenticator, user) + end + + def forti_token_cloud_enabled?(user) + ::Gitlab.config.forti_token_cloud.enabled && + Feature.enabled?(:forti_token_cloud, user) + end + end + end + end +end diff --git a/lib/gitlab/auth/otp/session_enforcer.rb b/lib/gitlab/auth/otp/session_enforcer.rb new file mode 100644 index 00000000000..8cc280756cc --- /dev/null +++ b/lib/gitlab/auth/otp/session_enforcer.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Gitlab + module Auth + module Otp + class SessionEnforcer + OTP_SESSIONS_NAMESPACE = 'session:otp' + DEFAULT_EXPIRATION = 15.minutes.to_i + + def initialize(key) + @key = key + end + + def update_session + Gitlab::Redis::SharedState.with do |redis| + redis.setex(key_name, DEFAULT_EXPIRATION, true) + end + end + + def access_restricted? + Gitlab::Redis::SharedState.with do |redis| + !redis.get(key_name) + end + end + + private + + attr_reader :key + + def key_name + @key_name ||= "#{OTP_SESSIONS_NAMESPACE}:#{key.id}" + end + end + end + end +end diff --git a/lib/gitlab/auth/otp/strategies/base.rb b/lib/gitlab/auth/otp/strategies/base.rb index 718630e0e31..7d8513642c4 100644 --- a/lib/gitlab/auth/otp/strategies/base.rb +++ b/lib/gitlab/auth/otp/strategies/base.rb @@ -25,6 +25,10 @@ module Gitlab result end + + def error_from_response(response) + error(response.message, response.code) + end end end end diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator.rb index fbcb9fd8cdb..c1433f05db2 100644 --- a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb +++ b/lib/gitlab/auth/otp/strategies/forti_authenticator.rb @@ -17,7 +17,10 @@ module Gitlab # Successful authentication results in HTTP 200: OK # https://docs.fortinet.com/document/fortiauthenticator/6.2.0/rest-api-solution-guide/704555/authentication-auth - response.ok? ? success : error(message: response.message, http_status: response.code) + response.ok? ? success : error_from_response(response) + rescue StandardError => ex + Gitlab::AppLogger.error(ex) + error(ex.message) end private @@ -32,7 +35,7 @@ module Gitlab def api_credentials { username: ::Gitlab.config.forti_authenticator.username, - password: ::Gitlab.config.forti_authenticator.token } + password: ::Gitlab.config.forti_authenticator.access_token } end end end diff --git a/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb b/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb new file mode 100644 index 00000000000..d7506eca242 --- /dev/null +++ b/lib/gitlab/auth/otp/strategies/forti_token_cloud.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Gitlab + module Auth + module Otp + module Strategies + class FortiTokenCloud < Base + include Gitlab::Utils::StrongMemoize + BASE_API_URL = 'https://ftc.fortinet.com:9696/api/v1' + + def validate(otp_code) + if access_token_create_response.created? + otp_verification_response = verify_otp(otp_code) + + otp_verification_response.ok? ? success : error_from_response(otp_verification_response) + else + error_from_response(access_token_create_response) + end + end + + private + + # TODO: Cache the access token: https://gitlab.com/gitlab-org/gitlab/-/issues/292437 + def access_token_create_response + # Returns '201 CREATED' on successful creation of a new access token. + strong_memoize(:access_token_create_response) do + post( + url: url('/login'), + body: { + client_id: ::Gitlab.config.forti_token_cloud.client_id, + client_secret: ::Gitlab.config.forti_token_cloud.client_secret + }.to_json + ) + end + end + + def access_token + Gitlab::Json.parse(access_token_create_response)['access_token'] + end + + def verify_otp(otp_code) + # Returns '200 OK' on successful verification. + # Uses the access token created via `access_token_create_response` as the auth token. + post( + url: url('/auth'), + headers: { 'Authorization': "Bearer #{access_token}" }, + body: { + username: user.username, + token: otp_code + }.to_json + ) + end + + def url(path) + BASE_API_URL + path + end + + def post(url:, body:, headers: {}) + Gitlab::HTTP.post( + url, + headers: { + 'Content-Type': 'application/json' + }.merge(headers), + body: body, + verify: false # FTC API Docs specifically mentions to turn off SSL Verification while making requests. + ) + end + end + end + end + end +end diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb index c6216fa9cad..d28ee54cfbc 100644 --- a/lib/gitlab/auth/request_authenticator.rb +++ b/lib/gitlab/auth/request_authenticator.rb @@ -49,9 +49,16 @@ module Gitlab private + def access_token + strong_memoize(:access_token) do + super || find_personal_access_token_from_http_basic_auth + end + end + def route_authentication_setting @route_authentication_setting ||= { - job_token_allowed: api_request? + job_token_allowed: api_request?, + basic_auth_personal_access_token: api_request? } end end |