minimalist_authentication 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +35 -3
- data/lib/minimalist_authentication.rb +2 -0
- data/lib/minimalist_authentication/configuration.rb +13 -2
- data/lib/minimalist_authentication/conversions/merge_password_hash.rb +38 -0
- data/lib/minimalist_authentication/password.rb +46 -0
- data/lib/minimalist_authentication/test_helper.rb +0 -1
- data/lib/minimalist_authentication/user.rb +52 -50
- data/lib/minimalist_authentication/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c706bf1ccf629eedb3a910b68a983ae2d4869f4d
|
4
|
+
data.tar.gz: 8d5f220c4f4ac6d91c445c3a946fc913ba5d1cbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aeb1bfee96f489dd77baa1c3fa9059fdcc3e335cc9b50e387be72c5efd6f859f92c4827783e4a51722bf6482edfd0c1f961e43d6c65b93dd6f2467786aebb3d2
|
7
|
+
data.tar.gz: a0e7282735c59607dc0373a8940885659b8df6981ffeefe6e47535f99cc23b0058ea4084a9a9233f73562811179a208f29628b744afdefdd593e468b1760d33e
|
data/README.md
CHANGED
@@ -16,12 +16,12 @@ $ bundle
|
|
16
16
|
|
17
17
|
Create a user model with **email** for an identifier:
|
18
18
|
```bash
|
19
|
-
bin/rails generate model user active:boolean email:string
|
19
|
+
bin/rails generate model user active:boolean email:string password_hash:string last_logged_in_at:datetime
|
20
20
|
```
|
21
21
|
|
22
22
|
OR create a user model with **username** for an identifier:
|
23
23
|
```bash
|
24
|
-
bin/rails generate model user active:boolean username:string
|
24
|
+
bin/rails generate model user active:boolean username:string password_hash:string last_logged_in_at:datetime
|
25
25
|
```
|
26
26
|
|
27
27
|
|
@@ -61,15 +61,47 @@ class ActiveSupport::TestCase
|
|
61
61
|
end
|
62
62
|
```
|
63
63
|
|
64
|
-
##
|
64
|
+
## Configuration
|
65
65
|
Customize the configuration with an initializer. Create a **minimalist_authentication.rb** file in /Users/baldwina/git/brightways/config/initializers.
|
66
66
|
```ruby
|
67
67
|
MinimalistAuthentication.configure do |configuration|
|
68
68
|
configuration.user_model_name = 'CustomModelName' # default is '::User'
|
69
69
|
configuration.session_key = :custom_session_key # default is ':user_id'
|
70
|
+
validate_email = true # default is true
|
71
|
+
validate_email_presence = true # default is true
|
70
72
|
end
|
71
73
|
```
|
72
74
|
|
75
|
+
|
76
|
+
## Fixtures
|
77
|
+
Use **MinimalistAuthentication::Password.create** to create a password for
|
78
|
+
fixture users.
|
79
|
+
```yaml
|
80
|
+
example_user:
|
81
|
+
email: user@example.com
|
82
|
+
password_hash: <%= MinimalistAuthentication::Password.create('password') %>
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
## Conversions
|
87
|
+
Pre 2.0 versions of MinimalistAuthentication supported multiple hash algorithms
|
88
|
+
and stored the hashed password and salt as separate fields in the database
|
89
|
+
(crypted_password and salt). The current version of MinimalistAuthentication
|
90
|
+
uses BCrypt to hash passwords and stores the result in the **password_hash** field.
|
91
|
+
|
92
|
+
To convert from a pre 2.0 version add the **password_hash** to your user model
|
93
|
+
and run the conversion routine.
|
94
|
+
```bash
|
95
|
+
bin/rails generate migration AddPasswordHashToUsers password_hash:string
|
96
|
+
```
|
97
|
+
```ruby
|
98
|
+
MinimalistAuthentication::Conversions::MergePasswordHash.run!
|
99
|
+
```
|
100
|
+
|
101
|
+
When the conversion is complete the **crypted_password**, **salt**, and
|
102
|
+
**using_digest_version** fields can safely be removed.
|
103
|
+
|
104
|
+
|
73
105
|
## Build
|
74
106
|
[](https://travis-ci.org/wwidea/minimalist_authentication)
|
75
107
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'minimalist_authentication/engine'
|
2
2
|
require 'minimalist_authentication/configuration'
|
3
3
|
require 'minimalist_authentication/user'
|
4
|
+
require 'minimalist_authentication/password'
|
4
5
|
require 'minimalist_authentication/null_password'
|
5
6
|
require 'minimalist_authentication/controller'
|
6
7
|
require 'minimalist_authentication/sessions'
|
7
8
|
require 'minimalist_authentication/test_helper'
|
9
|
+
require 'minimalist_authentication/conversions/merge_password_hash'
|
@@ -23,9 +23,20 @@ module MinimalistAuthentication
|
|
23
23
|
# Defaults to '::User'
|
24
24
|
attr_accessor :user_model_name
|
25
25
|
|
26
|
+
# Toggle all email validations.
|
27
|
+
# Defaults to true.
|
28
|
+
attr_accessor :validate_email
|
29
|
+
|
30
|
+
# Toggle email presence validation.
|
31
|
+
# Defaults to true.
|
32
|
+
# Note: validate_email_presence is only checked if validate_email is true.
|
33
|
+
attr_accessor :validate_email_presence
|
34
|
+
|
26
35
|
def initialize
|
27
|
-
self.user_model_name
|
28
|
-
self.session_key
|
36
|
+
self.user_model_name = '::User'
|
37
|
+
self.session_key = :user_id
|
38
|
+
self.validate_email = true
|
39
|
+
self.validate_email_presence = true
|
29
40
|
end
|
30
41
|
|
31
42
|
# Returns the user_model class
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module MinimalistAuthentication
|
2
|
+
module Conversions
|
3
|
+
class MergePasswordHash
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def run!
|
7
|
+
user_model.where(using_digest_version: 3, password_hash: nil).each do |user|
|
8
|
+
new(user).update!
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def user_model
|
15
|
+
MinimalistAuthentication.configuration.user_model
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :user
|
20
|
+
|
21
|
+
delegate :salt, :crypted_password, to: :user
|
22
|
+
|
23
|
+
def initialize(user)
|
24
|
+
self.user = user
|
25
|
+
end
|
26
|
+
|
27
|
+
def update!
|
28
|
+
user.update_column(:password_hash, merged_password_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def merged_password_hash
|
34
|
+
"#{salt}#{crypted_password}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module MinimalistAuthentication
|
2
|
+
class Password
|
3
|
+
class << self
|
4
|
+
# Create a bcrypt password hash with a calibrated cost factor.
|
5
|
+
def create(secret)
|
6
|
+
new ::BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost))
|
7
|
+
end
|
8
|
+
|
9
|
+
# Cache the calibrated bcrypt cost factor.
|
10
|
+
def cost
|
11
|
+
@bcrypt_cost ||= calibrate_cost
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# Calibrates cost so that new user passwords can automatically take
|
17
|
+
# advantage of faster server hardware in the future.
|
18
|
+
# Sets cost to BCrypt::Engine::MIN_COST in the test environment
|
19
|
+
def calibrate_cost
|
20
|
+
::Rails.env.test? ? ::BCrypt::Engine::MIN_COST : ::BCrypt::Engine.calibrate(750)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :bcrypt_password
|
25
|
+
|
26
|
+
# Returns a password object wrapping a valid BCrypt password or a NullPassword
|
27
|
+
def initialize(password_hash)
|
28
|
+
begin
|
29
|
+
self.bcrypt_password = ::BCrypt::Password.new(password_hash)
|
30
|
+
rescue ::BCrypt::Errors::InvalidHash
|
31
|
+
self.bcrypt_password = NullPassword.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Delegate methods to bcrypt_password
|
36
|
+
delegate :==, :to_s, :cost, to: :bcrypt_password
|
37
|
+
|
38
|
+
# Temporary access to checksum and salt for backwards compatibility
|
39
|
+
delegate :checksum, :salt, to: :bcrypt_password
|
40
|
+
|
41
|
+
# Checks if the password_hash cost factor is less than the current cost.
|
42
|
+
def stale?
|
43
|
+
cost < self.class.cost
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -7,58 +7,65 @@ module MinimalistAuthentication
|
|
7
7
|
GUEST_USER_EMAIL = 'guest'
|
8
8
|
EMAIL_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
|
9
9
|
|
10
|
-
# Recalibrates cost when class is loaded so that new user passwords
|
11
|
-
# can automatically take advantage of faster server hardware in the
|
12
|
-
# future for better encryption.
|
13
|
-
# sets cost to BCrypt::Engine::MIN_COST in the test environment
|
14
|
-
CALIBRATED_BCRYPT_COST = (::Rails.env.test? ? ::BCrypt::Engine::MIN_COST : ::BCrypt::Engine.calibrate(750))
|
15
|
-
|
16
10
|
included do
|
11
|
+
# Stores the plain text password.
|
17
12
|
attr_accessor :password
|
18
|
-
before_save :encrypt_password
|
19
13
|
|
20
|
-
#
|
14
|
+
# Hashes and stores the password on save.
|
15
|
+
before_save :hash_password
|
16
|
+
|
17
|
+
# Email validations
|
21
18
|
validates_presence_of :email, if: :validate_email_presence?
|
22
19
|
validates_uniqueness_of :email, allow_blank: true, if: :validate_email?
|
23
20
|
validates_format_of :email, allow_blank: true, with: EMAIL_REGEX, if: :validate_email?
|
24
21
|
|
25
|
-
#
|
26
|
-
validates_presence_of :password, if: :
|
27
|
-
validates_confirmation_of :password, if: :
|
28
|
-
validates_length_of :password, within:
|
22
|
+
# Password validations
|
23
|
+
validates_presence_of :password, if: :validate_password?
|
24
|
+
validates_confirmation_of :password, if: :validate_password?
|
25
|
+
validates_length_of :password, within: 8..40, if: :validate_password?
|
29
26
|
|
27
|
+
# Active scope
|
30
28
|
scope :active, ->(active = true) { where active: active }
|
31
29
|
end
|
32
30
|
|
33
31
|
module ClassMethods
|
32
|
+
# Authenticates a user form the params provied. Expects a params hash with
|
33
|
+
# email or username and passwod keys.
|
34
|
+
# Params examples:
|
35
|
+
# { email: 'user@example.com', password: 'abc123' }
|
36
|
+
# { username: 'user', password: 'abc123' }
|
37
|
+
# Returns user upon successful authentcation.
|
38
|
+
# Otherwise returns nil.
|
34
39
|
def authenticate(params)
|
40
|
+
# extract email or username and the associated value
|
35
41
|
field, value = params.to_h.select { |key, value| %w(email username).include?(key.to_s) && value.present? }.first
|
42
|
+
# return nil if field, value, or password is blank
|
36
43
|
return if field.blank? || value.blank? || params[:password].blank?
|
44
|
+
# attempt to find the user using field and value
|
37
45
|
user = active.where(field => value).first
|
46
|
+
# check if a user was found and if they can be authenticated
|
38
47
|
return unless user && user.authenticated?(params[:password])
|
48
|
+
# return the authenticated user
|
39
49
|
return user
|
40
50
|
end
|
41
51
|
|
42
|
-
|
43
|
-
::BCrypt::Password.create(password, cost: calibrated_bcrypt_cost)
|
44
|
-
end
|
45
|
-
|
46
|
-
def calibrated_bcrypt_cost
|
47
|
-
CALIBRATED_BCRYPT_COST
|
48
|
-
end
|
49
|
-
|
52
|
+
# Returns a frozen user with the email set to GUEST_USER_EMAIL.
|
50
53
|
def guest
|
51
|
-
new(email: GUEST_USER_EMAIL)
|
54
|
+
new(email: GUEST_USER_EMAIL).freeze
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
58
|
+
# Returns true if the user is active.
|
55
59
|
def active?
|
56
60
|
active
|
57
61
|
end
|
58
62
|
|
63
|
+
# Return true if password matches the hashed_password.
|
64
|
+
# If successful checks for an outdated password_hash and updates if
|
65
|
+
# necessary.
|
59
66
|
def authenticated?(password)
|
60
|
-
if
|
61
|
-
|
67
|
+
if password_object == password
|
68
|
+
update_hash!(password) if password_object.stale?
|
62
69
|
return true
|
63
70
|
end
|
64
71
|
|
@@ -70,54 +77,49 @@ module MinimalistAuthentication
|
|
70
77
|
update_column(:last_logged_in_at, Time.current)
|
71
78
|
end
|
72
79
|
|
80
|
+
# Check if user is a guest based on their email attribute
|
73
81
|
def is_guest?
|
74
82
|
email == GUEST_USER_EMAIL
|
75
83
|
end
|
76
84
|
|
77
85
|
private
|
78
86
|
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def update_encryption(password)
|
87
|
+
# Set self.password to password, hash, and save
|
88
|
+
def update_hash!(password)
|
84
89
|
self.password = password
|
85
|
-
|
90
|
+
hash_password
|
86
91
|
save
|
87
92
|
end
|
88
93
|
|
89
|
-
|
94
|
+
# Hash password and store in hash_password unless password is blank.
|
95
|
+
def hash_password
|
90
96
|
return if password.blank?
|
91
|
-
password_hash
|
92
|
-
self.salt = password_hash.salt
|
93
|
-
self.crypted_password = password_hash.checksum
|
94
|
-
end
|
95
|
-
|
96
|
-
def bcrypt_password
|
97
|
-
valid_hash? ? ::BCrypt::Password.new(password_hash) : null_password
|
98
|
-
end
|
99
|
-
|
100
|
-
def valid_hash?
|
101
|
-
::BCrypt::Password.valid_hash?(password_hash)
|
97
|
+
self.password_hash = Password.create(password)
|
102
98
|
end
|
103
99
|
|
104
|
-
|
105
|
-
|
100
|
+
# Retuns a MinimalistAuthentication::Password object.
|
101
|
+
def password_object
|
102
|
+
Password.new(password_hash)
|
106
103
|
end
|
107
104
|
|
108
|
-
|
109
|
-
|
105
|
+
# Requre password for active users that either do no have a password hash
|
106
|
+
# stored OR are attempting to set a new password.
|
107
|
+
def validate_password?
|
108
|
+
active? && (password_hash.blank? || password.present?)
|
110
109
|
end
|
111
110
|
|
112
|
-
# email
|
111
|
+
# Validate email for active users.
|
112
|
+
# Applications can turn off email validation by setting the validate_email
|
113
|
+
# configuration attribute to false.
|
113
114
|
def validate_email?
|
114
|
-
|
115
|
-
active?
|
115
|
+
MinimalistAuthentication.configuration.validate_email && active?
|
116
116
|
end
|
117
117
|
|
118
|
+
# Validate email presence for active users.
|
119
|
+
# Applications can turn offf email presence validation by setting
|
120
|
+
# validate_email_presence configuration attribute to false.
|
118
121
|
def validate_email_presence?
|
119
|
-
|
120
|
-
validate_email?
|
122
|
+
MinimalistAuthentication.configuration.validate_email_presence && validate_email?
|
121
123
|
end
|
122
124
|
end
|
123
125
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minimalist_authentication
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Baldwin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-09-
|
12
|
+
date: 2017-09-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -77,8 +77,10 @@ files:
|
|
77
77
|
- lib/minimalist_authentication.rb
|
78
78
|
- lib/minimalist_authentication/configuration.rb
|
79
79
|
- lib/minimalist_authentication/controller.rb
|
80
|
+
- lib/minimalist_authentication/conversions/merge_password_hash.rb
|
80
81
|
- lib/minimalist_authentication/engine.rb
|
81
82
|
- lib/minimalist_authentication/null_password.rb
|
83
|
+
- lib/minimalist_authentication/password.rb
|
82
84
|
- lib/minimalist_authentication/sessions.rb
|
83
85
|
- lib/minimalist_authentication/test_helper.rb
|
84
86
|
- lib/minimalist_authentication/user.rb
|