devise 1.1.3 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.rdoc +94 -0
  4. data/Gemfile +24 -13
  5. data/Gemfile.lock +119 -75
  6. data/MIT-LICENSE +1 -1
  7. data/README.rdoc +144 -101
  8. data/Rakefile +3 -24
  9. data/app/controllers/devise/confirmations_controller.rb +1 -1
  10. data/app/controllers/devise/omniauth_callbacks_controller.rb +26 -0
  11. data/app/controllers/devise/passwords_controller.rb +1 -1
  12. data/app/controllers/devise/registrations_controller.rb +60 -7
  13. data/app/controllers/devise/sessions_controller.rb +6 -4
  14. data/app/controllers/devise/unlocks_controller.rb +1 -1
  15. data/app/helpers/devise_helper.rb +10 -2
  16. data/app/mailers/devise/mailer.rb +27 -10
  17. data/app/views/devise/confirmations/new.html.erb +1 -1
  18. data/app/views/devise/passwords/edit.html.erb +2 -2
  19. data/app/views/devise/passwords/new.html.erb +1 -1
  20. data/app/views/devise/registrations/edit.html.erb +1 -1
  21. data/app/views/devise/registrations/new.html.erb +1 -1
  22. data/app/views/devise/sessions/new.html.erb +1 -1
  23. data/app/views/devise/shared/_links.erb +6 -0
  24. data/app/views/devise/unlocks/new.html.erb +1 -1
  25. data/config/locales/en.yml +11 -2
  26. data/devise.gemspec +25 -0
  27. data/lib/devise/controllers/helpers.rb +119 -116
  28. data/lib/devise/controllers/internal_helpers.rb +29 -8
  29. data/lib/devise/controllers/rememberable.rb +52 -0
  30. data/lib/devise/controllers/scoped_views.rb +4 -6
  31. data/lib/devise/controllers/url_helpers.rb +3 -5
  32. data/lib/devise/encryptors/authlogic_sha512.rb +1 -1
  33. data/lib/devise/encryptors/base.rb +1 -1
  34. data/lib/devise/encryptors/restful_authentication_sha1.rb +4 -4
  35. data/lib/devise/failure_app.rb +46 -10
  36. data/lib/devise/hooks/activatable.rb +2 -2
  37. data/lib/devise/hooks/forgetable.rb +1 -3
  38. data/lib/devise/hooks/rememberable.rb +5 -42
  39. data/lib/devise/hooks/timeoutable.rb +1 -1
  40. data/lib/devise/mapping.rb +18 -7
  41. data/lib/devise/models/authenticatable.rb +73 -31
  42. data/lib/devise/models/confirmable.rb +16 -20
  43. data/lib/devise/models/database_authenticatable.rb +27 -37
  44. data/lib/devise/models/encryptable.rb +72 -0
  45. data/lib/devise/models/lockable.rb +27 -21
  46. data/lib/devise/models/omniauthable.rb +23 -0
  47. data/lib/devise/models/recoverable.rb +13 -3
  48. data/lib/devise/models/registerable.rb +13 -0
  49. data/lib/devise/models/rememberable.rb +39 -34
  50. data/lib/devise/models/timeoutable.rb +20 -3
  51. data/lib/devise/models/token_authenticatable.rb +22 -10
  52. data/lib/devise/models/validatable.rb +16 -4
  53. data/lib/devise/models.rb +4 -16
  54. data/lib/devise/modules.rb +15 -8
  55. data/lib/devise/omniauth/config.rb +18 -0
  56. data/lib/devise/omniauth/url_helpers.rb +33 -0
  57. data/lib/devise/omniauth.rb +32 -0
  58. data/lib/devise/orm/active_record.rb +2 -0
  59. data/lib/devise/orm/mongoid.rb +4 -2
  60. data/lib/devise/rails/routes.rb +67 -20
  61. data/lib/devise/rails/warden_compat.rb +101 -15
  62. data/lib/devise/rails.rb +33 -44
  63. data/lib/devise/schema.rb +16 -16
  64. data/lib/devise/strategies/authenticatable.rb +48 -7
  65. data/lib/devise/strategies/database_authenticatable.rb +1 -1
  66. data/lib/devise/strategies/rememberable.rb +6 -5
  67. data/lib/devise/strategies/token_authenticatable.rb +6 -2
  68. data/lib/devise/test_helpers.rb +13 -3
  69. data/lib/devise/version.rb +1 -1
  70. data/lib/devise.rb +162 -50
  71. data/lib/generators/active_record/devise_generator.rb +2 -2
  72. data/lib/generators/active_record/templates/migration.rb +2 -0
  73. data/lib/generators/devise/devise_generator.rb +3 -1
  74. data/lib/generators/devise/orm_helpers.rb +1 -1
  75. data/lib/generators/devise/views_generator.rb +8 -45
  76. data/lib/generators/mongoid/devise_generator.rb +2 -2
  77. data/lib/generators/templates/devise.rb +82 -39
  78. data/test/controllers/helpers_test.rb +78 -72
  79. data/test/controllers/internal_helpers_test.rb +29 -8
  80. data/test/controllers/url_helpers_test.rb +2 -1
  81. data/test/devise_test.rb +10 -0
  82. data/test/failure_app_test.rb +86 -22
  83. data/test/generators/active_record_generator_test.rb +24 -0
  84. data/test/generators/devise_generator_test.rb +33 -0
  85. data/test/generators/install_generator_test.rb +13 -0
  86. data/test/generators/mongoid_generator_test.rb +22 -0
  87. data/test/generators/views_generator_test.rb +35 -0
  88. data/test/indifferent_hash.rb +33 -0
  89. data/test/integration/authenticatable_test.rb +165 -62
  90. data/test/integration/database_authenticatable_test.rb +22 -0
  91. data/test/integration/http_authenticatable_test.rb +12 -2
  92. data/test/integration/omniauthable_test.rb +138 -0
  93. data/test/integration/recoverable_test.rb +39 -20
  94. data/test/integration/registerable_test.rb +60 -4
  95. data/test/integration/rememberable_test.rb +72 -29
  96. data/test/integration/timeoutable_test.rb +10 -1
  97. data/test/integration/token_authenticatable_test.rb +55 -6
  98. data/test/mailers/confirmation_instructions_test.rb +4 -0
  99. data/test/mailers/reset_password_instructions_test.rb +4 -0
  100. data/test/mailers/unlock_instructions_test.rb +4 -0
  101. data/test/mapping_test.rb +37 -3
  102. data/test/models/confirmable_test.rb +32 -15
  103. data/test/models/database_authenticatable_test.rb +14 -71
  104. data/test/models/encryptable_test.rb +65 -0
  105. data/test/models/lockable_test.rb +48 -11
  106. data/test/models/recoverable_test.rb +28 -2
  107. data/test/models/rememberable_test.rb +186 -125
  108. data/test/models/token_authenticatable_test.rb +19 -1
  109. data/test/models_test.rb +12 -5
  110. data/test/omniauth/url_helpers_test.rb +54 -0
  111. data/test/orm/mongoid.rb +3 -2
  112. data/test/rails_app/Rakefile +10 -0
  113. data/test/rails_app/app/active_record/admin.rb +4 -1
  114. data/test/rails_app/app/active_record/user.rb +5 -4
  115. data/test/rails_app/app/controllers/{sessions_controller.rb → admins/sessions_controller.rb} +1 -1
  116. data/test/rails_app/app/controllers/application_controller.rb +0 -1
  117. data/test/rails_app/app/controllers/home_controller.rb +9 -0
  118. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
  119. data/test/rails_app/app/mongoid/admin.rb +4 -1
  120. data/test/rails_app/app/mongoid/shim.rb +16 -3
  121. data/test/rails_app/app/mongoid/user.rb +5 -5
  122. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  123. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  124. data/test/rails_app/app/views/home/index.html.erb +1 -0
  125. data/test/rails_app/app/views/home/private.html.erb +1 -0
  126. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  127. data/test/rails_app/app/views/users/index.html.erb +1 -0
  128. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  129. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  130. data/test/rails_app/config/application.rb +5 -0
  131. data/test/rails_app/config/boot.rb +2 -2
  132. data/test/rails_app/config/database.yml +18 -0
  133. data/test/rails_app/config/initializers/devise.rb +71 -31
  134. data/test/rails_app/config/routes.rb +14 -6
  135. data/test/rails_app/config.ru +4 -0
  136. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +21 -17
  137. data/test/rails_app/db/schema.rb +17 -51
  138. data/test/rails_app/lib/shared_admin.rb +9 -0
  139. data/test/rails_app/lib/shared_user.rb +23 -0
  140. data/test/rails_app/public/404.html +26 -0
  141. data/test/rails_app/public/422.html +26 -0
  142. data/test/rails_app/public/500.html +26 -0
  143. data/test/rails_app/public/favicon.ico +0 -0
  144. data/test/rails_app/script/rails +10 -0
  145. data/test/routes_test.rb +42 -9
  146. data/test/schema_test.rb +33 -0
  147. data/test/support/integration.rb +4 -4
  148. data/test/support/locale/en.yml +4 -0
  149. data/test/support/webrat/integrations/rails.rb +11 -19
  150. data/test/test_helper.rb +8 -2
  151. data/test/test_helpers_test.rb +64 -2
  152. metadata +106 -30
  153. data/TODO +0 -3
  154. data/lib/devise/encryptors/bcrypt.rb +0 -19
  155. data/lib/generators/devise_install_generator.rb +0 -4
  156. data/lib/generators/devise_views_generator.rb +0 -4
  157. data/test/support/test_silencer.rb +0 -5
@@ -9,7 +9,7 @@ module Devise
9
9
  attr_accessor :authentication_hash, :password
10
10
 
11
11
  def valid?
12
- valid_for_http_auth? || valid_for_params_auth?
12
+ valid_for_params_auth? || valid_for_http_auth?
13
13
  end
14
14
 
15
15
  private
@@ -19,13 +19,27 @@ module Devise
19
19
  result = resource && resource.valid_for_authentication?(&block)
20
20
 
21
21
  case result
22
- when Symbol, String
22
+ when String, Symbol
23
23
  fail!(result)
24
+ false
25
+ when TrueClass
26
+ decorate(resource)
27
+ true
24
28
  else
25
29
  result
26
30
  end
27
31
  end
28
32
 
33
+ # Get values from params and set in the resource.
34
+ def decorate(resource)
35
+ resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
36
+ end
37
+
38
+ # Should this resource be marked to be remembered?
39
+ def remember_me?
40
+ valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
41
+ end
42
+
29
43
  # Check if this is strategy is valid for http authentication by:
30
44
  #
31
45
  # * Validating if the model allows params authentication;
@@ -96,15 +110,17 @@ module Devise
96
110
 
97
111
  # Helper to decode credentials from HTTP.
98
112
  def decode_credentials
99
- return [] unless request.authorization && request.authorization =~ /^Basic (.*)/
113
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
100
114
  ActiveSupport::Base64.decode64($1).split(/:/, 2)
101
115
  end
102
116
 
103
117
  # Sets the authentication hash and the password from params_auth_hash or http_auth_hash.
104
- def with_authentication_hash(hash)
105
- self.authentication_hash = hash.slice(*authentication_keys)
106
- self.password = hash[:password]
107
- authentication_keys.all?{ |k| authentication_hash[k].present? }
118
+ def with_authentication_hash(auth_values)
119
+ self.authentication_hash = {}
120
+ self.password = auth_values[:password]
121
+
122
+ parse_authentication_key_values(auth_values, authentication_keys) &&
123
+ parse_authentication_key_values(request_values, request_keys)
108
124
  end
109
125
 
110
126
  # Holds the authentication keys.
@@ -112,6 +128,31 @@ module Devise
112
128
  @authentication_keys ||= mapping.to.authentication_keys
113
129
  end
114
130
 
131
+ # Holds request keys.
132
+ def request_keys
133
+ @request_keys ||= mapping.to.request_keys
134
+ end
135
+
136
+ # Returns values from the request object.
137
+ def request_values
138
+ keys = request_keys.respond_to?(:keys) ? request_keys.keys : request_keys
139
+ values = keys.map { |k| self.request.send(k) }
140
+ Hash[keys.zip(values)]
141
+ end
142
+
143
+ # Parse authentication keys considering if they should be enforced or not.
144
+ def parse_authentication_key_values(hash, keys)
145
+ keys.each do |key, enforce|
146
+ value = hash[key].presence
147
+ if value
148
+ self.authentication_hash[key] = value
149
+ else
150
+ return false unless enforce == false
151
+ end
152
+ end
153
+ true
154
+ end
155
+
115
156
  # Holds the authenticatable name for this class. Devise::Strategies::DatabaseAuthenticatable
116
157
  # becomes simply :database.
117
158
  def authenticatable_name
@@ -10,7 +10,7 @@ module Devise
10
10
  if validate(resource){ resource.valid_password?(password) }
11
11
  resource.after_database_authentication
12
12
  success!(resource)
13
- else
13
+ elsif !halted?
14
14
  fail(:invalid)
15
15
  end
16
16
  end
@@ -20,7 +20,7 @@ module Devise
20
20
 
21
21
  if validate(resource)
22
22
  success!(resource)
23
- else
23
+ elsif !halted?
24
24
  cookies.delete(remember_key)
25
25
  pass
26
26
  end
@@ -28,6 +28,11 @@ module Devise
28
28
 
29
29
  private
30
30
 
31
+ def decorate(resource)
32
+ super
33
+ resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
34
+ end
35
+
31
36
  def remember_me?
32
37
  true
33
38
  end
@@ -36,10 +41,6 @@ module Devise
36
41
  "remember_#{scope}_token"
37
42
  end
38
43
 
39
- def extend_remember_period?
40
- mapping.to.extend_remember_period
41
- end
42
-
43
44
  # Accessor for remember cookie
44
45
  def remember_cookie
45
46
  @remember_cookie ||= cookies.signed[remember_key]
@@ -10,13 +10,17 @@ module Devise
10
10
  # For HTTP, you can pass the token as username and blank password. Since some clients may require
11
11
  # a password, you can pass "X" as password and it will simply be ignored.
12
12
  class TokenAuthenticatable < Authenticatable
13
+ def store?
14
+ !mapping.to.stateless_token
15
+ end
16
+
13
17
  def authenticate!
14
18
  resource = mapping.to.find_for_token_authentication(authentication_hash)
15
19
 
16
20
  if validate(resource)
17
21
  resource.after_token_authentication
18
22
  success!(resource)
19
- else
23
+ elsif !halted?
20
24
  fail(:invalid_token)
21
25
  end
22
26
  end
@@ -28,7 +32,7 @@ module Devise
28
32
  true
29
33
  end
30
34
 
31
- # Do not use remember_me behavir with token.
35
+ # Do not use remember_me behavior with token.
32
36
  def remember_me?
33
37
  false
34
38
  end
@@ -1,4 +1,11 @@
1
1
  module Devise
2
+ # Devise::TestHelpers provides a facility to test controllers in isolation
3
+ # when using ActionController::TestCase allowing you to quickly sign_in or
4
+ # sign_out a user. Do not use Devise::TestHelpers in integration tests.
5
+ #
6
+ # Notice you should not test Warden specific behavior (like Warden callbacks)
7
+ # using Devise::TestHelpers since it is a stub of the actual behavior. Such
8
+ # callbacks should be tested in your integration suite instead.
2
9
  module TestHelpers
3
10
  def self.included(base)
4
11
  base.class_eval do
@@ -37,9 +44,9 @@ module Devise
37
44
  env = @controller.request.env
38
45
  env["PATH_INFO"] = "/#{result[:action]}"
39
46
  env["warden.options"] = result
40
- Warden::Manager._before_failure.each{ |hook| hook.call(env, result) }
47
+ Warden::Manager._run_callbacks(:before_failure, env, result)
41
48
 
42
- status, headers, body = Devise::FailureApp.call(env).to_a
49
+ status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
43
50
  @controller.send :render, :status => status, :text => body,
44
51
  :content_type => headers["Content-Type"], :location => headers["Location"]
45
52
 
@@ -61,6 +68,7 @@ module Devise
61
68
  end
62
69
 
63
70
  # sign_in a given resource by storing its keys in the session.
71
+ # This method bypass any warden authentication callback.
64
72
  #
65
73
  # Examples:
66
74
  #
@@ -74,6 +82,7 @@ module Devise
74
82
  end
75
83
 
76
84
  # Sign out a given resource or scope by calling logout on Warden.
85
+ # This method bypass any warden logout callback.
77
86
  #
78
87
  # Examples:
79
88
  #
@@ -83,7 +92,8 @@ module Devise
83
92
  def sign_out(resource_or_scope)
84
93
  scope = Devise::Mapping.find_scope!(resource_or_scope)
85
94
  @controller.instance_variable_set(:"@current_#{scope}", nil)
86
- warden.logout(scope)
95
+ user = warden.instance_variable_get(:@users).delete(scope)
96
+ warden.session_serializer.delete(scope, user)
87
97
  end
88
98
 
89
99
  end
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "1.1.3".freeze
2
+ VERSION = "1.2.0".freeze
3
3
  end
data/lib/devise.rb CHANGED
@@ -1,8 +1,12 @@
1
+ require 'rails'
1
2
  require 'active_support/core_ext/numeric/time'
2
3
  require 'active_support/dependencies'
4
+ require 'orm_adapter'
5
+ require 'set'
3
6
 
4
7
  module Devise
5
8
  autoload :FailureApp, 'devise/failure_app'
9
+ autoload :OmniAuth, 'devise/omniauth'
6
10
  autoload :PathChecker, 'devise/path_checker'
7
11
  autoload :Schema, 'devise/schema'
8
12
  autoload :TestHelpers, 'devise/test_helpers'
@@ -10,13 +14,13 @@ module Devise
10
14
  module Controllers
11
15
  autoload :Helpers, 'devise/controllers/helpers'
12
16
  autoload :InternalHelpers, 'devise/controllers/internal_helpers'
17
+ autoload :Rememberable, 'devise/controllers/rememberable'
13
18
  autoload :ScopedViews, 'devise/controllers/scoped_views'
14
19
  autoload :UrlHelpers, 'devise/controllers/url_helpers'
15
20
  end
16
21
 
17
22
  module Encryptors
18
23
  autoload :Base, 'devise/encryptors/base'
19
- autoload :Bcrypt, 'devise/encryptors/bcrypt'
20
24
  autoload :AuthlogicSha512, 'devise/encryptors/authlogic_sha512'
21
25
  autoload :ClearanceSha1, 'devise/encryptors/clearance_sha1'
22
26
  autoload :RestfulAuthenticationSha1, 'devise/encryptors/restful_authentication_sha1'
@@ -30,11 +34,12 @@ module Devise
30
34
  end
31
35
 
32
36
  # Constants which holds devise configuration for extensions. Those should
33
- # not be modified by the "end user".
37
+ # not be modified by the "end user" (this is why they are constants).
34
38
  ALL = []
35
39
  CONTROLLERS = ActiveSupport::OrderedHash.new
36
40
  ROUTES = ActiveSupport::OrderedHash.new
37
41
  STRATEGIES = ActiveSupport::OrderedHash.new
42
+ URL_HELPERS = ActiveSupport::OrderedHash.new
38
43
 
39
44
  # True values used to check params
40
45
  TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
@@ -45,31 +50,35 @@ module Devise
45
50
  :sha512 => 128,
46
51
  :clearance_sha1 => 40,
47
52
  :restful_authentication_sha1 => 40,
48
- :authlogic_sha512 => 128,
49
- :bcrypt => 60
53
+ :authlogic_sha512 => 128
50
54
  }
51
55
 
52
56
  # Custom domain for cookies. Not set by default
53
- mattr_accessor :cookie_domain
54
- @@cookie_domain = false
55
-
56
- # Used to encrypt password. Please generate one with rake secret.
57
- mattr_accessor :pepper
58
- @@pepper = nil
57
+ mattr_accessor :cookie_options
58
+ @@cookie_options = {}
59
59
 
60
60
  # The number of times to encrypt password.
61
61
  mattr_accessor :stretches
62
62
  @@stretches = 10
63
63
 
64
- # Keys used when authenticating an user.
64
+ # Keys used when authenticating a user.
65
65
  mattr_accessor :authentication_keys
66
66
  @@authentication_keys = [ :email ]
67
67
 
68
+ # Request keys used when authenticating a user.
69
+ mattr_accessor :request_keys
70
+ @@request_keys = []
71
+
72
+ # Keys that should be case-insensitive.
73
+ # False by default for backwards compatibility.
74
+ mattr_accessor :case_insensitive_keys
75
+ @@case_insensitive_keys = false
76
+
68
77
  # If http authentication is enabled by default.
69
78
  mattr_accessor :http_authenticatable
70
- @@http_authenticatable = true
79
+ @@http_authenticatable = false
71
80
 
72
- # If http authentication is used for ajax requests. True by default.
81
+ # If http headers should be returned for ajax requests. True by default.
73
82
  mattr_accessor :http_authenticatable_on_xhr
74
83
  @@http_authenticatable_on_xhr = true
75
84
 
@@ -83,7 +92,7 @@ module Devise
83
92
 
84
93
  # Email regex used to validate email formats. Adapted from authlogic.
85
94
  mattr_accessor :email_regexp
86
- @@email_regexp = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
95
+ @@email_regexp = /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
87
96
 
88
97
  # Range validation for password length
89
98
  mattr_accessor :password_length
@@ -101,22 +110,31 @@ module Devise
101
110
  mattr_accessor :extend_remember_period
102
111
  @@extend_remember_period = false
103
112
 
113
+ # If true, uses salt as remember token and does not create it in the database.
114
+ # By default is false for backwards compatibility.
115
+ mattr_accessor :use_salt_as_remember_token
116
+ @@use_salt_as_remember_token = false
117
+
104
118
  # Time interval you can access your account before confirming your account.
105
119
  mattr_accessor :confirm_within
106
120
  @@confirm_within = 0.days
107
121
 
122
+ # Defines which key will be used when confirming an account
123
+ mattr_accessor :confirmation_keys
124
+ @@confirmation_keys = [ :email ]
125
+
108
126
  # Time interval to timeout the user session without activity.
109
127
  mattr_accessor :timeout_in
110
128
  @@timeout_in = 30.minutes
111
129
 
130
+ # Used to encrypt password. Please generate one with rake secret.
131
+ mattr_accessor :pepper
132
+ @@pepper = nil
133
+
112
134
  # Used to define the password encryption algorithm.
113
135
  mattr_accessor :encryptor
114
136
  @@encryptor = nil
115
137
 
116
- # Store scopes mappings.
117
- mattr_accessor :mappings
118
- @@mappings = ActiveSupport::OrderedHash.new
119
-
120
138
  # Tells if devise should apply the schema in ORMs where devise declaration
121
139
  # and schema belongs to the same class (as Datamapper and Mongoid).
122
140
  mattr_accessor :apply_schema
@@ -132,6 +150,10 @@ module Devise
132
150
  mattr_accessor :lock_strategy
133
151
  @@lock_strategy = :failed_attempts
134
152
 
153
+ # Defines which key will be used when locking and unlocking an account
154
+ mattr_accessor :unlock_keys
155
+ @@unlock_keys = [ :email ]
156
+
135
157
  # Defines which strategy can be used to unlock an account.
136
158
  # Values: :email, :time, :both
137
159
  mattr_accessor :unlock_strategy
@@ -145,6 +167,10 @@ module Devise
145
167
  mattr_accessor :unlock_in
146
168
  @@unlock_in = 1.hour
147
169
 
170
+ # Defines which key will be used when recovering the password for an account
171
+ mattr_accessor :reset_password_keys
172
+ @@reset_password_keys = [ :email ]
173
+
148
174
  # The default scope which is used by warden.
149
175
  mattr_accessor :default_scope
150
176
  @@default_scope = nil
@@ -157,59 +183,98 @@ module Devise
157
183
  mattr_accessor :token_authentication_key
158
184
  @@token_authentication_key = :auth_token
159
185
 
186
+ # If true, authentication through token does not store user in session
187
+ mattr_accessor :stateless_token
188
+ @@stateless_token = false
189
+
160
190
  # Which formats should be treated as navigational.
191
+ # We need both :"*/*" and "*/*" to work on different Rails versions.
161
192
  mattr_accessor :navigational_formats
162
- @@navigational_formats = [:html]
193
+ @@navigational_formats = [:"*/*", "*/*", :html]
194
+
195
+ # When set to true, signing out a user signs out all other scopes.
196
+ mattr_accessor :sign_out_all_scopes
197
+ @@sign_out_all_scopes = true
198
+
199
+ # The default method used while signing out
200
+ mattr_accessor :sign_out_via
201
+ @@sign_out_via = :get
202
+
203
+ # PRIVATE CONFIGURATION
204
+
205
+ # Store scopes mappings.
206
+ mattr_reader :mappings
207
+ @@mappings = ActiveSupport::OrderedHash.new
208
+
209
+ # Omniauth configurations.
210
+ mattr_reader :omniauth_configs
211
+ @@omniauth_configs = ActiveSupport::OrderedHash.new
212
+
213
+ # Define a set of modules that are called when a mapping is added.
214
+ mattr_reader :helpers
215
+ @@helpers = Set.new
216
+ @@helpers << Devise::Controllers::Helpers
163
217
 
164
218
  # Private methods to interface with Warden.
165
219
  mattr_accessor :warden_config
166
220
  @@warden_config = nil
167
221
  @@warden_config_block = nil
168
222
 
169
- # When set to true, signing out an user signs out all other scopes.
170
- mattr_accessor :sign_out_all_scopes
171
- @@sign_out_all_scopes = false
172
-
173
- def self.use_default_scope=(*)
174
- ActiveSupport::Deprecation.warn "config.use_default_scope is deprecated and removed from Devise. " <<
175
- "If you are using non conventional routes in Devise, all you need to do is to pass the devise " <<
176
- "scope in the router DSL:\n\n as :user do\n get \"sign_in\", :to => \"devise/sessions\"\n end\n\n" <<
177
- "The method :as is also aliased to :devise_scope. Choose the one you prefer.", caller
178
- end
179
-
180
223
  # Default way to setup Devise. Run rails generate devise_install to create
181
224
  # a fresh initializer with all configuration values.
182
225
  def self.setup
183
226
  yield self
184
227
  end
185
228
 
229
+ def self.ref(arg)
230
+ if defined?(ActiveSupport::Dependencies::ClassCache)
231
+ ActiveSupport::Dependencies::Reference.store(arg)
232
+ else
233
+ ActiveSupport::Dependencies.ref(arg)
234
+ end
235
+ end
236
+
237
+ def self.omniauth_providers
238
+ omniauth_configs.keys
239
+ end
240
+
241
+ def self.cookie_domain=(value)
242
+ ActiveSupport::Deprecation.warn "Devise.cookie_domain=(value) is deprecated. "
243
+ "Please use Devise.cookie_options = { :domain => value } instead."
244
+ self.cookie_options[:domain] = value
245
+ end
246
+
186
247
  # Get the mailer class from the mailer reference object.
187
248
  def self.mailer
188
- @@mailer_ref.get
249
+ if defined?(ActiveSupport::Dependencies::ClassCache)
250
+ @@mailer_ref.get "Devise::Mailer"
251
+ else
252
+ @@mailer_ref.get
253
+ end
189
254
  end
190
255
 
191
256
  # Set the mailer reference object to access the mailer.
192
257
  def self.mailer=(class_name)
193
- @@mailer_ref = ActiveSupport::Dependencies.ref(class_name)
258
+ @@mailer_ref = ref(class_name)
194
259
  end
195
260
  self.mailer = "Devise::Mailer"
196
261
 
197
262
  # Small method that adds a mapping to Devise.
198
263
  def self.add_mapping(resource, options)
199
264
  mapping = Devise::Mapping.new(resource, options)
200
- self.mappings[mapping.name] = mapping
201
- self.default_scope ||= mapping.name
265
+ @@mappings[mapping.name] = mapping
266
+ @@default_scope ||= mapping.name
267
+ @@helpers.each { |h| h.define_helpers(mapping) }
202
268
  mapping
203
269
  end
204
270
 
205
- # Make Devise aware of an 3rd party Devise-module. For convenience.
271
+ # Make Devise aware of an 3rd party Devise-module (like invitable). For convenience.
206
272
  #
207
273
  # == Options:
208
274
  #
209
275
  # +model+ - String representing the load path to a custom *model* for this module (to autoload.)
210
276
  # +controller+ - Symbol representing the name of an exisiting or custom *controller* for this module.
211
277
  # +route+ - Symbol representing the named *route* helper for this module.
212
- # +flash+ - Symbol representing the *flash messages* used by this helper.
213
278
  # +strategy+ - Symbol representing if this module got a custom *strategy*.
214
279
  #
215
280
  # All values, except :model, accept also a boolean and will have the same name as the given module
@@ -225,26 +290,36 @@ module Devise
225
290
  ALL << module_name
226
291
  options.assert_valid_keys(:strategy, :model, :controller, :route)
227
292
 
228
- config = {
229
- :strategy => STRATEGIES,
230
- :route => ROUTES,
231
- :controller => CONTROLLERS
232
- }
293
+ if strategy = options[:strategy]
294
+ STRATEGIES[module_name] = (strategy == true ? module_name : strategy)
295
+ end
233
296
 
234
- config.each do |key, value|
235
- next unless options[key]
236
- name = (options[key] == true ? module_name : options[key])
297
+ if controller = options[:controller]
298
+ CONTROLLERS[module_name] = (controller == true ? module_name : controller)
299
+ end
237
300
 
238
- if value.is_a?(Hash)
239
- value[module_name] = name
301
+ if route = options[:route]
302
+ case route
303
+ when TrueClass
304
+ key, value = module_name, []
305
+ when Symbol
306
+ key, value = route, []
307
+ when Hash
308
+ key, value = route.keys.first, route.values.flatten
240
309
  else
241
- value << name unless value.include?(name)
310
+ raise ArgumentError, ":route should be true, a Symbol or a Hash"
242
311
  end
312
+
313
+ URL_HELPERS[key] ||= []
314
+ URL_HELPERS[key].concat(value)
315
+ URL_HELPERS[key].uniq!
316
+
317
+ ROUTES[module_name] = key
243
318
  end
244
319
 
245
320
  if options[:model]
246
- model_path = (options[:model] == true ? "devise/models/#{module_name}" : options[:model])
247
- Devise::Models.send(:autoload, module_name.to_s.camelize.to_sym, model_path)
321
+ path = (options[:model] == true ? "devise/models/#{module_name}" : options[:model])
322
+ Devise::Models.send(:autoload, module_name.to_s.camelize.to_sym, path)
248
323
  end
249
324
 
250
325
  Devise::Mapping.add_module module_name
@@ -265,12 +340,39 @@ module Devise
265
340
  @@warden_config_block = block
266
341
  end
267
342
 
343
+ # Specify an omniauth provider.
344
+ #
345
+ # config.omniauth :github, APP_ID, APP_SECRET
346
+ #
347
+ def self.omniauth(provider, *args)
348
+ @@helpers << Devise::OmniAuth::UrlHelpers
349
+ @@omniauth_configs[provider] = Devise::OmniAuth::Config.new(provider, args)
350
+ end
351
+
352
+ # Include helpers in the given scope to AC and AV.
353
+ def self.include_helpers(scope)
354
+ ActiveSupport.on_load(:action_controller) do
355
+ include scope::Helpers if defined?(scope::Helpers)
356
+ include scope::UrlHelpers
357
+ end
358
+
359
+ ActiveSupport.on_load(:action_view) do
360
+ include scope::UrlHelpers
361
+ end
362
+ end
363
+
364
+ # Returns true if Rails version is bigger than 3.0.x
365
+ def self.rack_session?
366
+ Rails::VERSION::STRING[0,3] != "3.0"
367
+ end
368
+
268
369
  # A method used internally to setup warden manager from the Rails initialize
269
370
  # block.
270
371
  def self.configure_warden! #:nodoc:
271
372
  @@warden_configured ||= begin
272
373
  warden_config.failure_app = Devise::FailureApp
273
374
  warden_config.default_scope = Devise.default_scope
375
+ warden_config.intercept_401 = false
274
376
 
275
377
  Devise.mappings.each_value do |mapping|
276
378
  warden_config.scope_defaults mapping.name, :strategies => mapping.strategies
@@ -283,7 +385,17 @@ module Devise
283
385
 
284
386
  # Generate a friendly string randomically to be used as token.
285
387
  def self.friendly_token
286
- ActiveSupport::SecureRandom.base64(15).tr('+/=', '-_ ').strip.delete("\n")
388
+ ActiveSupport::SecureRandom.base64(15).tr('+/=', 'xyz')
389
+ end
390
+
391
+ # constant-time comparison algorithm to prevent timing attacks
392
+ def self.secure_compare(a, b)
393
+ return false if a.blank? || b.blank? || a.bytesize != b.bytesize
394
+ l = a.unpack "C#{a.bytesize}"
395
+
396
+ res = 0
397
+ b.each_byte { |byte| res |= byte ^ l.shift }
398
+ res == 0
287
399
  end
288
400
  end
289
401
 
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  source_root File.expand_path("../templates", __FILE__)
11
11
 
12
12
  def generate_model
13
- invoke "active_record:model", [name], :migration => false unless model_exists?
13
+ invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
14
14
  end
15
15
 
16
16
  def copy_devise_migration
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  def inject_devise_content
21
- inject_into_class model_path, class_name, model_contents + <<-CONTENT
21
+ inject_into_class(model_path, class_name, model_contents + <<CONTENT) if model_exists?
22
22
  # Setup accessible (or protected) attributes for your model
23
23
  attr_accessible :email, :password, :password_confirmation, :remember_me
24
24
  CONTENT
@@ -6,6 +6,7 @@ class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
6
6
  t.rememberable
7
7
  t.trackable
8
8
 
9
+ # t.encryptable
9
10
  # t.confirmable
10
11
  # t.lockable :lock_strategy => :<%= Devise.lock_strategy %>, :unlock_strategy => :<%= Devise.unlock_strategy %>
11
12
  # t.token_authenticatable
@@ -21,6 +22,7 @@ class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
21
22
  add_index :<%= table_name %>, :reset_password_token, :unique => true
22
23
  # add_index :<%= table_name %>, :confirmation_token, :unique => true
23
24
  # add_index :<%= table_name %>, :unlock_token, :unique => true
25
+ # add_index :<%= table_name %>, :authentication_token, :unique => true
24
26
  end
25
27
 
26
28
  def self.down
@@ -10,7 +10,9 @@ module Devise
10
10
  hook_for :orm
11
11
 
12
12
  def add_devise_routes
13
- route "devise_for :#{table_name}"
13
+ devise_route = "devise_for :#{plural_name}"
14
+ devise_route += %Q(, :class_name => "#{class_name}") if class_name.include?("::")
15
+ route devise_route
14
16
  end
15
17
  end
16
18
  end
@@ -4,7 +4,7 @@ module Devise
4
4
  def model_contents
5
5
  <<-CONTENT
6
6
  # Include default devise modules. Others available are:
7
- # :token_authenticatable, :confirmable, :lockable and :timeoutable
7
+ # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
8
8
  devise :database_authenticatable, :registerable,
9
9
  :recoverable, :rememberable, :trackable, :validatable
10
10