From d54a5c2ae0d761e6d6ea445fd04921cf38369bba Mon Sep 17 00:00:00 2001 From: taitus Date: Mon, 9 Oct 2023 09:54:42 +0200 Subject: [PATCH] Allow define maximum_attemps and unlock_in --- app/models/user.rb | 8 +++ config/initializers/devise.rb | 4 +- config/secrets.yml.example | 12 ++++ .../users/sessions_controller_spec.rb | 2 +- spec/models/user_spec.rb | 66 +++++++++++++++++++ 5 files changed, 89 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 13efdf965..b7aa66e3f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -425,6 +425,14 @@ class User < ApplicationRecord end end + def self.maximum_attempts + (Tenant.current_secrets.dig(:security, :lockable, :maximum_attempts) || 20).to_i + end + + def self.unlock_in + (Tenant.current_secrets.dig(:security, :lockable, :unlock_in) || 1).to_f.hours + end + private def clean_document_number diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 58493f5b2..d351f06ad 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -181,10 +181,10 @@ Devise.setup do |config| # Number of authentication tries before locking an account if lock_strategy # is failed attempts. - config.maximum_attempts = 20 + config.maximum_attempts = 20 # Overwritten in User model # Time interval to unlock the account if :time is enabled as unlock_strategy. - config.unlock_in = 1.hour + config.unlock_in = 1.hour # Overwritten in User model # Warn on the last attempt before the account is locked. config.last_attempt_warning = false diff --git a/config/secrets.yml.example b/config/secrets.yml.example index 7edfa57cd..7b61e7a15 100644 --- a/config/secrets.yml.example +++ b/config/secrets.yml.example @@ -23,6 +23,9 @@ development: security: last_sign_in: false password_complexity: false + # lockable: + # maximum_attempts: 20 + # unlock_in: 1 # In hours secret_key_base: 56792feef405a59b18ea7db57b4777e855103882b926413d4afdfb8c0ea8aa86ea6649da4e729c5f5ae324c0ab9338f789174cf48c544173bc18fdc3b14262e4 <<: *maps @@ -58,6 +61,9 @@ staging: security: last_sign_in: false password_complexity: false + # lockable: + # maximum_attempts: 20 + # unlock_in: 1 # In hours tenants: # If you've enabled multitenancy, you can overwrite secrets for a # specific tenant with: @@ -98,6 +104,9 @@ preproduction: security: last_sign_in: false password_complexity: false + # lockable: + # maximum_attempts: 20 + # unlock_in: 1 # In hours tenants: # If you've enabled multitenancy, you can overwrite secrets for a # specific tenant with: @@ -143,6 +152,9 @@ production: security: last_sign_in: false password_complexity: false + # lockable: + # maximum_attempts: 20 + # unlock_in: 1 # In hours tenants: # If you've enabled multitenancy, you can overwrite secrets for a # specific tenant with: diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb index 57fecedb6..29f0ace2d 100644 --- a/spec/controllers/users/sessions_controller_spec.rb +++ b/spec/controllers/users/sessions_controller_spec.rb @@ -8,7 +8,7 @@ describe Users::SessionsController do describe "Devise lock" do context "when devise sign in maximum_attempts reached", :with_frozen_time do it "locks the user account and sends an email to the account with an unlock link" do - user.update(failed_attempts: 19) + allow(User).to receive(:maximum_attempts).and_return(1) expect do post :create, params: { user: { login: "citizen@consul.org", password: "wrongpassword" }} diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2ff3a9cb5..ed2bf468e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -882,4 +882,70 @@ describe User do end end end + + describe ".maximum_attempts" do + it "returns 20 as default when the secrets aren't configured" do + expect(User.maximum_attempts).to eq 20 + end + + context "when secrets are configured" do + before do + allow(Rails.application).to receive(:secrets).and_return(ActiveSupport::OrderedOptions.new.merge( + security: { + lockable: { maximum_attempts: "14" } + }, + tenants: { + superstrict: { + security: { + lockable: { maximum_attempts: "1" } + } + } + } + )) + end + + it "uses the general secrets for the main tenant" do + expect(User.maximum_attempts).to eq 14 + end + + it "uses the tenant secrets for a tenant" do + allow(Tenant).to receive(:current_schema).and_return("superstrict") + + expect(User.maximum_attempts).to eq 1 + end + end + end + + describe ".unlock_in" do + it "returns 1 as default when the secrets aren't configured" do + expect(User.unlock_in).to eq 1.hour + end + + context "when secrets are configured" do + before do + allow(Rails.application).to receive(:secrets).and_return(ActiveSupport::OrderedOptions.new.merge( + security: { + lockable: { unlock_in: "2" } + }, + tenants: { + superstrict: { + security: { + lockable: { unlock_in: "50" } + } + } + } + )) + end + + it "uses the general secrets for the main tenant" do + expect(User.unlock_in).to eq 2.hours + end + + it "uses the tenant secrets for a tenant" do + allow(Tenant).to receive(:current_schema).and_return("superstrict") + + expect(User.unlock_in).to eq 50.hours + end + end + end end