2016-04-06 06:26:10 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
describe SessionsController do
|
|
|
|
describe '#create' do
|
|
|
|
before do
|
|
|
|
@request.env['devise.mapping'] = Devise.mappings[:user]
|
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when using standard authentications' do
|
2016-04-06 06:26:10 -04:00
|
|
|
context 'invalid password' do
|
|
|
|
it 'does not authenticate user' do
|
|
|
|
post(:create, user: { login: 'invalid', password: 'invalid' })
|
|
|
|
|
2017-02-21 18:29:35 -05:00
|
|
|
expect(response).
|
|
|
|
to set_flash.now[:alert].to /Invalid Login or password/
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when using valid password' do
|
2016-04-06 06:26:10 -04:00
|
|
|
let(:user) { create(:user) }
|
|
|
|
|
|
|
|
it 'authenticates user correctly' do
|
|
|
|
post(:create, user: { login: user.username, password: user.password })
|
|
|
|
|
|
|
|
expect(subject.current_user). to eq user
|
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
|
|
|
|
it "creates an audit log record" do
|
|
|
|
expect { post(:create, user: { login: user.username, password: user.password }) }.to change { SecurityEvent.count }.by(1)
|
|
|
|
expect(SecurityEvent.last.details[:with]).to eq("standard")
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
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) }
|
|
|
|
|
|
|
|
def authenticate_2fa(user_params)
|
|
|
|
post(:create, { user: user_params }, { otp_user_id: user.id })
|
|
|
|
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)
|
|
|
|
expect(controller).
|
|
|
|
to receive(:remember_me).with(user).and_call_original
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2016-04-06 06:26:10 -04:00
|
|
|
##
|
|
|
|
# See #14900 issue
|
|
|
|
#
|
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
|
2016-04-07 05:19:29 -04:00
|
|
|
before { authenticate_2fa(otp_attempt: 'invalid') }
|
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
|
2017-02-21 18:29:35 -05:00
|
|
|
expect(response).to set_flash.now[:alert].
|
|
|
|
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
|
|
|
|
before { authenticate_2fa(otp_attempt: 'invalid') }
|
|
|
|
|
|
|
|
it 'does not authenticate' do
|
|
|
|
expect(subject.current_user).not_to eq user
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'warns about invalid login' do
|
2017-02-21 18:29:35 -05:00
|
|
|
expect(response).to set_flash.now[:alert].
|
|
|
|
to /Invalid Login or password/
|
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
|
|
|
|
post(:create, user: { login: user.username, password: user.password })
|
|
|
|
|
2017-02-21 18:29:35 -05:00
|
|
|
expect(response).
|
|
|
|
to set_flash.now[:alert].to /Invalid Login or password/
|
2016-09-02 09:30:19 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
context 'when another user does not have 2FA enabled' do
|
|
|
|
let(:another_user) { create(:user) }
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2016-04-07 05:19:29 -04:00
|
|
|
it 'does not leak that 2FA is disabled for another user' do
|
|
|
|
authenticate_2fa(login: another_user.username,
|
|
|
|
otp_attempt: 'invalid')
|
2016-04-06 06:26:10 -04:00
|
|
|
|
2017-02-21 18:29:35 -05:00
|
|
|
expect(response).to set_flash.now[:alert].
|
|
|
|
to /Invalid two-factor code/
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-06-06 00:52:06 -04:00
|
|
|
|
|
|
|
it "creates an audit log record" do
|
|
|
|
expect { authenticate_2fa(login: user.username, otp_attempt: user.current_otp) }.to change { SecurityEvent.count }.by(1)
|
|
|
|
expect(SecurityEvent.last.details[:with]).to eq("two-factor")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using two-factor authentication via U2F device' do
|
|
|
|
let(:user) { create(:user, :two_factor) }
|
|
|
|
|
|
|
|
def authenticate_2fa_u2f(user_params)
|
|
|
|
post(:create, { user: user_params }, { otp_user_id: user.id })
|
|
|
|
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)
|
|
|
|
expect(controller).
|
|
|
|
to receive(:remember_me).with(user).and_call_original
|
|
|
|
|
|
|
|
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)
|
|
|
|
expect { authenticate_2fa_u2f(login: user.username, device_response: "{}") }.to change { SecurityEvent.count }.by(1)
|
|
|
|
expect(SecurityEvent.last.details[:with]).to eq("two-factor-via-u2f-device")
|
|
|
|
end
|
2016-04-06 06:26:10 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|