2019-04-15 06:17:05 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-04-06 06:26:10 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-06-03 14:08:28 -04:00
|
|
|
RSpec.describe SessionsController do
|
2017-07-09 04:51:59 -04:00
|
|
|
include DeviseHelpers
|
2019-10-31 20:06:02 -04:00
|
|
|
include LdapHelpers
|
2017-07-09 04:51:59 -04:00
|
|
|
|
2020-09-01 05:10:28 -04:00
|
|
|
before do
|
|
|
|
set_devise_mapping(context: @request)
|
|
|
|
end
|
2017-03-23 09:49:59 -04:00
|
|
|
|
2020-09-01 05:10:28 -04:00
|
|
|
describe '#new' do
|
2017-03-23 09:49:59 -04:00
|
|
|
context 'when auto sign-in is enabled' do
|
|
|
|
before do
|
|
|
|
stub_omniauth_setting(auto_sign_in_with_provider: :saml)
|
2017-06-21 09:48:12 -04:00
|
|
|
allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml)
|
|
|
|
.and_return('/saml')
|
2017-03-23 09:49:59 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'and no auto_sign_in param is passed' do
|
|
|
|
it 'redirects to :omniauth_authorize_path' do
|
|
|
|
get(:new)
|
|
|
|
|
2020-01-27 07:08:35 -05:00
|
|
|
expect(response).to have_gitlab_http_status(:found)
|
2017-03-23 09:49:59 -04:00
|
|
|
expect(response).to redirect_to('/saml')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'and auto_sign_in=false param is passed' do
|
|
|
|
it 'responds with 200' do
|
2018-12-17 17:52:17 -05:00
|
|
|
get(:new, params: { auto_sign_in: 'false' })
|
2017-03-23 09:49:59 -04:00
|
|
|
|
2020-01-27 07:08:35 -05:00
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
2017-03-23 09:49:59 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-10-23 11:06:29 -04:00
|
|
|
|
2019-10-31 20:06:02 -04:00
|
|
|
context 'with LDAP enabled' do
|
|
|
|
before do
|
|
|
|
stub_ldap_setting(enabled: true)
|
|
|
|
end
|
|
|
|
|
2020-04-24 11:09:37 -04:00
|
|
|
it 'ldap_servers available in helper' do
|
2019-10-31 20:06:02 -04:00
|
|
|
get(:new)
|
|
|
|
|
2020-04-24 11:09:37 -04:00
|
|
|
expect(subject.ldap_servers.first.to_h).to include('label' => 'ldap', 'provider_name' => 'ldapmain')
|
2019-10-31 20:06:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with sign_in disabled' do
|
|
|
|
before do
|
|
|
|
stub_ldap_setting(prevent_ldap_sign_in: true)
|
|
|
|
end
|
|
|
|
|
2020-04-24 11:09:37 -04:00
|
|
|
it 'no ldap_servers available in helper' do
|
2019-10-31 20:06:02 -04:00
|
|
|
get(:new)
|
|
|
|
|
2020-04-24 11:09:37 -04:00
|
|
|
expect(subject.ldap_servers).to eq []
|
2019-10-31 20:06:02 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-03-23 09:49:59 -04:00
|
|
|
|
2020-09-01 05:10:28 -04:00
|
|
|
it "redirects correctly for referer on same host with params" do
|
|
|
|
host = "test.host"
|
|
|
|
search_path = "/search?search=seed_project"
|
|
|
|
request.headers[:HTTP_REFERER] = "http://#{host}#{search_path}"
|
|
|
|
|
|
|
|
get(:new, params: { redirect_to_referer: :yes })
|
|
|
|
|
|
|
|
expect(controller.stored_location_for(:redirect)).to eq(search_path)
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
2020-09-01 05:10:28 -04:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2020-09-01 05:10:28 -04:00
|
|
|
describe '#create' do
|
2020-05-12 23:08:26 -04:00
|
|
|
it_behaves_like 'known sign in' do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:post_action) { post(:create, params: { user: { login: user.username, password: user.password } }) }
|
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when using standard authentications' do
|
2020-10-02 08:09:03 -04:00
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:post_action) { post(:create, params: { user: { login: user.username, password: user.password } }) }
|
|
|
|
|
2016-04-06 06:26:10 -04:00
|
|
|
context 'invalid password' do
|
|
|
|
it 'does not authenticate user' do
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: { login: 'invalid', password: 'invalid' } })
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2021-04-22 17:09:53 -04:00
|
|
|
expect(controller).to set_flash.now[:alert].to(/Invalid login or password/)
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-02 08:09:03 -04:00
|
|
|
context 'a blocked user' do
|
|
|
|
it 'does not authenticate the user' do
|
|
|
|
user.block!
|
|
|
|
post_action
|
|
|
|
|
|
|
|
expect(@request.env['warden']).not_to be_authenticated
|
|
|
|
expect(flash[:alert]).to include('Your account has been blocked')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-06 08:08:38 -04:00
|
|
|
context 'a `blocked pending approval` user' do
|
|
|
|
it 'does not authenticate the user' do
|
|
|
|
user.block_pending_approval!
|
|
|
|
post_action
|
|
|
|
|
|
|
|
expect(@request.env['warden']).not_to be_authenticated
|
|
|
|
expect(flash[:alert]).to include('Your account is pending approval from your GitLab administrator and hence blocked')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-02 08:09:03 -04:00
|
|
|
context 'an internal user' do
|
|
|
|
it 'does not authenticate the user' do
|
|
|
|
user.ghost!
|
|
|
|
post_action
|
|
|
|
|
|
|
|
expect(@request.env['warden']).not_to be_authenticated
|
|
|
|
expect(flash[:alert]).to include('Your account does not have the required permission to login')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-10 23:35:47 -04:00
|
|
|
context 'when using valid password', :clean_gitlab_redis_shared_state do
|
2016-04-06 06:26:10 -04:00
|
|
|
let(:user) { create(:user) }
|
2018-06-21 14:13:08 -04:00
|
|
|
let(:user_params) { { login: user.username, password: user.password } }
|
2016-04-06 06:26:10 -04:00
|
|
|
|
|
|
|
it 'authenticates user correctly' do
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: user_params })
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2018-12-12 09:45:55 -05:00
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
|
2019-10-09 20:06:44 -04:00
|
|
|
context 'a deactivated user' do
|
|
|
|
before do
|
|
|
|
user.deactivate!
|
|
|
|
post(:create, params: { user: user_params })
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'is allowed to login' do
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'activates the user' do
|
|
|
|
expect(subject.current_user.active?).to be_truthy
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'shows reactivation flash message after logging in' do
|
|
|
|
expect(flash[:notice]).to eq('Welcome back! Your account had been deactivated due to inactivity but is now reactivated.')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-12 09:45:55 -05:00
|
|
|
context 'with password authentication disabled' do
|
|
|
|
before do
|
|
|
|
stub_application_setting(password_authentication_enabled_for_web: false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not sign in the user' do
|
|
|
|
post(:create, params: { user: user_params })
|
|
|
|
|
|
|
|
expect(@request.env['warden']).not_to be_authenticated
|
|
|
|
expect(subject.current_user).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns status 403' do
|
|
|
|
post(:create, params: { user: user_params })
|
|
|
|
|
2020-03-31 17:08:05 -04:00
|
|
|
expect(response).to have_gitlab_http_status(:forbidden)
|
2018-12-12 09:45:55 -05:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
|
2017-02-17 06:52:27 -05:00
|
|
|
it 'creates an audit log record' do
|
2020-08-21 05:10:08 -04:00
|
|
|
expect { post(:create, params: { user: user_params }) }.to change { AuditEvent.count }.by(1)
|
|
|
|
expect(AuditEvent.last.details[:with]).to eq('standard')
|
2017-02-17 06:52:27 -05:00
|
|
|
end
|
|
|
|
|
2020-09-18 02:09:31 -04:00
|
|
|
it 'creates an authentication event record' do
|
|
|
|
expect { post(:create, params: { user: user_params }) }.to change { AuthenticationEvent.count }.by(1)
|
|
|
|
expect(AuthenticationEvent.last.provider).to eq('standard')
|
|
|
|
end
|
|
|
|
|
2017-02-21 16:17:23 -05:00
|
|
|
include_examples 'user login request with unique ip limit', 302 do
|
|
|
|
def request
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: user_params })
|
2017-02-17 06:52:27 -05:00
|
|
|
expect(subject.current_user).to eq user
|
2017-02-21 16:17:23 -05:00
|
|
|
subject.sign_out user
|
2017-02-17 06:52:27 -05:00
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
end
|
2016-10-05 10:41:32 -04:00
|
|
|
|
|
|
|
it 'updates the user activity' do
|
|
|
|
expect do
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: user_params })
|
2018-07-12 07:21:08 -04:00
|
|
|
end.to change { user.reload.last_activity_on }.to(Date.today)
|
2016-10-05 10:41:32 -04:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
2018-06-21 14:13:08 -04:00
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
context 'with reCAPTCHA' do
|
|
|
|
def unsuccesful_login(user_params, sesion_params: {})
|
2018-11-15 05:13:50 -05:00
|
|
|
# Without this, `verify_recaptcha` arbitrarily returns true in test env
|
2018-06-21 14:13:08 -04:00
|
|
|
Recaptcha.configuration.skip_verify_env.delete('test')
|
2018-06-22 02:25:00 -04:00
|
|
|
counter = double(:counter)
|
|
|
|
|
|
|
|
expect(counter).to receive(:increment)
|
|
|
|
expect(Gitlab::Metrics).to receive(:counter)
|
|
|
|
.with(:failed_login_captcha_total, anything)
|
|
|
|
.and_return(counter)
|
2018-06-21 14:13:08 -04:00
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
post(:create, params: { user: user_params }, session: sesion_params)
|
2018-06-21 14:13:08 -04:00
|
|
|
end
|
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
def succesful_login(user_params, sesion_params: {})
|
2018-06-21 14:13:08 -04:00
|
|
|
# Avoid test ordering issue and ensure `verify_recaptcha` returns true
|
|
|
|
Recaptcha.configuration.skip_verify_env << 'test'
|
2018-06-22 02:25:00 -04:00
|
|
|
counter = double(:counter)
|
|
|
|
|
|
|
|
expect(counter).to receive(:increment)
|
|
|
|
expect(Gitlab::Metrics).to receive(:counter)
|
|
|
|
.with(:successful_login_captcha_total, anything)
|
|
|
|
.and_return(counter)
|
|
|
|
expect(Gitlab::Metrics).to receive(:counter).and_call_original
|
2018-06-21 14:13:08 -04:00
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
post(:create, params: { user: user_params }, session: sesion_params)
|
|
|
|
end
|
2018-06-21 14:13:08 -04:00
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
context 'when reCAPTCHA is enabled' do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:user_params) { { login: user.username, password: user.password } }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_application_setting(recaptcha_enabled: true)
|
2020-07-29 05:09:33 -04:00
|
|
|
request.headers[described_class::CAPTCHA_HEADER] = '1'
|
2019-07-18 04:27:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'displays an error when the reCAPTCHA is not solved' do
|
|
|
|
# Without this, `verify_recaptcha` arbitrarily returns true in test env
|
|
|
|
|
|
|
|
unsuccesful_login(user_params)
|
|
|
|
|
|
|
|
expect(response).to render_template(:new)
|
|
|
|
expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
|
|
|
|
expect(subject.current_user).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'successfully logs in a user when reCAPTCHA is solved' do
|
|
|
|
succesful_login(user_params)
|
|
|
|
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when reCAPTCHA login protection is enabled' do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:user_params) { { login: user.username, password: user.password } }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_application_setting(login_recaptcha_protection_enabled: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when user tried to login 5 times' do
|
|
|
|
it 'displays an error when the reCAPTCHA is not solved' do
|
|
|
|
unsuccesful_login(user_params, sesion_params: { failed_login_attempts: 6 })
|
|
|
|
|
|
|
|
expect(response).to render_template(:new)
|
|
|
|
expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
|
|
|
|
expect(subject.current_user).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'successfully logs in a user when reCAPTCHA is solved' do
|
|
|
|
succesful_login(user_params, sesion_params: { failed_login_attempts: 6 })
|
|
|
|
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when there are more than 5 anonymous session with the same IP' do
|
|
|
|
before do
|
2020-08-28 05:10:32 -04:00
|
|
|
allow(Gitlab::AnonymousSession).to receive_message_chain(:new, :session_count).and_return(6)
|
2019-07-18 04:27:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'displays an error when the reCAPTCHA is not solved' do
|
|
|
|
unsuccesful_login(user_params)
|
|
|
|
|
|
|
|
expect(response).to render_template(:new)
|
|
|
|
expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
|
|
|
|
expect(subject.current_user).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'successfully logs in a user when reCAPTCHA is solved' do
|
2020-08-28 05:10:32 -04:00
|
|
|
expect(Gitlab::AnonymousSession).to receive_message_chain(:new, :cleanup_session_per_ip_count)
|
2019-07-18 04:27:02 -04:00
|
|
|
|
|
|
|
succesful_login(user_params)
|
|
|
|
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
2018-06-21 14:13:08 -04:00
|
|
|
end
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
|
2016-06-06 00:52:06 -04:00
|
|
|
context 'when using two-factor authentication via OTP' do
|
2016-04-06 06:26:10 -04:00
|
|
|
let(:user) { create(:user, :two_factor) }
|
|
|
|
|
2020-11-13 10:09:24 -05:00
|
|
|
def authenticate_2fa(otp_user_id: user.id, **user_params)
|
2020-09-02 11:10:54 -04:00
|
|
|
post(:create, params: { user: user_params }, session: { otp_user_id: otp_user_id })
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
|
2016-05-30 22:17:26 -04:00
|
|
|
context 'remember_me field' do
|
|
|
|
it 'sets a remember_user_token cookie when enabled' do
|
|
|
|
allow(controller).to receive(:find_user).and_return(user)
|
2017-06-21 09:48:12 -04:00
|
|
|
expect(controller)
|
|
|
|
.to receive(:remember_me).with(user).and_call_original
|
2016-05-30 22:17:26 -04:00
|
|
|
|
|
|
|
authenticate_2fa(remember_me: '1', otp_attempt: user.current_otp)
|
|
|
|
|
|
|
|
expect(response.cookies['remember_user_token']).to be_present
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does nothing when disabled' do
|
|
|
|
allow(controller).to receive(:find_user).and_return(user)
|
|
|
|
expect(controller).not_to receive(:remember_me)
|
|
|
|
|
|
|
|
authenticate_2fa(remember_me: '0', otp_attempt: user.current_otp)
|
|
|
|
|
|
|
|
expect(response.cookies['remember_user_token']).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-12 09:45:55 -05:00
|
|
|
context 'with password authentication disabled' do
|
|
|
|
before do
|
|
|
|
stub_application_setting(password_authentication_enabled_for_web: false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows 2FA stage of non-password login' do
|
|
|
|
authenticate_2fa(otp_attempt: user.current_otp)
|
|
|
|
|
|
|
|
expect(@request.env['warden']).to be_authenticated
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-09-02 11:10:54 -04:00
|
|
|
# See issue gitlab-org/gitlab#20302.
|
|
|
|
context 'when otp_user_id is stale' do
|
|
|
|
render_views
|
|
|
|
|
|
|
|
it 'favors login over otp_user_id when password is present and does not authenticate the user' do
|
|
|
|
authenticate_2fa(
|
2020-11-13 10:09:24 -05:00
|
|
|
login: 'random_username',
|
|
|
|
password: user.password,
|
2020-09-02 11:10:54 -04:00
|
|
|
otp_user_id: user.id
|
|
|
|
)
|
|
|
|
|
2021-04-22 17:09:53 -04:00
|
|
|
expect(controller).to set_flash.now[:alert].to(/Invalid login or password/)
|
2020-09-02 11:10:54 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-06 06:26:10 -04:00
|
|
|
##
|
2020-09-02 11:10:54 -04:00
|
|
|
# See issue gitlab-org/gitlab-foss#14900
|
2016-04-06 06:26:10 -04:00
|
|
|
#
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when authenticating with login and OTP of another user' do
|
|
|
|
context 'when another user has 2FA enabled' do
|
|
|
|
let(:another_user) { create(:user, :two_factor) }
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when OTP is valid for another user' do
|
|
|
|
it 'does not authenticate' do
|
|
|
|
authenticate_2fa(login: another_user.username,
|
|
|
|
otp_attempt: another_user.current_otp)
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-05-23 19:37:59 -04:00
|
|
|
expect(subject.current_user).not_to eq another_user
|
2016-04-07 05:19:29 -04:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when OTP is invalid for another user' do
|
|
|
|
it 'does not authenticate' do
|
|
|
|
authenticate_2fa(login: another_user.username,
|
|
|
|
otp_attempt: 'invalid')
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-05-23 19:37:59 -04:00
|
|
|
expect(subject.current_user).not_to eq another_user
|
2016-04-07 05:19:29 -04:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when authenticating with OTP' do
|
|
|
|
context 'when OTP is valid' do
|
|
|
|
it 'authenticates correctly' do
|
|
|
|
authenticate_2fa(otp_attempt: user.current_otp)
|
|
|
|
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-04-07 05:45:04 -04:00
|
|
|
context 'when OTP is invalid' do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
authenticate_2fa(otp_attempt: 'invalid')
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
it 'does not authenticate' do
|
2016-05-23 19:37:59 -04:00
|
|
|
expect(subject.current_user).not_to eq user
|
2016-04-07 05:19:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about invalid OTP code' do
|
2021-04-22 17:09:53 -04:00
|
|
|
expect(controller).to set_flash.now[:alert]
|
2020-11-13 10:09:24 -05:00
|
|
|
.to(/Invalid two-factor code/)
|
2016-04-07 05:19:29 -04:00
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-09-02 09:30:19 -04:00
|
|
|
context 'when the user is on their last attempt' do
|
|
|
|
before do
|
|
|
|
user.update(failed_attempts: User.maximum_attempts.pred)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when OTP is valid' do
|
|
|
|
it 'authenticates correctly' do
|
|
|
|
authenticate_2fa(otp_attempt: user.current_otp)
|
|
|
|
|
|
|
|
expect(subject.current_user).to eq user
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when OTP is invalid' do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
authenticate_2fa(otp_attempt: 'invalid')
|
|
|
|
end
|
2016-09-02 09:30:19 -04:00
|
|
|
|
|
|
|
it 'does not authenticate' do
|
|
|
|
expect(subject.current_user).not_to eq user
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about invalid login' do
|
2020-07-29 14:09:50 -04:00
|
|
|
expect(flash[:alert]).to eq('Your account is locked.')
|
2016-09-02 09:30:19 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'locks the user' do
|
|
|
|
expect(user.reload).to be_access_locked
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'keeps the user locked on future login attempts' do
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: { login: user.username, password: user.password } })
|
2016-09-02 09:30:19 -04:00
|
|
|
|
2020-07-29 14:09:50 -04:00
|
|
|
expect(flash[:alert]).to eq('Your account is locked.')
|
2016-09-02 09:30:19 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
|
|
|
|
it "creates an audit log record" do
|
2020-08-21 05:10:08 -04:00
|
|
|
expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { AuditEvent.count }.by(1)
|
|
|
|
expect(AuditEvent.last.details[:with]).to eq("two-factor")
|
2016-06-06 00:52:06 -04:00
|
|
|
end
|
2020-09-18 02:09:31 -04:00
|
|
|
|
|
|
|
it "creates an authentication event record" do
|
|
|
|
expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { AuthenticationEvent.count }.by(1)
|
|
|
|
expect(AuthenticationEvent.last.provider).to eq("two-factor")
|
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using two-factor authentication via U2F device' do
|
|
|
|
let(:user) { create(:user, :two_factor) }
|
|
|
|
|
|
|
|
def authenticate_2fa_u2f(user_params)
|
2018-12-17 17:52:17 -05:00
|
|
|
post(:create, params: { user: user_params }, session: { otp_user_id: user.id })
|
2016-06-06 00:52:06 -04:00
|
|
|
end
|
|
|
|
|
2020-09-07 08:08:27 -04:00
|
|
|
before do
|
|
|
|
stub_feature_flags(webauthn: false)
|
|
|
|
end
|
|
|
|
|
2016-08-19 21:51:56 -04:00
|
|
|
context 'remember_me field' do
|
|
|
|
it 'sets a remember_user_token cookie when enabled' do
|
|
|
|
allow(U2fRegistration).to receive(:authenticate).and_return(true)
|
|
|
|
allow(controller).to receive(:find_user).and_return(user)
|
2017-06-21 09:48:12 -04:00
|
|
|
expect(controller)
|
|
|
|
.to receive(:remember_me).with(user).and_call_original
|
2016-08-19 21:51:56 -04:00
|
|
|
|
|
|
|
authenticate_2fa_u2f(remember_me: '1', login: user.username, device_response: "{}")
|
|
|
|
|
|
|
|
expect(response.cookies['remember_user_token']).to be_present
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does nothing when disabled' do
|
|
|
|
allow(U2fRegistration).to receive(:authenticate).and_return(true)
|
|
|
|
allow(controller).to receive(:find_user).and_return(user)
|
|
|
|
expect(controller).not_to receive(:remember_me)
|
|
|
|
|
|
|
|
authenticate_2fa_u2f(remember_me: '0', login: user.username, device_response: "{}")
|
|
|
|
|
|
|
|
expect(response.cookies['remember_user_token']).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-06-06 00:52:06 -04:00
|
|
|
it "creates an audit log record" do
|
|
|
|
allow(U2fRegistration).to receive(:authenticate).and_return(true)
|
2020-08-21 05:10:08 -04:00
|
|
|
expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { AuditEvent.count }.by(1)
|
|
|
|
expect(AuditEvent.last.details[:with]).to eq("two-factor-via-u2f-device")
|
2016-06-06 00:52:06 -04:00
|
|
|
end
|
2020-09-18 02:09:31 -04:00
|
|
|
|
|
|
|
it "creates an authentication event record" do
|
|
|
|
allow(U2fRegistration).to receive(:authenticate).and_return(true)
|
|
|
|
|
|
|
|
expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { AuthenticationEvent.count }.by(1)
|
|
|
|
expect(AuthenticationEvent.last.provider).to eq("two-factor-via-u2f-device")
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
2017-04-02 11:54:19 -04:00
|
|
|
|
2019-07-18 04:27:02 -04:00
|
|
|
context 'when login fails' do
|
|
|
|
before do
|
|
|
|
@request.env["warden.options"] = { action: 'unauthenticated' }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does increment failed login counts for session' do
|
|
|
|
get(:new, params: { user: { login: 'failed' } })
|
|
|
|
|
|
|
|
expect(session[:failed_login_attempts]).to eq(1)
|
|
|
|
end
|
|
|
|
end
|
2020-03-11 14:09:23 -04:00
|
|
|
|
|
|
|
describe '#set_current_context' do
|
2020-03-17 20:09:16 -04:00
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
|
2020-03-11 14:09:23 -04:00
|
|
|
context 'when signed in' do
|
|
|
|
before do
|
|
|
|
sign_in(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the username and caller_id in the context' do
|
|
|
|
expect(controller).to receive(:destroy).and_wrap_original do |m, *args|
|
2021-03-16 17:11:53 -04:00
|
|
|
expect(Gitlab::ApplicationContext.current)
|
2020-03-11 14:09:23 -04:00
|
|
|
.to include('meta.user' => user.username,
|
|
|
|
'meta.caller_id' => 'SessionsController#destroy')
|
|
|
|
|
|
|
|
m.call(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
delete :destroy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when not signed in' do
|
|
|
|
it 'sets the caller_id in the context' do
|
|
|
|
expect(controller).to receive(:new).and_wrap_original do |m, *args|
|
2021-03-16 17:11:53 -04:00
|
|
|
expect(Gitlab::ApplicationContext.current)
|
2020-03-11 14:09:23 -04:00
|
|
|
.to include('meta.caller_id' => 'SessionsController#new')
|
2021-03-16 17:11:53 -04:00
|
|
|
expect(Gitlab::ApplicationContext.current)
|
2020-03-11 14:09:23 -04:00
|
|
|
.not_to include('meta.user')
|
|
|
|
|
|
|
|
m.call(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
get :new
|
|
|
|
end
|
|
|
|
end
|
2020-03-17 20:09:16 -04:00
|
|
|
|
|
|
|
context 'when the user becomes locked' do
|
|
|
|
before do
|
|
|
|
user.update!(failed_attempts: User.maximum_attempts.pred)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the caller_id in the context' do
|
|
|
|
allow_any_instance_of(User).to receive(:lock_access!).and_wrap_original do |m, *args|
|
2021-03-16 17:11:53 -04:00
|
|
|
expect(Gitlab::ApplicationContext.current)
|
2020-03-17 20:09:16 -04:00
|
|
|
.to include('meta.caller_id' => 'SessionsController#create')
|
2021-03-16 17:11:53 -04:00
|
|
|
expect(Gitlab::ApplicationContext.current)
|
2020-03-17 20:09:16 -04:00
|
|
|
.not_to include('meta.user')
|
|
|
|
|
|
|
|
m.call(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
post(:create,
|
|
|
|
params: { user: { login: user.username, password: user.password.succ } })
|
|
|
|
end
|
|
|
|
end
|
2020-03-11 14:09:23 -04:00
|
|
|
end
|
2020-09-01 05:10:28 -04:00
|
|
|
|
|
|
|
describe '#destroy' do
|
|
|
|
before do
|
|
|
|
sign_in(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for a user whose password has expired' do
|
|
|
|
let(:user) { create(:user, password_expires_at: 2.days.ago) }
|
|
|
|
|
|
|
|
it 'allows to sign out successfully' do
|
|
|
|
delete :destroy
|
|
|
|
|
|
|
|
expect(response).to redirect_to(new_user_session_path)
|
|
|
|
expect(controller.current_user).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|