devise 0.7.3 → 0.7.4

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 (52) hide show
  1. data/CHANGELOG.rdoc +4 -0
  2. data/README.rdoc +1 -0
  3. data/Rakefile +11 -3
  4. data/TODO +6 -3
  5. data/generators/devise_install/templates/devise.rb +7 -3
  6. data/lib/devise.rb +12 -7
  7. data/lib/devise/controllers/helpers.rb +1 -1
  8. data/lib/devise/hooks/{confirmable.rb → activatable.rb} +5 -4
  9. data/lib/devise/locales/en.yml +2 -0
  10. data/lib/devise/models.rb +20 -7
  11. data/lib/devise/models/activatable.rb +16 -0
  12. data/lib/devise/models/authenticatable.rb +14 -22
  13. data/lib/devise/models/confirmable.rb +12 -6
  14. data/lib/devise/models/cookie_serializer.rb +21 -0
  15. data/lib/devise/models/rememberable.rb +2 -20
  16. data/lib/devise/models/session_serializer.rb +19 -0
  17. data/lib/devise/models/validatable.rb +0 -4
  18. data/lib/devise/serializers/base.rb +2 -9
  19. data/lib/devise/serializers/cookie.rb +43 -0
  20. data/lib/devise/serializers/session.rb +22 -0
  21. data/lib/devise/version.rb +1 -1
  22. data/test/integration/confirmable_test.rb +3 -2
  23. data/test/integration/recoverable_test.rb +1 -1
  24. data/test/mapping_test.rb +9 -9
  25. data/test/models/authenticatable_test.rb +1 -1
  26. data/test/models/confirmable_test.rb +7 -5
  27. data/test/models/recoverable_test.rb +3 -3
  28. data/test/models/rememberable_test.rb +11 -5
  29. data/test/models/validatable_test.rb +1 -0
  30. data/test/models_test.rb +10 -10
  31. data/test/orm/active_record.rb +29 -0
  32. data/test/orm/mongo_mapper.rb +21 -0
  33. data/test/rails_app/app/{models → active_record}/account.rb +0 -0
  34. data/test/rails_app/app/{models → active_record}/admin.rb +1 -1
  35. data/test/rails_app/app/{models → active_record}/user.rb +1 -1
  36. data/test/rails_app/app/mongo_mapper/account.rb +9 -0
  37. data/test/rails_app/app/mongo_mapper/admin.rb +9 -0
  38. data/test/rails_app/app/mongo_mapper/user.rb +6 -0
  39. data/test/rails_app/config/environment.rb +3 -3
  40. data/test/rails_app/config/initializers/devise.rb +76 -0
  41. data/test/rails_app/config/routes.rb +2 -5
  42. data/test/routes_test.rb +4 -16
  43. data/test/support/assertions_helper.rb +15 -0
  44. data/test/support/integration_tests_helper.rb +1 -1
  45. data/test/support/model_tests_helper.rb +0 -15
  46. data/test/support/test_silencer.rb +5 -0
  47. data/test/test_helper.rb +7 -30
  48. data/test/test_helpers_test.rb +2 -6
  49. metadata +18 -9
  50. data/lib/devise/serializers/authenticatable.rb +0 -11
  51. data/lib/devise/serializers/rememberable.rb +0 -30
  52. data/test/rails_app/app/models/organizer.rb +0 -3
@@ -1,3 +1,7 @@
1
+ * enhancements
2
+ * Extract Activatable from Confirmable
3
+ * Decouple Serializers from Devise modules
4
+
1
5
  == 0.7.3
2
6
 
3
7
  * bug fix
@@ -13,6 +13,7 @@ Right now it's composed of seven mainly modules:
13
13
  * Confirmable: responsible for verifying whether an account is already confirmed to sign in, and to send emails with confirmation instructions.
14
14
  * Recoverable: takes care of reseting the user password and send reset instructions.
15
15
  * Rememberable: manages generating and clearing token for remember the user from a saved cookie.
16
+ * Activatable: if you need to activate accounts by other means, which are not through confirmation, use this module.
16
17
  * Timeoutable: expires sessions without activity in a certain period of time.
17
18
  * Trackable: tracks sign in count, timestamps and ip.
18
19
  * Validatable: creates all needed validations for email and password. It's totally optional, so you're able to to customize validations by yourself.
data/Rakefile CHANGED
@@ -5,10 +5,18 @@ require 'rake/testtask'
5
5
  require 'rake/rdoctask'
6
6
  require File.join(File.dirname(__FILE__), 'lib', 'devise', 'version')
7
7
 
8
- desc 'Default: run unit tests.'
9
- task :default => :test
8
+ desc 'Default: run tests for all ORMs.'
9
+ task :default => :pre_commit
10
10
 
11
- desc 'Test Devise.'
11
+ desc 'Run Devise tests for all ORMs.'
12
+ task :pre_commit do
13
+ Dir[File.join(File.dirname(__FILE__), 'test', 'orm', '*.rb')].each do |file|
14
+ orm = File.basename(file).split(".").first
15
+ system "rake test DEVISE_ORM=#{orm}"
16
+ end
17
+ end
18
+
19
+ desc 'Run Devise unit tests.'
12
20
  Rake::TestTask.new(:test) do |t|
13
21
  t.libs << 'lib'
14
22
  t.libs << 'test'
data/TODO CHANGED
@@ -1,3 +1,6 @@
1
- * Make test run with different ORMs
2
- * Add registerable support
3
- * Add http authentication support
1
+ * Make test run with DataMapper (ActiveRecord, MongoMapper)
2
+ * Add Registerable support
3
+ * Add http authentication support
4
+ * Extract SessionSerializer tests from Authenticatable
5
+ * Extract CookieSerializer tests from Authenticatable
6
+ * Extract Activatable tests from Confirmable
@@ -1,10 +1,14 @@
1
1
  # Use this hook to configure devise mailer, warden hooks and so forth. The first
2
2
  # four configuration values can also be set straight in your models.
3
3
  Devise.setup do |config|
4
- # Configure the frameworks used by default. You should always set this value
5
- # because if Devise add a new strategy, it won't be added to your application
4
+ # Configure Devise modules used by default. You should always set this value
5
+ # because if Devise adds a new strategy, it won't be added to your application
6
6
  # by default, unless you configure it here.
7
- config.all = <%= Devise::ALL.inspect %>
7
+ #
8
+ # Remember that Devise includes other modules on its own (like :activatable
9
+ # and :timeoutable) which are not included here and also plugins. So be sure
10
+ # to check the docs for a complete set.
11
+ config.all = [:authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable]
8
12
 
9
13
  # Invoke `rake secret` and use the printed value to setup a pepper to generate
10
14
  # the encrypted password. By default no pepper is used.
@@ -24,7 +24,7 @@ module Devise
24
24
  autoload :MongoMapper, 'devise/orm/mongo_mapper'
25
25
  end
26
26
 
27
- ALL = [:authenticatable, :confirmable, :recoverable, :rememberable,
27
+ ALL = [:authenticatable, :activatable, :confirmable, :recoverable, :rememberable,
28
28
  :timeoutable, :trackable, :validatable]
29
29
 
30
30
  # Maps controller names to devise modules
@@ -35,12 +35,11 @@ module Devise
35
35
  }
36
36
 
37
37
  STRATEGIES = [:authenticatable]
38
- SERIALIZERS = [:authenticatable, :rememberable]
38
+ SERIALIZERS = [:session, :cookie]
39
39
  TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
40
40
 
41
- # Maps the messages types that are used in flash message. This array is not
42
- # frozen, so you can add messages from your own strategies.
43
- FLASH_MESSAGES = [ :unauthenticated, :unconfirmed, :invalid, :timeout ]
41
+ # Maps the messages types that are used in flash message.
42
+ FLASH_MESSAGES = [ :unauthenticated, :unconfirmed, :invalid, :timeout, :inactive ]
44
43
 
45
44
  # Declare encryptors length which are used in migrations.
46
45
  ENCRYPTORS_LENGTH = {
@@ -51,6 +50,9 @@ module Devise
51
50
  :authlogic_sha512 => 128
52
51
  }
53
52
 
53
+ # Email regex used to validate email formats. Retrieved from authlogic.
54
+ EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
55
+
54
56
  # Used to encrypt password. Please generate one with rake secret.
55
57
  mattr_accessor :pepper
56
58
  @@pepper = nil
@@ -166,6 +168,9 @@ rescue
166
168
  require 'warden'
167
169
  end
168
170
 
169
- # Set the default_scope to nil, so it's overwritten when the first route is declared.
171
+ # Clear some Warden default configuration which will be overwritten
172
+ Warden::Strategies.clear!
173
+ Warden::Serializers.clear!
170
174
  Warden::Manager.default_scope = nil
171
- require 'devise/rails'
175
+
176
+ require 'devise/rails'
@@ -99,7 +99,7 @@ module Devise
99
99
  # Render a view for the specified scope. Turned off by default.
100
100
  # Accepts just :controller as option.
101
101
  def render_with_scope(action, options={})
102
-  controller_name = options.delete(:controller) || self.controller_name
102
+ controller_name = options.delete(:controller) || self.controller_name
103
103
  if Devise.scoped_views
104
104
  begin
105
105
  render :template => "#{controller_name}/#{devise_mapping.as}/#{action}"
@@ -6,12 +6,13 @@ Warden::Manager.after_set_user do |record, warden, options|
6
6
  if record && record.respond_to?(:active?) && !record.active?
7
7
  scope = options[:scope]
8
8
  warden.logout(scope)
9
+
10
+ # If winning strategy was set, this is being called after authenticate and
11
+ # there is no need to force a redirect.
9
12
  if warden.winning_strategy
10
- # If winning strategy was set, this is being called after authenticate and
11
- # there is no need to force a redirect.
12
- warden.winning_strategy.fail!(:unconfirmed)
13
+ warden.winning_strategy.fail!(record.inactive_message)
13
14
  else
14
- throw :warden, :scope => scope, :message => :unconfirmed
15
+ throw :warden, :scope => scope, :message => record.inactive_message
15
16
  end
16
17
  end
17
18
  end
@@ -7,6 +7,7 @@ en:
7
7
  unconfirmed: 'You have to confirm your account before continuing.'
8
8
  invalid: 'Invalid email or password.'
9
9
  timeout: 'Your session expired, please sign in again to continue.'
10
+ inactive: 'Your account was not activated yet.'
10
11
  passwords:
11
12
  send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
12
13
  updated: 'Your password was changed successfully. You are now signed in.'
@@ -17,3 +18,4 @@ en:
17
18
  confirmation_instructions: 'Confirmation instructions'
18
19
  reset_password_instructions: 'Reset password instructions'
19
20
 
21
+
@@ -1,10 +1,11 @@
1
1
  module Devise
2
2
  module Models
3
+ autoload :Activatable, 'devise/models/activatable'
3
4
  autoload :Authenticatable, 'devise/models/authenticatable'
4
5
  autoload :Confirmable, 'devise/models/confirmable'
5
6
  autoload :Recoverable, 'devise/models/recoverable'
6
7
  autoload :Rememberable, 'devise/models/rememberable'
7
- autoload :SessionSerializer, 'devise/models/authenticatable'
8
+ autoload :SessionSerializer, 'devise/models/session_serializer'
8
9
  autoload :Timeoutable, 'devise/models/timeoutable'
9
10
  autoload :Trackable, 'devise/models/trackable'
10
11
  autoload :Validatable, 'devise/models/validatable'
@@ -81,9 +82,9 @@ module Devise
81
82
  raise "You need to give at least one Devise module" if modules.empty?
82
83
 
83
84
  options = modules.extract_options!
84
- modules = Devise.all if modules.include?(:all)
85
+ modules += Devise.all if modules.delete(:all)
85
86
  modules -= Array(options.delete(:except))
86
- modules = Devise::ALL & modules
87
+ modules = Devise::ALL & modules.uniq
87
88
 
88
89
  Devise.orm_class.included_modules_hook(self, modules) do
89
90
  modules.each do |m|
@@ -101,7 +102,7 @@ module Devise
101
102
  @devise_modules ||= []
102
103
  end
103
104
 
104
- # Find an initialize a record setting an error if it can't be found
105
+ # Find an initialize a record setting an error if it can't be found.
105
106
  def find_or_initialize_with_error_by(attribute, value, error=:invalid)
106
107
  if value.present?
107
108
  conditions = { attribute => value }
@@ -113,13 +114,25 @@ module Devise
113
114
 
114
115
  if value.present?
115
116
  record.send(:"#{attribute}=", value)
116
- record.errors.add(attribute, error, :default => error.to_s.gsub("_", " "))
117
117
  else
118
- record.errors.add(attribute, :blank)
118
+ error, skip_default = :blank, true
119
119
  end
120
+
121
+ add_error_on(record, attribute, error, !skip_default)
120
122
  end
121
123
 
122
124
  record
123
125
  end
126
+
127
+ # Wraps add error logic in a method that works for different frameworks.
128
+ def add_error_on(record, attribute, error, add_default=true)
129
+ options = add_default ? { :default => error.to_s.gsub("_", " ") } : {}
130
+
131
+ begin
132
+ record.errors.add(attribute, error, options)
133
+ rescue ArgumentError
134
+ record.errors.add(attribute, error.to_s.gsub("_", " "))
135
+ end
136
+ end
124
137
  end
125
- end
138
+ end
@@ -0,0 +1,16 @@
1
+ require 'devise/hooks/activatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # This module implements the default API required in activatable hook.
6
+ module Activatable
7
+ def active?
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def inactive_message
12
+ :inactive
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,22 +1,8 @@
1
1
  require 'devise/strategies/authenticatable'
2
- require 'devise/serializers/authenticatable'
2
+ require 'devise/models/session_serializer'
3
3
 
4
4
  module Devise
5
5
  module Models
6
- module SessionSerializer
7
- # Hook to serialize user into session. Overwrite if you want.
8
- def serialize_into_session(record)
9
- [record.class, record.id]
10
- end
11
-
12
- # Hook to serialize user from session. Overwrite if you want.
13
- def serialize_from_session(keys)
14
- klass, id = keys
15
- raise "#{self} cannot serialize from #{klass} session since it's not its ancestors" unless klass <= self
16
- klass.find(:first, :conditions => { :id => id })
17
- end
18
- end
19
-
20
6
  # Authenticable Module, responsible for encrypting password and validating
21
7
  # authenticity of a user while signing in.
22
8
  #
@@ -67,13 +53,18 @@ module Devise
67
53
  password_digest(incoming_password) == encrypted_password
68
54
  end
69
55
 
56
+ # Checks if a resource is valid upon authentication.
57
+ def valid_for_authentication?(attributes)
58
+ valid_password?(attributes[:password])
59
+ end
60
+
70
61
  # Update record attributes when :old_password matches, otherwise returns
71
62
  # error on :old_password.
72
63
  def update_with_password(params={})
73
64
  if valid_password?(params[:old_password])
74
65
  update_attributes(params)
75
66
  else
76
- errors.add(:old_password, :invalid)
67
+ self.class.add_error_on(self, :old_password, :invalid, false)
77
68
  false
78
69
  end
79
70
  end
@@ -93,7 +84,13 @@ module Devise
93
84
  return unless authentication_keys.all? { |k| attributes[k].present? }
94
85
  conditions = attributes.slice(*authentication_keys)
95
86
  resource = find_for_authentication(conditions)
96
- valid_for_authentication(resource, attributes) if resource
87
+ if respond_to?(:valid_for_authentication)
88
+ ActiveSupport::Deprecation.warn "valid_for_authentication class method is deprecated. " <<
89
+ "Use valid_for_authentication? in the instance instead."
90
+ valid_for_authentication(resource, attributes)
91
+ elsif resource.try(:valid_for_authentication?, attributes)
92
+ resource
93
+ end
97
94
  end
98
95
 
99
96
  # Returns the class for the configured encryptor.
@@ -117,11 +114,6 @@ module Devise
117
114
  find(:first, :conditions => conditions)
118
115
  end
119
116
 
120
- # Contains the logic used in authentication. Overwritten by other devise modules.
121
- def valid_for_authentication(resource, attributes)
122
- resource if resource.valid_password?(attributes[:password])
123
- end
124
-
125
117
  Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
126
118
  end
127
119
  end
@@ -1,4 +1,4 @@
1
- require 'devise/hooks/confirmable'
1
+ require 'devise/models/activatable'
2
2
 
3
3
  module Devise
4
4
  module Models
@@ -29,6 +29,7 @@ module Devise
29
29
  # User.find(1).send_confirmation_instructions # manually send instructions
30
30
  # User.find(1).resend_confirmation! # generates a new token and resent it
31
31
  module Confirmable
32
+ include Devise::Models::Activatable
32
33
 
33
34
  def self.included(base)
34
35
  base.class_eval do
@@ -70,14 +71,19 @@ module Devise
70
71
  end
71
72
  end
72
73
 
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.
74
+ # Overwrites active? from Devise::Models::Activatable for confirmation
75
+ # by verifying whether an user is active to sign in or not. If the user
76
+ # is already confirmed, it should never be blocked. Otherwise we need to
77
+ # calculate if the confirm time has not expired for this user.
77
78
  def active?
78
79
  confirmed? || confirmation_period_valid?
79
80
  end
80
81
 
82
+ # The message to be shown if the account is inactive.
83
+ def inactive_message
84
+ :unconfirmed
85
+ end
86
+
81
87
  # If you don't want confirmation to be sent on create, neither a code
82
88
  # to be generated, call skip_confirmation!
83
89
  def skip_confirmation!
@@ -121,7 +127,7 @@ module Devise
121
127
  unless confirmed?
122
128
  yield
123
129
  else
124
- errors.add(:email, :already_confirmed, :default => 'already confirmed')
130
+ self.class.add_error_on(self, :email, :already_confirmed)
125
131
  false
126
132
  end
127
133
  end
@@ -0,0 +1,21 @@
1
+ require 'devise/serializers/cookie'
2
+
3
+ module Devise
4
+ module Models
5
+ module CookieSerializer
6
+ # Create the cookie key using the record id and remember_token
7
+ def serialize_into_cookie(record)
8
+ "#{record.id}::#{record.remember_token}"
9
+ end
10
+
11
+ # Recreate the user based on the stored cookie
12
+ def serialize_from_cookie(cookie)
13
+ record_id, record_token = cookie.split('::')
14
+ record = find(:first, :conditions => { :id => record_id }) if record_id
15
+ record if record.try(:valid_remember_token?, record_token)
16
+ end
17
+
18
+ Devise::Models.config(self, :remember_for)
19
+ end
20
+ end
21
+ end
@@ -1,9 +1,7 @@
1
- require 'digest/sha1'
2
- require 'devise/serializers/rememberable'
1
+ require 'devise/models/cookie_serializer'
3
2
 
4
3
  module Devise
5
4
  module Models
6
-
7
5
  # Rememberable manages generating and clearing token for remember the user
8
6
  # from a saved cookie. Rememberable also has utility methods for dealing
9
7
  # with serializing the user into the cookie and back from the cookie, trying
@@ -34,7 +32,7 @@ module Devise
34
32
 
35
33
  def self.included(base)
36
34
  base.class_eval do
37
- extend ClassMethods
35
+ extend CookieSerializer
38
36
 
39
37
  # Remember me option available in after_authentication hook.
40
38
  attr_accessor :remember_me
@@ -72,22 +70,6 @@ module Devise
72
70
  def remember_expires_at
73
71
  remember_created_at + self.class.remember_for
74
72
  end
75
-
76
- module ClassMethods
77
- # Create the cookie key using the record id and remember_token
78
- def serialize_into_cookie(rememberable)
79
- "#{rememberable.id}::#{rememberable.remember_token}"
80
- end
81
-
82
- # Recreate the user based on the stored cookie
83
- def serialize_from_cookie(cookie)
84
- rememberable_id, remember_token = cookie.split('::')
85
- rememberable = find(:first, :conditions => { :id => rememberable_id }) if rememberable_id
86
- rememberable if rememberable.try(:valid_remember_token?, remember_token)
87
- end
88
-
89
- Devise::Models.config(self, :remember_for)
90
- end
91
73
  end
92
74
  end
93
75
  end
@@ -0,0 +1,19 @@
1
+ require 'devise/serializers/session'
2
+
3
+ module Devise
4
+ module Models
5
+ module SessionSerializer
6
+ # Hook to serialize user into session. Overwrite if you want.
7
+ def serialize_into_session(record)
8
+ [record.class, record.id]
9
+ end
10
+
11
+ # Hook to serialize user from session. Overwrite if you want.
12
+ def serialize_from_session(keys)
13
+ klass, id = keys
14
+ raise "#{self} cannot serialize from #{klass} session since it's not one of its ancestors" unless klass <= self
15
+ klass.find(:first, :conditions => { :id => id })
16
+ end
17
+ end
18
+ end
19
+ end
@@ -6,10 +6,6 @@ module Devise
6
6
  # Automatically validate if the email is present, unique and it's format is
7
7
  # valid. Also tests presence of password, confirmation and length
8
8
  module Validatable
9
-
10
- # Email regex used to validate email formats. Retrieved from authlogic.
11
- EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
12
-
13
9
  # All validations used by this module.
14
10
  VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
15
11
  :validates_confirmation_of, :validates_length_of ].freeze