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