Session: add trusted_device cookie

This commit is contained in:
simon lehericey 2018-10-30 18:24:29 +01:00
parent 5690599289
commit 0d8d2de5a6
7 changed files with 133 additions and 8 deletions

View file

@ -1,12 +1,17 @@
require 'zxcvbn'
class Administrateurs::ActivateController < ApplicationController
include TrustedDeviceConcern
layout "new_application"
def new
@administrateur = Administrateur.find_inactive_by_token(params[:token])
if !@administrateur
if @administrateur
# the administrateur activates its account from an email
trust_device
else
flash.alert = "Le lien de validation d'administrateur a expiré, #{helpers.contact_link('contactez-nous', tags: 'lien expiré')} pour obtenir un nouveau lien."
redirect_to root_path
end

View file

@ -1,10 +1,15 @@
class Gestionnaires::ActivateController < ApplicationController
include TrustedDeviceConcern
layout "new_application"
def new
@gestionnaire = Gestionnaire.with_reset_password_token(params[:token])
if !@gestionnaire
if @gestionnaire
# the gestionnaire activates its account from an email
trust_device
else
flash.alert = "Le lien de validation du compte instructeur a expiré, #{helpers.contact_link('contactez-nous', tags: 'lien expiré')} pour obtenir un nouveau lien."
redirect_to root_path
end

View file

@ -1,4 +1,7 @@
class Users::SessionsController < Sessions::SessionsController
include TrustedDeviceConcern
include ActionView::Helpers::DateHelper
layout "new_application"
# GET /resource/sign_in
@ -24,14 +27,17 @@ class Users::SessionsController < Sessions::SessionsController
end
if gestionnaire_signed_in?
gestionnaire = current_gestionnaire
if trusted_device?
redirect_to gestionnaire_procedures_path
else
gestionnaire = current_gestionnaire
login_token = gestionnaire.login_token!
GestionnaireMailer.send_login_token(gestionnaire, login_token).deliver_later
login_token = gestionnaire.login_token!
GestionnaireMailer.send_login_token(gestionnaire, login_token).deliver_later
[:user, :gestionnaire, :administrateur].each { |role| sign_out(role) }
[:user, :gestionnaire, :administrateur].each { |role| sign_out(role) }
redirect_to link_sent_path(email: gestionnaire.email)
redirect_to link_sent_path(email: gestionnaire.email)
end
elsif user_signed_in?
redirect_to after_sign_in_path_for(:user)
else
@ -79,6 +85,9 @@ class Users::SessionsController < Sessions::SessionsController
def sign_in_by_link
gestionnaire = Gestionnaire.find(params[:id])
if gestionnaire&.login_token_valid?(params[:jeton])
trust_device
flash.notice = "Merci davoir confirmé votre connexion. Votre navigateur est maintenant authentifié pour #{TRUSTED_DEVICE_PERIOD.to_i / ActiveSupport::Duration::SECONDS_PER_DAY} jours."
user = User.find_by(email: gestionnaire.email)
administrateur = Administrateur.find_by(email: gestionnaire.email)
[user, gestionnaire, administrateur].compact.each { |resource| sign_in(resource) }

View file

@ -0,0 +1,25 @@
module TrustedDeviceConcern
extend ActiveSupport::Concern
TRUSTED_DEVICE_COOKIE_NAME = :trusted_device
TRUSTED_DEVICE_PERIOD = 1.month
def trust_device
cookies.encrypted[TRUSTED_DEVICE_COOKIE_NAME] = {
value: JSON.generate({ created_at: Time.zone.now }),
expires: TRUSTED_DEVICE_PERIOD,
httponly: true
}
end
def trusted_device?
trusted_device_cookie.present? &&
Time.zone.now - TRUSTED_DEVICE_PERIOD < JSON.parse(trusted_device_cookie)['created_at']
end
private
def trusted_device_cookie
cookies.encrypted[TRUSTED_DEVICE_COOKIE_NAME]
end
end

View file

@ -0,0 +1,20 @@
describe Administrateurs::ActivateController, type: :controller do
describe '#new' do
let(:admin) { create(:administrateur) }
let(:token) { admin.send(:set_reset_password_token) }
before { allow(controller).to receive(:trust_device) }
context 'when the token is ok' do
before { get :new, params: { token: token } }
it { expect(controller).to have_received(:trust_device) }
end
context 'when the token is bad' do
before { get :new, params: { token: 'bad' } }
it { expect(controller).not_to have_received(:trust_device) }
end
end
end

View file

@ -0,0 +1,20 @@
describe Gestionnaires::ActivateController, type: :controller do
describe '#new' do
let(:gestionnaire) { create(:gestionnaire) }
let(:token) { gestionnaire.send(:set_reset_password_token) }
before { allow(controller).to receive(:trust_device) }
context 'when the token is ok' do
before { get :new, params: { token: token } }
it { expect(controller).to have_received(:trust_device) }
end
context 'when the token is bad' do
before { get :new, params: { token: 'bad' } }
it { expect(controller).not_to have_received(:trust_device) }
end
end
end

View file

@ -48,6 +48,20 @@ describe Users::SessionsController, type: :controller do
expect(subject.current_administrateur).to be(nil)
end
context 'when the device is trusted' do
before do
allow(controller).to receive(:trusted_device?).and_return(true)
post :create, params: { user: { email: gestionnaire.email, password: gestionnaire.password } }
end
it 'directly log the gestionnaire' do
expect(subject).to redirect_to gestionnaire_procedures_path
expect(subject.current_user).to be(nil)
expect(subject.current_gestionnaire).to eq(gestionnaire)
expect(subject.current_administrateur).to be(nil)
end
end
context 'signs administrateur in' do
# an admin has always an gestionnaire role
before { gestionnaire }
@ -237,6 +251,7 @@ describe Users::SessionsController, type: :controller do
context 'when the gestionnaire has non other account' do
let(:gestionnaire) { create(:gestionnaire) }
before do
allow(controller).to receive(:trust_device)
post :sign_in_by_link, params: { id: gestionnaire.id, login_token: login_token }
end
@ -245,6 +260,7 @@ describe Users::SessionsController, type: :controller do
it { is_expected.to redirect_to gestionnaire_procedures_path }
it { expect(controller.current_gestionnaire).to eq(gestionnaire) }
it { expect(controller).to have_received(:trust_device) }
end
context 'when the token is invalid' do
@ -252,6 +268,7 @@ describe Users::SessionsController, type: :controller do
it { is_expected.to redirect_to new_user_session_path }
it { expect(controller.current_gestionnaire).to be_nil }
it { expect(controller).not_to have_received(:trust_device) }
end
end
@ -276,4 +293,28 @@ describe Users::SessionsController, type: :controller do
end
end
end
describe '#trust_device and #trusted_device?' do
subject { controller.trusted_device? }
context 'when the trusted cookie is not present' do
it { is_expected.to be false }
end
context 'when the cookie is outdated' do
before do
Timecop.freeze(Time.zone.now - TrustedDeviceConcern::TRUSTED_DEVICE_PERIOD - 1.minute)
controller.trust_device
Timecop.return
end
it { is_expected.to be false }
end
context 'when the cookie is ok' do
before { controller.trust_device }
it { is_expected.to be true }
end
end
end