Add password complexity

This commit is contained in:
taitus
2023-10-09 09:16:08 +02:00
parent fe9da7988f
commit 7c771b28b5
7 changed files with 120 additions and 8 deletions

View File

@@ -2,16 +2,33 @@
"use strict"; "use strict";
App.Managers = { App.Managers = {
generatePassword: function() { generatePassword: function() {
var chars, possible_chars; var pass, possible_chars, possible_digits, possible_symbols, password_complexity;
possible_chars = "aAbcdeEfghiJkmnpqrstuUvwxyz23456789"; password_complexity = $(".generate-random-value").data("password-complexity");
chars = Array.apply(null, { possible_chars = "abcdefghijklmnopqrstuvwxyz";
length: 12 possible_digits = "123456789";
possible_symbols = "-_.,;!?";
pass = Array.apply(null, {
length: 8
}).map(function() { }).map(function() {
var i; var i;
i = Math.floor(Math.random() * possible_chars.length); i = Math.floor(Math.random() * possible_chars.length);
return possible_chars.charAt(i); return possible_chars.charAt(i);
}); }).join("");
return chars.join("");
for (var i = 0; i < password_complexity.upper; i++) {
pass += possible_chars.charAt(Math.floor(Math.random() * possible_chars.length)).toUpperCase();
}
for (var i = 0; i < password_complexity.digit; i++) {
pass += possible_digits.charAt(Math.floor(Math.random() * possible_digits.length));
}
for (var i = 0; i < password_complexity.symbol; i++) {
pass += possible_symbols.charAt(Math.floor(Math.random() * possible_symbols.length));
}
return pass;
}, },
togglePassword: function(type) { togglePassword: function(type) {
$("#user_password").prop("type", type); $("#user_password").prop("type", type);

View File

@@ -416,6 +416,14 @@ class User < ApplicationRecord
update!(subscriptions_token: SecureRandom.base58(32)) if subscriptions_token.blank? update!(subscriptions_token: SecureRandom.base58(32)) if subscriptions_token.blank?
end end
def self.password_complexity
if Tenant.current_secrets.dig(:security, :password_complexity)
{ digit: 1, lower: 1, symbol: 0, upper: 1 }
else
{ digit: 0, lower: 0, symbol: 0, upper: 0 }
end
end
private private
def clean_document_number def clean_document_number

View File

@@ -12,7 +12,10 @@
</span> </span>
</div> </div>
<%= link_to t("management.account.edit.password.random"), "#", class: "generate-random-value float-right" %> <%= link_to t("management.account.edit.password.random"),
"#",
class: "generate-random-value float-right",
data: { "password-complexity": User.password_complexity } %>
</div> </div>
<%= f.submit t("management.account.edit.password.save"), class: "button success" %> <%= f.submit t("management.account.edit.password.save"), class: "button success" %>

View File

@@ -8,7 +8,7 @@ Devise.setup do |config|
# Need 1 char each of: A-Z, a-z, 0-9, and a punctuation mark or symbol # Need 1 char each of: A-Z, a-z, 0-9, and a punctuation mark or symbol
# You may use "digits" in place of "digit" and "symbols" in place of # You may use "digits" in place of "digit" and "symbols" in place of
# "symbol" based on your preference # "symbol" based on your preference
config.password_complexity = { digit: 0, lower: 0, symbol: 0, upper: 0 } config.password_complexity = { digit: 0, lower: 0, symbol: 0, upper: 0 } # Overwritten in User model
# How many passwords to keep in archive # How many passwords to keep in archive
# config.password_archiving_count = 5 # config.password_archiving_count = 5

View File

@@ -21,6 +21,7 @@ development:
multitenancy: false multitenancy: false
security: security:
last_sign_in: false last_sign_in: false
password_complexity: false
secret_key_base: 56792feef405a59b18ea7db57b4777e855103882b926413d4afdfb8c0ea8aa86ea6649da4e729c5f5ae324c0ab9338f789174cf48c544173bc18fdc3b14262e4 secret_key_base: 56792feef405a59b18ea7db57b4777e855103882b926413d4afdfb8c0ea8aa86ea6649da4e729c5f5ae324c0ab9338f789174cf48c544173bc18fdc3b14262e4
<<: *maps <<: *maps
@@ -54,6 +55,7 @@ staging:
multitenancy: false multitenancy: false
security: security:
last_sign_in: false last_sign_in: false
password_complexity: false
tenants: tenants:
# If you've enabled multitenancy, you can overwrite secrets for a # If you've enabled multitenancy, you can overwrite secrets for a
# specific tenant with: # specific tenant with:
@@ -92,6 +94,7 @@ preproduction:
multitenancy: false multitenancy: false
security: security:
last_sign_in: false last_sign_in: false
password_complexity: false
tenants: tenants:
# If you've enabled multitenancy, you can overwrite secrets for a # If you've enabled multitenancy, you can overwrite secrets for a
# specific tenant with: # specific tenant with:
@@ -135,6 +138,7 @@ production:
multitenancy: false multitenancy: false
security: security:
last_sign_in: false last_sign_in: false
password_complexity: false
tenants: tenants:
# If you've enabled multitenancy, you can overwrite secrets for a # If you've enabled multitenancy, you can overwrite secrets for a
# specific tenant with: # specific tenant with:

View File

@@ -849,4 +849,37 @@ describe User do
expect(user.subscriptions_token).to eq "already_set" expect(user.subscriptions_token).to eq "already_set"
end end
end end
describe ".password_complexity" do
it "returns no complexity when the secrets aren't configured" do
expect(User.password_complexity).to eq({ digit: 0, lower: 0, symbol: 0, upper: 0 })
end
context "when secrets are configured" do
before do
allow(Rails.application).to receive(:secrets).and_return(ActiveSupport::OrderedOptions.new.merge(
security: {
password_complexity: true
},
tenants: {
tolerant: {
security: {
password_complexity: false
}
}
}
))
end
it "uses the general secrets for the main tenant" do
expect(User.password_complexity).to eq({ digit: 1, lower: 1, symbol: 0, upper: 1 })
end
it "uses the tenant secrets for a tenant" do
allow(Tenant).to receive(:current_schema).and_return("tolerant")
expect(User.password_complexity).to eq({ digit: 0, lower: 0, symbol: 0, upper: 0 })
end
end
end
end end

View File

@@ -755,4 +755,51 @@ describe "Users" do
expect(page).to have_content "must be different than the current password." expect(page).to have_content "must be different than the current password."
end end
context "Regular authentication with password complexity enabled" do
before do
allow(Rails.application).to receive(:secrets).and_return(ActiveSupport::OrderedOptions.new.merge(
security: { password_complexity: true }
))
end
context "Sign up" do
scenario "Success with password" do
message = "You have been sent a message containing a verification link. Please click on this" \
" link to activate your account."
visit "/"
click_link "Register"
fill_in "Username", with: "Manuela Carmena"
fill_in "Email", with: "manuela@consul.dev"
fill_in "Password", with: "ValidPassword1234"
fill_in "Confirm password", with: "ValidPassword1234"
check "user_terms_of_service"
click_button "Register"
expect(page).to have_content message
confirm_email
expect(page).to have_content "Your account has been confirmed."
end
scenario "Errors on sign up" do
visit "/"
click_link "Register"
fill_in "Username", with: "Manuela Carmena"
fill_in "Email", with: "manuela@consul.dev"
fill_in "Password", with: "invalid_password"
fill_in "Confirm password", with: "invalid_password"
check "user_terms_of_service"
click_button "Register"
expect(page).to have_content "must contain at least one digit, must contain at least one" \
" upper-case letter"
end
end
end
end end