clearance 1.17.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clearance might be problematic. Click here for more details.

Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -14
  3. data/Appraisals +11 -3
  4. data/Gemfile +3 -6
  5. data/Gemfile.lock +91 -87
  6. data/NEWS.md +233 -15
  7. data/README.md +54 -28
  8. data/app/controllers/clearance/base_controller.rb +8 -1
  9. data/app/controllers/clearance/passwords_controller.rb +35 -45
  10. data/app/controllers/clearance/sessions_controller.rb +3 -18
  11. data/app/controllers/clearance/users_controller.rb +2 -17
  12. data/clearance.gemspec +15 -9
  13. data/config/locales/clearance.en.yml +1 -0
  14. data/config/routes.rb +1 -1
  15. data/gemfiles/rails_5.0.gemfile +5 -6
  16. data/gemfiles/rails_5.1.gemfile +5 -6
  17. data/gemfiles/rails_5.2.gemfile +5 -6
  18. data/gemfiles/{rails_4.2.gemfile → rails_6.0.gemfile} +7 -7
  19. data/lib/clearance.rb +0 -8
  20. data/lib/clearance/authentication.rb +1 -9
  21. data/lib/clearance/authorization.rb +2 -11
  22. data/lib/clearance/back_door.rb +1 -1
  23. data/lib/clearance/configuration.rb +30 -19
  24. data/lib/clearance/password_strategies.rb +5 -4
  25. data/lib/clearance/password_strategies/argon2.rb +23 -0
  26. data/lib/clearance/password_strategies/bcrypt.rb +17 -11
  27. data/lib/clearance/rack_session.rb +5 -1
  28. data/lib/clearance/session.rb +40 -12
  29. data/lib/clearance/testing/deny_access_matcher.rb +10 -20
  30. data/lib/clearance/user.rb +3 -24
  31. data/lib/clearance/version.rb +1 -1
  32. data/lib/generators/clearance/install/install_generator.rb +12 -12
  33. data/lib/generators/clearance/install/templates/README +10 -4
  34. data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb.erb +1 -1
  35. data/lib/generators/clearance/install/templates/db/migrate/create_users.rb.erb +1 -1
  36. data/lib/generators/clearance/routes/templates/routes.rb +1 -1
  37. data/spec/acceptance/clearance_installation_spec.rb +0 -4
  38. data/spec/app_templates/app/models/user.rb +1 -1
  39. data/spec/app_templates/testapp/app/controllers/home_controller.rb +1 -5
  40. data/spec/app_templates/testapp/app/views/layouts/application.html.erb +24 -0
  41. data/spec/clearance/back_door_spec.rb +12 -6
  42. data/spec/clearance/rack_session_spec.rb +2 -0
  43. data/spec/clearance/session_spec.rb +91 -47
  44. data/spec/clearance/testing/deny_access_matcher_spec.rb +32 -0
  45. data/spec/configuration_spec.rb +46 -15
  46. data/spec/controllers/apis_controller_spec.rb +1 -5
  47. data/spec/controllers/forgeries_controller_spec.rb +1 -5
  48. data/spec/controllers/passwords_controller_spec.rb +41 -5
  49. data/spec/controllers/permissions_controller_spec.rb +3 -7
  50. data/spec/controllers/sessions_controller_spec.rb +1 -1
  51. data/spec/dummy/app/controllers/application_controller.rb +1 -5
  52. data/spec/dummy/application.rb +7 -3
  53. data/spec/generators/clearance/install/install_generator_spec.rb +33 -15
  54. data/spec/generators/clearance/views/views_generator_spec.rb +0 -2
  55. data/spec/models/user_spec.rb +5 -5
  56. data/spec/password_strategies/argon2_spec.rb +79 -0
  57. data/spec/password_strategies/bcrypt_spec.rb +18 -1
  58. data/spec/requests/authentication_cookie_spec.rb +55 -0
  59. data/spec/requests/token_expiration_spec.rb +5 -0
  60. data/spec/spec_helper.rb +4 -7
  61. data/spec/support/generator_spec_helpers.rb +1 -9
  62. metadata +51 -33
  63. data/app/views/layouts/application.html.erb +0 -23
  64. data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +0 -77
  65. data/lib/clearance/password_strategies/blowfish.rb +0 -61
  66. data/lib/clearance/password_strategies/sha1.rb +0 -59
  67. data/lib/clearance/testing.rb +0 -11
  68. data/lib/clearance/testing/helpers.rb +0 -15
  69. data/spec/app_templates/app/models/rails5/user.rb +0 -5
  70. data/spec/password_strategies/bcrypt_migration_from_sha1_spec.rb +0 -122
  71. data/spec/password_strategies/blowfish_spec.rb +0 -61
  72. data/spec/password_strategies/sha1_spec.rb +0 -59
  73. data/spec/support/environment.rb +0 -12
  74. data/spec/support/http_method_shim.rb +0 -25
@@ -13,7 +13,7 @@ if Clearance.configuration.routes_enabled?
13
13
  only: Clearance.configuration.user_actions do
14
14
  resource :password,
15
15
  controller: 'clearance/passwords',
16
- only: [:create, :edit, :update]
16
+ only: [:edit, :update]
17
17
  end
18
18
 
19
19
  get '/sign_in' => 'clearance/sessions#new', as: 'sign_in'
@@ -3,19 +3,18 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "addressable", "~> 2.6.0"
6
- gem "appraisal"
7
6
  gem "ammeter"
8
- gem "bundler", "~> 1.3"
7
+ gem "appraisal"
9
8
  gem "capybara", ">= 2.6.2"
10
9
  gem "database_cleaner", "~> 1.0"
11
10
  gem "factory_bot_rails", "~> 5.0"
12
11
  gem "nokogiri", "~> 1.10.0"
13
- gem "rspec-rails", "~> 3.1"
14
- gem "shoulda-matchers", "~> 4.0"
15
- gem "sqlite3", "~> 1.3.13"
16
- gem "timecop", "~> 0.6"
17
12
  gem "pry", require: false
13
+ gem "shoulda-matchers", "~> 4.1"
14
+ gem "timecop", "~> 0.6"
18
15
  gem "railties", "~> 5.0.0"
19
16
  gem "rails-controller-testing"
17
+ gem "sqlite3", "~> 1.3.13"
18
+ gem "rspec-rails", "~> 3.1"
20
19
 
21
20
  gemspec path: "../"
@@ -3,19 +3,18 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "addressable", "~> 2.6.0"
6
- gem "appraisal"
7
6
  gem "ammeter"
8
- gem "bundler", "~> 1.3"
7
+ gem "appraisal"
9
8
  gem "capybara", ">= 2.6.2"
10
9
  gem "database_cleaner", "~> 1.0"
11
10
  gem "factory_bot_rails", "~> 5.0"
12
11
  gem "nokogiri", "~> 1.10.0"
13
- gem "rspec-rails", "~> 3.1"
14
- gem "shoulda-matchers", "~> 4.0"
15
- gem "sqlite3", "~> 1.3.13"
16
- gem "timecop", "~> 0.6"
17
12
  gem "pry", require: false
13
+ gem "shoulda-matchers", "~> 4.1"
14
+ gem "timecop", "~> 0.6"
18
15
  gem "railties", "~> 5.1.0"
19
16
  gem "rails-controller-testing"
17
+ gem "sqlite3", "~> 1.3.13"
18
+ gem "rspec-rails", "~> 3.1"
20
19
 
21
20
  gemspec path: "../"
@@ -3,19 +3,18 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "addressable", "~> 2.6.0"
6
- gem "appraisal"
7
6
  gem "ammeter"
8
- gem "bundler", "~> 1.3"
7
+ gem "appraisal"
9
8
  gem "capybara", ">= 2.6.2"
10
9
  gem "database_cleaner", "~> 1.0"
11
10
  gem "factory_bot_rails", "~> 5.0"
12
11
  gem "nokogiri", "~> 1.10.0"
13
- gem "rspec-rails", "~> 3.1"
14
- gem "shoulda-matchers", "~> 4.0"
15
- gem "sqlite3", "~> 1.3.13"
16
- gem "timecop", "~> 0.6"
17
12
  gem "pry", require: false
13
+ gem "shoulda-matchers", "~> 4.1"
14
+ gem "timecop", "~> 0.6"
18
15
  gem "railties", "~> 5.2.0"
19
16
  gem "rails-controller-testing"
17
+ gem "sqlite3", "~> 1.3.13"
18
+ gem "rspec-rails", "~> 3.1"
20
19
 
21
20
  gemspec path: "../"
@@ -3,18 +3,18 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "addressable", "~> 2.6.0"
6
- gem "appraisal"
7
6
  gem "ammeter"
8
- gem "bundler", "~> 1.3"
7
+ gem "appraisal"
9
8
  gem "capybara", ">= 2.6.2"
10
9
  gem "database_cleaner", "~> 1.0"
11
10
  gem "factory_bot_rails", "~> 5.0"
12
11
  gem "nokogiri", "~> 1.10.0"
13
- gem "rspec-rails", "~> 3.1"
14
- gem "shoulda-matchers", "~> 4.0"
15
- gem "sqlite3", "~> 1.3.13"
16
- gem "timecop", "~> 0.6"
17
12
  gem "pry", require: false
18
- gem "railties", "~> 4.2.0"
13
+ gem "shoulda-matchers", "~> 4.1"
14
+ gem "timecop", "~> 0.6"
15
+ gem "railties", "~> 6.0.0"
16
+ gem "rails-controller-testing"
17
+ gem "rspec-rails", "~> 4.0.0.beta3"
18
+ gem "sqlite3", "~> 1.4.0"
19
19
 
20
20
  gemspec path: "../"
@@ -10,12 +10,4 @@ require 'clearance/password_strategies'
10
10
  require 'clearance/constraints'
11
11
 
12
12
  module Clearance
13
- # @deprecated Use `Gem::Specification` API if you need to access Clearance's
14
- # Gem root.
15
- def self.root
16
- warn "#{Kernel.caller.first}: [DEPRECATION] `Clearance.root` is " +
17
- "deprecated and will be removed in the next major release. If you need " +
18
- "to find Clearance's root, you can use the `Gem::Specification` API."
19
- File.expand_path('../..', __FILE__)
20
- end
21
13
  end
@@ -10,7 +10,6 @@ module Clearance
10
10
  private(
11
11
  :authenticate,
12
12
  :current_user,
13
- :current_user=,
14
13
  :handle_unverified_request,
15
14
  :sign_in,
16
15
  :sign_out,
@@ -40,13 +39,6 @@ module Clearance
40
39
  clearance_session.current_user
41
40
  end
42
41
 
43
- # @deprecated Use the {#sign_in} method instead.
44
- def current_user=(user)
45
- warn "#{Kernel.caller.first}: [DEPRECATION] " +
46
- 'Assigning the current_user has been deprecated. Use the sign_in method instead.'
47
- clearance_session.sign_in user
48
- end
49
-
50
42
  # Sign in the provided user.
51
43
  # @param [User] user
52
44
  #
@@ -67,7 +59,7 @@ module Clearance
67
59
  # {SessionsController#create}.
68
60
  #
69
61
  # Signing in will also regenerate the CSRF token for the current session,
70
- # provided {Configuration#rotate_csrf_token_on_sign_in} is set.
62
+ # provided {Configuration#rotate_csrf_on_sign_in?} is set.
71
63
  def sign_in(user, &block)
72
64
  clearance_session.sign_in(user, &block)
73
65
 
@@ -3,7 +3,7 @@ module Clearance
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- private :authorize, :deny_access, :require_login
6
+ private :deny_access, :require_login
7
7
  end
8
8
 
9
9
  # Use as a `before_action` to require a user be signed in to proceed.
@@ -23,15 +23,6 @@ module Clearance
23
23
  end
24
24
  end
25
25
 
26
- # @deprecated use {#require_login}
27
- def authorize
28
- warn "[DEPRECATION] Clearance's `authorize` before_action is " +
29
- "deprecated. Use `require_login` instead. Be sure to update any " +
30
- "instances of `skip_before_action :authorize` or " +
31
- "`skip_before_action :authorize` as well"
32
- require_login
33
- end
34
-
35
26
  # Responds to unauthorized requests in a manner fitting the request format.
36
27
  # `js`, `json`, and `xml` requests will receive a 401 with no body. All
37
28
  # other formats will be redirected appropriately and can optionally have the
@@ -63,7 +54,7 @@ module Clearance
63
54
  store_location
64
55
 
65
56
  if flash_message
66
- flash[:notice] = flash_message
57
+ flash[:alert] = flash_message
67
58
  end
68
59
 
69
60
  if signed_in?
@@ -68,7 +68,7 @@ module Clearance
68
68
 
69
69
  # @api private
70
70
  def environment_is_allowed?
71
- allowed_environments.include? ENV["RAILS_ENV"]
71
+ allowed_environments.include? Rails.env
72
72
  end
73
73
 
74
74
  # @api private
@@ -42,6 +42,16 @@ module Clearance
42
42
  # @return [Boolean]
43
43
  attr_accessor :httponly
44
44
 
45
+ # Same-site cookies ("First-Party-Only" or "First-Party") allow servers to
46
+ # mitigate the risk of CSRF and information leakage attacks by asserting
47
+ # that a particular cookie should only be sent with requests initiated from
48
+ # the same registrable domain.
49
+ # Defaults to `nil`. For more, see
50
+ # [RFC6265](https://tools.ietf.org/html/draft-west-first-party-cookies-06#section-4.1.1).
51
+ # and https://github.com/rack/rack/blob/6eda04886e3a57918ca2d6a482fda02a678fef0a/lib/rack/utils.rb#L232-L244
52
+ # @return [String]
53
+ attr_accessor :same_site
54
+
45
55
  # Controls the address the password reset email is sent from.
46
56
  # Defaults to reply@example.com.
47
57
  # @return [String]
@@ -88,7 +98,12 @@ module Clearance
88
98
  # The ActiveRecord class that represents users in your application.
89
99
  # Defaults to `::User`.
90
100
  # @return [ActiveRecord::Base]
91
- attr_accessor :user_model
101
+ attr_writer :user_model
102
+
103
+ # The controller class that all Clearance controllers will inherit from.
104
+ # Defaults to `::ApplicationController`.
105
+ # @return [ActionController::Base]
106
+ attr_writer :parent_controller
92
107
 
93
108
  # The array of allowed environments where `Clearance::BackDoor` is enabled.
94
109
  # Defaults to ["test", "ci", "development"]
@@ -103,16 +118,27 @@ module Clearance
103
118
  @cookie_name = "remember_token"
104
119
  @cookie_path = '/'
105
120
  @httponly = true
121
+ @same_site = nil
106
122
  @mailer_sender = 'reply@example.com'
107
123
  @redirect_url = '/'
108
- @rotate_csrf_on_sign_in = nil
124
+ @rotate_csrf_on_sign_in = true
109
125
  @routes = true
110
126
  @secure_cookie = false
111
127
  @sign_in_guards = []
112
128
  end
113
129
 
130
+ # The class representing the configured user model.
131
+ # In the default configuration, this is the `User` class.
132
+ # @return [Class]
114
133
  def user_model
115
- @user_model ||= ::User
134
+ (@user_model || "User").to_s.constantize
135
+ end
136
+
137
+ # The class representing the configured base controller.
138
+ # In the default configuration, this is the `ApplicationController` class.
139
+ # @return [Class]
140
+ def parent_controller
141
+ (@parent_controller || "ApplicationController").to_s.constantize
116
142
  end
117
143
 
118
144
  # Is the user sign up route enabled?
@@ -167,22 +193,7 @@ module Clearance
167
193
  end
168
194
 
169
195
  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
196
+ !!rotate_csrf_on_sign_in
186
197
  end
187
198
  end
188
199
 
@@ -13,10 +13,11 @@ module Clearance
13
13
  # `password=(new_password)`. For an example of how to implement these methods,
14
14
  # see {Clearance::PasswordStrategies::BCrypt}.
15
15
  module PasswordStrategies
16
- autoload :BCrypt, 'clearance/password_strategies/bcrypt'
16
+ autoload :BCrypt, "clearance/password_strategies/bcrypt"
17
+ autoload :Argon2, "clearance/password_strategies/argon2"
17
18
  autoload :BCryptMigrationFromSHA1,
18
- 'clearance/password_strategies/bcrypt_migration_from_sha1'
19
- autoload :Blowfish, 'clearance/password_strategies/blowfish'
20
- autoload :SHA1, 'clearance/password_strategies/sha1'
19
+ "clearance/password_strategies/bcrypt_migration_from_sha1"
20
+ autoload :Blowfish, "clearance/password_strategies/blowfish"
21
+ autoload :SHA1, "clearance/password_strategies/sha1"
21
22
  end
22
23
  end
@@ -0,0 +1,23 @@
1
+ module Clearance
2
+ module PasswordStrategies
3
+ # Uses Argon2 to authenticate users and store encrypted passwords.
4
+
5
+ module Argon2
6
+ require "argon2"
7
+
8
+ def authenticated?(password)
9
+ if encrypted_password.present?
10
+ ::Argon2::Password.verify_password(password, encrypted_password)
11
+ end
12
+ end
13
+
14
+ def password=(new_password)
15
+ @password = new_password
16
+
17
+ if new_password.present?
18
+ self.encrypted_password = ::Argon2::Password.new.create(new_password)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,10 +2,14 @@ module Clearance
2
2
  module PasswordStrategies
3
3
  # Uses BCrypt to authenticate users and store encrypted passwords.
4
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.
5
+ # BCrypt has a `cost` argument which determines how computationally
6
+ # expensive the hash is to calculate. The higher the cost, the harder it is
7
+ # for attackers to crack passwords even if they posess a database dump of
8
+ # the encrypted passwords. Clearance uses the `bcrypt-ruby` default cost
9
+ # except in the test environment, where it uses the minimum cost value for
10
+ # speed. If you wish to increase the cost over the default, you can do so
11
+ # by setting a higher cost in an initializer:
12
+ # `BCrypt::Engine.cost = 12`
9
13
  module BCrypt
10
14
  require 'bcrypt'
11
15
 
@@ -19,18 +23,20 @@ module Clearance
19
23
  @password = new_password
20
24
 
21
25
  if new_password.present?
22
- cost = if defined?(::Rails) && ::Rails.env.test?
23
- ::BCrypt::Engine::MIN_COST
24
- else
25
- ::BCrypt::Engine::DEFAULT_COST
26
- end
27
-
28
26
  self.encrypted_password = ::BCrypt::Password.create(
29
27
  new_password,
30
- cost: cost,
28
+ cost: configured_bcrypt_cost,
31
29
  )
32
30
  end
33
31
  end
32
+
33
+ def configured_bcrypt_cost
34
+ if defined?(::Rails) && ::Rails.env.test?
35
+ ::BCrypt::Engine::MIN_COST
36
+ else
37
+ ::BCrypt::Engine.cost
38
+ end
39
+ end
34
40
  end
35
41
  end
36
42
  end
@@ -21,7 +21,11 @@ module Clearance
21
21
  session = Clearance::Session.new(env)
22
22
  env[:clearance] = session
23
23
  response = @app.call(env)
24
- session.add_cookie_to_headers response[1]
24
+
25
+ if session.authentication_successful?
26
+ session.add_cookie_to_headers response[1]
27
+ end
28
+
25
29
  response
26
30
  end
27
31
  end
@@ -81,7 +81,7 @@ module Clearance
81
81
  end
82
82
 
83
83
  @current_user = nil
84
- cookies.delete remember_token_cookie
84
+ cookies.delete remember_token_cookie, delete_cookie_options
85
85
  end
86
86
 
87
87
  # True if {#current_user} is set.
@@ -98,6 +98,13 @@ module Clearance
98
98
  ! signed_in?
99
99
  end
100
100
 
101
+ # True if a successful authentication has been performed
102
+ #
103
+ # @return [Boolean]
104
+ def authentication_successful?
105
+ !!@current_user
106
+ end
107
+
101
108
  private
102
109
 
103
110
  # @api private
@@ -112,15 +119,7 @@ module Clearance
112
119
 
113
120
  # @api private
114
121
  def remember_token_expires
115
- if expires_configuration.arity == 1
116
- expires_configuration.call(cookies)
117
- else
118
- warn "#{Kernel.caller.first}: [DEPRECATION] " +
119
- 'Clearance.configuration.cookie_expiration lambda with no parameters ' +
120
- 'has been deprecated and will be removed from a future release. The ' +
121
- 'lambda should accept the collection of previously set cookies.'
122
- expires_configuration.call
123
- end
122
+ expires_configuration.call(cookies)
124
123
  end
125
124
 
126
125
  # @api private
@@ -155,20 +154,49 @@ module Clearance
155
154
  guards = Clearance.configuration.sign_in_guards
156
155
 
157
156
  guards.inject(default_guard) do |stack, guard_class|
158
- guard_class.new(self, stack)
157
+ guard_class.to_s.constantize.new(self, stack)
159
158
  end
160
159
  end
161
160
 
162
161
  # @api private
163
162
  def cookie_options
164
163
  {
165
- domain: Clearance.configuration.cookie_domain,
164
+ domain: domain,
166
165
  expires: remember_token_expires,
167
166
  httponly: Clearance.configuration.httponly,
167
+ same_site: Clearance.configuration.same_site,
168
168
  path: Clearance.configuration.cookie_path,
169
169
  secure: Clearance.configuration.secure_cookie,
170
170
  value: remember_token,
171
171
  }
172
172
  end
173
+
174
+ # @api private
175
+ def delete_cookie_options
176
+ Hash.new.tap do |options|
177
+ if configured_cookie_domain
178
+ options[:domain] = configured_cookie_domain
179
+ end
180
+ end
181
+ end
182
+
183
+ # @api private
184
+ def domain
185
+ if configured_cookie_domain.respond_to?(:call)
186
+ configured_cookie_domain.call(request_with_env)
187
+ else
188
+ configured_cookie_domain
189
+ end
190
+ end
191
+
192
+ # @api private
193
+ def configured_cookie_domain
194
+ Clearance.configuration.cookie_domain
195
+ end
196
+
197
+ # @api private
198
+ def request_with_env
199
+ ActionDispatch::Request.new(@env)
200
+ end
173
201
  end
174
202
  end