Compare commits

...

7 Commits

Author SHA1 Message Date
Lucas Mazza
89931ed533 Release 3.5.6. 2016-02-01 09:09:55 -02:00
Lucas Mazza
57fdae1e48 Attempt to coerce the generated_at cookie to a Time object.
Time objects aren't properly coerced back when using the JSON cookie serialization,
so we need to do it ourselves.

To avoid any new JSON serialization issues, we now store the `generated_at` as
an String with the timestamp seconds + miliseconds in the cookie but still the
previous JSON encoded format.

Thanks to @boblail at https://github.com/plataformatec/devise/pull/3917 for the
initial patch.
2016-01-31 16:25:10 -02:00
Lucas Mazza
30e494580c Refactor Rememberable.serialized_in_cookie? to split class/instance API.
We now expose a `remember_me?` instance method as internal API for the controller
layer check if the remember me cookie is still valid.
2016-01-27 14:45:14 -02:00
José Valim
048d05a553 Ensure generated_at is a Time 2016-01-25 11:17:05 +01:00
José Valim
8cbdeb54a5 Release v3.5.5 2016-01-22 20:22:34 +01:00
José Valim
14affc8a55 Do not timeout if remember me is enabled 2016-01-22 16:18:57 +01:00
José Valim
eb0f0b662f Readd remember_expired? 2016-01-22 15:57:57 +01:00
12 changed files with 126 additions and 66 deletions

View File

@@ -1,3 +1,14 @@
### 3.5.6 - 2016-01-02
* bug fixes
* Fix type coercion of the rememberable timestamp stored on cookies.
### 3.5.5 - 2016-22-01
* bug fixes
* Bring back remember_expired? implementation
* Ensure timeouts are not triggered if remember me is being used
### 3.5.4 - 2016-18-01
* bug fixes

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
devise (3.5.4)
devise (3.5.6)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -153,7 +153,7 @@ GEM
thread_safe (0.3.5)
tzinfo (1.2.2)
thread_safe (~> 0.1)
warden (1.2.4)
warden (1.2.6)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)

View File

@@ -49,7 +49,7 @@ GIT
PATH
remote: ..
specs:
devise (3.5.3)
devise (3.5.6)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -142,7 +142,7 @@ GEM
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.43)
warden (1.2.4)
warden (1.2.6)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)
@@ -169,4 +169,4 @@ DEPENDENCIES
webrat (= 0.7.3)
BUNDLED WITH
1.10.6
1.11.2

View File

@@ -1,6 +1,6 @@
GIT
remote: git://github.com/rails/rails.git
revision: 7ec9c9635bf4d57009135ed11e89d8bf32306d73
revision: 9be9597e510d185ca7964d0a05b4ea2a7f2d50d1
branch: 4-0-stable
specs:
actionmailer (4.0.13)
@@ -43,7 +43,7 @@ GIT
PATH
remote: ..
specs:
devise (3.5.3)
devise (3.5.6)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -54,24 +54,24 @@ PATH
GEM
remote: https://rubygems.org/
specs:
activerecord-deprecated_finders (1.0.3)
activerecord-deprecated_finders (1.0.4)
arel (4.0.2)
bcrypt (3.1.10)
bson (2.3.0)
bson (3.2.6)
builder (3.1.4)
connection_pool (2.1.3)
concurrent-ruby (1.0.0)
connection_pool (2.2.0)
erubis (2.7.0)
faraday (0.9.1)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
hashie (3.4.0)
hike (1.2.3)
hashie (3.4.3)
i18n (0.7.0)
jwt (1.4.1)
jwt (1.5.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
metaclass (0.0.4)
mime-types (2.4.3)
mini_portile (0.6.2)
mime-types (2.99)
mini_portile2 (2.0.0)
minitest (4.7.5)
mocha (1.1.0)
metaclass (~> 0.0.1)
@@ -80,15 +80,15 @@ GEM
moped (~> 2.0.0)
origin (~> 2.1)
tzinfo (>= 0.3.37)
moped (2.0.4)
bson (~> 2.2)
moped (2.0.7)
bson (~> 3.0)
connection_pool (~> 2.0)
optionable (~> 0.2.0)
multi_json (1.11.0)
multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
nokogiri (1.6.6.2)
mini_portile (~> 0.6.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
oauth2 (0.9.4)
faraday (>= 0.8, < 0.10)
jwt (~> 1.0)
@@ -109,34 +109,31 @@ GEM
omniauth (~> 1.0)
rack-openid (~> 1.3.1)
optionable (0.2.0)
origin (2.1.1)
origin (2.2.0)
orm_adapter (0.5.0)
rack (1.5.2)
rack (1.5.5)
rack-openid (1.3.1)
rack (>= 1.1.0)
ruby-openid (>= 2.1.8)
rack-test (0.6.3)
rack (>= 1.0)
rake (10.4.2)
rdoc (4.2.0)
rake (10.5.0)
rdoc (4.2.1)
responders (1.1.2)
railties (>= 3.2, < 4.2)
ruby-openid (2.7.0)
sprockets (2.12.3)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.2.4)
sprockets (3.5.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (2.3.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
sqlite3 (1.3.10)
sqlite3 (1.3.11)
thor (0.19.1)
thread_safe (0.3.5)
tilt (1.4.1)
tzinfo (0.3.43)
warden (1.2.4)
tzinfo (0.3.46)
warden (1.2.6)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)
@@ -163,4 +160,4 @@ DEPENDENCIES
webrat (= 0.7.3)
BUNDLED WITH
1.10.6
1.11.2

View File

@@ -48,7 +48,7 @@ GIT
PATH
remote: ..
specs:
devise (3.5.3)
devise (3.5.6)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -142,7 +142,7 @@ GEM
tilt (1.4.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
warden (1.2.4)
warden (1.2.6)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)
@@ -169,4 +169,4 @@ DEPENDENCIES
webrat (= 0.7.3)
BUNDLED WITH
1.10.6
1.11.2

View File

@@ -58,7 +58,7 @@ GIT
PATH
remote: ..
specs:
devise (3.5.3)
devise (3.5.6)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
@@ -146,8 +146,8 @@ GEM
loofah (~> 2.0)
rake (10.4.2)
rdoc (4.2.0)
responders (2.1.0)
railties (>= 4.2.0, < 5)
responders (2.1.1)
railties (>= 4.2.0, < 5.1)
ruby-openid (2.7.0)
sprockets (2.12.3)
hike (~> 1.2)
@@ -164,7 +164,7 @@ GEM
tilt (1.4.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
warden (1.2.4)
warden (1.2.6)
rack (>= 1.0)
webrat (0.7.3)
nokogiri (>= 1.2.0)
@@ -191,4 +191,4 @@ DEPENDENCIES
webrat (= 0.7.3)
BUNDLED WITH
1.10.6
1.11.2

View File

@@ -9,6 +9,13 @@ module Devise
Rails.configuration.session_options.slice(:path, :domain, :secure)
end
def remember_me_is_active?(resource)
return false unless resource.respond_to?(:remember_me)
scope = Devise::Mapping.find_scope!(resource)
_, token, generated_at = cookies.signed[remember_key(resource, scope)]
resource.remember_me?(token, generated_at)
end
# Remembers the given resource by setting up a cookie
def remember_me(resource)
return if env["devise.skip_storage"]

View File

@@ -19,9 +19,10 @@ Warden::Manager.after_set_user do |record, warden, options|
proxy = Devise::Hooks::Proxy.new(warden)
if record.timedout?(last_request_at) && !env['devise.skip_timeout']
if record.timedout?(last_request_at) &&
!env['devise.skip_timeout'] &&
!proxy.remember_me_is_active?(record)
Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
throw :warden, scope: scope, message: :timeout
end

View File

@@ -62,6 +62,11 @@ module Devise
save(validate: false)
end
# Remember token should be expired if expiration time not overpass now.
def remember_expired?
remember_created_at.nil?
end
def remember_expires_at
self.class.remember_for.from_now
end
@@ -96,32 +101,47 @@ module Devise
def after_remembered
end
protected
def remember_me?(token, generated_at)
# TODO: Normalize the JSON type coercion along with the Timeoutable hook
# in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
if generated_at.is_a?(String)
generated_at = time_from_json(generated_at)
end
# The token is only valid if:
# 1. we have a date
# 2. the current time does not pass the expiry period
# 3. the record has a remember_created_at date
# 4. the token date is bigger than the remember_created_at
# 5. the token matches
generated_at.is_a?(Time) &&
(self.class.remember_for.ago < generated_at) &&
(generated_at > (remember_created_at || Time.now).utc) &&
Devise.secure_compare(rememberable_value, token)
end
private
def time_from_json(value)
if value =~ /\A\d+\.\d+\Z/
Time.at(value.to_f)
else
Time.parse(value) rescue nil
end
end
module ClassMethods
# Create the cookie key using the record id and remember_token
def serialize_into_cookie(record)
[record.to_key, record.rememberable_value, Time.now.utc]
[record.to_key, record.rememberable_value, Time.now.utc.to_f.to_s]
end
# Recreate the user based on the stored cookie
def serialize_from_cookie(*args)
id, token, generated_at = args
id, token, generated_at = *args
# The token is only valid if:
# 1. we have a date
# 2. the current time does not pass the expiry period
# 3. there is a record with the given id
# 4. the record has a remember_created_at date
# 5. the token date is bigger than the remember_created_at
# 6. the token matches
if generated_at &&
(self.remember_for.ago < generated_at) &&
(record = to_adapter.get(id)) &&
(generated_at > (record.remember_created_at || Time.now).utc) &&
Devise.secure_compare(record.rememberable_value, token)
record
end
record = to_adapter.get(id)
record if record && record.remember_me?(token, generated_at)
end
# Generate a token checking if one does not already exist in the database.
@@ -132,6 +152,8 @@ module Devise
end
end
private
# TODO: extend_remember_period is no longer used
Devise::Models.config(self, :remember_for, :extend_remember_period, :rememberable_options, :expire_all_remember_me_on_sign_out)
end

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "3.5.4".freeze
VERSION = "3.5.6".freeze
end

View File

@@ -165,7 +165,17 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
end
end
test 'does not crashes when the last_request_at is a String' do
test 'time out not triggered if remembered' do
user = sign_in_as_user remember_me: true
get expire_user_path(user)
assert_not_nil last_request_at
get users_path
assert_response :success
assert warden.authenticated?(:user)
end
test 'does not crash when the last_request_at is a String' do
user = sign_in_as_user
get edit_form_user_path(user, last_request_at: Time.now.utc.to_s)

View File

@@ -37,7 +37,7 @@ class RememberableTest < ActiveSupport::TestCase
id, token, date = User.serialize_into_cookie(user)
assert_equal id, user.to_key
assert_equal token, user.authenticatable_salt
assert date.is_a?(Time)
assert date.is_a?(String)
end
test 'serialize from cookie' do
@@ -46,6 +46,18 @@ class RememberableTest < ActiveSupport::TestCase
assert_equal user, User.serialize_from_cookie(user.to_key, user.authenticatable_salt, Time.now.utc)
end
test 'serialize from cookie should accept a String with the datetime seconds and microseconds' do
user = create_user
user.remember_me!
assert_equal user, User.serialize_from_cookie(user.to_key, user.authenticatable_salt, Time.now.utc.to_f.to_json)
end
test 'serialize from cookie should return nil with invalid datetime' do
user = create_user
user.remember_me!
assert_nil User.serialize_from_cookie(user.to_key, user.authenticatable_salt, "2013")
end
test 'serialize from cookie should return nil if no resource is found' do
assert_nil resource_class.serialize_from_cookie([0], "123", Time.now.utc)
end