Merge branch 'internal_recovery_api' into 'master'
Add internal API to recovery 2FA ## What does this MR do? Add an internal API to make SSH 2FA recovery possible. Related to gitlab-org/gitlab-shell!74 See merge request !5510
This commit is contained in:
commit
6fb46b604e
6 changed files with 160 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
Please view this file on the master branch, on stable branches it's out of date.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
v 8.12.0 (unreleased)
|
v 8.12.0 (unreleased)
|
||||||
|
- Add two-factor recovery endpoint to internal API !5510
|
||||||
- Change merge_error column from string to text type
|
- Change merge_error column from string to text type
|
||||||
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
|
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
|
||||||
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
|
- Optimistic locking for Issues and Merge Requests (title and description overriding prevention)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## User documentation
|
## User documentation
|
||||||
|
|
||||||
|
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
|
||||||
- [API](api/README.md) Automate GitLab via a simple and powerful API.
|
- [API](api/README.md) Automate GitLab via a simple and powerful API.
|
||||||
- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
|
- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
|
||||||
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
|
||||||
|
|
3
doc/user/account/security.md
Normal file
3
doc/user/account/security.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Account Security
|
||||||
|
|
||||||
|
- [Two-Factor Authentication](two_factor_authentication.md)
|
68
doc/user/account/two_factor_authentication.md
Normal file
68
doc/user/account/two_factor_authentication.md
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Two-Factor Authentication
|
||||||
|
|
||||||
|
## Recovery options
|
||||||
|
|
||||||
|
If you lose your code generation device (such as your mobile phone) and you need
|
||||||
|
to disable two-factor authentication on your account, you have several options.
|
||||||
|
|
||||||
|
### Use a saved recovery code
|
||||||
|
|
||||||
|
When you enabled two-factor authentication for your account, a series of
|
||||||
|
recovery codes were generated. If you saved those codes somewhere safe, you
|
||||||
|
may use one to sign in.
|
||||||
|
|
||||||
|
First, enter your username/email and password on the GitLab sign in page. When
|
||||||
|
prompted for a two-factor code, enter one of the recovery codes you saved
|
||||||
|
previously.
|
||||||
|
|
||||||
|
> **Note:** Once a particular recovery code has been used, it cannot be used again.
|
||||||
|
You may still use the other saved recovery codes at a later time.
|
||||||
|
|
||||||
|
### Generate new recovery codes using SSH
|
||||||
|
|
||||||
|
It's not uncommon for users to forget to save the recovery codes when enabling
|
||||||
|
two-factor authentication. If you have an SSH key added to your GitLab account,
|
||||||
|
you can generate a new set of recovery codes using SSH.
|
||||||
|
|
||||||
|
Run `ssh git@gitlab.example.com 2fa_recovery_codes`. You will be prompted to
|
||||||
|
confirm that you wish to generate new codes. If you choose to continue, any
|
||||||
|
previously saved codes will be invalidated.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ssh git@gitlab.example.com 2fa_recovery_codes
|
||||||
|
Are you sure you want to generate new two-factor recovery codes?
|
||||||
|
Any existing recovery codes you saved will be invalidated. (yes/no)
|
||||||
|
yes
|
||||||
|
|
||||||
|
Your two-factor authentication recovery codes are:
|
||||||
|
|
||||||
|
119135e5a3ebce8e
|
||||||
|
11f6v2a498810dcd
|
||||||
|
3924c7ab2089c902
|
||||||
|
e79a3398bfe4f224
|
||||||
|
34bd7b74adbc8861
|
||||||
|
f061691d5107df1a
|
||||||
|
169bf32a18e63e7f
|
||||||
|
b510e7422e81c947
|
||||||
|
20dbed24c5e74663
|
||||||
|
df9d3b9403b9c9f0
|
||||||
|
|
||||||
|
During sign in, use one of the codes above when prompted for
|
||||||
|
your two-factor code. Then, visit your Profile Settings and add
|
||||||
|
a new device so you do not lose access to your account again.
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, go to the GitLab sign in page and enter your username/email and password.
|
||||||
|
When prompted for a two-factor code, enter one of the recovery codes obtained
|
||||||
|
from the command line output.
|
||||||
|
|
||||||
|
> **Note:** After signing in, you should immediately visit your **Profile Settings
|
||||||
|
-> Account** to set up two-factor authentication with a new device.
|
||||||
|
|
||||||
|
### Ask a GitLab administrator to disable two-factor on your account
|
||||||
|
|
||||||
|
If the above two methods are not possible, you may ask a GitLab global
|
||||||
|
administrator to disable two-factor authentication for your account. Please
|
||||||
|
be aware that this will temporarily leave your account in a less secure state.
|
||||||
|
You should sign in and re-enable two-factor authentication as soon as possible
|
||||||
|
after the administrator disables it.
|
|
@ -101,6 +101,31 @@ module API
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
post '/two_factor_recovery_codes' do
|
||||||
|
status 200
|
||||||
|
|
||||||
|
key = Key.find(params[:key_id])
|
||||||
|
user = key.user
|
||||||
|
|
||||||
|
# Make sure this isn't a deploy key
|
||||||
|
unless key.type.nil?
|
||||||
|
return { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
|
||||||
|
end
|
||||||
|
|
||||||
|
unless user.present?
|
||||||
|
return { success: false, message: 'Could not find a user for the given key' }
|
||||||
|
end
|
||||||
|
|
||||||
|
unless user.two_factor_enabled?
|
||||||
|
return { success: false, message: 'Two-factor authentication is not enabled for this user' }
|
||||||
|
end
|
||||||
|
|
||||||
|
codes = user.generate_otp_backup_codes!
|
||||||
|
user.save!
|
||||||
|
|
||||||
|
{ success: true, recovery_codes: codes }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,6 +38,68 @@ describe API::API, api: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /internal/two_factor_recovery_codes' do
|
||||||
|
it 'returns an error message when the key does not exist' do
|
||||||
|
post api('/internal/two_factor_recovery_codes'),
|
||||||
|
secret_token: secret_token,
|
||||||
|
key_id: 12345
|
||||||
|
|
||||||
|
expect(response).to have_http_status(404)
|
||||||
|
expect(json_response['message']).to eq('404 Not found')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an error message when the key is a deploy key' do
|
||||||
|
deploy_key = create(:deploy_key)
|
||||||
|
|
||||||
|
post api('/internal/two_factor_recovery_codes'),
|
||||||
|
secret_token: secret_token,
|
||||||
|
key_id: deploy_key.id
|
||||||
|
|
||||||
|
expect(json_response['success']).to be_falsey
|
||||||
|
expect(json_response['message']).to eq('Deploy keys cannot be used to retrieve recovery codes')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an error message when the user does not exist' do
|
||||||
|
key_without_user = create(:key, user: nil)
|
||||||
|
|
||||||
|
post api('/internal/two_factor_recovery_codes'),
|
||||||
|
secret_token: secret_token,
|
||||||
|
key_id: key_without_user.id
|
||||||
|
|
||||||
|
expect(json_response['success']).to be_falsey
|
||||||
|
expect(json_response['message']).to eq('Could not find a user for the given key')
|
||||||
|
expect(json_response['recovery_codes']).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when two-factor is enabled' do
|
||||||
|
it 'returns new recovery codes when the user exists' do
|
||||||
|
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(true)
|
||||||
|
allow_any_instance_of(User)
|
||||||
|
.to receive(:generate_otp_backup_codes!).and_return(%w(119135e5a3ebce8e 34bd7b74adbc8861))
|
||||||
|
|
||||||
|
post api('/internal/two_factor_recovery_codes'),
|
||||||
|
secret_token: secret_token,
|
||||||
|
key_id: key.id
|
||||||
|
|
||||||
|
expect(json_response['success']).to be_truthy
|
||||||
|
expect(json_response['recovery_codes']).to match_array(%w(119135e5a3ebce8e 34bd7b74adbc8861))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when two-factor is not enabled' do
|
||||||
|
it 'returns an error message' do
|
||||||
|
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(false)
|
||||||
|
|
||||||
|
post api('/internal/two_factor_recovery_codes'),
|
||||||
|
secret_token: secret_token,
|
||||||
|
key_id: key.id
|
||||||
|
|
||||||
|
expect(json_response['success']).to be_falsey
|
||||||
|
expect(json_response['recovery_codes']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "GET /internal/discover" do
|
describe "GET /internal/discover" do
|
||||||
it do
|
it do
|
||||||
get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
|
get(api("/internal/discover"), key_id: key.id, secret_token: secret_token)
|
||||||
|
|
Loading…
Reference in a new issue