upstream-devise 2.1.0.rc

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 (215) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +15 -0
  3. data/CHANGELOG.rdoc +846 -0
  4. data/Gemfile +35 -0
  5. data/Gemfile.lock +165 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +384 -0
  8. data/Rakefile +34 -0
  9. data/app/controllers/devise/confirmations_controller.rb +43 -0
  10. data/app/controllers/devise/omniauth_callbacks_controller.rb +24 -0
  11. data/app/controllers/devise/passwords_controller.rb +47 -0
  12. data/app/controllers/devise/registrations_controller.rb +107 -0
  13. data/app/controllers/devise/sessions_controller.rb +49 -0
  14. data/app/controllers/devise/unlocks_controller.rb +44 -0
  15. data/app/controllers/devise_controller.rb +177 -0
  16. data/app/helpers/devise_helper.rb +25 -0
  17. data/app/mailers/devise/mailer.rb +15 -0
  18. data/app/views/devise/_links.erb +3 -0
  19. data/app/views/devise/confirmations/new.html.erb +12 -0
  20. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  21. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  22. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  23. data/app/views/devise/passwords/edit.html.erb +16 -0
  24. data/app/views/devise/passwords/new.html.erb +12 -0
  25. data/app/views/devise/registrations/edit.html.erb +25 -0
  26. data/app/views/devise/registrations/new.html.erb +18 -0
  27. data/app/views/devise/sessions/new.html.erb +17 -0
  28. data/app/views/devise/shared/_links.erb +25 -0
  29. data/app/views/devise/unlocks/new.html.erb +12 -0
  30. data/config/locales/en.yml +57 -0
  31. data/devise.gemspec +25 -0
  32. data/gemfiles/Gemfile.rails-3.1.x +35 -0
  33. data/gemfiles/Gemfile.rails-3.1.x.lock +167 -0
  34. data/lib/devise.rb +455 -0
  35. data/lib/devise/controllers/helpers.rb +269 -0
  36. data/lib/devise/controllers/rememberable.rb +52 -0
  37. data/lib/devise/controllers/scoped_views.rb +17 -0
  38. data/lib/devise/controllers/url_helpers.rb +67 -0
  39. data/lib/devise/delegator.rb +17 -0
  40. data/lib/devise/encryptors/authlogic_sha512.rb +19 -0
  41. data/lib/devise/encryptors/base.rb +24 -0
  42. data/lib/devise/encryptors/clearance_sha1.rb +17 -0
  43. data/lib/devise/encryptors/restful_authentication_sha1.rb +22 -0
  44. data/lib/devise/encryptors/sha1.rb +25 -0
  45. data/lib/devise/encryptors/sha512.rb +25 -0
  46. data/lib/devise/failure_app.rb +185 -0
  47. data/lib/devise/hooks/activatable.rb +11 -0
  48. data/lib/devise/hooks/forgetable.rb +9 -0
  49. data/lib/devise/hooks/lockable.rb +7 -0
  50. data/lib/devise/hooks/rememberable.rb +6 -0
  51. data/lib/devise/hooks/timeoutable.rb +22 -0
  52. data/lib/devise/hooks/trackable.rb +9 -0
  53. data/lib/devise/mailers/helpers.rb +86 -0
  54. data/lib/devise/mapping.rb +172 -0
  55. data/lib/devise/models.rb +123 -0
  56. data/lib/devise/models/authenticatable.rb +231 -0
  57. data/lib/devise/models/confirmable.rb +242 -0
  58. data/lib/devise/models/database_authenticatable.rb +126 -0
  59. data/lib/devise/models/encryptable.rb +86 -0
  60. data/lib/devise/models/lockable.rb +185 -0
  61. data/lib/devise/models/omniauthable.rb +27 -0
  62. data/lib/devise/models/recoverable.rb +140 -0
  63. data/lib/devise/models/registerable.rb +25 -0
  64. data/lib/devise/models/rememberable.rb +125 -0
  65. data/lib/devise/models/timeoutable.rb +49 -0
  66. data/lib/devise/models/token_authenticatable.rb +77 -0
  67. data/lib/devise/models/trackable.rb +35 -0
  68. data/lib/devise/models/validatable.rb +66 -0
  69. data/lib/devise/modules.rb +30 -0
  70. data/lib/devise/omniauth.rb +28 -0
  71. data/lib/devise/omniauth/config.rb +45 -0
  72. data/lib/devise/omniauth/url_helpers.rb +33 -0
  73. data/lib/devise/orm/active_record.rb +3 -0
  74. data/lib/devise/orm/mongoid.rb +3 -0
  75. data/lib/devise/param_filter.rb +41 -0
  76. data/lib/devise/rails.rb +54 -0
  77. data/lib/devise/rails/routes.rb +412 -0
  78. data/lib/devise/rails/warden_compat.rb +43 -0
  79. data/lib/devise/strategies/authenticatable.rb +165 -0
  80. data/lib/devise/strategies/base.rb +15 -0
  81. data/lib/devise/strategies/database_authenticatable.rb +21 -0
  82. data/lib/devise/strategies/rememberable.rb +53 -0
  83. data/lib/devise/strategies/token_authenticatable.rb +57 -0
  84. data/lib/devise/test_helpers.rb +130 -0
  85. data/lib/devise/version.rb +3 -0
  86. data/lib/generators/active_record/devise_generator.rb +78 -0
  87. data/lib/generators/active_record/templates/migration.rb +19 -0
  88. data/lib/generators/active_record/templates/migration_existing.rb +26 -0
  89. data/lib/generators/devise/devise_generator.rb +24 -0
  90. data/lib/generators/devise/install_generator.rb +24 -0
  91. data/lib/generators/devise/orm_helpers.rb +32 -0
  92. data/lib/generators/devise/views_generator.rb +110 -0
  93. data/lib/generators/mongoid/devise_generator.rb +60 -0
  94. data/lib/generators/templates/README +31 -0
  95. data/lib/generators/templates/devise.rb +216 -0
  96. data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  97. data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  98. data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
  99. data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +15 -0
  100. data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
  101. data/lib/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
  102. data/lib/generators/templates/simple_form_for/registrations/edit.html.erb +22 -0
  103. data/lib/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
  104. data/lib/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
  105. data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +15 -0
  106. data/test/controllers/custom_strategy_test.rb +62 -0
  107. data/test/controllers/helpers_test.rb +254 -0
  108. data/test/controllers/internal_helpers_test.rb +97 -0
  109. data/test/controllers/sessions_controller_test.rb +36 -0
  110. data/test/controllers/url_helpers_test.rb +59 -0
  111. data/test/delegator_test.rb +19 -0
  112. data/test/devise_test.rb +72 -0
  113. data/test/encryptors_test.rb +30 -0
  114. data/test/failure_app_test.rb +211 -0
  115. data/test/generators/active_record_generator_test.rb +69 -0
  116. data/test/generators/devise_generator_test.rb +39 -0
  117. data/test/generators/install_generator_test.rb +13 -0
  118. data/test/generators/mongoid_generator_test.rb +23 -0
  119. data/test/generators/views_generator_test.rb +52 -0
  120. data/test/helpers/devise_helper_test.rb +51 -0
  121. data/test/indifferent_hash.rb +33 -0
  122. data/test/integration/authenticatable_test.rb +587 -0
  123. data/test/integration/confirmable_test.rb +255 -0
  124. data/test/integration/database_authenticatable_test.rb +82 -0
  125. data/test/integration/http_authenticatable_test.rb +97 -0
  126. data/test/integration/lockable_test.rb +224 -0
  127. data/test/integration/omniauthable_test.rb +133 -0
  128. data/test/integration/recoverable_test.rb +300 -0
  129. data/test/integration/registerable_test.rb +324 -0
  130. data/test/integration/rememberable_test.rb +158 -0
  131. data/test/integration/timeoutable_test.rb +114 -0
  132. data/test/integration/token_authenticatable_test.rb +161 -0
  133. data/test/integration/trackable_test.rb +92 -0
  134. data/test/mailers/confirmation_instructions_test.rb +95 -0
  135. data/test/mailers/reset_password_instructions_test.rb +83 -0
  136. data/test/mailers/unlock_instructions_test.rb +77 -0
  137. data/test/mapping_test.rb +127 -0
  138. data/test/models/authenticatable_test.rb +7 -0
  139. data/test/models/confirmable_test.rb +357 -0
  140. data/test/models/database_authenticatable_test.rb +189 -0
  141. data/test/models/encryptable_test.rb +73 -0
  142. data/test/models/lockable_test.rb +263 -0
  143. data/test/models/omniauthable_test.rb +7 -0
  144. data/test/models/recoverable_test.rb +205 -0
  145. data/test/models/registerable_test.rb +7 -0
  146. data/test/models/rememberable_test.rb +174 -0
  147. data/test/models/serializable_test.rb +48 -0
  148. data/test/models/timeoutable_test.rb +46 -0
  149. data/test/models/token_authenticatable_test.rb +55 -0
  150. data/test/models/trackable_test.rb +13 -0
  151. data/test/models/validatable_test.rb +117 -0
  152. data/test/models_test.rb +179 -0
  153. data/test/omniauth/config_test.rb +57 -0
  154. data/test/omniauth/url_helpers_test.rb +58 -0
  155. data/test/orm/active_record.rb +9 -0
  156. data/test/orm/mongoid.rb +14 -0
  157. data/test/rails_app/Rakefile +10 -0
  158. data/test/rails_app/app/active_record/admin.rb +6 -0
  159. data/test/rails_app/app/active_record/shim.rb +2 -0
  160. data/test/rails_app/app/active_record/user.rb +6 -0
  161. data/test/rails_app/app/controllers/admins/sessions_controller.rb +6 -0
  162. data/test/rails_app/app/controllers/admins_controller.rb +6 -0
  163. data/test/rails_app/app/controllers/application_controller.rb +8 -0
  164. data/test/rails_app/app/controllers/home_controller.rb +25 -0
  165. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  166. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  167. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
  168. data/test/rails_app/app/controllers/users_controller.rb +23 -0
  169. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  170. data/test/rails_app/app/mailers/users/mailer.rb +3 -0
  171. data/test/rails_app/app/mongoid/admin.rb +30 -0
  172. data/test/rails_app/app/mongoid/shim.rb +24 -0
  173. data/test/rails_app/app/mongoid/user.rb +45 -0
  174. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  175. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  176. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
  177. data/test/rails_app/app/views/home/index.html.erb +1 -0
  178. data/test/rails_app/app/views/home/join.html.erb +1 -0
  179. data/test/rails_app/app/views/home/private.html.erb +1 -0
  180. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
  181. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  182. data/test/rails_app/app/views/users/index.html.erb +1 -0
  183. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  184. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  185. data/test/rails_app/config.ru +4 -0
  186. data/test/rails_app/config/application.rb +41 -0
  187. data/test/rails_app/config/boot.rb +8 -0
  188. data/test/rails_app/config/database.yml +18 -0
  189. data/test/rails_app/config/environment.rb +5 -0
  190. data/test/rails_app/config/environments/development.rb +18 -0
  191. data/test/rails_app/config/environments/production.rb +33 -0
  192. data/test/rails_app/config/environments/test.rb +33 -0
  193. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  194. data/test/rails_app/config/initializers/devise.rb +186 -0
  195. data/test/rails_app/config/initializers/inflections.rb +2 -0
  196. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  197. data/test/rails_app/config/routes.rb +90 -0
  198. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +77 -0
  199. data/test/rails_app/db/schema.rb +52 -0
  200. data/test/rails_app/lib/shared_admin.rb +14 -0
  201. data/test/rails_app/lib/shared_user.rb +26 -0
  202. data/test/rails_app/public/404.html +26 -0
  203. data/test/rails_app/public/422.html +26 -0
  204. data/test/rails_app/public/500.html +26 -0
  205. data/test/rails_app/public/favicon.ico +0 -0
  206. data/test/rails_app/script/rails +10 -0
  207. data/test/routes_test.rb +248 -0
  208. data/test/support/assertions.rb +42 -0
  209. data/test/support/helpers.rb +91 -0
  210. data/test/support/integration.rb +90 -0
  211. data/test/support/locale/en.yml +4 -0
  212. data/test/support/webrat/integrations/rails.rb +24 -0
  213. data/test/test_helper.rb +27 -0
  214. data/test/test_helpers_test.rb +134 -0
  215. metadata +451 -0
@@ -0,0 +1,43 @@
1
+ module Warden::Mixins::Common
2
+ def request
3
+ @request ||= ActionDispatch::Request.new(env)
4
+ end
5
+
6
+ # This is called internally by Warden on logout
7
+ def reset_session!
8
+ request.reset_session
9
+ end
10
+
11
+ def cookies
12
+ request.cookie_jar
13
+ end
14
+ end
15
+
16
+ class Warden::SessionSerializer
17
+ def serialize(record)
18
+ klass = record.class
19
+ array = klass.serialize_into_session(record)
20
+ array.unshift(klass.name)
21
+ end
22
+
23
+ def deserialize(keys)
24
+ klass_name, *args = keys
25
+
26
+ begin
27
+ klass = ActiveSupport::Inflector.constantize(klass_name)
28
+ if klass.respond_to? :serialize_from_session
29
+ klass.serialize_from_session(*args)
30
+ else
31
+ Rails.logger.warn "[Devise] Stored serialized class #{klass_name} seems not to be Devise enabled anymore. Did you do that on purpose?"
32
+ nil
33
+ end
34
+ rescue NameError => e
35
+ if e.message =~ /uninitialized constant/
36
+ Rails.logger.debug "[Devise] Trying to deserialize invalid class #{klass_name}"
37
+ nil
38
+ else
39
+ raise
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,165 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # This strategy should be used as basis for authentication strategies. It retrieves
6
+ # parameters both from params or from http authorization headers. See database_authenticatable
7
+ # for an example.
8
+ class Authenticatable < Base
9
+ attr_accessor :authentication_hash, :authentication_type, :password
10
+
11
+ def store?
12
+ !mapping.to.skip_session_storage.include?(authentication_type)
13
+ end
14
+
15
+ def valid?
16
+ valid_for_params_auth? || valid_for_http_auth?
17
+ end
18
+
19
+ private
20
+
21
+ # Simply invokes valid_for_authentication? with the given block and deal with the result.
22
+ def validate(resource, &block)
23
+ result = resource && resource.valid_for_authentication?(&block)
24
+
25
+ case result
26
+ when Symbol, String
27
+ ActiveSupport::Deprecation.warn "valid_for_authentication should return a boolean value"
28
+ fail!(result)
29
+ return false
30
+ end
31
+
32
+ if result
33
+ decorate(resource)
34
+ true
35
+ else
36
+ if resource
37
+ fail!(resource.unauthenticated_message)
38
+ end
39
+ false
40
+ end
41
+ end
42
+
43
+ # Get values from params and set in the resource.
44
+ def decorate(resource)
45
+ resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
46
+ end
47
+
48
+ # Should this resource be marked to be remembered?
49
+ def remember_me?
50
+ valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
51
+ end
52
+
53
+ # Check if this is strategy is valid for http authentication by:
54
+ #
55
+ # * Validating if the model allows params authentication;
56
+ # * If any of the authorization headers were sent;
57
+ # * If all authentication keys are present;
58
+ #
59
+ def valid_for_http_auth?
60
+ http_authenticatable? && request.authorization && with_authentication_hash(:http_auth, http_auth_hash)
61
+ end
62
+
63
+ # Check if this is strategy is valid for params authentication by:
64
+ #
65
+ # * Validating if the model allows params authentication;
66
+ # * If the request hits the sessions controller through POST;
67
+ # * If the params[scope] returns a hash with credentials;
68
+ # * If all authentication keys are present;
69
+ #
70
+ def valid_for_params_auth?
71
+ params_authenticatable? && valid_params_request? &&
72
+ valid_params? && with_authentication_hash(:params_auth, params_auth_hash)
73
+ end
74
+
75
+ # Check if the model accepts this strategy as http authenticatable.
76
+ def http_authenticatable?
77
+ mapping.to.http_authenticatable?(authenticatable_name)
78
+ end
79
+
80
+ # Check if the model accepts this strategy as params authenticatable.
81
+ def params_authenticatable?
82
+ mapping.to.params_authenticatable?(authenticatable_name)
83
+ end
84
+
85
+ # Extract the appropriate subhash for authentication from params.
86
+ def params_auth_hash
87
+ params[scope]
88
+ end
89
+
90
+ # Extract a hash with attributes:values from the http params.
91
+ def http_auth_hash
92
+ keys = [authentication_keys.first, :password]
93
+ Hash[*keys.zip(decode_credentials).flatten]
94
+ end
95
+
96
+ # By default, a request is valid if the controller set the proper env variable.
97
+ def valid_params_request?
98
+ !!env["devise.allow_params_authentication"]
99
+ end
100
+
101
+ # If the request is valid, finally check if params_auth_hash returns a hash.
102
+ def valid_params?
103
+ params_auth_hash.is_a?(Hash)
104
+ end
105
+
106
+ # Check if password is present and is not equal to "X" (default value for token).
107
+ def valid_password?
108
+ password.present? && password != "X"
109
+ end
110
+
111
+ # Helper to decode credentials from HTTP.
112
+ def decode_credentials
113
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
114
+ Base64.decode64($1).split(/:/, 2)
115
+ end
116
+
117
+ # Sets the authentication hash and the password from params_auth_hash or http_auth_hash.
118
+ def with_authentication_hash(auth_type, auth_values)
119
+ self.authentication_hash, self.authentication_type = {}, auth_type
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)
124
+ end
125
+
126
+ # Holds the authentication keys.
127
+ def authentication_keys
128
+ @authentication_keys ||= mapping.to.authentication_keys
129
+ end
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
+
156
+ # Holds the authenticatable name for this class. Devise::Strategies::DatabaseAuthenticatable
157
+ # becomes simply :database.
158
+ def authenticatable_name
159
+ @authenticatable_name ||=
160
+ ActiveSupport::Inflector.underscore(self.class.name.split("::").last).
161
+ sub("_authenticatable", "").to_sym
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,15 @@
1
+ module Devise
2
+ module Strategies
3
+ # Base strategy for Devise. Responsible for verifying correct scope and mapping.
4
+ class Base < ::Warden::Strategies::Base
5
+ # Checks if a valid scope was given for devise and find mapping based on this scope.
6
+ def mapping
7
+ @mapping ||= begin
8
+ mapping = Devise.mappings[scope]
9
+ raise "Could not find mapping for #{scope}" unless mapping
10
+ mapping
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Default strategy for signing in a user, based on his email and password in the database.
6
+ class DatabaseAuthenticatable < Authenticatable
7
+ def authenticate!
8
+ resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
+
10
+ if validate(resource){ resource.valid_password?(password) }
11
+ resource.after_database_authentication
12
+ success!(resource)
13
+ elsif !halted?
14
+ fail(:invalid)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ Warden::Strategies.add(:database_authenticatable, Devise::Strategies::DatabaseAuthenticatable)
@@ -0,0 +1,53 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Remember the user through the remember token. This strategy is responsible
6
+ # to verify whether there is a cookie with the remember token, and to
7
+ # recreate the user from this cookie if it exists. Must be called *before*
8
+ # authenticatable.
9
+ class Rememberable < Authenticatable
10
+ # A valid strategy for rememberable needs a remember token in the cookies.
11
+ def valid?
12
+ @remember_cookie = nil
13
+ remember_cookie.present?
14
+ end
15
+
16
+ # To authenticate a user we deserialize the cookie and attempt finding
17
+ # the record in the database. If the attempt fails, we pass to another
18
+ # strategy handle the authentication.
19
+ def authenticate!
20
+ resource = mapping.to.serialize_from_cookie(*remember_cookie)
21
+
22
+ if validate(resource)
23
+ success!(resource)
24
+ elsif !halted?
25
+ cookies.delete(remember_key)
26
+ pass
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def decorate(resource)
33
+ super
34
+ resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
35
+ end
36
+
37
+ def remember_me?
38
+ true
39
+ end
40
+
41
+ def remember_key
42
+ "remember_#{scope}_token"
43
+ end
44
+
45
+ def remember_cookie
46
+ @remember_cookie ||= cookies.signed[remember_key]
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+
53
+ Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)
@@ -0,0 +1,57 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Strategy for signing in a user, based on a authenticatable token. This works for both params
6
+ # and http. For the former, all you need to do is to pass the params in the URL:
7
+ #
8
+ # http://myapp.example.com/?user_token=SECRET
9
+ #
10
+ # For HTTP, you can pass the token as username and blank password. Since some clients may require
11
+ # a password, you can pass "X" as password and it will simply be ignored.
12
+ class TokenAuthenticatable < Authenticatable
13
+ def store?
14
+ super && !mapping.to.skip_session_storage.include?(:token_auth)
15
+ end
16
+
17
+ def authenticate!
18
+ resource = mapping.to.find_for_token_authentication(authentication_hash)
19
+
20
+ if validate(resource)
21
+ resource.after_token_authentication
22
+ success!(resource)
23
+ elsif !halted?
24
+ fail(:invalid_token)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ # Token Authenticatable can be authenticated with params in any controller and any verb.
31
+ def valid_params_request?
32
+ true
33
+ end
34
+
35
+ # Do not use remember_me behavior with token.
36
+ def remember_me?
37
+ false
38
+ end
39
+
40
+ # Try both scoped and non scoped keys.
41
+ def params_auth_hash
42
+ if params[scope].kind_of?(Hash) && params[scope].has_key?(authentication_keys.first)
43
+ params[scope]
44
+ else
45
+ params
46
+ end
47
+ end
48
+
49
+ # Overwrite authentication keys to use token_authentication_key.
50
+ def authentication_keys
51
+ @authentication_keys ||= [mapping.to.token_authentication_key]
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Warden::Strategies.add(:token_authenticatable, Devise::Strategies::TokenAuthenticatable)
@@ -0,0 +1,130 @@
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.
9
+ module TestHelpers
10
+ def self.included(base)
11
+ base.class_eval do
12
+ setup :setup_controller_for_warden, :warden if respond_to?(:setup)
13
+ end
14
+ end
15
+
16
+ # Override process to consider warden.
17
+ def process(*)
18
+ # Make sure we always return @response, a la ActionController::TestCase::Behaviour#process, even if warden interrupts
19
+ _catch_warden { super } || @response
20
+ end
21
+
22
+ # We need to setup the environment variables and the response in the controller.
23
+ def setup_controller_for_warden #:nodoc:
24
+ @request.env['action_controller.instance'] = @controller
25
+ end
26
+
27
+ # Quick access to Warden::Proxy.
28
+ def warden #:nodoc:
29
+ @warden ||= begin
30
+ manager = Warden::Manager.new(nil) do |config|
31
+ config.merge! Devise.warden_config
32
+ end
33
+ @request.env['warden'] = Warden::Proxy.new(@request.env, manager)
34
+ end
35
+ end
36
+
37
+ # sign_in a given resource by storing its keys in the session.
38
+ # This method bypass any warden authentication callback.
39
+ #
40
+ # Examples:
41
+ #
42
+ # sign_in :user, @user # sign_in(scope, resource)
43
+ # sign_in @user # sign_in(resource)
44
+ #
45
+ def sign_in(resource_or_scope, resource=nil)
46
+ scope ||= Devise::Mapping.find_scope!(resource_or_scope)
47
+ resource ||= resource_or_scope
48
+ warden.session_serializer.store(resource, scope)
49
+ end
50
+
51
+ # Sign out a given resource or scope by calling logout on Warden.
52
+ # This method bypass any warden logout callback.
53
+ #
54
+ # Examples:
55
+ #
56
+ # sign_out :user # sign_out(scope)
57
+ # sign_out @user # sign_out(resource)
58
+ #
59
+ def sign_out(resource_or_scope)
60
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
61
+ @controller.instance_variable_set(:"@current_#{scope}", nil)
62
+ user = warden.instance_variable_get(:@users).delete(scope)
63
+ warden.session_serializer.delete(scope, user)
64
+ end
65
+
66
+ protected
67
+
68
+ # Catch warden continuations and handle like the middleware would.
69
+ # Returns nil when interrupted, otherwise the normal result of the block.
70
+ def _catch_warden(&block)
71
+ result = catch(:warden, &block)
72
+
73
+ env = @controller.request.env
74
+
75
+ result ||= {}
76
+
77
+ # Set the response. In production, the rack result is returned
78
+ # from Warden::Manager#call, which the following is modelled on.
79
+ case result
80
+ when Array
81
+ if result.first == 401 && intercept_401?(env) # does this happen during testing?
82
+ _process_unauthenticated(env)
83
+ else
84
+ result
85
+ end
86
+ when Hash
87
+ _process_unauthenticated(env, result)
88
+ else
89
+ result
90
+ end
91
+ end
92
+
93
+ def _process_unauthenticated(env, options = {})
94
+ options[:action] ||= :unauthenticated
95
+ proxy = env['warden']
96
+ result = options[:result] || proxy.result
97
+
98
+ ret = case result
99
+ when :redirect
100
+ body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
101
+ [proxy.status, proxy.headers, [body]]
102
+ when :custom
103
+ proxy.custom_response
104
+ else
105
+ env["PATH_INFO"] = "/#{options[:action]}"
106
+ env["warden.options"] = options
107
+ Warden::Manager._run_callbacks(:before_failure, env, options)
108
+
109
+ status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
110
+ @controller.send :render, :status => status, :text => body,
111
+ :content_type => headers["Content-Type"], :location => headers["Location"]
112
+ nil # causes process return @response
113
+ end
114
+
115
+ # ensure that the controller response is set up. In production, this is
116
+ # not necessary since warden returns the results to rack. However, at
117
+ # testing time, we want the response to be available to the testing
118
+ # framework to verify what would be returned to rack.
119
+ if ret.is_a?(Array)
120
+ # ensure the controller response is set to our response.
121
+ @controller.response ||= @response
122
+ @response.status = ret.first
123
+ @response.headers = ret.second
124
+ @response.body = ret.third
125
+ end
126
+
127
+ ret
128
+ end
129
+ end
130
+ end