Commit 79c523c3 authored by Pavel Shutsin's avatar Pavel Shutsin

Require access token for git when 2fa is required

Prohibit basic credientials auth in git when 2fa is required.
Please use personal access token instead

Changelog: security
parent 4d66cb9a
......@@ -172,7 +172,11 @@ module Gitlab
user = find_with_user_password(login, password)
return unless user
raise Gitlab::Auth::MissingPersonalAccessTokenError if user.two_factor_enabled?
verifier = TwoFactorAuthVerifier.new(user)
if user.two_factor_enabled? || verifier.two_factor_authentication_enforced?
raise Gitlab::Auth::MissingPersonalAccessTokenError
end
Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
end
......
......@@ -9,6 +9,10 @@ module Gitlab
@current_user = current_user
end
def two_factor_authentication_enforced?
two_factor_authentication_required? && two_factor_grace_period_expired?
end
def two_factor_authentication_required?
Gitlab::CurrentSettings.require_two_factor_authentication? ||
current_user&.require_two_factor_authentication_from_group?
......
......@@ -3,33 +3,50 @@
require 'spec_helper'
RSpec.describe Gitlab::Auth::TwoFactorAuthVerifier do
let(:user) { create(:user) }
using RSpec::Parameterized::TableSyntax
subject { described_class.new(user) }
subject(:verifier) { described_class.new(user) }
describe '#two_factor_authentication_required?' do
describe 'when it is required on application level' do
it 'returns true' do
stub_application_setting require_two_factor_authentication: true
let(:user) { build_stubbed(:user, otp_grace_period_started_at: Time.zone.now) }
expect(subject.two_factor_authentication_required?).to be_truthy
end
end
describe '#two_factor_authentication_enforced?' do
subject { verifier.two_factor_authentication_enforced? }
describe 'when it is required on group level' do
it 'returns true' do
allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(true)
where(:instance_level_enabled, :group_level_enabled, :grace_period_expired, :should_be_enforced) do
false | false | true | false
true | false | false | false
true | false | true | true
false | true | false | false
false | true | true | true
end
expect(subject.two_factor_authentication_required?).to be_truthy
with_them do
before do
stub_application_setting(require_two_factor_authentication: instance_level_enabled)
allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(group_level_enabled)
stub_application_setting(two_factor_grace_period: grace_period_expired ? 0 : 1.month.in_hours)
end
it { is_expected.to eq(should_be_enforced) }
end
end
describe 'when it is not required' do
it 'returns false when not required on group level' do
allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(false)
describe '#two_factor_authentication_required?' do
subject { verifier.two_factor_authentication_required? }
where(:instance_level_enabled, :group_level_enabled, :should_be_required) do
true | false | true
false | true | true
false | false | false
end
expect(subject.two_factor_authentication_required?).to be_falsey
with_them do
before do
stub_application_setting(require_two_factor_authentication: instance_level_enabled)
allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(group_level_enabled)
end
it { is_expected.to eq(should_be_required) }
end
end
......@@ -85,25 +102,21 @@ RSpec.describe Gitlab::Auth::TwoFactorAuthVerifier do
end
describe '#two_factor_grace_period_expired?' do
before do
allow(user).to receive(:otp_grace_period_started_at).and_return(4.hours.ago)
end
it 'returns true if the grace period has expired' do
allow(subject).to receive(:two_factor_grace_period).and_return(2)
stub_application_setting two_factor_grace_period: 0
expect(subject.two_factor_grace_period_expired?).to be_truthy
end
it 'returns false if the grace period has not expired' do
allow(subject).to receive(:two_factor_grace_period).and_return(6)
stub_application_setting two_factor_grace_period: 1.month.in_hours
expect(subject.two_factor_grace_period_expired?).to be_falsey
end
context 'when otp_grace_period_started_at is nil' do
it 'returns false' do
allow(user).to receive(:otp_grace_period_started_at).and_return(nil)
user.otp_grace_period_started_at = nil
expect(subject.two_factor_grace_period_expired?).to be_falsey
end
......
......@@ -386,7 +386,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
shared_examples 'with an invalid access token' do
it 'fails for a non-member' do
expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip'))
.to have_attributes(auth_failure )
.to have_attributes(auth_failure)
end
context 'when project bot user is blocked' do
......@@ -396,7 +396,7 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'fails for a blocked project bot' do
expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip'))
.to have_attributes(auth_failure )
.to have_attributes(auth_failure)
end
end
end
......@@ -466,6 +466,41 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
.to have_attributes(auth_failure)
end
context 'when 2fa is enabled globally' do
let_it_be(:user) do
create(:user, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago)
end
before do
stub_application_setting(require_two_factor_authentication: true)
end
it 'fails if grace period expired' do
stub_application_setting(two_factor_grace_period: 0)
expect { gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip') }
.to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError)
end
it 'goes through if grace period is not expired yet' do
stub_application_setting(two_factor_grace_period: 72)
expect(gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip'))
.to have_attributes(actor: user, project: nil, type: :gitlab_or_ldap, authentication_abilities: described_class.full_authentication_abilities)
end
end
context 'when 2fa is enabled personally' do
let(:user) do
create(:user, :two_factor, username: 'normal_user', password: 'my-secret', otp_grace_period_started_at: 1.day.ago)
end
it 'fails' do
expect { gl_auth.find_for_git_client(user.username, user.password, project: nil, ip: 'ip') }
.to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError)
end
end
it 'goes through lfs authentication' do
user = create(
:user,
......@@ -757,16 +792,16 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
describe 'find_with_user_password' do
let!(:user) do
create(:user,
username: username,
password: password,
password_confirmation: password)
username: username,
password: password,
password_confirmation: password)
end
let(:username) { 'John' } # username isn't lowercase, test this
let(:password) { 'my-secret' }
it "finds user by valid login/password" do
expect( gl_auth.find_with_user_password(username, password) ).to eql user
expect(gl_auth.find_with_user_password(username, password)).to eql user
end
it 'finds user by valid email/password with case-insensitive email' do
......@@ -779,12 +814,12 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it "does not find user with invalid password" do
password = 'wrong'
expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
end
it "does not find user with invalid login" do
user = 'wrong'
expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
end
include_examples 'user login operation with unique ip limit' do
......@@ -796,13 +831,13 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it 'finds the user in deactivated state' do
user.deactivate!
expect( gl_auth.find_with_user_password(username, password) ).to eql user
expect(gl_auth.find_with_user_password(username, password)).to eql user
end
it "does not find user in blocked state" do
user.block
expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
end
it 'does not find user in locked state' do
......@@ -814,13 +849,13 @@ RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do
it "does not find user in ldap_blocked state" do
user.ldap_block
expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
end
it 'does not find user in blocked_pending_approval state' do
user.block_pending_approval
expect( gl_auth.find_with_user_password(username, password) ).not_to eql user
expect(gl_auth.find_with_user_password(username, password)).not_to eql user
end
context 'with increment_failed_attempts' do
......
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