devise 1.1.rc2 → 1.1.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 (77) hide show
  1. data/CHANGELOG.rdoc +22 -2
  2. data/Gemfile +7 -13
  3. data/Gemfile.lock +118 -0
  4. data/README.rdoc +15 -13
  5. data/app/controllers/devise/unlocks_controller.rb +0 -7
  6. data/app/mailers/devise/mailer.rb +7 -4
  7. data/app/views/devise/confirmations/new.html.erb +1 -1
  8. data/app/views/devise/passwords/new.html.erb +1 -1
  9. data/app/views/devise/unlocks/new.html.erb +1 -1
  10. data/lib/devise.rb +27 -9
  11. data/lib/devise/controllers/helpers.rb +19 -5
  12. data/lib/devise/controllers/internal_helpers.rb +2 -8
  13. data/lib/devise/encryptors/base.rb +1 -1
  14. data/lib/devise/encryptors/bcrypt.rb +2 -2
  15. data/lib/devise/failure_app.rb +6 -2
  16. data/lib/devise/hooks/rememberable.rb +9 -1
  17. data/lib/devise/mapping.rb +15 -50
  18. data/lib/devise/models/authenticatable.rb +8 -0
  19. data/lib/devise/models/confirmable.rb +10 -6
  20. data/lib/devise/models/database_authenticatable.rb +9 -1
  21. data/lib/devise/models/recoverable.rb +6 -1
  22. data/lib/devise/models/rememberable.rb +36 -7
  23. data/lib/devise/models/token_authenticatable.rb +5 -5
  24. data/lib/devise/models/validatable.rb +1 -1
  25. data/lib/devise/path_checker.rb +7 -2
  26. data/lib/devise/rails.rb +6 -1
  27. data/lib/devise/rails/routes.rb +137 -50
  28. data/lib/devise/rails/warden_compat.rb +16 -2
  29. data/lib/devise/strategies/authenticatable.rb +12 -0
  30. data/lib/devise/strategies/base.rb +0 -18
  31. data/lib/devise/strategies/rememberable.rb +9 -1
  32. data/lib/devise/test_helpers.rb +2 -0
  33. data/lib/devise/version.rb +1 -1
  34. data/lib/generators/active_record/devise_generator.rb +28 -0
  35. data/lib/generators/{devise/devise → active_record}/templates/migration.rb +4 -0
  36. data/lib/generators/devise/devise_generator.rb +17 -0
  37. data/lib/generators/devise/{install/install_generator.rb → install_generator.rb} +1 -1
  38. data/lib/generators/devise/orm_helpers.rb +23 -0
  39. data/lib/generators/devise/{install/templates → templates}/README +0 -0
  40. data/lib/generators/devise/{install/templates → templates}/devise.rb +20 -13
  41. data/lib/generators/devise/{views/views_generator.rb → views_generator.rb} +2 -2
  42. data/lib/generators/mongoid/devise_generator.rb +17 -0
  43. data/test/controllers/helpers_test.rb +9 -0
  44. data/test/controllers/internal_helpers_test.rb +7 -16
  45. data/test/controllers/url_helpers_test.rb +11 -0
  46. data/test/encryptors_test.rb +1 -1
  47. data/test/failure_app_test.rb +18 -5
  48. data/test/integration/authenticatable_test.rb +76 -11
  49. data/test/integration/confirmable_test.rb +16 -9
  50. data/test/integration/lockable_test.rb +11 -13
  51. data/test/integration/registerable_test.rb +4 -4
  52. data/test/integration/rememberable_test.rb +54 -1
  53. data/test/mapping_test.rb +10 -45
  54. data/test/models/confirmable_test.rb +1 -1
  55. data/test/models/rememberable_test.rb +108 -0
  56. data/test/models/validatable_test.rb +2 -4
  57. data/test/models_test.rb +4 -4
  58. data/test/rails_app/app/active_record/admin.rb +1 -1
  59. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  60. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  61. data/test/rails_app/app/controllers/users_controller.rb +5 -1
  62. data/test/rails_app/app/mongoid/admin.rb +1 -1
  63. data/test/rails_app/config/application.rb +2 -2
  64. data/test/rails_app/config/environments/test.rb +2 -0
  65. data/test/rails_app/config/initializers/devise.rb +95 -34
  66. data/test/rails_app/config/routes.rb +32 -14
  67. data/test/routes_test.rb +34 -2
  68. data/test/support/integration.rb +22 -6
  69. data/test/test_helpers_test.rb +16 -2
  70. metadata +24 -27
  71. data/lib/devise/orm/data_mapper.rb +0 -97
  72. data/lib/generators/devise/devise/devise_generator.rb +0 -86
  73. data/lib/generators/devise_generator.rb +0 -2
  74. data/test/orm/data_mapper.rb +0 -10
  75. data/test/rails_app/app/data_mapper/admin.rb +0 -12
  76. data/test/rails_app/app/data_mapper/shim.rb +0 -2
  77. data/test/rails_app/app/data_mapper/user.rb +0 -23
@@ -12,7 +12,7 @@ module Devise
12
12
  raise NotImplemented
13
13
  end
14
14
 
15
- def self.salt
15
+ def self.salt(stretches)
16
16
  Devise.friendly_token
17
17
  end
18
18
  end
@@ -11,8 +11,8 @@ module Devise
11
11
  ::BCrypt::Engine.hash_secret([password, pepper].join, salt, stretches)
12
12
  end
13
13
 
14
- def self.salt
15
- ::BCrypt::Engine.generate_salt
14
+ def self.salt(stretches)
15
+ ::BCrypt::Engine.generate_salt(stretches)
16
16
  end
17
17
  end
18
18
  end
@@ -47,7 +47,7 @@ module Devise
47
47
  def redirect
48
48
  store_location!
49
49
  flash[:alert] = i18n_message unless flash[:notice]
50
- redirect_to send(:"new_#{scope}_session_path")
50
+ redirect_to redirect_url
51
51
  end
52
52
 
53
53
  protected
@@ -63,8 +63,12 @@ module Devise
63
63
  end
64
64
  end
65
65
 
66
+ def redirect_url
67
+ send(:"new_#{scope}_session_path")
68
+ end
69
+
66
70
  def http_auth?
67
- !Devise.navigational_formats.include?(request.format.to_sym) || request.xhr?
71
+ !Devise.navigational_formats.include?(request.format.to_sym) || (request.xhr? && Devise.http_authenticatable_on_xhr)
68
72
  end
69
73
 
70
74
  def http_auth_body
@@ -9,7 +9,7 @@ module Devise
9
9
  super
10
10
 
11
11
  if succeeded? && resource.respond_to?(:remember_me!) && remember_me?
12
- resource.remember_me!
12
+ resource.remember_me!(extend_remember_period?)
13
13
 
14
14
  configuration = {
15
15
  :value => resource.class.serialize_into_cookie(resource),
@@ -24,6 +24,14 @@ module Devise
24
24
 
25
25
  protected
26
26
 
27
+ def succeeded?
28
+ @result == :success
29
+ end
30
+
31
+ def extend_remember_period?
32
+ false
33
+ end
34
+
27
35
  def remember_me?
28
36
  valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
29
37
  end
@@ -22,19 +22,9 @@ module Devise
22
22
  # # is the modules included in the class
23
23
  #
24
24
  class Mapping #:nodoc:
25
- attr_reader :singular, :plural, :path, :controllers, :path_names, :path_prefix, :class_name
25
+ attr_reader :singular, :plural, :path, :controllers, :path_names, :class_name
26
26
  alias :name :singular
27
27
 
28
- # Loop through all mappings looking for a map that matches with the requested
29
- # path (ie /users/sign_in). If a path prefix is given, it's taken into account.
30
- def self.find_by_path(path)
31
- Devise.mappings.each_value do |mapping|
32
- route = path.split("/")[mapping.segment_position]
33
- return mapping if route && mapping.path == route.to_sym
34
- end
35
- nil
36
- end
37
-
38
28
  # Receives an object and find a scope for it. If a scope cannot be found,
39
29
  # raises an error. If a symbol is given, it's considered to be the scope.
40
30
  def self.find_scope!(duck)
@@ -51,31 +41,22 @@ module Devise
51
41
  end
52
42
 
53
43
  def initialize(name, options) #:nodoc:
54
- if as = options.delete(:as)
55
- ActiveSupport::Deprecation.warn ":as is deprecated, please use :path instead."
56
- options[:path] ||= as
57
- end
44
+ @plural = (options[:as] ? "#{options[:as]}_#{name}" : name).to_sym
45
+ @singular = (options[:singular] || @plural.to_s.singularize).to_sym
58
46
 
59
- if scope = options.delete(:scope)
60
- ActiveSupport::Deprecation.warn ":scope is deprecated, please use :singular instead."
61
- options[:singular] ||= scope
62
- end
63
-
64
- @plural = name.to_sym
65
- @path = (options.delete(:path) || name).to_sym
66
- @singular = (options.delete(:singular) || name.to_s.singularize).to_sym
67
-
68
- @class_name = (options.delete(:class_name) || name.to_s.classify).to_s
47
+ @class_name = (options[:class_name] || name.to_s.classify).to_s
69
48
  @ref = ActiveSupport::Dependencies.ref(@class_name)
70
49
 
71
- @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
50
+ @path = (options[:path] || name).to_s
51
+ @path_prefix = options[:path_prefix]
72
52
 
73
- @controllers = Hash.new { |h,k| h[k] = "devise/#{k}" }
74
- @controllers.merge!(options.delete(:controllers) || {})
53
+ mod = options[:module] || "devise"
54
+ @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
55
+ @controllers.merge!(options[:controllers] || {})
75
56
 
76
- @path_names = Hash.new { |h,k| h[k] = k.to_s }
57
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
77
58
  @path_names.merge!(:registration => "")
78
- @path_names.merge!(options.delete(:path_names) || {})
59
+ @path_names.merge!(options[:path_names] || {})
79
60
  end
80
61
 
81
62
  # Return modules for the mapping.
@@ -96,30 +77,14 @@ module Devise
96
77
  @routes ||= ROUTES.values_at(*self.modules).compact.uniq
97
78
  end
98
79
 
99
- # Keep a list of allowed controllers for this mapping. It's useful to ensure
100
- # that an Admin cannot access the registrations controller unless it has
101
- # :registerable in the model.
102
- def allowed_controllers
103
- @allowed_controllers ||= begin
104
- canonical = CONTROLLERS.values_at(*self.modules).compact
105
- @controllers.values_at(*canonical)
106
- end
107
- end
108
-
109
- # Return in which position in the path prefix devise should find the as mapping.
110
- def segment_position
111
- self.path_prefix.count("/")
112
- end
113
-
114
- # Returns the raw path using path_prefix and as.
115
- def full_path
116
- path_prefix + path.to_s
117
- end
118
-
119
80
  def authenticatable?
120
81
  @authenticatable ||= self.modules.any? { |m| m.to_s =~ /authenticatable/ }
121
82
  end
122
83
 
84
+ def fullpath
85
+ "#{@path_prefix}/#{@path}".squeeze("/")
86
+ end
87
+
123
88
  # Create magic predicates for verifying what module is activated by this map.
124
89
  # Example:
125
90
  #
@@ -112,6 +112,14 @@ module Devise
112
112
 
113
113
  record
114
114
  end
115
+
116
+ # Generate a token by looping and ensuring does not already exist.
117
+ def generate_token(column)
118
+ loop do
119
+ token = Devise.friendly_token
120
+ break token unless find(:first, :conditions => { column => token })
121
+ end
122
+ end
115
123
  end
116
124
  end
117
125
  end
@@ -45,12 +45,12 @@ module Devise
45
45
 
46
46
  # Verifies whether a user is confirmed or not
47
47
  def confirmed?
48
- persisted? && !confirmed_at.nil?
48
+ !!confirmed_at
49
49
  end
50
50
 
51
51
  # Send confirmation instructions by email
52
52
  def send_confirmation_instructions
53
- generate_confirmation_token if self.confirmation_token.nil?
53
+ generate_confirmation_token! if self.confirmation_token.nil?
54
54
  ::Devise.mailer.confirmation_instructions(self).deliver
55
55
  end
56
56
 
@@ -75,15 +75,14 @@ module Devise
75
75
  # If you don't want confirmation to be sent on create, neither a code
76
76
  # to be generated, call skip_confirmation!
77
77
  def skip_confirmation!
78
- self.confirmed_at = Time.now
79
- @skip_confirmation = true
78
+ self.confirmed_at = Time.now
80
79
  end
81
80
 
82
81
  protected
83
82
 
84
83
  # Callback to overwrite if confirmation is required or not.
85
84
  def confirmation_required?
86
- !@skip_confirmation
85
+ !confirmed?
87
86
  end
88
87
 
89
88
  # Checks if the confirmation for the user is within the limit time.
@@ -128,6 +127,10 @@ module Devise
128
127
  self.confirmation_sent_at = Time.now.utc
129
128
  end
130
129
 
130
+ def generate_confirmation_token!
131
+ generate_confirmation_token && save(:validate => false)
132
+ end
133
+
131
134
  module ClassMethods
132
135
  # Attempt to find a user by it's email. If a record is found, send new
133
136
  # confirmation instructions to it. If not user is found, returns a new user
@@ -149,8 +152,9 @@ module Devise
149
152
  confirmable
150
153
  end
151
154
 
155
+ # Generate a token checking if one does not already exist in the database.
152
156
  def confirmation_token
153
- Devise.friendly_token
157
+ generate_token(:confirmation_token)
154
158
  end
155
159
 
156
160
  Devise::Models.config(self, :confirm_within)
@@ -37,7 +37,7 @@ module Devise
37
37
  @password = new_password
38
38
 
39
39
  if @password.present?
40
- self.password_salt = self.class.encryptor_class.salt
40
+ self.password_salt = self.class.password_salt
41
41
  self.encrypted_password = password_digest(@password)
42
42
  end
43
43
  end
@@ -93,6 +93,14 @@ module Devise
93
93
  @encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
94
94
  end
95
95
 
96
+ def password_salt
97
+ self.encryptor_class.salt(self.stretches)
98
+ end
99
+
100
+ # We assume this method already gets the sanitized values from the
101
+ # DatabaseAuthenticatable strategy. If you are using this method on
102
+ # your own, be sure to sanitize the conditions hash to only include
103
+ # the proper fields.
96
104
  def find_for_database_authentication(conditions)
97
105
  find_for_authentication(conditions)
98
106
  end
@@ -35,7 +35,7 @@ module Devise
35
35
 
36
36
  # Generates a new random token for reset password
37
37
  def generate_reset_password_token
38
- self.reset_password_token = Devise.friendly_token
38
+ self.reset_password_token = self.class.reset_password_token
39
39
  end
40
40
 
41
41
  # Resets the reset password token with and save the record without
@@ -60,6 +60,11 @@ module Devise
60
60
  recoverable
61
61
  end
62
62
 
63
+ # Generate a token checking if one does not already exist in the database.
64
+ def reset_password_token
65
+ generate_token(:reset_password_token)
66
+ end
67
+
63
68
  # Attempt to find a user by it's reset_password_token to reset it's
64
69
  # password. If a user is found, reset it's password and automatically
65
70
  # try saving the record. If not user is found, returns a new user
@@ -18,7 +18,15 @@ module Devise
18
18
  # blocked and will have to enter his credentials again.
19
19
  # This configuration is also used to calculate the expires
20
20
  # time for the cookie created to remember the user.
21
- # By default remember_for is 2.weeks.
21
+ # 2.weeks by default.
22
+ #
23
+ # remember_across_browsers: if true, a valid remember token can be
24
+ # re-used between multiple browsers.
25
+ # True by default.
26
+ #
27
+ # extend_remember_period: if true, extends the user's remember period
28
+ # when remembered via cookie.
29
+ # False by default.
22
30
  #
23
31
  # Examples:
24
32
  #
@@ -38,10 +46,11 @@ module Devise
38
46
  attr_accessor :remember_me
39
47
  end
40
48
 
41
- # Generate a new remember token and save the record without validations.
42
- def remember_me!
43
- self.remember_token = Devise.friendly_token
44
- self.remember_created_at = Time.now.utc
49
+ # Generate a new remember token and save the record without validations
50
+ # unless remember_across_browsers is true and the user already has a valid token.
51
+ def remember_me!(extend_period=false)
52
+ self.remember_token = self.class.remember_token if generate_remember_token?
53
+ self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
45
54
  save(:validate => false)
46
55
  end
47
56
 
@@ -57,7 +66,7 @@ module Devise
57
66
 
58
67
  # Remember token should be expired if expiration time not overpass now.
59
68
  def remember_expired?
60
- remember_expires_at <= Time.now.utc
69
+ remember_created_at && (remember_expires_at <= Time.now.utc)
61
70
  end
62
71
 
63
72
  # Remember token expires at created time + remember_for configuration
@@ -73,6 +82,20 @@ module Devise
73
82
  self.class.cookie_domain != false
74
83
  end
75
84
 
85
+ protected
86
+
87
+ # Generate a token unless remember_across_browsers is true and there is
88
+ # an existing remember_token or the existing remember_token has expried.
89
+ def generate_remember_token? #:nodoc:
90
+ !(self.class.remember_across_browsers && remember_token) || remember_expired?
91
+ end
92
+
93
+ # Generate a timestamp if extend_remember_period is true, if no remember_token
94
+ # exists, or if an existing remember token has expired.
95
+ def generate_remember_timestamp?(extend_period) #:nodoc:
96
+ extend_period || remember_created_at.nil? || remember_expired?
97
+ end
98
+
76
99
  module ClassMethods
77
100
  # Create the cookie key using the record id and remember_token
78
101
  def serialize_into_cookie(record)
@@ -86,7 +109,13 @@ module Devise
86
109
  record if record && !record.remember_expired?
87
110
  end
88
111
 
89
- Devise::Models.config(self, :remember_for, :cookie_domain)
112
+ # Generate a token checking if one does not already exist in the database.
113
+ def remember_token
114
+ generate_token(:remember_token)
115
+ end
116
+
117
+ Devise::Models.config(self, :remember_for, :remember_across_browsers,
118
+ :extend_remember_period, :cookie_domain)
90
119
  end
91
120
  end
92
121
  end
@@ -44,16 +44,16 @@ module Devise
44
44
  end
45
45
 
46
46
  module ClassMethods
47
- ::Devise::Models.config(self, :token_authentication_key)
48
-
49
47
  def find_for_token_authentication(conditions)
50
- conditions[:authentication_token] ||= conditions.delete(token_authentication_key)
51
- find_for_authentication(conditions)
48
+ find_for_authentication(:authentication_token => conditions[token_authentication_key])
52
49
  end
53
50
 
51
+ # Generate a token checking if one does not already exist in the database.
54
52
  def authentication_token
55
- ::Devise.friendly_token
53
+ generate_token(:authentication_token)
56
54
  end
55
+
56
+ ::Devise::Models.config(self, :token_authentication_key)
57
57
  end
58
58
  end
59
59
  end
@@ -16,7 +16,7 @@ module Devise
16
16
 
17
17
  base.class_eval do
18
18
  validates_presence_of :email
19
- validates_uniqueness_of :email, :scope => authentication_keys[1..-1], :allow_blank => true
19
+ validates_uniqueness_of :email, :scope => authentication_keys[1..-1], :case_sensitive => false, :allow_blank => true
20
20
  validates_format_of :email, :with => email_regexp, :allow_blank => true
21
21
 
22
22
  with_options :if => :password_required? do |v|
@@ -2,12 +2,17 @@ module Devise
2
2
  class PathChecker
3
3
  include Rails.application.routes.url_helpers
4
4
 
5
+ def self.default_url_options(*args)
6
+ ApplicationController.default_url_options(*args)
7
+ end
8
+
5
9
  def initialize(env, scope)
6
- @env, @scope = env, scope
10
+ @current_path = "/#{env["SCRIPT_NAME"]}/#{env["PATH_INFO"]}".squeeze("/")
11
+ @scope = scope
7
12
  end
8
13
 
9
14
  def signing_out?
10
- @env["PATH_INFO"] == send("destroy_#{@scope}_session_path")
15
+ @current_path == send("destroy_#{@scope}_session_path")
11
16
  end
12
17
  end
13
18
  end
@@ -20,11 +20,16 @@ module Devise
20
20
  Devise.encryptor ||= begin
21
21
  warn "[WARNING] config.encryptor is not set in your config/initializers/devise.rb. " \
22
22
  "Devise will then set it to :bcrypt. If you were using the previous default " \
23
- "encryptor, please add config.encryptor = :sha1 to your configuration file."
23
+ "encryptor, please add config.encryptor = :sha1 to your configuration file." if Devise.mailer_sender
24
24
  :bcrypt
25
25
  end
26
26
  end
27
27
 
28
+ initializer "devise.add_filters" do |app|
29
+ app.config.filter_parameters += [:password, :password_confirmation]
30
+ app.config.filter_parameters.uniq
31
+ end
32
+
28
33
  unless Rails.env.production?
29
34
  config.after_initialize do
30
35
  actions = [:confirmation_instructions, :reset_password_instructions, :unlock_instructions]
@@ -15,9 +15,10 @@ module ActionDispatch::Routing
15
15
  # generate all needed routes for devise, based on what modules you have
16
16
  # defined in your model.
17
17
  #
18
- # Examples: Let's say you have an User model configured to use
19
- # authenticatable, confirmable and recoverable modules. After creating this
20
- # inside your routes:
18
+ # ==== Examples
19
+ #
20
+ # Let's say you have an User model configured to use authenticatable,
21
+ # confirmable and recoverable modules. After creating this inside your routes:
21
22
  #
22
23
  # devise_for :users
23
24
  #
@@ -25,20 +26,22 @@ module ActionDispatch::Routing
25
26
  # needed routes:
26
27
  #
27
28
  # # Session routes for Authenticatable (default)
28
- # new_user_session GET /users/sign_in {:controller=>"sessions", :action=>"new"}
29
- # user_session POST /users/sign_in {:controller=>"sessions", :action=>"create"}
30
- # destroy_user_session GET /users/sign_out {:controller=>"sessions", :action=>"destroy"}
29
+ # new_user_session GET /users/sign_in {:controller=>"devise/sessions", :action=>"new"}
30
+ # user_session POST /users/sign_in {:controller=>"devise/sessions", :action=>"create"}
31
+ # destroy_user_session GET /users/sign_out {:controller=>"devise/sessions", :action=>"destroy"}
31
32
  #
32
33
  # # Password routes for Recoverable, if User model has :recoverable configured
33
- # new_user_password GET /users/password/new(.:format) {:controller=>"passwords", :action=>"new"}
34
- # edit_user_password GET /users/password/edit(.:format) {:controller=>"passwords", :action=>"edit"}
35
- # user_password PUT /users/password(.:format) {:controller=>"passwords", :action=>"update"}
36
- # POST /users/password(.:format) {:controller=>"passwords", :action=>"create"}
34
+ # new_user_password GET /users/password/new(.:format) {:controller=>"devise/passwords", :action=>"new"}
35
+ # edit_user_password GET /users/password/edit(.:format) {:controller=>"devise/passwords", :action=>"edit"}
36
+ # user_password PUT /users/password(.:format) {:controller=>"devise/passwords", :action=>"update"}
37
+ # POST /users/password(.:format) {:controller=>"devise/passwords", :action=>"create"}
37
38
  #
38
39
  # # Confirmation routes for Confirmable, if User model has :confirmable configured
39
- # new_user_confirmation GET /users/confirmation/new(.:format) {:controller=>"confirmations", :action=>"new"}
40
- # user_confirmation GET /users/confirmation(.:format) {:controller=>"confirmations", :action=>"show"}
41
- # POST /users/confirmation(.:format) {:controller=>"confirmations", :action=>"create"}
40
+ # new_user_confirmation GET /users/confirmation/new(.:format) {:controller=>"devise/confirmations", :action=>"new"}
41
+ # user_confirmation GET /users/confirmation(.:format) {:controller=>"devise/confirmations", :action=>"show"}
42
+ # POST /users/confirmation(.:format) {:controller=>"devise/confirmations", :action=>"create"}
43
+ #
44
+ # ==== Options
42
45
  #
43
46
  # You can configure your routes with some options:
44
47
  #
@@ -62,37 +65,84 @@ module ActionDispatch::Routing
62
65
  #
63
66
  # devise_for :users, :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification' }
64
67
  #
65
- # * :path_prefix => the path prefix to be used in all routes.
68
+ # * :controllers => the controller which should be used. All routes by default points to Devise controllers.
69
+ # However, if you want them to point to custom controller, you should do:
66
70
  #
67
- # devise_for :users, :path_prefix => "/:locale"
71
+ # devise_for :users, :controllers => { :sessions => "users/sessions" }
68
72
  #
69
- # If you are using a dynamic prefix, like :locale above, you need to configure default_url_options in your ApplicationController
70
- # class level, so Devise can pick it:
73
+ # * :module => the namespace to find controlers. By default, devise will access devise/sessions,
74
+ # devise/registrations and so on. If you want to namespace all at once, use module:
71
75
  #
72
- # class ApplicationController < ActionController::Base
73
- # def self.default_url_options
74
- # { :locale => I18n.locale }
75
- # end
76
- # end
76
+ # devise_for :users, :module => "users"
77
77
  #
78
- # * :controllers => the controller which should be used. All routes by default points to Devise controllers.
79
- # However, if you want them to point to custom controller, you should do:
78
+ # Notice that whenever you use namespace in the router DSL, it automatically sets the module.
79
+ # So the following setup:
80
80
  #
81
- # devise_for :users, :controllers => { :sessions => "users/sessions" }
81
+ # namespace :publisher
82
+ # devise_for :account
83
+ # end
84
+ #
85
+ # Will use publisher/sessions controller instead of devise/sessions controller. You can revert
86
+ # this by providing the :module option to devise_for.
82
87
  #
83
88
  # * :skip => tell which controller you want to skip routes from being created:
84
89
  #
85
90
  # devise_for :users, :skip => :sessions
86
91
  #
92
+ # ==== Scoping
93
+ #
94
+ # Following Rails 3 routes DSL, you can nest devise_for calls inside a scope:
95
+ #
96
+ # scope "/my" do
97
+ # devise_for :users
98
+ # end
99
+ #
100
+ # However, since Devise uses the request path to retrieve the current user, it has one caveats.
101
+ # If you are using a dynamic segment, as below:
102
+ #
103
+ # scope ":locale" do
104
+ # devise_for :users
105
+ # end
106
+ #
107
+ # You are required to configure default_url_options in your ApplicationController class level, so
108
+ # Devise can pick it:
109
+ #
110
+ # class ApplicationController < ActionController::Base
111
+ # def self.default_url_options
112
+ # { :locale => I18n.locale }
113
+ # end
114
+ # end
115
+ #
87
116
  def devise_for(*resources)
88
117
  options = resources.extract_options!
118
+
119
+ if as = options.delete(:as)
120
+ ActiveSupport::Deprecation.warn ":as is deprecated, please use :path instead."
121
+ options[:path] ||= as
122
+ end
123
+
124
+ if scope = options.delete(:scope)
125
+ ActiveSupport::Deprecation.warn ":scope is deprecated, please use :singular instead."
126
+ options[:singular] ||= scope
127
+ end
128
+
129
+ options[:as] ||= @scope[:as] if @scope[:as].present?
130
+ options[:module] ||= @scope[:module] if @scope[:module].present?
131
+ options[:path_prefix] ||= @scope[:path] if @scope[:path].present?
132
+ options[:path_names] = (@scope[:path_names] || {}).merge(options[:path_names] || {})
133
+
89
134
  resources.map!(&:to_sym)
90
135
 
91
136
  resources.each do |resource|
92
- mapping = Devise.add_model(resource, options)
137
+ mapping = Devise.add_mapping(resource, options)
93
138
 
94
139
  begin
95
140
  raise_no_devise_method_error!(mapping.class_name) unless mapping.to.respond_to?(:devise)
141
+ rescue NameError => e
142
+ raise unless mapping.class_name == resource.to_s.classify
143
+ warn "[WARNING] You provided devise_for #{resource.inspect} but there is " <<
144
+ "no model #{mapping.class_name} defined in your application"
145
+ next
96
146
  rescue NoMethodError => e
97
147
  raise unless e.message.include?("undefined method `devise'")
98
148
  raise_no_devise_method_error!(mapping.class_name)
@@ -101,12 +151,21 @@ module ActionDispatch::Routing
101
151
  routes = mapping.routes
102
152
  routes -= Array(options.delete(:skip)).map { |s| s.to_s.singularize.to_sym }
103
153
 
104
- routes.each do |mod|
105
- send(:"devise_#{mod}", mapping, mapping.controllers)
154
+ devise_scope mapping.name do
155
+ yield if block_given?
156
+ with_devise_exclusive_scope mapping.fullpath, mapping.name do
157
+ routes.each { |mod| send(:"devise_#{mod}", mapping, mapping.controllers) }
158
+ end
106
159
  end
107
160
  end
108
161
  end
109
162
 
163
+ # Allow you to add authentication request from the router:
164
+ #
165
+ # authenticate(:user) do
166
+ # resources :post
167
+ # end
168
+ #
110
169
  def authenticate(scope)
111
170
  constraint = lambda do |request|
112
171
  request.env["warden"].authenticate!(:scope => scope)
@@ -117,42 +176,70 @@ module ActionDispatch::Routing
117
176
  end
118
177
  end
119
178
 
179
+ # Sets the devise scope to be used in the controller. If you have custom routes,
180
+ # you are required to call this method (also aliased as :as) in order to specify
181
+ # to which controller it is targetted.
182
+ #
183
+ # as :user do
184
+ # get "sign_in", :to => "devise/sessions#new"
185
+ # end
186
+ #
187
+ # Notice you cannot have two scopes mapping to the same URL. And remember, if
188
+ # you try to access a devise controller without specifying a scope, it will
189
+ # raise ActionNotFound error.
190
+ def devise_scope(scope)
191
+ constraint = lambda do |request|
192
+ request.env["devise.mapping"] = Devise.mappings[scope]
193
+ true
194
+ end
195
+
196
+ constraints(constraint) do
197
+ yield
198
+ end
199
+ end
200
+ alias :as :devise_scope
201
+
120
202
  protected
121
203
 
122
- def devise_session(mapping, controllers)
123
- scope mapping.full_path do
124
- get mapping.path_names[:sign_in], :to => "#{controllers[:sessions]}#new", :as => :"new_#{mapping.name}_session"
125
- post mapping.path_names[:sign_in], :to => "#{controllers[:sessions]}#create", :as => :"#{mapping.name}_session"
126
- get mapping.path_names[:sign_out], :to => "#{controllers[:sessions]}#destroy", :as => :"destroy_#{mapping.name}_session"
204
+ def devise_session(mapping, controllers) #:nodoc:
205
+ scope :controller => controllers[:sessions], :as => :session do
206
+ get :new, :path => mapping.path_names[:sign_in]
207
+ post :create, :path => mapping.path_names[:sign_in], :as => ""
208
+ get :destroy, :path => mapping.path_names[:sign_out]
127
209
  end
128
210
  end
129
211
 
130
- def devise_password(mapping, controllers)
131
- scope mapping.full_path, :name_prefix => mapping.name do
132
- resource :password, :only => [:new, :create, :edit, :update], :path => mapping.path_names[:password], :controller => controllers[:passwords]
133
- end
212
+ def devise_password(mapping, controllers) #:nodoc:
213
+ resource :password, :only => [:new, :create, :edit, :update],
214
+ :path => mapping.path_names[:password], :controller => controllers[:passwords]
134
215
  end
135
216
 
136
- def devise_confirmation(mapping, controllers)
137
- scope mapping.full_path, :name_prefix => mapping.name do
138
- resource :confirmation, :only => [:new, :create, :show], :path => mapping.path_names[:confirmation], :controller => controllers[:confirmations]
139
- end
217
+ def devise_confirmation(mapping, controllers) #:nodoc:
218
+ resource :confirmation, :only => [:new, :create, :show],
219
+ :path => mapping.path_names[:confirmation], :controller => controllers[:confirmations]
140
220
  end
141
221
 
142
- def devise_unlock(mapping, controllers)
143
- scope mapping.full_path, :name_prefix => mapping.name do
144
- resource :unlock, :only => [:new, :create, :show], :path => mapping.path_names[:unlock], :controller => controllers[:unlocks]
222
+ def devise_unlock(mapping, controllers) #:nodoc:
223
+ if mapping.to.unlock_strategy_enabled?(:email)
224
+ resource :unlock, :only => [:new, :create, :show],
225
+ :path => mapping.path_names[:unlock], :controller => controllers[:unlocks]
145
226
  end
146
227
  end
147
228
 
148
- def devise_registration(mapping, controllers)
149
- scope mapping.full_path[1..-1], :name_prefix => mapping.name do
150
- resource :registration, :only => [:new, :create, :edit, :update, :destroy], :path => mapping.path_names[:registration],
151
- :path_names => { :new => mapping.path_names[:sign_up] }, :controller => controllers[:registrations]
152
- end
229
+ def devise_registration(mapping, controllers) #:nodoc:
230
+ resource :registration, :only => [:new, :create, :edit, :update, :destroy], :path => mapping.path_names[:registration],
231
+ :path_names => { :new => mapping.path_names[:sign_up] }, :controller => controllers[:registrations]
232
+ end
233
+
234
+ def with_devise_exclusive_scope(new_path, new_as) #:nodoc:
235
+ old_as, old_path, old_module = @scope[:as], @scope[:path], @scope[:module]
236
+ @scope[:as], @scope[:path], @scope[:module] = new_as, new_path, nil
237
+ yield
238
+ ensure
239
+ @scope[:as], @scope[:path], @scope[:module] = old_as, old_path, old_module
153
240
  end
154
241
 
155
- def raise_no_devise_method_error!(klass)
242
+ def raise_no_devise_method_error!(klass) #:nodoc:
156
243
  raise "#{klass} does not respond to 'devise' method. This usually means you haven't " <<
157
244
  "loaded your ORM file or it's being loaded too late. To fix it, be sure to require 'devise/orm/YOUR_ORM' " <<
158
245
  "inside 'config/initializers/devise.rb' or before your application definition in 'config/application.rb'"