devise 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of devise might be problematic. Click here for more details.
- data/CHANGELOG.rdoc +42 -0
- data/README.rdoc +26 -4
- data/Rakefile +2 -2
- data/TODO +4 -33
- data/app/controllers/sessions_controller.rb +6 -2
- data/config/locales/en.yml +1 -0
- data/generators/devise/USAGE +5 -0
- data/generators/devise/devise_generator.rb +25 -0
- data/generators/devise/lib/route_devise.rb +32 -0
- data/generators/devise/templates/README +21 -0
- data/generators/devise/templates/migration.rb +20 -0
- data/generators/devise/templates/model.rb +5 -0
- data/generators/devise_views/USAGE +3 -0
- data/generators/devise_views/devise_views_generator.rb +24 -0
- data/generators/devise_views/templates/confirmations/new.html.erb +16 -0
- data/generators/devise_views/templates/notifier/confirmation_instructions.html.erb +5 -0
- data/generators/devise_views/templates/notifier/reset_password_instructions.html.erb +8 -0
- data/generators/devise_views/templates/passwords/edit.html.erb +20 -0
- data/generators/devise_views/templates/passwords/new.html.erb +16 -0
- data/generators/devise_views/templates/sessions/new.html.erb +23 -0
- data/lib/devise.rb +36 -14
- data/lib/devise/active_record.rb +15 -20
- data/lib/devise/controllers/helpers.rb +8 -2
- data/lib/devise/failure.rb +29 -0
- data/lib/devise/hooks/confirmable.rb +11 -0
- data/lib/devise/hooks/rememberable.rb +5 -2
- data/lib/devise/migrations.rb +7 -6
- data/lib/devise/models/authenticable.rb +4 -5
- data/lib/devise/models/confirmable.rb +55 -12
- data/lib/devise/models/rememberable.rb +32 -8
- data/lib/devise/strategies/authenticable.rb +3 -6
- data/lib/devise/strategies/rememberable.rb +2 -0
- data/lib/devise/version.rb +1 -1
- data/lib/devise/warden.rb +5 -8
- data/test/active_record_test.rb +18 -3
- data/test/failure_test.rb +34 -0
- data/test/integration/authenticable_test.rb +5 -4
- data/test/integration/confirmable_test.rb +29 -0
- data/test/integration/rememberable_test.rb +9 -0
- data/test/models/authenticable_test.rb +0 -20
- data/test/models/confirmable_test.rb +58 -12
- data/test/models/recoverable_test.rb +0 -4
- data/test/models/rememberable_test.rb +76 -6
- data/test/rails_app/app/models/admin.rb +1 -1
- data/test/support/model_tests_helper.rb +0 -4
- data/test/test_helper.rb +1 -1
- metadata +52 -34
data/lib/devise/active_record.rb
CHANGED
@@ -17,8 +17,18 @@ module Devise
|
|
17
17
|
#
|
18
18
|
# devise :all, :stretches => 20
|
19
19
|
#
|
20
|
-
#
|
21
|
-
#
|
20
|
+
# * confirm_in: the time you want your user to confirm it's account. During
|
21
|
+
# this time he will be able to access your application without confirming.
|
22
|
+
#
|
23
|
+
# devise :all, :confirm_in => 7.days
|
24
|
+
#
|
25
|
+
# * remember_for: the time the user will be remembered without asking for
|
26
|
+
# credentials again.
|
27
|
+
#
|
28
|
+
# devise :all, :remember_for => 2.weeks
|
29
|
+
#
|
30
|
+
# You can refer to Authenticable, Confirmable and Rememberable for more
|
31
|
+
# information about writing your own method to setup each model apart.
|
22
32
|
#
|
23
33
|
# Examples:
|
24
34
|
#
|
@@ -48,10 +58,9 @@ module Devise
|
|
48
58
|
#
|
49
59
|
def devise(*modules)
|
50
60
|
options = modules.extract_options!
|
51
|
-
options.assert_valid_keys(:except, *Devise::MODEL_CONFIG)
|
52
61
|
|
53
|
-
modules = Devise::ALL
|
54
|
-
modules -= Array(options.delete(:except))
|
62
|
+
modules = Devise::ALL if modules.include?(:all)
|
63
|
+
modules -= Array(options.delete(:except))
|
55
64
|
modules |= [:authenticable]
|
56
65
|
|
57
66
|
modules.each do |m|
|
@@ -60,21 +69,7 @@ module Devise
|
|
60
69
|
end
|
61
70
|
|
62
71
|
# Convert new keys to methods which overwrites Devise defaults
|
63
|
-
options.each
|
64
|
-
case value
|
65
|
-
when Proc
|
66
|
-
define_method key, &value
|
67
|
-
next
|
68
|
-
when ActiveSupport::Duration
|
69
|
-
value = value.to_i
|
70
|
-
end
|
71
|
-
|
72
|
-
class_eval <<-END_EVAL, __FILE__, __LINE__
|
73
|
-
def #{key}
|
74
|
-
#{value.inspect}
|
75
|
-
end
|
76
|
-
END_EVAL
|
77
|
-
end
|
72
|
+
options.each { |key, value| send(:"#{key}=", value) }
|
78
73
|
end
|
79
74
|
|
80
75
|
# Stores all modules included inside the model, so we are able to verify
|
@@ -81,11 +81,17 @@ module Devise
|
|
81
81
|
#
|
82
82
|
# Please refer to README or en.yml locale file to check what messages are
|
83
83
|
# available.
|
84
|
-
def set_flash_message(key, kind)
|
85
|
-
|
84
|
+
def set_flash_message(key, kind, now=false)
|
85
|
+
flash_hash = now ? flash.now : flash
|
86
|
+
flash_hash[key] = I18n.t(:"#{resource_name}.#{kind}",
|
86
87
|
:scope => [:devise, controller_name.to_sym], :default => kind)
|
87
88
|
end
|
88
89
|
|
90
|
+
# Shortcut to set flash.now message. Same rules applied from set_flash_message
|
91
|
+
def set_now_flash_message(key, kind)
|
92
|
+
set_flash_message(key, kind, true)
|
93
|
+
end
|
94
|
+
|
89
95
|
end
|
90
96
|
end
|
91
97
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Devise
|
2
|
+
module Failure
|
3
|
+
mattr_accessor :default_url
|
4
|
+
|
5
|
+
# Failure application that will be called every time :warden is thrown from
|
6
|
+
# any strategy or hook. Responsible for redirect the user to the sign in
|
7
|
+
# page based on current scope and mapping. If no scope is given, redirect
|
8
|
+
# to the default_url.
|
9
|
+
def self.call(env)
|
10
|
+
options = env['warden.options']
|
11
|
+
params = options[:params] || {}
|
12
|
+
scope = options[:scope]
|
13
|
+
|
14
|
+
redirect_path = if mapping = Devise.mappings[scope]
|
15
|
+
"/#{mapping.as}/#{mapping.path_names[:sign_in]}"
|
16
|
+
else
|
17
|
+
"/#{default_url}"
|
18
|
+
end
|
19
|
+
|
20
|
+
headers = {}
|
21
|
+
headers["Location"] = redirect_path
|
22
|
+
headers["Location"] << "?" << Rack::Utils.build_query(params) unless params.empty?
|
23
|
+
headers["Content-Type"] = 'text/plain'
|
24
|
+
|
25
|
+
message = options[:message] || "You are being redirected to #{redirect_path}"
|
26
|
+
[302, headers, message]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Each time the user is set we verify if it is still able to really sign in.
|
2
|
+
# This is done by checking the time frame the user is able to sign in without
|
3
|
+
# confirming it's account. If the user has not confirmed it's account during
|
4
|
+
# this time frame, he/she will not able to sign in anymore.
|
5
|
+
Warden::Manager.after_set_user do |record, auth, options|
|
6
|
+
if record && record.respond_to?(:active?) && !record.active?
|
7
|
+
scope = options[:scope]
|
8
|
+
auth.logout(scope)
|
9
|
+
throw :warden, :scope => scope, :params => { :unconfirmed => true }
|
10
|
+
end
|
11
|
+
end
|
@@ -9,7 +9,10 @@ Warden::Manager.after_authentication do |record, auth, options|
|
|
9
9
|
|
10
10
|
if Devise::TRUE_VALUES.include?(remember_me) && record.respond_to?(:remember_me!)
|
11
11
|
record.remember_me!
|
12
|
-
auth.cookies['remember_token'] =
|
12
|
+
auth.cookies['remember_token'] = {
|
13
|
+
:value => record.class.serialize_into_cookie(record),
|
14
|
+
:expires => record.remember_expires_at
|
15
|
+
}
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
@@ -19,6 +22,6 @@ end
|
|
19
22
|
Warden::Manager.before_logout do |record, auth, scope|
|
20
23
|
if record.respond_to?(:forget_me!)
|
21
24
|
record.forget_me!
|
22
|
-
auth.cookies
|
25
|
+
auth.cookies.delete('remember_token')
|
23
26
|
end
|
24
27
|
end
|
data/lib/devise/migrations.rb
CHANGED
@@ -19,10 +19,11 @@ module Devise
|
|
19
19
|
|
20
20
|
# Creates email, encrypted_password and password_salt.
|
21
21
|
#
|
22
|
-
def authenticable
|
23
|
-
|
24
|
-
string :
|
25
|
-
string :
|
22
|
+
def authenticable(options={})
|
23
|
+
null = options[:null] || false
|
24
|
+
string :email, :limit => 100, :null => null
|
25
|
+
string :encrypted_password, :limit => 40, :null => null
|
26
|
+
string :password_salt, :limit => 20, :null => null
|
26
27
|
end
|
27
28
|
|
28
29
|
# Creates confirmation_token, confirmed_at and confirmation_sent_at.
|
@@ -39,11 +40,11 @@ module Devise
|
|
39
40
|
string :reset_password_token, :limit => 20
|
40
41
|
end
|
41
42
|
|
42
|
-
# Creates remember_token and
|
43
|
+
# Creates remember_token and remember_created_at.
|
43
44
|
#
|
44
45
|
def rememberable
|
45
46
|
string :remember_token, :limit => 20
|
46
|
-
datetime :
|
47
|
+
datetime :remember_created_at
|
47
48
|
end
|
48
49
|
|
49
50
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
+
require 'devise/strategies/authenticable'
|
2
3
|
|
3
4
|
module Devise
|
4
5
|
module Models
|
@@ -24,16 +25,12 @@ module Devise
|
|
24
25
|
# User.find(1).valid_password?('password123') # returns true/false
|
25
26
|
#
|
26
27
|
module Authenticable
|
27
|
-
Devise.model_config(self, :pepper)
|
28
|
-
Devise.model_config(self, :stretches, 10)
|
29
|
-
|
30
28
|
def self.included(base)
|
31
29
|
base.class_eval do
|
32
30
|
extend ClassMethods
|
33
31
|
|
34
32
|
attr_reader :password
|
35
33
|
attr_accessor :password_confirmation
|
36
|
-
attr_accessible :email, :password, :password_confirmation
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
@@ -74,7 +71,6 @@ module Devise
|
|
74
71
|
end
|
75
72
|
|
76
73
|
module ClassMethods
|
77
|
-
|
78
74
|
# Authenticate a user based on email and password. Returns the
|
79
75
|
# authenticated user if it's valid or nil.
|
80
76
|
# Attributes are :email and :password
|
@@ -93,6 +89,9 @@ module Devise
|
|
93
89
|
perishable
|
94
90
|
end
|
95
91
|
end
|
92
|
+
|
93
|
+
Devise.model_config(self, :pepper)
|
94
|
+
Devise.model_config(self, :stretches, 10)
|
96
95
|
end
|
97
96
|
end
|
98
97
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'devise/hooks/confirmable'
|
2
|
+
|
1
3
|
module Devise
|
2
4
|
module Models
|
3
5
|
|
@@ -9,6 +11,17 @@ module Devise
|
|
9
11
|
# Whenever the user update it's email, his account is automatically unconfirmed,
|
10
12
|
# it means it won't be able to sign in again without confirming the account
|
11
13
|
# again through the email that was sent.
|
14
|
+
#
|
15
|
+
# Configuration:
|
16
|
+
#
|
17
|
+
# confirm_in: the time you want the user will have to confirm it's account
|
18
|
+
# without blocking his access. When confirm_in is zero, the
|
19
|
+
# user won't be able to sign in without confirming. You can
|
20
|
+
# use this to let your user access some features of your
|
21
|
+
# application without confirming the account, but blocking it
|
22
|
+
# after a certain period (ie 7 days). By default confirm_in is
|
23
|
+
# zero, it means users always have to confirm to sign in.
|
24
|
+
#
|
12
25
|
# Examples:
|
13
26
|
#
|
14
27
|
# User.find(1).confirm! # returns true unless it's already confirmed
|
@@ -30,8 +43,9 @@ module Devise
|
|
30
43
|
# is already confirmed, add en error to email field
|
31
44
|
def confirm!
|
32
45
|
unless_confirmed do
|
33
|
-
|
34
|
-
|
46
|
+
self.confirmation_token = nil
|
47
|
+
self.confirmed_at = Time.now
|
48
|
+
save(false)
|
35
49
|
end
|
36
50
|
end
|
37
51
|
|
@@ -56,13 +70,38 @@ module Devise
|
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
73
|
+
# Verify whether a user is active to sign in or not. If the user is
|
74
|
+
# already confirmed, it should never be blocked. Otherwise we need to
|
75
|
+
# calculate if the confirm time has not expired for this user, in other
|
76
|
+
# words, if the confirmation is still valid.
|
77
|
+
def active?
|
78
|
+
confirmed? || confirmation_period_valid?
|
79
|
+
end
|
80
|
+
|
59
81
|
protected
|
60
82
|
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
83
|
+
# Checks if the confirmation for the user is within the limit time.
|
84
|
+
# We do this by calculating if the difference between today and the
|
85
|
+
# confirmation sent date does not exceed the confirm in time configured.
|
86
|
+
# Confirm_in is a model configuration, must always be an integer value.
|
87
|
+
#
|
88
|
+
# Example:
|
89
|
+
#
|
90
|
+
# # confirm_in = 1.day and confirmation_sent_at = today
|
91
|
+
# confirmation_period_valid? # returns true
|
92
|
+
#
|
93
|
+
# # confirm_in = 5.days and confirmation_sent_at = 4.days.ago
|
94
|
+
# confirmation_period_valid? # returns true
|
95
|
+
#
|
96
|
+
# # confirm_in = 5.days and confirmation_sent_at = 5.days.ago
|
97
|
+
# confirmation_period_valid? # returns false
|
98
|
+
#
|
99
|
+
# # confirm_in = 0.days
|
100
|
+
# confirmation_period_valid? # will always return false
|
101
|
+
#
|
102
|
+
def confirmation_period_valid?
|
103
|
+
confirmation_sent_at? &&
|
104
|
+
(Date.today - confirmation_sent_at.to_date).days < confirm_in
|
66
105
|
end
|
67
106
|
|
68
107
|
# Checks whether the record is confirmed or not, yielding to the block
|
@@ -76,6 +115,13 @@ module Devise
|
|
76
115
|
end
|
77
116
|
end
|
78
117
|
|
118
|
+
# Remove confirmation date from the user, ensuring after a user update
|
119
|
+
# it's email, it won't be able to sign in without confirming it.
|
120
|
+
def reset_confirmation
|
121
|
+
generate_confirmation_token
|
122
|
+
self.confirmed_at = nil
|
123
|
+
end
|
124
|
+
|
79
125
|
# Generates a new random token for confirmation, and stores the time
|
80
126
|
# this token is being generated
|
81
127
|
def generate_confirmation_token
|
@@ -89,11 +135,6 @@ module Devise
|
|
89
135
|
generate_confirmation_token && save(false)
|
90
136
|
end
|
91
137
|
|
92
|
-
# Removes confirmation token
|
93
|
-
def clear_confirmation_token
|
94
|
-
self.confirmation_token = nil
|
95
|
-
end
|
96
|
-
|
97
138
|
module ClassMethods
|
98
139
|
|
99
140
|
# Attempt to find a user by it's email. If a record is found, send new
|
@@ -120,6 +161,8 @@ module Devise
|
|
120
161
|
confirmable
|
121
162
|
end
|
122
163
|
end
|
164
|
+
|
165
|
+
Devise.model_config(self, :confirm_in, 0.days)
|
123
166
|
end
|
124
167
|
end
|
125
168
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
+
require 'devise/hooks/rememberable'
|
3
|
+
require 'devise/strategies/rememberable'
|
2
4
|
|
3
5
|
module Devise
|
4
6
|
module Models
|
@@ -9,6 +11,16 @@ module Devise
|
|
9
11
|
# to lookup the record based on the saved information.
|
10
12
|
# You probably wouldn't use rememberable methods directly, they are used
|
11
13
|
# mostly internally for handling the remember token.
|
14
|
+
#
|
15
|
+
# Configuration:
|
16
|
+
#
|
17
|
+
# remember_for: the time you want the user will be remembered without
|
18
|
+
# asking for credentials. After this time the user will be
|
19
|
+
# blocked and will have to enter his credentials again.
|
20
|
+
# This configuration is also used to calculate the expires
|
21
|
+
# time for the cookie created to remember the user.
|
22
|
+
# By default remember_for is 2.weeks.
|
23
|
+
#
|
12
24
|
# Examples:
|
13
25
|
#
|
14
26
|
# User.find(1).remember_me! # regenerating the token
|
@@ -27,13 +39,13 @@ module Devise
|
|
27
39
|
|
28
40
|
# Remember me option available in after_authentication hook.
|
29
41
|
attr_accessor :remember_me
|
30
|
-
attr_accessible :remember_me
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
34
45
|
# Generate a new remember token and save the record without validations.
|
35
46
|
def remember_me!
|
36
47
|
self.remember_token = friendly_token
|
48
|
+
self.remember_created_at = Time.now
|
37
49
|
save(false)
|
38
50
|
end
|
39
51
|
|
@@ -42,30 +54,42 @@ module Devise
|
|
42
54
|
def forget_me!
|
43
55
|
if remember_token?
|
44
56
|
self.remember_token = nil
|
57
|
+
self.remember_created_at = nil
|
45
58
|
save(false)
|
46
59
|
end
|
47
60
|
end
|
48
61
|
|
49
62
|
# Checks whether the incoming token matches or not with the record token.
|
50
63
|
def valid_remember_token?(token)
|
51
|
-
remember_token
|
64
|
+
remember_token? && !remember_expired? && remember_token == token
|
65
|
+
end
|
66
|
+
|
67
|
+
# Remember token should be expired if expiration time not overpass now.
|
68
|
+
def remember_expired?
|
69
|
+
remember_expires_at <= Time.now
|
70
|
+
end
|
71
|
+
|
72
|
+
# Remember token expires at created time + remember_for configuration
|
73
|
+
def remember_expires_at
|
74
|
+
remember_created_at + remember_for
|
52
75
|
end
|
53
76
|
|
54
77
|
module ClassMethods
|
55
78
|
|
56
79
|
# Create the cookie key using the record id and remember_token
|
57
|
-
def serialize_into_cookie(
|
58
|
-
"#{
|
80
|
+
def serialize_into_cookie(rememberable)
|
81
|
+
"#{rememberable.id}::#{rememberable.remember_token}"
|
59
82
|
end
|
60
83
|
|
61
84
|
# Recreate the user based on the stored cookie
|
62
85
|
def serialize_from_cookie(cookie)
|
63
|
-
|
64
|
-
|
65
|
-
|
86
|
+
rememberable_id, remember_token = cookie.split('::')
|
87
|
+
rememberable = find_by_id(rememberable_id) if rememberable_id
|
88
|
+
rememberable if rememberable.try(:valid_remember_token?, remember_token)
|
66
89
|
end
|
67
|
-
|
68
90
|
end
|
91
|
+
|
92
|
+
Devise.model_config(self, :remember_for, 2.weeks)
|
69
93
|
end
|
70
94
|
end
|
71
95
|
end
|
@@ -12,7 +12,7 @@ module Devise
|
|
12
12
|
success!(resource)
|
13
13
|
else
|
14
14
|
store_location
|
15
|
-
|
15
|
+
throw :warden, :scope => scope, :params => { :unauthenticated => true }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -35,11 +35,8 @@ module Devise
|
|
35
35
|
def store_location
|
36
36
|
session[:"#{mapping.name}.return_to"] = request.request_uri if request.get?
|
37
37
|
end
|
38
|
-
|
39
|
-
# Create path to sign in the resource
|
40
|
-
def sign_in_path
|
41
|
-
"/#{mapping.as}/#{mapping.path_names[:sign_in]}"
|
42
|
-
end
|
43
38
|
end
|
44
39
|
end
|
45
40
|
end
|
41
|
+
|
42
|
+
Warden::Strategies.add(:authenticable, Devise::Strategies::Authenticable)
|
data/lib/devise/version.rb
CHANGED
data/lib/devise/warden.rb
CHANGED
@@ -49,16 +49,13 @@ Warden::Manager.before_failure do |env, opts|
|
|
49
49
|
env['warden'].request.params['action'] = 'new'
|
50
50
|
end
|
51
51
|
|
52
|
+
# Setup devise strategies for Warden
|
53
|
+
require 'devise/strategies/base'
|
54
|
+
|
52
55
|
# Adds Warden Manager to Rails middleware stack, configuring default devise
|
53
56
|
# strategy and also the controller who will manage not authenticated users.
|
54
57
|
Rails.configuration.middleware.use Warden::Manager do |manager|
|
55
58
|
manager.default_strategies :rememberable, :authenticable
|
56
|
-
manager.failure_app =
|
59
|
+
manager.failure_app = Devise::Failure
|
60
|
+
manager.silence_missing_strategies!
|
57
61
|
end
|
58
|
-
|
59
|
-
# Setup devise strategies for Warden
|
60
|
-
Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)
|
61
|
-
Warden::Strategies.add(:authenticable, Devise::Strategies::Authenticable)
|
62
|
-
|
63
|
-
# Require rememberable hooks
|
64
|
-
require 'devise/hooks/rememberable'
|