Skip to content

Commit

Permalink
Déconnecter les agents et usagers après un temps d'inactivité (#4241)
Browse files Browse the repository at this point in the history
* Ne plus utiliser le module :rememberable de Devise

* Déconnecter les agents soient 14 jours après la dernière visite

* Prendre des exemples de test plus explicites

* Déconnecter les usagers 30 minutes après la dernière visite

* Utiliser un critère plus précis pour déterminer qu'on est déconnecté

* Move spec to the right directory

* Move specs to account/ subdirectory

* Déplacer la méthode `timeout_in` près de `devise`

* Correction d'un titre de spec où j'anticipais trop
  • Loading branch information
francois-ferrandis committed Apr 29, 2024
1 parent 9ff8e47 commit 7ff66c3
Show file tree
Hide file tree
Showing 15 changed files with 75 additions and 19 deletions.
12 changes: 6 additions & 6 deletions app/models/agent.rb
@@ -1,6 +1,8 @@
class SoftDeleteError < StandardError; end

class Agent < ApplicationRecord
self.ignored_columns = [:remember_created_at]

# Mixins
has_paper_trail(
only: %w[email first_name last_name starts_at invitation_sent_at invitation_accepted_at]
Expand All @@ -26,8 +28,10 @@ def self.search_options
}
end

devise :invitable, :database_authenticatable, :trackable,
:recoverable, :rememberable, :validatable, :confirmable, :async, validate_on_invite: true
devise :invitable, :database_authenticatable, :trackable, :timeoutable,
:recoverable, :validatable, :confirmable, :async, validate_on_invite: true

def timeout_in = 14.days # Used by Devise's :timeoutable

# HACK : Ces accesseurs permettent d'utiliser Devise::Models::Trackable mais sans persister les valeurs en base
attr_accessor :current_sign_in_ip, :last_sign_in_ip, :sign_in_count, :current_sign_in_at
Expand Down Expand Up @@ -121,10 +125,6 @@ def confreres
Agent.in_any_of_these_services(services)
end

def remember_me # Override from Devise::rememberable to enable it by default
super.nil? ? true : super
end

def reverse_full_name_and_service
services.present? ? "#{reverse_full_name} (#{services_short_names})" : full_name
end
Expand Down
12 changes: 6 additions & 6 deletions app/models/user.rb
@@ -1,4 +1,6 @@
class User < ApplicationRecord
self.ignored_columns = [:remember_created_at]

# Mixins
has_paper_trail(
only: %w[
Expand All @@ -8,8 +10,10 @@ class User < ApplicationRecord
]
)

devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable, :async
devise :invitable, :database_authenticatable, :registerable, :timeoutable,
:recoverable, :validatable, :confirmable, :async

def timeout_in = 30.minutes # Used by Devise's :timeoutable

include PgSearch::Model
include FullNameConcern
Expand Down Expand Up @@ -81,10 +85,6 @@ def self.search_options

## -

def remember_me # Override from Devise::rememberable to enable it by default
super.nil? ? true : super
end

def to_s
full_name
end
Expand Down
4 changes: 0 additions & 4 deletions app/views/common/_session_form.html.slim
Expand Up @@ -10,9 +10,5 @@

.mt-1.mb-3= link_to "Mot de passe oublié ?", new_password_path(resource_name)

.form-group.mb-3
.custom-control.custom-checkbox
= f.check_box :remember_me, class: "custom-control-input"
= f.label :remember_me, t("devise.sessions.new.remember_me"),class: "custom-control-label"
.form-group.mb-0.text-center
= f.submit "Se connecter", class: "btn btn-primary"
2 changes: 2 additions & 0 deletions config/application.rb
Expand Up @@ -58,6 +58,8 @@ class Application < Rails::Application

config.x.redis_namespace = "app"

# Avec cette configuration, on crée un cookie sans date d'expiration.
# L'expiration est gérée avec le module Timeoutable de Devise.
config.session_store :cookie_store, key: "_rdv_sp_session"

# Devise layout
Expand Down
3 changes: 0 additions & 3 deletions config/locales/views/devise.fr.yml
Expand Up @@ -4,6 +4,3 @@ fr:
new:
email: Email
password: Mot de passe
sessions:
new:
remember_me: Se souvenir de moi
33 changes: 33 additions & 0 deletions spec/features/agents/account/agent_session_expires_spec.rb
@@ -0,0 +1,33 @@
RSpec.describe "Agent session expiration" do
let(:password) { Faker::Internet.password(min_length: 12) }
let!(:agent) { create(:agent, password: password, password_confirmation: password) }

def expect_to_be_logged_in
visit agents_preferences_path
expect(page).to have_content("Préférences de notifications")
end

def expect_to_be_logged_out
visit agents_preferences_path
expect(page).to have_content("Entrez votre email et votre mot de passe")
end

it "is done 14 days after last visit" do
login_time = Time.zone.parse("2024-01-01 12:00")
travel_to(login_time)
visit new_agent_session_path
fill_in "Email", with: agent.email
fill_in "password", with: password
click_on "Se connecter"
expect_to_be_logged_in

travel_to(Time.zone.parse("2024-01-10 12:00")) # 10 days after last visit
expect_to_be_logged_in

travel_to(Time.zone.parse("2024-01-24 11:55")) # almost 14 days after last visit
expect_to_be_logged_in

travel_to(Time.zone.parse("2024-02-07 12:00")) # 14 days and 5 minutes after last visit
expect_to_be_logged_out
end
end
File renamed without changes.
28 changes: 28 additions & 0 deletions spec/features/users/account/user_session_expires_spec.rb
@@ -0,0 +1,28 @@
RSpec.describe "Agent session expiration" do
let(:password) { Faker::Internet.password(min_length: 12) }
let!(:user) { create(:user, password: password, password_confirmation: password) }

def expect_to_be_logged_in
visit users_informations_path
expect(page).to have_content("Mes informations")
end

def expect_to_be_logged_out
visit users_informations_path
expect(page).to have_content("Entrez votre email et votre mot de passe")
end

it "is done 30 minutes after last visit" do
visit new_user_session_path
fill_in "Email", with: user.email
fill_in "password", with: password
click_on "Se connecter"
expect_to_be_logged_in

travel_to(28.minutes.from_now)
expect_to_be_logged_in

travel_to(31.minutes.from_now)
expect_to_be_logged_out
end
end

0 comments on commit 7ff66c3

Please sign in to comment.