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.

Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +25 -6
  4. data/.yardopts +6 -0
  5. data/Appraisals +15 -4
  6. data/CONTRIBUTING.md +4 -1
  7. data/Gemfile +5 -3
  8. data/Gemfile.lock +102 -96
  9. data/NEWS.md +742 -311
  10. data/README.md +217 -339
  11. data/app/controllers/clearance/passwords_controller.rb +35 -21
  12. data/app/controllers/clearance/sessions_controller.rb +17 -4
  13. data/app/controllers/clearance/users_controller.rb +10 -4
  14. data/app/mailers/clearance_mailer.rb +2 -3
  15. data/app/views/clearance_mailer/change_password.html.erb +6 -3
  16. data/app/views/clearance_mailer/change_password.text.erb +5 -0
  17. data/app/views/layouts/application.html.erb +2 -2
  18. data/app/views/passwords/create.html.erb +1 -1
  19. data/app/views/passwords/edit.html.erb +2 -2
  20. data/app/views/passwords/new.html.erb +2 -2
  21. data/app/views/sessions/_form.html.erb +2 -2
  22. data/app/views/sessions/new.html.erb +1 -1
  23. data/app/views/users/new.html.erb +2 -2
  24. data/bin/setup +6 -2
  25. data/config/locales/clearance.en.yml +6 -0
  26. data/db/migrate/20110111224543_create_clearance_users.rb +1 -1
  27. data/gemfiles/{rails3.2.gemfile → rails32.gemfile} +4 -2
  28. data/gemfiles/{rails4.0.gemfile → rails40.gemfile} +6 -3
  29. data/gemfiles/{rails4.1.gemfile → rails41.gemfile} +6 -3
  30. data/gemfiles/{rails4.2.gemfile → rails42.gemfile} +6 -3
  31. data/gemfiles/rails50.gemfile +21 -0
  32. data/lib/clearance/authentication.rb +61 -2
  33. data/lib/clearance/authorization.rb +47 -4
  34. data/lib/clearance/back_door.rb +29 -6
  35. data/lib/clearance/configuration.rb +152 -15
  36. data/lib/clearance/constraints/signed_in.rb +21 -0
  37. data/lib/clearance/constraints/signed_out.rb +12 -0
  38. data/lib/clearance/constraints.rb +12 -0
  39. data/lib/clearance/controller.rb +13 -0
  40. data/lib/clearance/default_sign_in_guard.rb +17 -0
  41. data/lib/clearance/engine.rb +24 -4
  42. data/lib/clearance/password_strategies/bcrypt.rb +16 -21
  43. data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +19 -0
  44. data/lib/clearance/password_strategies/blowfish.rb +17 -0
  45. data/lib/clearance/password_strategies/sha1.rb +17 -0
  46. data/lib/clearance/password_strategies.rb +13 -0
  47. data/lib/clearance/rack_session.rb +13 -0
  48. data/lib/clearance/rspec.rb +15 -4
  49. data/lib/clearance/session.rb +46 -1
  50. data/lib/clearance/session_status.rb +7 -0
  51. data/lib/clearance/sign_in_guard.rb +65 -0
  52. data/lib/clearance/test_unit.rb +3 -3
  53. data/lib/clearance/testing/controller_helpers.rb +44 -0
  54. data/lib/clearance/testing/deny_access_matcher.rb +36 -2
  55. data/lib/clearance/testing/helpers.rb +9 -25
  56. data/lib/clearance/testing/view_helpers.rb +32 -0
  57. data/lib/clearance/token.rb +7 -0
  58. data/lib/clearance/user.rb +182 -4
  59. data/lib/clearance/version.rb +1 -1
  60. data/lib/clearance.rb +2 -0
  61. data/lib/generators/clearance/install/install_generator.rb +24 -5
  62. data/lib/generators/clearance/install/templates/clearance.rb +1 -0
  63. data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb +3 -3
  64. data/lib/generators/clearance/install/templates/db/migrate/create_users.rb +2 -2
  65. data/lib/generators/clearance/install/templates/user.rb.erb +3 -0
  66. data/lib/generators/clearance/routes/routes_generator.rb +23 -0
  67. data/lib/generators/clearance/routes/templates/routes.rb +7 -7
  68. data/lib/generators/clearance/specs/templates/features/clearance/user_signs_out_spec.rb.tt +1 -1
  69. data/lib/generators/clearance/specs/templates/features/clearance/visitor_resets_password_spec.rb.tt +12 -2
  70. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_in_spec.rb.tt +1 -1
  71. data/lib/generators/clearance/specs/templates/features/clearance/visitor_signs_up_spec.rb.tt +1 -1
  72. data/lib/generators/clearance/specs/templates/features/clearance/visitor_updates_password_spec.rb.tt +1 -1
  73. data/spec/acceptance/clearance_installation_spec.rb +4 -1
  74. data/spec/app_templates/app/models/rails5/user.rb +5 -0
  75. data/spec/app_templates/config/initializers/clearance.rb +2 -0
  76. data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -1
  77. data/spec/app_templates/testapp/config/initializers/action_mailer.rb +1 -3
  78. data/spec/clearance/back_door_spec.rb +25 -6
  79. data/spec/clearance/controller_spec.rb +11 -0
  80. data/spec/clearance/rack_session_spec.rb +5 -5
  81. data/spec/clearance/session_spec.rb +2 -15
  82. data/spec/clearance/testing/{helpers_spec.rb → controller_helpers_spec.rb} +12 -12
  83. data/spec/clearance/testing/view_helpers_spec.rb +37 -0
  84. data/spec/configuration_spec.rb +94 -86
  85. data/spec/controllers/apis_controller_spec.rb +6 -2
  86. data/spec/controllers/forgeries_controller_spec.rb +6 -1
  87. data/spec/controllers/passwords_controller_spec.rb +17 -16
  88. data/spec/controllers/permissions_controller_spec.rb +13 -3
  89. data/spec/controllers/sessions_controller_spec.rb +4 -4
  90. data/spec/dummy/app/controllers/application_controller.rb +5 -1
  91. data/spec/dummy/application.rb +4 -0
  92. data/spec/generators/clearance/install/install_generator_spec.rb +29 -3
  93. data/spec/generators/clearance/routes/routes_generator_spec.rb +5 -1
  94. data/spec/generators/clearance/views/views_generator_spec.rb +11 -10
  95. data/spec/helpers/helper_helpers_spec.rb +10 -0
  96. data/spec/mailers/clearance_mailer_spec.rb +13 -19
  97. data/spec/password_strategies/bcrypt_migration_from_sha1_spec.rb +6 -0
  98. data/spec/password_strategies/blowfish_spec.rb +6 -0
  99. data/spec/password_strategies/sha1_spec.rb +6 -0
  100. data/spec/requests/csrf_rotation_spec.rb +33 -0
  101. data/spec/spec_helper.rb +11 -2
  102. data/spec/support/generator_spec_helpers.rb +13 -1
  103. data/spec/support/http_method_shim.rb +23 -0
  104. data/spec/user_spec.rb +9 -0
  105. data/spec/views/view_helpers_spec.rb +10 -0
  106. metadata +22 -9
  107. data/lib/generators/clearance/install/templates/user.rb +0 -3
@@ -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['QUERY_STRING'])
34
- user_id = params['as']
47
+ params = Rack::Utils.parse_query(env["QUERY_STRING"])
48
+ user_param = params["as"]
35
49
 
36
- if user_id.present?
37
- user = Clearance.configuration.user_model.find(user_id)
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
- attr_writer :allow_sign_up, :routes
4
-
5
- attr_accessor \
6
- :cookie_domain,
7
- :cookie_expiration,
8
- :cookie_name,
9
- :cookie_path,
10
- :httponly,
11
- :mailer_sender,
12
- :password_strategy,
13
- :redirect_url,
14
- :secure_cookie,
15
- :sign_in_guards,
16
- :user_model
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
- "#{user_model.model_name.singular}_id".to_sym
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
@@ -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,
@@ -1,12 +1,32 @@
1
- require 'clearance'
2
- require 'rails'
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 'clearance.filter' do |app|
22
+ initializer "clearance.filter" do |app|
7
23
  app.config.filter_parameters += [:password, :token]
8
24
  end
9
25
 
10
- config.app_middleware.insert_after ActionDispatch::ParamsParser, Clearance::RackSession
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
- self.encrypted_password = encrypt(new_password)
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