Commit 19f29cf1 authored by Imre Farkas's avatar Imre Farkas

Merge branch 'sav-220515-2fa-user-setup' into 'master'

Add /internal/two_factor_config

See merge request gitlab-org/gitlab!39703
parents 2e1cb42a 71d61c67
---
name: two_factor_for_cli
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39703
rollout_issue_url:
type: development
group: group::access
default_enabled: false
......@@ -99,6 +99,14 @@ module API
@project = @container = access_checker.container
end
end
def validate_actor_key(actor, key_id)
return 'Could not find a user without a key' unless key_id
return 'Could not find the given key' unless actor.key
'Could not find a user for the given key' unless actor.user
end
end
namespace 'internal' do
......@@ -163,28 +171,23 @@ module API
redis: redis_ping
}
end
post '/two_factor_recovery_codes' do
status 200
actor.update_last_used_at!
user = actor.user
if params[:key_id]
unless actor.key
break { success: false, message: 'Could not find the given key' }
end
if actor.key.is_a?(DeployKey)
break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' }
end
error_message = validate_actor_key(actor, params[:key_id])
unless user
break { success: false, message: 'Could not find a user for the given key' }
end
elsif params[:user_id] && user.nil?
if params[:user_id] && user.nil?
break { success: false, message: 'Could not find the given user' }
elsif error_message
break { success: false, message: error_message }
end
break { success: false, message: 'Deploy keys cannot be used to retrieve recovery codes' } if actor.key.is_a?(DeployKey)
unless user.two_factor_enabled?
break { success: false, message: 'Two-factor authentication is not enabled for this user' }
end
......@@ -204,20 +207,14 @@ module API
actor.update_last_used_at!
user = actor.user
if params[:key_id]
unless actor.key
break { success: false, message: 'Could not find the given key' }
end
error_message = validate_actor_key(actor, params[:key_id])
if actor.key.is_a?(DeployKey)
break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' }
end
break { success: false, message: 'Deploy keys cannot be used to create personal access tokens' } if actor.key.is_a?(DeployKey)
unless user
break { success: false, message: 'Could not find a user for the given key' }
end
elsif params[:user_id] && user.nil?
if params[:user_id] && user.nil?
break { success: false, message: 'Could not find the given user' }
elsif error_message
break { success: false, message: error_message }
end
if params[:name].blank?
......@@ -269,6 +266,28 @@ module API
present response, with: Entities::InternalPostReceive::Response
end
post '/two_factor_config' do
status 200
break { success: false } unless Feature.enabled?(:two_factor_for_cli)
actor.update_last_used_at!
user = actor.user
error_message = validate_actor_key(actor, params[:key_id])
if error_message
{ success: false, message: error_message }
elsif actor.key.is_a?(DeployKey)
{ success: true, two_factor_required: false }
else
{
success: true,
two_factor_required: user.two_factor_enabled?
}
end
end
end
end
end
......
......@@ -50,43 +50,63 @@ RSpec.describe API::Internal::Base do
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'),
params: {
secret_token: secret_token,
key_id: non_existing_record_id
}
shared_examples 'actor key validations' do
context 'key id is not provided' do
let(:key_id) { nil }
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find the given key')
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find a user without a key')
end
end
it 'returns an error message when the key is a deploy key' do
deploy_key = create(:deploy_key)
context 'key does not exist' do
let(:key_id) { non_existing_record_id }
post api('/internal/two_factor_recovery_codes'),
params: {
secret_token: secret_token,
key_id: deploy_key.id
}
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Deploy keys cannot be used to retrieve recovery codes')
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find the given key')
end
end
it 'returns an error message when the user does not exist' do
key_without_user = create(:key, user: nil)
context 'key without user' do
let(:key_id) { create(:key, user: nil).id }
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find a user for the given key')
end
end
end
describe 'GET /internal/two_factor_recovery_codes' do
let(:key_id) { key.id }
subject do
post api('/internal/two_factor_recovery_codes'),
params: {
secret_token: secret_token,
key_id: key_without_user.id
key_id: key_id
}
end
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
it_behaves_like 'actor key validations'
context 'key is a deploy key' do
let(:key_id) { create(:deploy_key).id }
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Deploy keys cannot be used to retrieve recovery codes')
end
end
context 'when two-factor is enabled' do
......@@ -95,11 +115,7 @@ RSpec.describe API::Internal::Base do
allow_any_instance_of(User)
.to receive(:generate_otp_backup_codes!).and_return(%w(119135e5a3ebce8e 34bd7b74adbc8861))
post api('/internal/two_factor_recovery_codes'),
params: {
secret_token: secret_token,
key_id: key.id
}
subject
expect(json_response['success']).to be_truthy
expect(json_response['recovery_codes']).to match_array(%w(119135e5a3ebce8e 34bd7b74adbc8861))
......@@ -110,11 +126,7 @@ RSpec.describe API::Internal::Base 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'),
params: {
secret_token: secret_token,
key_id: key.id
}
subject
expect(json_response['success']).to be_falsey
expect(json_response['recovery_codes']).to be_nil
......@@ -123,42 +135,27 @@ RSpec.describe API::Internal::Base do
end
describe 'POST /internal/personal_access_token' do
it 'returns an error message when the key does not exist' do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: non_existing_record_id
}
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Could not find the given key')
end
it 'returns an error message when the key is a deploy key' do
deploy_key = create(:deploy_key)
let(:key_id) { key.id }
subject do
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: deploy_key.id
key_id: key_id
}
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Deploy keys cannot be used to create personal access tokens')
end
it 'returns an error message when the user does not exist' do
key_without_user = create(:key, user: nil)
it_behaves_like 'actor key validations'
post api('/internal/personal_access_token'),
params: {
secret_token: secret_token,
key_id: key_without_user.id
}
context 'key is a deploy key' do
let(:key_id) { create(:deploy_key).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['token']).to be_nil
it 'returns an error message' do
subject
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq('Deploy keys cannot be used to create personal access tokens')
end
end
it 'returns an error message when given an non existent user' do
......@@ -1209,6 +1206,73 @@ RSpec.describe API::Internal::Base do
end
end
describe 'POST /internal/two_factor_config' do
let(:key_id) { key.id }
before do
stub_feature_flags(two_factor_for_cli: true)
end
subject do
post api('/internal/two_factor_config'),
params: {
secret_token: secret_token,
key_id: key_id
}
end
it_behaves_like 'actor key validations'
context 'when the key is a deploy key' do
let(:key) { create(:deploy_key) }
it 'does not required two factor' do
subject
expect(json_response['success']).to be_truthy
expect(json_response['two_factor_required']).to be_falsey
end
end
context 'when two-factor is enabled' do
it 'returns user two factor config' do
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(true)
subject
expect(json_response['success']).to be_truthy
expect(json_response['two_factor_required']).to be_truthy
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)
subject
expect(json_response['success']).to be_truthy
expect(json_response['two_factor_required']).to be_falsey
end
end
context 'two_factor_for_cli feature is disabled' do
before do
stub_feature_flags(two_factor_for_cli: false)
end
context 'when two-factor is enabled for the user' do
it 'returns user two factor config' do
allow_any_instance_of(User).to receive(:two_factor_enabled?).and_return(true)
subject
expect(json_response['success']).to be_falsey
end
end
end
end
def lfs_auth_project(project)
post(
api("/internal/lfs_authenticate"),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment