Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
317dc0af
Commit
317dc0af
authored
Dec 10, 2020
by
Manoj M J
Committed by
Imre Farkas
Dec 10, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Integrate FortiCloud Token to GitLab 2FA options
This change integrates FortiCloud Token to GitLab 2FA options
parent
3e55ec99
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
393 additions
and
5 deletions
+393
-5
app/models/user.rb
app/models/user.rb
+4
-1
app/services/users/validate_otp_service.rb
app/services/users/validate_otp_service.rb
+5
-1
config/feature_flags/development/forti_token_cloud.yml
config/feature_flags/development/forti_token_cloud.yml
+8
-0
config/gitlab.yml.example
config/gitlab.yml.example
+9
-0
config/initializers/1_settings.rb
config/initializers/1_settings.rb
+6
-0
doc/user/profile/account/two_factor_authentication.md
doc/user/profile/account/two_factor_authentication.md
+80
-0
lib/gitlab/auth/otp/fortinet.rb
lib/gitlab/auth/otp/fortinet.rb
+20
-0
lib/gitlab/auth/otp/strategies/base.rb
lib/gitlab/auth/otp/strategies/base.rb
+4
-0
lib/gitlab/auth/otp/strategies/forti_authenticator.rb
lib/gitlab/auth/otp/strategies/forti_authenticator.rb
+1
-1
lib/gitlab/auth/otp/strategies/forti_token_cloud.rb
lib/gitlab/auth/otp/strategies/forti_token_cloud.rb
+72
-0
spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
...ib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
+2
-1
spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
.../lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
+87
-0
spec/models/user_spec.rb
spec/models/user_spec.rb
+74
-0
spec/services/users/validate_otp_service_spec.rb
spec/services/users/validate_otp_service_spec.rb
+17
-1
spec/spec_helper.rb
spec/spec_helper.rb
+4
-0
No files found.
app/models/user.rb
View file @
317dc0af
...
...
@@ -25,6 +25,7 @@ class User < ApplicationRecord
include
IgnorableColumns
include
UpdateHighestRole
include
HasUserType
include
Gitlab
::
Auth
::
Otp
::
Fortinet
DEFAULT_NOTIFICATION_LEVEL
=
:participating
...
...
@@ -810,7 +811,9 @@ class User < ApplicationRecord
end
def
two_factor_otp_enabled?
otp_required_for_login?
||
Feature
.
enabled?
(
:forti_authenticator
,
self
)
otp_required_for_login?
||
forti_authenticator_enabled?
(
self
)
||
forti_token_cloud_enabled?
(
self
)
end
def
two_factor_u2f_enabled?
...
...
app/services/users/validate_otp_service.rb
View file @
317dc0af
...
...
@@ -2,10 +2,14 @@
module
Users
class
ValidateOtpService
<
BaseService
include
::
Gitlab
::
Auth
::
Otp
::
Fortinet
def
initialize
(
current_user
)
@current_user
=
current_user
@strategy
=
if
Feature
.
enabled?
(
:forti_authenticator
,
current_user
)
@strategy
=
if
forti_authenticator_enabled?
(
current_user
)
::
Gitlab
::
Auth
::
Otp
::
Strategies
::
FortiAuthenticator
.
new
(
current_user
)
elsif
forti_token_cloud_enabled?
(
current_user
)
::
Gitlab
::
Auth
::
Otp
::
Strategies
::
FortiTokenCloud
.
new
(
current_user
)
else
::
Gitlab
::
Auth
::
Otp
::
Strategies
::
Devise
.
new
(
current_user
)
end
...
...
config/feature_flags/development/forti_token_cloud.yml
0 → 100644
View file @
317dc0af
---
name
:
forti_token_cloud
introduced_by_url
:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49089
rollout_issue_url
:
milestone
:
'
13.7'
type
:
development
group
:
group::access
default_enabled
:
false
config/gitlab.yml.example
View file @
317dc0af
...
...
@@ -1042,6 +1042,15 @@ production: &base
# Access token for FortiAuthenticator API
# access_token: 123s3cr3t456
# FortiToken Cloud settings
forti_token_cloud:
# Allow using FortiToken Cloud as OTP provider
enabled: false
# Client ID and Secret to access FortiToken Cloud API
# client_id: 'YOUR_FORTI_TOKEN_CLOUD_CLIENT_ID'
# client_secret: 'YOUR_FORTI_TOKEN_CLOUD_CLIENT_SECRET'
# Shared file storage settings
shared:
# path: /mnt/gitlab # Default: shared
...
...
config/initializers/1_settings.rb
View file @
317dc0af
...
...
@@ -791,6 +791,12 @@ Settings['forti_authenticator'] ||= Settingslogic.new({})
Settings
.
forti_authenticator
[
'enabled'
]
=
false
if
Settings
.
forti_authenticator
[
'enabled'
].
nil?
Settings
.
forti_authenticator
[
'port'
]
=
443
if
Settings
.
forti_authenticator
[
'port'
].
to_i
==
0
#
# FortiToken Cloud
#
Settings
[
'forti_token_cloud'
]
||=
Settingslogic
.
new
({})
Settings
.
forti_token_cloud
[
'enabled'
]
=
false
if
Settings
.
forti_token_cloud
[
'enabled'
].
nil?
#
# Extra customization
#
...
...
doc/user/profile/account/two_factor_authentication.md
View file @
317dc0af
...
...
@@ -142,6 +142,86 @@ to run the following command:
Feature
.
enable
(
:forti_authenticator
,
User
.
find
(
<
user
ID
>
))
```
### One-time password via FortiToken Cloud
> - Introduced in [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/212313).
> - It's deployed behind a feature flag, disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-fortitoken-cloud-integration).
WARNING:
This feature might not be available to you. Check the
**version history**
note above for details.
You can use FortiToken Cloud as an OTP provider in GitLab. Users must exist in
both FortiToken Cloud and GitLab with the exact same username, and users must
have FortiToken configured in FortiToken Cloud.
You'll also need a
`client_id`
and
`client_secret`
to configure FortiToken Cloud.
To get these, see the
`REST API Guide`
at
[
`Fortinet Document Library`
](
https://docs.fortinet.com/document/fortitoken-cloud/20.4.d/rest-api
)
.
First configure FortiToken Cloud in GitLab. On your GitLab server:
1.
Open the configuration file.
For Omnibus GitLab:
```
shell
sudo
editor /etc/gitlab/gitlab.rb
```
For installations from source:
```
shell
cd
/home/git/gitlab
sudo
-u
git
-H
editor config/gitlab.yml
```
1.
Add the provider configuration:
For Omnibus package:
```
ruby
gitlab_rails
[
'forti_token_cloud_enabled'
]
=
true
gitlab_rails
[
'forti_token_cloud_client_id'
]
=
'<your_fortinet_cloud_client_id>'
gitlab_rails
[
'forti_token_cloud_client_secret'
]
=
'<your_fortinet_cloud_client_secret>'
```
For installations from source:
```
yaml
forti_token_cloud
:
enabled
:
true
client_id
:
YOUR_FORTI_TOKEN_CLOUD_CLIENT_ID
client_secret
:
YOUR_FORTI_TOKEN_CLOUD_CLIENT_SECRET
```
1.
Save the configuration file.
1.
[
Reconfigure
](
../../../administration/restart_gitlab.md#omnibus-gitlab-reconfigure
)
or
[
restart GitLab
](
../../../administration/restart_gitlab.md#installations-from-source
)
for the changes to take effect if you installed GitLab via Omnibus or from
source respectively.
#### Enable or disable FortiToken Cloud integration
FortiToken Cloud integration is under development and not ready for production use.
It is deployed behind a feature flag that is
**disabled by default**
.
[
GitLab administrators with access to the GitLab Rails console
](
../../../administration/feature_flags.md
)
can enable it.
To enable it:
```
ruby
Feature
.
enable
(
:forti_token_cloud
,
User
.
find
(
<
user
ID
>
))
```
To disable it:
```
ruby
Feature
.
disable
(
:forti_token_cloud
,
User
.
find
(
<
user
ID
>
))
```
### U2F device
> Introduced in [GitLab 8.9](https://about.gitlab.com/blog/2016/06/22/gitlab-adds-support-for-u2f/).
...
...
lib/gitlab/auth/otp/fortinet.rb
0 → 100644
View file @
317dc0af
# 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
lib/gitlab/auth/otp/strategies/base.rb
View file @
317dc0af
...
...
@@ -25,6 +25,10 @@ module Gitlab
result
end
def
error_from_response
(
response
)
error
(
response
.
message
,
response
.
code
)
end
end
end
end
...
...
lib/gitlab/auth/otp/strategies/forti_authenticator.rb
View file @
317dc0af
...
...
@@ -17,7 +17,7 @@ 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
.
cod
e
)
response
.
ok?
?
success
:
error
_from_response
(
respons
e
)
end
private
...
...
lib/gitlab/auth/otp/strategies/forti_token_cloud.rb
0 → 100644
View file @
317dc0af
# 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
spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
View file @
317dc0af
...
...
@@ -16,9 +16,10 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do
subject
(
:validate
)
{
described_class
.
new
(
user
).
validate
(
otp_code
)
}
before
do
stub_feature_flags
(
forti_authenticator:
true
)
stub_feature_flags
(
forti_authenticator:
user
)
stub_forti_authenticator_config
(
enabled:
true
,
host:
host
,
port:
port
,
username:
api_username
,
...
...
spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
0 → 100644
View file @
317dc0af
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
Auth
::
Otp
::
Strategies
::
FortiTokenCloud
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:otp_code
)
{
42
}
let
(
:url
)
{
'https://ftc.example.com:9696/api/v1'
}
let
(
:client_id
)
{
'client_id'
}
let
(
:client_secret
)
{
's3cr3t'
}
let
(
:access_token_create_url
)
{
url
+
'/login'
}
let
(
:otp_verification_url
)
{
url
+
'/auth'
}
let
(
:access_token
)
{
'an_access_token'
}
let
(
:access_token_create_response_body
)
{
''
}
subject
(
:validate
)
{
described_class
.
new
(
user
).
validate
(
otp_code
)
}
before
do
stub_feature_flags
(
forti_token_cloud:
user
)
stub_const
(
"
#{
described_class
}
::BASE_API_URL"
,
url
)
stub_forti_token_cloud_config
(
enabled:
true
,
client_id:
client_id
,
client_secret:
client_secret
)
access_token_request_body
=
{
client_id:
client_id
,
client_secret:
client_secret
}
stub_request
(
:post
,
access_token_create_url
)
.
with
(
body:
JSON
(
access_token_request_body
),
headers:
{
'Content-Type'
=>
'application/json'
})
.
to_return
(
status:
access_token_create_response_status
,
body:
Gitlab
::
Json
.
generate
(
access_token_create_response_body
),
headers:
{}
)
end
context
'access token is created successfully'
do
let
(
:access_token_create_response_body
)
{
{
access_token:
access_token
,
expires_in:
3600
}
}
let
(
:access_token_create_response_status
)
{
201
}
before
do
otp_verification_request_body
=
{
username:
user
.
username
,
token:
otp_code
}
stub_request
(
:post
,
otp_verification_url
)
.
with
(
body:
JSON
(
otp_verification_request_body
),
headers:
{
'Content-Type'
=>
'application/json'
,
'Authorization'
=>
"Bearer
#{
access_token
}
"
})
.
to_return
(
status:
otp_verification_response_status
,
body:
''
,
headers:
{})
end
context
'otp verification is successful'
do
let
(
:otp_verification_response_status
)
{
200
}
it
'returns success'
do
expect
(
validate
[
:status
]).
to
eq
(
:success
)
end
end
context
'otp verification is not successful'
do
let
(
:otp_verification_response_status
)
{
401
}
it
'returns error'
do
expect
(
validate
[
:status
]).
to
eq
(
:error
)
end
end
end
context
'access token creation fails'
do
let
(
:access_token_create_response_status
)
{
400
}
it
'returns error'
do
expect
(
validate
[
:status
]).
to
eq
(
:error
)
end
end
def
stub_forti_token_cloud_config
(
forti_token_cloud_settings
)
allow
(
::
Gitlab
.
config
.
forti_token_cloud
).
to
(
receive_messages
(
forti_token_cloud_settings
))
end
end
spec/models/user_spec.rb
View file @
317dc0af
...
...
@@ -1585,6 +1585,80 @@ RSpec.describe User do
end
end
describe
'#two_factor_otp_enabled?'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
context
'when 2FA is enabled by an MFA Device'
do
let
(
:user
)
{
create
(
:user
,
:two_factor
)
}
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
true
)
}
end
context
'FortiAuthenticator'
do
context
'when enabled via GitLab settings'
do
before
do
allow
(
::
Gitlab
.
config
.
forti_authenticator
).
to
receive
(
:enabled
).
and_return
(
true
)
end
context
'when feature is disabled for the user'
do
before
do
stub_feature_flags
(
forti_authenticator:
false
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
false
)
}
end
context
'when feature is enabled for the user'
do
before
do
stub_feature_flags
(
forti_authenticator:
user
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
true
)
}
end
end
context
'when disabled via GitLab settings'
do
before
do
allow
(
::
Gitlab
.
config
.
forti_authenticator
).
to
receive
(
:enabled
).
and_return
(
false
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
false
)
}
end
end
context
'FortiTokenCloud'
do
context
'when enabled via GitLab settings'
do
before
do
allow
(
::
Gitlab
.
config
.
forti_token_cloud
).
to
receive
(
:enabled
).
and_return
(
true
)
end
context
'when feature is disabled for the user'
do
before
do
stub_feature_flags
(
forti_token_cloud:
false
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
false
)
}
end
context
'when feature is enabled for the user'
do
before
do
stub_feature_flags
(
forti_token_cloud:
user
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
true
)
}
end
end
context
'when disabled via GitLab settings'
do
before
do
allow
(
::
Gitlab
.
config
.
forti_token_cloud
).
to
receive
(
:enabled
).
and_return
(
false
)
end
it
{
expect
(
user
.
two_factor_otp_enabled?
).
to
eq
(
false
)
}
end
end
end
describe
'projects'
do
before
do
@user
=
create
(
:user
)
...
...
spec/services/users/validate_otp_service_spec.rb
View file @
317dc0af
...
...
@@ -20,7 +20,8 @@ RSpec.describe Users::ValidateOtpService do
context
'FortiAuthenticator'
do
before
do
stub_feature_flags
(
forti_authenticator:
true
)
stub_feature_flags
(
forti_authenticator:
user
)
allow
(
::
Gitlab
.
config
.
forti_authenticator
).
to
receive
(
:enabled
).
and_return
(
true
)
end
it
'calls FortiAuthenticator strategy'
do
...
...
@@ -31,4 +32,19 @@ RSpec.describe Users::ValidateOtpService do
validate
end
end
context
'FortiTokenCloud'
do
before
do
stub_feature_flags
(
forti_token_cloud:
user
)
allow
(
::
Gitlab
.
config
.
forti_token_cloud
).
to
receive
(
:enabled
).
and_return
(
true
)
end
it
'calls FortiTokenCloud strategy'
do
expect_next_instance_of
(
::
Gitlab
::
Auth
::
Otp
::
Strategies
::
FortiTokenCloud
)
do
|
strategy
|
expect
(
strategy
).
to
receive
(
:validate
).
with
(
otp_code
).
once
end
validate
end
end
end
spec/spec_helper.rb
View file @
317dc0af
...
...
@@ -230,6 +230,10 @@ RSpec.configure do |config|
# tests, until we introduce it in user settings
stub_feature_flags
(
forti_authenticator:
false
)
# Using FortiToken Cloud as OTP provider is disabled by default in
# tests, until we introduce it in user settings
stub_feature_flags
(
forti_token_cloud:
false
)
enable_rugged
=
example
.
metadata
[
:enable_rugged
].
present?
# Disable Rugged features by default
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment