clearance 2.0.0 → 2.3.1
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.
- checksums.yaml +4 -4
- data/.erb-lint.yml +5 -0
- data/.github/workflows/tests.yml +52 -0
- data/Appraisals +14 -19
- data/Gemfile +11 -7
- data/Gemfile.lock +142 -87
- data/NEWS.md +94 -0
- data/README.md +4 -24
- data/RELEASING.md +25 -0
- data/Rakefile +6 -1
- data/app/controllers/clearance/base_controller.rb +8 -1
- data/app/controllers/clearance/passwords_controller.rb +16 -3
- data/app/views/clearance_mailer/change_password.html.erb +2 -2
- data/app/views/clearance_mailer/change_password.text.erb +2 -2
- data/app/views/passwords/edit.html.erb +1 -1
- data/clearance.gemspec +9 -2
- data/config/locales/clearance.en.yml +1 -0
- data/config/routes.rb +1 -1
- data/gemfiles/rails_5.0.gemfile +10 -9
- data/gemfiles/rails_5.1.gemfile +11 -10
- data/gemfiles/rails_5.2.gemfile +11 -10
- data/gemfiles/rails_6.0.gemfile +11 -10
- data/gemfiles/rails_6.1.gemfile +21 -0
- data/lib/clearance/authentication.rb +1 -1
- data/lib/clearance/back_door.rb +2 -1
- data/lib/clearance/configuration.rb +37 -18
- data/lib/clearance/password_strategies.rb +2 -5
- data/lib/clearance/password_strategies/argon2.rb +23 -0
- data/lib/clearance/rack_session.rb +5 -1
- data/lib/clearance/session.rb +40 -12
- data/lib/clearance/user.rb +12 -3
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/install_generator.rb +13 -0
- data/lib/generators/clearance/install/templates/README +10 -4
- data/lib/generators/clearance/install/templates/db/migrate/add_clearance_to_users.rb.erb +1 -1
- data/lib/generators/clearance/install/templates/db/migrate/create_users.rb.erb +1 -1
- data/lib/generators/clearance/routes/templates/routes.rb +1 -1
- data/spec/acceptance/clearance_installation_spec.rb +0 -4
- data/spec/app_templates/app/models/user.rb +1 -1
- data/spec/app_templates/testapp/app/views/layouts/application.html.erb +24 -0
- data/spec/clearance/back_door_spec.rb +20 -4
- data/spec/clearance/rack_session_spec.rb +3 -2
- data/spec/clearance/session_spec.rb +154 -51
- data/spec/configuration_spec.rb +60 -14
- data/spec/controllers/passwords_controller_spec.rb +19 -5
- data/spec/dummy/app/controllers/application_controller.rb +1 -1
- data/spec/generators/clearance/install/install_generator_spec.rb +36 -1
- data/spec/generators/clearance/views/views_generator_spec.rb +0 -1
- data/spec/mailers/clearance_mailer_spec.rb +33 -0
- data/spec/models/user_spec.rb +34 -5
- data/spec/password_strategies/argon2_spec.rb +79 -0
- data/spec/requests/authentication_cookie_spec.rb +55 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/clearance.rb +11 -0
- data/spec/support/generator_spec_helpers.rb +1 -5
- data/spec/support/request_with_remember_token.rb +8 -6
- metadata +42 -12
- data/.travis.yml +0 -32
- data/app/views/layouts/application.html.erb +0 -23
- data/spec/app_templates/app/models/rails5/user.rb +0 -5
@@ -13,10 +13,7 @@ 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,
|
17
|
-
autoload :
|
18
|
-
'clearance/password_strategies/bcrypt_migration_from_sha1'
|
19
|
-
autoload :Blowfish, 'clearance/password_strategies/blowfish'
|
20
|
-
autoload :SHA1, 'clearance/password_strategies/sha1'
|
16
|
+
autoload :BCrypt, "clearance/password_strategies/bcrypt"
|
17
|
+
autoload :Argon2, "clearance/password_strategies/argon2"
|
21
18
|
end
|
22
19
|
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
|
@@ -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
|
-
|
24
|
+
|
25
|
+
if session.authentication_successful?
|
26
|
+
session.add_cookie_to_headers
|
27
|
+
end
|
28
|
+
|
25
29
|
response
|
26
30
|
end
|
27
31
|
end
|
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
|
|
@@ -81,7 +75,7 @@ module Clearance
|
|
81
75
|
end
|
82
76
|
|
83
77
|
@current_user = nil
|
84
|
-
cookies.delete remember_token_cookie
|
78
|
+
cookies.delete remember_token_cookie, delete_cookie_options
|
85
79
|
end
|
86
80
|
|
87
81
|
# True if {#current_user} is set.
|
@@ -98,6 +92,13 @@ module Clearance
|
|
98
92
|
! signed_in?
|
99
93
|
end
|
100
94
|
|
95
|
+
# True if a successful authentication has been performed
|
96
|
+
#
|
97
|
+
# @return [Boolean]
|
98
|
+
def authentication_successful?
|
99
|
+
!!@current_user
|
100
|
+
end
|
101
|
+
|
101
102
|
private
|
102
103
|
|
103
104
|
# @api private
|
@@ -105,9 +106,27 @@ module Clearance
|
|
105
106
|
@cookies ||= ActionDispatch::Request.new(@env).cookie_jar
|
106
107
|
end
|
107
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
|
+
|
108
120
|
# @api private
|
109
121
|
def remember_token
|
110
|
-
|
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
|
111
130
|
end
|
112
131
|
|
113
132
|
# @api private
|
@@ -152,7 +171,7 @@ module Clearance
|
|
152
171
|
end
|
153
172
|
|
154
173
|
# @api private
|
155
|
-
def cookie_options
|
174
|
+
def cookie_options(value)
|
156
175
|
{
|
157
176
|
domain: domain,
|
158
177
|
expires: remember_token_expires,
|
@@ -160,10 +179,19 @@ module Clearance
|
|
160
179
|
same_site: Clearance.configuration.same_site,
|
161
180
|
path: Clearance.configuration.cookie_path,
|
162
181
|
secure: Clearance.configuration.secure_cookie,
|
163
|
-
value:
|
182
|
+
value: value,
|
164
183
|
}
|
165
184
|
end
|
166
185
|
|
186
|
+
# @api private
|
187
|
+
def delete_cookie_options
|
188
|
+
Hash.new.tap do |options|
|
189
|
+
if configured_cookie_domain
|
190
|
+
options[:domain] = domain
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
167
195
|
# @api private
|
168
196
|
def domain
|
169
197
|
if configured_cookie_domain.respond_to?(:call)
|
data/lib/clearance/user.rb
CHANGED
@@ -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
|
@@ -117,11 +117,13 @@ module Clearance
|
|
117
117
|
if password.present? && user.authenticated?(password)
|
118
118
|
user
|
119
119
|
end
|
120
|
+
else
|
121
|
+
prevent_timing_attack
|
120
122
|
end
|
121
123
|
end
|
122
124
|
|
123
125
|
def find_by_normalized_email(email)
|
124
|
-
|
126
|
+
find_by(email: normalize_email(email))
|
125
127
|
end
|
126
128
|
|
127
129
|
def normalize_email(email)
|
@@ -130,6 +132,13 @@ module Clearance
|
|
130
132
|
|
131
133
|
private
|
132
134
|
|
135
|
+
DUMMY_PASSWORD = "*"
|
136
|
+
|
137
|
+
def prevent_timing_attack
|
138
|
+
new(password: DUMMY_PASSWORD)
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
133
142
|
def password_strategy
|
134
143
|
Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
|
135
144
|
end
|
@@ -143,7 +152,7 @@ module Clearance
|
|
143
152
|
validates :email,
|
144
153
|
email: { strict_mode: true },
|
145
154
|
presence: true,
|
146
|
-
uniqueness: { allow_blank: true },
|
155
|
+
uniqueness: { allow_blank: true, case_sensitive: true },
|
147
156
|
unless: :email_optional?
|
148
157
|
|
149
158
|
validates :password, presence: true, unless: :skip_password_validation?
|
data/lib/clearance/version.rb
CHANGED
@@ -122,6 +122,19 @@ 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
|
+
active_record = Rails.configuration.generators.active_record
|
133
|
+
active_record ||= Rails.configuration.generators.options[:active_record]
|
134
|
+
|
135
|
+
active_record[:primary_key_type]
|
136
|
+
end
|
137
|
+
|
125
138
|
def models_inherit_from
|
126
139
|
"ApplicationRecord"
|
127
140
|
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
|
11
|
+
In the production environment it should be your application's full hostname.
|
12
12
|
|
13
|
-
2. Display user session
|
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
|
-
|
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
|
@@ -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
|
|
@@ -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>
|
@@ -46,6 +46,18 @@ describe Clearance::BackDoor do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
it "strips 'as' from the params" do
|
50
|
+
user_id = "123"
|
51
|
+
user = double("user")
|
52
|
+
allow(User).to receive(:find).with(user_id).and_return(user)
|
53
|
+
env = build_env(as: user_id, foo: :bar)
|
54
|
+
back_door = Clearance::BackDoor.new(mock_app)
|
55
|
+
|
56
|
+
back_door.call(env)
|
57
|
+
|
58
|
+
expect(env["QUERY_STRING"]).to eq("foo=bar")
|
59
|
+
end
|
60
|
+
|
49
61
|
context "when the environments are disabled" do
|
50
62
|
before do
|
51
63
|
Clearance.configuration.allowed_backdoor_environments = nil
|
@@ -84,14 +96,18 @@ describe Clearance::BackDoor do
|
|
84
96
|
env_for_user_id("")
|
85
97
|
end
|
86
98
|
|
87
|
-
def
|
99
|
+
def build_env(params)
|
100
|
+
query = Rack::Utils.build_query(params)
|
88
101
|
clearance = double("clearance", sign_in: true)
|
89
|
-
Rack::MockRequest.env_for("
|
102
|
+
Rack::MockRequest.env_for("/?#{query}").merge(clearance: clearance)
|
103
|
+
end
|
104
|
+
|
105
|
+
def env_for_user_id(user_id)
|
106
|
+
build_env(as: user_id)
|
90
107
|
end
|
91
108
|
|
92
109
|
def env_for_username(username)
|
93
|
-
|
94
|
-
Rack::MockRequest.env_for("/?as=#{username}").merge(clearance: clearance)
|
110
|
+
build_env(as: username)
|
95
111
|
end
|
96
112
|
|
97
113
|
def mock_app
|
@@ -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)
|
@@ -18,7 +20,6 @@ describe Clearance::RackSession do
|
|
18
20
|
response = Rack::MockResponse.new(*app.call(env))
|
19
21
|
|
20
22
|
expect(response.body).to eq expected_session
|
21
|
-
expect(expected_session).to have_received(:add_cookie_to_headers)
|
22
|
-
with(hash_including(headers))
|
23
|
+
expect(expected_session).to have_received(:add_cookie_to_headers)
|
23
24
|
end
|
24
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,28 +371,86 @@ 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
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
387
|
+
describe "#sign_out" do
|
388
|
+
it "signs out a user" do
|
389
|
+
user = create(:user)
|
390
|
+
old_remember_token = user.remember_token
|
391
|
+
env = env_with_remember_token(old_remember_token)
|
392
|
+
session = Clearance::Session.new(env)
|
393
|
+
cookie_jar = ActionDispatch::Request.new(env).cookie_jar
|
394
|
+
expect(cookie_jar.deleted?(:remember_token)).to be false
|
395
|
+
|
396
|
+
session.sign_out
|
397
|
+
|
398
|
+
expect(cookie_jar.deleted?(:remember_token)).to be true
|
399
|
+
expect(session.current_user).to be_nil
|
400
|
+
expect(user.reload.remember_token).not_to eq old_remember_token
|
401
|
+
end
|
402
|
+
|
403
|
+
context "with custom cookie domain" do
|
404
|
+
let(:domain) { ".example.com" }
|
405
|
+
|
406
|
+
before do
|
407
|
+
Clearance.configuration.cookie_domain = domain
|
408
|
+
end
|
409
|
+
|
410
|
+
it "clears cookie" do
|
411
|
+
user = create(:user)
|
412
|
+
env = env_with_remember_token(
|
413
|
+
value: user.remember_token,
|
414
|
+
domain: domain,
|
415
|
+
)
|
416
|
+
session = Clearance::Session.new(env)
|
417
|
+
cookie_jar = ActionDispatch::Request.new(env).cookie_jar
|
418
|
+
expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be false
|
419
|
+
|
420
|
+
session.sign_out
|
421
|
+
|
422
|
+
expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be true
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'with callable cookie domain' do
|
427
|
+
it 'clears cookie' do
|
428
|
+
domain = '.example.com'
|
429
|
+
Clearance.configuration.cookie_domain = ->(_) { domain }
|
430
|
+
user = create(:user)
|
431
|
+
env = env_with_remember_token(
|
432
|
+
value: user.remember_token,
|
433
|
+
domain: domain
|
434
|
+
)
|
435
|
+
session = Clearance::Session.new(env)
|
436
|
+
cookie_jar = ActionDispatch::Request.new(env).cookie_jar
|
437
|
+
expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be false
|
438
|
+
|
439
|
+
session.sign_out
|
440
|
+
|
441
|
+
expect(cookie_jar.deleted?(:remember_token, domain: domain)).to be true
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
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]
|
351
454
|
end
|
352
455
|
|
353
456
|
def env_with_cookies(cookies)
|