clearance 2.3.0 → 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of clearance might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yml +52 -0
- data/Gemfile.lock +52 -50
- data/NEWS.md +10 -0
- data/app/views/clearance_mailer/change_password.html.erb +1 -1
- data/app/views/clearance_mailer/change_password.text.erb +1 -1
- data/app/views/passwords/edit.html.erb +1 -1
- data/gemfiles/rails_6.1.gemfile +21 -0
- data/lib/clearance/configuration.rb +19 -0
- data/lib/clearance/rack_session.rb +1 -1
- data/lib/clearance/session.rb +23 -11
- data/lib/clearance/user.rb +1 -1
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/install_generator.rb +4 -1
- data/spec/clearance/rack_session_spec.rb +1 -2
- data/spec/clearance/session_spec.rb +97 -43
- data/spec/configuration_spec.rb +28 -0
- data/spec/generators/clearance/install/install_generator_spec.rb +8 -2
- data/spec/mailers/clearance_mailer_spec.rb +33 -0
- data/spec/models/user_spec.rb +2 -2
- data/spec/support/clearance.rb +11 -0
- data/spec/support/request_with_remember_token.rb +8 -6
- metadata +4 -3
- data/.travis.yml +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 73e524b6026ced3c81ba4f5755fcc40190b5ca08e058d4297780600dc09dfa9a
|
4
|
+
data.tar.gz: 5c8fe49a083f5bddf070ed33eed1c78b5154d5da2c4f6bb3b52f5709c3db7875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8f2689813bcd73ed5d8cd9f5783f3659dbf001f924af4c595c2a5470ad5d1b9d9f57126117626204f0cec9e13b989d757e4baa33e077bc7b6cfde394d6a2f3d
|
7
|
+
data.tar.gz: ac38abe61a29243c8e253954accad74c8ada5532876b53483ce4991b745124c265674a6df908814a78b3ef4d467e8abd27e9355332e9162bfd25865f8b7bea2b
|
@@ -0,0 +1,52 @@
|
|
1
|
+
name: CI Tests
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: "master"
|
6
|
+
pull_request:
|
7
|
+
branches: "*"
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
name: "Ruby ${{ matrix.ruby }}, Rails ${{ matrix.gemfile }}"
|
12
|
+
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
gemfile:
|
19
|
+
- "5.0"
|
20
|
+
- "5.1"
|
21
|
+
- "5.2"
|
22
|
+
- "6.0"
|
23
|
+
- "6.1"
|
24
|
+
ruby:
|
25
|
+
- "2.4.9"
|
26
|
+
- "2.5.7"
|
27
|
+
- "2.6.5"
|
28
|
+
- "2.7.2"
|
29
|
+
exclude:
|
30
|
+
- gemfile: "6.0"
|
31
|
+
ruby: "2.4.9"
|
32
|
+
- gemfile: "6.1"
|
33
|
+
ruby: "2.4.9"
|
34
|
+
|
35
|
+
env:
|
36
|
+
BUNDLE_GEMFILE: gemfiles/rails_${{ matrix.gemfile }}.gemfile
|
37
|
+
RAILS_ENV: test
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- uses: actions/checkout@v2
|
41
|
+
|
42
|
+
- name: "Install Ruby ${{ matrix.ruby }}"
|
43
|
+
uses: ruby/setup-ruby@v1
|
44
|
+
with:
|
45
|
+
ruby-version: ${{ matrix.ruby }}
|
46
|
+
bundler-cache: true
|
47
|
+
|
48
|
+
- name: "Reset app database"
|
49
|
+
run: bundle exec rake dummy:db:reset
|
50
|
+
|
51
|
+
- name: "Run tests"
|
52
|
+
run: bundle exec rake
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
clearance (2.3.
|
4
|
+
clearance (2.3.1)
|
5
5
|
actionmailer (>= 5.0)
|
6
6
|
activemodel (>= 5.0)
|
7
7
|
activerecord (>= 5.0)
|
@@ -13,39 +13,40 @@ PATH
|
|
13
13
|
GEM
|
14
14
|
remote: https://rubygems.org/
|
15
15
|
specs:
|
16
|
-
actionmailer (6.
|
17
|
-
actionpack (= 6.
|
18
|
-
actionview (= 6.
|
19
|
-
activejob (= 6.
|
16
|
+
actionmailer (6.1.3)
|
17
|
+
actionpack (= 6.1.3)
|
18
|
+
actionview (= 6.1.3)
|
19
|
+
activejob (= 6.1.3)
|
20
|
+
activesupport (= 6.1.3)
|
20
21
|
mail (~> 2.5, >= 2.5.4)
|
21
22
|
rails-dom-testing (~> 2.0)
|
22
|
-
actionpack (6.
|
23
|
-
actionview (= 6.
|
24
|
-
activesupport (= 6.
|
25
|
-
rack (~> 2.0, >= 2.0.
|
23
|
+
actionpack (6.1.3)
|
24
|
+
actionview (= 6.1.3)
|
25
|
+
activesupport (= 6.1.3)
|
26
|
+
rack (~> 2.0, >= 2.0.9)
|
26
27
|
rack-test (>= 0.6.3)
|
27
28
|
rails-dom-testing (~> 2.0)
|
28
29
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
29
|
-
actionview (6.
|
30
|
-
activesupport (= 6.
|
30
|
+
actionview (6.1.3)
|
31
|
+
activesupport (= 6.1.3)
|
31
32
|
builder (~> 3.1)
|
32
33
|
erubi (~> 1.4)
|
33
34
|
rails-dom-testing (~> 2.0)
|
34
35
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
35
|
-
activejob (6.
|
36
|
-
activesupport (= 6.
|
36
|
+
activejob (6.1.3)
|
37
|
+
activesupport (= 6.1.3)
|
37
38
|
globalid (>= 0.3.6)
|
38
|
-
activemodel (6.
|
39
|
-
activesupport (= 6.
|
40
|
-
activerecord (6.
|
41
|
-
activemodel (= 6.
|
42
|
-
activesupport (= 6.
|
43
|
-
activesupport (6.
|
39
|
+
activemodel (6.1.3)
|
40
|
+
activesupport (= 6.1.3)
|
41
|
+
activerecord (6.1.3)
|
42
|
+
activemodel (= 6.1.3)
|
43
|
+
activesupport (= 6.1.3)
|
44
|
+
activesupport (6.1.3)
|
44
45
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
45
|
-
i18n (>=
|
46
|
-
minitest (
|
47
|
-
tzinfo (~>
|
48
|
-
zeitwerk (~> 2.
|
46
|
+
i18n (>= 1.6, < 2)
|
47
|
+
minitest (>= 5.1)
|
48
|
+
tzinfo (~> 2.0)
|
49
|
+
zeitwerk (~> 2.3)
|
49
50
|
addressable (2.7.0)
|
50
51
|
public_suffix (>= 2.0.2, < 5.0)
|
51
52
|
ammeter (1.1.4)
|
@@ -56,12 +57,12 @@ GEM
|
|
56
57
|
bundler
|
57
58
|
rake
|
58
59
|
thor (>= 0.14.0)
|
59
|
-
argon2 (2.0.
|
60
|
-
ffi (~> 1.
|
61
|
-
ffi-compiler (
|
62
|
-
ast (2.4.
|
63
|
-
bcrypt (3.1.
|
64
|
-
better_html (1.0.
|
60
|
+
argon2 (2.0.3)
|
61
|
+
ffi (~> 1.14)
|
62
|
+
ffi-compiler (~> 1.0)
|
63
|
+
ast (2.4.2)
|
64
|
+
bcrypt (3.1.16)
|
65
|
+
better_html (1.0.16)
|
65
66
|
actionview (>= 4.0)
|
66
67
|
activesupport (>= 4.0)
|
67
68
|
ast (~> 2.0)
|
@@ -79,11 +80,11 @@ GEM
|
|
79
80
|
regexp_parser (~> 1.5)
|
80
81
|
xpath (~> 3.2)
|
81
82
|
coderay (1.1.3)
|
82
|
-
concurrent-ruby (1.1.
|
83
|
+
concurrent-ruby (1.1.8)
|
83
84
|
crass (1.0.6)
|
84
85
|
database_cleaner (1.8.5)
|
85
86
|
diff-lcs (1.4.4)
|
86
|
-
email_validator (2.
|
87
|
+
email_validator (2.2.2)
|
87
88
|
activemodel
|
88
89
|
erb_lint (0.0.34)
|
89
90
|
activesupport
|
@@ -92,39 +93,41 @@ GEM
|
|
92
93
|
rainbow
|
93
94
|
rubocop (~> 0.79)
|
94
95
|
smart_properties
|
95
|
-
erubi (1.
|
96
|
+
erubi (1.10.0)
|
96
97
|
factory_bot (6.1.0)
|
97
98
|
activesupport (>= 5.0.0)
|
98
99
|
factory_bot_rails (6.1.0)
|
99
100
|
factory_bot (~> 6.1.0)
|
100
101
|
railties (>= 5.0.0)
|
101
|
-
ffi (1.
|
102
|
+
ffi (1.14.2)
|
102
103
|
ffi-compiler (1.0.1)
|
103
104
|
ffi (>= 1.0.0)
|
104
105
|
rake
|
105
106
|
globalid (0.4.2)
|
106
107
|
activesupport (>= 4.2.0)
|
107
108
|
html_tokenizer (0.0.7)
|
108
|
-
i18n (1.8.
|
109
|
+
i18n (1.8.9)
|
109
110
|
concurrent-ruby (~> 1.0)
|
110
|
-
loofah (2.
|
111
|
+
loofah (2.9.0)
|
111
112
|
crass (~> 1.0.2)
|
112
113
|
nokogiri (>= 1.5.9)
|
113
114
|
mail (2.7.1)
|
114
115
|
mini_mime (>= 0.1.1)
|
115
116
|
method_source (1.0.0)
|
116
117
|
mini_mime (1.0.2)
|
117
|
-
mini_portile2 (2.
|
118
|
-
minitest (5.14.
|
119
|
-
nokogiri (1.
|
120
|
-
mini_portile2 (~> 2.
|
118
|
+
mini_portile2 (2.5.0)
|
119
|
+
minitest (5.14.4)
|
120
|
+
nokogiri (1.11.1)
|
121
|
+
mini_portile2 (~> 2.5.0)
|
122
|
+
racc (~> 1.4)
|
121
123
|
parallel (1.19.2)
|
122
|
-
parser (
|
124
|
+
parser (3.0.0.0)
|
123
125
|
ast (~> 2.4.1)
|
124
126
|
pry (0.13.1)
|
125
127
|
coderay (~> 1.1)
|
126
128
|
method_source (~> 1.0)
|
127
129
|
public_suffix (4.0.5)
|
130
|
+
racc (1.5.2)
|
128
131
|
rack (2.2.3)
|
129
132
|
rack-test (1.1.0)
|
130
133
|
rack (>= 1.0, < 3)
|
@@ -137,14 +140,14 @@ GEM
|
|
137
140
|
nokogiri (>= 1.6)
|
138
141
|
rails-html-sanitizer (1.3.0)
|
139
142
|
loofah (~> 2.3)
|
140
|
-
railties (6.
|
141
|
-
actionpack (= 6.
|
142
|
-
activesupport (= 6.
|
143
|
+
railties (6.1.3)
|
144
|
+
actionpack (= 6.1.3)
|
145
|
+
activesupport (= 6.1.3)
|
143
146
|
method_source
|
144
147
|
rake (>= 0.8.7)
|
145
|
-
thor (
|
148
|
+
thor (~> 1.0)
|
146
149
|
rainbow (3.0.0)
|
147
|
-
rake (13.0.
|
150
|
+
rake (13.0.3)
|
148
151
|
regexp_parser (1.7.1)
|
149
152
|
rexml (3.2.4)
|
150
153
|
rspec-core (3.9.2)
|
@@ -180,15 +183,14 @@ GEM
|
|
180
183
|
activesupport (>= 4.2.0)
|
181
184
|
smart_properties (1.15.0)
|
182
185
|
sqlite3 (1.4.2)
|
183
|
-
thor (1.0
|
184
|
-
thread_safe (0.3.6)
|
186
|
+
thor (1.1.0)
|
185
187
|
timecop (0.9.1)
|
186
|
-
tzinfo (
|
187
|
-
|
188
|
+
tzinfo (2.0.4)
|
189
|
+
concurrent-ruby (~> 1.0)
|
188
190
|
unicode-display_width (1.7.0)
|
189
191
|
xpath (3.2.0)
|
190
192
|
nokogiri (~> 1.8)
|
191
|
-
zeitwerk (2.4.
|
193
|
+
zeitwerk (2.4.2)
|
192
194
|
|
193
195
|
PLATFORMS
|
194
196
|
ruby
|
data/NEWS.md
CHANGED
@@ -3,6 +3,16 @@
|
|
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.3.1] - March 5, 2021
|
7
|
+
|
8
|
+
### Fixed
|
9
|
+
|
10
|
+
- Support for accessing Rails 6.x primary_key_type in generator.
|
11
|
+
- Fix password reset URLs when using a custom model
|
12
|
+
- Fix flaky test that relied on too specific time delta
|
13
|
+
- Revert case sensitivity for email uniqueness
|
14
|
+
- Bump nokogiri and actionview dependencies to address security vulnerabilities
|
15
|
+
|
6
16
|
## [2.3.0] - August 14, 2020
|
7
17
|
|
8
18
|
### Fixed
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<p><%= t(".description") %></p>
|
5
5
|
|
6
6
|
<%= form_for :password_reset,
|
7
|
-
url:
|
7
|
+
url: [@user, :password, token: @user.confirmation_token],
|
8
8
|
html: { method: :put } do |form| %>
|
9
9
|
<div class="password-field">
|
10
10
|
<%= form.label :password %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "addressable"
|
6
|
+
gem "ammeter"
|
7
|
+
gem "appraisal"
|
8
|
+
gem "capybara"
|
9
|
+
gem "database_cleaner"
|
10
|
+
gem "erb_lint", require: false
|
11
|
+
gem "factory_bot_rails"
|
12
|
+
gem "nokogiri"
|
13
|
+
gem "pry", require: false
|
14
|
+
gem "rails-controller-testing"
|
15
|
+
gem "rspec-rails"
|
16
|
+
gem "shoulda-matchers"
|
17
|
+
gem "sqlite3"
|
18
|
+
gem "timecop"
|
19
|
+
gem "railties", "~> 6.1"
|
20
|
+
|
21
|
+
gemspec path: "../"
|
@@ -88,6 +88,14 @@ module Clearance
|
|
88
88
|
# @return [Boolean]
|
89
89
|
attr_accessor :secure_cookie
|
90
90
|
|
91
|
+
# Controls whether cookies are signed.
|
92
|
+
# Defaults to `false` for backwards compatibility.
|
93
|
+
# When set, uses Rails' signed cookies
|
94
|
+
# (more secure against timing/brute-force attacks)
|
95
|
+
# see [ActionDispatch::Cookies](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html)
|
96
|
+
# @return [Boolean|:migrate]
|
97
|
+
attr_reader :signed_cookie
|
98
|
+
|
91
99
|
# The array of sign in guards to run when signing a user in.
|
92
100
|
# Defaults to an empty array. Sign in guards respond to `call` and are
|
93
101
|
# initialized with a session and the current stack. Each guard can decide
|
@@ -124,9 +132,20 @@ module Clearance
|
|
124
132
|
@rotate_csrf_on_sign_in = true
|
125
133
|
@routes = true
|
126
134
|
@secure_cookie = false
|
135
|
+
@signed_cookie = false
|
127
136
|
@sign_in_guards = []
|
128
137
|
end
|
129
138
|
|
139
|
+
def signed_cookie=(value)
|
140
|
+
if [true, false, :migrate].include? value
|
141
|
+
@signed_cookie = value
|
142
|
+
else
|
143
|
+
raise "Clearance's signed_cookie configuration value is invalid. " \
|
144
|
+
"Valid values are true, false, or :migrate. " \
|
145
|
+
"Set this option via Clearance.configure in an initializer"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
130
149
|
# The class representing the configured user model.
|
131
150
|
# In the default configuration, this is the `User` class.
|
132
151
|
# @return [Class]
|
data/lib/clearance/session.rb
CHANGED
@@ -14,15 +14,9 @@ module Clearance
|
|
14
14
|
# Called by {RackSession} to add the Clearance session cookie to a response.
|
15
15
|
#
|
16
16
|
# @return [void]
|
17
|
-
def add_cookie_to_headers
|
17
|
+
def add_cookie_to_headers
|
18
18
|
if signed_in_with_remember_token?
|
19
|
-
|
20
|
-
headers,
|
21
|
-
remember_token_cookie,
|
22
|
-
cookie_options.merge(
|
23
|
-
value: current_user.remember_token,
|
24
|
-
),
|
25
|
-
)
|
19
|
+
set_remember_token(current_user.remember_token)
|
26
20
|
end
|
27
21
|
end
|
28
22
|
|
@@ -112,9 +106,27 @@ module Clearance
|
|
112
106
|
@cookies ||= ActionDispatch::Request.new(@env).cookie_jar
|
113
107
|
end
|
114
108
|
|
109
|
+
# @api private
|
110
|
+
def set_remember_token(token)
|
111
|
+
case Clearance.configuration.signed_cookie
|
112
|
+
when true, :migrate
|
113
|
+
cookies.signed[remember_token_cookie] = cookie_options(token)
|
114
|
+
when false
|
115
|
+
cookies[remember_token_cookie] = cookie_options(token)
|
116
|
+
end
|
117
|
+
remember_token
|
118
|
+
end
|
119
|
+
|
115
120
|
# @api private
|
116
121
|
def remember_token
|
117
|
-
|
122
|
+
case Clearance.configuration.signed_cookie
|
123
|
+
when true
|
124
|
+
cookies.signed[remember_token_cookie]
|
125
|
+
when :migrate
|
126
|
+
cookies.signed[remember_token_cookie] || cookies[remember_token_cookie]
|
127
|
+
when false
|
128
|
+
cookies[remember_token_cookie]
|
129
|
+
end
|
118
130
|
end
|
119
131
|
|
120
132
|
# @api private
|
@@ -159,7 +171,7 @@ module Clearance
|
|
159
171
|
end
|
160
172
|
|
161
173
|
# @api private
|
162
|
-
def cookie_options
|
174
|
+
def cookie_options(value)
|
163
175
|
{
|
164
176
|
domain: domain,
|
165
177
|
expires: remember_token_expires,
|
@@ -167,7 +179,7 @@ module Clearance
|
|
167
179
|
same_site: Clearance.configuration.same_site,
|
168
180
|
path: Clearance.configuration.cookie_path,
|
169
181
|
secure: Clearance.configuration.secure_cookie,
|
170
|
-
value:
|
182
|
+
value: value,
|
171
183
|
}
|
172
184
|
end
|
173
185
|
|
data/lib/clearance/user.rb
CHANGED
@@ -152,7 +152,7 @@ module Clearance
|
|
152
152
|
validates :email,
|
153
153
|
email: { strict_mode: true },
|
154
154
|
presence: true,
|
155
|
-
uniqueness: { allow_blank: true, case_sensitive:
|
155
|
+
uniqueness: { allow_blank: true, case_sensitive: true },
|
156
156
|
unless: :email_optional?
|
157
157
|
|
158
158
|
validates :password, presence: true, unless: :skip_password_validation?
|
data/lib/clearance/version.rb
CHANGED
@@ -129,7 +129,10 @@ module Clearance
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def configured_key_type
|
132
|
-
Rails.configuration.generators.active_record
|
132
|
+
active_record = Rails.configuration.generators.active_record
|
133
|
+
active_record ||= Rails.configuration.generators.options[:active_record]
|
134
|
+
|
135
|
+
active_record[:primary_key_type]
|
133
136
|
end
|
134
137
|
|
135
138
|
def models_inherit_from
|
@@ -20,7 +20,6 @@ describe Clearance::RackSession do
|
|
20
20
|
response = Rack::MockResponse.new(*app.call(env))
|
21
21
|
|
22
22
|
expect(response.body).to eq expected_session
|
23
|
-
expect(expected_session).to have_received(:add_cookie_to_headers)
|
24
|
-
with(hash_including(headers))
|
23
|
+
expect(expected_session).to have_received(:add_cookie_to_headers)
|
25
24
|
end
|
26
25
|
end
|
@@ -4,7 +4,6 @@ describe Clearance::Session do
|
|
4
4
|
before { Timecop.freeze }
|
5
5
|
after { Timecop.return }
|
6
6
|
|
7
|
-
let(:headers) { {} }
|
8
7
|
let(:session) { Clearance::Session.new(env_without_remember_token) }
|
9
8
|
let(:user) { create(:user) }
|
10
9
|
|
@@ -35,9 +34,63 @@ describe Clearance::Session do
|
|
35
34
|
Clearance.configuration.cookie_name = "custom_cookie_name"
|
36
35
|
|
37
36
|
session.sign_in user
|
38
|
-
session.add_cookie_to_headers
|
37
|
+
session.add_cookie_to_headers
|
39
38
|
|
40
|
-
expect(
|
39
|
+
expect(remember_token_cookie(session, "custom_cookie_name")).to be_present
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with signed cookies == false" do
|
44
|
+
it "uses cookies.signed" do
|
45
|
+
Clearance.configuration.signed_cookie = true
|
46
|
+
|
47
|
+
cookie_jar = {}
|
48
|
+
expect(session).to receive(:cookies).and_return(cookie_jar)
|
49
|
+
expect(cookie_jar).to receive(:signed).and_return(cookie_jar)
|
50
|
+
|
51
|
+
session.sign_in user
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with signed cookies == true" do
|
56
|
+
it "uses cookies.signed" do
|
57
|
+
Clearance.configuration.signed_cookie = true
|
58
|
+
|
59
|
+
cookie_jar = {}
|
60
|
+
expect(session).to receive(:cookies).and_return(cookie_jar)
|
61
|
+
expect(cookie_jar).to receive(:signed).and_return(cookie_jar)
|
62
|
+
|
63
|
+
session.sign_in user
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "with signed cookies == :migrate" do
|
68
|
+
before do
|
69
|
+
Clearance.configuration.signed_cookie = :migrate
|
70
|
+
end
|
71
|
+
|
72
|
+
context "signed cookie exists" do
|
73
|
+
it "uses cookies.signed[remember_token]" do
|
74
|
+
cookie_jar = { "remember_token" => "signed cookie" }
|
75
|
+
expect(session).to receive(:cookies).and_return(cookie_jar)
|
76
|
+
expect(cookie_jar).to receive(:signed).and_return(cookie_jar)
|
77
|
+
|
78
|
+
session.sign_in user
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "signed cookie does not exist yet" do
|
83
|
+
it "uses cookies[remember_token] instead" do
|
84
|
+
cookie_jar = { "remember_token" => "signed cookie" }
|
85
|
+
# first call will try to get the signed cookie
|
86
|
+
expect(session).to receive(:cookies).and_return(cookie_jar)
|
87
|
+
# ... but signed_cookie doesn't exist
|
88
|
+
expect(cookie_jar).to receive(:signed).and_return({})
|
89
|
+
# then it attempts to retrieve the unsigned cookie
|
90
|
+
expect(session).to receive(:cookies).and_return(cookie_jar)
|
91
|
+
|
92
|
+
session.sign_in user
|
93
|
+
end
|
41
94
|
end
|
42
95
|
end
|
43
96
|
|
@@ -157,9 +210,9 @@ describe Clearance::Session do
|
|
157
210
|
end
|
158
211
|
|
159
212
|
it 'sets a httponly cookie' do
|
160
|
-
session.add_cookie_to_headers
|
213
|
+
session.add_cookie_to_headers
|
161
214
|
|
162
|
-
expect(
|
215
|
+
expect(remember_token_cookie(session)[:httponly]).to be_truthy
|
163
216
|
end
|
164
217
|
end
|
165
218
|
|
@@ -170,9 +223,9 @@ describe Clearance::Session do
|
|
170
223
|
end
|
171
224
|
|
172
225
|
it 'sets a standard cookie' do
|
173
|
-
session.add_cookie_to_headers
|
226
|
+
session.add_cookie_to_headers
|
174
227
|
|
175
|
-
expect(
|
228
|
+
expect(remember_token_cookie(session)[:httponly]).to be_falsey
|
176
229
|
end
|
177
230
|
end
|
178
231
|
|
@@ -183,9 +236,9 @@ describe Clearance::Session do
|
|
183
236
|
end
|
184
237
|
|
185
238
|
it "sets a same-site cookie" do
|
186
|
-
session.add_cookie_to_headers
|
239
|
+
session.add_cookie_to_headers
|
187
240
|
|
188
|
-
expect(
|
241
|
+
expect(remember_token_cookie(session)[:same_site]).to eq(:lax)
|
189
242
|
end
|
190
243
|
end
|
191
244
|
|
@@ -195,9 +248,9 @@ describe Clearance::Session do
|
|
195
248
|
end
|
196
249
|
|
197
250
|
it "sets a standard cookie" do
|
198
|
-
session.add_cookie_to_headers
|
251
|
+
session.add_cookie_to_headers
|
199
252
|
|
200
|
-
expect(
|
253
|
+
expect(remember_token_cookie(session)[:same_site]).to be_nil
|
201
254
|
end
|
202
255
|
end
|
203
256
|
|
@@ -205,15 +258,11 @@ describe Clearance::Session do
|
|
205
258
|
context 'default configuration' do
|
206
259
|
it 'is set to 1 year from now' do
|
207
260
|
user = double("User", remember_token: "123abc")
|
208
|
-
headers = {}
|
209
261
|
session = Clearance::Session.new(env_without_remember_token)
|
210
262
|
session.sign_in user
|
211
|
-
session.add_cookie_to_headers
|
263
|
+
session.add_cookie_to_headers
|
212
264
|
|
213
|
-
expect(
|
214
|
-
'remember_token',
|
215
|
-
user.remember_token, 1.year.from_now
|
216
|
-
)
|
265
|
+
expect(remember_token_cookie(session)[:expires]).to eq(1.year.from_now)
|
217
266
|
end
|
218
267
|
end
|
219
268
|
|
@@ -225,18 +274,14 @@ describe Clearance::Session do
|
|
225
274
|
end
|
226
275
|
with_custom_expiration expires_at do
|
227
276
|
user = double("User", remember_token: "123abc")
|
228
|
-
headers = {}
|
229
277
|
environment = env_with_cookies(remember_me: 'true')
|
230
278
|
session = Clearance::Session.new(environment)
|
231
279
|
session.sign_in user
|
232
|
-
session.add_cookie_to_headers
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
user.remember_token
|
237
|
-
remembered_expires
|
238
|
-
)
|
239
|
-
|
280
|
+
session.add_cookie_to_headers
|
281
|
+
expect(remember_token_cookie(session)[:expires]).to \
|
282
|
+
eq(remembered_expires)
|
283
|
+
expect(remember_token_cookie(session)[:value]).to \
|
284
|
+
eq(user.remember_token)
|
240
285
|
end
|
241
286
|
end
|
242
287
|
end
|
@@ -249,9 +294,9 @@ describe Clearance::Session do
|
|
249
294
|
end
|
250
295
|
|
251
296
|
it 'sets a standard cookie' do
|
252
|
-
session.add_cookie_to_headers
|
297
|
+
session.add_cookie_to_headers
|
253
298
|
|
254
|
-
expect(
|
299
|
+
expect(remember_token_cookie(session)[:secure]).to be_falsey
|
255
300
|
end
|
256
301
|
end
|
257
302
|
|
@@ -262,9 +307,9 @@ describe Clearance::Session do
|
|
262
307
|
end
|
263
308
|
|
264
309
|
it 'sets a secure cookie' do
|
265
|
-
session.add_cookie_to_headers
|
310
|
+
session.add_cookie_to_headers
|
266
311
|
|
267
|
-
expect(
|
312
|
+
expect(remember_token_cookie(session)[:secure]).to be_truthy
|
268
313
|
end
|
269
314
|
end
|
270
315
|
end
|
@@ -280,9 +325,9 @@ describe Clearance::Session do
|
|
280
325
|
let(:cookie_domain) { ".example.com" }
|
281
326
|
|
282
327
|
it "sets a standard cookie" do
|
283
|
-
session.add_cookie_to_headers
|
328
|
+
session.add_cookie_to_headers
|
284
329
|
|
285
|
-
expect(
|
330
|
+
expect(remember_token_cookie(session)[:domain]).to eq(cookie_domain)
|
286
331
|
end
|
287
332
|
end
|
288
333
|
|
@@ -290,9 +335,9 @@ describe Clearance::Session do
|
|
290
335
|
let(:cookie_domain) { lambda { |_r| ".example.com" } }
|
291
336
|
|
292
337
|
it "sets a standard cookie" do
|
293
|
-
session.add_cookie_to_headers
|
338
|
+
session.add_cookie_to_headers
|
294
339
|
|
295
|
-
expect(
|
340
|
+
expect(remember_token_cookie(session)[:domain]).to eq(".example.com")
|
296
341
|
end
|
297
342
|
end
|
298
343
|
end
|
@@ -301,9 +346,9 @@ describe Clearance::Session do
|
|
301
346
|
before { session.sign_in(user) }
|
302
347
|
|
303
348
|
it 'sets a standard cookie' do
|
304
|
-
session.add_cookie_to_headers
|
349
|
+
session.add_cookie_to_headers
|
305
350
|
|
306
|
-
expect(
|
351
|
+
expect(remember_token_cookie(session)[:domain]).to be_nil
|
307
352
|
end
|
308
353
|
end
|
309
354
|
end
|
@@ -313,9 +358,9 @@ describe Clearance::Session do
|
|
313
358
|
before { session.sign_in(user) }
|
314
359
|
|
315
360
|
it 'sets a standard cookie' do
|
316
|
-
session.add_cookie_to_headers
|
361
|
+
session.add_cookie_to_headers
|
317
362
|
|
318
|
-
expect(
|
363
|
+
expect(remember_token_cookie(session)[:domain]).to be_nil
|
319
364
|
end
|
320
365
|
end
|
321
366
|
|
@@ -326,18 +371,17 @@ describe Clearance::Session do
|
|
326
371
|
end
|
327
372
|
|
328
373
|
it 'sets a standard cookie' do
|
329
|
-
session.add_cookie_to_headers
|
374
|
+
session.add_cookie_to_headers
|
330
375
|
|
331
|
-
expect(
|
376
|
+
expect(remember_token_cookie(session)[:path]).to eq("/user")
|
332
377
|
end
|
333
378
|
end
|
334
379
|
end
|
335
380
|
|
336
381
|
it 'does not set a remember token when signed out' do
|
337
|
-
headers = {}
|
338
382
|
session = Clearance::Session.new(env_without_remember_token)
|
339
|
-
session.add_cookie_to_headers
|
340
|
-
expect(
|
383
|
+
session.add_cookie_to_headers
|
384
|
+
expect(remember_token_cookie(session)).to be_nil
|
341
385
|
end
|
342
386
|
|
343
387
|
describe "#sign_out" do
|
@@ -399,6 +443,16 @@ describe Clearance::Session do
|
|
399
443
|
end
|
400
444
|
end
|
401
445
|
|
446
|
+
# a bit of a hack to get the cookies that ActionDispatch sets inside session
|
447
|
+
def remember_token_cookie(session, cookie_name = "remember_token")
|
448
|
+
cookies = session.send(:cookies)
|
449
|
+
# see https://stackoverflow.com/a/21315095
|
450
|
+
set_cookies = cookies.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
451
|
+
@set_cookies
|
452
|
+
RUBY
|
453
|
+
set_cookies[cookie_name]
|
454
|
+
end
|
455
|
+
|
402
456
|
def env_with_cookies(cookies)
|
403
457
|
Rack::MockRequest.env_for '/', 'HTTP_COOKIE' => serialize_cookies(cookies)
|
404
458
|
end
|
data/spec/configuration_spec.rb
CHANGED
@@ -66,6 +66,34 @@ describe Clearance::Configuration do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
context "when signed_cookie is set to true" do
|
70
|
+
it "returns true" do
|
71
|
+
Clearance.configure { |config| config.signed_cookie = true }
|
72
|
+
expect(Clearance.configuration.signed_cookie).to eq true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when signed_cookie is not specified" do
|
77
|
+
it "defaults to false" do
|
78
|
+
expect(Clearance.configuration.signed_cookie).to eq false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when signed_cookie is set to :migrate" do
|
83
|
+
it "returns :migrate" do
|
84
|
+
Clearance.configure { |config| config.signed_cookie = :migrate }
|
85
|
+
expect(Clearance.configuration.signed_cookie).to eq :migrate
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when signed_cookie is set to an unexpected value" do
|
90
|
+
it "returns :migrate" do
|
91
|
+
expect {
|
92
|
+
Clearance.configure { |config| config.signed_cookie = "unknown" }
|
93
|
+
}.to raise_exception(RuntimeError)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
69
97
|
context "when no redirect URL specified" do
|
70
98
|
it 'returns "/" as redirect URL' do
|
71
99
|
expect(Clearance::Configuration.new.redirect_url).to eq "/"
|
@@ -147,9 +147,15 @@ describe Clearance::Generators::InstallGenerator, :generator do
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def preserve_original_primary_key_type_setting
|
150
|
-
|
150
|
+
active_record = Rails.configuration.generators.active_record
|
151
|
+
active_record ||= Rails.configuration.generators.options[:active_record]
|
152
|
+
original = active_record[:primary_key_type]
|
153
|
+
|
151
154
|
yield
|
152
|
-
|
155
|
+
|
156
|
+
Rails.application.config.generators do |g|
|
157
|
+
g.orm :active_record, primary_key_type: original
|
158
|
+
end
|
153
159
|
end
|
154
160
|
|
155
161
|
def contain_models_inherit_from
|
@@ -55,4 +55,37 @@ describe ClearanceMailer do
|
|
55
55
|
text: I18n.t("clearance_mailer.change_password.link_text")
|
56
56
|
)
|
57
57
|
end
|
58
|
+
|
59
|
+
context "when using a custom model" do
|
60
|
+
it "contains a link for a custom model" do
|
61
|
+
define_people_routes
|
62
|
+
Person = Class.new(User)
|
63
|
+
person = Person.new(email: "person@example.com", password: "password")
|
64
|
+
|
65
|
+
person.forgot_password!
|
66
|
+
host = ActionMailer::Base.default_url_options[:host]
|
67
|
+
link = "http://#{host}/people/#{person.id}/password/edit" \
|
68
|
+
"?token=#{person.confirmation_token}"
|
69
|
+
|
70
|
+
email = ClearanceMailer.change_password(person)
|
71
|
+
|
72
|
+
expect(email.text_part.body).to include(link)
|
73
|
+
expect(email.html_part.body).to include(link)
|
74
|
+
|
75
|
+
Object.send(:remove_const, :Person)
|
76
|
+
Rails.application.reload_routes!
|
77
|
+
end
|
78
|
+
|
79
|
+
def define_people_routes
|
80
|
+
Rails.application.routes.draw do
|
81
|
+
resources :people, controller: "clearance/users", only: :create do
|
82
|
+
resource(
|
83
|
+
:password,
|
84
|
+
controller: "clearance/passwords",
|
85
|
+
only: %i[edit update],
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
58
91
|
end
|
data/spec/models/user_spec.rb
CHANGED
@@ -59,7 +59,7 @@ describe User do
|
|
59
59
|
User.authenticate("bad_email@example.com", password)
|
60
60
|
end
|
61
61
|
|
62
|
-
expect(user_does_not_exist_time). to be_within(0.
|
62
|
+
expect(user_does_not_exist_time). to be_within(0.01).of(user_exists_time)
|
63
63
|
end
|
64
64
|
|
65
65
|
it "takes the same amount of time to fail authentication regardless of whether user exists" do
|
@@ -73,7 +73,7 @@ describe User do
|
|
73
73
|
User.authenticate("bad_email@example.com", "bad_password")
|
74
74
|
end
|
75
75
|
|
76
|
-
expect(user_does_not_exist_time). to be_within(0.
|
76
|
+
expect(user_does_not_exist_time). to be_within(0.01).of(user_exists_time)
|
77
77
|
end
|
78
78
|
|
79
79
|
it "is retrieved via a case-insensitive search" do
|
data/spec/support/clearance.rb
CHANGED
@@ -4,6 +4,17 @@ Clearance.configure do |config|
|
|
4
4
|
# need an empty block to initialize the configuration object
|
5
5
|
end
|
6
6
|
|
7
|
+
# NOTE: to run the entire suite with signed cookies
|
8
|
+
# you can set the signed_cookie default to true
|
9
|
+
# and run all specs.
|
10
|
+
# However, to fake the actual signing process you
|
11
|
+
# can monkey-patch ActionDispatch so signed cookies
|
12
|
+
# behave like normal ones
|
13
|
+
#
|
14
|
+
# class ActionDispatch::Cookies::CookieJar
|
15
|
+
# def signed; self; end
|
16
|
+
# end
|
17
|
+
|
7
18
|
module Clearance
|
8
19
|
module Test
|
9
20
|
module Redirects
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module RememberTokenHelpers
|
2
2
|
def request_with_remember_token(remember_token)
|
3
|
-
cookies = {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
cookies = ActionDispatch::Request.new({}).cookie_jar
|
4
|
+
if Clearance.configuration.signed_cookie
|
5
|
+
cookies.signed[Clearance.configuration.cookie_name] = remember_token
|
6
|
+
else
|
7
|
+
cookies[Clearance.configuration.cookie_name] = remember_token
|
8
|
+
end
|
9
|
+
|
10
|
+
env = { clearance: Clearance::Session.new(cookies.request.env) }
|
9
11
|
Rack::Request.new env
|
10
12
|
end
|
11
13
|
|
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.3.
|
4
|
+
version: 2.3.1
|
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:
|
28
|
+
date: 2021-03-05 00:00:00.000000000 Z
|
29
29
|
dependencies:
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: bcrypt
|
@@ -145,8 +145,8 @@ extra_rdoc_files:
|
|
145
145
|
- README.md
|
146
146
|
files:
|
147
147
|
- ".erb-lint.yml"
|
148
|
+
- ".github/workflows/tests.yml"
|
148
149
|
- ".gitignore"
|
149
|
-
- ".travis.yml"
|
150
150
|
- ".yardopts"
|
151
151
|
- Appraisals
|
152
152
|
- CONTRIBUTING.md
|
@@ -184,6 +184,7 @@ files:
|
|
184
184
|
- gemfiles/rails_5.1.gemfile
|
185
185
|
- gemfiles/rails_5.2.gemfile
|
186
186
|
- gemfiles/rails_6.0.gemfile
|
187
|
+
- gemfiles/rails_6.1.gemfile
|
187
188
|
- lib/clearance.rb
|
188
189
|
- lib/clearance/authentication.rb
|
189
190
|
- lib/clearance/authorization.rb
|
data/.travis.yml
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
cache: bundler
|
2
|
-
|
3
|
-
language:
|
4
|
-
- ruby
|
5
|
-
|
6
|
-
rvm:
|
7
|
-
- 2.4.9
|
8
|
-
- 2.5.7
|
9
|
-
- 2.6.5
|
10
|
-
- 2.7.0
|
11
|
-
|
12
|
-
gemfile:
|
13
|
-
- gemfiles/rails_5.0.gemfile
|
14
|
-
- gemfiles/rails_5.1.gemfile
|
15
|
-
- gemfiles/rails_5.2.gemfile
|
16
|
-
- gemfiles/rails_6.0.gemfile
|
17
|
-
|
18
|
-
install:
|
19
|
-
- "bin/setup"
|
20
|
-
|
21
|
-
branches:
|
22
|
-
only:
|
23
|
-
- master
|
24
|
-
|
25
|
-
matrix:
|
26
|
-
exclude:
|
27
|
-
- rvm: 2.4.9
|
28
|
-
gemfile: gemfiles/rails_6.0.gemfile
|