Compare commits

...

109 Commits

Author SHA1 Message Date
José Valim
071f54ac59 Release v1.4.4 with a fix for those using :skip in routes. 2011-08-31 16:45:12 +02:00
José Valim
8796c1c601 Do not always skip helpers, instead provide :skip_helpers as option to trigger it manually 2011-08-31 12:02:57 +02:00
José Valim
ef4eb47d50 Release v1.4.3 with improve Rails 3.1 compatibility. 2011-08-30 14:35:19 +02:00
José Valim
295088f9e8 Depend on latest Rails. 2011-08-30 14:33:23 +02:00
José Valim
4222afe3c7 Tidy up latest commits, update CHANGELOG. 2011-08-30 10:50:40 +02:00
José Valim
79d89a3c20 Merge pull request #1297 from seanski/master
Patch for Issue 1259 -- New
2011-08-30 01:44:30 -07:00
Sean Hill
8ad414b217 Added a helper to look for an modifying migration. If one is found during :revoke, the modifying migration is deleted. If the modifying migration is not found, the creating migration is deleted. 2011-08-29 17:53:32 -05:00
José Valim
7b1418be3b Improve the regexp a bit more. 2011-08-29 14:55:56 +02:00
José Valim
7396c6911d Quack, quack, quack. Use duck typing instead of hardcoding everything, closes #1281. 2011-08-29 14:40:49 +02:00
José Valim
673c707085 Update CHANGELOG. 2011-08-29 14:31:09 +02:00
José Valim
f21d05a235 Only generate helpers for the used mappings, closes #1288 2011-08-29 14:29:38 +02:00
José Valim
1dd19c1a72 Wrap :actions constraints in the proper hash, closes #1280 2011-08-29 13:29:30 +02:00
José Valim
a8d9695324 Simplify regexp and ensure it runs against UTF8 chars, closes #1235 2011-08-29 13:15:40 +02:00
Sean Hill
99d539b9a7 Chagned the copy_devise_migration method to properly handle the :revoke behavior using @daf's commit: acf7e9e2ad as a guide. 2011-08-28 16:29:06 -05:00
Sean Hill
179cb2c88a Changed the order of the devise_generator methods to create the model after the migration to properly use model_exists?, and I added tests to prove the generator works. 2011-08-28 16:02:58 -05:00
Sean Hill
9629da863d Added a template to create a migration when the model already exists. Changed the generator code to check if model exists, and if it does, call the new template instead of the standard template. 2011-08-25 07:05:19 -05:00
José Valim
4f76e6f577 Depend on latest bcrypt. 2011-08-25 13:22:48 +02:00
José Valim
0f3cef4091 Merge pull request #1282 from apocalyptiq/master
Allow to set default 'from' header of Devise mails in custom mailer class
2011-08-20 13:48:45 -07:00
Szymon Przybył
7665cbf21d Allow to set default 'from' header of Devise mails in custom mailer class 2011-08-20 22:35:47 +02:00
José Valim
b9413ab316 Merge pull request #1275 from bquorning/it's_not_"it's",_it's_"its"
Fix usage of “its” / “it’s” in documentation
2011-08-16 15:20:01 -07:00
Benjamin Quorning
a3bff7d6a2 Fix usage of "its" / "it's" in documentation 2011-08-16 22:15:25 +02:00
José Valim
7e54dc7903 Edited lib/devise/models/lockable.rb via GitHub 2011-08-16 08:00:44 -07:00
José Valim
fb1220af97 Merge pull request #1264 from invisiblefunnel/master
Fixes small typo in routes documentation
2011-08-11 20:40:25 -07:00
Daniel Whalen
282c7e85ec Fix documentation typo 2011-08-11 20:16:20 -07:00
José Valim
bc15bb53dc Merge pull request #1261 from rumblestrut/patch-1
Changed "It's composed of 12 modules:" to "It's comprised of 12 modules .
2011-08-10 22:20:08 -07:00
Eric J. Gruber
a729e059de Changed "It's composed of 12 modules:" to "It's comprised of 12 modules ..." Comprised is the better word choice in this instance, meaning "Consist of; be made up of." 2011-08-10 21:46:52 -05:00
José Valim
7c628f7689 Merge pull request #1237 from novalis/master
friendly_token should be friendlier
2011-08-10 17:25:06 -07:00
David Turner
6d65c28f1f remove hard-to-distinguish characters from friendly_token 2011-08-10 12:44:30 -04:00
José Valim
31e4ef5e4b Merge pull request #1258 from wildchild/master
Don't require explicit scope in authenticate router extension
2011-08-10 05:23:29 -07:00
Alexander Uvarov
d38341b5e0 Don't require explicit scope in authenticate router extension 2011-08-10 15:41:06 +06:00
Rafael Mendonça França
ff90d9da9c Merge pull request #1251 from michaelklishin/patch-1
Re-add JRuby to the build matrix
2011-08-08 08:01:41 -07:00
Michael Klishin
84c62ed034 Re-add JRuby to the build matrix
We hopefully resolved our JRuby woes on travis-ci.org.
2011-08-07 20:55:59 +04:00
José Valim
e76ba05516 Merge pull request #1249 from ches/mongoid-fixes
Tests that should no longer exclude Mongoid
2011-08-06 08:21:48 -07:00
José Valim
90d99b16f1 Merge pull request #1250 from ches/unenforced-auth-keys
Fix optional enforcement of particular authentication keys
2011-08-06 08:20:30 -07:00
Ches Martin
3cedba1de8 Fix optional enforcement of particular authentication keys
Documentation states that authentication_keys should accept a hash with
values indicating whether or not each key is required. This was added in
b2066cc2 but tests only covered request_keys, and 29afe2d2 later broke
it with a << array operator.
2011-08-06 20:07:49 +07:00
Ches Martin
ccbf26be0a Tests that should no longer exclude Mongoid 2011-08-06 19:28:19 +07:00
José Valim
6448490de5 Edited lib/generators/templates/devise.rb via GitHub 2011-08-05 20:45:39 +03:00
José Valim
98651dae9d Merge pull request #1241 from dbloete/patch-1
Speed up tests by defaulting to 1 stretch in test env
2011-08-05 10:43:54 -07:00
José Valim
bf57ef400d Merge pull request #1247 from michaelklishin/patch-1
It is #rbxday, lets test against Rubinius 2.0 preview, too
2011-08-05 09:36:31 -07:00
Michael Klishin
27cae04446 It is #rbxday, lets test against Rubinius 2.0 preview, too 2011-08-05 20:34:33 +04:00
José Valim
430eadc1b2 Merge pull request #1245 from michaelklishin/patch-1
Disable testing against JRuby, it has known issues on travis-ci.org.
2011-08-05 09:31:36 -07:00
Michael Klishin
e954737a92 Disable testing against JRuby, it has known issues on travis-ci.org.
We will figure out JRuby issues with the JRuby core team and let you know
2011-08-05 16:26:11 +04:00
José Valim
191d5222e7 Merge pull request #1244 from ches/docs
Doc tweaks
2011-08-05 05:22:24 -07:00
Ches Martin
e77af4b40f Doc: DB authenticatable schema no longer includes salt 2011-08-05 20:13:01 +08:00
Ches Martin
da3c47259d Doc: DB authenticatable schema no longer includes salt 2011-08-05 17:33:50 +07:00
Ches Martin
c95575b284 Doc: DatabaseAuthenticatable still uses pepper option 2011-08-05 17:08:26 +07:00
José Valim
7a5086a2be Merge pull request #1242 from ches/mongoid-fixes
Fix failing Mongoid tests
2011-08-04 13:38:57 -07:00
Ches Martin
c3a665320e Merge branch 'master' into mongoid-fixes
* master:
  Untrack Gemfile.lock

Conflicts:
	Gemfile.lock
2011-08-05 03:19:05 +07:00
Dennis Reimann
372dfecf05 Added a comment about the stretch change 2011-08-04 23:00:12 +03:00
Dennis Reimann
68bd6809a7 Speed up tests by defaulting to 1 stretch in test env 2011-08-04 22:50:06 +03:00
Ches Martin
7bfdd07a71 Initializer caveats warning for Mongoid < 2.1 2011-08-05 02:41:31 +07:00
Ches Martin
6bc93173f1 Restore forlorn Mongoid generator tests 2011-08-05 02:40:42 +07:00
Ches Martin
55af9f8a40 Fix failing Mongoid tests
- attr_accessible not set for test user model, making Serializable tests
  inaccurate
- Mongoid does not `include_root_in_json` by default, so enable this for
  consistency with AR tests
- Mark tests pending for Mongoid < 2.1 that fail there due to known bugs
- Add `:mongoid` key for i18n model labels
- Remove outdated shim of `update_attribute` that caused mass assignment
  security to be applied (ugh, that took awhile to find)
2011-08-05 01:38:42 +07:00
José Valim
866b35d27c Merge pull request #1239 from ches/gemfile-lock
Untrack Gemfile.lock
2011-08-04 04:16:50 -07:00
Ches Martin
5374119f18 Untrack Gemfile.lock
Gemfile.lock is for app deployment, not public library development:

http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
2011-08-04 11:33:53 +07:00
José Valim
919404df53 Merge pull request #1228 from dasil003/master
Set up Recoverable to automatically confirm! if Comfirmable
2011-08-02 12:41:09 -07:00
Gabe da Silveira
fd07c74578 Refactor to use method and override 2011-08-02 12:08:38 -07:00
José Valim
560961e3fc Merge pull request #1233 from asanghi/master
sign_in should return true and assert that. fixes #1232
2011-08-02 03:04:51 -07:00
Aditya Sanghi
873e49ab66 sign_in should return true in case user is already signed in and assert that. 2011-08-02 14:35:46 +05:30
José Valim
0f8f786f9f Use a default key on inactive reason. 2011-08-02 11:18:23 +03:00
José Valim
7b3e267729 Merge pull request #1231 from hck/master
added I18n support to inactive_message in RegistrationsController
2011-08-02 01:14:16 -07:00
hck
7264bb55b2 added I18n support to inactive_message in RegistrationsController 2011-08-02 08:11:25 +03:00
Gabe da Silveira
ed6dfed67f Resetting password should confirm implicitly 2011-07-29 14:26:19 -07:00
Gabe da Silveira
3488066031 Spelling correction 2011-07-29 14:17:31 -07:00
José Valim
bc9ea93f50 Merge pull request #1224 from gtalton/patch-2
Edited lib/devise/rails/routes.rb via GitHub
2011-07-29 03:18:40 -07:00
Gerald Talton
1920b79a0d Edited lib/devise/rails/routes.rb via GitHub 2011-07-28 15:23:23 -05:00
José Valim
9131570703 Merge pull request #1221 from hazah/master
Change :except to :only for registration routes
2011-07-26 10:45:36 -07:00
=
770c1a973f Changed :except to :only for devise_registration method of the routing mapper. 2011-07-26 13:42:00 -04:00
José Valim
b7b21a0b72 Merge pull request #1211 from mathieuravaux/master
Issue #1206 fix ((TokenAuthenticatable#params_auth_hash behavior)
2011-07-25 05:11:26 -07:00
Mathieu Ravaux
4d91fb4910 Untangled the logic in Devise::Strategies::TokenAuthenticatable#params_auth_hash 2011-07-25 09:10:25 +02:00
Rafael Mendonça França
4ce9d6f29a Merge pull request #1212 from mattupstate/patch-1
Typo in doc comment
2011-07-23 15:06:02 -07:00
Matt Wright
d2a4c503f7 Typo in doc comment 2011-07-23 15:03:10 -07:00
Mathieu Ravaux
a2700bc17f Fixed issue #1206: TokenAuthenticatable#params_auth_hash behaving in an unexpected way for the authentication token lookup 2011-07-23 17:45:45 +02:00
José Valim
0a04d73883 Merge pull request #1198 from andriytyurnikov/issue_1197
div instead of p in forms
2011-07-14 05:47:46 -07:00
Andriy Tyurnikov
502dae3c1f div instead of p in forms 2011-07-14 15:43:10 +03:00
José Valim
e9c766277d Merge pull request #1194 from waynn/patch-1
quick typo fix
2011-07-11 11:37:18 -07:00
Waynn Lue
762f3907fa quick typo fix 2011-07-11 11:30:52 -07:00
José Valim
3698b64a10 Merge pull request #1190 from joliss/whitespace
Remove whitespace from generator template
2011-07-08 11:25:35 -07:00
Jo Liss
2a6c160d39 Remove whitespace from generator template 2011-07-08 18:41:16 +02:00
José Valim
795754cd60 Merge pull request #1189 from netmute/master
Add ability to override redirect_to path after account confirmation.
2011-07-08 03:27:21 -07:00
Simon Ernst
517343ddb5 Add ability to override redirect_to path after account confirmation. 2011-07-08 11:21:01 +02:00
Rodrigo Flores
90a5e2f9af Added rdoc on the Gemfile 2011-07-02 01:33:34 -03:00
Rodrigo Flores
93180fbb6d Removed a rdoc deprecation warning 2011-07-01 20:37:21 -03:00
José Valim
04cb78947a Edited CHANGELOG.rdoc via GitHub 2011-07-01 04:23:31 -07:00
José Valim
5423f2d5cb Properly deprecate setup_mail 2011-07-01 04:23:12 -07:00
José Valim
51d15b7293 Add more missing :require => true 2011-06-30 15:29:35 -03:00
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
87 changed files with 995 additions and 457 deletions

4
.gitignore vendored
View File

@@ -7,4 +7,6 @@ coverage/*
rdoc/*
pkg
log
test/tmp/*
test/tmp/*
Gemfile.lock

View File

@@ -3,5 +3,6 @@ rvm:
- 1.8.7
- 1.9.2
- ree
- jruby
- rubinius
- rbx
- rbx-2.0
- jruby

View File

@@ -1,3 +1,40 @@
== 1.4.4
* bug fix
* Do not always skip helpers, instead provide :skip_helpers as option to trigger it manually
== 1.4.3
* enhancements
* Improve Rails 3.1 compatibility
* Use serialize_into_session and serialize_from_session in Warden serialize to improve extensibility
* bug fix
* Generator properly generates a change_table migration if a model already exists
* Properly deprecate setup_mail
* Fix encoding issues with email regexp
* Only generate helpers for the used mappings
* Wrap :action constraints in the proper hash
* deprecations
* Loosened the used email regexp to simply assert the existent of "@". If someone relies on a more strict regexp, they may use https://github.com/SixArm/sixarm_ruby_email_address_validation
== 1.4.2
* bug fix
* 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

View File

@@ -2,10 +2,12 @@ source "http://rubygems.org"
gemspec
gem "rails", "~> 3.0.7"
gem "rails", "~> 3.1.0.rc8"
gem "oa-oauth", '~> 0.2.0', :require => "omniauth/oauth"
gem "oa-openid", '~> 0.2.0', :require => "omniauth/openid"
gem "rdoc"
group :test do
gem "webrat", "0.7.2", :require => false
gem "mocha", :require => false
@@ -26,7 +28,7 @@ platforms :ruby do
group :mongoid do
gem "mongo", "~> 1.3.0"
gem "mongoid", "2.0.1"
gem "mongoid", "~> 2.0"
gem "bson_ext", "~> 1.3.0"
end
end

View File

@@ -1,159 +0,0 @@
PATH
remote: .
specs:
devise (1.4.0.dev)
bcrypt-ruby (~> 2.1.2)
orm_adapter (~> 0.0.3)
warden (~> 1.0.3)
GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.7)
actionpack (= 3.0.7)
mail (~> 2.2.15)
actionpack (3.0.7)
activemodel (= 3.0.7)
activesupport (= 3.0.7)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.5.0)
rack (~> 1.2.1)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.7)
activesupport (= 3.0.7)
builder (~> 2.1.2)
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.7)
activemodel (= 3.0.7)
activesupport (= 3.0.7)
activesupport (3.0.7)
addressable (2.2.4)
arel (2.0.9)
bcrypt-ruby (2.1.4)
bcrypt-ruby (2.1.4-java)
bson (1.3.0)
bson_ext (1.3.0)
builder (2.1.2)
columnize (0.3.2)
erubis (2.6.6)
abstract (>= 1.0.0)
faraday (0.5.7)
addressable (~> 2.2.4)
multipart-post (~> 1.1.0)
rack (>= 1.1.0, < 2)
i18n (0.5.0)
jdbc-sqlite3 (3.6.14.2.056-java)
linecache (0.43)
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.3.0)
bson (>= 1.3.0)
mongoid (2.0.1)
activemodel (~> 3.0)
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.3.1)
nokogiri (1.4.3.1-java)
weakling (>= 0.0.3)
oa-core (0.2.0)
rack (~> 1.1)
oa-oauth (0.2.0)
multi_json (~> 0.0.2)
nokogiri (~> 1.4.2)
oa-core (= 0.2.0)
oauth (~> 0.4.0)
oauth2 (~> 0.1.1)
oa-openid (0.2.0)
oa-core (= 0.2.0)
rack-openid (~> 1.2.0)
ruby-openid-apps-discovery
oauth (0.4.4)
oauth2 (0.1.1)
faraday (~> 0.5.0)
multi_json (~> 0.0.4)
orm_adapter (0.0.4)
polyglot (0.3.1)
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.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.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)
ruby-debug (0.10.4)
columnize (>= 0.1)
ruby-debug-base (~> 0.10.4.0)
ruby-debug-base (0.10.4)
linecache (>= 0.3)
ruby-openid (2.1.8)
ruby-openid-apps-discovery (1.2.0)
ruby-openid (>= 2.1.7)
sqlite3 (1.3.3)
sqlite3-ruby (1.3.3)
sqlite3 (>= 1.3.3)
thor (0.14.6)
treetop (1.4.9)
polyglot (>= 0.3.1)
tzinfo (0.3.27)
warden (1.0.3)
rack (>= 1.0.0)
weakling (0.0.4-java)
webrat (0.7.2)
nokogiri (>= 1.2.0)
rack (>= 1.0)
rack-test (>= 0.5.3)
will_paginate (3.0.pre2)
PLATFORMS
java
ruby
DEPENDENCIES
activerecord-jdbcsqlite3-adapter
bson_ext (~> 1.3.0)
devise!
mocha
mongo (~> 1.3.0)
mongoid (= 2.0.1)
oa-oauth (~> 0.2.0)
oa-openid (~> 0.2.0)
rails (~> 3.0.7)
ruby-debug (>= 0.10.3)
sqlite3-ruby
webrat (= 0.7.2)

View File

@@ -7,7 +7,7 @@ Devise is a flexible authentication solution for Rails based on Warden. It:
* Allows you to have multiple roles (or models/scopes) signed in at the same time;
* Is based on a modularity concept: use just what you really need.
It's composed of 12 modules:
It's comprised of 12 modules:
* Database Authenticatable: encrypts and stores a password in the database to validate the authenticity of a user while signing in. The authentication can be done both through POST requests or HTTP Basic Authentication.
* Token Authenticatable: signs in a user based on an authentication token (also known as "single access token"). The token can be given both through query string or HTTP Basic Authentication.
@@ -97,7 +97,7 @@ If you are building your first Rails application, we recommend you to *not* use
* Michael Hartl's online book: http://railstutorial.org/chapters/modeling-and-viewing-users-two#top
* Ryan Bates' Railscast: http://railscasts.com/episodes/250-authentication-from-scratch
Once you have solidified you understanding of Rails and authentication mechanisms, we assure you Devise will be very pleasant to work with. :)
Once you have solidified your understanding of Rails and authentication mechanisms, we assure you Devise will be very pleasant to work with. :)
== Getting started
@@ -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.

View File

@@ -1,7 +1,7 @@
# encoding: UTF-8
require 'rake/testtask'
require 'rake/rdoctask'
require 'rdoc/task'
desc 'Default: run tests for all ORMs.'
task :default => :test

View File

@@ -26,7 +26,7 @@ class Devise::ConfirmationsController < ApplicationController
if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_navigational_format?
sign_in(resource_name, resource)
respond_with_navigational(resource){ redirect_to redirect_location(resource_name, resource) }
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
else
respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render_with_scope :new }
end
@@ -38,4 +38,10 @@ class Devise::ConfirmationsController < ApplicationController
def after_resending_confirmation_instructions_path_for(resource_name)
new_session_path(resource_name)
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
redirect_location(resource_name, resource)
end
end

View File

@@ -19,7 +19,7 @@ class Devise::RegistrationsController < ApplicationController
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
else
set_flash_message :notice, :inactive_signed_up, :reason => resource.inactive_message.to_s if is_navigational_format?
set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) if is_navigational_format?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
@@ -84,10 +84,16 @@ class Devise::RegistrationsController < ApplicationController
end
# Overwrite redirect_for_sign_in so it takes uses after_sign_up_path_for.
def redirect_location(scope, resource) #:nodoc:
def redirect_location(scope, resource)
stored_location_for(scope) || after_sign_up_path_for(resource)
end
# Returns the inactive reason translated.
def inactive_reason(resource)
reason = resource.inactive_message.to_s
I18n.t("devise.registrations.reasons.#{reason}", :default => reason)
end
# The path used after sign up for inactive accounts. You need to overwrite
# this method in your own RegistrationsController.
def after_inactive_sign_up_path_for(resource)

View File

@@ -38,8 +38,10 @@ class Devise::SessionsController < ApplicationController
protected
def stub_options(resource)
array = resource_class.authentication_keys.dup
array << :password if resource.respond_to?(:password)
{ :methods => array, :only => [:password] }
methods = resource_class.authentication_keys.dup
methods = methods.keys if methods.is_a?(Hash)
methods << :password if resource.respond_to?(:password)
{ :methods => methods, :only => [:password] }
end
end
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,10 +3,10 @@
<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.submit "Resend confirmation instructions" %></p>
<div><%= f.submit "Resend confirmation instructions" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -4,13 +4,13 @@
<%= devise_error_messages! %>
<%= f.hidden_field :reset_password_token %>
<p><%= f.label :password, "New password" %><br />
<%= f.password_field :password %></p>
<div><%= f.label :password, "New password" %><br />
<%= f.password_field :password %></div>
<p><%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation %></p>
<div><%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation %></div>
<p><%= f.submit "Change my password" %></p>
<div><%= f.submit "Change my password" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -3,10 +3,10 @@
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.submit "Send me reset password instructions" %></p>
<div><%= f.submit "Send me reset password instructions" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -3,19 +3,19 @@
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password %></p>
<div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password %></div>
<p><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></p>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<p><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %></p>
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password %></div>
<p><%= f.submit "Update" %></p>
<div><%= f.submit "Update" %></div>
<% end %>
<h3>Cancel my account</h3>

View File

@@ -3,16 +3,16 @@
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<p><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></p>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<p><%= f.submit "Sign up" %></p>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -1,17 +1,17 @@
<h2>Sign in</h2>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<% if devise_mapping.rememberable? -%>
<p><%= f.check_box :remember_me %> <%= f.label :remember_me %></p>
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
<% end -%>
<p><%= f.submit "Sign in" %></p>
<div><%= f.submit "Sign in" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -3,10 +3,10 @@
<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
<%= devise_error_messages! %>
<p><%= f.label :email %><br />
<%= f.email_field :email %></p>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<p><%= f.submit "Resend unlock instructions" %></p>
<div><%= f.submit "Resend unlock instructions" %></div>
<% end %>
<%= render :partial => "devise/shared/links" %>

View File

@@ -37,6 +37,10 @@ en:
inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
updated: 'You updated your account successfully.'
destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
reasons:
inactive: 'inactive'
unconfirmed: 'unconfirmed'
locked: 'locked'
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.'

View File

@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
s.add_dependency("warden", "~> 1.0.3")
s.add_dependency("orm_adapter", "~> 0.0.3")
s.add_dependency("bcrypt-ruby", "~> 2.1.2")
s.add_dependency("bcrypt-ruby", "~> 3.0")
end

View File

@@ -11,7 +11,6 @@ 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'
@@ -31,6 +30,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'
@@ -101,11 +104,11 @@ module Devise
mattr_accessor :http_authentication_realm
@@http_authentication_realm = "Application"
# 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).
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
mattr_accessor :email_regexp
@@email_regexp = Devise::Email::EXACT_PATTERN
@@email_regexp = /\A[^@]+@([^@\.]+\.)+[^@\.]+\z/
# Range validation for password length
mattr_accessor :password_length
@@ -394,6 +397,12 @@ module Devise
Rails::VERSION::STRING[0,3] != "3.0"
end
# Renegeres url helpers considering Devise.mapping
def self.regenerate_helpers!
Devise::Controllers::UrlHelpers.remove_helpers!
Devise::Controllers::UrlHelpers.generate_helpers!
end
# A method used internally to setup warden manager from the Rails initialize
# block.
def self.configure_warden! #:nodoc:
@@ -413,7 +422,7 @@ module Devise
# Generate a friendly string randomically to be used as token.
def self.friendly_token
SecureRandom.base64(15).tr('+/=', 'xyz')
SecureRandom.base64(15).tr('+/=lIO0', 'pqrsxyz')
end
# constant-time comparison algorithm to prevent timing attacks

View File

@@ -106,6 +106,7 @@ module Devise
warden.session_serializer.store(resource, scope)
elsif warden.user(scope) == resource && !options.delete(:force)
# Do nothing. User already signed in and we are not forcing it.
true
else
warden.set_user(resource, options.merge!(:scope => scope))
end
@@ -218,9 +219,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

@@ -18,22 +18,31 @@ module Devise
#
# Those helpers are added to your ApplicationController.
module UrlHelpers
Devise::URL_HELPERS.each do |module_name, actions|
[:path, :url].each do |path_or_url|
actions.each do |action|
action = action ? "#{action}_" : ""
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
def #{action}#{module_name}_#{path_or_url}(resource_or_scope, *args)
scope = Devise::Mapping.find_scope!(resource_or_scope)
send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
end
URL_HELPERS
end
def self.remove_helpers!
self.instance_methods.map(&:to_s).grep(/_(url|path)$/).each do |method|
remove_method method
end
end
def self.generate_helpers!
mappings = Devise.mappings.values.map(&:used_helpers).flatten.uniq
routes = Devise::URL_HELPERS.slice(*mappings)
routes.each do |module_name, actions|
[:path, :url].each do |path_or_url|
actions.each do |action|
action = action ? "#{action}_" : ""
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
def #{action}#{module_name}_#{path_or_url}(resource_or_scope, *args)
scope = Devise::Mapping.find_scope!(resource_or_scope)
send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
end
URL_HELPERS
end
end
end
end
end
end
end

View File

@@ -1,23 +0,0 @@
# 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

@@ -1,7 +1,7 @@
# Each time a record is set we check whether its session has already timed out
# or not, based on last request time. If so, the record is logged out and
# redirected to the sign in page. Also, each time the request comes and the
# record is set, we set the last request time inside it's scoped session to
# record is set, we set the last request time inside its scoped session to
# verify timeout in the following request.
Warden::Manager.after_set_user do |record, warden, options|
scope = options[:scope]

View File

@@ -0,0 +1,91 @@
module Devise
module Mailers
module Helpers
extend ActiveSupport::Concern
included do
include Devise::Controllers::ScopedViews
attr_reader :scope_name, :resource
end
protected
def setup_mail(*args)
ActiveSupport::Deprecation.warn "setup_mail is deprecated, please use devise_mail instead", caller
devise_mail(*args)
end
# 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 default_params[:from].present?
default_params[:from]
elsif 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,8 @@ 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, :used_routes, :used_helpers
alias :name :singular
# Receives an object and find a scope for it. If a scope cannot be found,
@@ -67,7 +68,27 @@ module Devise
@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]
singularizer = lambda { |s| s.to_s.singularize.to_sym }
if options.has_key?(:only)
@used_routes = self.routes & Array(options[:only]).map(&singularizer)
else
@used_routes = self.routes - Array(options[:skip]).map(&singularizer)
end
if options[:skip_helpers] == true
@used_helpers = @used_routes
elsif skip = options[:skip_helpers]
@used_helpers = self.routes - Array(skip).map(&singularizer)
else
@used_helpers = self.routes
end
end
# Return modules for the mapping.
@@ -104,6 +125,10 @@ module Devise
@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 ||= []
@@ -76,18 +79,18 @@ module Devise
def authenticatable_salt
end
def serializable_hash(options={})
if self.class.respond_to?(:accessible_attributes)
options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
super(options)
else
super
end
end
module ClassMethods
Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
def serialize_into_session(record)
[record.to_key, record.authenticatable_salt]
end
def serialize_from_session(key, salt)
record = to_adapter.get(key)
record if record && record.authenticatable_salt == salt
end
def params_authenticatable?(strategy)
params_authenticatable.is_a?(Array) ?
params_authenticatable.include?(strategy) : params_authenticatable

View File

@@ -29,7 +29,7 @@ module Devise
after_create :send_confirmation_instructions, :if => :confirmation_required?
end
# Confirm a user by setting it's confirmed_at to actual time. If the user
# Confirm a user by setting its confirmed_at to actual time. If the user
# is already confirmed, add en error to email field
def confirm!
unless_confirmed do
@@ -127,8 +127,13 @@ module Devise
generate_confirmation_token && save(:validate => false)
end
def after_password_reset
super
confirm! unless confirmed?
end
module ClassMethods
# Attempt to find a user by it's email. If a record is found, send new
# Attempt to find a user by its email. If a record is found, send new
# confirmation instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
@@ -138,7 +143,7 @@ module Devise
confirmable
end
# Find a user by it's confirmation token and try to confirm it.
# Find a user by its confirmation token and try to confirm it.
# If no user is found, returns a new user with an error.
# If the user is already confirmed, create an error for the user
# Options must have the confirmation_token

View File

@@ -10,6 +10,9 @@ module Devise
#
# DatabaseAuthenticable adds the following options to devise_for:
#
# * +pepper+: a random string used to provide a more secure hash. Use
# `rake secret` to generate new keys.
#
# * +stretches+: the cost given to bcrypt.
#
# == Examples

View File

@@ -3,13 +3,13 @@ module Devise
# Handles blocking a user access after a certain number of attempts.
# Lockable accepts two different strategies to unlock a user after it's
# blocked: email and time. The former will send an email to the user when
# the lock happens, containing a link to unlock it's account. The second
# the lock happens, containing a link to unlock its account. The second
# will unlock the user automatically after some configured time (ie 2.hours).
# It's also possible to setup lockable to use both email and time strategies.
#
# == Options
#
# Lockable adds the following options to devise_for:
# Lockable adds the following options to +devise+:
#
# * +maximum_attempts+: how many attempts should be accepted before blocking the user.
# * +lock_strategy+: lock the user account by :failed_attempts or :none.
@@ -22,7 +22,7 @@ module Devise
delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
# Lock a user setting it's locked_at to actual time.
# Lock a user setting its locked_at to actual time.
def lock_access!
self.locked_at = Time.now
@@ -132,7 +132,7 @@ module Devise
end
module ClassMethods
# Attempt to find a user by it's email. If a record is found, send new
# Attempt to find a user by its email. If a record is found, send new
# unlock instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user email
@@ -142,7 +142,7 @@ module Devise
lockable
end
# Find a user by it's unlock token and try to unlock it.
# Find a user by its unlock token and try to unlock it.
# If no user is found, returns a new user with an error.
# If the user is not locked, creates an error for the user
# Options must have the unlock_token

View File

@@ -29,7 +29,11 @@ module Devise
def reset_password!(new_password, new_password_confirmation)
self.password = new_password
self.password_confirmation = new_password_confirmation
clear_reset_password_token if valid?
if valid?
clear_reset_password_token
after_password_reset
end
save
end
@@ -89,8 +93,11 @@ module Devise
self.reset_password_sent_at = nil if respond_to?(:reset_password_sent_at=)
end
def after_password_reset
end
module ClassMethods
# Attempt to find a user by it's email. If a record is found, send new
# Attempt to find a user by its email. If a record is found, send new
# password instructions to it. If not user is found, returns a new user
# with an email not found error.
# Attributes must contain the user email
@@ -105,7 +112,7 @@ module Devise
generate_token(:reset_password_token)
end
# Attempt to find a user by it's reset_password_token to reset its
# Attempt to find a user by its reset_password_token to reset its
# password. If a user is found and token is still valid, reset its password and automatically
# try saving the record. If not user is found, returns a new user
# containing an error in reset_password_token attribute.

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

@@ -8,7 +8,7 @@ module Devise
# * current_sign_in_at - A tiemstamp updated when the user signs in
# * last_sign_in_at - Holds the timestamp of the previous sign in
# * current_sign_in_ip - The remote ip updated when the user sign in
# * last_sign_in_at - Holds the remote ip of the previous sign in
# * last_sign_in_ip - Holds the remote ip of the previous sign in
#
module Trackable
def update_tracked_fields!(request)

View File

@@ -2,7 +2,7 @@ module Devise
module Models
# Validatable creates all needed validations for a user email and password.
# It's optional, given you may want to create the validations by yourself.
# Automatically validate if the email is present, unique and it's format is
# Automatically validate if the email is present, unique and its format is
# valid. Also tests presence of password, confirmation and length.
#
# == Options

View File

@@ -39,5 +39,18 @@ module Devise
Devise.include_helpers(Devise::OmniAuth)
end
end
initializer "devise.mongoid_version_warning" do
if defined?(Mongoid)
require 'mongoid/version'
if Mongoid::VERSION.to_f < 2.1
puts "\n[DEVISE] Please note that Mongoid versions prior to 2.1 handle dirty model " \
"object attributes in such a way that the Devise `validatable` module will not apply " \
"its usual uniqueness and format validations for the email field. It is recommended " \
"that you upgrade to Mongoid 2.1+ for this and other fixes, but if for some reason you " \
"are unable to do so, you should add these validations manually.\n"
end
end
end
end
end

View File

@@ -5,6 +5,7 @@ module ActionDispatch::Routing
def finalize_with_devise!
finalize_without_devise!
Devise.configure_warden!
Devise.regenerate_helpers!
end
alias_method_chain :finalize!, :devise
end
@@ -93,7 +94,7 @@ module ActionDispatch::Routing
#
# Also pay attention that when you use a namespace it will affect all the helpers and methods for controllers
# and views. For example, using the above setup you'll end with following methods:
# current_publisher_account, authenticate_publisher_account!, pusblisher_account_signed_in, etc.
# current_publisher_account, authenticate_publisher_account!, publisher_account_signed_in, etc.
#
# * :skip => tell which controller you want to skip routes from being created:
#
@@ -103,6 +104,21 @@ module ActionDispatch::Routing
#
# devise_for :users, :only => :sessions
#
# * :skip_helpers => skip generating Devise url helpers like new_session_path(@user).
# This is useful to avoid conflicts with previous routes and is false by default.
# It accepts true as option, meaning it will skip all the helpers for the controllers
# given in :skip but it also accepts specific helpers to be skipped:
#
# devise_for :users, :skip => [:registrations, :confirmations], :skip_helpers => true
# devise_for :users, :skip_helpers => [:registrations, :confirmations]
#
# * :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
#
@@ -160,6 +176,9 @@ module ActionDispatch::Routing
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)
@@ -178,15 +197,11 @@ module ActionDispatch::Routing
raise_no_devise_method_error!(mapping.class_name)
end
routes = mapping.routes
if options.has_key?(:only)
routes = Array(options.delete(:only)).map { |s| s.to_s.singularize.to_sym } & mapping.routes
end
routes -= Array(options.delete(:skip)).map { |s| s.to_s.singularize.to_sym }
routes = mapping.used_routes
devise_scope mapping.name do
yield if block_given?
with_devise_exclusive_scope mapping.fullpath, mapping.name, mapping.constraints 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
@@ -195,11 +210,15 @@ module ActionDispatch::Routing
# Allow you to add authentication request from the router:
#
# authenticate(:user) do
# authenticate do
# resources :post
# end
#
def authenticate(scope)
# authenticate(:admin) do
# resources :users
# end
#
def authenticate(scope=nil)
constraint = lambda do |request|
request.env["warden"].authenticate!(:scope => scope)
end
@@ -264,6 +283,17 @@ module ActionDispatch::Routing
# Notice you cannot have two scopes mapping to the same URL. And remember, if
# you try to access a devise controller without specifying a scope, it will
# raise ActionNotFound error.
#
# Also be aware of that 'devise_scope' and 'as' use the singular form of the
# noun where other devise route commands expect the plural form. This would be a
# good and working example.
#
# devise_scope :user do
# match "/some/route" => "some_devise_controller"
# end
# devise_for :users
#
# Notice and be aware of the differences above between :user and :users
def devise_scope(scope)
constraint = lambda do |request|
request.env["devise.mapping"] = Devise.mappings[scope]
@@ -309,7 +339,7 @@ module ActionDispatch::Routing
:cancel => mapping.path_names[:cancel]
}
resource :registration, :except => :show, :path => mapping.path_names[:registration],
resource :registration, :only => [:new, :create, :edit, :update, :destroy], :path => mapping.path_names[:registration],
:path_names => path_names, :controller => controllers[:registrations] do
get :cancel
end
@@ -325,23 +355,23 @@ module ActionDispatch::Routing
::OmniAuth.config.path_prefix = path_prefix
end
match "#{path_prefix}/:action/callback", :action => Regexp.union(mapping.to.omniauth_providers.map(&:to_s)),
match "#{path_prefix}/:action/callback", :constraints => { :action => Regexp.union(mapping.to.omniauth_providers.map(&:to_s)) },
:to => controllers[:omniauth_callbacks], :as => :omniauth_callback
ensure
@scope[:path] = path
end
def with_devise_exclusive_scope(new_path, new_as, new_constraints) #:nodoc:
old_as, old_path, old_module, old_constraints = @scope[:as], @scope[:path], @scope[:module], @scope[:constraints]
@scope[:as], @scope[:path], @scope[:module], @scope[:constraints] = new_as, new_path, nil, new_constraints
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], @scope[:constraints] = old_as, old_path, old_module, old_constraints
@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

@@ -15,21 +15,16 @@ end
class Warden::SessionSerializer
def serialize(record)
[record.class.name, record.to_key, record.authenticatable_salt]
klass = record.class
array = klass.serialize_into_session(record)
array.unshift(klass.name)
end
def deserialize(keys)
if keys.size == 2
raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
"you can fix it by changing one character in your secret_token or cleaning up your " <<
"database sessions if you are using a db store."
end
klass, id, salt = keys
klass, *args = keys
begin
record = ActiveSupport::Inflector.constantize(klass).to_adapter.get(id)
record if record && record.authenticatable_salt == salt
ActiveSupport::Inflector.constantize(klass).serialize_from_session(*args)
rescue NameError => e
if e.message =~ /uninitialized constant/
Rails.logger.debug "[Devise] Trying to deserialize invalid class #{klass}"

View File

@@ -3,11 +3,12 @@ module Devise
# and overwrite the apply_schema method.
module Schema
# Creates email when enabled (on by default), encrypted_password and password_salt.
# Creates encrypted_password, and email when it is used as an authentication
# key (default).
#
# == Options
# * :null - When true, allow columns to be null.
# * :default - Should be set to "" when :null is false.
# * :default - Set to "" when :null is false, unless overridden.
#
# == Notes
# For Datamapper compatibility, we explicitly hardcode the limit for the
@@ -21,7 +22,8 @@ module Devise
apply_devise_schema :encrypted_password, String, :null => null, :default => default, :limit => 128
end
# Creates password salt for encryption support.
# Creates password salt for encryption support when using encryptors other
# than the database_authenticable default of bcrypt.
def encryptable
apply_devise_schema :password_salt, String
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

@@ -39,7 +39,11 @@ module Devise
# Try both scoped and non scoped keys.
def params_auth_hash
params[scope] || params
if params[scope].kind_of?(Hash) && params[scope].has_key?(authentication_keys.first)
params[scope]
else
params
end
end
# Overwrite authentication keys to use token_authentication_key.

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "1.4.0".freeze
VERSION = "1.4.4".freeze
end

View File

@@ -1,6 +1,7 @@
require 'rails/generators/active_record'
require 'generators/devise/orm_helpers'
module ActiveRecord
module Generators
class DeviseGenerator < ActiveRecord::Generators::Base
@@ -9,14 +10,18 @@ module ActiveRecord
include Devise::Generators::OrmHelpers
source_root File.expand_path("../templates", __FILE__)
def copy_devise_migration
if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
migration_template "migration_existing.rb", "db/migrate/add_devise_to_#{table_name}"
else
migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
end
end
def generate_model
invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
end
def copy_devise_migration
migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
end
def inject_devise_content
inject_into_class(model_path, class_name, model_contents + <<CONTENT) if model_exists?
# Setup accessible (or protected) attributes for your model

View File

@@ -0,0 +1,34 @@
class AddDeviseTo<%= table_name.camelize %> < ActiveRecord::Migration
def self.up
change_table(:<%= table_name %>) do |t|
t.database_authenticatable :null => false
t.recoverable
t.rememberable
t.trackable
# t.encryptable
# t.confirmable
# t.lockable :lock_strategy => :<%= Devise.lock_strategy %>, :unlock_strategy => :<%= Devise.unlock_strategy %>
# t.token_authenticatable
<% for attribute in attributes -%>
t.<%= attribute.type %> :<%= attribute.name %>
<% end -%>
# Uncomment below if timestamps were not included in your original model.
# t.timestamps
end
add_index :<%= table_name %>, :email, :unique => true
add_index :<%= table_name %>, :reset_password_token, :unique => true
# add_index :<%= table_name %>, :confirmation_token, :unique => true
# add_index :<%= table_name %>, :unlock_token, :unique => true
# add_index :<%= table_name %>, :authentication_token, :unique => true
end
def self.down
# By default, we don't want to make any assumption about how to roll back a migration when your
# model already existed. Please edit below which fields you would like to remove in this migration.
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -14,6 +14,14 @@ CONTENT
def model_exists?
File.exists?(File.join(destination_root, model_path))
end
def migration_exists?(table_name)
Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
end
def migration_path
@migration_path ||= File.join("db", "migrate")
end
def model_path
@model_path ||= File.join("app", "models", "#{file_path}.rb")

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

@@ -2,7 +2,8 @@
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in DeviseMailer.
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class with default "from" parameter.
config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
# Configure the class responsible to send e-mails.
@@ -35,7 +36,7 @@ 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.
@@ -61,7 +62,11 @@ Devise.setup do |config|
# ==> 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
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments.
config.stretches = Rails.env.test? ? 1 : 10
# Setup a pepper to generate the encrypted password.
# config.pepper = <%= SecureRandom.hex(64).inspect %>
@@ -100,8 +105,10 @@ Devise.setup do |config|
# Range for password length. Default is 6..128.
# config.password_length = 6..128
# Regex to use to validate the email address
# config.email_regexp = /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
# Email regex used to validate email formats. It simply asserts that
# an one (and only one) @ exists in the given string. This is mainly
# to give user feedback and not to assert the e-mail validity.
# config.email_regexp = /\A[^@]+@[^@]+\z/
# ==> Configuration for :timeoutable
# The time you want to timeout the user session without activity. After this
@@ -184,8 +191,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, :required => true, :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, :required => true, :autofocus => true %>
<%= f.input :password, :required => true %>
<%= f.input :password_confirmation, :required => true %>
</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

@@ -106,7 +106,7 @@ class ControllerAuthenticatableTest < ActionController::TestCase
user = User.new
@mock_warden.expects(:user).returns(user)
@mock_warden.expects(:set_user).never
@controller.sign_in(user)
assert @controller.sign_in(user)
end
test 'sign in again when the user is already in only if force is given' do

View File

@@ -13,9 +13,22 @@ if DEVISE_ORM == :active_record
assert_file "app/models/monster.rb", /devise/, /attr_accessible (:[a-z_]+(, )?)+/
assert_migration "db/migrate/devise_create_monsters.rb"
end
test "update model migration when model exists" do
run_generator %w(monster)
assert_file "app/models/monster.rb"
run_generator %w(monster)
assert_migration "db/migrate/add_devise_to_monsters.rb"
end
test "all files are properly deleted" do
run_generator %w(monster)
run_generator %w(monster)
assert_migration "db/migrate/devise_create_monsters.rb"
assert_migration "db/migrate/add_devise_to_monsters.rb"
run_generator %w(monster), :behavior => :revoke
assert_no_migration "db/migrate/add_devise_to_monsters.rb"
assert_migration "db/migrate/devise_create_monsters.rb"
run_generator %w(monster), :behavior => :revoke
assert_no_file "app/models/monster.rb"
assert_no_migration "db/migrate/devise_create_monsters.rb"

View File

@@ -1,7 +1,7 @@
require "test_helper"
if DEVISE_ORM == :mongo_id
require "generators/mongo_id/devise_generator"
if DEVISE_ORM == :mongoid
require "generators/mongoid/devise_generator"
class MongoidGeneratorTest < Rails::Generators::TestCase
tests Mongoid::Generators::DeviseGenerator
@@ -19,4 +19,5 @@ if DEVISE_ORM == :mongo_id
assert_no_file "app/models/monster.rb"
end
end
end
end

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,51 @@
require 'test_helper'
class DeviseHelperTest < ActionController::IntegrationTest
setup do
model_labels = { :models => { :user => "utilisateur" } }
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 => model_labels,
:mongoid => model_labels
}
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
# Dirty tracking behavior prevents email validations from being applied:
# https://github.com/mongoid/mongoid/issues/756
(pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
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

@@ -354,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'
@@ -440,6 +454,23 @@ class AuthenticationOthersTest < ActionController::IntegrationTest
end
end
class AuthenticationKeysTest < ActionController::IntegrationTest
test 'missing authentication keys cause authentication to abort' do
swap Devise, :authentication_keys => [:subdomain] do
sign_in_as_user
assert_contain "Invalid email or password."
assert_not warden.authenticated?(:user)
end
end
test 'missing authentication keys cause authentication to abort unless marked as not required' do
swap Devise, :authentication_keys => { :email => true, :subdomain => false } do
sign_in_as_user
assert warden.authenticated?(:user)
end
end
end
class AuthenticationRequestKeysTest < ActionController::IntegrationTest
test 'request keys are used on authentication' do
host! 'foo.bar.baz'

View File

@@ -37,6 +37,15 @@ class ConfirmationTest < ActionController::IntegrationTest
assert user.reload.confirmed?
end
test 'user should be redirected to a custom path after confirmation' do
Devise::ConfirmationsController.any_instance.stubs(:after_confirmation_path_for).returns("/?custom=1")
user = create_user(:confirm => false)
visit_user_confirmation_with_token(user.confirmation_token)
assert_current_url "/?custom=1"
end
test 'already confirmed user should not be able to confirm the account again' do
user = create_user(:confirm => false)
user.confirmed_at = Time.now
@@ -60,7 +69,7 @@ class ConfirmationTest < ActionController::IntegrationTest
assert_contain 'already confirmed'
end
test 'sign in user automatically after confirming it\'s email' do
test 'sign in user automatically after confirming its email' do
user = create_user(:confirm => false)
visit_user_confirmation_with_token(user.confirmation_token)

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

@@ -73,7 +73,7 @@ class LockTest < ActionController::IntegrationTest
assert_not user.reload.access_locked?
end
test "sign in user automatically after unlocking it's account" do
test "sign in user automatically after unlocking its account" do
user = create_user(:locked => true)
visit_user_unlock_with_token(user.unlock_token)
assert warden.authenticated?(:user)

View File

@@ -166,7 +166,7 @@ class PasswordTest < ActionController::IntegrationTest
assert user.reload.valid_password?('987654321')
end
test 'sign in user automatically after changing it\'s password' do
test 'sign in user automatically after changing its password' do
user = create_user
request_forgot_password
reset_password :reset_password_token => user.reload.reset_password_token
@@ -174,8 +174,8 @@ class PasswordTest < ActionController::IntegrationTest
assert warden.authenticated?(:user)
end
test 'does not sign in user automatically after changing it\'s password if it\'s not active' do
user = create_user(:confirm => false)
test 'does not sign in user automatically after changing its password if it\'s locked' do
user = create_user(:locked => true)
request_forgot_password
reset_password :reset_password_token => user.reload.reset_password_token
@@ -183,6 +183,15 @@ class PasswordTest < ActionController::IntegrationTest
assert !warden.authenticated?(:user)
end
test 'sign in user automatically and confirm after changing its password if it\'s not confirmed' do
user = create_user(:confirm => false)
request_forgot_password
reset_password :reset_password_token => user.reload.reset_password_token
assert warden.authenticated?(:user)
assert user.reload.confirmed?
end
test 'reset password request with valid E-Mail in XML format should return valid response' do
create_user
post user_password_path(:format => 'xml'), :user => {:email => "user@test.com"}

View File

@@ -69,6 +69,10 @@ class RegistrationTest < ActionController::IntegrationTest
end
test 'a guest user cannot sign up with invalid information' do
# Dirty tracking behavior prevents email validations from being applied:
# https://github.com/mongoid/mongoid/issues/756
(pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
get new_user_registration_path
fill_in 'email', :with => 'invalid_email'
@@ -87,6 +91,10 @@ class RegistrationTest < ActionController::IntegrationTest
end
test 'a guest should not sign up with email/password that already exists' do
# Dirty tracking behavior prevents email validations from being applied:
# https://github.com/mongoid/mongoid/issues/756
(pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
user = create_user
get new_user_registration_path
@@ -211,14 +219,14 @@ class RegistrationTest < ActionController::IntegrationTest
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
assert_no_match(/<confirmation-token/, response.body)
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
assert_no_match(/"confirmation_token"/, response.body)
end
test 'an admin sign up with valid information in XML format should return valid response' do

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"]

View File

@@ -13,6 +13,17 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
end
end
test 'authenticate with valid authentication token key and value through params, when params with the same key as scope exist' do
swap Devise, :token_authentication_key => :secret_token do
user = create_user_with_authentication_token
post exhibit_user_path(user), Devise.token_authentication_key => user.authentication_token, :user => { :some => "data" }
assert_response :success
assert_contain 'User is authenticated'
assert warden.authenticated?(:user)
end
end
test 'authenticate with valid authentication token key but does not store if stateless' do
swap Devise, :token_authentication_key => :secret_token, :stateless_token => true do
sign_in_as_new_user_with_token

View File

@@ -4,6 +4,7 @@ class ConfirmationInstructionsTest < ActionMailer::TestCase
def setup
setup_mailer
Devise.mailer = 'Devise::Mailer'
Devise.mailer_sender = 'test@example.com'
end
@@ -35,6 +36,11 @@ class ConfirmationInstructionsTest < ActionMailer::TestCase
assert_equal ['test@example.com'], mail.from
end
test 'setup sender from custom mailer defaults' do
Devise.mailer = 'Users::Mailer'
assert_equal ['custom@example.com'], mail.from
end
test 'setup reply to as copy from sender' do
assert_equal ['test@example.com'], mail.reply_to
end

View File

@@ -4,6 +4,7 @@ class ResetPasswordInstructionsTest < ActionMailer::TestCase
def setup
setup_mailer
Devise.mailer = 'Devise::Mailer'
Devise.mailer_sender = 'test@example.com'
end
@@ -38,6 +39,11 @@ class ResetPasswordInstructionsTest < ActionMailer::TestCase
assert_equal ['test@example.com'], mail.from
end
test 'setup sender from custom mailer defaults' do
Devise.mailer = 'Users::Mailer'
assert_equal ['custom@example.com'], mail.from
end
test 'setup reply to as copy from sender' do
assert_equal ['test@example.com'], mail.reply_to
end

View File

@@ -4,6 +4,7 @@ class UnlockInstructionsTest < ActionMailer::TestCase
def setup
setup_mailer
Devise.mailer = 'Devise::Mailer'
Devise.mailer_sender = 'test@example.com'
end
@@ -38,6 +39,11 @@ class UnlockInstructionsTest < ActionMailer::TestCase
assert_equal ['test@example.com'], mail.from
end
test 'setup sender from custom mailer defaults' do
Devise.mailer = 'Users::Mailer'
assert_equal ['custom@example.com'], mail.from
end
test 'setup reply to as copy from sender' do
assert_equal ['test@example.com'], mail.reply_to
end

View File

@@ -121,7 +121,7 @@ class ConfirmableTest < ActiveSupport::TestCase
assert_equal "not found", confirmation_user.errors[:email].join
end
test 'should send email instructions for the user confirm it\'s email' do
test 'should send email instructions for the user confirm its email' do
user = create_user
assert_email_sent do
User.send_confirmation_instructions(:email => user.email)
@@ -219,7 +219,7 @@ class ConfirmableTest < ActiveSupport::TestCase
assert user.reload.active_for_authentication?
end
test 'should find a user to send email instructions for the user confirm it\'s email by authentication_keys' do
test 'should find a user to send email instructions for the user confirm its email by authentication_keys' do
swap Devise, :authentication_keys => [:username, :email] do
user = create_user
confirm_user = User.send_confirmation_instructions(:email => user.email, :username => user.username)

View File

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

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

@@ -1,3 +1,4 @@
# encoding: UTF-8
require 'test_helper'
class ValidatableTest < ActiveSupport::TestCase
@@ -28,7 +29,7 @@ class ValidatableTest < ActiveSupport::TestCase
assert user.invalid?
assert_not_equal 'is invalid', user.errors[:email].join
%w(invalid_email_format 123 $$$ \(\) ).each do |email|
%w{invalid_email_format 123 $$$ () ☃ bla@bla.}.each do |email|
user.email = email
assert user.invalid?, 'should be invalid with email ' << email
assert_equal 'is invalid', user.errors[:email].join
@@ -39,7 +40,7 @@ class ValidatableTest < ActiveSupport::TestCase
end
test 'should accept valid emails' do
%w(a.b.c@example.com test_mail@gmail.com any@any.net email@test.br 123@mail.test).each do |email|
%w(a.b.c@example.com test_mail@gmail.com any@any.net email@test.br 123@mail.test 1☃3@mail.test).each do |email|
user = new_user(:email => email)
assert user.valid?, 'should be valid with email ' << email
assert_blank user.errors[:email]

View File

@@ -42,18 +42,16 @@ class ActiveRecordTest < ActiveSupport::TestCase
assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable, :recoverable, :lockable, :rememberable, :encryptable
end
if DEVISE_ORM == :active_record
test 'validations options are not applied to late' do
validators = WithValidation.validators_on :password
length = validators.find { |v| v.kind == :length }
assert_equal 2, length.options[:minimum]
assert_equal 6, length.options[:maximum]
end
test 'validations options are not applied too late' do
validators = WithValidation.validators_on :password
length = validators.find { |v| v.kind == :length }
assert_equal 2, length.options[:minimum]
assert_equal 6, length.options[:maximum]
end
test 'validations are applied just once' do
validators = Several.validators_on :password
assert_equal 1, validators.select{ |v| v.kind == :length }.length
end
test 'validations are applied just once' do
validators = Several.validators_on :password
assert_equal 1, validators.select{ |v| v.kind == :length }.length
end
test 'chosen modules are inheritable' do

View File

@@ -1,6 +1,9 @@
require 'mongoid/version'
Mongoid.configure do |config|
config.master = Mongo::Connection.new('127.0.0.1', 27017).db("devise-test-suite")
config.use_utc = true
config.include_root_in_json = true
end
class ActiveSupport::TestCase

View File

@@ -3,6 +3,4 @@ require 'shared_user'
class User < ActiveRecord::Base
include Shim
include SharedUser
attr_accessible :username, :email, :password, :password_confirmation, :remember_me
end

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,3 @@
class Users::Mailer < Devise::Mailer
default :from => 'custom@example.com'
end

View File

@@ -21,9 +21,4 @@ module Shim
def ==(other)
other.is_a?(self.class) && _id == other._id
end
# Mongoid does not have this method in the current beta version (2.0.0.beta.20)
def update_attribute(attribute, value)
update_attributes(attribute => value)
end
end

View File

@@ -29,6 +29,7 @@ module RailsApp
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters << :password
config.assets.enabled = false
config.action_mailer.default_url_options = { :host => "localhost:3000" }

View File

@@ -2,7 +2,8 @@
# four configuration values can also be set straight in your models.
Devise.setup do |config|
# ==> Mailer Configuration
# Configure the e-mail address which will be shown in DeviseMailer.
# Configure the e-mail address which will be shown in Devise::Mailer,
# note that it will be overwritten if you use your own mailer class with default "from" parameter.
config.mailer_sender = "please-change-me@config-initializers-devise.com"
# Configure the class responsible to send e-mails.
@@ -35,7 +36,7 @@ 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.

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 do
post :exhibit, :on => :member
end
end
resources :admins, :only => [:index]
@@ -47,6 +51,10 @@ Rails.application.routes.draw 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, :skip_helpers => [:confirmations, :unlocks]
devise_for :htmlonly_users, :class_name => "User", :only => [:confirmations, :unlocks], :path => "htmlonly_users", :format => false, :skip_helpers => true
# Other routes for routing_test.rb
devise_for :reader, :class_name => "User", :only => :passwords
@@ -72,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

@@ -7,6 +7,7 @@ module SharedUser
:trackable, :validatable, :omniauthable
attr_accessor :other_key
attr_accessible :username, :email, :password, :password_confirmation, :remember_me
# They need to be included after Devise is called.
extend ExtendMethods

View File

@@ -190,6 +190,41 @@ class CustomizedRoutingTest < ActionController::TestCase
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