diff options
Diffstat (limited to 'lib/gitlab/auth/otp')
-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 |
5 files changed, 137 insertions, 2 deletions
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 |