clearance 1.10.1 → 1.17.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +20 -8
- data/.yardopts +3 -0
- data/Appraisals +13 -16
- data/Gemfile +7 -5
- data/Gemfile.lock +124 -130
- data/NEWS.md +171 -2
- data/README.md +99 -42
- data/app/controllers/clearance/passwords_controller.rb +35 -21
- data/app/controllers/clearance/sessions_controller.rb +17 -3
- data/app/controllers/clearance/users_controller.rb +10 -4
- data/app/mailers/clearance_mailer.rb +2 -3
- data/app/views/clearance_mailer/change_password.text.erb +1 -1
- data/app/views/layouts/application.html.erb +0 -1
- data/bin/setup +6 -2
- data/clearance.gemspec +5 -2
- data/config/locales/clearance.en.yml +9 -0
- data/gemfiles/rails_4.2.gemfile +20 -0
- data/gemfiles/rails_5.0.gemfile +21 -0
- data/gemfiles/rails_5.1.gemfile +21 -0
- data/gemfiles/rails_5.2.gemfile +21 -0
- data/lib/clearance/authentication.rb +63 -3
- data/lib/clearance/authorization.rb +48 -5
- data/lib/clearance/back_door.rb +55 -6
- data/lib/clearance/configuration.rb +50 -10
- 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 +18 -5
- data/lib/clearance/password_strategies/bcrypt.rb +16 -21
- data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +10 -0
- data/lib/clearance/password_strategies/blowfish.rb +10 -1
- data/lib/clearance/password_strategies/sha1.rb +9 -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 +62 -13
- 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 +57 -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 +183 -4
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/install_generator.rb +28 -9
- data/lib/generators/clearance/install/templates/README +1 -1
- data/lib/generators/clearance/install/templates/clearance.rb +1 -0
- data/lib/generators/clearance/install/templates/db/migrate/{add_clearance_to_users.rb → add_clearance_to_users.rb.erb} +3 -3
- data/lib/generators/clearance/install/templates/db/migrate/{create_users.rb → create_users.rb.erb} +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/factories/clearance.rb +2 -2
- 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 -3
- data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_in_spec.rb.tt +3 -3
- 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 +2 -2
- data/lib/generators/clearance/specs/templates/support/features/clearance_helpers.rb +2 -2
- data/spec/acceptance/clearance_installation_spec.rb +15 -7
- 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/Gemfile +1 -1
- data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -1
- data/spec/clearance/back_door_spec.rb +70 -6
- data/spec/clearance/session_spec.rb +4 -16
- data/spec/clearance/testing/controller_helpers_spec.rb +38 -0
- data/spec/clearance/testing/view_helpers_spec.rb +37 -0
- data/spec/configuration_spec.rb +79 -86
- data/spec/controllers/apis_controller_spec.rb +6 -2
- data/spec/controllers/forgeries_controller_spec.rb +12 -3
- data/spec/controllers/passwords_controller_spec.rb +74 -38
- data/spec/controllers/permissions_controller_spec.rb +13 -3
- data/spec/controllers/sessions_controller_spec.rb +40 -11
- data/spec/controllers/users_controller_spec.rb +16 -8
- data/spec/dummy/app/controllers/application_controller.rb +5 -1
- data/spec/dummy/application.rb +9 -11
- data/spec/factories.rb +5 -5
- data/spec/generators/clearance/install/install_generator_spec.rb +29 -3
- data/spec/generators/clearance/routes/routes_generator_spec.rb +5 -1
- data/spec/helpers/helper_helpers_spec.rb +10 -0
- data/spec/{user_spec.rb → models/user_spec.rb} +10 -1
- data/spec/password_strategies/blowfish_spec.rb +1 -1
- data/spec/requests/cookie_options_spec.rb +52 -0
- data/spec/requests/csrf_rotation_spec.rb +35 -0
- data/spec/requests/password_maintenance_spec.rb +18 -0
- data/spec/requests/token_expiration_spec.rb +54 -0
- data/spec/spec_helper.rb +22 -4
- data/spec/support/environment.rb +12 -0
- data/spec/support/generator_spec_helpers.rb +13 -1
- data/spec/support/http_method_shim.rb +25 -0
- data/spec/support/request_with_remember_token.rb +5 -0
- data/spec/views/view_helpers_spec.rb +10 -0
- metadata +69 -15
- data/gemfiles/rails3.2.gemfile +0 -18
- data/gemfiles/rails4.0.gemfile +0 -19
- data/gemfiles/rails4.1.gemfile +0 -18
- data/gemfiles/rails4.2.gemfile +0 -18
- data/lib/generators/clearance/install/templates/user.rb +0 -3
- data/spec/clearance/testing/helpers_spec.rb +0 -38
@@ -3,8 +3,11 @@ module Clearance
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
|
6
|
+
if respond_to?(:helper_method)
|
7
|
+
helper_method :current_user, :signed_in?, :signed_out?
|
8
|
+
end
|
9
|
+
|
10
|
+
private(
|
8
11
|
:authenticate,
|
9
12
|
:current_user,
|
10
13
|
:current_user=,
|
@@ -16,40 +19,96 @@ module Clearance
|
|
16
19
|
)
|
17
20
|
end
|
18
21
|
|
22
|
+
# Authenticate a user with a provided email and password
|
23
|
+
# @param [ActionController::Parameters] params The parameters from the
|
24
|
+
# sign in form. `params[:session][:email]` and
|
25
|
+
# `params[:session][:password]` are required.
|
26
|
+
# @return [User, nil] The user or nil if authentication fails.
|
19
27
|
def authenticate(params)
|
20
28
|
Clearance.configuration.user_model.authenticate(
|
21
29
|
params[:session][:email], params[:session][:password]
|
22
30
|
)
|
23
31
|
end
|
24
32
|
|
33
|
+
# Get the user from the current clearance session. Exposed as a
|
34
|
+
# `helper_method`, making it visible to views. Prefer {#signed_in?} or
|
35
|
+
# {#signed_out?} if you only want to check for the presence of a current
|
36
|
+
# user rather than access the actual user.
|
37
|
+
#
|
38
|
+
# @return [User, nil] The user if one is signed in or nil otherwise.
|
25
39
|
def current_user
|
26
40
|
clearance_session.current_user
|
27
41
|
end
|
28
42
|
|
43
|
+
# @deprecated Use the {#sign_in} method instead.
|
29
44
|
def current_user=(user)
|
30
45
|
warn "#{Kernel.caller.first}: [DEPRECATION] " +
|
31
46
|
'Assigning the current_user has been deprecated. Use the sign_in method instead.'
|
32
47
|
clearance_session.sign_in user
|
33
48
|
end
|
34
49
|
|
50
|
+
# Sign in the provided user.
|
51
|
+
# @param [User] user
|
52
|
+
#
|
53
|
+
# Signing in will run the stack of {Configuration#sign_in_guards}.
|
54
|
+
#
|
55
|
+
# You can provide a block to this method to handle the result of that stack.
|
56
|
+
# Your block will receive either a {SuccessStatus} or {FailureStatus}
|
57
|
+
#
|
58
|
+
# sign_in(user) do |status|
|
59
|
+
# if status.success?
|
60
|
+
# # ...
|
61
|
+
# else
|
62
|
+
# # ...
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# For an example of how clearance uses this internally, see
|
67
|
+
# {SessionsController#create}.
|
68
|
+
#
|
69
|
+
# Signing in will also regenerate the CSRF token for the current session,
|
70
|
+
# provided {Configuration#rotate_csrf_token_on_sign_in} is set.
|
35
71
|
def sign_in(user, &block)
|
36
|
-
clearance_session.sign_in
|
72
|
+
clearance_session.sign_in(user, &block)
|
73
|
+
|
74
|
+
if signed_in? && Clearance.configuration.rotate_csrf_on_sign_in?
|
75
|
+
session.delete(:_csrf_token)
|
76
|
+
form_authenticity_token
|
77
|
+
end
|
37
78
|
end
|
38
79
|
|
80
|
+
# Destroy the current user's Clearance session.
|
81
|
+
# See {Session#sign_out} for specifics.
|
39
82
|
def sign_out
|
40
83
|
clearance_session.sign_out
|
41
84
|
end
|
42
85
|
|
86
|
+
# True if there is a currently-signed-in user. Exposed as a `helper_method`,
|
87
|
+
# making it available to views.
|
88
|
+
#
|
89
|
+
# Using `signed_in?` is preferable to checking {#current_user} against nil
|
90
|
+
# as it will allow you to introduce a null user object more simply at a
|
91
|
+
# later date.
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
43
94
|
def signed_in?
|
44
95
|
clearance_session.signed_in?
|
45
96
|
end
|
46
97
|
|
98
|
+
# True if there is no currently-signed-in user. Exposed as a
|
99
|
+
# `helper_method`, making it available to views.
|
100
|
+
#
|
101
|
+
# Usings `signed_out?` is preferable to checking for presence of
|
102
|
+
# {#current_user} as it will allow you to introduce a null user object more
|
103
|
+
# simply at a later date.
|
47
104
|
def signed_out?
|
48
105
|
!signed_in?
|
49
106
|
end
|
50
107
|
|
51
108
|
# CSRF protection in Rails >= 3.0.4
|
109
|
+
#
|
52
110
|
# http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails
|
111
|
+
# @private
|
53
112
|
def handle_unverified_request
|
54
113
|
super
|
55
114
|
sign_out
|
@@ -57,6 +116,7 @@ module Clearance
|
|
57
116
|
|
58
117
|
protected
|
59
118
|
|
119
|
+
# @api private
|
60
120
|
def clearance_session
|
61
121
|
request.env[:clearance]
|
62
122
|
end
|
@@ -3,23 +3,52 @@ module Clearance
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
6
|
+
private :authorize, :deny_access, :require_login
|
7
7
|
end
|
8
8
|
|
9
|
+
# Use as a `before_action` to require a user be signed in to proceed.
|
10
|
+
# {Authentication#signed_in?} is used to determine if there is a signed in
|
11
|
+
# user or not.
|
12
|
+
#
|
13
|
+
# class PostsController < ApplicationController
|
14
|
+
# before_action :require_login
|
15
|
+
#
|
16
|
+
# def index
|
17
|
+
# # ...
|
18
|
+
# end
|
19
|
+
# end
|
9
20
|
def require_login
|
10
21
|
unless signed_in?
|
11
|
-
deny_access
|
22
|
+
deny_access(I18n.t("flashes.failure_when_not_signed_in"))
|
12
23
|
end
|
13
24
|
end
|
14
25
|
|
26
|
+
# @deprecated use {#require_login}
|
15
27
|
def authorize
|
16
|
-
warn "[DEPRECATION] Clearance's `authorize`
|
28
|
+
warn "[DEPRECATION] Clearance's `authorize` before_action is " +
|
17
29
|
"deprecated. Use `require_login` instead. Be sure to update any " +
|
18
|
-
"instances of `
|
30
|
+
"instances of `skip_before_action :authorize` or " +
|
19
31
|
"`skip_before_action :authorize` as well"
|
20
32
|
require_login
|
21
33
|
end
|
22
34
|
|
35
|
+
# Responds to unauthorized requests in a manner fitting the request format.
|
36
|
+
# `js`, `json`, and `xml` requests will receive a 401 with no body. All
|
37
|
+
# other formats will be redirected appropriately and can optionally have the
|
38
|
+
# flash message set.
|
39
|
+
#
|
40
|
+
# When redirecting, the originally requested url will be stored in the
|
41
|
+
# session (`session[:return_to]`), allowing it to be used as a redirect url
|
42
|
+
# once the user has successfully signed in.
|
43
|
+
#
|
44
|
+
# If there is a signed in user, the request will be redirected according to
|
45
|
+
# the value returned from {#url_after_denied_access_when_signed_in}.
|
46
|
+
#
|
47
|
+
# If there is no signed in user, the request will be redirected according to
|
48
|
+
# the value returned from {#url_after_denied_access_when_signed_out}.
|
49
|
+
# For the exact redirect behavior, see {#redirect_request}.
|
50
|
+
#
|
51
|
+
# @param [String] flash_message
|
23
52
|
def deny_access(flash_message = nil)
|
24
53
|
respond_to do |format|
|
25
54
|
format.any(:js, :json, :xml) { head :unauthorized }
|
@@ -29,6 +58,7 @@ module Clearance
|
|
29
58
|
|
30
59
|
protected
|
31
60
|
|
61
|
+
# @api private
|
32
62
|
def redirect_request(flash_message)
|
33
63
|
store_location
|
34
64
|
|
@@ -43,36 +73,49 @@ module Clearance
|
|
43
73
|
end
|
44
74
|
end
|
45
75
|
|
76
|
+
# @api private
|
46
77
|
def clear_return_to
|
47
78
|
session[:return_to] = nil
|
48
79
|
end
|
49
80
|
|
81
|
+
# @api private
|
50
82
|
def store_location
|
51
83
|
if request.get?
|
52
84
|
session[:return_to] = request.original_fullpath
|
53
85
|
end
|
54
86
|
end
|
55
87
|
|
88
|
+
# @api private
|
56
89
|
def redirect_back_or(default)
|
57
90
|
redirect_to(return_to || default)
|
58
91
|
clear_return_to
|
59
92
|
end
|
60
93
|
|
94
|
+
# @api private
|
61
95
|
def return_to
|
62
96
|
if return_to_url
|
63
97
|
uri = URI.parse(return_to_url)
|
64
|
-
"#{uri.path}?#{uri.query}".chomp(
|
98
|
+
"#{uri.path}?#{uri.query}".chomp("?") + "##{uri.fragment}".chomp("#")
|
65
99
|
end
|
66
100
|
end
|
67
101
|
|
102
|
+
# @api private
|
68
103
|
def return_to_url
|
69
104
|
session[:return_to]
|
70
105
|
end
|
71
106
|
|
107
|
+
# Used as the redirect location when {#deny_access} is called and there is a
|
108
|
+
# currently signed in user.
|
109
|
+
#
|
110
|
+
# @return [String]
|
72
111
|
def url_after_denied_access_when_signed_in
|
73
112
|
Clearance.configuration.redirect_url
|
74
113
|
end
|
75
114
|
|
115
|
+
# Used as the redirect location when {#deny_access} is called and there is
|
116
|
+
# no currently signed in user.
|
117
|
+
#
|
118
|
+
# @return [String]
|
76
119
|
def url_after_denied_access_when_signed_out
|
77
120
|
sign_in_url
|
78
121
|
end
|
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,28 @@ 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)
|
34
|
+
unless environment_is_allowed?
|
35
|
+
raise error_message
|
36
|
+
end
|
37
|
+
|
22
38
|
@app = app
|
39
|
+
@block = block
|
23
40
|
end
|
24
41
|
|
25
42
|
def call(env)
|
@@ -29,14 +46,46 @@ module Clearance
|
|
29
46
|
|
30
47
|
private
|
31
48
|
|
49
|
+
# @api private
|
32
50
|
def sign_in_through_the_back_door(env)
|
33
|
-
params = Rack::Utils.parse_query(env[
|
34
|
-
|
51
|
+
params = Rack::Utils.parse_query(env["QUERY_STRING"])
|
52
|
+
user_param = params["as"]
|
35
53
|
|
36
|
-
if
|
37
|
-
user =
|
54
|
+
if user_param.present?
|
55
|
+
user = find_user(user_param)
|
38
56
|
env[:clearance].sign_in(user)
|
39
57
|
end
|
40
58
|
end
|
59
|
+
|
60
|
+
# @api private
|
61
|
+
def find_user(user_param)
|
62
|
+
if @block
|
63
|
+
@block.call(user_param)
|
64
|
+
else
|
65
|
+
Clearance.configuration.user_model.find(user_param)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def environment_is_allowed?
|
71
|
+
allowed_environments.include? ENV["RAILS_ENV"]
|
72
|
+
end
|
73
|
+
|
74
|
+
# @api private
|
75
|
+
def allowed_environments
|
76
|
+
Clearance.configuration.allowed_backdoor_environments || []
|
77
|
+
end
|
78
|
+
|
79
|
+
# @api private
|
80
|
+
def error_message
|
81
|
+
unless allowed_environments.empty?
|
82
|
+
<<-EOS.squish
|
83
|
+
Can't use auth backdoor outside of
|
84
|
+
configured environments (#{allowed_environments.join(", ")}).
|
85
|
+
EOS
|
86
|
+
else
|
87
|
+
"BackDoor auth is disabled."
|
88
|
+
end
|
89
|
+
end
|
41
90
|
end
|
42
91
|
end
|
@@ -35,8 +35,8 @@ module Clearance
|
|
35
35
|
# @return [String]
|
36
36
|
attr_accessor :cookie_path
|
37
37
|
|
38
|
-
# Controls whether the
|
39
|
-
# cookie. Defaults to `
|
38
|
+
# Controls whether the HttpOnly flag should be set on the remember token
|
39
|
+
# cookie. Defaults to `true`, which prevents the cookie from being made
|
40
40
|
# available to JavaScript. For more see
|
41
41
|
# [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.2.6).
|
42
42
|
# @return [Boolean]
|
@@ -48,8 +48,8 @@ module Clearance
|
|
48
48
|
attr_accessor :mailer_sender
|
49
49
|
|
50
50
|
# The password strategy to use when authenticating and setting passwords.
|
51
|
-
# Defaults to
|
52
|
-
# @return [
|
51
|
+
# Defaults to {Clearance::PasswordStrategies::BCrypt}.
|
52
|
+
# @return [Module #authenticated? #password=]
|
53
53
|
attr_accessor :password_strategy
|
54
54
|
|
55
55
|
# The default path Clearance will redirect signed in users to.
|
@@ -58,6 +58,11 @@ module Clearance
|
|
58
58
|
# @return [String]
|
59
59
|
attr_accessor :redirect_url
|
60
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
|
+
|
61
66
|
# Set to `false` to disable Clearance's built-in routes.
|
62
67
|
# Defaults to `true`. When set to false, your app is responsible for all
|
63
68
|
# routes. You can dump a copy of Clearance's default routes with
|
@@ -81,19 +86,26 @@ module Clearance
|
|
81
86
|
attr_accessor :sign_in_guards
|
82
87
|
|
83
88
|
# The ActiveRecord class that represents users in your application.
|
84
|
-
#
|
89
|
+
# Defaults to `::User`.
|
85
90
|
# @return [ActiveRecord::Base]
|
86
91
|
attr_accessor :user_model
|
87
92
|
|
93
|
+
# The array of allowed environments where `Clearance::BackDoor` is enabled.
|
94
|
+
# Defaults to ["test", "ci", "development"]
|
95
|
+
# @return [Array<String>]
|
96
|
+
attr_accessor :allowed_backdoor_environments
|
97
|
+
|
88
98
|
def initialize
|
89
99
|
@allow_sign_up = true
|
90
|
-
@
|
100
|
+
@allowed_backdoor_environments = ["test", "ci", "development"]
|
91
101
|
@cookie_domain = nil
|
92
|
-
@
|
102
|
+
@cookie_expiration = ->(cookies) { 1.year.from_now.utc }
|
93
103
|
@cookie_name = "remember_token"
|
94
|
-
@
|
104
|
+
@cookie_path = '/'
|
105
|
+
@httponly = true
|
95
106
|
@mailer_sender = 'reply@example.com'
|
96
107
|
@redirect_url = '/'
|
108
|
+
@rotate_csrf_on_sign_in = nil
|
97
109
|
@routes = true
|
98
110
|
@secure_cookie = false
|
99
111
|
@sign_in_guards = []
|
@@ -121,12 +133,20 @@ module Clearance
|
|
121
133
|
end
|
122
134
|
end
|
123
135
|
|
136
|
+
# The name of user parameter for the configured user model.
|
137
|
+
# This is derived from the `model_name` of the `user_model` setting.
|
138
|
+
# In the default configuration, this is `user`.
|
139
|
+
# @return [Symbol]
|
140
|
+
def user_parameter
|
141
|
+
user_model.model_name.singular.to_sym
|
142
|
+
end
|
143
|
+
|
124
144
|
# The name of foreign key parameter for the configured user model.
|
125
145
|
# This is derived from the `model_name` of the `user_model` setting.
|
126
146
|
# In the default configuration, this is `user_id`.
|
127
147
|
# @return [Symbol]
|
128
148
|
def user_id_parameter
|
129
|
-
"#{
|
149
|
+
"#{user_parameter}_id".to_sym
|
130
150
|
end
|
131
151
|
|
132
152
|
# @return [Boolean] are Clearance's built-in routes enabled?
|
@@ -138,12 +158,32 @@ module Clearance
|
|
138
158
|
# This is called from the Clearance engine to reload the configured
|
139
159
|
# user class during each request while in development mode, but only once
|
140
160
|
# in production.
|
141
|
-
#
|
161
|
+
#
|
162
|
+
# @api private
|
142
163
|
def reload_user_model
|
143
164
|
if @user_model.present?
|
144
165
|
@user_model = @user_model.to_s.constantize
|
145
166
|
end
|
146
167
|
end
|
168
|
+
|
169
|
+
def rotate_csrf_on_sign_in?
|
170
|
+
if rotate_csrf_on_sign_in.nil?
|
171
|
+
warn <<-EOM.squish
|
172
|
+
Clearance's `rotate_csrf_on_sign_in` configuration setting is unset and
|
173
|
+
will be treated as `false`. Setting this value to `true` is
|
174
|
+
recommended to avoid session fixation attacks and will be the default
|
175
|
+
in Clearance 2.0. It is recommended that you opt-in to this setting
|
176
|
+
now and test your application. To silence this warning, set
|
177
|
+
`rotate_csrf_on_sign_in` to `true` or `false` in Clearance's
|
178
|
+
initializer.
|
179
|
+
|
180
|
+
For more information on session fixation, see:
|
181
|
+
https://www.owasp.org/index.php/Session_fixation
|
182
|
+
EOM
|
183
|
+
end
|
184
|
+
|
185
|
+
rotate_csrf_on_sign_in
|
186
|
+
end
|
147
187
|
end
|
148
188
|
|
149
189
|
# @return [Clearance::Configuration] Clearance's current configuration
|
@@ -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,16 +1,29 @@
|
|
1
1
|
require "clearance"
|
2
|
-
require "rails"
|
2
|
+
require "rails/engine"
|
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
22
|
initializer "clearance.filter" do |app|
|
7
23
|
app.config.filter_parameters += [:password, :token]
|
8
24
|
end
|
9
25
|
|
10
|
-
config.app_middleware.
|
11
|
-
ActionDispatch::ParamsParser,
|
12
|
-
Clearance::RackSession
|
13
|
-
)
|
26
|
+
config.app_middleware.use(Clearance::RackSession)
|
14
27
|
|
15
28
|
config.to_prepare do
|
16
29
|
Clearance.configuration.reload_user_model
|
@@ -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
|