describe SuperAdmin, type: :model do describe '#invite_admin' do let(:super_admin) { create :super_admin } let(:valid_email) { 'paul@tps.fr' } subject { super_admin.invite_admin(valid_email) } it { user = subject expect(user.errors).to be_empty expect(user).to be_persisted } it { expect(super_admin.invite_admin(nil).errors).not_to be_empty } it { expect(super_admin.invite_admin('toto').errors).not_to be_empty } it 'creates a corresponding user account for the email' do subject user = User.find_by(email: valid_email) expect(user).to be_present end it 'creates a corresponding instructeur account for the email' do subject instructeur = Instructeur.by_email(valid_email) expect(instructeur).to be_present end context 'when there already is a user account with the same email' do before { create(:user, email: valid_email) } it 'still creates an admin account' do expect(subject.errors).to be_empty expect(subject).to be_persisted end end end describe 'enable_otp!' do let(:super_admin) { create(:super_admin, otp_required_for_login: false) } let(:subject) { super_admin.enable_otp! } it 'updates otp_required_for_login' do expect { subject }.to change { super_admin.otp_required_for_login? }.from(false).to(true) end it 'updates otp_secret' do expect { subject }.to change { super_admin.otp_secret } end end describe 'disable_otp!' do let(:super_admin) { create(:super_admin, otp_required_for_login: true) } let(:subject) { super_admin.disable_otp! } it 'updates otp_required_for_login' do expect { subject }.to change { super_admin.otp_required_for_login? }.from(true).to(false) end it 'nullifies otp_secret' do super_admin.enable_otp! expect { subject }.to change { super_admin.reload.otp_secret }.to(nil) end end describe '#password_complexity' do # This password list is sorted by password complexity, according to zxcvbn (used for complexity evaluation) # 0 - too guessable: risky password. (guesses < 10^3) # 1 - very guessable: protection from throttled online attacks. (guesses < 10^6) # 2 - somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8) # 3 - safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10) # 4 - very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10) passwords = ['password', '12pass23', 'démarches ', 'démarches-simple', '{My-$3cure-p4ssWord}'] min_complexity = PASSWORD_COMPLEXITY_FOR_ADMIN let(:email) { 'mail@beta.gouv.fr' } let(:super_admin) { build(:super_admin, email: email, password: password) } subject do super_admin.valid? super_admin.errors.full_messages end context 'when the password is too short' do let(:password) { 's' * (PASSWORD_MIN_LENGTH - 1) } it 'reports an error about password length (but not about complexity)' do expect(subject).to eq(["Le champ « Mot de passe » est trop court. Saisir un mot de passe avec au moins 8 caractères"]) end end passwords[0..(min_complexity - 1)].each do |simple_password| context 'when the password is long enough, but too simple' do let(:password) { simple_password } it { expect(subject).to eq(["Le champ « Mot de passe » n’est pas assez complexe. Saisir un mot de passe plus complexe"]) } end end context 'when the password is long and complex' do let(:password) { passwords[min_complexity] } it { expect(subject).to be_empty } end end end