clearance 1.8.0 → 1.16.0
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +25 -6
- data/.yardopts +6 -0
- data/Appraisals +15 -4
- data/CONTRIBUTING.md +4 -1
- data/Gemfile +5 -3
- data/Gemfile.lock +102 -96
- data/NEWS.md +742 -311
- data/README.md +217 -339
- data/app/controllers/clearance/passwords_controller.rb +35 -21
- data/app/controllers/clearance/sessions_controller.rb +17 -4
- data/app/controllers/clearance/users_controller.rb +10 -4
- data/app/mailers/clearance_mailer.rb +2 -3
- data/app/views/clearance_mailer/change_password.html.erb +6 -3
- data/app/views/clearance_mailer/change_password.text.erb +5 -0
- data/app/views/layouts/application.html.erb +2 -2
- data/app/views/passwords/create.html.erb +1 -1
- data/app/views/passwords/edit.html.erb +2 -2
- data/app/views/passwords/new.html.erb +2 -2
- data/app/views/sessions/_form.html.erb +2 -2
- data/app/views/sessions/new.html.erb +1 -1
- data/app/views/users/new.html.erb +2 -2
- data/bin/setup +6 -2
- data/config/locales/clearance.en.yml +6 -0
- data/db/migrate/20110111224543_create_clearance_users.rb +1 -1
- data/gemfiles/{rails3.2.gemfile → rails32.gemfile} +4 -2
- data/gemfiles/{rails4.0.gemfile → rails40.gemfile} +6 -3
- data/gemfiles/{rails4.1.gemfile → rails41.gemfile} +6 -3
- data/gemfiles/{rails4.2.gemfile → rails42.gemfile} +6 -3
- data/gemfiles/rails50.gemfile +21 -0
- data/lib/clearance/authentication.rb +61 -2
- data/lib/clearance/authorization.rb +47 -4
- data/lib/clearance/back_door.rb +29 -6
- data/lib/clearance/configuration.rb +152 -15
- data/lib/clearance/constraints/signed_in.rb +21 -0
- data/lib/clearance/constraints/signed_out.rb +12 -0
- data/lib/clearance/constraints.rb +12 -0
- data/lib/clearance/controller.rb +13 -0
- data/lib/clearance/default_sign_in_guard.rb +17 -0
- data/lib/clearance/engine.rb +24 -4
- data/lib/clearance/password_strategies/bcrypt.rb +16 -21
- data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +19 -0
- data/lib/clearance/password_strategies/blowfish.rb +17 -0
- data/lib/clearance/password_strategies/sha1.rb +17 -0
- data/lib/clearance/password_strategies.rb +13 -0
- data/lib/clearance/rack_session.rb +13 -0
- data/lib/clearance/rspec.rb +15 -4
- data/lib/clearance/session.rb +46 -1
- data/lib/clearance/session_status.rb +7 -0
- data/lib/clearance/sign_in_guard.rb +65 -0
- data/lib/clearance/test_unit.rb +3 -3
- data/lib/clearance/testing/controller_helpers.rb +44 -0
- data/lib/clearance/testing/deny_access_matcher.rb +36 -2
- data/lib/clearance/testing/helpers.rb +9 -25
- data/lib/clearance/testing/view_helpers.rb +32 -0
- data/lib/clearance/token.rb +7 -0
- data/lib/clearance/user.rb +182 -4
- data/lib/clearance/version.rb +1 -1
- data/lib/clearance.rb +2 -0
- data/lib/generators/clearance/install/install_generator.rb +24 -5
- data/lib/generators/clearance/install/templates/clearance.rb +1 -0
- data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb +3 -3
- data/lib/generators/clearance/install/templates/db/migrate/create_users.rb +2 -2
- data/lib/generators/clearance/install/templates/user.rb.erb +3 -0
- data/lib/generators/clearance/routes/routes_generator.rb +23 -0
- data/lib/generators/clearance/routes/templates/routes.rb +7 -7
- data/lib/generators/clearance/specs/templates/features/clearance/user_signs_out_spec.rb.tt +1 -1
- data/lib/generators/clearance/specs/templates/features/clearance/visitor_resets_password_spec.rb.tt +12 -2
- data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_in_spec.rb.tt +1 -1
- data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_up_spec.rb.tt +1 -1
- data/lib/generators/clearance/specs/templates/features/clearance/visitor_updates_password_spec.rb.tt +1 -1
- data/spec/acceptance/clearance_installation_spec.rb +4 -1
- data/spec/app_templates/app/models/rails5/user.rb +5 -0
- data/spec/app_templates/config/initializers/clearance.rb +2 -0
- data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -1
- data/spec/app_templates/testapp/config/initializers/action_mailer.rb +1 -3
- data/spec/clearance/back_door_spec.rb +25 -6
- data/spec/clearance/controller_spec.rb +11 -0
- data/spec/clearance/rack_session_spec.rb +5 -5
- data/spec/clearance/session_spec.rb +2 -15
- data/spec/clearance/testing/{helpers_spec.rb → controller_helpers_spec.rb} +12 -12
- data/spec/clearance/testing/view_helpers_spec.rb +37 -0
- data/spec/configuration_spec.rb +94 -86
- data/spec/controllers/apis_controller_spec.rb +6 -2
- data/spec/controllers/forgeries_controller_spec.rb +6 -1
- data/spec/controllers/passwords_controller_spec.rb +17 -16
- data/spec/controllers/permissions_controller_spec.rb +13 -3
- data/spec/controllers/sessions_controller_spec.rb +4 -4
- data/spec/dummy/app/controllers/application_controller.rb +5 -1
- data/spec/dummy/application.rb +4 -0
- data/spec/generators/clearance/install/install_generator_spec.rb +29 -3
- data/spec/generators/clearance/routes/routes_generator_spec.rb +5 -1
- data/spec/generators/clearance/views/views_generator_spec.rb +11 -10
- data/spec/helpers/helper_helpers_spec.rb +10 -0
- data/spec/mailers/clearance_mailer_spec.rb +13 -19
- data/spec/password_strategies/bcrypt_migration_from_sha1_spec.rb +6 -0
- data/spec/password_strategies/blowfish_spec.rb +6 -0
- data/spec/password_strategies/sha1_spec.rb +6 -0
- data/spec/requests/csrf_rotation_spec.rb +33 -0
- data/spec/spec_helper.rb +11 -2
- data/spec/support/generator_spec_helpers.rb +13 -1
- data/spec/support/http_method_shim.rb +23 -0
- data/spec/user_spec.rb +9 -0
- data/spec/views/view_helpers_spec.rb +10 -0
- metadata +22 -9
- data/lib/generators/clearance/install/templates/user.rb +0 -3
data/lib/clearance/back_door.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Clearance
|
2
2
|
# Middleware which allows signing in by passing as=USER_ID in a query
|
3
|
-
# parameter.
|
3
|
+
# parameter. If `User#to_param` is overriden you may pass a block to
|
4
|
+
# override the default user lookup behaviour
|
4
5
|
#
|
5
6
|
# Designed to eliminate time in integration tests wasted by visiting and
|
6
7
|
# submitting the sign in form.
|
@@ -14,12 +15,24 @@ module Clearance
|
|
14
15
|
# # ...
|
15
16
|
# end
|
16
17
|
#
|
18
|
+
# # or if `User#to_param` is overridden (to `username` for example):
|
19
|
+
#
|
20
|
+
# # config/environments/test.rb
|
21
|
+
# MyRailsApp::Application.configure do
|
22
|
+
# # ...
|
23
|
+
# config.middleware.use Clearance::BackDoor do |username|
|
24
|
+
# User.find_by(username: username)
|
25
|
+
# end
|
26
|
+
# # ...
|
27
|
+
# end
|
28
|
+
#
|
17
29
|
# Usage:
|
18
30
|
#
|
19
31
|
# visit new_feedback_path(as: user)
|
20
32
|
class BackDoor
|
21
|
-
def initialize(app)
|
33
|
+
def initialize(app, &block)
|
22
34
|
@app = app
|
35
|
+
@block = block
|
23
36
|
end
|
24
37
|
|
25
38
|
def call(env)
|
@@ -29,14 +42,24 @@ module Clearance
|
|
29
42
|
|
30
43
|
private
|
31
44
|
|
45
|
+
# @api private
|
32
46
|
def sign_in_through_the_back_door(env)
|
33
|
-
params = Rack::Utils.parse_query(env[
|
34
|
-
|
47
|
+
params = Rack::Utils.parse_query(env["QUERY_STRING"])
|
48
|
+
user_param = params["as"]
|
35
49
|
|
36
|
-
if
|
37
|
-
user =
|
50
|
+
if user_param.present?
|
51
|
+
user = find_user(user_param)
|
38
52
|
env[:clearance].sign_in(user)
|
39
53
|
end
|
40
54
|
end
|
55
|
+
|
56
|
+
# @api private
|
57
|
+
def find_user(user_param)
|
58
|
+
if @block
|
59
|
+
@block.call(user_param)
|
60
|
+
else
|
61
|
+
Clearance.configuration.user_model.find(user_param)
|
62
|
+
end
|
63
|
+
end
|
41
64
|
end
|
42
65
|
end
|
@@ -1,29 +1,106 @@
|
|
1
1
|
module Clearance
|
2
2
|
class Configuration
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
3
|
+
# Controls whether the sign up route is enabled.
|
4
|
+
# Defaults to `true`. Set to `false` to disable user creation routes.
|
5
|
+
# The setting is ignored if routes are disabled.
|
6
|
+
# @param [Boolean] value
|
7
|
+
# @return [Boolean]
|
8
|
+
attr_writer :allow_sign_up
|
9
|
+
|
10
|
+
# The domain to use for the clearance remember token cookie.
|
11
|
+
# Defaults to `nil`, which causes the cookie domain to default to the
|
12
|
+
# domain of the request. For more, see
|
13
|
+
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.2.3).
|
14
|
+
# @return [String]
|
15
|
+
attr_accessor :cookie_domain
|
16
|
+
|
17
|
+
# A lambda called to set the remember token cookie expires attribute.
|
18
|
+
# The lambda accepts the collection of cookies as an argument which
|
19
|
+
# allows for changing the expiration according to those cookies.
|
20
|
+
# This could be used, for example, to set a session cookie unless
|
21
|
+
# a `remember_me` cookie was also present. By default, cookie expiration
|
22
|
+
# is one year. For more on cookie expiration see
|
23
|
+
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.2.1).
|
24
|
+
# @return [Lambda]
|
25
|
+
attr_accessor :cookie_expiration
|
26
|
+
|
27
|
+
# The name of Clearance's remember token cookie.
|
28
|
+
# Defaults to `remember_token`.
|
29
|
+
# @return [String]
|
30
|
+
attr_accessor :cookie_name
|
31
|
+
|
32
|
+
# Controls which paths the remember token cookie is valid for.
|
33
|
+
# Defaults to `"/"` for the entire domain. For more, see
|
34
|
+
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.1.4).
|
35
|
+
# @return [String]
|
36
|
+
attr_accessor :cookie_path
|
37
|
+
|
38
|
+
# Controls whether the HttpOnly flag should be set on the remember token
|
39
|
+
# cookie. Defaults to `false`. If `true`, the cookie will not be made
|
40
|
+
# available to JavaScript. For more see
|
41
|
+
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.2.6).
|
42
|
+
# @return [Boolean]
|
43
|
+
attr_accessor :httponly
|
44
|
+
|
45
|
+
# Controls the address the password reset email is sent from.
|
46
|
+
# Defaults to reply@example.com.
|
47
|
+
# @return [String]
|
48
|
+
attr_accessor :mailer_sender
|
49
|
+
|
50
|
+
# The password strategy to use when authenticating and setting passwords.
|
51
|
+
# Defaults to {Clearance::PasswordStrategies::BCrypt}.
|
52
|
+
# @return [Module #authenticated? #password=]
|
53
|
+
attr_accessor :password_strategy
|
54
|
+
|
55
|
+
# The default path Clearance will redirect signed in users to.
|
56
|
+
# Defaults to `"/"`. This can often be overridden for specific scenarios by
|
57
|
+
# overriding controller methods that rely on it.
|
58
|
+
# @return [String]
|
59
|
+
attr_accessor :redirect_url
|
60
|
+
|
61
|
+
# Controls whether Clearance will rotate the CSRF token on sign in.
|
62
|
+
# Defaults to `nil` which generates a warning. Will default to true in
|
63
|
+
# Clearance 2.0.
|
64
|
+
attr_accessor :rotate_csrf_on_sign_in
|
65
|
+
|
66
|
+
# Set to `false` to disable Clearance's built-in routes.
|
67
|
+
# Defaults to `true`. When set to false, your app is responsible for all
|
68
|
+
# routes. You can dump a copy of Clearance's default routes with
|
69
|
+
# `rails generate clearance:routes`.
|
70
|
+
# @return [Boolean]
|
71
|
+
attr_writer :routes
|
72
|
+
|
73
|
+
# Controls the secure setting on the remember token cookie.
|
74
|
+
# Defaults to `false`. When set, the browser will only send the
|
75
|
+
# cookie to the server over HTTPS. You should set this value to true in
|
76
|
+
# live environments to prevent session hijacking. For more, see
|
77
|
+
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.2.5).
|
78
|
+
# @return [Boolean]
|
79
|
+
attr_accessor :secure_cookie
|
80
|
+
|
81
|
+
# The array of sign in guards to run when signing a user in.
|
82
|
+
# Defaults to an empty array. Sign in guards respond to `call` and are
|
83
|
+
# initialized with a session and the current stack. Each guard can decide
|
84
|
+
# to fail the sign in, yield to the next guard, or allow the sign in.
|
85
|
+
# @return [Array<#call>]
|
86
|
+
attr_accessor :sign_in_guards
|
87
|
+
|
88
|
+
# The ActiveRecord class that represents users in your application.
|
89
|
+
# Defualts to `::User`.
|
90
|
+
# @return [ActiveRecord::Base]
|
91
|
+
attr_accessor :user_model
|
17
92
|
|
18
93
|
def initialize
|
19
94
|
@allow_sign_up = true
|
20
95
|
@cookie_expiration = ->(cookies) { 1.year.from_now.utc }
|
96
|
+
@cookie_domain = nil
|
21
97
|
@cookie_path = '/'
|
22
98
|
@cookie_name = "remember_token"
|
23
99
|
@httponly = false
|
24
100
|
@mailer_sender = 'reply@example.com'
|
25
101
|
@redirect_url = '/'
|
26
102
|
@routes = true
|
103
|
+
@rotate_csrf_on_sign_in = nil
|
27
104
|
@secure_cookie = false
|
28
105
|
@sign_in_guards = []
|
29
106
|
end
|
@@ -32,10 +109,16 @@ module Clearance
|
|
32
109
|
@user_model ||= ::User
|
33
110
|
end
|
34
111
|
|
112
|
+
# Is the user sign up route enabled?
|
113
|
+
# @return [Boolean]
|
35
114
|
def allow_sign_up?
|
36
115
|
@allow_sign_up
|
37
116
|
end
|
38
117
|
|
118
|
+
# Specifies which controller actions are allowed for user resources.
|
119
|
+
# This will be `[:create]` is `allow_sign_up` is true (the default), and
|
120
|
+
# empty otherwise.
|
121
|
+
# @return [Array<Symbol>]
|
39
122
|
def user_actions
|
40
123
|
if allow_sign_up?
|
41
124
|
[:create]
|
@@ -44,23 +127,77 @@ module Clearance
|
|
44
127
|
end
|
45
128
|
end
|
46
129
|
|
130
|
+
# The name of user parameter for the configured user model.
|
131
|
+
# This is derived from the `model_name` of the `user_model` setting.
|
132
|
+
# In the default configuration, this is `user`.
|
133
|
+
# @return [Symbol]
|
134
|
+
def user_parameter
|
135
|
+
user_model.model_name.singular.to_sym
|
136
|
+
end
|
137
|
+
|
138
|
+
# The name of foreign key parameter for the configured user model.
|
139
|
+
# This is derived from the `model_name` of the `user_model` setting.
|
140
|
+
# In the default configuration, this is `user_id`.
|
141
|
+
# @return [Symbol]
|
47
142
|
def user_id_parameter
|
48
|
-
"#{
|
143
|
+
"#{user_parameter}_id".to_sym
|
49
144
|
end
|
50
145
|
|
146
|
+
# @return [Boolean] are Clearance's built-in routes enabled?
|
51
147
|
def routes_enabled?
|
52
148
|
@routes
|
53
149
|
end
|
150
|
+
|
151
|
+
# Reloads the clearance user model class.
|
152
|
+
# This is called from the Clearance engine to reload the configured
|
153
|
+
# user class during each request while in development mode, but only once
|
154
|
+
# in production.
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def reload_user_model
|
158
|
+
if @user_model.present?
|
159
|
+
@user_model = @user_model.to_s.constantize
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def rotate_csrf_on_sign_in?
|
164
|
+
if rotate_csrf_on_sign_in.nil?
|
165
|
+
warn <<-EOM.squish
|
166
|
+
Clearance's `rotate_csrf_on_sign_in` configration setting is unset and
|
167
|
+
will be treated as `false`. Setting this value to `true` is
|
168
|
+
recommended to avoid session fixation attacks and will be the default
|
169
|
+
in Clearance 2.0. It is recommended that you opt-in to this setting
|
170
|
+
now and test your application. To silence this warning, set
|
171
|
+
`rotate_csrf_on_sign_in` to `true` or `false` in Clearance's
|
172
|
+
inializer.
|
173
|
+
|
174
|
+
For more information on session fixation, see:
|
175
|
+
https://www.owasp.org/index.php/Session_fixation
|
176
|
+
EOM
|
177
|
+
end
|
178
|
+
|
179
|
+
rotate_csrf_on_sign_in
|
180
|
+
end
|
54
181
|
end
|
55
182
|
|
183
|
+
# @return [Clearance::Configuration] Clearance's current configuration
|
56
184
|
def self.configuration
|
57
185
|
@configuration ||= Configuration.new
|
58
186
|
end
|
59
187
|
|
188
|
+
# Set Clearance's configuration
|
189
|
+
# @param config [Clearance::Configuration]
|
60
190
|
def self.configuration=(config)
|
61
191
|
@configuration = config
|
62
192
|
end
|
63
193
|
|
194
|
+
# Modify Clearance's current configuration
|
195
|
+
# @yieldparam [Clearance::Configuration] config current Clearance config
|
196
|
+
# ```
|
197
|
+
# Clearance.configure do |config|
|
198
|
+
# config.routes = false
|
199
|
+
# end
|
200
|
+
# ```
|
64
201
|
def self.configure
|
65
202
|
yield configuration
|
66
203
|
end
|
@@ -1,5 +1,22 @@
|
|
1
1
|
module Clearance
|
2
2
|
module Constraints
|
3
|
+
# Can be applied to make a set of routes visible only to users that are
|
4
|
+
# signed in.
|
5
|
+
#
|
6
|
+
# # config/routes.rb
|
7
|
+
# constraints Clearance::Constraints::SignedIn.new do
|
8
|
+
# resources :posts
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# In the example above, requests to `/posts` from users that are not signed
|
12
|
+
# in will result in a 404. You can make additional assertions about the user
|
13
|
+
# by passing a block. For instance, if you want to require that the
|
14
|
+
# signed-in user be an admin:
|
15
|
+
#
|
16
|
+
# # config/routes.rb
|
17
|
+
# constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
|
18
|
+
# resources :posts
|
19
|
+
# end
|
3
20
|
class SignedIn
|
4
21
|
def initialize(&block)
|
5
22
|
@block = block || lambda { |user| true }
|
@@ -12,18 +29,22 @@ module Clearance
|
|
12
29
|
|
13
30
|
private
|
14
31
|
|
32
|
+
# @api private
|
15
33
|
def clearance_session
|
16
34
|
@request.env[:clearance]
|
17
35
|
end
|
18
36
|
|
37
|
+
# @api private
|
19
38
|
def current_user
|
20
39
|
clearance_session.current_user
|
21
40
|
end
|
22
41
|
|
42
|
+
# @api private
|
23
43
|
def current_user_fulfills_additional_requirements?
|
24
44
|
@block.call current_user
|
25
45
|
end
|
26
46
|
|
47
|
+
# @api private
|
27
48
|
def signed_in?
|
28
49
|
clearance_session.present? && clearance_session.signed_in?
|
29
50
|
end
|
@@ -1,5 +1,15 @@
|
|
1
1
|
module Clearance
|
2
2
|
module Constraints
|
3
|
+
# Can be applied to make a set of routes visible only to users that are
|
4
|
+
# signed out.
|
5
|
+
#
|
6
|
+
# # config/routes.rb
|
7
|
+
# constraints Clearance::Constraints::SignedOut.new do
|
8
|
+
# resources :registrations, only: [:new, :create]
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# In the example above, requests to `/registrations/new` from users that are
|
12
|
+
# signed in will result in a 404.
|
3
13
|
class SignedOut
|
4
14
|
def matches?(request)
|
5
15
|
@request = request
|
@@ -8,10 +18,12 @@ module Clearance
|
|
8
18
|
|
9
19
|
private
|
10
20
|
|
21
|
+
# @api private
|
11
22
|
def clearance_session
|
12
23
|
@request.env[:clearance]
|
13
24
|
end
|
14
25
|
|
26
|
+
# @api private
|
15
27
|
def missing_session?
|
16
28
|
clearance_session.nil?
|
17
29
|
end
|
@@ -1,2 +1,14 @@
|
|
1
1
|
require 'clearance/constraints/signed_in'
|
2
2
|
require 'clearance/constraints/signed_out'
|
3
|
+
|
4
|
+
module Clearance
|
5
|
+
# Clearance provides Rails routing constraints that can control access and the
|
6
|
+
# visibility of routes at the routing layer. The {Constraints::SignedIn}
|
7
|
+
# constraint can be used to make routes visible only to signed in users. The
|
8
|
+
# {Constraints::SignedOut} constraint can be used to make routes visible only
|
9
|
+
# to signed out users.
|
10
|
+
#
|
11
|
+
# @see http://guides.rubyonrails.org/routing.html#advanced-constraints
|
12
|
+
module Constraints
|
13
|
+
end
|
14
|
+
end
|
data/lib/clearance/controller.rb
CHANGED
@@ -2,6 +2,19 @@ require 'clearance/authentication'
|
|
2
2
|
require 'clearance/authorization'
|
3
3
|
|
4
4
|
module Clearance
|
5
|
+
# Adds clearance controller helpers to the controller it is mixed into.
|
6
|
+
#
|
7
|
+
# This exposes clearance controller and helper methods such as `current_user`.
|
8
|
+
# See {Authentication} and {Authorization} documentation for complete
|
9
|
+
# documentation on the methods.
|
10
|
+
#
|
11
|
+
# The `clearance:install` generator automatically adds this mixin to
|
12
|
+
# `ApplicationController`, which is the recommended configuration.
|
13
|
+
#
|
14
|
+
# class ApplicationController < ActionController::Base
|
15
|
+
# include Clearance::Controller
|
16
|
+
# end
|
17
|
+
#
|
5
18
|
module Controller
|
6
19
|
extend ActiveSupport::Concern
|
7
20
|
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module Clearance
|
2
|
+
# Runs as the base {SignInGuard} for all requests, regardless of configured
|
3
|
+
# {Configuration#sign_in_guards}.
|
2
4
|
class DefaultSignInGuard < SignInGuard
|
5
|
+
# Runs the default sign in guard.
|
6
|
+
#
|
7
|
+
# If there is a value set in the clearance session object, then the guard
|
8
|
+
# returns {SuccessStatus}. Otherwise, it returns {FailureStatus} with the
|
9
|
+
# message returned by {#default_failure_message}.
|
10
|
+
#
|
11
|
+
# @return [SuccessStatus, FailureStatus]
|
3
12
|
def call
|
4
13
|
if session.signed_in?
|
5
14
|
success
|
@@ -8,6 +17,14 @@ module Clearance
|
|
8
17
|
end
|
9
18
|
end
|
10
19
|
|
20
|
+
# The default failure message pulled from the i18n framework.
|
21
|
+
#
|
22
|
+
# Will use the value returned from the following i18n keys, in this order:
|
23
|
+
#
|
24
|
+
# * `clearance.controllers.sessions.bad_email_or_password`
|
25
|
+
# * `flashes.failure_after_create`
|
26
|
+
#
|
27
|
+
# @return [String]
|
11
28
|
def default_failure_message
|
12
29
|
I18n.t(
|
13
30
|
:bad_email_or_password,
|
data/lib/clearance/engine.rb
CHANGED
@@ -1,12 +1,32 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "clearance"
|
2
|
+
require "rails"
|
3
3
|
|
4
4
|
module Clearance
|
5
|
+
# Makes Clearance behavior available to Rails apps on initialization. By using
|
6
|
+
# a Rails Engine rather than a Railtie, Clearance can automatically expose its
|
7
|
+
# own routes and views to the hosting application.
|
8
|
+
#
|
9
|
+
# Requiring `clearance` (likely by having it in your `Gemfile`) will
|
10
|
+
# automatically require the engine. You can opt-out of Clearance's internal
|
11
|
+
# routes by using {Configuration#routes=}. You can override the Clearance
|
12
|
+
# views by running `rails generate clearance:views`.
|
13
|
+
#
|
14
|
+
# In addition to providing routes and views, the Clearance engine:
|
15
|
+
#
|
16
|
+
# * Ensures `password` and `token` parameters are filtered out of Rails logs.
|
17
|
+
# * Mounts the {RackSession} middleware in the appropriate location
|
18
|
+
# * Reloads classes referenced in your {Configuration} on every request in
|
19
|
+
# development mode.
|
20
|
+
#
|
5
21
|
class Engine < Rails::Engine
|
6
|
-
initializer
|
22
|
+
initializer "clearance.filter" do |app|
|
7
23
|
app.config.filter_parameters += [:password, :token]
|
8
24
|
end
|
9
25
|
|
10
|
-
config.app_middleware.
|
26
|
+
config.app_middleware.use(Clearance::RackSession)
|
27
|
+
|
28
|
+
config.to_prepare do
|
29
|
+
Clearance.configuration.reload_user_model
|
30
|
+
end
|
11
31
|
end
|
12
32
|
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
module Clearance
|
2
2
|
module PasswordStrategies
|
3
|
+
# Uses BCrypt to authenticate users and store encrypted passwords.
|
4
|
+
#
|
5
|
+
# The BCrypt cost (the measure of how many key expansion iterations BCrypt
|
6
|
+
# will perform) is automatically set to the minimum allowed value when
|
7
|
+
# Rails is operating in the test environment and the default cost in all
|
8
|
+
# other envionments. This provides a speed boost in tests.
|
3
9
|
module BCrypt
|
4
10
|
require 'bcrypt'
|
5
11
|
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
12
|
def authenticated?(password)
|
9
13
|
if encrypted_password.present?
|
10
14
|
::BCrypt::Password.new(encrypted_password) == password
|
@@ -15,27 +19,18 @@ module Clearance
|
|
15
19
|
@password = new_password
|
16
20
|
|
17
21
|
if new_password.present?
|
18
|
-
|
22
|
+
cost = if defined?(::Rails) && ::Rails.env.test?
|
23
|
+
::BCrypt::Engine::MIN_COST
|
24
|
+
else
|
25
|
+
::BCrypt::Engine::DEFAULT_COST
|
26
|
+
end
|
27
|
+
|
28
|
+
self.encrypted_password = ::BCrypt::Password.create(
|
29
|
+
new_password,
|
30
|
+
cost: cost,
|
31
|
+
)
|
19
32
|
end
|
20
33
|
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def encrypt(password)
|
25
|
-
::BCrypt::Password.create(password, cost: cost)
|
26
|
-
end
|
27
|
-
|
28
|
-
def cost
|
29
|
-
if test_environment?
|
30
|
-
::BCrypt::Engine::MIN_COST
|
31
|
-
else
|
32
|
-
::BCrypt::Engine::DEFAULT_COST
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_environment?
|
37
|
-
defined?(::Rails) && ::Rails.env.test?
|
38
|
-
end
|
39
34
|
end
|
40
35
|
end
|
41
36
|
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module Clearance
|
2
2
|
module PasswordStrategies
|
3
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies` gem
|
3
4
|
module BCryptMigrationFromSHA1
|
5
|
+
DEPRECATION_MESSAGE = "[DEPRECATION] The BCryptMigrationFromSha1 " \
|
6
|
+
"password strategy has been deprecated and will be removed from " \
|
7
|
+
"Clearance 2.0. BCrypt is the only officially supported strategy, " \
|
8
|
+
"though you are free to provide your own. To continue using this " \
|
9
|
+
"strategy, add clearance-deprecated_password_strategies to your " \
|
10
|
+
"Gemfile."
|
11
|
+
|
12
|
+
# @api private
|
4
13
|
class BCryptUser
|
5
14
|
include Clearance::PasswordStrategies::BCrypt
|
6
15
|
|
@@ -11,6 +20,7 @@ module Clearance
|
|
11
20
|
delegate :encrypted_password, :encrypted_password=, to: :@user
|
12
21
|
end
|
13
22
|
|
23
|
+
# @api private
|
14
24
|
class SHA1User
|
15
25
|
include Clearance::PasswordStrategies::SHA1
|
16
26
|
|
@@ -21,17 +31,24 @@ module Clearance
|
|
21
31
|
delegate :salt, :salt=, :encrypted_password, :encrypted_password=, to: :@user
|
22
32
|
end
|
23
33
|
|
34
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
35
|
+
# gem
|
24
36
|
def authenticated?(password)
|
37
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
25
38
|
authenticated_with_sha1?(password) || authenticated_with_bcrypt?(password)
|
26
39
|
end
|
27
40
|
|
41
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
42
|
+
# gem
|
28
43
|
def password=(new_password)
|
44
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
29
45
|
@password = new_password
|
30
46
|
BCryptUser.new(self).password = new_password
|
31
47
|
end
|
32
48
|
|
33
49
|
private
|
34
50
|
|
51
|
+
# @api private
|
35
52
|
def authenticated_with_bcrypt?(password)
|
36
53
|
begin
|
37
54
|
BCryptUser.new(self).authenticated? password
|
@@ -40,6 +57,7 @@ module Clearance
|
|
40
57
|
end
|
41
58
|
end
|
42
59
|
|
60
|
+
# @api private
|
43
61
|
def authenticated_with_sha1?(password)
|
44
62
|
if sha1_password?
|
45
63
|
if SHA1User.new(self).authenticated? password
|
@@ -50,6 +68,7 @@ module Clearance
|
|
50
68
|
end
|
51
69
|
end
|
52
70
|
|
71
|
+
# @api private
|
53
72
|
def sha1_password?
|
54
73
|
self.encrypted_password =~ /^[a-f0-9]{40}$/
|
55
74
|
end
|
@@ -3,12 +3,25 @@ require 'base64'
|
|
3
3
|
|
4
4
|
module Clearance
|
5
5
|
module PasswordStrategies
|
6
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies` gem
|
6
7
|
module Blowfish
|
8
|
+
DEPRECATION_MESSAGE = "[DEPRECATION] The Blowfish password strategy " \
|
9
|
+
"has been deprecated and will be removed from Clearance 2.0. BCrypt " \
|
10
|
+
"is the only officially supported strategy, though you are free to " \
|
11
|
+
"provide your own. To continue using this strategy add " \
|
12
|
+
"clearance-deprecated_password_strategies to your Gemfile."
|
13
|
+
|
14
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
15
|
+
# gem
|
7
16
|
def authenticated?(password)
|
17
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
8
18
|
encrypted_password == encrypt(password)
|
9
19
|
end
|
10
20
|
|
21
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
22
|
+
# gem
|
11
23
|
def password=(new_password)
|
24
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
12
25
|
@password = new_password
|
13
26
|
initialize_salt_if_necessary
|
14
27
|
|
@@ -19,10 +32,12 @@ module Clearance
|
|
19
32
|
|
20
33
|
protected
|
21
34
|
|
35
|
+
# @api private
|
22
36
|
def encrypt(string)
|
23
37
|
generate_hash("--#{salt}--#{string}--")
|
24
38
|
end
|
25
39
|
|
40
|
+
# @api private
|
26
41
|
def generate_hash(string)
|
27
42
|
cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
|
28
43
|
cipher.key = Digest::SHA256.digest(salt)
|
@@ -30,12 +45,14 @@ module Clearance
|
|
30
45
|
Base64.encode64(hash).encode('utf-8')
|
31
46
|
end
|
32
47
|
|
48
|
+
# @api private
|
33
49
|
def initialize_salt_if_necessary
|
34
50
|
if salt.blank?
|
35
51
|
self.salt = generate_salt
|
36
52
|
end
|
37
53
|
end
|
38
54
|
|
55
|
+
# @api private
|
39
56
|
def generate_salt
|
40
57
|
Base64.encode64(SecureRandom.hex(20)).encode('utf-8')
|
41
58
|
end
|
@@ -1,15 +1,28 @@
|
|
1
1
|
module Clearance
|
2
2
|
module PasswordStrategies
|
3
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies` gem
|
3
4
|
module SHA1
|
4
5
|
require 'digest/sha1'
|
5
6
|
|
7
|
+
DEPRECATION_MESSAGE = "[DEPRECATION] The SHA1 password strategy " \
|
8
|
+
"has been deprecated and will be removed from Clearance 2.0. BCrypt " \
|
9
|
+
"is the only officially supported strategy, though you are free to " \
|
10
|
+
"provide your own. To continue using this strategy add " \
|
11
|
+
"clearance-deprecated_password_strategies to your Gemfile."
|
12
|
+
|
6
13
|
extend ActiveSupport::Concern
|
7
14
|
|
15
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
16
|
+
# gem
|
8
17
|
def authenticated?(password)
|
18
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
9
19
|
encrypted_password == encrypt(password)
|
10
20
|
end
|
11
21
|
|
22
|
+
# @deprecated Use {BCrypt} or `clearance-deprecated_password_strategies`
|
23
|
+
# gem
|
12
24
|
def password=(new_password)
|
25
|
+
warn "#{Kernel.caller.first}: #{DEPRECATION_MESSAGE}"
|
13
26
|
@password = new_password
|
14
27
|
initialize_salt_if_necessary
|
15
28
|
|
@@ -20,20 +33,24 @@ module Clearance
|
|
20
33
|
|
21
34
|
private
|
22
35
|
|
36
|
+
# @api private
|
23
37
|
def encrypt(string)
|
24
38
|
generate_hash "--#{salt}--#{string}--"
|
25
39
|
end
|
26
40
|
|
41
|
+
# @api private
|
27
42
|
def generate_hash(string)
|
28
43
|
Digest::SHA1.hexdigest(string).encode 'UTF-8'
|
29
44
|
end
|
30
45
|
|
46
|
+
# @api private
|
31
47
|
def initialize_salt_if_necessary
|
32
48
|
if salt.blank?
|
33
49
|
self.salt = generate_salt
|
34
50
|
end
|
35
51
|
end
|
36
52
|
|
53
|
+
# @api private
|
37
54
|
def generate_salt
|
38
55
|
SecureRandom.hex(20).encode('UTF-8')
|
39
56
|
end
|