Skip to content

Commit 60733ec

Browse files
committed
feat(lib): allow to configure and customize a password generator for the temporary password
1 parent e35ac59 commit 60733ec

File tree

8 files changed

+94
-3
lines changed

8 files changed

+94
-3
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22
All notable changes to this project made by Monade Team are documented in this file. For info refer to team@monade.io
33

4+
## [1.1.0] - 2023-04-27
5+
### Added
6+
- A password generator that follows [Cognito policies](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-policies.html)
7+
8+
### Fixed
9+
- Allow passing a custom password generator
10+
- Override default password generated when explicitly passing one to the model
11+
412
## [1.0.0] - 2023-03-30
513
### Added
614
- `sync_from_cognito!` to create users in the local database from cognito

lib/cognito_rails.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module CognitoRails
1515
autoload :Model
1616
autoload :User
1717
autoload :JWT
18+
autoload :PasswordGenerator
1819

1920
# @private
2021
module ModelInitializer

lib/cognito_rails/config.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def aws_client_credentials
1919
# @!attribute default_user_class [w]
2020
# @return [String,nil]
2121
attr_writer :aws_client_credentials, :skip_model_hooks, :aws_region,
22-
:aws_user_pool_id, :default_user_class
22+
:aws_user_pool_id, :default_user_class, :password_generator
2323

2424
# @return [Boolean] skip model hooks
2525
def skip_model_hooks
@@ -49,6 +49,10 @@ def aws_user_pool_id
4949
def default_user_class
5050
@default_user_class || (raise 'Missing config default_user_class')
5151
end
52+
53+
def password_generator
54+
@password_generator || CognitoRails::PasswordGenerator.method(:generate)
55+
end
5256
end
5357
end
5458
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
module CognitoRails
4+
class PasswordGenerator
5+
NUMERIC = (0..9).to_a.freeze
6+
LOWER_CASE = ('a'..'z').to_a.freeze
7+
UPPER_CASE = ('A'..'Z').to_a.freeze
8+
SPECIAL = [
9+
'^', '$', '*', '.', '[', ']', '{', '}',
10+
'(', ')', '?', '"', '!', '@', '#', '%',
11+
'&', '/', '\\', ',', '>', '<', "'", ':',
12+
';', '|', '_', '~', '`', '=', '+', '-'
13+
].freeze
14+
15+
# Generates a random password given a length range
16+
#
17+
# @param range [Range]
18+
# @return [String]
19+
def self.generate(range = 8..16)
20+
password_length = rand(range)
21+
numeric_count = rand(1..(password_length-3))
22+
23+
lower_case_count = rand(1..(password_length-(numeric_count+2)))
24+
upper_case_count = rand(1..(password_length-(numeric_count + lower_case_count + 1)))
25+
special_count = password_length-(numeric_count + lower_case_count + upper_case_count)
26+
27+
numeric_characters = numeric_count.times.map { NUMERIC.sample }
28+
lower_case_characters = lower_case_count.times.map { LOWER_CASE.sample }
29+
upper_case_characters = upper_case_count.times.map { UPPER_CASE.sample }
30+
special_characters = special_count.times.map { SPECIAL.sample }
31+
32+
(numeric_characters + lower_case_characters + upper_case_characters + special_characters).shuffle.join
33+
end
34+
end
35+
end

lib/cognito_rails/user.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class User
3939
def initialize(attributes = {})
4040
attributes = attributes.with_indifferent_access
4141
self.email = attributes[:email]
42-
self.password = attributes[:password] || SecureRandom.urlsafe_base64
42+
self.password = attributes[:password] || Config.password_generator.call
4343
self.phone = attributes[:phone]
4444
self.user_class = attributes[:user_class] || Config.default_user_class.constantize
4545
self.custom_attributes = attributes[:custom_attributes]

lib/cognito_rails/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
module CognitoRails
44
# @return [String] gem version
5-
VERSION = '1.0.0'
5+
VERSION = '1.1.0'
66
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe CognitoRails::PasswordGenerator do
6+
it 'generates a password' do
7+
expect(described_class.generate).to be_a(String)
8+
end
9+
10+
it 'generates a password with the correct length' do
11+
1000.times do
12+
expect(described_class.generate(8..8).length).to eq(8)
13+
end
14+
end
15+
16+
it 'contains at least one letter, one number, one upper case letter, one symbol' do
17+
1000.times do
18+
password = described_class.generate
19+
expect(password).to match(/[a-z]/)
20+
expect(password).to match(/[A-Z]/)
21+
expect(password).to match(/[0-9]/)
22+
include_symbol = CognitoRails::PasswordGenerator::SPECIAL.any? do |symbol|
23+
password.include?(symbol)
24+
end
25+
expect(include_symbol).to be_truthy
26+
end
27+
end
28+
end

spec/cognito_rails/user_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@
8282
user.destroy!
8383
end
8484

85+
it 'uses the password generator defined in config' do
86+
CognitoRails::Config.password_generator = -> { 'ciao' }
87+
expect(CognitoRails::User).to receive(:cognito_client).at_least(:once).and_return(fake_cognito_client)
88+
89+
expect(fake_cognito_client).to receive(:admin_create_user).with(
90+
hash_including(
91+
temporary_password: 'ciao'
92+
)
93+
)
94+
user = User.new(email: sample_cognito_email)
95+
user.save!
96+
ensure
97+
CognitoRails::Config.password_generator = nil
98+
end
99+
85100
it 'uses the custom password passed as parameter' do
86101
expect(CognitoRails::User).to receive(:cognito_client).at_least(:once).and_return(fake_cognito_client)
87102

0 commit comments

Comments
 (0)