2021-06-01 12:20:20 +00:00
|
|
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
2017-04-19 10:09:54 +00:00
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
RSpec.describe Auth do
|
2021-08-16 06:49:32 +00:00
|
|
|
let(:password) { 'zammad' }
|
|
|
|
let(:user) { create(:user, password: password) }
|
|
|
|
let(:instance) { described_class.new(user.login, password) }
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
describe '.valid?' do
|
|
|
|
it 'responds to valid?' do
|
|
|
|
expect(instance).to respond_to(:valid?)
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with an internal user' do
|
|
|
|
context 'with valid credentials' do
|
|
|
|
it 'check for valid credentials' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'check for not increased failed login count' do
|
|
|
|
expect { instance.valid? }.not_to change { user.reload.login_failed }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when not case-sensitive' do
|
|
|
|
let(:instance) { described_class.new(user.login.upcase, password) }
|
|
|
|
|
|
|
|
it 'returns true' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when email is used' do
|
|
|
|
let(:instance) { described_class.new(user.email, password) }
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
it 'check for valid credentials' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'when previous login was' do
|
|
|
|
context 'when never logged in' do
|
|
|
|
it 'updates #last_login and #updated_at' do
|
|
|
|
expect { instance.valid? }.to change { user.reload.last_login }.and change { user.reload.updated_at }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when less than 10 minutes ago' do
|
|
|
|
before do
|
|
|
|
instance.valid?
|
|
|
|
travel 9.minutes
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not update #last_login and #updated_at' do
|
|
|
|
expect { instance.valid? }.to not_change { user.reload.last_login }.and not_change { user.reload.updated_at }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when more than 10 minutes ago' do
|
|
|
|
before do
|
|
|
|
instance.valid?
|
|
|
|
travel 11.minutes
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'updates #last_login and #updated_at' do
|
|
|
|
expect { instance.valid? }.to change { user.reload.last_login }.and change { user.reload.updated_at }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with valid user and invalid password' do
|
|
|
|
let(:instance) { described_class.new(user.login, 'wrong') }
|
|
|
|
|
|
|
|
it 'check for invalid credentials' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'check for increased failed login count' do
|
|
|
|
expect { instance.valid? }.to change { user.reload.login_failed }.from(0).to(1)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'failed login avoids brute force attack' do
|
|
|
|
allow(instance).to receive(:sleep)
|
|
|
|
instance.valid?
|
|
|
|
expect(instance).to have_received(:sleep).with(1)
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with inactive user login' do
|
|
|
|
let(:user) { create(:user, active: false) }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
2020-08-28 14:07:30 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with non-existent user login' do
|
|
|
|
let(:instance) { described_class.new('not_existing', password) }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
2020-08-28 14:07:30 +00:00
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with empty user login' do
|
|
|
|
let(:instance) { described_class.new('', password) }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
2020-08-28 14:07:30 +00:00
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'when password is empty' do
|
|
|
|
before do
|
|
|
|
# Remove adapter from auth developer setting, to avoid execution for this test case, because of special empty
|
|
|
|
# password handling in adapter.
|
|
|
|
Setting.set('auth_developer', {})
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with empty password string' do
|
|
|
|
let(:password) { '' }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'check empty password' do
|
|
|
|
context 'when password is an empty string' do
|
|
|
|
let(:password) { '' }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'when password is nil' do
|
|
|
|
let(:password) { nil }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with empty password string when the stored password is an empty string' do
|
|
|
|
before { user.update_column(:password, '') }
|
|
|
|
|
|
|
|
include_examples 'check empty password'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with empty password string when the stored hash represents an empty string' do
|
|
|
|
before { user.update(password: PasswordHash.crypt('')) }
|
|
|
|
|
|
|
|
include_examples 'check empty password'
|
|
|
|
end
|
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with a ldap user' do
|
|
|
|
let(:password_ldap) { 'zammad_ldap' }
|
|
|
|
let(:ldap_user) { instance_double(Ldap::User) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
Setting.set('ldap_integration', true)
|
|
|
|
|
|
|
|
allow(Ldap::User).to receive(:new).with(any_args).and_return(ldap_user)
|
|
|
|
end
|
|
|
|
|
2021-10-27 18:10:17 +00:00
|
|
|
shared_examples 'check empty password' do
|
|
|
|
before do
|
|
|
|
# Remove adapter from auth developer setting, to avoid execution for this test case, because of special empty
|
|
|
|
# password handling in adapter.
|
|
|
|
Setting.set('auth_developer', {})
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with empty password string' do
|
|
|
|
let(:password) { '' }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when password is nil' do
|
|
|
|
let(:password) { nil }
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-16 06:49:32 +00:00
|
|
|
context 'with a ldap user without internal password' do
|
|
|
|
let(:user) { create(:user, source: 'Ldap') }
|
|
|
|
let(:password) { password_ldap }
|
|
|
|
|
|
|
|
context 'with valid credentials' do
|
|
|
|
before do
|
|
|
|
allow(ldap_user).to receive(:valid?).with(any_args).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with invalid credentials' do
|
|
|
|
let(:password) { 'wrong' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(ldap_user).to receive(:valid?).with(any_args).and_return(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'check for not increased failed login count' do
|
|
|
|
expect { instance.valid? }.not_to change { user.reload.login_failed }
|
|
|
|
end
|
|
|
|
end
|
2021-10-27 18:10:17 +00:00
|
|
|
|
|
|
|
include_examples 'check empty password'
|
2021-08-16 06:49:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a ldap user which also has a internal password' do
|
|
|
|
let(:user) { create(:user, source: 'Ldap', password: password) }
|
|
|
|
let(:password) { password_ldap }
|
|
|
|
|
|
|
|
context 'with valid ldap credentials' do
|
|
|
|
before do
|
|
|
|
allow(ldap_user).to receive(:valid?).with(any_args).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with invalid ldap credentials' do
|
|
|
|
let(:instance) { described_class.new(user.login, 'wrong') }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(ldap_user).to receive(:valid?).with(any_args).and_return(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns false' do
|
|
|
|
expect(instance.valid?).to be false
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'check for not increased failed login count' do
|
|
|
|
expect { instance.valid? }.to change { user.reload.login_failed }.from(0).to(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with valid internal credentials' do
|
|
|
|
before do
|
|
|
|
allow(ldap_user).to receive(:valid?).with(any_args).and_return(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns true' do
|
|
|
|
expect(instance.valid?).to be true
|
|
|
|
end
|
|
|
|
end
|
2021-10-27 18:10:17 +00:00
|
|
|
|
|
|
|
include_examples 'check empty password'
|
2021-08-16 06:49:32 +00:00
|
|
|
end
|
2017-04-19 10:09:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|