clearance 0.16.3 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of clearance might be problematic. Click here for more details.
- data/.gitignore +1 -0
- data/.travis.yml +0 -2
- data/Appraisals +2 -2
- data/CONTRIBUTING.md +10 -19
- data/Gemfile +1 -1
- data/Gemfile.lock +81 -82
- data/NEWS.md +17 -4
- data/README.md +176 -113
- data/app/controllers/clearance/passwords_controller.rb +44 -31
- data/app/controllers/clearance/sessions_controller.rb +11 -10
- data/app/controllers/clearance/users_controller.rb +8 -12
- data/app/mailers/clearance_mailer.rb +4 -5
- data/app/views/clearance_mailer/change_password.html.erb +2 -4
- data/app/views/layouts/application.html.erb +7 -5
- data/app/views/passwords/edit.html.erb +8 -7
- data/app/views/passwords/new.html.erb +6 -5
- data/app/views/sessions/_form.html.erb +7 -5
- data/app/views/sessions/new.html.erb +3 -2
- data/app/views/users/_form.html.erb +4 -3
- data/clearance.gemspec +29 -27
- data/config/routes.rb +10 -13
- data/db/migrate/20110111224543_create_clearance_users.rb +18 -0
- data/db/schema.rb +4 -5
- data/features/engine/visitor_resets_password.feature +0 -7
- data/features/engine/visitor_signs_in.feature +7 -0
- data/features/engine/visitor_signs_up.feature +2 -2
- data/features/integration.feature +0 -1
- data/features/integration_with_test_unit.feature +43 -0
- data/features/step_definitions/configuration_steps.rb +8 -15
- data/features/step_definitions/engine/clearance_steps.rb +38 -38
- data/features/support/clearance.rb +1 -1
- data/features/support/env.rb +4 -21
- data/gemfiles/{3.0.12.gemfile → 3.0.15.gemfile} +1 -1
- data/gemfiles/{3.0.12.gemfile.lock → 3.0.15.gemfile.lock} +75 -76
- data/gemfiles/{3.2.3.gemfile → 3.1.6.gemfile} +1 -1
- data/gemfiles/{3.1.4.gemfile.lock → 3.1.6.gemfile.lock} +79 -80
- data/gemfiles/{3.1.4.gemfile → 3.2.6.gemfile} +1 -1
- data/gemfiles/{3.2.3.gemfile.lock → 3.2.6.gemfile.lock} +80 -81
- data/lib/clearance.rb +1 -0
- data/lib/clearance/authentication.rb +37 -69
- data/lib/clearance/configuration.rb +3 -18
- data/lib/clearance/constraints.rb +2 -0
- data/lib/clearance/constraints/signed_in.rb +28 -0
- data/lib/clearance/constraints/signed_out.rb +9 -0
- data/lib/clearance/engine.rb +4 -4
- data/lib/clearance/password_strategies.rb +5 -1
- data/lib/clearance/password_strategies/bcrypt.rb +27 -0
- data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +52 -0
- data/lib/clearance/password_strategies/blowfish.rb +11 -15
- data/lib/clearance/password_strategies/fake.rb +23 -0
- data/lib/clearance/password_strategies/sha1.rb +15 -21
- data/lib/clearance/session.rb +28 -20
- data/lib/clearance/testing.rb +8 -3
- data/lib/clearance/testing/assertion_error.rb +2 -7
- data/lib/clearance/testing/deny_access_matcher.rb +27 -32
- data/lib/clearance/testing/helpers.rb +7 -8
- data/lib/clearance/user.rb +26 -92
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/templates/db/migrate/upgrade_clearance_to_diesel.rb +24 -26
- data/spec/clearance/constraints/signed_in_spec.rb +51 -0
- data/spec/clearance/constraints/signed_out_spec.rb +15 -0
- data/spec/clearance/rack_session_spec.rb +8 -7
- data/spec/clearance/session_spec.rb +28 -27
- data/spec/configuration_spec.rb +7 -6
- data/spec/controllers/denies_controller_spec.rb +11 -10
- data/spec/controllers/flashes_controller_spec.rb +5 -5
- data/spec/controllers/forgeries_controller_spec.rb +9 -9
- data/spec/controllers/passwords_controller_spec.rb +42 -55
- data/spec/controllers/sessions_controller_spec.rb +26 -33
- data/spec/controllers/users_controller_spec.rb +16 -14
- data/spec/factories.rb +1 -3
- data/spec/mailers/clearance_mailer_spec.rb +4 -4
- data/spec/models/bcrypt_migration_from_sha1_spec.rb +71 -0
- data/spec/models/bcrypt_spec.rb +40 -0
- data/spec/models/blowfish_spec.rb +14 -13
- data/spec/models/{clearance_user_spec.rb → password_strategies_spec.rb} +5 -5
- data/spec/models/sha1_spec.rb +18 -13
- data/spec/models/user_spec.rb +58 -73
- data/spec/spec_helper.rb +5 -6
- data/spec/support/clearance.rb +0 -4
- data/spec/support/cookies.rb +25 -27
- data/spec/support/request_with_remember_token.rb +19 -0
- metadata +95 -90
- data/db/migrate/20110111224543_create_diesel_clearance_users.rb +0 -19
- data/init.rb +0 -1
data/lib/clearance.rb
CHANGED
@@ -4,90 +4,58 @@ module Clearance
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
helper_method :current_user, :signed_in?, :signed_out?
|
7
|
-
hide_action
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
hide_action :authorize, :current_user, :current_user=, :deny_access,
|
8
|
+
:sign_in, :sign_out, :signed_in?, :signed_out?
|
9
|
+
end
|
10
|
+
|
11
|
+
def authenticate(params)
|
12
|
+
Clearance.configuration.user_model.authenticate(
|
13
|
+
params[:session][:email], params[:session][:password]
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorize
|
18
|
+
unless signed_in?
|
19
|
+
deny_access
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
|
-
# Finds the user from the rack clearance session
|
14
|
-
#
|
15
|
-
# @return [User, nil]
|
16
23
|
def current_user
|
17
24
|
clearance_session.current_user
|
18
25
|
end
|
19
26
|
|
20
|
-
# Set the current user
|
21
|
-
#
|
22
|
-
# @param [User]
|
23
27
|
def current_user=(user)
|
24
28
|
clearance_session.sign_in user
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
# @return [true, false]
|
30
|
-
def signed_in?
|
31
|
-
clearance_session.signed_in?
|
32
|
-
end
|
31
|
+
def deny_access(flash_message = nil)
|
32
|
+
store_location
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
if flash_message
|
35
|
+
flash[:notice] = flash_message
|
36
|
+
end
|
37
|
+
|
38
|
+
if signed_in?
|
39
|
+
redirect_to url_after_denied_access_when_signed_in
|
40
|
+
else
|
41
|
+
redirect_to url_after_denied_access_when_signed_out
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
|
-
# Sign user in to cookie.
|
42
|
-
#
|
43
|
-
# @param [User]
|
44
|
-
#
|
45
|
-
# @example
|
46
|
-
# sign_in(@user)
|
47
45
|
def sign_in(user)
|
48
46
|
clearance_session.sign_in user
|
49
47
|
end
|
50
48
|
|
51
|
-
# Sign user out of cookie.
|
52
|
-
#
|
53
|
-
# @example
|
54
|
-
# sign_out
|
55
49
|
def sign_out
|
56
50
|
clearance_session.sign_out
|
57
51
|
end
|
58
52
|
|
59
|
-
|
60
|
-
|
61
|
-
# Redefine this method and User.authenticate for other mechanisms
|
62
|
-
# such as username and password.
|
63
|
-
#
|
64
|
-
# @example
|
65
|
-
# @user = authenticate(params)
|
66
|
-
def authenticate(params)
|
67
|
-
Clearance.configuration.user_model.authenticate(params[:session][:email],
|
68
|
-
params[:session][:password])
|
69
|
-
end
|
70
|
-
|
71
|
-
# Deny the user access if they are signed out.
|
72
|
-
#
|
73
|
-
# @example
|
74
|
-
# before_filter :authorize
|
75
|
-
def authorize
|
76
|
-
deny_access unless signed_in?
|
53
|
+
def signed_in?
|
54
|
+
clearance_session.signed_in?
|
77
55
|
end
|
78
56
|
|
79
|
-
|
80
|
-
|
81
|
-
#
|
82
|
-
# @param [String] optional flash message to display to denied user
|
83
|
-
def deny_access(flash_message = nil)
|
84
|
-
store_location
|
85
|
-
flash[:notice] = flash_message if flash_message
|
86
|
-
if signed_in?
|
87
|
-
redirect_to(url_after_denied_access_when_signed_in)
|
88
|
-
else
|
89
|
-
redirect_to(url_after_denied_access_when_signed_out)
|
90
|
-
end
|
57
|
+
def signed_out?
|
58
|
+
!signed_in?
|
91
59
|
end
|
92
60
|
|
93
61
|
# CSRF protection in Rails >= 3.0.4
|
@@ -99,6 +67,10 @@ module Clearance
|
|
99
67
|
|
100
68
|
protected
|
101
69
|
|
70
|
+
def clear_return_to
|
71
|
+
session[:return_to] = nil
|
72
|
+
end
|
73
|
+
|
102
74
|
def clearance_session
|
103
75
|
request.env[:clearance]
|
104
76
|
end
|
@@ -114,18 +86,14 @@ module Clearance
|
|
114
86
|
clear_return_to
|
115
87
|
end
|
116
88
|
|
117
|
-
def return_to
|
118
|
-
session[:return_to] || params[:return_to]
|
119
|
-
end
|
120
|
-
|
121
|
-
def clear_return_to
|
122
|
-
session[:return_to] = nil
|
123
|
-
end
|
124
|
-
|
125
89
|
def redirect_to_root
|
126
90
|
redirect_to('/')
|
127
91
|
end
|
128
92
|
|
93
|
+
def return_to
|
94
|
+
session[:return_to] || params[:return_to]
|
95
|
+
end
|
96
|
+
|
129
97
|
def url_after_denied_access_when_signed_in
|
130
98
|
'/'
|
131
99
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Clearance
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :cookie_expiration, :mailer_sender, :password_strategy, :user_model
|
4
4
|
|
5
5
|
def initialize
|
6
|
-
@mailer_sender = 'reply@example.com'
|
7
6
|
@cookie_expiration = lambda { 1.year.from_now.utc }
|
7
|
+
@mailer_sender = 'reply@example.com'
|
8
8
|
end
|
9
9
|
|
10
10
|
def user_model
|
@@ -16,23 +16,8 @@ module Clearance
|
|
16
16
|
attr_accessor :configuration
|
17
17
|
end
|
18
18
|
|
19
|
-
# Configure Clearance someplace sensible,
|
20
|
-
# like config/initializers/clearance.rb
|
21
|
-
#
|
22
|
-
# If you want users to only be signed in during the current session
|
23
|
-
# instead of being remembered, do this:
|
24
|
-
#
|
25
|
-
# config.cookie_expiration = lambda { }
|
26
|
-
#
|
27
|
-
# @example
|
28
|
-
# Clearance.configure do |config|
|
29
|
-
# config.mailer_sender = 'me@example.com'
|
30
|
-
# config.cookie_expiration = lambda { 2.weeks.from_now.utc }
|
31
|
-
# config.password_strategy = MyPasswordStrategy
|
32
|
-
# config.user_model = MyNamespace::MyUser
|
33
|
-
# end
|
34
19
|
def self.configure
|
35
20
|
self.configuration ||= Configuration.new
|
36
|
-
yield
|
21
|
+
yield configuration
|
37
22
|
end
|
38
23
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Clearance
|
2
|
+
module Constraints
|
3
|
+
class SignedIn
|
4
|
+
def initialize(&block)
|
5
|
+
@block = block || lambda { |user| true }
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?(request)
|
9
|
+
@request = request
|
10
|
+
signed_in? && current_user_fulfills_additional_requirements?
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def current_user
|
16
|
+
@request.env[:clearance].current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_user_fulfills_additional_requirements?
|
20
|
+
@block.call current_user
|
21
|
+
end
|
22
|
+
|
23
|
+
def signed_in?
|
24
|
+
@request.env[:clearance].signed_in?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/clearance/engine.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'clearance'
|
2
|
+
require 'rails'
|
3
3
|
|
4
4
|
module Clearance
|
5
5
|
class Engine < Rails::Engine
|
6
|
-
initializer
|
7
|
-
app.config.filter_parameters += [:
|
6
|
+
initializer 'clearance.filter' do |app|
|
7
|
+
app.config.filter_parameters += [:password, :token]
|
8
8
|
end
|
9
9
|
|
10
10
|
config.app_middleware.insert_after ActionDispatch::Cookies, Clearance::RackSession
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module Clearance
|
2
2
|
module PasswordStrategies
|
3
|
-
autoload :
|
3
|
+
autoload :BCrypt, 'clearance/password_strategies/bcrypt'
|
4
|
+
autoload :BCryptMigrationFromSHA1,
|
5
|
+
'clearance/password_strategies/bcrypt_migration_from_sha1'
|
4
6
|
autoload :Blowfish, 'clearance/password_strategies/blowfish'
|
7
|
+
autoload :Fake, 'clearance/password_strategies/fake'
|
8
|
+
autoload :SHA1, 'clearance/password_strategies/sha1'
|
5
9
|
end
|
6
10
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Clearance
|
2
|
+
module PasswordStrategies
|
3
|
+
module BCrypt
|
4
|
+
require 'bcrypt'
|
5
|
+
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def authenticated?(password)
|
9
|
+
::BCrypt::Password.new(encrypted_password) == password
|
10
|
+
end
|
11
|
+
|
12
|
+
def password=(new_password)
|
13
|
+
@password = new_password
|
14
|
+
|
15
|
+
if new_password.present?
|
16
|
+
self.encrypted_password = encrypt(new_password)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def encrypt(password)
|
23
|
+
::BCrypt::Password.create(password)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Clearance
|
2
|
+
module PasswordStrategies
|
3
|
+
module BCryptMigrationFromSHA1
|
4
|
+
class BCryptUser
|
5
|
+
include Clearance::PasswordStrategies::BCrypt
|
6
|
+
|
7
|
+
def initialize(user)
|
8
|
+
@user = user
|
9
|
+
end
|
10
|
+
|
11
|
+
delegate :encrypted_password, :encrypted_password=, to: :@user
|
12
|
+
end
|
13
|
+
|
14
|
+
class SHA1User
|
15
|
+
include Clearance::PasswordStrategies::SHA1
|
16
|
+
|
17
|
+
def initialize(user)
|
18
|
+
@user = user
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate :salt, :salt=, :encrypted_password, :encrypted_password=, to: :@user
|
22
|
+
end
|
23
|
+
|
24
|
+
def authenticated?(password)
|
25
|
+
authenticated_with_sha1?(password) || authenticated_with_bcrypt?(password)
|
26
|
+
end
|
27
|
+
|
28
|
+
def password=(new_password)
|
29
|
+
BCryptUser.new(self).password = new_password
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def authenticated_with_bcrypt?(password)
|
35
|
+
BCryptUser.new(self).authenticated? password
|
36
|
+
end
|
37
|
+
|
38
|
+
def authenticated_with_sha1?(password)
|
39
|
+
if sha1_password?
|
40
|
+
if SHA1User.new(self).authenticated? password
|
41
|
+
self.password = password
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def sha1_password?
|
48
|
+
self.encrypted_password =~ /^[a-f0-9]{40}$/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -3,35 +3,31 @@ require 'openssl'
|
|
3
3
|
module Clearance
|
4
4
|
module PasswordStrategies
|
5
5
|
module Blowfish
|
6
|
-
# Am I authenticated with given password?
|
7
|
-
#
|
8
|
-
# @param [String] plain-text password
|
9
|
-
# @return [true, false]
|
10
|
-
# @example
|
11
|
-
# user.authenticated?('password')
|
12
6
|
def authenticated?(password)
|
13
7
|
encrypted_password == encrypt(password)
|
14
8
|
end
|
15
9
|
|
16
|
-
|
17
|
-
|
18
|
-
def encrypt_password
|
10
|
+
def password=(new_password)
|
11
|
+
@password = new_password
|
19
12
|
initialize_salt_if_necessary
|
20
|
-
|
21
|
-
|
13
|
+
|
14
|
+
if new_password.present?
|
15
|
+
self.encrypted_password = encrypt(new_password)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
|
19
|
+
protected
|
20
|
+
|
21
|
+
def encrypt(string)
|
22
|
+
generate_hash("--#{salt}--#{string}--")
|
23
|
+
end
|
24
|
+
|
25
25
|
def generate_hash(string)
|
26
26
|
cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
|
27
27
|
cipher.key = Digest::SHA256.digest(salt)
|
28
28
|
cipher.update(string) << cipher.final
|
29
29
|
end
|
30
30
|
|
31
|
-
def encrypt(string)
|
32
|
-
generate_hash("--#{salt}--#{string}--")
|
33
|
-
end
|
34
|
-
|
35
31
|
def initialize_salt_if_necessary
|
36
32
|
if salt.blank?
|
37
33
|
self.salt = generate_random_code
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Clearance
|
2
|
+
module PasswordStrategies
|
3
|
+
module Fake
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def authenticated?(password)
|
7
|
+
encrypted_password == password
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypt(password)
|
11
|
+
password
|
12
|
+
end
|
13
|
+
|
14
|
+
def password=(new_password)
|
15
|
+
@password = new_password
|
16
|
+
|
17
|
+
if new_password.present?
|
18
|
+
self.encrypted_password = encrypt(password)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,37 +1,31 @@
|
|
1
|
-
require 'digest/sha1'
|
2
|
-
|
3
1
|
module Clearance
|
4
2
|
module PasswordStrategies
|
5
3
|
module SHA1
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# @example
|
11
|
-
# user.authenticated?('password')
|
4
|
+
require 'digest/sha1'
|
5
|
+
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
12
8
|
def authenticated?(password)
|
13
9
|
encrypted_password == encrypt(password)
|
14
10
|
end
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
def encrypt_password
|
12
|
+
def password=(new_password)
|
13
|
+
@password = new_password
|
19
14
|
initialize_salt_if_necessary
|
20
|
-
if password.present?
|
21
|
-
self.encrypted_password = encrypt(password)
|
22
|
-
end
|
23
|
-
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
Digest::SHA1.hexdigest(string).encode('UTF-8')
|
28
|
-
else
|
29
|
-
Digest::SHA1.hexdigest(string)
|
16
|
+
if new_password.present?
|
17
|
+
self.encrypted_password = encrypt(new_password)
|
30
18
|
end
|
31
19
|
end
|
32
20
|
|
21
|
+
private
|
22
|
+
|
33
23
|
def encrypt(string)
|
34
|
-
generate_hash
|
24
|
+
generate_hash "--#{salt}--#{string}--"
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_hash(string)
|
28
|
+
Digest::SHA1.hexdigest(string).encode 'UTF-8'
|
35
29
|
end
|
36
30
|
|
37
31
|
def initialize_salt_if_necessary
|