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.

Files changed (47) hide show
  1. data/CHANGELOG.rdoc +42 -0
  2. data/README.rdoc +26 -4
  3. data/Rakefile +2 -2
  4. data/TODO +4 -33
  5. data/app/controllers/sessions_controller.rb +6 -2
  6. data/config/locales/en.yml +1 -0
  7. data/generators/devise/USAGE +5 -0
  8. data/generators/devise/devise_generator.rb +25 -0
  9. data/generators/devise/lib/route_devise.rb +32 -0
  10. data/generators/devise/templates/README +21 -0
  11. data/generators/devise/templates/migration.rb +20 -0
  12. data/generators/devise/templates/model.rb +5 -0
  13. data/generators/devise_views/USAGE +3 -0
  14. data/generators/devise_views/devise_views_generator.rb +24 -0
  15. data/generators/devise_views/templates/confirmations/new.html.erb +16 -0
  16. data/generators/devise_views/templates/notifier/confirmation_instructions.html.erb +5 -0
  17. data/generators/devise_views/templates/notifier/reset_password_instructions.html.erb +8 -0
  18. data/generators/devise_views/templates/passwords/edit.html.erb +20 -0
  19. data/generators/devise_views/templates/passwords/new.html.erb +16 -0
  20. data/generators/devise_views/templates/sessions/new.html.erb +23 -0
  21. data/lib/devise.rb +36 -14
  22. data/lib/devise/active_record.rb +15 -20
  23. data/lib/devise/controllers/helpers.rb +8 -2
  24. data/lib/devise/failure.rb +29 -0
  25. data/lib/devise/hooks/confirmable.rb +11 -0
  26. data/lib/devise/hooks/rememberable.rb +5 -2
  27. data/lib/devise/migrations.rb +7 -6
  28. data/lib/devise/models/authenticable.rb +4 -5
  29. data/lib/devise/models/confirmable.rb +55 -12
  30. data/lib/devise/models/rememberable.rb +32 -8
  31. data/lib/devise/strategies/authenticable.rb +3 -6
  32. data/lib/devise/strategies/rememberable.rb +2 -0
  33. data/lib/devise/version.rb +1 -1
  34. data/lib/devise/warden.rb +5 -8
  35. data/test/active_record_test.rb +18 -3
  36. data/test/failure_test.rb +34 -0
  37. data/test/integration/authenticable_test.rb +5 -4
  38. data/test/integration/confirmable_test.rb +29 -0
  39. data/test/integration/rememberable_test.rb +9 -0
  40. data/test/models/authenticable_test.rb +0 -20
  41. data/test/models/confirmable_test.rb +58 -12
  42. data/test/models/recoverable_test.rb +0 -4
  43. data/test/models/rememberable_test.rb +76 -6
  44. data/test/rails_app/app/models/admin.rb +1 -1
  45. data/test/support/model_tests_helper.rb +0 -4
  46. data/test/test_helper.rb +1 -1
  47. metadata +52 -34
@@ -17,8 +17,18 @@ module Devise
17
17
  #
18
18
  # devise :all, :stretches => 20
19
19
  #
20
- # You can refer to Authenticable for more information about writing your own
21
- # method to setup pepper and stretches.
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 if modules.include?(:all)
54
- modules -= Array(options.delete(:except)) if options.key?(: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 do |key, value|
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
- flash[key] = I18n.t(:"#{resource_name}.#{kind}",
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'] = record.class.serialize_into_cookie(record)
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['remember_token'] = nil
25
+ auth.cookies.delete('remember_token')
23
26
  end
24
27
  end
@@ -19,10 +19,11 @@ module Devise
19
19
 
20
20
  # Creates email, encrypted_password and password_salt.
21
21
  #
22
- def authenticable
23
- string :email, :limit => 100, :null => false
24
- string :encrypted_password, :limit => 40, :null => false
25
- string :password_salt, :limit => 20, :null => false
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 remember_expires_at.
43
+ # Creates remember_token and remember_created_at.
43
44
  #
44
45
  def rememberable
45
46
  string :remember_token, :limit => 20
46
- datetime :remember_expires_at
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
- clear_confirmation_token
34
- update_attribute(:confirmed_at, Time.now)
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
- # Remove confirmation date from the user, ensuring after a user update
62
- # it's email, it won't be able to sign in without confirming it.
63
- def reset_confirmation
64
- generate_confirmation_token
65
- self.confirmed_at = nil
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.present? && remember_token == 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(record)
58
- "#{record.id}::#{record.remember_token}"
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
- record_id, remember_token = cookie.split('::')
64
- record = find_by_id(record_id)
65
- record if record.try(:valid_remember_token?, remember_token)
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
- redirect!(sign_in_path, :unauthenticated => true)
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)
@@ -31,3 +31,5 @@ module Devise
31
31
  end
32
32
  end
33
33
  end
34
+
35
+ Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "0.1.1".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -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 = SessionsController
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'