devise 0.9.2 → 1.0.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 (54) hide show
  1. data/CHANGELOG.rdoc +11 -0
  2. data/README.rdoc +3 -2
  3. data/TODO +0 -1
  4. data/app/controllers/confirmations_controller.rb +18 -7
  5. data/app/controllers/passwords_controller.rb +18 -7
  6. data/app/controllers/registrations_controller.rb +55 -0
  7. data/app/controllers/sessions_controller.rb +17 -5
  8. data/app/controllers/unlocks_controller.rb +18 -7
  9. data/app/models/devise_mailer.rb +3 -2
  10. data/app/views/registrations/edit.html.erb +25 -0
  11. data/app/views/registrations/new.html.erb +17 -0
  12. data/app/views/sessions/new.html.erb +10 -12
  13. data/app/views/shared/_devise_links.erb +5 -1
  14. data/generators/devise_install/templates/devise.rb +3 -1
  15. data/lib/devise.rb +15 -8
  16. data/lib/devise/controllers/helpers.rb +1 -1
  17. data/lib/devise/controllers/internal_helpers.rb +15 -6
  18. data/lib/devise/controllers/url_helpers.rb +7 -7
  19. data/lib/devise/locales/en.yml +6 -0
  20. data/lib/devise/mapping.rb +9 -15
  21. data/lib/devise/models.rb +9 -16
  22. data/lib/devise/models/authenticatable.rb +38 -8
  23. data/lib/devise/models/lockable.rb +42 -24
  24. data/lib/devise/models/registerable.rb +8 -0
  25. data/lib/devise/models/timeoutable.rb +1 -1
  26. data/lib/devise/orm/active_record.rb +3 -2
  27. data/lib/devise/orm/data_mapper.rb +3 -3
  28. data/lib/devise/orm/mongo_mapper.rb +3 -3
  29. data/lib/devise/rails/routes.rb +9 -6
  30. data/lib/devise/strategies/authenticatable.rb +11 -1
  31. data/lib/devise/strategies/base.rb +5 -13
  32. data/lib/devise/strategies/http_authenticatable.rb +49 -0
  33. data/lib/devise/strategies/rememberable.rb +1 -1
  34. data/lib/devise/strategies/token_authenticatable.rb +9 -10
  35. data/lib/devise/version.rb +1 -1
  36. data/test/devise_test.rb +1 -1
  37. data/test/integration/authenticatable_test.rb +59 -43
  38. data/test/integration/http_authenticatable_test.rb +44 -0
  39. data/test/integration/registerable_test.rb +130 -0
  40. data/test/integration/token_authenticatable_test.rb +4 -4
  41. data/test/mailers/confirmation_instructions_test.rb +9 -0
  42. data/test/mapping_test.rb +14 -10
  43. data/test/models/authenticatable_test.rb +31 -9
  44. data/test/models/lockable_test.rb +8 -8
  45. data/test/models_test.rb +1 -1
  46. data/test/rails_app/app/active_record/admin.rb +1 -1
  47. data/test/rails_app/app/active_record/user.rb +4 -2
  48. data/test/rails_app/config/routes.rb +3 -2
  49. data/test/routes_test.rb +35 -0
  50. data/test/support/integration_tests_helper.rb +5 -1
  51. data/test/support/tests_helper.rb +35 -1
  52. metadata +9 -4
  53. data/lib/devise/controllers/common.rb +0 -24
  54. data/test/support/model_tests_helper.rb +0 -36
@@ -20,7 +20,7 @@ module Devise
20
20
  #
21
21
  module ActiveRecord
22
22
  # Required ORM hook. Just yield the given block in ActiveRecord.
23
- def self.included_modules_hook(klass, modules)
23
+ def self.included_modules_hook(klass)
24
24
  yield
25
25
  end
26
26
 
@@ -36,5 +36,6 @@ end
36
36
 
37
37
  if defined?(ActiveRecord)
38
38
  ActiveRecord::Base.extend Devise::Models
39
+ ActiveRecord::ConnectionAdapters::Table.send :include, Devise::Orm::ActiveRecord
39
40
  ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Orm::ActiveRecord
40
- end
41
+ end
@@ -11,13 +11,13 @@ module Devise
11
11
  end
12
12
  end
13
13
 
14
- def self.included_modules_hook(klass, modules)
14
+ def self.included_modules_hook(klass)
15
15
  klass.send :extend, self
16
16
  klass.send :include, InstanceMethods
17
17
 
18
18
  yield
19
19
 
20
- modules.each do |mod|
20
+ klass.devise_modules.each do |mod|
21
21
  klass.send(mod) if klass.respond_to?(mod)
22
22
  end
23
23
  end
@@ -80,4 +80,4 @@ module Devise
80
80
  end
81
81
  end
82
82
 
83
- DataMapper::Model.send(:include, Devise::Models)
83
+ DataMapper::Model.send(:include, Devise::Models)
@@ -11,12 +11,12 @@ module Devise
11
11
  end
12
12
  end
13
13
 
14
- def self.included_modules_hook(klass, modules)
14
+ def self.included_modules_hook(klass)
15
15
  klass.send :extend, self
16
16
  klass.send :include, InstanceMethods
17
17
  yield
18
18
 
19
- modules.each do |mod|
19
+ klass.devise_modules.each do |mod|
20
20
  klass.send(mod) if klass.respond_to?(mod)
21
21
  end
22
22
  end
@@ -47,4 +47,4 @@ module Devise
47
47
  end
48
48
 
49
49
  MongoMapper::Document::ClassMethods.send(:include, Devise::Models)
50
- MongoMapper::EmbeddedDocument::ClassMethods.send(:include, Devise::Models)
50
+ MongoMapper::EmbeddedDocument::ClassMethods.send(:include, Devise::Models)
@@ -88,8 +88,8 @@ module ActionController::Routing
88
88
  route_options = mapping.route_options.merge(:path_prefix => mapping.raw_path, :name_prefix => "#{mapping.name}_")
89
89
 
90
90
  with_options(route_options) do |routes|
91
- mapping.for.each do |strategy|
92
- send(strategy, routes, mapping) if self.respond_to?(strategy, true)
91
+ mapping.for.each do |mod|
92
+ send(mod, routes, mapping) if self.respond_to?(mod, true)
93
93
  end
94
94
  end
95
95
  end
@@ -105,10 +105,6 @@ module ActionController::Routing
105
105
  end
106
106
  end
107
107
 
108
- def recoverable(routes, mapping)
109
- routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
110
- end
111
-
112
108
  def confirmable(routes, mapping)
113
109
  routes.resource :confirmation, :only => [:new, :create, :show], :as => mapping.path_names[:confirmation]
114
110
  end
@@ -117,6 +113,13 @@ module ActionController::Routing
117
113
  routes.resource :unlock, :only => [:new, :create, :show], :as => mapping.path_names[:unlock]
118
114
  end
119
115
 
116
+ def recoverable(routes, mapping)
117
+ routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
118
+ end
119
+
120
+ def registerable(routes, mapping)
121
+ routes.resource :registration, :only => [:new, :create, :edit, :update, :destroy], :as => mapping.raw_path[1..-1], :path_prefix => nil, :path_names => { :new => mapping.path_names[:sign_up] }
122
+ end
120
123
  end
121
124
  end
122
125
  end
@@ -6,7 +6,7 @@ module Devise
6
6
  # Redirects to sign_in page if it's not authenticated
7
7
  class Authenticatable < Base
8
8
  def valid?
9
- super && params[scope] && params[scope][:password].present?
9
+ valid_controller? && valid_params? && mapping.to.respond_to?(:authenticate)
10
10
  end
11
11
 
12
12
  # Authenticate a user based on email and password params, returning to warden
@@ -19,6 +19,16 @@ module Devise
19
19
  fail!(:invalid)
20
20
  end
21
21
  end
22
+
23
+ protected
24
+
25
+ def valid_controller?
26
+ params[:controller] == 'sessions'
27
+ end
28
+
29
+ def valid_params?
30
+ params[scope] && params[scope][:password].present?
31
+ end
22
32
  end
23
33
  end
24
34
  end
@@ -2,22 +2,14 @@ module Devise
2
2
  module Strategies
3
3
  # Base strategy for Devise. Responsible for verifying correct scope and mapping.
4
4
  class Base < ::Warden::Strategies::Base
5
- # Validate strategy. By default will raise an error if no scope or an
6
- # invalid mapping is found.
7
- def valid?
8
- raise "Could not find mapping for #{scope}" unless mapping
9
- mapping.for.include?(klass_type)
10
- end
11
-
12
5
  # Checks if a valid scope was given for devise and find mapping based on
13
6
  # this scope.
14
7
  def mapping
15
- Devise.mappings[scope]
16
- end
17
-
18
- # Store this class type.
19
- def klass_type
20
- @klass_type ||= self.class.name.split("::").last.underscore.to_sym
8
+ @mapping ||= begin
9
+ mapping = Devise.mappings[scope]
10
+ raise "Could not find mapping for #{scope}" unless mapping
11
+ mapping
12
+ end
21
13
  end
22
14
  end
23
15
  end
@@ -0,0 +1,49 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Sign in an user using HTTP authentication.
6
+ class HttpAuthenticatable < Base
7
+ def valid?
8
+ http_authentication? && mapping.to.respond_to?(:authenticate_with_http)
9
+ end
10
+
11
+ def authenticate!
12
+ username, password = username_and_password
13
+
14
+ if resource = mapping.to.authenticate_with_http(username, password)
15
+ success!(resource)
16
+ else
17
+ custom!([401, custom_headers, ["HTTP Basic: Access denied.\n"]])
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def username_and_password
24
+ decode_credentials(request).split(/:/, 2)
25
+ end
26
+
27
+ def http_authentication
28
+ request.env['HTTP_AUTHORIZATION'] ||
29
+ request.env['X-HTTP_AUTHORIZATION'] ||
30
+ request.env['X_HTTP_AUTHORIZATION'] ||
31
+ request.env['REDIRECT_X_HTTP_AUTHORIZATION']
32
+ end
33
+ alias :http_authentication? :http_authentication
34
+
35
+ def decode_credentials(request)
36
+ ActiveSupport::Base64.decode64(http_authentication.split(' ', 2).last || '')
37
+ end
38
+
39
+ def custom_headers
40
+ {
41
+ "Content-Type" => "text/plain",
42
+ "WWW-Authenticate" => %(Basic realm="#{Devise.http_authentication_realm.gsub(/"/, "")}")
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ Warden::Strategies.add(:http_authenticatable, Devise::Strategies::HttpAuthenticatable)
@@ -10,7 +10,7 @@ module Devise
10
10
 
11
11
  # A valid strategy for rememberable needs a remember token in the cookies.
12
12
  def valid?
13
- super && remember_me_cookie.present?
13
+ remember_me_cookie.present? && mapping.to.respond_to?(:serialize_from_cookie)
14
14
  end
15
15
 
16
16
  # To authenticate a user we deserialize the cookie and attempt finding
@@ -6,7 +6,7 @@ module Devise
6
6
  # Redirects to sign_in page if it's not authenticated.
7
7
  class TokenAuthenticatable < Base
8
8
  def valid?
9
- super && authentication_token(scope).present?
9
+ mapping.to.respond_to?(:authenticate_with_token) && authentication_token(scope).present?
10
10
  end
11
11
 
12
12
  # Authenticate a user based on authenticatable token params, returning to warden
@@ -20,17 +20,16 @@ module Devise
20
20
  end
21
21
  end
22
22
 
23
- private
23
+ private
24
24
 
25
- # Detect authentication token in params: scoped or not.
26
- def authentication_token(scope)
27
- if params[scope]
28
- params[scope][mapping.to.token_authentication_key]
29
- else
30
- params[mapping.to.token_authentication_key]
31
- end
25
+ # Detect authentication token in params: scoped or not.
26
+ def authentication_token(scope)
27
+ if params[scope]
28
+ params[scope][mapping.to.token_authentication_key]
29
+ else
30
+ params[mapping.to.token_authentication_key]
32
31
  end
33
-
32
+ end
34
33
  end
35
34
  end
36
35
  end
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "0.9.2".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -25,7 +25,7 @@ class DeviseTest < ActiveSupport::TestCase
25
25
  Devise.configure_warden(config)
26
26
 
27
27
  assert_equal Devise::FailureApp, config.failure_app
28
- assert_equal [:rememberable, :token_authenticatable, :authenticatable], config.default_strategies
28
+ assert_equal [:rememberable, :http_authenticatable, :token_authenticatable, :authenticatable], config.default_strategies
29
29
  assert_equal :user, config.default_scope
30
30
  assert config.silence_missing_strategies?
31
31
  end
@@ -1,8 +1,7 @@
1
1
  require 'test/test_helper'
2
2
 
3
- class AuthenticationTest < ActionController::IntegrationTest
4
-
5
- test 'home should be accessible without signed in' do
3
+ class AuthenticationSanityTest < ActionController::IntegrationTest
4
+ test 'home should be accessible without sign in' do
6
5
  visit '/'
7
6
  assert_response :success
8
7
  assert_template 'home/index'
@@ -76,14 +75,47 @@ class AuthenticationTest < ActionController::IntegrationTest
76
75
  assert_contain 'Welcome Admin'
77
76
  end
78
77
 
79
- test 'sign in as user should not authenticate if not using proper authentication keys' do
78
+ test 'authenticated admin should not be able to sign as admin again' do
79
+ sign_in_as_admin
80
+ get new_admin_session_path
81
+
82
+ assert_response :redirect
83
+ assert_redirected_to admin_root_path
84
+ assert warden.authenticated?(:admin)
85
+ end
86
+
87
+ test 'authenticated admin should be able to sign out' do
88
+ sign_in_as_admin
89
+ assert warden.authenticated?(:admin)
90
+
91
+ get destroy_admin_session_path
92
+ assert_response :redirect
93
+ assert_redirected_to root_path
94
+
95
+ get root_path
96
+ assert_contain 'Signed out successfully'
97
+ assert_not warden.authenticated?(:admin)
98
+ end
99
+
100
+ test 'unauthenticated admin does not set message on sign out' do
101
+ get destroy_admin_session_path
102
+ assert_response :redirect
103
+ assert_redirected_to root_path
104
+
105
+ get root_path
106
+ assert_not_contain 'Signed out successfully'
107
+ end
108
+ end
109
+
110
+ class AuthenticationTest < ActionController::IntegrationTest
111
+ test 'sign in should not authenticate if not using proper authentication keys' do
80
112
  swap Devise, :authentication_keys => [:username] do
81
113
  sign_in_as_user
82
114
  assert_not warden.authenticated?(:user)
83
115
  end
84
116
  end
85
117
 
86
- test 'admin signing in with invalid email should return to sign in form with error message' do
118
+ test 'sign in with invalid email should return to sign in form with error message' do
87
119
  sign_in_as_admin do
88
120
  fill_in 'email', :with => 'wrongemail@test.com'
89
121
  end
@@ -92,7 +124,7 @@ class AuthenticationTest < ActionController::IntegrationTest
92
124
  assert_not warden.authenticated?(:admin)
93
125
  end
94
126
 
95
- test 'admin signing in with invalid pasword should return to sign in form with error message' do
127
+ test 'sign in with invalid pasword should return to sign in form with error message' do
96
128
  sign_in_as_admin do
97
129
  fill_in 'password', :with => 'abcdef'
98
130
  end
@@ -113,37 +145,6 @@ class AuthenticationTest < ActionController::IntegrationTest
113
145
  end
114
146
  end
115
147
 
116
- test 'authenticated admin should not be able to sign as admin again' do
117
- sign_in_as_admin
118
- get new_admin_session_path
119
-
120
- assert_response :redirect
121
- assert_redirected_to admin_root_path
122
- assert warden.authenticated?(:admin)
123
- end
124
-
125
- test 'authenticated admin should be able to sign out' do
126
- sign_in_as_admin
127
- assert warden.authenticated?(:admin)
128
-
129
- get destroy_admin_session_path
130
- assert_response :redirect
131
- assert_redirected_to root_path
132
-
133
- get root_path
134
- assert_contain 'Signed out successfully'
135
- assert_not warden.authenticated?(:admin)
136
- end
137
-
138
- test 'unauthenticated admin does not set message on sign out' do
139
- get destroy_admin_session_path
140
- assert_response :redirect
141
- assert_redirected_to root_path
142
-
143
- get root_path
144
- assert_not_contain 'Signed out successfully'
145
- end
146
-
147
148
  test 'redirect from warden shows sign in or sign up message' do
148
149
  get admins_path
149
150
 
@@ -194,20 +195,21 @@ class AuthenticationTest < ActionController::IntegrationTest
194
195
  assert_equal "/admin_area/home", @request.path
195
196
  end
196
197
 
197
- test 'allows session to be set by a given scope' do
198
+ test 'destroyed account is signed out' do
198
199
  sign_in_as_user
199
200
  visit 'users/index'
200
- assert_equal "Cart", @controller.user_session[:cart]
201
- end
202
201
 
203
- test 'destroyed account is logged out' do
204
- sign_in_as_user
205
- visit 'users/index'
206
202
  User.destroy_all
207
203
  visit 'users/index'
208
204
  assert_redirected_to '/users/sign_in?unauthenticated=true'
209
205
  end
210
206
 
207
+ test 'allows session to be set by a given scope' do
208
+ sign_in_as_user
209
+ visit 'users/index'
210
+ assert_equal "Cart", @controller.user_session[:cart]
211
+ end
212
+
211
213
  test 'renders the scoped view if turned on and view is available' do
212
214
  swap Devise, :scoped_views => true do
213
215
  assert_raise Webrat::NotFoundError do
@@ -217,6 +219,20 @@ class AuthenticationTest < ActionController::IntegrationTest
217
219
  end
218
220
  end
219
221
 
222
+ test 'renders the scoped view if turned on in an specific controller' do
223
+ begin
224
+ SessionsController.scoped_views = true
225
+ assert_raise Webrat::NotFoundError do
226
+ sign_in_as_user
227
+ end
228
+
229
+ assert_match /Special user view/, response.body
230
+ assert !PasswordsController.scoped_views
231
+ ensure
232
+ SessionsController.send :remove_instance_variable, :@scoped_views
233
+ end
234
+ end
235
+
220
236
  test 'does not render the scoped view if turned off' do
221
237
  swap Devise, :scoped_views => false do
222
238
  assert_nothing_raised do
@@ -0,0 +1,44 @@
1
+ require 'test/test_helper'
2
+
3
+ class HttpAuthenticationTest < ActionController::IntegrationTest
4
+
5
+ test 'sign in should authenticate with http' do
6
+ sign_in_as_new_user_with_http
7
+ assert_response :success
8
+ assert_template 'users/index'
9
+ assert_contain 'Welcome'
10
+ assert warden.authenticated?(:user)
11
+ end
12
+
13
+ test 'returns a custom response with www-authenticate header on failures' do
14
+ sign_in_as_new_user_with_http("unknown")
15
+ assert_equal 401, status
16
+ assert_equal 'Basic realm="Application"', headers["WWW-Authenticate"]
17
+ end
18
+
19
+ test 'returns a custom response with www-authenticate and chosen realm' do
20
+ swap Devise, :http_authentication_realm => "MyApp" do
21
+ sign_in_as_new_user_with_http("unknown")
22
+ assert_equal 401, status
23
+ assert_equal 'Basic realm="MyApp"', headers["WWW-Authenticate"]
24
+ end
25
+ end
26
+
27
+ test 'sign in should authenticate with http even with specific authentication keys' do
28
+ swap Devise, :authentication_keys => [:username] do
29
+ sign_in_as_new_user_with_http "usertest"
30
+ assert_response :success
31
+ assert_template 'users/index'
32
+ assert_contain 'Welcome'
33
+ assert warden.authenticated?(:user)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def sign_in_as_new_user_with_http(username="user@test.com", password="123456")
40
+ user = create_user
41
+ get users_path, {}, :authorization => "Basic #{ActiveSupport::Base64.encode64("#{username}:#{password}")}"
42
+ user
43
+ end
44
+ end