clearance 2.0.0 → 2.1.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -9
  3. data/Appraisals +1 -1
  4. data/Gemfile.lock +30 -30
  5. data/NEWS.md +42 -0
  6. data/README.md +2 -5
  7. data/app/controllers/clearance/base_controller.rb +8 -1
  8. data/app/controllers/clearance/passwords_controller.rb +14 -0
  9. data/clearance.gemspec +8 -2
  10. data/config/locales/clearance.en.yml +1 -0
  11. data/config/routes.rb +1 -1
  12. data/gemfiles/rails_6.0.gemfile +1 -1
  13. data/lib/clearance/authentication.rb +1 -1
  14. data/lib/clearance/configuration.rb +18 -18
  15. data/lib/clearance/rack_session.rb +5 -1
  16. data/lib/clearance/session.rb +17 -1
  17. data/lib/clearance/user.rb +1 -1
  18. data/lib/clearance/version.rb +1 -1
  19. data/lib/generators/clearance/install/install_generator.rb +10 -0
  20. data/lib/generators/clearance/install/templates/README +10 -4
  21. data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb.erb +1 -1
  22. data/lib/generators/clearance/install/templates/db/migrate/create_users.rb.erb +1 -1
  23. data/lib/generators/clearance/routes/templates/routes.rb +1 -1
  24. data/spec/acceptance/clearance_installation_spec.rb +0 -4
  25. data/spec/app_templates/app/models/user.rb +1 -1
  26. data/spec/app_templates/testapp/app/views/layouts/application.html.erb +24 -0
  27. data/spec/clearance/rack_session_spec.rb +2 -0
  28. data/spec/clearance/session_spec.rb +38 -8
  29. data/spec/configuration_spec.rb +32 -14
  30. data/spec/controllers/passwords_controller_spec.rb +19 -5
  31. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  32. data/spec/generators/clearance/install/install_generator_spec.rb +30 -1
  33. data/spec/generators/clearance/views/views_generator_spec.rb +0 -1
  34. data/spec/models/user_spec.rb +5 -5
  35. data/spec/requests/authentication_cookie_spec.rb +55 -0
  36. data/spec/spec_helper.rb +0 -1
  37. data/spec/support/generator_spec_helpers.rb +1 -5
  38. metadata +12 -7
  39. data/app/views/layouts/application.html.erb +0 -23
  40. data/spec/app_templates/app/models/rails5/user.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd2d43e71f4cbe272a3a1b19577f453b986a27711e6327c6050d1170c73c09d8
4
- data.tar.gz: ba32fcfb82fa0ab33f3764e381a2d2a93484dde956189b5395fa63b759142231
3
+ metadata.gz: a955d1866bf1879846034e89ca4f5a85cdf4602e67667e1ba93fae12ec5c832e
4
+ data.tar.gz: 3e2c19f661f4910f0bf6c31a12dfae58b44c4a49283750e07ae28eb33ced6d17
5
5
  SHA512:
6
- metadata.gz: 89cd499f030c7bb42c044e772eda264a899f382395b15331631dd4e7b148f173ca02b99ff232cf2f6a969ecad3ae284c341fd58f3cbd19ebf23de9f4126ae657
7
- data.tar.gz: 51d38e93bdc439337d22c7e675f3ae826991ca40225c81309fcd8b131220b9463f7df4dd1015c72421907dd6989b60592a7d3235bb49969fcbb900a2c354ef89
6
+ metadata.gz: c8a2870d5567ae747cccbeda37a88fc6f31d5fca6cac57d2d2cbdb7016e22112f413c19a73b8f0edeb390b01ee04efe8da4633b69a2bd629bbfdfd2b6dbbbe5d
7
+ data.tar.gz: ed611c62911b8ac335ae12fd6af9e4eeaa36b6541451ec2322b29a07820b6eb6d2ce3232262cc75d69697e2530e93746ee0089911e9b7ff39ee9ef64961fbe15
@@ -4,9 +4,9 @@ language:
4
4
  - ruby
5
5
 
6
6
  rvm:
7
- - 2.4.6
8
- - 2.5.5
9
- - 2.6.2
7
+ - 2.4.9
8
+ - 2.5.7
9
+ - 2.6.5
10
10
 
11
11
  gemfile:
12
12
  - gemfiles/rails_5.0.gemfile
@@ -14,9 +14,6 @@ gemfile:
14
14
  - gemfiles/rails_5.2.gemfile
15
15
  - gemfiles/rails_6.0.gemfile
16
16
 
17
- before_install:
18
- - gem update --system
19
-
20
17
  install:
21
18
  - "bin/setup"
22
19
 
@@ -26,7 +23,5 @@ branches:
26
23
 
27
24
  matrix:
28
25
  exclude:
29
- - rvm: 2.4.6
26
+ - rvm: 2.4.9
30
27
  gemfile: gemfiles/rails_6.0.gemfile
31
-
32
- sudo: false
data/Appraisals CHANGED
@@ -12,7 +12,7 @@ rails_versions.each do |version|
12
12
 
13
13
  if Gem::Version.new(version) >= Gem::Version.new("6.0")
14
14
  # TODO - Switch to 4.0 gem once release is made
15
- gem 'rspec-rails', '~> 4.0.0.beta2'
15
+ gem 'rspec-rails', '~> 4.0.0.beta3'
16
16
  gem 'sqlite3', '~> 1.4.0'
17
17
  else
18
18
  gem 'sqlite3', '~> 1.3.13'
@@ -1,45 +1,45 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- clearance (2.0.0)
4
+ clearance (2.1.0)
5
5
  actionmailer (>= 5.0)
6
6
  activemodel (>= 5.0)
7
7
  activerecord (>= 5.0)
8
8
  bcrypt (>= 3.1.1)
9
- email_validator (~> 1.4)
9
+ email_validator (~> 2.0)
10
10
  railties (>= 5.0)
11
11
 
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- actionmailer (6.0.1)
16
- actionpack (= 6.0.1)
17
- actionview (= 6.0.1)
18
- activejob (= 6.0.1)
15
+ actionmailer (6.0.2.1)
16
+ actionpack (= 6.0.2.1)
17
+ actionview (= 6.0.2.1)
18
+ activejob (= 6.0.2.1)
19
19
  mail (~> 2.5, >= 2.5.4)
20
20
  rails-dom-testing (~> 2.0)
21
- actionpack (6.0.1)
22
- actionview (= 6.0.1)
23
- activesupport (= 6.0.1)
24
- rack (~> 2.0)
21
+ actionpack (6.0.2.1)
22
+ actionview (= 6.0.2.1)
23
+ activesupport (= 6.0.2.1)
24
+ rack (~> 2.0, >= 2.0.8)
25
25
  rack-test (>= 0.6.3)
26
26
  rails-dom-testing (~> 2.0)
27
27
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
28
- actionview (6.0.1)
29
- activesupport (= 6.0.1)
28
+ actionview (6.0.2.1)
29
+ activesupport (= 6.0.2.1)
30
30
  builder (~> 3.1)
31
31
  erubi (~> 1.4)
32
32
  rails-dom-testing (~> 2.0)
33
33
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
34
- activejob (6.0.1)
35
- activesupport (= 6.0.1)
34
+ activejob (6.0.2.1)
35
+ activesupport (= 6.0.2.1)
36
36
  globalid (>= 0.3.6)
37
- activemodel (6.0.1)
38
- activesupport (= 6.0.1)
39
- activerecord (6.0.1)
40
- activemodel (= 6.0.1)
41
- activesupport (= 6.0.1)
42
- activesupport (6.0.1)
37
+ activemodel (6.0.2.1)
38
+ activesupport (= 6.0.2.1)
39
+ activerecord (6.0.2.1)
40
+ activemodel (= 6.0.2.1)
41
+ activesupport (= 6.0.2.1)
42
+ activesupport (6.0.2.1)
43
43
  concurrent-ruby (~> 1.0, >= 1.0.2)
44
44
  i18n (>= 0.7, < 2)
45
45
  minitest (~> 5.1)
@@ -56,7 +56,7 @@ GEM
56
56
  rake
57
57
  thor (>= 0.14.0)
58
58
  bcrypt (3.1.13)
59
- builder (3.2.3)
59
+ builder (3.2.4)
60
60
  capybara (3.29.0)
61
61
  addressable
62
62
  mini_mime (>= 0.1.3)
@@ -70,7 +70,7 @@ GEM
70
70
  crass (1.0.5)
71
71
  database_cleaner (1.7.0)
72
72
  diff-lcs (1.3)
73
- email_validator (1.6.0)
73
+ email_validator (2.0.1)
74
74
  activemodel
75
75
  erubi (1.9.0)
76
76
  factory_bot (5.1.1)
@@ -82,7 +82,7 @@ GEM
82
82
  activesupport (>= 4.2.0)
83
83
  i18n (1.7.0)
84
84
  concurrent-ruby (~> 1.0)
85
- loofah (2.3.1)
85
+ loofah (2.4.0)
86
86
  crass (~> 1.0.2)
87
87
  nokogiri (>= 1.5.9)
88
88
  mail (2.7.1)
@@ -91,13 +91,13 @@ GEM
91
91
  mini_mime (1.0.2)
92
92
  mini_portile2 (2.4.0)
93
93
  minitest (5.13.0)
94
- nokogiri (1.10.5)
94
+ nokogiri (1.10.7)
95
95
  mini_portile2 (~> 2.4.0)
96
96
  pry (0.12.2)
97
97
  coderay (~> 1.1.0)
98
98
  method_source (~> 0.9.0)
99
99
  public_suffix (3.1.1)
100
- rack (2.0.7)
100
+ rack (2.0.8)
101
101
  rack-test (1.1.0)
102
102
  rack (>= 1.0, < 3)
103
103
  rails-dom-testing (2.0.3)
@@ -105,9 +105,9 @@ GEM
105
105
  nokogiri (>= 1.6)
106
106
  rails-html-sanitizer (1.3.0)
107
107
  loofah (~> 2.3)
108
- railties (6.0.1)
109
- actionpack (= 6.0.1)
110
- activesupport (= 6.0.1)
108
+ railties (6.0.2.1)
109
+ actionpack (= 6.0.2.1)
110
+ activesupport (= 6.0.2.1)
111
111
  method_source
112
112
  rake (>= 0.8.7)
113
113
  thor (>= 0.20.3, < 2.0)
@@ -132,14 +132,14 @@ GEM
132
132
  rspec-support (3.9.0)
133
133
  shoulda-matchers (4.1.2)
134
134
  activesupport (>= 4.2.0)
135
- thor (0.20.3)
135
+ thor (1.0.1)
136
136
  thread_safe (0.3.6)
137
137
  timecop (0.9.1)
138
138
  tzinfo (1.2.5)
139
139
  thread_safe (~> 0.1)
140
140
  xpath (3.2.0)
141
141
  nokogiri (~> 1.8)
142
- zeitwerk (2.2.1)
142
+ zeitwerk (2.2.2)
143
143
 
144
144
  PLATFORMS
145
145
  ruby
data/NEWS.md CHANGED
@@ -3,6 +3,48 @@
3
3
  The noteworthy changes for each Clearance version are included here. For a
4
4
  complete changelog, see the git history for each version via the version links.
5
5
 
6
+ ## [2.1.0] - December 19, 2019
7
+
8
+ ### Added
9
+
10
+ - Add a `parent_controller` configuration option to specify the controller that
11
+ Clearance's `BaseController` will inherit from. Defaults to a value of
12
+ `ApplicationController`.
13
+ - Use the configured `primary_key_type` from the Active Record settings of the
14
+ project including Clearance, if it is set, while generating migrations. For
15
+ example, a setting of `:uuid` in a Rails app using Clearance will cause the
16
+ clearance-generated migrations to use this for the `users` table id type.
17
+
18
+ ### Fixed
19
+
20
+ - Delete cookies correctly when a custom domain setting is being used.
21
+ - Do not set the authorization cookie on requests which did not exercise the
22
+ authorization code. Reduces the chances of leaving an auth cookie in a
23
+ publicly cacheable page that didn't require authorization to access.
24
+
25
+ ### Changed
26
+
27
+ - Update the `email_validator` gem to a newer version embrace the more relaxed
28
+ email validation options which it now defaults to.
29
+ - When a password reset request is submitted without an email address, a flash
30
+ alert is now provided. Previously this continued silently as though it had
31
+ worked. We still proceed that way when there is an invalid (but present)
32
+ value, so as not to reveal existent vs. non-existent emails in the database.
33
+
34
+ ### Removed
35
+
36
+ - Remove an unused route to `passwords#create` nested under `users`.
37
+ - No longer include the (rarely used in practice) application layout as part of
38
+ the views installer; but continue to provide some stock sign-in/out and flash
39
+ partial code in the gem installation README output.
40
+
41
+ ### Deprecated
42
+
43
+ - Remove the existing deprecation notice around the `rotate_csrf_on_sign_in`
44
+ setting, and make that setting default to true.
45
+
46
+ [2.1.0]: https://github.com/thoughtbot/clearance/compare/v2.0.0...v2.1.0
47
+
6
48
  ## [2.0.0] - November 12, 2019
7
49
 
8
50
  ### Added
data/README.md CHANGED
@@ -59,17 +59,14 @@ Clearance.configure do |config|
59
59
  config.mailer_sender = "reply@example.com"
60
60
  config.password_strategy = Clearance::PasswordStrategies::BCrypt
61
61
  config.redirect_url = "/"
62
- config.rotate_csrf_on_sign_in = false
62
+ config.rotate_csrf_on_sign_in = true
63
63
  config.secure_cookie = false
64
64
  config.sign_in_guards = []
65
65
  config.user_model = "User"
66
+ config.parent_controller = "ApplicationController"
66
67
  end
67
68
  ```
68
69
 
69
- The install generator will set `rotate_csrf_on_sign_in` to `true`, so new
70
- installations will get this behavior from the start. This helps avoid session
71
- fixation attacks, and will become the default in Clearance 2.0.
72
-
73
70
  ## Use
74
71
 
75
72
  ### Access Control
@@ -1,2 +1,9 @@
1
- class Clearance::BaseController < ApplicationController
1
+ module Clearance
2
+ # Top-level base class that all Clearance controllers inherit from.
3
+ # Inherits from `ApplicationController` by default and can be overridden by
4
+ # setting a new value with {Configuration#parent_controller=}.
5
+ # @!parse
6
+ # class BaseController < ApplicationController; end
7
+ class BaseController < Clearance.configuration.parent_controller
8
+ end
2
9
  end
@@ -2,6 +2,7 @@ require 'active_support/deprecation'
2
2
 
3
3
  class Clearance::PasswordsController < Clearance::BaseController
4
4
  before_action :ensure_existing_user, only: [:edit, :update]
5
+ before_action :ensure_email_present, only: [:create]
5
6
  skip_before_action :require_login, only: [:create, :edit, :new, :update], raise: false
6
7
 
7
8
  def new
@@ -77,6 +78,13 @@ class Clearance::PasswordsController < Clearance::BaseController
77
78
  find_user_by_id_and_confirmation_token
78
79
  end
79
80
 
81
+ def ensure_email_present
82
+ if email_from_password_params.blank?
83
+ flash_failure_when_missing_email
84
+ render template: "passwords/new"
85
+ end
86
+ end
87
+
80
88
  def ensure_existing_user
81
89
  unless find_user_by_id_and_confirmation_token
82
90
  flash_failure_when_forbidden
@@ -96,6 +104,12 @@ class Clearance::PasswordsController < Clearance::BaseController
96
104
  default: t("flashes.failure_after_update"))
97
105
  end
98
106
 
107
+ def flash_failure_when_missing_email
108
+ flash.now[:alert] = translate(:missing_email,
109
+ scope: [:clearance, :controllers, :passwords],
110
+ default: t("flashes.failure_when_missing_email"))
111
+ end
112
+
99
113
  def url_after_update
100
114
  Clearance.configuration.redirect_url
101
115
  end
@@ -3,7 +3,7 @@ require 'clearance/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.add_dependency 'bcrypt', '>= 3.1.1'
6
- s.add_dependency 'email_validator', '~> 1.4'
6
+ s.add_dependency 'email_validator', '~> 2.0'
7
7
  s.add_dependency 'railties', '>= 5.0'
8
8
  s.add_dependency 'activemodel', '>= 5.0'
9
9
  s.add_dependency 'activerecord', '>= 5.0'
@@ -28,7 +28,13 @@ Gem::Specification.new do |s|
28
28
  'Galen Frechette',
29
29
  'Josh Steiner'
30
30
  ]
31
- s.description = 'Rails authentication & authorization with email & password.'
31
+ s.description = <<-DESCRIPTION
32
+ Clearance is built to support authentication and authorization via an
33
+ email/password sign-in mechanism in applications.
34
+
35
+ It provides some core classes commonly used for these features, along with
36
+ some opinionated defaults - but is intended to be easy to override.
37
+ DESCRIPTION
32
38
  s.email = 'support@thoughtbot.com'
33
39
  s.extra_rdoc_files = %w(LICENSE README.md)
34
40
  s.files = `git ls-files`.split("\n")
@@ -17,6 +17,7 @@ en:
17
17
  failure_when_forbidden: Please double check the URL or try submitting
18
18
  the form again.
19
19
  failure_when_not_signed_in: Please sign in to continue.
20
+ failure_when_missing_email: Email can't be blank.
20
21
  helpers:
21
22
  label:
22
23
  password:
@@ -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'
@@ -14,7 +14,7 @@ gem "shoulda-matchers", "~> 4.1"
14
14
  gem "timecop", "~> 0.6"
15
15
  gem "railties", "~> 6.0.0"
16
16
  gem "rails-controller-testing"
17
- gem "rspec-rails", "~> 4.0.0.beta2"
17
+ gem "rspec-rails", "~> 4.0.0.beta3"
18
18
  gem "sqlite3", "~> 1.4.0"
19
19
 
20
20
  gemspec path: "../"
@@ -59,7 +59,7 @@ module Clearance
59
59
  # {SessionsController#create}.
60
60
  #
61
61
  # Signing in will also regenerate the CSRF token for the current session,
62
- # provided {Configuration#rotate_csrf_token_on_sign_in} is set.
62
+ # provided {Configuration#rotate_csrf_on_sign_in?} is set.
63
63
  def sign_in(user, &block)
64
64
  clearance_session.sign_in(user, &block)
65
65
 
@@ -98,7 +98,12 @@ module Clearance
98
98
  # The ActiveRecord class that represents users in your application.
99
99
  # Defaults to `::User`.
100
100
  # @return [ActiveRecord::Base]
101
- 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
102
107
 
103
108
  # The array of allowed environments where `Clearance::BackDoor` is enabled.
104
109
  # Defaults to ["test", "ci", "development"]
@@ -116,16 +121,26 @@ module Clearance
116
121
  @same_site = nil
117
122
  @mailer_sender = 'reply@example.com'
118
123
  @redirect_url = '/'
119
- @rotate_csrf_on_sign_in = nil
124
+ @rotate_csrf_on_sign_in = true
120
125
  @routes = true
121
126
  @secure_cookie = false
122
127
  @sign_in_guards = []
123
128
  end
124
129
 
130
+ # The class representing the configured user model.
131
+ # In the default configuration, this is the `User` class.
132
+ # @return [Class]
125
133
  def user_model
126
134
  (@user_model || "User").to_s.constantize
127
135
  end
128
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
142
+ end
143
+
129
144
  # Is the user sign up route enabled?
130
145
  # @return [Boolean]
131
146
  def allow_sign_up?
@@ -178,22 +193,7 @@ module Clearance
178
193
  end
179
194
 
180
195
  def rotate_csrf_on_sign_in?
181
- if rotate_csrf_on_sign_in.nil?
182
- warn <<-EOM.squish
183
- Clearance's `rotate_csrf_on_sign_in` configuration setting is unset and
184
- will be treated as `false`. Setting this value to `true` is
185
- recommended to avoid session fixation attacks and will be the default
186
- in Clearance 2.0. It is recommended that you opt-in to this setting
187
- now and test your application. To silence this warning, set
188
- `rotate_csrf_on_sign_in` to `true` or `false` in Clearance's
189
- initializer.
190
-
191
- For more information on session fixation, see:
192
- https://www.owasp.org/index.php/Session_fixation
193
- EOM
194
- end
195
-
196
- rotate_csrf_on_sign_in
196
+ !!rotate_csrf_on_sign_in
197
197
  end
198
198
  end
199
199
 
@@ -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
@@ -164,6 +171,15 @@ module Clearance
164
171
  }
165
172
  end
166
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
+
167
183
  # @api private
168
184
  def domain
169
185
  if configured_cookie_domain.respond_to?(:call)
@@ -60,7 +60,7 @@ module Clearance
60
60
  # @see PasswordStrategies
61
61
  # @return [void]
62
62
  #
63
- # @!method authenticated?
63
+ # @!method authenticated?(password)
64
64
  # Check's the provided password against the user's encrypted password using
65
65
  # the configured password strategy. By default, this will be
66
66
  # {PasswordStrategies::BCrypt#authenticated?}, but can be changed with
@@ -1,3 +1,3 @@
1
1
  module Clearance
2
- VERSION = "2.0.0".freeze
2
+ VERSION = "2.1.0".freeze
3
3
  end
@@ -122,6 +122,16 @@ module Clearance
122
122
  "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
123
123
  end
124
124
 
125
+ def migration_primary_key_type_string
126
+ if configured_key_type
127
+ ", id: :#{configured_key_type}"
128
+ end
129
+ end
130
+
131
+ def configured_key_type
132
+ Rails.configuration.generators.active_record[:primary_key_type]
133
+ end
134
+
125
135
  def models_inherit_from
126
136
  "ApplicationRecord"
127
137
  end
@@ -8,9 +8,11 @@ Next steps:
8
8
  # config/environments/{development,test}.rb
9
9
  config.action_mailer.default_url_options = { host: 'localhost:3000' }
10
10
 
11
- In production it should be your app's domain name.
11
+ In the production environment it should be your application's full hostname.
12
12
 
13
- 2. Display user session and flashes. For example, in your application layout:
13
+ 2. Display user session status.
14
+
15
+ From somewhere in your layout, render sign in and sign out buttons:
14
16
 
15
17
  <% if signed_in? %>
16
18
  Signed in as: <%= current_user.email %>
@@ -19,14 +21,18 @@ Next steps:
19
21
  <%= link_to 'Sign in', sign_in_path %>
20
22
  <% end %>
21
23
 
24
+ 3. Render the flash contents.
25
+
26
+ Make sure the flash is being rendered in your views using something like:
27
+
22
28
  <div id="flash">
23
29
  <% flash.each do |key, value| %>
24
30
  <div class="flash <%= key %>"><%= value %></div>
25
31
  <% end %>
26
32
  </div>
27
33
 
28
- 3. Migrate:
34
+ 4. Migrate:
29
35
 
30
- rails db:migrate
36
+ Run `rails db:migrate` to add the clearance database changes.
31
37
 
32
38
  *******************************************************************************
@@ -13,7 +13,7 @@ class AddClearanceToUsers < ActiveRecord::Migration<%= migration_version %>
13
13
  users = select_all("SELECT id FROM users WHERE remember_token IS NULL")
14
14
 
15
15
  users.each do |user|
16
- update <<-SQL
16
+ update <<-SQL.squish
17
17
  UPDATE users
18
18
  SET remember_token = '#{Clearance::Token.new}'
19
19
  WHERE id = '#{user['id']}'
@@ -1,6 +1,6 @@
1
1
  class CreateUsers < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
- create_table :users do |t|
3
+ create_table :users<%= migration_primary_key_type_string %> do |t|
4
4
  t.timestamps null: false
5
5
  t.string :email, null: false
6
6
  t.string :encrypted_password, limit: 128, null: false
@@ -4,7 +4,7 @@
4
4
  resources :users, controller: "clearance/users", only: [:create] do
5
5
  resource :password,
6
6
  controller: "clearance/passwords",
7
- only: [:create, :edit, :update]
7
+ only: [:edit, :update]
8
8
  end
9
9
 
10
10
  get "/sign_in" => "clearance/sessions#new", as: "sign_in"
@@ -36,9 +36,6 @@ describe "Clearance Installation" do
36
36
  --skip-keeps
37
37
  --skip-sprockets
38
38
  CMD
39
-
40
- FileUtils.rm_f("public/index.html")
41
- FileUtils.rm_f("app/views/layouts/application.html.erb")
42
39
  end
43
40
 
44
41
  def testapp_templates
@@ -47,7 +44,6 @@ describe "Clearance Installation" do
47
44
 
48
45
  def configure_test_app
49
46
  FileUtils.rm_f("public/index.html")
50
- FileUtils.rm_f("app/views/layouts/application.html.erb")
51
47
  FileUtils.cp_r(testapp_templates, "..")
52
48
  end
53
49
 
@@ -1,4 +1,4 @@
1
- class User < ActiveRecord::Base
1
+ class User < ApplicationRecord
2
2
  def previously_existed?
3
3
  true
4
4
  end
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%= javascript_include_tag 'application' %>
5
+ <%= csrf_meta_tag %>
6
+ </head>
7
+ <body>
8
+ <div id="header">
9
+ <% if signed_in? -%>
10
+ <%= button_to t(".sign_out"), sign_out_path, method: :delete %>
11
+ <% else -%>
12
+ <%= link_to t(".sign_in"), sign_in_path %>
13
+ <% end -%>
14
+ </div>
15
+
16
+ <div id="flash">
17
+ <% flash.each do |key, value| -%>
18
+ <div id="flash_<%= key %>"><%=h value %></div>
19
+ <% end %>
20
+ </div>
21
+
22
+ <%= yield %>
23
+ </body>
24
+ </html>
@@ -11,6 +11,8 @@ describe Clearance::RackSession do
11
11
  env = Rack::MockRequest.env_for('/')
12
12
  expected_session = "the session"
13
13
  allow(expected_session).to receive(:add_cookie_to_headers)
14
+ allow(expected_session).to receive(:authentication_successful?).
15
+ and_return(true)
14
16
  allow(Clearance::Session).to receive(:new).
15
17
  with(env).
16
18
  and_return(expected_session)
@@ -340,14 +340,44 @@ describe Clearance::Session do
340
340
  expect(headers["Set-Cookie"]).to be nil
341
341
  end
342
342
 
343
- it 'signs out a user' do
344
- user = create(:user)
345
- old_remember_token = user.remember_token
346
- env = env_with_remember_token(old_remember_token)
347
- session = Clearance::Session.new(env)
348
- session.sign_out
349
- expect(session.current_user).to be_nil
350
- expect(user.reload.remember_token).not_to eq old_remember_token
343
+ describe "#sign_out" do
344
+ it "signs out a user" do
345
+ user = create(:user)
346
+ old_remember_token = user.remember_token
347
+ env = env_with_remember_token(old_remember_token)
348
+ session = Clearance::Session.new(env)
349
+ cookie_jar = ActionDispatch::Request.new(env).cookie_jar
350
+ expect(cookie_jar.deleted?(:remember_token)).to be false
351
+
352
+ session.sign_out
353
+
354
+ expect(cookie_jar.deleted?(:remember_token)).to be true
355
+ expect(session.current_user).to be_nil
356
+ expect(user.reload.remember_token).not_to eq old_remember_token
357
+ end
358
+
359
+ context "with custom cookie domain" do
360
+ let(:domain) { ".example.com" }
361
+
362
+ before do
363
+ Clearance.configuration.cookie_domain = domain
364
+ end
365
+
366
+ it "clears cookie" do
367
+ user = create(:user)
368
+ env = env_with_remember_token(
369
+ value: user.remember_token,
370
+ domain: domain,
371
+ )
372
+ session = Clearance::Session.new(env)
373
+ cookie_jar = ActionDispatch::Request.new(env).cookie_jar
374
+ expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be false
375
+
376
+ session.sign_out
377
+
378
+ expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be true
379
+ end
380
+ end
351
381
  end
352
382
 
353
383
  def env_with_cookies(cookies)
@@ -1,6 +1,8 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Clearance::Configuration do
4
+ let(:config) { Clearance.configuration }
5
+
4
6
  context "when no user_model_name is specified" do
5
7
  it "defaults to User" do
6
8
  expect(Clearance.configuration.user_model).to eq ::User
@@ -29,6 +31,28 @@ describe Clearance::Configuration do
29
31
  end
30
32
  end
31
33
 
34
+ context "when no parent_controller is specified" do
35
+ it "defaults to ApplicationController" do
36
+ expect(config.parent_controller).to eq ::ApplicationController
37
+ end
38
+ end
39
+
40
+ context "when a custom parent_controller is specified" do
41
+ before(:each) do
42
+ MyController = Class.new
43
+ end
44
+
45
+ after(:each) do
46
+ Object.send(:remove_const, :MyController)
47
+ end
48
+
49
+ it "is used instead of ApplicationController" do
50
+ Clearance.configure { |config| config.parent_controller = MyController }
51
+
52
+ expect(config.parent_controller).to eq ::MyController
53
+ end
54
+ end
55
+
32
56
  context "when secure_cookie is set to true" do
33
57
  it "returns true" do
34
58
  Clearance.configure { |config| config.secure_cookie = true }
@@ -159,28 +183,22 @@ describe Clearance::Configuration do
159
183
  end
160
184
 
161
185
  describe "#rotate_csrf_on_sign_in?" do
162
- it "defaults to falsey and warns" do
163
- Clearance.configuration = Clearance::Configuration.new
164
- allow(Clearance.configuration).to receive(:warn)
165
-
166
- expect(Clearance.configuration.rotate_csrf_on_sign_in?).to be_falsey
167
- expect(Clearance.configuration).to have_received(:warn)
168
- end
169
-
170
- it "is true and does not warn when `rotate_csrf_on_sign_in` is true" do
186
+ it "is true when `rotate_csrf_on_sign_in` is set to true" do
171
187
  Clearance.configure { |config| config.rotate_csrf_on_sign_in = true }
172
- allow(Clearance.configuration).to receive(:warn)
173
188
 
174
189
  expect(Clearance.configuration.rotate_csrf_on_sign_in?).to be true
175
- expect(Clearance.configuration).not_to have_received(:warn)
176
190
  end
177
191
 
178
- it "is false and does not warn when `rotate_csrf_on_sign_in` is false" do
192
+ it "is false when `rotate_csrf_on_sign_in` is set to false" do
179
193
  Clearance.configure { |config| config.rotate_csrf_on_sign_in = false }
180
- allow(Clearance.configuration).to receive(:warn)
181
194
 
182
195
  expect(Clearance.configuration.rotate_csrf_on_sign_in?).to be false
183
- expect(Clearance.configuration).not_to have_received(:warn)
196
+ end
197
+
198
+ it "is false when `rotate_csrf_on_sign_in` is set to nil" do
199
+ Clearance.configure { |config| config.rotate_csrf_on_sign_in = nil }
200
+
201
+ expect(Clearance.configuration.rotate_csrf_on_sign_in?).to be false
184
202
  end
185
203
  end
186
204
  end
@@ -38,12 +38,26 @@ describe Clearance::PasswordsController do
38
38
  end
39
39
 
40
40
  context "email param is missing" do
41
- it "does not raise error" do
42
- expect do
43
- post :create, params: {
44
- password: {},
41
+ it "displays flash error on new page" do
42
+ post :create, params: {
43
+ password: {},
44
+ }
45
+
46
+ expect(flash.now[:alert]).to match(/email can't be blank/i)
47
+ expect(response).to render_template(:new)
48
+ end
49
+ end
50
+
51
+ context "email param is blank" do
52
+ it "displays flash error on new page" do
53
+ post :create, params: {
54
+ password: {
55
+ email: "",
45
56
  }
46
- end.not_to raise_error
57
+ }
58
+
59
+ expect(flash.now[:alert]).to match(/email can't be blank/i)
60
+ expect(response).to render_template(:new)
47
61
  end
48
62
  end
49
63
 
@@ -2,6 +2,6 @@ class ApplicationController < ActionController::Base
2
2
  include Clearance::Controller
3
3
 
4
4
  def show
5
- render html: "", layout: "application"
5
+ render inline: "Hello user #<%= current_user.id %>", layout: false
6
6
  end
7
7
  end
@@ -70,7 +70,30 @@ describe Clearance::Generators::InstallGenerator, :generator do
70
70
 
71
71
  expect(migration).to exist
72
72
  expect(migration).to have_correct_syntax
73
- expect(migration).to contain("create_table :users")
73
+ expect(migration).to contain("create_table :users do")
74
+ end
75
+
76
+ context "active record configured for uuid" do
77
+ around do |example|
78
+ preserve_original_primary_key_type_setting do
79
+ Rails.application.config.generators do |g|
80
+ g.orm :active_record, primary_key_type: :uuid
81
+ end
82
+ example.run
83
+ end
84
+ end
85
+
86
+ it "creates a migration to create the users table with key type set" do
87
+ provide_existing_application_controller
88
+ table_does_not_exist(:users)
89
+
90
+ run_generator
91
+ migration = migration_file("db/migrate/create_users.rb")
92
+
93
+ expect(migration).to exist
94
+ expect(migration).to have_correct_syntax
95
+ expect(migration).to contain("create_table :users, id: :uuid do")
96
+ end
74
97
  end
75
98
  end
76
99
 
@@ -123,6 +146,12 @@ describe Clearance::Generators::InstallGenerator, :generator do
123
146
  and_return(false)
124
147
  end
125
148
 
149
+ def preserve_original_primary_key_type_setting
150
+ original = Rails.configuration.generators.active_record[:primary_key_type]
151
+ yield
152
+ Rails.configuration.generators.active_record[:primary_key_type] = original
153
+ end
154
+
126
155
  def contain_models_inherit_from
127
156
  contain "< #{models_inherit_from}\n"
128
157
  end
@@ -8,7 +8,6 @@ describe Clearance::Generators::ViewsGenerator, :generator do
8
8
  views = %w(
9
9
  clearance_mailer/change_password.html.erb
10
10
  clearance_mailer/change_password.text.erb
11
- layouts/application.html.erb
12
11
  passwords/create.html.erb
13
12
  passwords/edit.html.erb
14
13
  passwords/new.html.erb
@@ -5,15 +5,15 @@ describe User do
5
5
  it { is_expected.to have_db_index(:remember_token) }
6
6
  it { is_expected.to validate_presence_of(:email) }
7
7
  it { is_expected.to validate_presence_of(:password) }
8
+ it { is_expected.to allow_value("foo;@example.com").for(:email) }
9
+ it { is_expected.to allow_value("foo@.example.com").for(:email) }
10
+ it { is_expected.to allow_value("foo@example..com").for(:email) }
8
11
  it { is_expected.to allow_value("foo@example.co.uk").for(:email) }
9
12
  it { is_expected.to allow_value("foo@example.com").for(:email) }
10
13
  it { is_expected.to allow_value("foo+bar@example.com").for(:email) }
11
- it { is_expected.not_to allow_value("foo@").for(:email) }
12
- it { is_expected.not_to allow_value("foo@example..com").for(:email) }
13
- it { is_expected.not_to allow_value("foo@.example.com").for(:email) }
14
- it { is_expected.not_to allow_value("foo").for(:email) }
15
14
  it { is_expected.not_to allow_value("example.com").for(:email) }
16
- it { is_expected.not_to allow_value("foo;@example.com").for(:email) }
15
+ it { is_expected.not_to allow_value("foo").for(:email) }
16
+ it { is_expected.not_to allow_value("foo@").for(:email) }
17
17
 
18
18
  describe "#email" do
19
19
  it "stores email in down case and removes whitespace" do
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ class PagesController < ApplicationController
4
+ include Clearance::Controller
5
+ before_action :require_login, only: :private
6
+
7
+ # A page requiring user authentication
8
+ def private
9
+ head :ok
10
+ end
11
+
12
+ # A page that does not require user authentication
13
+ def public
14
+ head :ok
15
+ end
16
+ end
17
+
18
+ describe "Authentication cookies in the response" do
19
+ before do
20
+ draw_test_routes
21
+ create_user_and_sign_in
22
+ end
23
+
24
+ after do
25
+ Rails.application.reload_routes!
26
+ end
27
+
28
+ it "are not present if the request does not authenticate" do
29
+ get public_path
30
+
31
+ expect(headers["Set-Cookie"]).to be_nil
32
+ end
33
+
34
+ it "are present if the request does authenticate" do
35
+ get private_path
36
+
37
+ expect(headers["Set-Cookie"]).to match(/remember_token=/)
38
+ end
39
+
40
+ def draw_test_routes
41
+ Rails.application.routes.draw do
42
+ get "/private" => "pages#private", as: :private
43
+ get "/public" => "pages#public", as: :public
44
+ resource :session, controller: "clearance/sessions", only: [:create]
45
+ end
46
+ end
47
+
48
+ def create_user_and_sign_in
49
+ user = create(:user, password: "password")
50
+
51
+ post session_path, params: {
52
+ session: { email: user.email, password: "password" },
53
+ }
54
+ end
55
+ end
@@ -46,5 +46,4 @@ end
46
46
 
47
47
  def restore_default_warning_free_config
48
48
  Clearance.configuration = nil
49
- Clearance.configure { |config| config.rotate_csrf_on_sign_in = true }
50
49
  end
@@ -18,7 +18,7 @@ module GeneratorSpecHelpers
18
18
  end
19
19
 
20
20
  def provide_existing_user_class
21
- copy_to_generator_root("app/models", versionize_template("user.rb"))
21
+ copy_to_generator_root("app/models", "user.rb")
22
22
  allow(File).to receive(:exist?).and_call_original
23
23
  allow(File).to receive(:exist?).with("app/models/user.rb").and_return(true)
24
24
  end
@@ -32,10 +32,6 @@ module GeneratorSpecHelpers
32
32
  FileUtils.mkdir_p(destination)
33
33
  FileUtils.cp(template_file, destination)
34
34
  end
35
-
36
- def versionize_template(template_file)
37
- ["rails5", template_file].join("/")
38
- end
39
35
  end
40
36
 
41
37
  RSpec.configure do |config|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clearance
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Croak
@@ -25,7 +25,7 @@ authors:
25
25
  autorequire:
26
26
  bindir: bin
27
27
  cert_chain: []
28
- date: 2019-11-12 00:00:00.000000000 Z
28
+ date: 2019-12-19 00:00:00.000000000 Z
29
29
  dependencies:
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: bcrypt
@@ -47,14 +47,14 @@ dependencies:
47
47
  requirements:
48
48
  - - "~>"
49
49
  - !ruby/object:Gem::Version
50
- version: '1.4'
50
+ version: '2.0'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
55
  - - "~>"
56
56
  - !ruby/object:Gem::Version
57
- version: '1.4'
57
+ version: '2.0'
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: railties
60
60
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +111,12 @@ dependencies:
111
111
  - - ">="
112
112
  - !ruby/object:Gem::Version
113
113
  version: '5.0'
114
- description: Rails authentication & authorization with email & password.
114
+ description: |2
115
+ Clearance is built to support authentication and authorization via an
116
+ email/password sign-in mechanism in applications.
117
+
118
+ It provides some core classes commonly used for these features, along with
119
+ some opinionated defaults - but is intended to be easy to override.
115
120
  email: support@thoughtbot.com
116
121
  executables: []
117
122
  extensions: []
@@ -137,7 +142,6 @@ files:
137
142
  - app/mailers/clearance_mailer.rb
138
143
  - app/views/clearance_mailer/change_password.html.erb
139
144
  - app/views/clearance_mailer/change_password.text.erb
140
- - app/views/layouts/application.html.erb
141
145
  - app/views/passwords/create.html.erb
142
146
  - app/views/passwords/edit.html.erb
143
147
  - app/views/passwords/new.html.erb
@@ -205,12 +209,12 @@ files:
205
209
  - lib/generators/clearance/views/views_generator.rb
206
210
  - spec/acceptance/clearance_installation_spec.rb
207
211
  - spec/app_templates/app/controllers/application_controller.rb
208
- - spec/app_templates/app/models/rails5/user.rb
209
212
  - spec/app_templates/app/models/user.rb
210
213
  - spec/app_templates/config/initializers/clearance.rb
211
214
  - spec/app_templates/config/routes.rb
212
215
  - spec/app_templates/testapp/Gemfile
213
216
  - spec/app_templates/testapp/app/controllers/home_controller.rb
217
+ - spec/app_templates/testapp/app/views/layouts/application.html.erb
214
218
  - spec/app_templates/testapp/config/initializers/action_mailer.rb
215
219
  - spec/app_templates/testapp/config/routes.rb
216
220
  - spec/clearance/back_door_spec.rb
@@ -248,6 +252,7 @@ files:
248
252
  - spec/models/user_spec.rb
249
253
  - spec/password_strategies/bcrypt_spec.rb
250
254
  - spec/password_strategies/password_strategies_spec.rb
255
+ - spec/requests/authentication_cookie_spec.rb
251
256
  - spec/requests/cookie_options_spec.rb
252
257
  - spec/requests/csrf_rotation_spec.rb
253
258
  - spec/requests/password_maintenance_spec.rb
@@ -1,23 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <%= csrf_meta_tag %>
5
- </head>
6
- <body>
7
- <div id="header">
8
- <% if signed_in? -%>
9
- <%= button_to t(".sign_out"), sign_out_path, method: :delete %>
10
- <% else -%>
11
- <%= link_to t(".sign_in"), sign_in_path %>
12
- <% end -%>
13
- </div>
14
-
15
- <div id="flash">
16
- <% flash.each do |key, value| -%>
17
- <div id="flash_<%= key %>"><%=h value %></div>
18
- <% end %>
19
- </div>
20
-
21
- <%= yield %>
22
- </body>
23
- </html>
@@ -1,5 +0,0 @@
1
- class User < ApplicationRecord
2
- def previously_existed?
3
- true
4
- end
5
- end