minimalist_authentication 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/wwidea/minimalist_authentication.svg?branch=master)](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
|