Compare commits

...

142 Commits

Author SHA1 Message Date
José Valim
7a2e018df8 Release 1.4.2. 2011-06-30 15:20:13 -03:00
José Valim
1fd4beb18a Move simple_form_for directories to a new location. 2011-06-30 15:09:54 -03:00
José Valim
33d7644b4f Provide a more robust behavior to serializers and add :force_except option 2011-06-30 10:43:33 -03:00
José Valim
5a98e4f4e8 Temporary fix for 3-1-stable that does not implement key? on cookie jar, closes #1179 2011-06-30 08:04:32 -03:00
José Valim
82ae53dd75 Released v1.4.1. 2011-06-29 20:26:35 -03:00
José Valim
07db04e773 Work around AR that does not call serializable_hash on to_xml. 2011-06-29 20:18:10 -03:00
José Valim
6a8ee475fd This was fixed in mongoid, closes #770. 2011-06-29 20:18:10 -03:00
José Valim
767a7a7c21 Ensure handle_unverified_request clean up any cached signed-in user 2011-06-29 20:18:10 -03:00
José Valim
a3b56f7eb1 Merge pull request #1175 from jhllnd/patch-1
Added :registerable to the examples
2011-06-28 04:58:51 -07:00
jhllnd
5e1a998cde Added :registerable to the examples 2011-06-28 03:19:00 -07:00
José Valim
219fbc0056 Fix tests, remove whitespace and update CHANGELOG. 2011-06-28 07:05:35 -03:00
José Valim
dcee6413f4 Merge pull request #1173 from zedtux/master
Internationalization of resource_name in DeviseHelper#devise_error_messages!
2011-06-28 02:58:15 -07:00
Guillaume Hain
e2895239d8 Internationalization of resource_name in DeviseHelper#devise_error_messages! 2011-06-27 20:31:03 +02:00
José Valim
c8077c8d9f Add more docs to routes. 2011-06-27 13:04:40 -03:00
José Valim
46c2d5a8f2 Update CHANGELOG. 2011-06-27 12:16:06 -03:00
José Valim
adb127bb3e Default to delete on new templates. 2011-06-27 12:15:23 -03:00
José Valim
42f0285278 Extract mailer functionality, closes #1164 2011-06-27 12:13:00 -03:00
José Valim
13ef23f517 Add simple form generators. 2011-06-27 11:50:28 -03:00
José Valim
0bf28b19ab Simple use hook_for. 2011-06-27 11:31:29 -03:00
José Valim
29a0af78bc Refactor view generators. 2011-06-27 11:22:44 -03:00
José Valim
cf487c771e Merge pull request #1172 from bjnord/format-false
More transparent implementation of :format => false
2011-06-27 05:15:05 -07:00
Brent J. Nordquist
7e711089a8 More transparent implementat'n of :format => false
Per josevalim, by setting {:format => false} in @scope[:options],
Rails will pick it up, without the need to alter each devise_*()
method individually.
2011-06-27 06:51:04 -05:00
Brent J. Nordquist
1c81a40a76 Support options[:defaults] and @scope[:defaults]
similar to support for :constraints
2011-06-26 19:48:20 -05:00
Rodrigo Flores
dba8f91f99 \o/ Bumping to 1.4.0 \o/ 2011-06-23 09:53:05 -03:00
José Valim
8e70e71143 Update CHANGELOG. 2011-06-23 09:42:32 -03:00
José Valim
f43a7c4f24 Merge pull request #1147 from sj26/master
Authenticated Route Constraints
2011-06-23 05:29:17 -07:00
José Valim
4e56d92ed8 Override serializable_hash instead of to_xml and to_json. 2011-06-23 09:25:50 -03:00
José Valim
5645d733ba Update CHANGELOG. 2011-06-23 09:22:30 -03:00
José Valim
42b07f1f47 Merge remote branch 'origin/issue_json_response'
Conflicts:
	app/controllers/devise/passwords_controller.rb
2011-06-23 09:16:04 -03:00
Samuel Cochran
e75354b3b0 Tests. 2011-06-23 10:44:46 +08:00
Samuel Cochran
80122856c4 Switch to Warden::Proxy#authenticate?
Warden::Proxy#authenticated? and Warden::Proxy#unauthenticated? don't try strategies first.
2011-06-23 10:44:23 +08:00
Rodrigo Flores
308238ab42 Not responding the object on creating unlockable requests 2011-06-22 23:04:55 -03:00
Rodrigo Flores
e937cf78cf Added a test for empty json on unlockable controller response 2011-06-22 23:00:15 -03:00
Rodrigo Flores
2ae2fdc9f8 Returning an empty JSON for creating confirmation requests 2011-06-22 21:27:20 -03:00
Rodrigo Flores
3184834d72 Added tests for confirmable 2011-06-22 21:26:32 -03:00
Rodrigo Flores
8ef5534d92 Moved the test to after the XML tests 2011-06-22 21:04:50 -03:00
Rodrigo Flores
41bb96be5d Returning empty json and empty xml 2011-06-22 19:38:21 -03:00
Rodrigo Flores
b37055fa47 Added a test to return empty json 2011-06-22 19:38:21 -03:00
José Valim
515b727f52 Change default rake. 2011-06-22 15:39:50 -03:00
José Valim
77d6d1e8c2 Do not try to authenticate with no_input_strategies are empty. 2011-06-22 15:28:49 -03:00
José Valim
04b614ce10 Edited app/controllers/devise/passwords_controller.rb via GitHub 2011-06-22 11:03:08 -07:00
José Valim
62e11ddbe4 Update CHANGELOG. 2011-06-22 13:07:19 -03:00
Rodrigo Flores
8e3ab5921f Merge branch 'paranoid-mode' 2011-06-22 12:57:37 -03:00
Rodrigo Flores
1d87997647 Improved paranoid mode generator explanation 2011-06-22 12:52:11 -03:00
Rodrigo Flores
b58e0ef991 Removed useless blank lines 2011-06-22 12:52:11 -03:00
Rodrigo Flores
db959da247 Added a commented line for paranoid mode on generator 2011-06-22 12:52:11 -03:00
Rodrigo Flores
6199252011 Refactored paranoid mode actions for it to appear once 2011-06-22 12:52:11 -03:00
Rodrigo Flores
40c0a7b50b Added paranoid mode to lockable 2011-06-22 12:52:11 -03:00
Rodrigo Flores
422bc4aaa6 Added tests for lockable 2011-06-22 12:52:11 -03:00
Rodrigo Flores
225c89fbf1 Paranoid mode on confirmable 2011-06-22 12:52:10 -03:00
Rodrigo Flores
76281ff15c Added tests for paranoid mode on confirmable 2011-06-22 12:52:10 -03:00
Rodrigo Flores
8b54ff699f Refactoring passwords controller 2011-06-22 12:52:10 -03:00
Rodrigo Flores
7d160650b8 Using the same url in success and failure 2011-06-22 12:52:10 -03:00
Rodrigo Flores
803e46cda7 Refactoring the paranoid controller 2011-06-22 12:52:10 -03:00
Rodrigo Flores
004fe2ad12 Paranoid mode for recoverable 2011-06-22 12:50:34 -03:00
Rodrigo Flores
09baf2091e Refactored the tests to use swap 2011-06-22 12:50:34 -03:00
Rodrigo Flores
135a94b4f7 Added a test for the paranoid mode on recoverable 2011-06-22 12:50:34 -03:00
José Valim
a41d185768 Merge pull request #1158 from crx/validate_after_failing_pw_change
Run validations even when password change will fail
2011-06-22 02:28:06 -07:00
Chase DuBois
c3432e57b1 Test for previous commit 2011-06-21 20:45:07 -04:00
Chase DuBois
a8d88d193e Run validations even when password change will fail, to show a complete list of errors 2011-06-21 17:55:32 -04:00
Samuel Cochran
471e4d6e72 Oh hey, Warden has API for this. Should probably match the name, too. 2011-06-21 08:36:06 +08:00
Samuel Cochran
14fec4cfb3 Add #authenticated and #not_authenticated route constraints 2011-06-18 15:53:46 +08:00
José Valim
4bcd1c6fcd Add script so we can skip mongoid tests. 2011-06-17 11:04:15 -03:00
José Valim
84e1a89c44 Fix failing tests. 2011-06-17 10:46:39 -03:00
José Valim
e0162df465 Merge pull request #1145 from macmartine/master
Added support for Devise routes to honor constraints.
2011-06-17 06:27:05 -07:00
Mac Martine
7631456047 Added support for Devise routes to honor constraints. 2011-06-16 21:24:20 -07:00
José Valim
2bc51ab2bd Merge pull request #1143 from joshk/patch-1
the script section in the travis yml isn't needed
2011-06-16 08:21:45 -07:00
Josh Kalderimis
d23ecef831 the script section in the travis yml isn't needed as this is the default behavior 2011-06-16 07:44:50 -07:00
José Valim
c0017ce76d Merge pull request #1138 from fschwahn/update_without_password
added update_without_password method, closes #801
2011-06-15 10:45:18 -07:00
José Valim
30f9da9d71 Update CHANGELOG. 2011-06-10 11:14:08 +02:00
José Valim
e242ca0937 Fix failing tests. 2011-06-10 11:10:56 +02:00
José Valim
17596b0dc6 Merge pull request #1126 from swrobel/master
Add option to strip extra whitespace from emails or other fields
2011-06-10 01:45:43 -07:00
Stefan Wrobel
8e87a2d80d Add strip_whitespace_keys which works like case_insensitive_keys but strips whitespace from emails 2011-06-10 01:37:43 -07:00
José Valim
4964f53a42 Merge pull request #1092 from xavier/filterwhitelist
Conditional string conversion of auth params (Closes #1079)
2011-06-08 08:39:53 -07:00
José Valim
6250fa8529 Revert "Merge pull request #1103 from Mab879/master"
This reverts commit 944e1c0378, reversing
changes made to 97659a1193.
2011-06-08 17:37:49 +02:00
José Valim
944e1c0378 Merge pull request #1103 from Mab879/master
Controller Generator
2011-06-08 08:34:07 -07:00
Matthew Burket
4f446241a4 Added Controller Generator and tests to go along with it. 2011-05-30 23:07:20 -05:00
José Valim
97659a1193 Merge pull request #1099 from paulbellamy/master
Changing references to SecureRandom in generators and encryptable_test
2011-05-28 07:52:09 -07:00
Paul Bellamy
0bcf71f8df Rails has removed SecureRandom from ActiveSupport in Rails 3.2,
deprecated

* Changing references in generators and encryptable_test
2011-05-28 14:44:54 +01:00
Xavier Defrang
ab3bb9cf4d Conditional string conversion of auth params (Closes #1079) 2011-05-26 15:45:03 +02:00
José Valim
69126a31db Merge pull request #1052 from Thibaut/email-validation
Don't validate email format and uniqueness unless it's changed
2011-05-26 05:24:45 -07:00
José Valim
03e3803cff Merge pull request #1089 from skojin/master
support for named/dedicated openid
2011-05-24 13:33:32 -07:00
Sergey Kojin
58f8c7c613 support for named omniauth open_id strategies 2011-05-25 00:15:24 +04:00
José Valim
acd1c76c3b Merge pull request #1087 from eMxyzptlk/patch-2
Rails has removed SecureRandom from ActiveSupport in Rails 3.2, deprecated
2011-05-23 23:49:59 -07:00
Wael Nasreddine
4c8cc5e3f3 SecureRandom is available since Ruby 1.8.7, remove the unnecessary defined? check. 2011-05-24 08:29:15 +02:00
Wael Nasreddine
72bb33f962 Rails has remove SecureRandom from ActiveSupport in Rails 3.2, deprecated in 3.1
Since it has been deprecated, this patch favors SecureRandom on ActiveSupport::SecureRandom
2011-05-23 15:34:09 -07:00
José Valim
2e61e52520 Fix failing test. 2011-05-23 18:22:32 +02:00
José Valim
75490d0375 Merge pull request #1085 from pechkin/master
Devise.mailer
2011-05-23 08:05:38 -07:00
Artem Pechenyi
0fbe4cb6b9 fix reference storage to support new and old class cache api 2011-05-23 17:29:10 +03:00
José Valim
d2b3f49384 Merge pull request #1067 from naomik/master
added ability to override redirect_to path after sending reset password instructions
2011-05-20 11:12:35 -07:00
José Valim
136992b8df Merge pull request #1070 from joshk/filter_auth_params_fix
dup the conditions hash before calling filter_auth_params, this fixes an
2011-05-20 11:07:34 -07:00
José Valim
a130f848f2 Let's run on rubinius again. 2011-05-18 14:13:33 -04:00
Josh Kalderimis
e10bc9e3c0 dup the conditions hash before calling filter_auth_params, this fixes an issue with reseting your password when using a custom auth field like login 2011-05-16 14:08:22 -04:00
naomi
d56741ca4f added ability to override redirect_to path after sending reset password instructions 2011-05-13 11:39:11 -05:00
Thibaut
92c9ed2d6c Don't validate email format and uniqueness unless it's changed. 2011-05-07 19:02:44 +02:00
José Valim
50a5ad54ce Merge pull request #1051 from rymai/patch-1
Remove :anybody_signed_in? from the controller helpers
2011-05-06 02:59:59 -07:00
Rémy Coutable
9dcb44ba61 :anybody_signed_in? helper was deprecated in 73669e09c8 and removed in fe5ef25614 but not removed from the #helper_method call. 2011-05-06 02:46:23 -07:00
fabian
8bdc4b544f added update_without_password method 2011-05-05 09:24:21 +02:00
José Valim
e763f843c4 Tests pass on 3.0.7 2011-05-04 19:24:11 +02:00
José Valim
b5732e31c6 Bump to 3.0.7. 2011-05-04 19:24:11 +02:00
José Valim
ea24ffdd51 Merge pull request #1046 from fschwahn/do-not-follow.
respect do not track headers in trackable
2011-05-04 06:29:52 -07:00
fabian
56f7fbf2d1 use 'devise.skip_trackable' instead of DNT headers 2011-05-04 15:21:12 +02:00
fabian
89b53ea39c respect do not track headers in trackable 2011-05-04 14:44:37 +02:00
Hugo Baraúna
9cc69277bc Fixes a little typo. 2011-04-29 11:26:12 -07:00
José Valim
7e79ed6d4e Update CHANGELOG. 2011-04-29 14:17:33 +02:00
José Valim
b5190d8df2 Refactor. 2011-04-29 14:11:47 +02:00
José Valim
b03c93c4fe Merged pull request #1041 from ecarnevale/master.
fix for issues #999: HTTP_ACCEPT=*/* should redirect to the default location
2011-04-29 05:03:50 -07:00
Emanuel Carnevale
8e12ddf7ed fix for issues #999: HTTP_ACCEPT=*/* should redirect to the default location 2011-04-29 13:22:52 +02:00
José Valim
4fd866d113 User cannot access sign up and similar pages if he is already signed in through a cookie or token, closes #1036. 2011-04-29 08:56:57 +02:00
José Valim
a59410a254 password_required? should not affect length validation, closes #1037. 2011-04-29 08:33:33 +02:00
José Valim
b1a27ad633 Allow controllers to be given as symbols. 2011-04-25 10:00:28 +02:00
Chase DuBois
4a3ad7f348 Use updated Mongoid gem and remove #destroyed? 2011-04-25 15:50:00 +08:00
Chase DuBois
89e4ab8a45 Moved check against record deletion into forget_me! method in model; added unit test. 2011-04-25 15:49:59 +08:00
Chase DuBois
fd8e34650e Make copy of resource only for update action 2011-04-25 15:49:59 +08:00
Chase DuBois
e26c8ce499 Use #persisted? instead of #frozen? to check whether #forget_me should attempt to clear remember token from database 2011-04-25 15:49:59 +08:00
José Valim
64cf8a4534 Update CHANGELOG. 2011-04-21 19:20:48 +02:00
José Valim
ae976f60ca Mark the token as expired, because invalid gives no clue of what to do next. 2011-04-21 19:17:33 +02:00
José Valim
f152860bc5 Update CHANGELOG. 2011-04-21 14:00:47 +02:00
José Valim
40153b7422 Add tests to previous commit. 2011-04-21 13:56:10 +02:00
Alexander Dreher
fdfe6a8340 Fixes error on missing reset_password_sent_at column.
If the column is not present, you are unabled to reset your password.
2011-04-21 19:51:12 +08:00
Carlos Antonio da Silva
79c100008a Remove old OmniAuth test helpers 2011-04-19 13:13:29 -03:00
José Valim
f132c7891b Update CHANGELOG, bump to 1.4.0.dev. 2011-04-19 14:24:33 +02:00
José Valim
bdad984ace Use Devise::Email::EXACT_PATTERN. 2011-04-19 14:22:14 +02:00
Rodrigo Flores
a229627a54 Removed failing test cases on e-mail validations 2011-04-19 20:16:20 +08:00
Rodrigo Flores
0b598cf132 Changed the e-mail validation regex 2011-04-19 20:16:19 +08:00
Rodrigo Flores
0e657eece1 Added email validation regexes 2011-04-19 20:16:19 +08:00
José Valim
b6cf1df659 Release 1.3.1. 2011-04-19 10:39:56 +02:00
José Valim
29afe2d21c Other minor improvements in the REST code. 2011-04-19 08:36:52 +02:00
José Valim
a722c6236c to_json does not guarantee the order. 2011-04-18 13:03:31 +02:00
José Valim
14aedc416a Wording. 2011-04-18 10:03:39 +02:00
José Valim
9fe0cb4954 use @example.com 2011-04-18 10:00:00 +02:00
SixArm
2a5669967f Change test email addresses to ues RFC 2606 reserved domain example.com 2011-04-18 15:59:13 +08:00
durrantm
13376d22de Edited README.rdoc via GitHub 2011-04-18 15:58:40 +08:00
José Valim
6b21531916 Update CHANGELOG. 2011-04-18 09:56:44 +02:00
José Valim
4a4dcb30ef sessions/new also responds to xml and json now 2011-04-18 09:56:24 +02:00
José Valim
2cfa58b433 Create shared_helpers for duplicated logic in Devise controllers and failure app. 2011-04-17 19:43:54 +02:00
José Valim
c6dd846718 Move the catch to the test level. 2011-04-17 19:37:19 +02:00
José Valim
7a2d76d002 Update changelog, improve coverage. 2011-04-17 18:06:29 +02:00
Andre Arko
e5a8febe3b Change the XML error tag from <hash> to <errors>. Makes way more sense. 2011-04-17 23:54:06 +08:00
Steve Hodgkiss
60809719b8 Fix bug when the reset_password_sent_at field doesn't exist generate_password_token returns nil causing the token not to be saved. 2011-04-17 23:53:50 +08:00
José Valim
ee6a8ab93a Temporarily remove rubinius because C extensions are not compiling. 2011-04-17 00:57:02 -07:00
Juan M. Cuello
c3d92095f8 Update README.
Replace success/failure with notice/alert in I18n section.
2011-04-17 15:15:49 +08:00
85 changed files with 1518 additions and 385 deletions

View File

@@ -1,7 +1,7 @@
script: "rake test"
script: "bundle exec rake test"
rvm:
- 1.8.7
- 1.9.2
- ree
- rbx
- jruby
- jruby
- rubinius

View File

@@ -1,3 +1,62 @@
== 1.4.2
* bug fix
* Improve Rails 3.1 compatibility
* Provide a more robust behavior to serializers and add :force_except option
== 1.4.1
* enhancements
* Add :defaults and :format support on router
* Add simple form generators
* Better localization for devise_error_messages! (by github.com/zedtux)
* bug fix
* Ensure to_xml is properly white listened
* Ensure handle_unverified_request clean up any cached signed-in user
== 1.4.0
* enhancements
* Added authenticated and unauthenticated to the router to route the used based on his status (by github.com/sj26)
* Improve e-mail regexp (by github.com/rodrigoflores)
* Add strip_whitespace_keys and default to e-mail (by github.com/swrobel)
* Do not run format and uniqueness validations on e-mail if it hasn't changed (by github.com/Thibaut)
* Added update_without_password to update models but not allowing the password to change (by github.com/fschwahn)
* Added config.paranoid, check the generator for more information (by github.com/rodrigoflores)
* bug fix
* password_required? should not affect length validation
* User cannot access sign up and similar pages if he is already signed in through a cookie or token
* Do not convert booleans to strings on finders (by github.com/xavier)
* Run validations even if current_password fails (by github.com/crx)
* Devise now honors routes constraints (by github.com/macmartine)
* Do not return the user resource when requesting instructions (by github.com/rodrigoflores)
== 1.3.4
* bug fix
* Do not add formats if html or "*/*"
== 1.3.3
* bug fix
* Explicitly mark the token as expired if so
== 1.3.2
* bug fix
* Fix another regression related to reset_password_sent_at (by github.com/alexdreher)
== 1.3.1
* enhancements
* Improve failure_app responses (by github.com/indirect)
* sessions/new and registrations/new also respond to xml and json now
* bug fix
* Fix a regression that occurred if reset_password_sent_at is not present (by github.com/stevehodgkiss)
== 1.3.0
* enhancements

23
Gemfile
View File

@@ -2,7 +2,7 @@ source "http://rubygems.org"
gemspec
gem "rails", "~> 3.0.4"
gem "rails", "~> 3.0.7"
gem "oa-oauth", '~> 0.2.0', :require => "omniauth/oauth"
gem "oa-openid", '~> 0.2.0', :require => "omniauth/openid"
@@ -15,15 +15,18 @@ platforms :jruby do
gem 'activerecord-jdbcsqlite3-adapter'
end
platforms :ruby do
platforms :mri_18 do
group :test do
gem "sqlite3-ruby"
gem "ruby-debug", ">= 0.10.3" if RUBY_VERSION < '1.9'
end
group :mongoid do
gem "mongo", "1.1.2"
gem "mongoid", "2.0.0.beta.20"
gem "bson_ext", "1.2.1"
gem "ruby-debug", ">= 0.10.3"
end
end
platforms :ruby do
gem "sqlite3-ruby"
group :mongoid do
gem "mongo", "~> 1.3.0"
gem "mongoid", "2.0.1"
gem "bson_ext", "~> 1.3.0"
end
end

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
devise (1.3.0.dev)
devise (1.4.1)
bcrypt-ruby (~> 2.1.2)
orm_adapter (~> 0.0.3)
warden (~> 1.0.3)
@@ -10,41 +10,41 @@ GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.4)
actionpack (= 3.0.4)
actionmailer (3.0.7)
actionpack (= 3.0.7)
mail (~> 2.2.15)
actionpack (3.0.4)
activemodel (= 3.0.4)
activesupport (= 3.0.4)
actionpack (3.0.7)
activemodel (= 3.0.7)
activesupport (= 3.0.7)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.4)
i18n (~> 0.5.0)
rack (~> 1.2.1)
rack-mount (~> 0.6.13)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.4)
activesupport (= 3.0.4)
activemodel (3.0.7)
activesupport (= 3.0.7)
builder (~> 2.1.2)
i18n (~> 0.4)
activerecord (3.0.4)
activemodel (= 3.0.4)
activesupport (= 3.0.4)
i18n (~> 0.5.0)
activerecord (3.0.7)
activemodel (= 3.0.7)
activesupport (= 3.0.7)
arel (~> 2.0.2)
tzinfo (~> 0.3.23)
activerecord-jdbc-adapter (1.1.1)
activerecord-jdbcsqlite3-adapter (1.1.1)
activerecord-jdbc-adapter (= 1.1.1)
jdbc-sqlite3 (~> 3.6.0)
activeresource (3.0.4)
activemodel (= 3.0.4)
activesupport (= 3.0.4)
activesupport (3.0.4)
activeresource (3.0.7)
activemodel (= 3.0.7)
activesupport (= 3.0.7)
activesupport (3.0.7)
addressable (2.2.4)
arel (2.0.8)
arel (2.0.9)
bcrypt-ruby (2.1.4)
bson (1.2.1)
bson_ext (1.2.1)
bson (1.3.0)
bson_ext (1.3.0)
builder (2.1.2)
columnize (0.3.2)
erubis (2.6.6)
@@ -56,24 +56,24 @@ GEM
i18n (0.5.0)
jdbc-sqlite3 (3.6.14.2.056-java)
linecache (0.43)
mail (2.2.15)
mail (2.2.19)
activesupport (>= 2.3.6)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.16)
mocha (0.9.12)
mongo (1.1.2)
bson (>= 1.1.1)
mongoid (2.0.0.beta.20)
mongo (1.3.0)
bson (>= 1.3.0)
mongoid (2.0.1)
activemodel (~> 3.0)
mongo (~> 1.1)
mongo (~> 1.3)
tzinfo (~> 0.3.22)
will_paginate (~> 3.0.pre)
multi_json (0.0.5)
multipart-post (1.1.0)
nokogiri (1.4.4)
nokogiri (1.4.4-java)
nokogiri (1.4.3.1)
nokogiri (1.4.3.1-java)
weakling (>= 0.0.3)
oa-core (0.2.0)
rack (~> 1.1)
@@ -91,27 +91,27 @@ GEM
oauth2 (0.1.1)
faraday (~> 0.5.0)
multi_json (~> 0.0.4)
orm_adapter (0.0.4)
orm_adapter (0.0.5)
polyglot (0.3.1)
rack (1.2.1)
rack-mount (0.6.13)
rack (1.2.2)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-openid (1.2.0)
rack (>= 1.1.0)
ruby-openid (>= 2.1.8)
rack-test (0.5.7)
rack (>= 1.0)
rails (3.0.4)
actionmailer (= 3.0.4)
actionpack (= 3.0.4)
activerecord (= 3.0.4)
activeresource (= 3.0.4)
activesupport (= 3.0.4)
rails (3.0.7)
actionmailer (= 3.0.7)
actionpack (= 3.0.7)
activerecord (= 3.0.7)
activeresource (= 3.0.7)
activesupport (= 3.0.7)
bundler (~> 1.0)
railties (= 3.0.4)
railties (3.0.4)
actionpack (= 3.0.4)
activesupport (= 3.0.4)
railties (= 3.0.7)
railties (3.0.7)
actionpack (= 3.0.7)
activesupport (= 3.0.7)
rake (>= 0.8.7)
thor (~> 0.14.4)
rake (0.8.7)
@@ -129,9 +129,9 @@ GEM
thor (0.14.6)
treetop (1.4.9)
polyglot (>= 0.3.1)
tzinfo (0.3.24)
warden (1.0.3)
rack (>= 1.0.0)
tzinfo (0.3.27)
warden (1.0.4)
rack (>= 1.0)
weakling (0.0.4-java)
webrat (0.7.2)
nokogiri (>= 1.2.0)
@@ -145,14 +145,14 @@ PLATFORMS
DEPENDENCIES
activerecord-jdbcsqlite3-adapter
bson_ext (= 1.2.1)
bson_ext (~> 1.3.0)
devise!
mocha
mongo (= 1.1.2)
mongoid (= 2.0.0.beta.20)
mongo (~> 1.3.0)
mongoid (= 2.0.1)
oa-oauth (~> 0.2.0)
oa-openid (~> 0.2.0)
rails (~> 3.0.4)
rails (~> 3.0.7)
ruby-debug (>= 0.10.3)
sqlite3-ruby
webrat (= 0.7.2)

View File

@@ -86,7 +86,7 @@ The generator will install an initializer which describes ALL Devise's configura
rails generate devise MODEL
Replace MODEL by the class name you want to add devise, like User, Admin, etc. This will create a model (if one does not exist) and configure it with default Devise modules. The generator will also create a migration file (if your ORM support them) and configure your routes. Continue reading this file to understand exactly what the generator produces and how to use it.
Replace MODEL by the class name used for the applications users, it's frequently 'User' but could also be 'Admin'. This will create a model (if one does not exist) and configure it with default Devise modules. Next, you'll usually run db:migrate as the generator will have created a migration file (if your ORM supports them). This generator also configures your config/routes.rb file, continue reading this file to understand exactly what the generator produces and how to use it.
Support for Rails 2.3.x can be found by installing Devise 1.0.x from the v1.0 branch.
@@ -108,7 +108,7 @@ Devise must be set up within the model (or models) you want to use. Devise route
We're assuming here you want a User model with some Devise modules, as outlined below:
class User < ActiveRecord::Base
devise :database_authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
end
After you choose which modules to use, you need to set up your migrations. Luckily, Devise has some helpers to save you from this boring work:
@@ -174,7 +174,7 @@ Notice that if your devise model is not called "user" but "member", then the hel
The devise method in your models also accepts some options to configure its modules. For example, you can choose which encryptor to use in database_authenticatable:
devise :database_authenticatable, :confirmable, :recoverable, :stretches => 20
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :stretches => 20
Besides :stretches, you can define :pepper, :encryptor, :confirm_within, :remember_for, :timeout_in, :unlock_in and other values. For details, see the initializer file that was created when you invoked the "devise:install" generator described above.
@@ -259,7 +259,7 @@ Feel free to choose the one you prefer!
=== I18n
Devise uses flash messages with I18n with the flash keys :success and :failure. To customize your app, you can set up your locale file:
Devise uses flash messages with I18n with the flash keys :notice and :alert. To customize your app, you can set up your locale file:
en:
devise:

View File

@@ -4,7 +4,7 @@ require 'rake/testtask'
require 'rake/rdoctask'
desc 'Default: run tests for all ORMs.'
task :default => :pre_commit
task :default => :test
desc 'Run Devise tests for all ORMs.'
task :pre_commit do

View File

@@ -11,9 +11,9 @@ class Devise::ConfirmationsController < ApplicationController
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name])
if resource.errors.empty?
if successful_and_sane?(resource)
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, :location => after_resending_confirmation_instructions_path_for(resource_name)
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
else
respond_with_navigational(resource){ render_with_scope :new }
end

View File

@@ -12,9 +12,9 @@ class Devise::PasswordsController < ApplicationController
def create
self.resource = resource_class.send_reset_password_instructions(params[resource_name])
if resource.errors.empty?
if successful_and_sane?(resource)
set_flash_message(:notice, :send_instructions) if is_navigational_format?
respond_with resource, :location => new_session_path(resource_name)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with_navigational(resource){ render_with_scope :new }
end
@@ -39,4 +39,12 @@ class Devise::PasswordsController < ApplicationController
respond_with_navigational(resource){ render_with_scope :edit }
end
end
protected
# The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
new_session_path(resource_name)
end
end

View File

@@ -5,8 +5,8 @@ class Devise::RegistrationsController < ApplicationController
# GET /resource/sign_up
def new
build_resource({})
render_with_scope :new
resource = build_resource({})
respond_with_navigational(resource){ render_with_scope :new }
end
# POST /resource
@@ -35,7 +35,11 @@ class Devise::RegistrationsController < ApplicationController
end
# PUT /resource
# We need to use a copy of the resource because we don't want to change
# the current user in place.
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
if resource.update_with_password(params[resource_name])
set_flash_message :notice, :updated if is_navigational_format?
sign_in resource_name, resource, :bypass => true
@@ -102,11 +106,9 @@ class Devise::RegistrationsController < ApplicationController
end
end
# Authenticates the current scope and gets a copy of the current resource.
# We need to use a copy because we don't want actions like update changing
# the current user in place.
# Authenticates the current scope and gets the current resource from the session.
def authenticate_scope!
send(:"authenticate_#{resource_name}!", true)
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
self.resource = send(:"current_#{resource_name}")
end
end

View File

@@ -4,24 +4,14 @@ class Devise::SessionsController < ApplicationController
# GET /resource/sign_in
def new
clean_up_passwords(build_resource)
render_with_scope :new
resource = build_resource
clean_up_passwords(resource)
respond_with_navigational(resource, stub_options(resource)){ render_with_scope :new }
end
# POST /resource/sign_in
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
# In the running app, the previous line would actually cause this method to
# exit by throwing `:warden` if the authentication failed. Unfortunately,
# this doesn't happen in the Rails test environment if you have included the
# Devise::TestHelpers (see `Devise::TestHelpers::TestWarden#authenticate!`),
# which makes it difficult to unit test extensions to this controller. Since
# the resource is nil if authentication fails, just short-circuit the method
# in that case. This should not affect the running app.
return if resource.nil?
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
@@ -44,4 +34,12 @@ class Devise::SessionsController < ApplicationController
end
end
end
end
protected
def stub_options(resource)
array = resource_class.authentication_keys.dup
array << :password if resource.respond_to?(:password)
{ :methods => array, :only => [:password] }
end
end

View File

@@ -12,9 +12,9 @@ class Devise::UnlocksController < ApplicationController
def create
self.resource = resource_class.send_unlock_instructions(params[resource_name])
if resource.errors.empty?
if successful_and_sane?(resource)
set_flash_message :notice, :send_instructions if is_navigational_format?
respond_with resource, :location => new_session_path(resource_name)
respond_with({}, :location => new_session_path(resource_name))
else
respond_with_navigational(resource){ render_with_scope :new }
end

View File

@@ -11,7 +11,7 @@ module DeviseHelper
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count,
:resource => resource_name)
:resource => resource.class.model_name.human.downcase)
html = <<-HTML
<div id="error_explanation">

View File

@@ -1,88 +1,15 @@
class Devise::Mailer < ::ActionMailer::Base
include Devise::Controllers::ScopedViews
attr_reader :scope_name, :resource
include Devise::Mailers::Helpers
def confirmation_instructions(record)
setup_mail(record, :confirmation_instructions)
devise_mail(record, :confirmation_instructions)
end
def reset_password_instructions(record)
setup_mail(record, :reset_password_instructions)
devise_mail(record, :reset_password_instructions)
end
def unlock_instructions(record)
setup_mail(record, :unlock_instructions)
end
private
# Configure default email options
def setup_mail(record, action)
initialize_from_record(record)
mail headers_for(action)
end
def initialize_from_record(record)
@scope_name = Devise::Mapping.find_scope!(record)
@resource = instance_variable_set("@#{devise_mapping.name}", record)
end
def devise_mapping
@devise_mapping ||= Devise.mappings[scope_name]
end
def headers_for(action)
headers = {
:subject => translate(devise_mapping, action),
:from => mailer_sender(devise_mapping),
:to => resource.email,
:template_path => template_paths
}
if resource.respond_to?(:headers_for)
headers.merge!(resource.headers_for(action))
end
unless headers.key?(:reply_to)
headers[:reply_to] = headers[:from]
end
headers
end
def mailer_sender(mapping)
if Devise.mailer_sender.is_a?(Proc)
Devise.mailer_sender.call(mapping.name)
else
Devise.mailer_sender
end
end
def template_paths
template_path = [self.class.mailer_name]
template_path.unshift "#{@devise_mapping.scoped_path}/mailer" if self.class.scoped_views?
template_path
end
# Setup a subject doing an I18n lookup. At first, it attemps to set a subject
# based on the current mapping:
#
# en:
# devise:
# mailer:
# confirmation_instructions:
# user_subject: '...'
#
# If one does not exist, it fallbacks to ActionMailer default:
#
# en:
# devise:
# mailer:
# confirmation_instructions:
# subject: '...'
#
def translate(mapping, key)
I18n.t(:"#{mapping.name}_subject", :scope => [:devise, :mailer, key],
:default => [:subject, key.to_s.humanize])
devise_mail(record, :unlock_instructions)
end
end

View File

@@ -3,6 +3,7 @@
en:
errors:
messages:
expired: "has expired, please request a new one"
not_found: "not found"
already_confirmed: "was already confirmed, please try signing in"
not_locked: "was not locked"
@@ -26,8 +27,10 @@ en:
passwords:
send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
updated: 'Your password was changed successfully. You are now signed in.'
send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
confirmations:
send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
confirmed: 'Your account was successfully confirmed. You are now signed in.'
registrations:
signed_up: 'Welcome! You have signed up successfully.'
@@ -37,6 +40,7 @@ en:
unlocks:
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
unlocked: 'Your account was successfully unlocked. You are now signed in.'
send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
omniauth_callbacks:
success: 'Successfully authorized from %{kind} account.'
failure: 'Could not authorize you from %{kind} because "%{reason}".'

View File

@@ -3,6 +3,7 @@ require 'active_support/core_ext/numeric/time'
require 'active_support/dependencies'
require 'orm_adapter'
require 'set'
require 'securerandom'
module Devise
autoload :FailureApp, 'devise/failure_app'
@@ -10,12 +11,14 @@ module Devise
autoload :PathChecker, 'devise/path_checker'
autoload :Schema, 'devise/schema'
autoload :TestHelpers, 'devise/test_helpers'
autoload :Email, 'devise/email'
module Controllers
autoload :Helpers, 'devise/controllers/helpers'
autoload :InternalHelpers, 'devise/controllers/internal_helpers'
autoload :Rememberable, 'devise/controllers/rememberable'
autoload :ScopedViews, 'devise/controllers/scoped_views'
autoload :SharedHelpers, 'devise/controllers/shared_helpers'
autoload :UrlHelpers, 'devise/controllers/url_helpers'
end
@@ -28,6 +31,10 @@ module Devise
autoload :Sha1, 'devise/encryptors/sha1'
end
module Mailers
autoload :Helpers, 'devise/mailers/helpers'
end
module Strategies
autoload :Base, 'devise/strategies/base'
autoload :Authenticatable, 'devise/strategies/authenticatable'
@@ -41,6 +48,9 @@ module Devise
STRATEGIES = ActiveSupport::OrderedHash.new
URL_HELPERS = ActiveSupport::OrderedHash.new
# Strategies that do not require user input.
NO_INPUT = []
# True values used to check params
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
@@ -73,6 +83,11 @@ module Devise
# False by default for backwards compatibility.
mattr_accessor :case_insensitive_keys
@@case_insensitive_keys = false
# Keys that should have whitespace stripped.
# False by default for backwards compatibility.
mattr_accessor :strip_whitespace_keys
@@strip_whitespace_keys = false
# If http authentication is enabled by default.
mattr_accessor :http_authenticatable
@@ -90,9 +105,11 @@ module Devise
mattr_accessor :http_authentication_realm
@@http_authentication_realm = "Application"
# Email regex used to validate email formats. Adapted from authlogic.
# Email regex used to validate email formats. Based on RFC 822 and
# retrieved from Sixarm email validation gem
# (https://github.com/SixArm/sixarm_ruby_email_address_validation).
mattr_accessor :email_regexp
@@email_regexp = /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
@@email_regexp = Devise::Email::EXACT_PATTERN
# Range validation for password length
mattr_accessor :password_length
@@ -171,7 +188,7 @@ module Devise
mattr_accessor :reset_password_keys
@@reset_password_keys = [ :email ]
# Time interval you can reset your password with a reset password key
# Time interval you can reset your password with a reset password key
mattr_accessor :reset_password_within
@@reset_password_within = nil
@@ -224,15 +241,30 @@ module Devise
@@warden_config = nil
@@warden_config_block = nil
# When true, enter in paranoid mode to avoid user enumeration.
mattr_accessor :paranoid
@@paranoid = false
# Default way to setup Devise. Run rails generate devise_install to create
# a fresh initializer with all configuration values.
def self.setup
yield self
end
class Getter
def initialize name
@name = name
end
def get
ActiveSupport::Dependencies.constantize(@name)
end
end
def self.ref(arg)
if defined?(ActiveSupport::Dependencies::ClassCache)
ActiveSupport::Dependencies::Reference.store(arg)
ActiveSupport::Dependencies::reference(arg)
Getter.new(arg)
else
ActiveSupport::Dependencies.ref(arg)
end
@@ -244,11 +276,7 @@ module Devise
# Get the mailer class from the mailer reference object.
def self.mailer
if defined?(ActiveSupport::Dependencies::ClassCache)
@@mailer_ref.get "Devise::Mailer"
else
@@mailer_ref.get
end
@@mailer_ref.get
end
# Set the mailer reference object to access the mailer.
@@ -289,13 +317,17 @@ module Devise
options.assert_valid_keys(:strategy, :model, :controller, :route)
if strategy = options[:strategy]
STRATEGIES[module_name] = (strategy == true ? module_name : strategy)
strategy = (strategy == true ? module_name : strategy)
STRATEGIES[module_name] = strategy
end
if controller = options[:controller]
CONTROLLERS[module_name] = (controller == true ? module_name : controller)
controller = (controller == true ? module_name : controller)
CONTROLLERS[module_name] = controller
end
NO_INPUT << strategy if strategy && controller != :sessions
if route = options[:route]
case route
when TrueClass
@@ -345,7 +377,8 @@ module Devise
#
def self.omniauth(provider, *args)
@@helpers << Devise::OmniAuth::UrlHelpers
@@omniauth_configs[provider] = Devise::OmniAuth::Config.new(provider, args)
config = Devise::OmniAuth::Config.new(provider, args)
@@omniauth_configs[config.strategy_name.to_sym] = config
end
# Include helpers in the given scope to AC and AV.
@@ -384,7 +417,7 @@ module Devise
# Generate a friendly string randomically to be used as token.
def self.friendly_token
ActiveSupport::SecureRandom.base64(15).tr('+/=', 'xyz')
SecureRandom.base64(15).tr('+/=', 'xyz')
end
# constant-time comparison algorithm to prevent timing attacks

View File

@@ -5,7 +5,7 @@ module Devise
extend ActiveSupport::Concern
included do
helper_method :warden, :signed_in?, :devise_controller?, :anybody_signed_in?
helper_method :warden, :signed_in?, :devise_controller?
end
# Define authentication filters and accessor helpers based on mappings.
@@ -218,9 +218,12 @@ module Devise
session.keys.grep(/^devise\./).each { |k| session.delete(k) }
end
# Overwrite Rails' handle unverified request to sign out all scopes.
# Overwrite Rails' handle unverified request to sign out all scopes,
# clear run strategies and remove cached variables.
def handle_unverified_request
sign_out_all_scopes
warden.clear_strategies_cache!
Devise.mappings.each { |_,m| instance_variable_set("@current_#{m.name}", nil) }
super # call the default behaviour which resets the session
end
end

View File

@@ -6,19 +6,7 @@ module Devise
module InternalHelpers #:nodoc:
extend ActiveSupport::Concern
include Devise::Controllers::ScopedViews
MIME_REFERENCES = Mime::HTML.respond_to?(:ref)
# Helper used by FailureApp and Devise controllers to retrieve proper formats.
def self.request_format(request)
if request.format.respond_to?(:ref)
request.format.ref
elsif MIME_REFERENCES
request.format
elsif request.format # Rails < 3.0.4
request.format.to_sym
end
end
include Devise::Controllers::SharedHelpers
included do
helper DeviseHelper
@@ -65,10 +53,6 @@ module Devise
protected
def request_format
@request_format ||= Devise::Controllers::InternalHelpers.request_format(request)
end
# Checks whether it's a devise mapped resource or not.
def is_devise_resource? #:nodoc:
unknown_action! <<-MESSAGE unless devise_mapping
@@ -81,11 +65,6 @@ Maybe you forgot to wrap your route inside the scope block? For example:
MESSAGE
end
# Check whether it's navigational format, such as :html or :iphone, or not.
def is_navigational_format?
Devise.navigational_formats.include?(request_format)
end
# Returns real navigational formats which are supported by Rails
def navigational_formats
@navigational_formats ||= Devise.navigational_formats.select{ |format| Mime::EXTENSION_LOOKUP[format.to_s] }
@@ -112,13 +91,27 @@ MESSAGE
# Example:
# before_filter :require_no_authentication, :only => :new
def require_no_authentication
if warden.authenticated?(resource_name)
no_input = devise_mapping.no_input_strategies
args = no_input.dup.push :scope => resource_name
if no_input.present? && warden.authenticate?(*args)
resource = warden.user(resource_name)
flash[:alert] = I18n.t("devise.failure.already_authenticated")
redirect_to after_sign_in_path_for(resource)
end
end
# Helper for use to validate if an resource is errorless. If we are on paranoid mode, we always should assume it is
# and return false.
def successful_and_sane?(resource)
if Devise.paranoid
set_flash_message :notice, :send_paranoid_instructions if is_navigational_format?
resource.errors.clear
false
else
resource.errors.empty?
end
end
# Sets the flash message with :key, using I18n. By default you are able
# to setup your messages using specific resource scope, and if no one is
# found we look to default scope.

View File

@@ -29,7 +29,7 @@ module Devise
# Forgets the given resource by deleting a cookie
def forget_me(resource)
scope = Devise::Mapping.find_scope!(resource)
resource.forget_me! unless resource.frozen?
resource.forget_me!
cookies.delete("remember_#{scope}_token", forget_cookie_values(resource))
end

View File

@@ -0,0 +1,26 @@
module Devise
module Controllers
# Helpers used in both FailureApp and Devise controllers.
module SharedHelpers
MIME_REFERENCES = Mime::HTML.respond_to?(:ref)
protected
# Helper used by FailureApp and Devise controllers to retrieve proper formats.
def request_format
@request_format ||= if request.format.respond_to?(:ref)
request.format.ref
elsif MIME_REFERENCES
request.format
elsif request.format # Rails < 3.0.4
request.format.to_sym
end
end
# Check whether it's navigational format, such as :html or :iphone, or not.
def is_navigational_format?
Devise.navigational_formats.include?(request_format)
end
end
end
end

23
lib/devise/email.rb Normal file
View File

@@ -0,0 +1,23 @@
# This e-mail validation regexes were retrieved from SixArm Ruby
# e-mail validation gem (https://github.com/SixArm/sixarm_ruby_email_address_validation)
# As said on https://github.com/SixArm/sixarm_ruby_email_address_validation/blob/master/LICENSE.txt,
# we added it using Ruby license terms.
module Devise
module Email
QTEXT = Regexp.new '[^\\x0d\\x22\\x5c\\x80-\\xff]', nil, 'n'
DTEXT = Regexp.new '[^\\x0d\\x5b-\\x5d\\x80-\\xff]', nil, 'n'
ATOM = Regexp.new '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+', nil, 'n'
QUOTED_PAIR = Regexp.new '\\x5c[\\x00-\\x7f]', nil, 'n'
DOMAIN_LITERAL = Regexp.new "\\x5b(?:#{DTEXT}|#{QUOTED_PAIR})*\\x5d", nil, 'n'
QUOTED_STRING = Regexp.new "\\x22(?:#{QTEXT}|#{QUOTED_PAIR})*\\x22", nil, 'n'
DOMAIN_REF = ATOM
SUB_DOMAIN = "(?:#{DOMAIN_REF}|#{DOMAIN_LITERAL})"
WORD = "(?:#{ATOM}|#{QUOTED_STRING})"
DOMAIN = "#{SUB_DOMAIN}(?:\\x2e#{SUB_DOMAIN})*"
LOCAL_PART = "#{WORD}(?:\\x2e#{WORD})*"
SPEC = "#{LOCAL_PART}\\x40#{DOMAIN}"
PATTERN = Regexp.new "#{SPEC}", nil, 'n'
EXACT_PATTERN = Regexp.new "\\A#{SPEC}\\z", nil, 'n'
end
end

View File

@@ -10,6 +10,7 @@ module Devise
include ActionController::UrlFor
include ActionController::Redirecting
include Rails.application.routes.url_helpers
include Devise::Controllers::SharedHelpers
delegate :flash, :to => :request
@@ -64,13 +65,17 @@ module Devise
end
def redirect_url
if request_format == :html
if skip_format?
send(:"new_#{scope}_session_path")
else
send(:"new_#{scope}_session_path", :format => request_format)
end
end
def skip_format?
%w(html */*).include? request_format.to_s
end
# Choose whether we should respond in a http authentication fashion,
# including 401 and optional headers.
#
@@ -83,7 +88,7 @@ module Devise
if request.xhr?
Devise.http_authenticatable_on_xhr
else
!(request_format && Devise.navigational_formats.include?(request_format))
!(request_format && is_navigational_format?)
end
end
@@ -96,7 +101,13 @@ module Devise
def http_auth_body
return i18n_message unless request_format
method = "to_#{request_format}"
{}.respond_to?(method) ? { :error => i18n_message }.send(method) : i18n_message
if method == "to_xml"
{ :error => i18n_message }.to_xml(:root => "errors")
elsif {}.respond_to?(method)
{ :error => i18n_message }.send(method)
else
i18n_message
end
end
def recall_app(app)
@@ -129,9 +140,5 @@ module Devise
def store_location!
session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
end
def request_format
@request_format ||= Devise::Controllers::InternalHelpers.request_format(request)
end
end
end

View File

@@ -1,6 +1,6 @@
# Before logout hook to forget the user in the given scope, if it responds
# to forget_me! Also clear remember token to ensure the user won't be
# remembered again. Notice that we forget the user unless the record is frozen.
# remembered again. Notice that we forget the user unless the record is not persisted.
# This avoids forgetting deleted users.
Warden::Manager.before_logout do |record, warden, options|
if record.respond_to?(:forget_me!)

View File

@@ -3,7 +3,7 @@
# and on authentication. Retrieving the user from session (:fetch) does
# not trigger it.
Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope])
if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && !warden.request.env['devise.skip_trackable']
record.update_tracked_fields!(warden.request)
end
end

View File

@@ -0,0 +1,84 @@
module Devise
module Mailers
module Helpers
extend ActiveSupport::Concern
included do
include Devise::Controllers::ScopedViews
attr_reader :scope_name, :resource
end
protected
# Configure default email options
def devise_mail(record, action)
initialize_from_record(record)
mail headers_for(action)
end
def initialize_from_record(record)
@scope_name = Devise::Mapping.find_scope!(record)
@resource = instance_variable_set("@#{devise_mapping.name}", record)
end
def devise_mapping
@devise_mapping ||= Devise.mappings[scope_name]
end
def headers_for(action)
headers = {
:subject => translate(devise_mapping, action),
:from => mailer_sender(devise_mapping),
:to => resource.email,
:template_path => template_paths
}
if resource.respond_to?(:headers_for)
headers.merge!(resource.headers_for(action))
end
unless headers.key?(:reply_to)
headers[:reply_to] = headers[:from]
end
headers
end
def mailer_sender(mapping)
if Devise.mailer_sender.is_a?(Proc)
Devise.mailer_sender.call(mapping.name)
else
Devise.mailer_sender
end
end
def template_paths
template_path = [self.class.mailer_name]
template_path.unshift "#{@devise_mapping.scoped_path}/mailer" if self.class.scoped_views?
template_path
end
# Setup a subject doing an I18n lookup. At first, it attemps to set a subject
# based on the current mapping:
#
# en:
# devise:
# mailer:
# confirmation_instructions:
# user_subject: '...'
#
# If one does not exist, it fallbacks to ActionMailer default:
#
# en:
# devise:
# mailer:
# confirmation_instructions:
# subject: '...'
#
def translate(mapping, key)
I18n.t(:"#{mapping.name}_subject", :scope => [:devise, :mailer, key],
:default => [:subject, key.to_s.humanize])
end
end
end
end

View File

@@ -22,7 +22,7 @@ module Devise
# # is the modules included in the class
#
class Mapping #:nodoc:
attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via
attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via, :format
alias :name :singular
# Receives an object and find a scope for it. If a scope cannot be found,
@@ -58,12 +58,20 @@ module Devise
mod = options[:module] || "devise"
@controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
@controllers.merge!(options[:controllers] || {})
@controllers.each { |k,v| @controllers[k] = v.to_s }
@path_names = Hash.new { |h,k| h[k] = k.to_s }
@path_names.merge!(:registration => "")
@path_names.merge!(options[:path_names] || {})
@constraints = Hash.new { |h,k| h[k] = k.to_s }
@constraints.merge!(options[:constraints] || {})
@defaults = Hash.new { |h,k| h[k] = k.to_s }
@defaults.merge!(options[:defaults] || {})
@sign_out_via = options[:sign_out_via] || Devise.sign_out_via
@format = options[:format]
end
# Return modules for the mapping.
@@ -73,17 +81,17 @@ module Devise
# Gives the class the mapping points to.
def to
if defined?(ActiveSupport::Dependencies::ClassCache)
@ref.get @class_name
else
@ref.get
end
@ref.get
end
def strategies
@strategies ||= STRATEGIES.values_at(*self.modules).compact.uniq.reverse
end
def no_input_strategies
self.strategies & Devise::NO_INPUT
end
def routes
@routes ||= ROUTES.values_at(*self.modules).compact.uniq
end
@@ -95,7 +103,15 @@ module Devise
def fullpath
"/#{@path_prefix}/#{@path}".squeeze("/")
end
def constraints
@constraints
end
def defaults
@defaults
end
# Create magic predicates for verifying what module is activated by this map.
# Example:
#

View File

@@ -1,4 +1,5 @@
require 'devise/hooks/activatable'
require 'devise/models/serializable'
module Devise
module Models
@@ -46,6 +47,8 @@ module Devise
module Authenticatable
extend ActiveSupport::Concern
include Devise::Models::Serializable
included do
class_attribute :devise_modules, :instance_writer => false
self.devise_modules ||= []
@@ -77,7 +80,7 @@ module Devise
end
module ClassMethods
Devise::Models.config(self, :authentication_keys, :request_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
def params_authenticatable?(strategy)
params_authenticatable.is_a?(Array) ?
@@ -100,8 +103,9 @@ module Devise
# end
#
def find_for_authentication(conditions)
filter_auth_params(conditions)
conditions = filter_auth_params(conditions.dup)
(case_insensitive_keys || []).each { |k| conditions[k].try(:downcase!) }
(strip_whitespace_keys || []).each { |k| conditions[k].try(:strip!) }
to_adapter.find_first(conditions)
end
@@ -113,14 +117,15 @@ module Devise
# Find an initialize a group of attributes based on a list of required attributes.
def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
(case_insensitive_keys || []).each { |k| attributes[k].try(:downcase!) }
(strip_whitespace_keys || []).each { |k| attributes[k].try(:strip!) }
attributes = attributes.slice(*required_attributes)
attributes.delete_if { |key, value| value.blank? }
if attributes.size == required_attributes.size
record = to_adapter.find_first(filter_auth_params(attributes))
end
unless record
record = new
@@ -139,9 +144,14 @@ module Devise
# Force keys to be string to avoid injection on mongoid related database.
def filter_auth_params(conditions)
conditions.each do |k, v|
conditions[k] = v.to_s
conditions[k] = v.to_s if auth_param_requires_string_conversion?(v)
end if conditions.is_a?(Hash)
end
# Determine which values should be transformed to string or passed as-is to the query builder underneath
def auth_param_requires_string_conversion?(value)
true unless value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Fixnum)
end
# Generate a token by looping and ensuring does not already exist.
def generate_token(column)

View File

@@ -23,6 +23,7 @@ module Devise
attr_reader :password, :current_password
attr_accessor :password_confirmation
before_validation :downcase_keys
before_validation :strip_whitespace
end
# Generates password encryption based on the given value.
@@ -41,7 +42,7 @@ module Devise
# Set password and password confirmation to nil
def clean_up_passwords
self.password = self.password_confirmation = nil
self.password = self.password_confirmation = ""
end
# Update record attributes when :current_password matches, otherwise returns
@@ -58,8 +59,9 @@ module Devise
result = if valid_password?(current_password)
update_attributes(params)
else
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
self.attributes = params
self.valid?
self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
end
@@ -67,6 +69,17 @@ module Devise
result
end
# Updates record attributes without asking for the current password.
# Never allows to change the current password
def update_without_password(params={})
params.delete(:password)
params.delete(:password_confirmation)
result = update_attributes(params)
clean_up_passwords
result
end
def after_database_authentication
end
@@ -81,6 +94,10 @@ module Devise
def downcase_keys
(self.class.case_insensitive_keys || []).each { |k| self[k].try(:downcase!) }
end
def strip_whitespace
(self.class.strip_whitespace_keys || []).each { |k| self[k].try(:strip!) }
end
# Digests the password using bcrypt.
def password_digest(password)

View File

@@ -42,6 +42,7 @@ module Devise
# Checks if the reset password token sent is within the limit time.
# We do this by calculating if the difference between today and the
# sending date does not exceed the confirm in time configured.
# Returns true if the resource is not responding to reset_password_sent_at at all.
# reset_password_within is a model configuration, must always be an integer value.
#
# Example:
@@ -59,8 +60,8 @@ module Devise
# reset_password_period_valid? # will always return false
#
def reset_password_period_valid?
respond_to?(:reset_password_sent_at) && reset_password_sent_at &&
reset_password_sent_at.utc >= self.class.reset_password_within.ago
return true unless respond_to?(:reset_password_sent_at)
reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
end
protected
@@ -73,6 +74,7 @@ module Devise
def generate_reset_password_token
self.reset_password_token = self.class.reset_password_token
self.reset_password_sent_at = Time.now.utc if respond_to?(:reset_password_sent_at=)
self.reset_password_token
end
# Resets the reset password token with and save the record without
@@ -114,7 +116,7 @@ module Devise
if recoverable.reset_password_period_valid?
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
else
recoverable.errors.add(:reset_password_token, :invalid)
recoverable.errors.add(:reset_password_token, :expired)
end
end
recoverable

View File

@@ -54,12 +54,14 @@ module Devise
save(:validate => false)
end
# Removes the remember token only if it exists, and save the record
# without validations.
# If the record is persisted, remove the remember token (but only if
# it exists), and save the record without validations.
def forget_me!
self.remember_token = nil if respond_to?(:remember_token=)
self.remember_created_at = nil
save(:validate => false)
if persisted?
self.remember_token = nil if respond_to?(:remember_token=)
self.remember_created_at = nil
save(:validate => false)
end
end
# Remember token should be expired if expiration time not overpass now.

View File

@@ -0,0 +1,43 @@
module Devise
module Models
# This module redefine to_xml and serializable_hash in models for more
# secure defaults. By default, it removes from the serializable model
# all attributes that are *not* accessible. You can remove this default
# by using :force_except and passing a new list of attributes you want
# to exempt. All attributes given to :except will simply add names to
# exempt to Devise internal list.
module Serializable
extend ActiveSupport::Concern
# TODO: to_xml does not call serializable_hash. Hopefully someone will fix this in AR.
%w(to_xml serializable_hash).each do |method|
class_eval <<-RUBY, __FILE__, __LINE__
def #{method}(options=nil)
options ||= {}
if options.key?(:force_except)
options[:except] = options.delete(:force_except)
super(options)
elsif self.class.blacklist_keys?
except = Array(options[:except])
super(options.merge(:except => except + self.class.blacklist_keys))
else
super
end
end
RUBY
end
module ClassMethods
# Return true if we can retrieve blacklist keys from the record.
def blacklist_keys?
@has_except_keys ||= respond_to?(:accessible_attributes) && !accessible_attributes.to_a.empty?
end
# Returns keys that should be removed when serializing the record.
def blacklist_keys
@blacklist_keys ||= to_adapter.column_names.map(&:to_s) - accessible_attributes.to_a.map(&:to_s)
end
end
end
end
end

View File

@@ -23,14 +23,12 @@ module Devise
base.class_eval do
validates_presence_of :email, :if => :email_required?
validates_uniqueness_of :email, :case_sensitive => (case_insensitive_keys != false), :allow_blank => true
validates_format_of :email, :with => email_regexp, :allow_blank => true
validates_uniqueness_of :email, :case_sensitive => (case_insensitive_keys != false), :allow_blank => true, :if => :email_changed?
validates_format_of :email, :with => email_regexp, :allow_blank => true, :if => :email_changed?
with_options :if => :password_required? do |v|
v.validates_presence_of :password
v.validates_confirmation_of :password
v.validates_length_of :password, :within => password_length, :allow_blank => true
end
validates_presence_of :password, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :password, :within => password_length, :allow_blank => true
end
end

View File

@@ -5,7 +5,7 @@ Devise.with_options :model => true do |d|
d.with_options :strategy => true do |s|
routes = [nil, :new, :destroy]
s.add_module :database_authenticatable, :controller => :sessions, :route => { :session => routes }
s.add_module :token_authenticatable, :controller => :sessions, :route => { :session => routes }
s.add_module :token_authenticatable
s.add_module :rememberable
end

View File

@@ -23,10 +23,5 @@ module Devise
module OmniAuth
autoload :Config, "devise/omniauth/config"
autoload :UrlHelpers, "devise/omniauth/url_helpers"
class << self
delegate :short_circuit_authorizers!, :unshort_circuit_authorizers!,
:test_mode!, :stub!, :reset_stubs!, :to => "Devise::OmniAuth::TestHelpers"
end
end
end

View File

@@ -10,6 +10,12 @@ module Devise
@strategy = nil
end
# open_id strategy can have configurable name
def strategy_name
options = @args.last.is_a?(Hash) && @args.last
options && options[:name] ? options[:name] : @provider
end
def strategy_class
::OmniAuth::Strategies.const_get("#{::OmniAuth::Utils.camelize(@provider.to_s)}")
end

View File

@@ -103,6 +103,13 @@ module ActionDispatch::Routing
#
# devise_for :users, :only => :sessions
#
# * :format => include "(.:format)" in the generated routes? true by default, set to false to disable:
#
# devise_for :users, :format => false
#
# * :constraints => works the same as Rails' contraints
#
# * :defaults => works the same as Rails' defaults
#
# ==== Scoping
#
@@ -129,9 +136,9 @@ module ActionDispatch::Routing
# end
#
# ==== Adding custom actions to override controllers
#
# You can pass a block to devise_for that will add any routes defined in the block to Devise's
# list of known actions. This is important if you add a custom action to a controller that
#
# You can pass a block to devise_for that will add any routes defined in the block to Devise's
# list of known actions. This is important if you add a custom action to a controller that
# overrides an out of the box Devise controller.
# For example:
#
@@ -159,6 +166,10 @@ module ActionDispatch::Routing
options[:module] ||= @scope[:module] if @scope[:module].present?
options[:path_prefix] ||= @scope[:path] if @scope[:path].present?
options[:path_names] = (@scope[:path_names] || {}).merge(options[:path_names] || {})
options[:constraints] = (@scope[:constraints] || {}).merge(options[:constraints] || {})
options[:defaults] = (@scope[:defaults] || {}).merge(options[:defaults] || {})
@scope[:options] = (@scope[:options] || {}).merge({:format => false}) if options[:format] == false
resources.map!(&:to_sym)
@@ -185,7 +196,7 @@ module ActionDispatch::Routing
devise_scope mapping.name do
yield if block_given?
with_devise_exclusive_scope mapping.fullpath, mapping.name do
with_devise_exclusive_scope mapping.fullpath, mapping.name, mapping.constraints, mapping.defaults do
routes.each { |mod| send("devise_#{mod}", mapping, mapping.controllers) }
end
end
@@ -208,6 +219,50 @@ module ActionDispatch::Routing
end
end
# Allow you to route based on whether a scope is authenticated. You
# can optionally specify which scope.
#
# authenticated :admin do
# root :to => 'admin/dashboard#show'
# end
#
# authenticated do
# root :to => 'dashboard#show'
# end
#
# root :to => 'landing#show'
#
def authenticated(scope=nil)
constraint = lambda do |request|
request.env["warden"].authenticate? :scope => scope
end
constraints(constraint) do
yield
end
end
# Allow you to route based on whether a scope is *not* authenticated.
# You can optionally specify which scope.
#
# unauthenticated do
# as :user do
# root :to => 'devise/registrations#new'
# end
# end
#
# root :to => 'dashboard#show'
#
def unauthenticated(scope=nil)
constraint = lambda do |request|
not request.env["warden"].authenticate? :scope => scope
end
constraints(constraint) do
yield
end
end
# Sets the devise scope to be used in the controller. If you have custom routes,
# you are required to call this method (also aliased as :as) in order to specify
# to which controller it is targetted.
@@ -286,17 +341,17 @@ module ActionDispatch::Routing
@scope[:path] = path
end
def with_devise_exclusive_scope(new_path, new_as) #:nodoc:
old_as, old_path, old_module = @scope[:as], @scope[:path], @scope[:module]
@scope[:as], @scope[:path], @scope[:module] = new_as, new_path, nil
def with_devise_exclusive_scope(new_path, new_as, new_constraints, new_defaults) #:nodoc:
old_as, old_path, old_module, old_constraints, old_defaults = @scope[:as], @scope[:path], @scope[:module], @scope[:constraints], @scope[:defaults]
@scope[:as], @scope[:path], @scope[:module], @scope[:constraints], @scope[:defaults] = new_as, new_path, nil, new_constraints, new_defaults
yield
ensure
@scope[:as], @scope[:path], @scope[:module] = old_as, old_path, old_module
@scope[:as], @scope[:path], @scope[:module], @scope[:constraints], @scope[:defaults] = old_as, old_path, old_module, old_constraints, old_defaults
end
def raise_no_devise_method_error!(klass) #:nodoc:
raise "#{klass} does not respond to 'devise' method. This usually means you haven't " <<
"loaded your ORM file or it's being loaded too late. To fix it, be sure to require 'devise/orm/YOUR_ORM' " <<
raise "#{klass} does not respond to 'devise' method. This usually means you haven't " \
"loaded your ORM file or it's being loaded too late. To fix it, be sure to require 'devise/orm/YOUR_ORM' " \
"inside 'config/initializers/devise.rb' or before your application definition in 'config/application.rb'"
end
end

View File

@@ -9,6 +9,7 @@ module Devise
class Rememberable < Authenticatable
# A valid strategy for rememberable needs a remember token in the cookies.
def valid?
@remember_cookie = nil
remember_cookie.present?
end
@@ -41,10 +42,10 @@ module Devise
"remember_#{scope}_token"
end
# Accessor for remember cookie
def remember_cookie
@remember_cookie ||= cookies.signed[remember_key]
end
end
end
end

View File

@@ -13,48 +13,11 @@ module Devise
end
end
# This is a Warden::Proxy customized for functional tests. It's meant to
# some of Warden::Manager responsibilities, as retrieving configuration
# options and calling the FailureApp.
class TestWarden < Warden::Proxy #:nodoc:
attr_reader :controller
def initialize(controller)
@controller = controller
manager = Warden::Manager.new(nil) do |config|
config.merge! Devise.warden_config
end
super(controller.request.env, manager)
end
def authenticate!(*args)
catch_with_redirect { super }
end
def user(*args)
catch_with_redirect { super }
end
def catch_with_redirect(&block)
result = catch(:warden, &block)
if result.is_a?(Hash) && !custom_failure? && !@controller.send(:performed?)
result[:action] ||= :unauthenticated
env = @controller.request.env
env["PATH_INFO"] = "/#{result[:action]}"
env["warden.options"] = result
Warden::Manager._run_callbacks(:before_failure, env, result)
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
@controller.send :render, :status => status, :text => body,
:content_type => headers["Content-Type"], :location => headers["Location"]
nil
else
result
end
end
# Override process to consider warden.
def process(*)
result = nil
_catch_warden { result = super }
result
end
# We need to setup the environment variables and the response in the controller.
@@ -64,7 +27,12 @@ module Devise
# Quick access to Warden::Proxy.
def warden #:nodoc:
@warden ||= (@request.env['warden'] = TestWarden.new(@controller))
@warden ||= begin
manager = Warden::Manager.new(nil) do |config|
config.merge! Devise.warden_config
end
@request.env['warden'] = Warden::Proxy.new(@request.env, manager)
end
end
# sign_in a given resource by storing its keys in the session.
@@ -96,5 +64,27 @@ module Devise
warden.session_serializer.delete(scope, user)
end
protected
def _catch_warden(&block)
result = catch(:warden, &block)
if result.is_a?(Hash) && !warden.custom_failure? && !@controller.send(:performed?)
result[:action] ||= :unauthenticated
env = @controller.request.env
env["PATH_INFO"] = "/#{result[:action]}"
env["warden.options"] = result
Warden::Manager._run_callbacks(:before_failure, env, result)
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
@controller.send :render, :status => status, :text => body,
:content_type => headers["Content-Type"], :location => headers["Location"]
nil
else
result
end
end
end
end

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "1.3.0".freeze
VERSION = "1.4.2".freeze
end

View File

@@ -1,4 +1,4 @@
require 'active_support/secure_random'
require 'securerandom'
module Devise
module Generators
@@ -21,4 +21,4 @@ module Devise
end
end
end
end
end

View File

@@ -1,20 +1,72 @@
require 'tmpdir'
module Devise
module Generators
# Include this module in your generator to generate Devise views.
# `copy_views` is the main method and by default copies all views
# with forms.
module ViewPathTemplates #:nodoc:
extend ActiveSupport::Concern
included do
argument :scope, :required => false, :default => nil,
:desc => "The scope to copy views to"
public_task :copy_views
end
def copy_views
view_directory :confirmations
view_directory :passwords
view_directory :registrations
view_directory :sessions
view_directory :unlocks
end
protected
def view_directory(name)
directory name.to_s, "#{target_path}/#{name}"
end
def target_path
@target_path ||= "app/views/#{scope || :devise}"
end
end
class SharedViewsGenerator < Rails::Generators::Base #:nodoc:
include ViewPathTemplates
source_root File.expand_path("../../../../app/views/devise", __FILE__)
desc "Copies shared Devise views to your application."
# Override copy_views to just copy mailer and shared.
def copy_views
view_directory :mailer
view_directory :shared
end
end
class FormForGenerator < Rails::Generators::Base #:nodoc:
include ViewPathTemplates
source_root File.expand_path("../../../../app/views/devise", __FILE__)
desc "Copies default Devise views to your application."
end
class SimpleFormForGenerator < Rails::Generators::Base #:nodoc:
include ViewPathTemplates
source_root File.expand_path("../../templates/simple_form_for", __FILE__)
desc "Copies simple form enabled views to your application."
end
class ViewsGenerator < Rails::Generators::Base
source_root File.expand_path("../../../../app/views", __FILE__)
desc "Copies all Devise views to your application."
desc "Copies Devise views to your application."
argument :scope, :required => false, :default => nil,
:desc => "The scope to copy views to"
# class_option :template_engine, :type => :string, :aliases => "-t",
# :desc => "Template engine for the views. Available options are 'erb', 'haml' and 'slim'."
invoke SharedViewsGenerator
def copy_views
directory "devise", "app/views/#{scope || :devise}"
end
hook_for :form_builder, :aliases => "-b",
:desc => "Form builder to be used",
:default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
end
end
end

View File

@@ -3,7 +3,7 @@
Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in DeviseMailer.
config.mailer_sender = "please-change-me@config-initializers-devise.com"
config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
# Configure the class responsible to send e-mails.
# config.mailer = "Devise::Mailer"
@@ -35,6 +35,11 @@ Devise.setup do |config|
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [ :email ]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [ :email ]
# Tell if authentication through request.params is enabled. True by default.
# config.params_authenticatable = true
@@ -48,13 +53,18 @@ Devise.setup do |config|
# The realm used in Http Basic Authentication. "Application" by default.
# config.http_authentication_realm = "Application"
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
# using other encryptors, it sets how many times you want the password re-encrypted.
config.stretches = 10
# Setup a pepper to generate the encrypted password.
# config.pepper = <%= ActiveSupport::SecureRandom.hex(64).inspect %>
# config.pepper = <%= SecureRandom.hex(64).inspect %>
# ==> Configuration for :confirmable
# The time you want to give your user to confirm his account. During this time
@@ -174,8 +184,8 @@ Devise.setup do |config|
# Explorer requests.
# config.navigational_formats = [:"*/*", "*/*", :html]
# The default HTTP method used to sign out a resource. Default is :get.
# config.sign_out_via = :get
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting

View File

@@ -0,0 +1,15 @@
<h2>Resend confirmation instructions</h2>
<%= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :required => true %>
</div>
<div class="actions">
<%= f.button :submit, "Resend confirmation instructions" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -0,0 +1,19 @@
<h2>Change your password</h2>
<%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
<%= f.error_notification %>
<%= f.input :reset_password_token, :as => :hidden %>
<%= f.full_error :reset_password_token %>
<div class="inputs">
<%= f.input :password, :label => "New password", :required => true %>
<%= f.input :password_confirmation, :label => "Confirm your new password", :required => true %>
</div>
<div class="actions">
<%= f.button :submit, "Change my password" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -0,0 +1,15 @@
<h2>Forgot your password?</h2>
<%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :required => true %>
</div>
<div class="actions">
<%= f.button :submit, "Send me reset password instructions" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -0,0 +1,22 @@
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :autofocus => true %>
<%= f.input :password, :hint => "leave it blank if you don't want to change it", :required => false %>
<%= f.input :password_confirmation, :required => false %>
<%= f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true %>
</div>
<div class="actions">
<%= f.button :submit, "Update" %>
</div>
<% end %>
<h3>Cancel my account</h3>
<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.</p>
<%= link_to "Back", :back %>

View File

@@ -0,0 +1,17 @@
<h2>Sign up</h2>
<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :autofocus => true %>
<%= f.input :password %>
<%= f.input :password_confirmation %>
</div>
<div class="actions">
<%= f.button :submit, "Sign up" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -0,0 +1,15 @@
<h2>Sign in</h2>
<%= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div class="inputs">
<%= f.input :email, :required => false, :autofocus => true %>
<%= f.input :password, :required => false %>
<%= f.input :remember_me, :as => :boolean if devise_mapping.rememberable? %>
</div>
<div class="actions">
<%= f.button :submit, "Sign in" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -0,0 +1,15 @@
<h2>Resend unlock instructions</h2>
<%= simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<%= f.input :email, :required => true %>
</div>
<div class="actions">
<%= f.button :submit, "Resend unlock instructions" %>
</div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -39,14 +39,21 @@ class HelpersTest < ActionController::TestCase
end
test 'require no authentication tests current mapping' do
@mock_warden.expects(:authenticated?).with(:user).returns(true)
@mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
@mock_warden.expects(:user).with(:user).returns(User.new)
@controller.expects(:redirect_to).with(root_path)
@controller.send :require_no_authentication
end
test 'require no authentication skips if no inputs are available' do
Devise.mappings[:user].expects(:no_input_strategies).returns([])
@mock_warden.expects(:authenticate?).never
@controller.expects(:redirect_to).never
@controller.send :require_no_authentication
end
test 'require no authentication sets a flash message' do
@mock_warden.expects(:authenticated?).with(:user).returns(true)
@mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
@mock_warden.expects(:user).with(:user).returns(User.new)
@controller.expects(:redirect_to).with(root_path)
@controller.send :require_no_authentication

View File

@@ -4,14 +4,13 @@ class SessionsControllerTest < ActionController::TestCase
tests Devise::SessionsController
include Devise::TestHelpers
test "#create doesn't raise exception after Warden authentication fails " \
+ "when TestHelpers included" do
test "#create doesn't raise exception after Warden authentication fails when TestHelpers included" do
request.env["devise.mapping"] = Devise.mappings[:user]
assert_nothing_raised(NoMethodError) do
post :create, :user => {
:email => "nosuchuser@example.com",
:password => "wevdude"
}
end
post :create, :user => {
:email => "nosuchuser@example.com",
:password => "wevdude"
}
assert_equal 200, @response.status
assert_template "devise/sessions/new"
end
end

View File

@@ -39,6 +39,11 @@ class FailureTest < ActiveSupport::TestCase
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
end
test 'return to the default redirect location for wildcard requests' do
call_failure 'action_dispatch.request.formats' => nil, 'HTTP_ACCEPT' => '*/*'
assert_equal 'http://test.host/users/sign_in', @response.second['Location']
end
test 'uses the proxy failure message as symbol' do
call_failure('warden' => OpenStruct.new(:message => :test))
assert_equal 'test', @request.flash[:alert]
@@ -84,6 +89,18 @@ class FailureTest < ActiveSupport::TestCase
assert_equal 401, @response.first
end
test 'return appropriate body for xml' do
call_failure('formats' => :xml)
result = %(<?xml version="1.0" encoding="UTF-8"?>\n<errors>\n <error>You need to sign in or sign up before continuing.</error>\n</errors>\n)
assert_equal result, @response.last.body
end
test 'return appropriate body for json' do
call_failure('formats' => :json)
result = %({"error":"You need to sign in or sign up before continuing."})
assert_equal result, @response.last.body
end
test 'return 401 status for unknown formats' do
call_failure 'formats' => []
assert_equal 401, @response.first

View File

@@ -18,6 +18,16 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
assert_files "admins"
end
test "Assert views with simple form" do
run_generator %w(-b simple_form_for)
assert_files
assert_file "app/views/devise/confirmations/new.html.erb", /simple_form_for/
run_generator %w(users -b simple_form_for)
assert_files "users"
assert_file "app/views/users/confirmations/new.html.erb", /simple_form_for/
end
def assert_files(scope = nil, template_engine = nil)
scope = "devise" if scope.nil?
assert_file "app/views/#{scope}/confirmations/new.html.erb"

View File

@@ -0,0 +1,43 @@
require 'test_helper'
class DeviseHelperTest < ActionController::IntegrationTest
setup do
I18n.backend.store_translations :fr,
{
:errors => { :messages => { :not_saved => {
:one => "Erreur lors de l'enregistrement de '%{resource}': 1 erreur.",
:other => "Erreur lors de l'enregistrement de '%{resource}': %{count} erreurs."
} } },
:activerecord => { :models => { :user => "utilisateur" } }
}
I18n.locale = 'fr'
end
teardown do
I18n.locale = 'en'
end
test 'test errors.messages.not_saved with single error from i18n' do
get new_user_registration_path
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user123'
click_button 'Sign up'
assert_have_selector '#error_explanation'
assert_contain "Erreur lors de l'enregistrement de 'utilisateur': 1 erreur"
end
test 'test errors.messages.not_saved with multiple errors from i18n' do
get new_user_registration_path
fill_in 'email', :with => 'invalid_email'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user321'
click_button 'Sign up'
assert_have_selector '#error_explanation'
assert_contain "Erreur lors de l'enregistrement de 'utilisateur': 2 erreurs"
end
end

View File

@@ -101,6 +101,54 @@ class AuthenticationSanityTest < ActionController::IntegrationTest
assert_contain 'Private!'
end
test 'signed in as admin should get admin dashboard' do
sign_in_as_admin
assert warden.authenticated?(:admin)
assert_not warden.authenticated?(:user)
get dashboard_path
assert_response :success
assert_template 'home/admin'
assert_contain 'Admin dashboard'
end
test 'signed in as user should get user dashboard' do
sign_in_as_user
assert warden.authenticated?(:user)
assert_not warden.authenticated?(:admin)
get dashboard_path
assert_response :success
assert_template 'home/user'
assert_contain 'User dashboard'
end
test 'not signed in should get no dashboard' do
assert_raises ActionController::RoutingError do
get dashboard_path
end
end
test 'signed in user should not see join page' do
sign_in_as_user
assert warden.authenticated?(:user)
assert_not warden.authenticated?(:admin)
assert_raises ActionController::RoutingError do
get join_path
end
end
test 'not signed in should see join page' do
get join_path
assert_response :success
assert_template 'home/join'
assert_contain 'Join'
end
test 'signed in as user should not be able to access admins actions' do
sign_in_as_user
assert warden.authenticated?(:user)
@@ -306,6 +354,20 @@ class AuthenticationWithScopesTest < ActionController::IntegrationTest
end
class AuthenticationOthersTest < ActionController::IntegrationTest
test 'handles unverified requests gets rid of caches' do
swap UsersController, :allow_forgery_protection => true do
post exhibit_user_url(1)
assert_not warden.authenticated?(:user)
sign_in_as_user
assert warden.authenticated?(:user)
post exhibit_user_url(1)
assert_not warden.authenticated?(:user)
assert_equal "User is not authenticated", response.body
end
end
test 'uses the custom controller with the custom controller view' do
get '/admin_area/sign_in'
assert_contain 'Sign in'
@@ -314,8 +376,9 @@ class AuthenticationOthersTest < ActionController::IntegrationTest
end
test 'render 404 on roles without routes' do
get '/admin_area/password/new'
assert_equal 404, response.status
assert_raise ActionController::RoutingError do
get '/admin_area/password/new'
end
end
test 'does not intercept Rails 401 responses' do
@@ -336,9 +399,24 @@ class AuthenticationOthersTest < ActionController::IntegrationTest
end
end
test 'registration in xml format works when recognizing path' do
assert_nothing_raised do
post user_registration_path(:format => 'xml', :user => {:email => "test@example.com", :password => "invalid"} )
test 'sign in stub in xml format' do
get new_user_session_path(:format => 'xml')
assert_equal "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>\n <email></email>\n <password></password>\n</user>\n", response.body
end
test 'sign in stub in json format' do
get new_user_session_path(:format => 'json')
assert_match '{"user":{', response.body
assert_match '"email":""', response.body
assert_match '"password":""', response.body
end
test 'sign in stub in json with non attribute key' do
swap Devise, :authentication_keys => [:other_key] do
get new_user_session_path(:format => 'json')
assert_match '{"user":{', response.body
assert_match '"other_key":null', response.body
assert_match '"password":""', response.body
end
end
@@ -431,7 +509,9 @@ class AuthenticationSignOutViaTest < ActionController::IntegrationTest
test 'do not allow sign out via get when sign_out_via provides only delete' do
sign_in!(:sign_out_via_delete)
get destroy_sign_out_via_delete_session_path
assert_raise ActionController::RoutingError do
get destroy_sign_out_via_delete_session_path
end
assert warden.authenticated?(:sign_out_via_delete)
end
@@ -443,7 +523,9 @@ class AuthenticationSignOutViaTest < ActionController::IntegrationTest
test 'do not allow sign out via get when sign_out_via provides only post' do
sign_in!(:sign_out_via_post)
get destroy_sign_out_via_delete_session_path
assert_raise ActionController::RoutingError do
get destroy_sign_out_via_delete_session_path
end
assert warden.authenticated?(:sign_out_via_post)
end
@@ -461,7 +543,9 @@ class AuthenticationSignOutViaTest < ActionController::IntegrationTest
test 'do not allow sign out via get when sign_out_via provides delete and post' do
sign_in!(:sign_out_via_delete_or_post)
get destroy_sign_out_via_delete_or_post_session_path
assert_raise ActionController::RoutingError do
get destroy_sign_out_via_delete_or_post_session_path
end
assert warden.authenticated?(:sign_out_via_delete_or_post)
end
end

View File

@@ -106,7 +106,7 @@ class ConfirmationTest < ActionController::IntegrationTest
user = create_user(:confirm => false)
post user_confirmation_path(:format => 'xml'), :user => { :email => user.email }
assert_response :success
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>)
assert_equal response.body, {}.to_xml
end
test 'resent confirmation token with invalid E-Mail in XML format should return invalid response' do
@@ -129,4 +129,42 @@ class ConfirmationTest < ActionController::IntegrationTest
assert_response :unprocessable_entity
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>)
end
test 'request an account confirmation account with JSON, should return an empty JSON' do
user = create_user(:confirm => false)
post user_confirmation_path, :user => { :email => user.email }, :format => :json
assert_response :success
assert_equal response.body, {}.to_json
end
test "when in paranoid mode and with a valid e-mail, should not say that the e-mail is valid" do
swap Devise, :paranoid => true do
user = create_user(:confirm => false)
visit new_user_session_path
click_link "Didn't receive confirmation instructions?"
fill_in 'email', :with => user.email
click_button 'Resend confirmation instructions'
assert_contain "If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes."
assert_current_url "/users/confirmation"
end
end
test "when in paranoid mode and with a invalid e-mail, should not say that the e-mail is invalid" do
swap Devise, :paranoid => true do
visit new_user_session_path
click_link "Didn't receive confirmation instructions?"
fill_in 'email', :with => "idonthavethisemail@gmail.com"
click_button 'Resend confirmation instructions'
assert_not_contain "1 error prohibited this user from being saved:"
assert_not_contain "Email not found"
assert_contain "If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes."
assert_current_url "/users/confirmation"
end
end
end

View File

@@ -22,6 +22,28 @@ class DatabaseAuthenticationTest < ActionController::IntegrationTest
assert_not warden.authenticated?(:user)
end
end
test 'sign in with email including extra spaces should succeed when email is in the list of strip whitespace keys' do
create_user(:email => ' foo@bar.com ')
sign_in_as_user do
fill_in 'email', :with => 'foo@bar.com'
end
assert warden.authenticated?(:user)
end
test 'sign in with email including extra spaces should fail when email is NOT the list of strip whitespace keys' do
swap Devise, :strip_whitespace_keys => [] do
create_user(:email => 'foo@bar.com')
sign_in_as_user do
fill_in 'email', :with => ' foo@bar.com '
end
assert_not warden.authenticated?(:user)
end
end
test 'sign in should not authenticate if not using proper authentication keys' do
swap Devise, :authentication_keys => [:username] do

View File

@@ -1,6 +1,14 @@
require 'test_helper'
class HttpAuthenticationTest < ActionController::IntegrationTest
test 'handles unverified requests gets rid of caches but continues signed in' do
swap UsersController, :allow_forgery_protection => true do
create_user
post exhibit_user_url(1), {}, "HTTP_AUTHORIZATION" => "Basic #{ActiveSupport::Base64.encode64("user@test.com:123456")}"
assert warden.authenticated?(:user)
assert_equal "User is authenticated", response.body
end
end
test 'sign in should authenticate with http' do
sign_in_as_new_user_with_http

View File

@@ -37,7 +37,7 @@ class LockTest < ActionController::IntegrationTest
end
test 'unlocked pages should not be available if email strategy is disabled' do
visit "/admins/sign_in"
visit "/admin_area/sign_in"
assert_raise Webrat::NotFoundError do
click_link "Didn't receive unlock instructions?"
@@ -47,8 +47,9 @@ class LockTest < ActionController::IntegrationTest
visit new_admin_unlock_path
end
visit "/admins/unlock/new"
assert_response :not_found
assert_raise ActionController::RoutingError do
visit "/admin_area/unlock/new"
end
end
test 'user with invalid unlock token should not be able to unlock an account' do
@@ -112,7 +113,7 @@ class LockTest < ActionController::IntegrationTest
post user_unlock_path(:format => 'xml'), :user => {:email => user.email}
assert_response :success
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>)
assert_equal response.body, {}.to_xml
assert_equal 1, ActionMailer::Base.deliveries.size
end
@@ -140,4 +141,61 @@ class LockTest < ActionController::IntegrationTest
assert_response :unprocessable_entity
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>)
end
test "when using json to ask a unlock request, should not return the user" do
user = create_user(:locked => true)
post user_unlock_path(:format => "json", :user => {:email => user.email})
assert_response :success
assert_equal response.body, {}.to_json
end
test "in paranoid mode, when trying to unlock an user that exists it should not say that it exists if it is locked" do
swap Devise, :paranoid => true do
user = create_user(:locked => true)
visit new_user_session_path
click_link "Didn't receive unlock instructions?"
fill_in 'email', :with => user.email
click_button 'Resend unlock instructions'
assert_current_url "/users/unlock"
assert_contain "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes."
end
end
test "in paranoid mode, when trying to unlock an user that exists it should not say that it exists if it is not locked" do
swap Devise, :paranoid => true do
user = create_user(:locked => false)
visit new_user_session_path
click_link "Didn't receive unlock instructions?"
fill_in 'email', :with => user.email
click_button 'Resend unlock instructions'
assert_current_url "/users/unlock"
assert_contain "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes."
end
end
test "in paranoid mode, when trying to unlock an user that does not exists it should not say that it does not exists" do
swap Devise, :paranoid => true do
visit new_user_session_path
click_link "Didn't receive unlock instructions?"
fill_in 'email', :with => "arandomemail@hotmail.com"
click_button 'Resend unlock instructions'
assert_not_contain "1 error prohibited this user from being saved:"
assert_not_contain "Email not found"
assert_current_url "/users/unlock"
assert_contain "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes."
end
end
end

View File

@@ -114,9 +114,7 @@ class OmniauthableIntegrationTest < ActionController::IntegrationTest
test "generates a proper link when SCRIPT_NAME is set" do
header 'SCRIPT_NAME', '/q'
visit "/users/sign_in"
click_link "Sign in with Facebook"
assert_equal '/q/users/auth/facebook', current_url
assert_select "a", :href => "/q/users/auth/facebook"
end
test "handles callback error parameter according to the specification" do

View File

@@ -17,7 +17,7 @@ class PasswordTest < ActionController::IntegrationTest
click_button 'Send me reset password instructions'
end
def reset_password(options={}, &block)
def reset_password(options={}, &block)
visit edit_user_password_path(:reset_password_token => options[:reset_password_token]) unless options[:visit] == false
assert_response :success
@@ -29,11 +29,11 @@ class PasswordTest < ActionController::IntegrationTest
test 'reset password with email of different case should succeed when email is in the list of case insensitive keys' do
create_user(:email => 'Foo@Bar.com')
request_forgot_password do
fill_in 'email', :with => 'foo@bar.com'
end
assert_current_url '/users/sign_in'
assert_contain 'You will receive an email with instructions about how to reset your password in a few minutes.'
end
@@ -41,11 +41,11 @@ class PasswordTest < ActionController::IntegrationTest
test 'reset password with email of different case should fail when email is NOT the list of case insensitive keys' do
swap Devise, :case_insensitive_keys => [] do
create_user(:email => 'Foo@Bar.com')
request_forgot_password do
fill_in 'email', :with => 'foo@bar.com'
end
assert_response :success
assert_current_url '/users/password'
assert_have_selector "input[type=email][value='foo@bar.com']"
@@ -53,6 +53,32 @@ class PasswordTest < ActionController::IntegrationTest
end
end
test 'reset password with email with extra whitespace should succeed when email is in the list of strip whitespace keys' do
create_user(:email => 'foo@bar.com')
request_forgot_password do
fill_in 'email', :with => ' foo@bar.com '
end
assert_current_url '/users/sign_in'
assert_contain 'You will receive an email with instructions about how to reset your password in a few minutes.'
end
test 'reset password with email with extra whitespace should fail when email is NOT the list of strip whitespace keys' do
swap Devise, :strip_whitespace_keys => [] do
create_user(:email => 'foo@bar.com')
request_forgot_password do
fill_in 'email', :with => ' foo@bar.com '
end
assert_response :success
assert_current_url '/users/password'
assert_have_selector "input[type=email][value=' foo@bar.com ']"
assert_contain 'not found'
end
end
test 'authenticated user should not be able to visit forgot password page' do
sign_in_as_user
assert warden.authenticated?(:user)
@@ -161,7 +187,7 @@ class PasswordTest < ActionController::IntegrationTest
create_user
post user_password_path(:format => 'xml'), :user => {:email => "user@test.com"}
assert_response :success
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>)
assert_equal response.body, { }.to_xml
end
test 'reset password request with invalid E-Mail in XML format should return valid response' do
@@ -194,4 +220,38 @@ class PasswordTest < ActionController::IntegrationTest
assert_response :unprocessable_entity
assert response.body.include? %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>)
end
test "when using json requests to ask a confirmable request, should not return the object" do
user = create_user(:confirm => false)
post user_password_path(:format => :json), :user => { :email => user.email }
assert_response :success
assert_equal response.body, "{}"
end
test "when in paranoid mode and with an invalid e-mail, asking to reset a password should display a message that does not indicates that the e-mail does not exists in the database" do
swap Devise, :paranoid => true do
visit_new_password_path
fill_in "email", :with => "arandomemail@test.com"
click_button 'Send me reset password instructions'
assert_not_contain "1 error prohibited this user from being saved:"
assert_not_contain "Email not found"
assert_contain "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
assert_current_url "/users/password"
end
end
test "when in paranoid mode and with a valid e-mail, asking to reset password should display a message that does not indicates that the email exists in the database and redirect to the failure route" do
swap Devise, :paranoid => true do
user = create_user
visit_new_password_path
fill_in 'email', :with => user.email
click_button 'Send me reset password instructions'
assert_contain "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
assert_current_url "/users/password"
end
end
end

View File

@@ -118,14 +118,14 @@ class RegistrationTest < ActionController::IntegrationTest
sign_in_as_user
get edit_user_registration_path
fill_in 'email', :with => 'user.new@email.com'
fill_in 'email', :with => 'user.new@example.com'
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_current_url '/'
assert_contain 'You updated your account successfully.'
assert_equal "user.new@email.com", User.first.email
assert_equal "user.new@example.com", User.first.email
end
test 'a signed in user should still be able to use the website after changing his password' do
@@ -146,13 +146,13 @@ class RegistrationTest < ActionController::IntegrationTest
sign_in_as_user
get edit_user_registration_path
fill_in 'email', :with => 'user.new@email.com'
fill_in 'email', :with => 'user.new@example.com'
fill_in 'current password', :with => 'invalid'
click_button 'Update'
assert_template 'registrations/edit'
assert_contain 'user@test.com'
assert_have_selector 'form input[value="user.new@email.com"]'
assert_have_selector 'form input[value="user.new@example.com"]'
assert_equal "user@test.com", User.first.email
end
@@ -207,6 +207,20 @@ class RegistrationTest < ActionController::IntegrationTest
assert_redirected_to new_user_registration_path
end
test 'a user with XML sign up stub' do
get new_user_registration_path(:format => 'xml')
assert_response :success
assert_match %(<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<user>), response.body
assert_no_match(/<confirmation-token/, response.body) if DEVISE_ORM == :active_record
end
test 'a user with JSON sign up stub' do
get new_user_registration_path(:format => 'json')
assert_response :success
assert_match %({"user":), response.body
assert_no_match(/"confirmation_token"/, response.body) if DEVISE_ORM == :active_record
end
test 'an admin sign up with valid information in XML format should return valid response' do
post admin_registration_path(:format => 'xml'), :admin => { :email => 'new_user@test.com', :password => 'new_user123', :password_confirmation => 'new_user123' }
assert_response :success

View File

@@ -38,6 +38,18 @@ class RememberMeTest < ActionController::IntegrationTest
assert_nil request.cookies["remember_user_cookie"]
end
test 'handles unverified requests gets rid of caches' do
swap UsersController, :allow_forgery_protection => true do
post exhibit_user_url(1)
assert_not warden.authenticated?(:user)
create_user_and_remember
post exhibit_user_url(1)
assert_equal "User is not authenticated", response.body
assert_not warden.authenticated?(:user)
end
end
test 'generate remember token after sign in' do
user = sign_in_as_user :remember_me => true
assert request.cookies["remember_user_token"]
@@ -69,7 +81,14 @@ class RememberMeTest < ActionController::IntegrationTest
assert_response :success
assert warden.authenticated?(:user)
assert warden.user(:user) == user
assert_match /remember_user_token[^\n]*HttpOnly\n/, response.headers["Set-Cookie"], "Expected Set-Cookie header in response to set HttpOnly flag on remember_user_token cookie."
assert_match /remember_user_token[^\n]*HttpOnly/, response.headers["Set-Cookie"], "Expected Set-Cookie header in response to set HttpOnly flag on remember_user_token cookie."
end
test 'remember the user before sign up and redirect him to his home' do
user = create_user_and_remember
get new_user_registration_path
assert warden.authenticated?(:user)
assert_redirected_to root_path
end
test 'cookies are destroyed on unverified requests' do

View File

@@ -61,4 +61,21 @@ class TrackableHooksTest < ActionController::IntegrationTest
assert_nil user.last_sign_in_at
end
end
test "do not track if devise.skip_trackable is set" do
user = create_user
sign_in_as_user do
header 'devise.skip_trackable', '1'
end
user.reload
assert_equal 0, user.sign_in_count
visit destroy_user_session_path
sign_in_as_user do
header 'devise.skip_trackable', false
end
user.reload
assert_equal 1, user.sign_in_count
end
end

View File

@@ -50,6 +50,11 @@ class MappingTest < ActiveSupport::TestCase
assert_equal [:rememberable, :database_authenticatable], Devise.mappings[:admin].strategies
end
test 'has no input strategies depending on the model declaration' do
assert_equal [:rememberable, :token_authenticatable], Devise.mappings[:user].no_input_strategies
assert_equal [:rememberable], Devise.mappings[:admin].no_input_strategies
end
test 'find scope for a given object' do
assert_equal :user, Devise::Mapping.find_scope!(User)
assert_equal :user, Devise::Mapping.find_scope!(:user)

View File

@@ -111,12 +111,12 @@ class ConfirmableTest < ActiveSupport::TestCase
end
test 'should return a new user if no email was found' do
confirmation_user = User.send_confirmation_instructions(:email => "invalid@email.com")
confirmation_user = User.send_confirmation_instructions(:email => "invalid@example.com")
assert_not confirmation_user.persisted?
end
test 'should add error to new user email if no email was found' do
confirmation_user = User.send_confirmation_instructions(:email => "invalid@email.com")
confirmation_user = User.send_confirmation_instructions(:email => "invalid@example.com")
assert confirmation_user.errors[:email]
assert_equal "not found", confirmation_user.errors[:email].join
end

View File

@@ -11,6 +11,39 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
user.save!
assert_equal email.downcase, user.email
end
test 'should remove whitespace from strip whitespace keys when saving' do
# strip_whitespace_keys is set to :email by default.
email = ' foo@bar.com '
user = new_user(:email => email)
assert_equal email, user.email
user.save!
assert_equal email.strip, user.email
end
test 'find_for_authentication and filter_auth_params should not modify the conditions hash' do
FilterAuthUser = Class.new(User) do
def self.filter_auth_params(conditions)
if conditions.is_a?(Hash) && login = conditions.delete('login')
key = login.include?('@') ? :email : :username
conditions[key] = login
end
super(conditions)
end
end
conditions = { 'login' => 'foo@bar.com' }
FilterAuthUser.find_for_authentication(conditions)
assert_equal({ 'login' => 'foo@bar.com' }, conditions)
end
test "filter_auth_params should not convert booleans and integer to strings" do
conditions = { 'login' => 'foo@bar.com', "bool1" => true, "bool2" => false, "fixnum" => 123, "will_be_converted" => (1..10) }
conditions = User.__send__(:filter_auth_params, conditions)
assert_equal( { 'login' => 'foo@bar.com', "bool1" => true, "bool2" => false, "fixnum" => 123, "will_be_converted" => "1..10" }, conditions)
end
test 'should respond to password and password confirmation' do
user = new_user
@@ -70,7 +103,7 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
:password => 'pass321', :password_confirmation => 'pass321')
assert user.reload.valid_password?('pass321')
end
test 'should add an error to current password when it is invalid' do
user = create_user
assert_not user.update_with_password(:current_password => 'other',
@@ -87,10 +120,19 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
assert_match "can't be blank", user.errors[:current_password].join
end
test 'should run validations even when current password is invalid or blank' do
user = UserWithValidation.create!(valid_attributes)
user.save
assert user.persisted?
assert_not user.update_with_password(:username => "")
assert_match "usertest", user.reload.username
assert_match "can't be blank", user.errors[:username].join
end
test 'should ignore password and its confirmation if they are blank' do
user = create_user
assert user.update_with_password(:current_password => '123456', :email => "new@email.com")
assert_equal "new@email.com", user.email
assert user.update_with_password(:current_password => '123456', :email => "new@example.com")
assert_equal "new@example.com", user.email
end
test 'should not update password with invalid confirmation' do
@@ -108,6 +150,19 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
assert user.password_confirmation.blank?
end
test 'should update the user without password' do
user = create_user
user.update_without_password(:email => 'new@example.com')
assert_equal 'new@example.com', user.email
end
test 'should not update password without password' do
user = create_user
user.update_without_password(:password => 'pass321', :password_confirmation => 'pass321')
assert !user.reload.valid_password?('pass321')
assert user.valid_password?('123456')
end
test 'downcase_keys with validation' do
user = User.create(:email => "HEllO@example.com", :password => "123456")
user = User.create(:email => "HEllO@example.com", :password => "123456")

View File

@@ -31,7 +31,7 @@ class EncryptableTest < ActiveSupport::TestCase
test 'should generate a base64 hash using SecureRandom for password salt' do
swap_with_encryptor Admin, :sha1 do
ActiveSupport::SecureRandom.expects(:base64).with(15).returns('friendly_token')
SecureRandom.expects(:base64).with(15).returns('friendly_token')
assert_equal 'friendly_token', create_admin.password_salt
end
end

View File

@@ -163,12 +163,12 @@ class LockableTest < ActiveSupport::TestCase
end
test 'should return a new user if no email was found' do
unlock_user = User.send_unlock_instructions(:email => "invalid@email.com")
unlock_user = User.send_unlock_instructions(:email => "invalid@example.com")
assert_not unlock_user.persisted?
end
test 'should add error to new user email if no email was found' do
unlock_user = User.send_unlock_instructions(:email => "invalid@email.com")
unlock_user = User.send_unlock_instructions(:email => "invalid@example.com")
assert_equal 'not found', unlock_user.errors[:email].join
end

View File

@@ -72,7 +72,7 @@ class RecoverableTest < ActiveSupport::TestCase
end
test 'should return a new record with errors if user was not found by e-mail' do
reset_password_user = User.send_reset_password_instructions(:email => "invalid@email.com")
reset_password_user = User.send_reset_password_instructions(:email => "invalid@example.com")
assert_not reset_password_user.persisted?
assert_equal "not found", reset_password_user.errors[:email].join
end
@@ -192,8 +192,34 @@ class RecoverableTest < ActiveSupport::TestCase
assert user.valid_password?(old_password)
assert_not user.valid_password?('new_password')
assert_equal "is invalid", reset_password_user.errors[:reset_password_token].join
assert_equal "has expired, please request a new one", reset_password_user.errors[:reset_password_token].join
end
end
test 'should save the model when the reset_password_sent_at doesnt exist' do
user = create_user
def user.respond_to?(meth, *)
if meth == :reset_password_sent_at=
false
else
super
end
end
user.send_reset_password_instructions
user.reload
assert_not_nil user.reset_password_token
end
test 'should have valid period if does not respond to reset_password_sent_at' do
user = create_user
def user.respond_to?(meth, *)
if meth == :reset_password_sent_at
false
else
super
end
end
assert user.reset_password_period_valid?
end
end

View File

@@ -15,6 +15,14 @@ module SharedRememberableTest
resource.forget_me!
assert resource.remember_created_at.nil?
end
test 'forget_me should not try to update resource if it has been destroyed' do
resource = create_resource
resource.destroy
resource.expects(:remember_created_at).never
resource.expects(:save).never
resource.forget_me!
end
test 'remember is expired if not created at timestamp is set' do
assert create_resource.remember_expired?

View File

@@ -0,0 +1,38 @@
require 'test_helper'
class SerializableTest < ActiveSupport::TestCase
setup do
@user = create_user
end
test 'should not include unsafe keys on XML' do
assert_match /email/, @user.to_xml
assert_no_match /confirmation-token/, @user.to_xml
end
test 'should not include unsafe keys on XML even if a new except is provided' do
assert_no_match /email/, @user.to_xml(:except => :email)
assert_no_match /confirmation-token/, @user.to_xml(:except => :email)
end
test 'should include unsafe keys on XML if a force_except is provided' do
assert_no_match /email/, @user.to_xml(:force_except => :email)
assert_match /confirmation-token/, @user.to_xml(:force_except => :email)
end
test 'should not include unsafe keys on JSON' do
assert_match /"email":/, @user.to_json
assert_no_match /"confirmation_token":/, @user.to_json
end
test 'should not include unsafe keys on JSON even if a new except is provided' do
assert_no_match /"email":/, @user.to_json(:except => :email)
assert_no_match /"confirmation_token":/, @user.to_json(:except => :email)
end
test 'should include unsafe keys on JSON if a force_except is provided' do
assert_no_match /"email":/, @user.to_json(:force_except => :email)
assert_match /"confirmation_token":/, @user.to_json(:force_except => :email)
end
end

View File

@@ -27,12 +27,6 @@ class TokenAuthenticatableTest < ActiveSupport::TestCase
end
test 'should return nil when authenticating an invalid user by authentication token' do
if DEVISE_ORM == :mongoid
raise 'There is an incompatibility between Devise and Mongoid' <<
' that makes this test break. For more information, check' <<
' this issue: https://github.com/mongoid/mongoid/issues/725'
end
user = create_user
user.ensure_authentication_token!
user.confirm!

View File

@@ -8,7 +8,7 @@ class ValidatableTest < ActiveSupport::TestCase
assert_equal 'can\'t be blank', user.errors[:email].join
end
test 'should require uniqueness of email, allowing blank' do
test 'should require uniqueness of email if email has changed, allowing blank' do
existing_user = create_user
user = new_user(:email => '')
@@ -18,18 +18,24 @@ class ValidatableTest < ActiveSupport::TestCase
user.email = existing_user.email
assert user.invalid?
assert_match(/taken/, user.errors[:email].join)
user.save(:validate => false)
assert user.valid?
end
test 'should require correct email format, allowing blank' do
test 'should require correct email format if email has changed, allowing blank' do
user = new_user(:email => '')
assert user.invalid?
assert_not_equal 'is invalid', user.errors[:email].join
%w(invalid_email_format email@invalid invalid$character@mail.com other@not 123).each do |email|
%w(invalid_email_format 123 $$$ \(\) ).each do |email|
user.email = email
assert user.invalid?, 'should be invalid with email ' << email
assert_equal 'is invalid', user.errors[:email].join
end
user.save(:validate => false)
assert user.valid?
end
test 'should accept valid emails' do
@@ -85,12 +91,19 @@ class ValidatableTest < ActiveSupport::TestCase
user = create_user.reload
user.password = user.password_confirmation = nil
assert user.valid?
user.password_confirmation = 'confirmation'
assert user.invalid?
assert_not (user.errors[:password].join =~ /is too long/)
end
test 'should complain about length even if possword is not required' do
user = new_user(:password => 'x'*129, :password_confirmation => 'x'*129)
user.stubs(:password_required?).returns(false)
assert user.invalid?
assert_equal 'is too long (maximum is 128 characters)', user.errors[:password].join
end
test 'shuold not be included in objects with invalid API' do
assert_raise RuntimeError do
Class.new.send :include, Devise::Models::Validatable

View File

@@ -10,6 +10,10 @@ class WithValidation < Admin
devise :database_authenticatable, :validatable, :password_length => 2..6
end
class UserWithValidation < User
validates_presence_of :username
end
class Several < Admin
devise :validatable
devise :lockable

View File

@@ -35,6 +35,10 @@ class OmniAuthRoutesTest < ActionController::TestCase
end
end
test 'should generate authorization path for named open_id omniauth' do
assert_match "/users/auth/google", @controller.omniauth_authorize_path(:user, :google)
end
test 'should generate authorization path with params' do
assert_match "/users/auth/open_id?openid_url=http%3A%2F%2Fyahoo.com",
@controller.omniauth_authorize_path(:user, :open_id, :openid_url => "http://yahoo.com")

View File

@@ -5,6 +5,15 @@ class HomeController < ApplicationController
def private
end
def user_dashboard
end
def admin_dashboard
end
def join
end
def set
session["devise.foo_bar"] = "something"
head :ok

View File

@@ -1,5 +1,6 @@
class UsersController < ApplicationController
before_filter :authenticate_user!, :except => :accept
prepend_before_filter :current_user, :only => :exhibit
before_filter :authenticate_user!, :except => [:accept, :exhibit]
respond_to :html, :xml
def index
@@ -11,6 +12,10 @@ class UsersController < ApplicationController
@current_user = current_user
end
def exhibit
render :text => current_user ? "User is authenticated" : "User is not authenticated"
end
def expire
user_session['last_request_at'] = 31.minutes.ago.utc
render :text => 'User will be expired on next request'

View File

@@ -0,0 +1 @@
Admin dashboard

View File

@@ -0,0 +1 @@
Join

View File

@@ -0,0 +1 @@
User dashboard

View File

@@ -35,6 +35,11 @@ Devise.setup do |config|
# These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [ :email ]
# Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [ :email ]
# Tell if authentication through request.params is enabled. True by default.
# config.params_authenticatable = true
@@ -172,6 +177,7 @@ Devise.setup do |config|
# ==> OmniAuth
config.omniauth :facebook, 'APP_ID', 'APP_SECRET', :scope => 'email,offline_access'
config.omniauth :open_id
config.omniauth :open_id, :name => 'google', :identifier => 'https://www.google.com/accounts/o8/id'
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or

View File

@@ -3,6 +3,10 @@ Rails.application.routes.draw do
resources :users, :only => [:index] do
get :expire, :on => :member
get :accept, :on => :member
authenticate :user do
post :exhibit, :on => :member
end
end
resources :admins, :only => [:index]
@@ -19,7 +23,7 @@ Rails.application.routes.draw do
match "/sign_in", :to => "devise/sessions#new"
# Admin scope
devise_for :admin, :path => "admin_area", :controllers => { :sessions => "admins/sessions" }, :skip => :passwords
devise_for :admin, :path => "admin_area", :controllers => { :sessions => :"admins/sessions" }, :skip => :passwords
match "/admin_area/home", :to => "admins#index", :as => :admin_root
match "/anywhere", :to => "foo#bar", :as => :new_admin_password
@@ -28,6 +32,29 @@ Rails.application.routes.draw do
match "/private", :to => "home#private", :as => :private
end
authenticated :admin do
match "/dashboard", :to => "home#admin_dashboard"
end
authenticated do
match "/dashboard", :to => "home#user_dashboard"
end
unauthenticated do
match "/join", :to => "home#join"
end
# Routes for constraints testing
devise_for :headquarters_admin, :class_name => "Admin", :path => "headquarters", :constraints => {:host => /192\.168\.1\.\d\d\d/}
constraints(:host => /192\.168\.1\.\d\d\d/) do
devise_for :homebase_admin, :class_name => "Admin", :path => "homebase"
end
# Routes for format=false testing
devise_for :htmlonly_admin, :class_name => "Admin", :skip => [:confirmations, :unlocks], :path => "htmlonly_admin", :format => false
devise_for :htmlonly_users, :class_name => "User", :only => [:confirmations, :unlocks], :path => "htmlonly_users", :format => false
# Other routes for routing_test.rb
devise_for :reader, :class_name => "User", :only => :passwords
@@ -53,5 +80,6 @@ Rails.application.routes.draw do
match "/set", :to => "home#set"
match "/unauthenticated", :to => "home#unauthenticated"
root :to => "home#index"
end
end

View File

@@ -6,6 +6,8 @@ module SharedUser
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable, :omniauthable
attr_accessor :other_key
# They need to be included after Devise is called.
extend ExtendMethods
end

View File

@@ -96,6 +96,11 @@ class DefaultRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'users/omniauth_callbacks', :action => 'facebook'}, {:path => 'users/auth/facebook/callback', :method => :post})
assert_named_route "/users/auth/facebook/callback", :user_omniauth_callback_path, :facebook
# named open_id
assert_recognizes({:controller => 'users/omniauth_callbacks', :action => 'google'}, {:path => 'users/auth/google/callback', :method => :get})
assert_recognizes({:controller => 'users/omniauth_callbacks', :action => 'google'}, {:path => 'users/auth/google/callback', :method => :post})
assert_named_route "/users/auth/google/callback", :user_omniauth_callback_path, :google
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'ysers/omniauth_callbacks', :action => 'twitter'}, {:path => 'users/auth/twitter/callback', :method => :get})
end
@@ -171,6 +176,55 @@ class CustomizedRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'devise/sessions', :action => 'destroy'}, {:path => '/sign_out_via/delete_or_posts/sign_out', :method => :get})
end
end
test 'map with constraints defined in hash' do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => 'http://192.168.1.100/headquarters/sign_up', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => 'http://10.0.0.100/headquarters/sign_up', :method => :get})
end
end
test 'map with constraints defined in block' do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => 'http://192.168.1.100/homebase/sign_up', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => 'http://10.0.0.100//homebase/sign_up', :method => :get})
end
end
test 'map with format false for sessions' do
assert_recognizes({:controller => 'devise/sessions', :action => 'new'}, {:path => '/htmlonly_admin/sign_in', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/sessions', :action => 'new'}, {:path => '/htmlonly_admin/sign_in.xml', :method => :get})
end
end
test 'map with format false for passwords' do
assert_recognizes({:controller => 'devise/passwords', :action => 'create'}, {:path => '/htmlonly_admin/password', :method => :post})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/passwords', :action => 'create'}, {:path => '/htmlonly_admin/password.xml', :method => :post})
end
end
test 'map with format false for registrations' do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => '/htmlonly_admin/sign_up', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => '/htmlonly_admin/sign_up.xml', :method => :get})
end
end
test 'map with format false for confirmations' do
assert_recognizes({:controller => 'devise/confirmations', :action => 'show'}, {:path => '/htmlonly_users/confirmation', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/confirmations', :action => 'show'}, {:path => '/htmlonly_users/confirmation.xml', :method => :get})
end
end
test 'map with format false for unlocks' do
assert_recognizes({:controller => 'devise/unlocks', :action => 'show'}, {:path => '/htmlonly_users/unlock', :method => :get})
assert_raise ActionController::RoutingError do
assert_recognizes({:controller => 'devise/unlocks', :action => 'show'}, {:path => '/htmlonly_users/unlock.xml', :method => :get})
end
end
end
class ScopedRoutingTest < ActionController::TestCase

View File

@@ -19,7 +19,7 @@ class ActiveSupport::TestCase
def generate_unique_email
@@email_count ||= 0
@@email_count += 1
"test#{@@email_count}@email.com"
"test#{@@email_count}@example.com"
end
def valid_attributes(attributes={})
@@ -57,4 +57,4 @@ class ActiveSupport::TestCase
object.send :"#{key}=", value
end
end
end
end