Compare commits

...

99 Commits

Author SHA1 Message Date
José Valim
1741a79114 Release Devise 1.0.8 with a few fixes and latest mongomapper support. 2010-06-23 12:20:50 +02:00
Iván Valdés (@ivanvc)
a41025e421 Fixed MongoMapper adapter in order to get it working with versions >= 0.8.0
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-06-23 11:18:14 +02:00
Luke Cunningham
73e5d848c1 updated Document and EmbeddedDocument hooks to fix issue with MongoMapper v0.8 2010-06-17 18:48:24 +08:00
Maxim Filatov
ca512267c5 anybody_signed_in? added to helper methods 2010-06-12 19:22:59 +08:00
SSDany
55a47128bf anybody_signed_in? helper 2010-06-12 19:22:58 +08:00
Mike Breen
0609a5e192 :authenticatable as module is deprecated so the model generator should use :database_authenticatable 2010-05-26 03:42:44 +08:00
Paul Rosania
201cfa9824 Automatically create the confirmation_token when email is sent for optionally confirmable models
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-16 14:55:50 +02:00
Paul Rosania
d853c376d4 Mark confirmable roles as active when confirmation_required? is false
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-05-16 14:55:49 +02:00
José Valim
18f6e06963 Update README. 2010-05-03 13:59:21 +02:00
José Valim
c38b2f69d0 Release devise 1.0.7 with small fixes. 2010-05-03 13:56:25 +02:00
José Valim
c29b6ca4ea Confirmable is not default anymore. This provides a better bootstrap experience. 2010-05-03 00:10:21 +02:00
Ryan Booker
e666fae249 Fix grammar in notice, closes #229
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-04-25 09:57:05 +02:00
José Valim
ad730da580 Include model_session helpers in view, closes #227 2010-04-25 09:56:09 +02:00
José Valim
1a9092c61b Ensure password confirmation is always required, closes #228 2010-04-25 09:55:11 +02:00
José Valim
234af4b14a Help avoid common pitfalls. 2010-04-13 00:15:30 -07:00
José Valim
f69bc53f04 Authenticatable => DatabaseAuthenticatable. 2010-04-12 04:50:49 -07:00
José Valim
681f816074 Backport small updates done in master. 2010-04-11 08:05:21 +02:00
José Valim
6915e6226a Update instructions on README. 2010-04-04 04:41:56 -07:00
José Valim
1865298074 Release Devise 1.0.6. 2010-04-03 13:25:06 +02:00
José Valim
1e4394e361 Update generated migration. 2010-04-02 20:37:54 +02:00
postmodern
b033c8e938 Expend the length of the encrypted_password field to 128 to allow storing BCrypt or SHA512 passwords.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-04-02 20:36:32 +02:00
José Valim
f7d134ba9d Tidy up previous commit. 2010-04-02 20:29:42 +02:00
Nat Budin
0bc15286b4 Pass back the custom response, if the winning strategy uses the custom\! method
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-04-02 20:28:26 +02:00
José Valim
b425c701e0 Update warden dependency. 2010-04-01 19:27:04 +02:00
José Valim
c18d8e50d3 Backport database_authenticatable change,. 2010-04-01 12:16:43 +02:00
José Valim
52f729e74f Bug fixes on unlockable. 2010-03-28 23:14:36 +02:00
José Valim
bb026205cb Do not force halt on authenticatable. This allows other strategies (like devise_imapable or even devise_facebook_connectable) to hook into sessions controller as well.
Those strategies should follow the same convention, allowing them to be cascated.
2010-03-28 14:57:32 +02:00
Josh Kalderimis
e80b46b68f removed duplicated method
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-03-28 07:26:22 +02:00
José Valim
ce3926fea4 Bump tiny. 2010-03-26 13:04:05 +01:00
José Valim
e2793fc69e sign_in_count shoud default to zero. 2010-03-26 12:57:36 +01:00
Carlos Antonio da Silva
867e896bc8 Merge branch 'v1.0' of github.com:plataformatec/devise into v1.0 2010-03-26 08:45:49 -03:00
José Valim
053c6f1a3a Move password_required? to authenticatable. This allow you to reuse it when building your own validations. 2010-03-26 12:19:15 +01:00
Carlos Antonio da Silva
a73fead23e Merge branch 'v1.0' of github.com:plataformatec/devise into v1.0 2010-03-26 08:19:10 -03:00
Carlos Antonio da Silva
42eb89b909 Use prepend_before_filter in require_no_authentication.
We need to be sure require_no_authentication runs before other user filters that may call some Devise helper (ie current_xxx).
2010-03-26 08:14:58 -03:00
José Valim
913444059c Allow devise to work with association proxies. 2010-03-26 10:26:38 +01:00
Josh Kalderimis
b305b7f357 changed add_module to add modules to the bottom of ALL, also added test to confirm order in ALL is being adhered to
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-03-26 09:32:25 +01:00
José Valim
06d43525d6 Require no authentication on unlockable. 2010-03-25 16:28:36 -03:00
Josh Kalderimis
6d08646ddc added routes option to add_module so route view helpers are created
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-03-25 09:05:13 +01:00
José Valim
1bee9fbef9 Clean up lockable and class methods API. 2010-03-10 16:18:28 +01:00
José Valim
5a4b797265 Remove deprecated behavior. 2010-03-04 08:20:51 +01:00
José Valim
d36e1012f8 Release 1.0.4 with a couple bug fixes. 2010-03-03 12:24:29 +01:00
Lucas de Castro
5d187ff278 Fixing session controllers when within namespaces
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-03-03 12:16:24 +01:00
Cyril Mougel
a0220243c3 fix spec failed with mongo_mapper DEVISE_ORM
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-25 08:44:08 +01:00
José Valim
4c10f86e74 Do not forget frozen records.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-25 08:35:07 +01:00
Lucas Uyezu
cf66e935a9 SQLite requries a default value when the column is NOT NULL
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-25 08:34:47 +01:00
José Valim
fbe485f3df Update warden which fixes a security issue. 2010-02-23 19:52:53 +01:00
José Valim
545462e964 Bump to 1.0.3. 2010-02-23 15:45:07 +01:00
José Valim
42df192df8 Do not remove options from MongoMapper find. 2010-02-23 15:41:52 +01:00
Andre Arko
7f451ed9cc Add rails/init.rb to the gemspec
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-19 18:06:38 +01:00
Daniel Jagszent
27fe3023ae renamed init.rb -> rails/init.rb. So that rails can find and initalize the GemPlugin even without a config.gem "devise" line in environment.rb (for using with bundler)
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-19 17:10:03 +01:00
Paul Campbell
41d416a18e add paragraphs to html emails
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-19 10:18:00 +01:00
José Valim
c36cd84c31 Returns the proper response body based on the rquest for 401. 2010-02-18 19:52:37 +01:00
José Valim
fd96335d05 Autoload Devise::Models. 2010-02-18 07:22:26 +01:00
José Valim
23568bda82 Bump to 1.0.2. 2010-02-17 21:30:54 +01:00
José Valim
ee7f5270fc Uses the same content type as request on http authenticatable 401 responses 2010-02-17 21:25:31 +01:00
José Valim
f294700723 Update test files. 2010-02-17 21:15:11 +01:00
Glenn Roberts
c86ce298dc add content type test, update config doc
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-17 21:13:27 +01:00
Glenn Roberts
b0ff0d46dd add content_type config parameter
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-17 21:13:16 +01:00
José Valim
187ef5c452 Update README. 2010-02-17 13:56:00 +01:00
Paul Campbell
6d29bcc467 Add mention of flash[:notice] and flash[:alert] 2010-02-15 22:29:23 +08:00
José Valim
ee87ec398a Updated gemspec. 2010-02-15 14:23:00 +01:00
José Valim
3e37fe8d4d Bump to 1.0.1 2010-02-15 14:19:08 +01:00
José Valim
48a94cdece Avoid mass assignment error messages with current password. 2010-02-15 14:17:12 +01:00
José Valim
bdacffab58 Make HttpAuthenticatable opt-in. 2010-02-15 14:11:33 +01:00
José Valim
085b12a710 Add registerable to defaults. 2010-02-15 14:06:50 +01:00
Carlos Antonio da Silva
3435c53725 Fix typo: autoload Clearance encryptor and not Authlogic one. 2010-02-12 13:02:11 -02:00
Carlos Antonio da Silva
01dec7fc78 README and TODO minor updates. 2010-02-12 01:54:47 -02:00
José Valim
4bfbeea7e6 Release 1.0.0 2010-02-09 02:17:20 +01:00
José Valim
2a9e8dca73 Allow authenticatable to used in change_table statements 2010-02-09 00:26:26 +01:00
José Valim
1b6f1b9752 Add registerable integration tests. 2010-02-09 00:08:57 +01:00
José Valim
732e31528e More changes in update_with_password. 2010-02-08 23:14:03 +01:00
José Valim
d7db5b1eea More work on edit. 2010-02-08 20:38:47 +01:00
José Valim
2761a75437 Refactor on routes. 2010-02-08 20:25:20 +01:00
José Valim
8a15ac6e4a Stub out other views for Registerable. 2010-02-08 19:07:24 +01:00
José Valim
9798ad7455 Allow scoped views to be customized per controller/mailer class. 2010-02-08 17:33:22 +01:00
José Valim
54cd2cc0e8 Use _ instead of . 2010-02-08 17:15:12 +01:00
Carlos Antonio da Silva
445070f6ec Use sign_up instead of registration in routes. Fix issue with users being signed in while attempting to sign up with info from already existing user. Also fix signed up flash. 2010-02-08 11:03:15 -02:00
Carlos Antonio da Silva
9856646fac Merge with master 2010-02-06 09:24:00 -02:00
José Valim
60fd9d26ea Rely on duck type instead of mappings settings. 2010-02-06 10:06:22 +01:00
José Valim
1cf4dc798d Add Http Basic Authentication support. 2010-02-06 01:33:32 +01:00
José Valim
2f441fb60b Commit new gemspec. 2010-02-05 21:38:50 +01:00
José Valim
e02810d528 Move to 0.9.2. 2010-02-05 21:37:28 +01:00
José Valim
c146cad448 Ensure inactive user cannot sign in. 2010-02-05 21:36:19 +01:00
Carlos Antonio da Silva
49d1978863 We do not need to check for registerable active inside the view. 2010-02-05 00:02:49 -02:00
Carlos Antonio da Silva
658059f31a Bring Devise::ALL back to modules and improving lockable docs. 2010-02-04 20:09:53 -02:00
Carlos Antonio da Silva
21359fb433 Refactoring a bit models and lockable. Also remove devise :all deprecation. 2010-02-04 20:09:53 -02:00
Carlos Antonio da Silva
60714cd449 autload Registerable. Extracting default routes and path_names to constants. Fix evals for better debugging and stack trace. 2010-02-04 20:09:53 -02:00
Carlos Antonio da Silva
6b837cb285 Introducing Registerable module, allowing users to sign up. 2010-02-04 20:08:38 -02:00
Carlos Antonio da Silva
4de1e43b7a Fix "return_to" to always save the request_uri, overwriting the return to url when the user types another forbidden url before sign in.
This way the user will be redirected to the last attempted url and not the first one.
2010-02-04 08:46:22 -02:00
Matt Powell
02a99b9766 deprecate find(:first) and find(:all) in MongoMapper 2010-02-03 16:53:49 +08:00
José Valim
a9e2337aeb Change EMAIL_REGEXP so it can be used in javascript. 2010-02-02 13:28:47 +01:00
José Valim
3781a0f47b Tidy up token authentication implementation. 2010-02-02 13:21:00 +01:00
Jonas Grimfelt
4878bdb60b Second version of token_authenticatable reflecting feedback: Nuked all hook-stuff. Should be easy to custom-reset authentication tokens by inheritance.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-02 12:37:06 +01:00
Jonas Grimfelt
e1440fb430 Initial support for authorization using "authentication token" (a.k.a. "single access token") - new module. Corresponding changes to Devise core to hook events like "after_changed_password" (only one added now - only one that makes much sense for latest module) easily. Unit and integration tests included. NOTE: One failing test for hooking Warden::Manager.after_authentication - gets ignored for some reason.
Signed-off-by: José Valim <jose.valim@gmail.com>
2010-02-02 12:36:44 +01:00
José Valim
c03b4ff339 Add gemspec since it's required by bundler. 2010-02-01 16:32:36 +01:00
José Valim
ca794776c1 Bump to 0.9.1. 2010-01-25 20:19:47 +01:00
José Valim
b4707c2bae Use self. just to stay in the same pattern. 2010-01-25 19:29:14 +01:00
jgeiger
b3fd742aea bcrypt requires the salt to be over 29 chars 2010-01-22 23:04:03 +08:00
David Palm
bc05d28d3f Devise::Mapping#raw_path does not consider relative_url_root so that route helpers work (no more session_path(:user) => '/abc/abc/users/sign_in')
Devise::Mapping#parsed_path considers relative_url_root so that initial redirects still work with non empty relative_url_roots
2010-01-22 23:03:48 +08:00
87 changed files with 1792 additions and 517 deletions

1
.gitignore vendored
View File

@@ -4,5 +4,4 @@
coverage/*
*.sqlite3
rdoc/*
devise.gemspec
pkg

View File

@@ -1,3 +1,93 @@
== 1.0.8
* enhancements
* Support for latest MongoMapper
* Added anybody_signed_in? helper (by github.com/SSDany)
* bug fix
* confirmation_required? is properly honored on active? calls. (by github.com/paulrosania)
== 1.0.7
* bug fix
* Ensure password confirmation is always required
* deprecations
* authenticatable was deprecated and renamed to database_authenticatable
* confirmable is not included by default on generation
== 1.0.6
* bug fix
* Do not allow unlockable strategies based on time to access a controller.
* Do not send unlockable email several times.
* Allow controller to upstram custom! failures to Warden.
== 1.0.5
* bug fix
* Use prepend_before_filter in require_no_authentication.
* require_no_authentication on unlockable.
* Fix a bug when giving an association proxy to devise.
* Do not use lock! on lockable since it's part of ActiveRecord API.
== 1.0.4
* bug fix
* Fixed a bug when deleting an account with rememberable
* Fixed a bug with custom controllers
== 1.0.3
* enhancements
* HTML e-mails now have proper formatting
* Do not remove MongoMapper options in find
== 1.0.2
* enhancements
* Allows you set mailer content type (by github.com/glennr)
* bug fix
* Uses the same content type as request on http authenticatable 401 responses
== 1.0.1
* enhancements
* HttpAuthenticatable is not added by default automatically.
* Avoid mass assignment error messages with current password.
* bug fix
* Fixed encryptors autoload
== 1.0.0
* deprecation
* :old_password in update_with_password is deprecated, use :current_password instead
* enhancements
* Added Registerable
* Added Http Basic Authentication support
* Allow scoped_views to be customized per controller/mailer class
* [#99] Allow authenticatable to used in change_table statements
* Add mailer_content_type configuration parameter (by github.com/glennr)
== 0.9.2
* bug fix
* Ensure inactive user cannot sign in
* Ensure redirect to proper url after sign up
* enhancements
* Added gemspec to repo
* Added token authenticatable (by github.com/grimen)
== 0.9.1
* bug fix
* Allow bigger salt size (by github.com/jgeiger)
* Fix relative url root
== 0.9.0
* deprecation

View File

@@ -7,15 +7,18 @@ 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.
Right now it's composed of nine modules:
Right now it's composed of 12 modules:
* Authenticatable: responsible for encrypting password and validating authenticity of a user while signing in.
* Database Authenticatable: responsible for encrypting password and validating authenticity of a user while signing in.
* Token Authenticatable: validates authenticity of a user while signing in using an authentication token (also known as "single access token").
* HttpAuthenticatable: sign in users using basic HTTP authentication.
* Confirmable: responsible for verifying whether an account is already confirmed to sign in, and to send emails with confirmation instructions.
* Recoverable: takes care of reseting the user password and send reset instructions.
* Registerable: handles signing up users through a registration process.
* Rememberable: manages generating and clearing token for remember the user from a saved cookie.
* Trackable: tracks sign in count, timestamps and ip.
* Validatable: creates all needed validations for email and password. It's totally optional, so you're able to to customize validations by yourself.
* Timeoutable: expires sessions without activity in a certain period of time.
* Validatable: creates all needed validations for email and password. It's totally optional, so you're able to to customize validations by yourself.
* Lockable: takes care of locking an account based on the number of failed sign in attempts. Handles unlock via expire and email.
* Activatable: if you need to activate accounts by other means, which are not through confirmation, use this module.
@@ -27,17 +30,13 @@ Devise is based on Warden (http://github.com/hassox/warden), a Rack Authenticati
== Installation
All gems are on gemcutter, so you need to add gemcutter to your sources if you haven't yet:
sudo gem sources -a http://gemcutter.org/
Install warden gem if you don't have it installed (requires 0.6.4 or higher):
Install warden gem if you don't have it installed:
sudo gem install warden
Install devise gem:
sudo gem install devise
sudo gem install devise --version=1.0.7
Configure warden and devise gems inside your app:
@@ -52,6 +51,10 @@ And you're ready to go. The generator will install an initializer which describe
http://rdoc.info/projects/plataformatec/devise
If you want to use Devise with bundler on Rails 2.3, you need to follow the instructions here:
http://github.com/carlhuda/bundler/issues/issue/83
== Basic Usage
This is a walkthrough with all steps you need to setup a devise resource, including model, migration, route files, and optional configuration. You MUST also check out the *Generators* section below to help you start.
@@ -61,13 +64,13 @@ Devise must be set up within the model (or models) you want to use, and devise r
We're assuming here you want a User model with some modules, as outlined below:
class User < ActiveRecord::Base
devise :authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
devise :database_authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
end
After you choose which modules to use, you need to setup your migrations. Luckily, devise has some helpers to save you from this boring work:
create_table :users do |t|
t.authenticatable
t.database_authenticatable
t.confirmable
t.recoverable
t.rememberable
@@ -125,13 +128,13 @@ Devise let's you setup as many roles as you want, so let's say you already have
# Create a migration with the required fields
create_table :admins do |t|
t.authenticatable
t.database_authenticatable
t.lockable
t.trackable
end
# Inside your Admin model
devise :authenticatable, :trackable, :timeoutable, :lockable
devise :database_authenticatable, :trackable, :timeoutable, :lockable
# Inside your routes
map.devise_for :admin
@@ -158,9 +161,9 @@ A model configured with all devise modules and attr_accessible for default field
== Model configuration
The devise method in your models also accept some options to configure its modules. For example, you can chose which encryptor to use in authenticatable:
The devise method in your models also accept some options to configure its modules. For example, you can chose which encryptor to use in database_authenticatable:
devise :authenticatable, :confirmable, :recoverable, :encryptor => :bcrypt
devise :database_authenticatable, :confirmable, :recoverable, :encryptor => :bcrypt
Besides :encryptor, you can provide :pepper, :stretches, :confirm_within, :remember_for, :timeout_in, :unlock_in and others. All those are describer in the initializer created when you invoke the devise_install generator describer above.
@@ -170,10 +173,12 @@ Since devise is an engine, it has all default views inside the gem. They are goo
ruby script/generate devise_views
By default Devise will use the same views for all roles you have. But what if you need so different views to each of them? Devise also has an easy way to accomplish it: just setup config.scoped_views to true inside "config/initializers/devise.rb".
By default Devise will use the same views for all roles you have. But what if you need so different views to each of them? Devise also has an easy way to accomplish it: just setup config.scoped_views to true inside "config/initializers/devise.rb".
After doing so you will be able to have views based on the scope like 'sessions/users/new' and 'sessions/admin/new'. If no view is found within the scope, Devise will fallback to the default view.
Devise uses flash messages to let users know if their login is successful or not. Devise expects your application to call 'flash[:notice]' and 'flash[:alert]' as appropriate.
== I18n
Devise uses flash messages with I18n with the flash keys :success and :failure. To customize your app, you can setup your locale file this way:
@@ -242,7 +247,9 @@ Please refer to TODO file.
== Contributors
We have a long running list of contributors. Check them in the CHANGELOG or do `git shortlog -s -n` in the cloned repository.
We have a long running list of contributors. Check them all here:
http://github.com/plataformatec/devise/contributors
== Bugs and Feedback

View File

@@ -43,11 +43,11 @@ begin
s.homepage = "http://github.com/plataformatec/devise"
s.description = "Flexible authentication solution for Rails with Warden"
s.authors = ['José Valim', 'Carlos Antônio']
s.files = FileList["[A-Z]*", "{app,config,generators,lib}/**/*", "init.rb"]
s.add_dependency("warden", "~> 0.9.0")
s.files = FileList["[A-Z]*", "{app,config,generators,lib}/**/*", "rails/init.rb"]
s.add_dependency("warden", "~> 0.10.3")
end
Jeweler::GemcutterTasks.new
rescue LoadError
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
end

4
TODO
View File

@@ -1,4 +1,2 @@
* Make test run with DataMapper
* Add Registerable support
* Add http authentication support
* Extract Activatable tests from Confirmable
* Extract Activatable tests from Confirmable

View File

@@ -1,10 +1,27 @@
class ConfirmationsController < ApplicationController
include Devise::Controllers::InternalHelpers
include Devise::Controllers::Common
# GET /resource/confirmation/new
def new
build_resource
render_with_scope :new
end
# POST /resource/confirmation
def create
self.resource = resource_class.send_confirmation_instructions(params[resource_name])
if resource.errors.empty?
set_flash_message :notice, :send_instructions
redirect_to new_session_path(resource_name)
else
render_with_scope :new
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
self.resource = resource_class.confirm!(:confirmation_token => params[:confirmation_token])
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
if resource.errors.empty?
set_flash_message :notice, :confirmed
@@ -13,10 +30,4 @@ class ConfirmationsController < ApplicationController
render_with_scope :new
end
end
protected
def send_instructions_with
:send_confirmation_instructions
end
end

View File

@@ -1,8 +1,24 @@
class PasswordsController < ApplicationController
prepend_before_filter :require_no_authentication
include Devise::Controllers::InternalHelpers
include Devise::Controllers::Common
before_filter :require_no_authentication
# GET /resource/password/new
def new
build_resource
render_with_scope :new
end
# POST /resource/password
def create
self.resource = resource_class.send_reset_password_instructions(params[resource_name])
if resource.errors.empty?
set_flash_message :notice, :send_instructions
redirect_to new_session_path(resource_name)
else
render_with_scope :new
end
end
# GET /resource/password/edit?reset_password_token=abcdef
def edit
@@ -13,7 +29,7 @@ class PasswordsController < ApplicationController
# PUT /resource/password
def update
self.resource = resource_class.reset_password!(params[resource_name])
self.resource = resource_class.reset_password_by_token(params[resource_name])
if resource.errors.empty?
set_flash_message :notice, :updated
@@ -22,10 +38,4 @@ class PasswordsController < ApplicationController
render_with_scope :edit
end
end
protected
def send_instructions_with
:send_reset_password_instructions
end
end

View File

@@ -0,0 +1,53 @@
class RegistrationsController < ApplicationController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]
include Devise::Controllers::InternalHelpers
# GET /resource/sign_in
def new
build_resource
render_with_scope :new
end
# POST /resource/sign_up
def create
build_resource
if resource.save
set_flash_message :notice, :signed_up
sign_in_and_redirect(resource_name, resource)
else
render_with_scope :new
end
end
# GET /resource/edit
def edit
render_with_scope :edit
end
# PUT /resource
def update
if self.resource.update_with_password(params[resource_name])
set_flash_message :notice, :updated
redirect_to after_sign_in_path_for(self.resource)
else
render_with_scope :edit
end
end
# DELETE /resource
def destroy
self.resource.destroy
set_flash_message :notice, :destroyed
sign_out_and_redirect(self.resource)
end
protected
# Authenticates the current scope and dup the resource
def authenticate_scope!
send(:"authenticate_#{resource_name}!")
self.resource = send(:"current_#{resource_name}").dup
end
end

View File

@@ -1,15 +1,17 @@
class SessionsController < ApplicationController
prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
include Devise::Controllers::InternalHelpers
include Devise::Controllers::Common
before_filter :require_no_authentication, :only => [ :new, :create ]
# GET /resource/sign_in
def new
Devise::FLASH_MESSAGES.each do |message|
set_now_flash_message :alert, message if params.try(:[], message) == "true"
unless flash[:notice].present?
Devise::FLASH_MESSAGES.each do |message|
set_now_flash_message :alert, message if params.try(:[], message) == "true"
end
end
super
build_resource
render_with_scope :new
end
# POST /resource/sign_in
@@ -17,9 +19,11 @@ class SessionsController < ApplicationController
if resource = authenticate(resource_name)
set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource, true)
elsif [:custom, :redirect].include?(warden.result)
throw :warden, :scope => resource_name
else
set_now_flash_message :alert, warden.message || :invalid
build_resource
set_now_flash_message :alert, (warden.message || :invalid)
clean_up_passwords(build_resource)
render_with_scope :new
end
end
@@ -30,4 +34,9 @@ class SessionsController < ApplicationController
sign_out_and_redirect(resource_name)
end
protected
def clean_up_passwords(object)
object.clean_up_passwords if object.respond_to?(:clean_up_passwords)
end
end

View File

@@ -1,10 +1,29 @@
class UnlocksController < ApplicationController
prepend_before_filter :ensure_email_as_unlock_strategy
prepend_before_filter :require_no_authentication
include Devise::Controllers::InternalHelpers
include Devise::Controllers::Common
# GET /resource/unlock/new
def new
build_resource
render_with_scope :new
end
# POST /resource/unlock
def create
self.resource = resource_class.send_unlock_instructions(params[resource_name])
if resource.errors.empty?
set_flash_message :notice, :send_instructions
redirect_to new_session_path(resource_name)
else
render_with_scope :new
end
end
# GET /resource/unlock?unlock_token=abcdef
def show
self.resource = resource_class.unlock!(:unlock_token => params[:unlock_token])
self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
if resource.errors.empty?
set_flash_message :notice, :unlocked
@@ -16,7 +35,7 @@ class UnlocksController < ApplicationController
protected
def send_instructions_with
:send_unlock_instructions
end
def ensure_email_as_unlock_strategy
raise ActionController::UnknownAction unless resource_class.unlock_strategy_enabled?(:email)
end
end

View File

@@ -1,4 +1,5 @@
class DeviseMailer < ::ActionMailer::Base
extend Devise::Controllers::InternalHelpers::ScopedViews
# Deliver confirmation instructions when the user is created or its email is
# updated, and also when confirmation is manually requested
@@ -19,19 +20,19 @@ class DeviseMailer < ::ActionMailer::Base
# Configure default email options
def setup_mail(record, key)
mapping = Devise::Mapping.find_by_class(record.class)
raise "Invalid devise resource #{record}" unless mapping
scope_name = Devise::Mapping.find_scope!(record)
mapping = Devise.mappings[scope_name]
subject translate(mapping, key)
from mailer_sender(mapping)
recipients record.email
sent_on Time.now
content_type 'text/html'
content_type Devise.mailer_content_type
body render_with_scope(key, mapping, mapping.name => record, :resource => record)
end
def render_with_scope(key, mapping, assigns)
if Devise.scoped_views
if self.class.scoped_views
begin
render :file => "devise_mailer/#{mapping.as}/#{key}", :body => assigns
rescue ActionView::MissingTemplate
@@ -45,7 +46,7 @@ class DeviseMailer < ::ActionMailer::Base
def mailer_sender(mapping)
if Devise.mailer_sender.is_a?(Proc)
block_args = mapping.name if Devise.mailer_sender.arity > 0
Devise.mailer_sender.call(*block_args)
Devise.mailer_sender.call(block_args)
else
Devise.mailer_sender
end

View File

@@ -1,5 +1,5 @@
Welcome <%= @resource.email %>!
<p>Welcome <%= @resource.email %>!</p>
You can confirm your account through the link below:
<p>You can confirm your account through the link below:</p>
<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %></p>

View File

@@ -1,8 +1,8 @@
Hello <%= @resource.email %>!
<p>Hello <%= @resource.email %>!</p>
Someone has requested a link to change your password, and you can do this through the link below.
<p>Someone has requested a link to change your password, and you can do this through the link below.</p>
<%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %></p>
If you didn't request this, please ignore this email.
Your password won't change until you access the link above and create a new one.
<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>

View File

@@ -1,7 +1,7 @@
Hello <%= @resource.email %>!
<p>Hello <%= @resource.email %>!</p>
Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
<p>Your account has been locked due to an excessive amount of unsuccessful sign in attempts.</p>
Click the link below to unlock your account:
<p>Click the link below to unlock your account:</p>
<%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
<p><%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %></p>

View File

@@ -0,0 +1,25 @@
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<% form_for resource_name, resource, :url => registration_path(resource_name), :html => { :method => :put } do |f| -%>
<%= f.error_messages %>
<p><%= f.label :email %></p>
<p><%= f.text_field :email %></p>
<p><%= f.label :password %> <i>(leave blank if you don't want to change it)</i></p>
<p><%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %></p>
<p><%= f.password_field :password_confirmation %></p>
<p><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i></p>
<p><%= f.password_field :current_password %></p>
<p><%= f.submit "Update" %></p>
<% end -%>
<h3>Cancel my account</h3>
<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.</p>
<%= render :partial => "shared/devise_links" %>

View File

@@ -0,0 +1,17 @@
<h2>Sign up</h2>
<% form_for resource_name, resource, :url => registration_path(resource_name) do |f| -%>
<%= f.error_messages %>
<p><%= f.label :email %></p>
<p><%= f.text_field :email %></p>
<p><%= f.label :password %></p>
<p><%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %></p>
<p><%= f.password_field :password_confirmation %></p>
<p><%= f.submit "Sign up" %></p>
<% end -%>
<%= render :partial => "shared/devise_links" %>

View File

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

View File

@@ -2,6 +2,10 @@
<%= link_to t('devise.sessions.link'), new_session_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to t('devise.registrations.link'), new_registration_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
<%= link_to t('devise.passwords.link'), new_password_path(resource_name) %><br />
<% end -%>
@@ -12,4 +16,4 @@
<%- if devise_mapping.lockable? && controller_name != 'unlocks' %>
<%= link_to t('devise.unlocks.link'), new_unlock_path(resource_name) %><br />
<% end -%>
<% end -%>

180
devise.gemspec Normal file
View File

@@ -0,0 +1,180 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{devise}
s.version = "1.0.8"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jos\303\251 Valim", "Carlos Ant\303\264nio"]
s.date = %q{2010-06-23}
s.description = %q{Flexible authentication solution for Rails with Warden}
s.email = %q{contact@plataformatec.com.br}
s.extra_rdoc_files = [
"README.rdoc",
"TODO"
]
s.files = [
"CHANGELOG.rdoc",
"MIT-LICENSE",
"README.rdoc",
"Rakefile",
"TODO",
"app/controllers/confirmations_controller.rb",
"app/controllers/passwords_controller.rb",
"app/controllers/registrations_controller.rb",
"app/controllers/sessions_controller.rb",
"app/controllers/unlocks_controller.rb",
"app/models/devise_mailer.rb",
"app/views/confirmations/new.html.erb",
"app/views/devise_mailer/confirmation_instructions.html.erb",
"app/views/devise_mailer/reset_password_instructions.html.erb",
"app/views/devise_mailer/unlock_instructions.html.erb",
"app/views/passwords/edit.html.erb",
"app/views/passwords/new.html.erb",
"app/views/registrations/edit.html.erb",
"app/views/registrations/new.html.erb",
"app/views/sessions/new.html.erb",
"app/views/shared/_devise_links.erb",
"app/views/unlocks/new.html.erb",
"generators/devise/USAGE",
"generators/devise/devise_generator.rb",
"generators/devise/lib/route_devise.rb",
"generators/devise/templates/migration.rb",
"generators/devise/templates/model.rb",
"generators/devise_install/USAGE",
"generators/devise_install/devise_install_generator.rb",
"generators/devise_install/templates/README",
"generators/devise_install/templates/devise.rb",
"generators/devise_views/USAGE",
"generators/devise_views/devise_views_generator.rb",
"lib/devise.rb",
"lib/devise/controllers/helpers.rb",
"lib/devise/controllers/internal_helpers.rb",
"lib/devise/controllers/url_helpers.rb",
"lib/devise/encryptors/authlogic_sha512.rb",
"lib/devise/encryptors/base.rb",
"lib/devise/encryptors/bcrypt.rb",
"lib/devise/encryptors/clearance_sha1.rb",
"lib/devise/encryptors/restful_authentication_sha1.rb",
"lib/devise/encryptors/sha1.rb",
"lib/devise/encryptors/sha512.rb",
"lib/devise/failure_app.rb",
"lib/devise/hooks/activatable.rb",
"lib/devise/hooks/rememberable.rb",
"lib/devise/hooks/timeoutable.rb",
"lib/devise/hooks/trackable.rb",
"lib/devise/locales/en.yml",
"lib/devise/mapping.rb",
"lib/devise/models.rb",
"lib/devise/models/activatable.rb",
"lib/devise/models/confirmable.rb",
"lib/devise/models/database_authenticatable.rb",
"lib/devise/models/http_authenticatable.rb",
"lib/devise/models/lockable.rb",
"lib/devise/models/recoverable.rb",
"lib/devise/models/registerable.rb",
"lib/devise/models/rememberable.rb",
"lib/devise/models/timeoutable.rb",
"lib/devise/models/token_authenticatable.rb",
"lib/devise/models/trackable.rb",
"lib/devise/models/validatable.rb",
"lib/devise/orm/active_record.rb",
"lib/devise/orm/data_mapper.rb",
"lib/devise/orm/mongo_mapper.rb",
"lib/devise/rails.rb",
"lib/devise/rails/routes.rb",
"lib/devise/rails/warden_compat.rb",
"lib/devise/schema.rb",
"lib/devise/strategies/base.rb",
"lib/devise/strategies/database_authenticatable.rb",
"lib/devise/strategies/http_authenticatable.rb",
"lib/devise/strategies/rememberable.rb",
"lib/devise/strategies/token_authenticatable.rb",
"lib/devise/test_helpers.rb",
"lib/devise/version.rb",
"rails/init.rb"
]
s.homepage = %q{http://github.com/plataformatec/devise}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.7}
s.summary = %q{Flexible authentication solution for Rails with Warden}
s.test_files = [
"test/controllers/helpers_test.rb",
"test/controllers/internal_helpers_test.rb",
"test/controllers/url_helpers_test.rb",
"test/devise_test.rb",
"test/encryptors_test.rb",
"test/failure_app_test.rb",
"test/integration/authenticatable_test.rb",
"test/integration/confirmable_test.rb",
"test/integration/http_authenticatable_test.rb",
"test/integration/lockable_test.rb",
"test/integration/rack_middleware_test.rb",
"test/integration/recoverable_test.rb",
"test/integration/registerable_test.rb",
"test/integration/rememberable_test.rb",
"test/integration/timeoutable_test.rb",
"test/integration/token_authenticatable_test.rb",
"test/integration/trackable_test.rb",
"test/mailers/confirmation_instructions_test.rb",
"test/mailers/reset_password_instructions_test.rb",
"test/mailers/unlock_instructions_test.rb",
"test/mapping_test.rb",
"test/models/authenticatable_test.rb",
"test/models/confirmable_test.rb",
"test/models/lockable_test.rb",
"test/models/recoverable_test.rb",
"test/models/rememberable_test.rb",
"test/models/timeoutable_test.rb",
"test/models/token_authenticatable_test.rb",
"test/models/trackable_test.rb",
"test/models/validatable_test.rb",
"test/models_test.rb",
"test/orm/active_record.rb",
"test/orm/mongo_mapper.rb",
"test/rails_app/app/active_record/admin.rb",
"test/rails_app/app/active_record/user.rb",
"test/rails_app/app/controllers/admins_controller.rb",
"test/rails_app/app/controllers/application_controller.rb",
"test/rails_app/app/controllers/home_controller.rb",
"test/rails_app/app/controllers/users_controller.rb",
"test/rails_app/app/helpers/application_helper.rb",
"test/rails_app/app/mongo_mapper/admin.rb",
"test/rails_app/app/mongo_mapper/user.rb",
"test/rails_app/config/boot.rb",
"test/rails_app/config/environment.rb",
"test/rails_app/config/environments/development.rb",
"test/rails_app/config/environments/production.rb",
"test/rails_app/config/environments/test.rb",
"test/rails_app/config/initializers/devise.rb",
"test/rails_app/config/initializers/inflections.rb",
"test/rails_app/config/initializers/new_rails_defaults.rb",
"test/rails_app/config/initializers/session_store.rb",
"test/rails_app/config/routes.rb",
"test/routes_test.rb",
"test/support/assertions_helper.rb",
"test/support/integration_tests_helper.rb",
"test/support/test_silencer.rb",
"test/support/tests_helper.rb",
"test/test_helper.rb",
"test/test_helpers_test.rb"
]
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<warden>, ["~> 0.10.3"])
else
s.add_dependency(%q<warden>, ["~> 0.10.3"])
end
else
s.add_dependency(%q<warden>, ["~> 0.10.3"])
end
end

View File

@@ -1,7 +1,7 @@
class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
def self.up
create_table(:<%= table_name %>) do |t|
t.authenticatable :encryptor => :sha1, :null => false
t.database_authenticatable :null => false
t.confirmable
t.recoverable
t.rememberable

View File

@@ -1,7 +1,8 @@
class <%= class_name %> < ActiveRecord::Base
# Include default devise modules.
# Others available are :lockable, :timeoutable and :activatable.
devise :authenticatable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
# Include default devise modules. Others available are:
# :http_authenticatable, :token_authenticatable, :confirmable, :lockable, :timeoutable and :activatable
devise :registerable, :database_authenticatable, :recoverable,
:rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation

View File

@@ -1,4 +1,3 @@
===============================================================================
Some setup you must do manually if you haven't yet:
@@ -15,4 +14,10 @@ Some setup you must do manually if you haven't yet:
map.root :controller => 'home'
3. Ensure you have a default layout in app/views/layouts and it shows
flash messages. For example:
<p class="notice"><%= flash[:notice] %></p>
<p class="alert"><%= flash[:alert] %></p>
===============================================================================

View File

@@ -3,6 +3,9 @@
Devise.setup do |config|
# Configure the e-mail address which will be shown in DeviseMailer.
config.mailer_sender = "please-change-me@config-initializers-devise.com"
# Configure the content type of DeviseMailer mails (defaults to text/html")
# config.mailer_content_type = "text/plain"
# ==> Configuration for :authenticatable
# Invoke `rake secret` and use the printed value to setup a pepper to generate
@@ -26,6 +29,9 @@ Devise.setup do |config|
# session. If you need permissions, you should implement that in a before filter.
# config.authentication_keys = [ :email ]
# The realm used in Http Basic Authentication
# config.http_authentication_realm = "Application"
# ==> Configuration for :confirmable
# The time you want give to your user to confirm his account. During this time
# he will be able to access your application without confirming. Default is nil.
@@ -53,6 +59,10 @@ Devise.setup do |config|
# Time interval to unlock the account if :time is enabled as unlock_strategy.
# config.unlock_in = 1.hour
# ==> Configuration for :token_authenticatable
# Defines name of the authentication token params key
# config.token_authentication_key = :auth_token
# ==> General configuration
# Load and configure the ORM. Supports :active_record (default), :mongo_mapper
# (requires mongo_ext installed) and :data_mapper (experimental).
@@ -89,7 +99,6 @@ Devise.setup do |config|
# Configure default_url_options if you are using dynamic segments in :path_prefix
# for devise_for.
#
# config.default_url_options do
# { :locale => I18n.locale }
# end

View File

@@ -1,10 +1,10 @@
module Devise
autoload :FailureApp, 'devise/failure_app'
autoload :Models, 'devise/models'
autoload :Schema, 'devise/schema'
autoload :TestHelpers, 'devise/test_helpers'
module Controllers
autoload :Common, 'devise/controllers/common'
autoload :Helpers, 'devise/controllers/helpers'
autoload :InternalHelpers, 'devise/controllers/internal_helpers'
autoload :UrlHelpers, 'devise/controllers/url_helpers'
@@ -14,7 +14,7 @@ module Devise
autoload :Base, 'devise/encryptors/base'
autoload :Bcrypt, 'devise/encryptors/bcrypt'
autoload :AuthlogicSha512, 'devise/encryptors/authlogic_sha512'
autoload :AuthlogicSha1, 'devise/encryptors/authlogic_sha1'
autoload :ClearanceSha1, 'devise/encryptors/clearance_sha1'
autoload :RestfulAuthenticationSha1, 'devise/encryptors/restful_authentication_sha1'
autoload :Sha512, 'devise/encryptors/sha512'
autoload :Sha1, 'devise/encryptors/sha1'
@@ -26,22 +26,38 @@ module Devise
autoload :MongoMapper, 'devise/orm/mongo_mapper'
end
ALL = [:authenticatable, :activatable, :confirmable, :recoverable,
:rememberable, :validatable, :trackable, :timeoutable, :lockable]
ALL = []
# Maps controller names to devise modules
# Authentication ones first
ALL.push :database_authenticatable, :http_authenticatable, :token_authenticatable, :rememberable
# Misc after
ALL.push :recoverable, :registerable, :validatable
# The ones which can sign out after
ALL.push :activatable, :confirmable, :lockable, :timeoutable
# Stats for last, so we make sure the user is really signed in
ALL.push :trackable
# Maps controller names to devise modules.
CONTROLLERS = {
:sessions => [:authenticatable],
:sessions => [:database_authenticatable, :token_authenticatable],
:passwords => [:recoverable],
:confirmations => [:confirmable],
:registrations => [:registerable],
:unlocks => [:lockable]
}
STRATEGIES = [:rememberable, :authenticatable]
# Routes for generating url helpers.
ROUTES = [:session, :password, :confirmation, :registration, :unlock]
STRATEGIES = [:rememberable, :http_authenticatable, :token_authenticatable, :database_authenticatable]
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
# Maps the messages types that are used in flash message.
FLASH_MESSAGES = [ :unauthenticated, :unconfirmed, :invalid, :timeout, :inactive, :locked ]
FLASH_MESSAGES = [:unauthenticated, :unconfirmed, :invalid, :invalid_token, :timeout, :inactive, :locked]
# Declare encryptors length which are used in migrations.
ENCRYPTORS_LENGTH = {
@@ -53,8 +69,8 @@ module Devise
:bcrypt => 60
}
# Email regex used to validate email formats. Retrieved from authlogic.
EMAIL_REGEX = /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2,4}|museum|travel)\z/i
# Email regex used to validate email formats. Adapted from authlogic.
EMAIL_REGEX = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
# Used to encrypt password. Please generate one with rake secret.
mattr_accessor :pepper
@@ -121,7 +137,7 @@ module Devise
# Tell when to use the default scope, if one cannot be found from routes.
mattr_accessor :use_default_scope
@@use_default_scope
@@use_default_scope = false
# The default scope which is used by warden.
mattr_accessor :default_scope
@@ -129,7 +145,19 @@ module Devise
# Address which sends Devise e-mails.
mattr_accessor :mailer_sender
@@mailer_sender
@@mailer_sender = nil
# Content Type of Devise e-mails.
mattr_accessor :mailer_content_type
@@mailer_content_type = 'text/html'
# Authentication token params key name of choice. E.g. /users/sign_in?some_key=...
mattr_accessor :token_authentication_key
@@token_authentication_key = :auth_token
# The realm used in Http Basic Authentication
mattr_accessor :http_authentication_realm
@@http_authentication_realm = "Application"
class << self
# Default way to setup Devise. Run script/generate devise_install to create
@@ -190,6 +218,9 @@ module Devise
# Default is +nil+ (i.e. +false+).
# +controller+ - Symbol representing a name of an exisiting or custom *controller* for this module.
# Default is +nil+ (i.e. +false+).
# +route+ - Symbol representing the name of a *route* related to this module which a set of
# route view helpers should be created for.
# Default is +nil+ (i.e. +false+).
#
# == Examples:
#
@@ -198,7 +229,7 @@ module Devise
# Devise.add_module(:party_module, :model => 'party_module/model')
#
def add_module(module_name, options = {})
Devise::ALL.unshift module_name unless Devise::ALL.include?(module_name)
Devise::ALL << module_name unless Devise::ALL.include?(module_name)
Devise::STRATEGIES.unshift module_name if options[:strategy] && !Devise::STRATEGIES.include?(module_name)
if options[:controller]
@@ -207,6 +238,10 @@ module Devise
Devise::CONTROLLERS[controller].unshift module_name unless Devise::CONTROLLERS[controller].include?(module_name)
end
if options[:route]
Devise::ROUTES.unshift options[:route] unless Devise::ROUTES.include?(options[:route])
end
if options[:model]
Devise::Models.module_eval do
autoload :"#{module_name.to_s.classify}", options[:model]
@@ -226,4 +261,4 @@ rescue
end
require 'devise/mapping'
require 'devise/rails'
require 'devise/rails'

View File

@@ -1,24 +0,0 @@
module Devise
module Controllers
# Common actions shared between Devise controllers
module Common #:nodoc:
# GET /resource/controller/new
def new
build_resource
render_with_scope :new
end
# POST /resource/controller
def create
self.resource = resource_class.send(send_instructions_with, params[resource_name])
if resource.errors.empty?
set_flash_message :notice, :send_instructions
redirect_to new_session_path(resource_name)
else
render_with_scope :new
end
end
end
end
end

View File

@@ -5,8 +5,8 @@ module Devise
def self.included(base)
base.class_eval do
helper_method :warden, :signed_in?, :devise_controller?,
*Devise.mappings.keys.map { |m| [:"current_#{m}", :"#{m}_signed_in?"] }.flatten
helper_method :warden, :signed_in?, :devise_controller?, :anybody_signed_in?,
*Devise.mappings.keys.map { |m| [:"current_#{m}", :"#{m}_signed_in?", :"#{m}_session"] }.flatten
# Use devise default_url_options. We have to declare it here to overwrite
# default definitions.
@@ -48,6 +48,12 @@ module Devise
warden.authenticate?(:scope => scope)
end
# Check if the any scope is signed in session, without running
# authentication hooks.
def anybody_signed_in?
Devise.mappings.keys.any? { |scope| signed_in?(scope) }
end
# Sign in an user that already was authenticated. This helper is useful for logging
# users in after sign up.
#
@@ -176,7 +182,7 @@ module Devise
# before_filter :authenticate_admin! # Tell devise to use :admin map
#
Devise.mappings.each_key do |mapping|
class_eval <<-METHODS, __FILE__, __LINE__
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!
warden.authenticate!(:scope => :#{mapping})
end

View File

@@ -7,13 +7,24 @@ module Devise
def self.included(base)
base.class_eval do
extend ScopedViews
unloadable
helper_method :resource, :scope_name, :resource_name, :resource_class, :devise_mapping, :devise_controller?
hide_action :resource, :scope_name, :resource_name, :resource_class, :devise_mapping, :devise_controller?
skip_before_filter *Devise.mappings.keys.map { |m| :"authenticate_#{m}!" }
before_filter :is_devise_resource?
prepend_before_filter :is_devise_resource?
end
end
module ScopedViews
def scoped_views
defined?(@scoped_views) ? @scoped_views : Devise.scoped_views
end
def scoped_views=(value)
@scoped_views = value
end
end
@@ -59,12 +70,9 @@ module Devise
instance_variable_set(:"@#{resource_name}", new_resource)
end
# Build a devise resource without setting password and password confirmation fields.
# Build a devise resource.
def build_resource
self.resource ||= begin
attributes = params[resource_name].try(:except, :password, :password_confirmation)
resource_class.new(attributes || {})
end
self.resource ||= resource_class.new(params[resource_name] || {})
end
# Helper for use in before_filters where no authentication is required.
@@ -104,7 +112,8 @@ module Devise
# Accepts just :controller as option.
def render_with_scope(action, options={})
controller_name = options.delete(:controller) || self.controller_name
if Devise.scoped_views
if self.class.scoped_views
begin
render :template => "#{controller_name}/#{devise_mapping.as}/#{action}"
rescue ActionView::MissingTemplate

View File

@@ -19,17 +19,17 @@ module Devise
# Those helpers are added to your ApplicationController.
module UrlHelpers
[:session, :password, :confirmation, :unlock].each do |module_name|
Devise::ROUTES.each do |module_name|
[:path, :url].each do |path_or_url|
actions = [ nil, :new_ ]
actions << :edit_ if module_name == :password
actions << :destroy_ if module_name == :session
actions << :edit_ if [:password, :registration].include?(module_name)
actions << :destroy_ if [:session].include?(module_name)
actions.each do |action|
class_eval <<-URL_HELPERS
def #{action}#{module_name}_#{path_or_url}(resource, *args)
resource = Devise::Mapping.find_scope!(resource)
send("#{action}\#{resource}_#{module_name}_#{path_or_url}", *args)
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

View File

@@ -59,7 +59,7 @@ module Devise
# yet, but we still need to store the uri based on scope, so different scopes
# would never use the same uri to redirect.
def store_location!(scope)
session[:"#{scope}.return_to"] ||= request.request_uri if request && request.get?
session[:"#{scope}.return_to"] = request.request_uri if request && request.get?
end
end
end

View File

@@ -3,7 +3,7 @@
# that specific user and adds a cookie with this user info to sign in this user
# automatically without asking for credentials. Refer to rememberable strategy
# for more info.
Warden::Manager.after_authentication do |record, warden, options|
Warden::Manager.prepend_after_authentication do |record, warden, options|
scope = options[:scope]
remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
@@ -22,9 +22,11 @@ end
# Before logout hook to forget the user in the given scope, only if rememberable
# is activated for this scope. Also clear remember token to ensure the user
# won't be remembered again.
# Notice that we forget the user if the record is frozen. This usually means the
# user was just deleted.
Warden::Manager.before_logout do |record, warden, scope|
if record.respond_to?(:forget_me!)
record.forget_me!
record.forget_me! unless record.frozen?
warden.response.delete_cookie "remember_#{scope}_token"
end
end

View File

@@ -8,6 +8,7 @@ en:
unconfirmed: 'You have to confirm your account before continuing.'
locked: 'Your account is locked.'
invalid: 'Invalid email or password.'
invalid_token: 'Invalid authentication token.'
timeout: 'Your session expired, please sign in again to continue.'
inactive: 'Your account was not activated yet.'
passwords:
@@ -18,6 +19,11 @@ en:
link: "Didn't receive confirmation instructions?"
send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
confirmed: 'Your account was successfully confirmed. You are now signed in.'
registrations:
link: 'Sign up'
signed_up: 'You have signed up successfully. If enabled, a confirmation was sent to your e-mail.'
updated: 'You updated your account successfully.'
destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
unlocks:
link: "Didn't receive unlock instructions?"
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
@@ -26,3 +32,4 @@ en:
confirmation_instructions: 'Confirmation instructions'
reset_password_instructions: 'Reset password instructions'
unlock_instructions: 'Unlock Instructions'

View File

@@ -34,23 +34,19 @@ module Devise
nil
end
# Find a mapping by a given class. It takes into account single table inheritance as well.
def self.find_by_class(klass)
Devise.mappings.values.find { |m| return m if klass <= m.to }
end
# Receives an object and find a scope for it. If a scope cannot be found,
# raises an error. If a symbol is given, it's considered to be the scope.
def self.find_scope!(duck)
case duck
when String, Symbol
duck
return duck
when Class
Devise.mappings.each_value { |m| return m.name if duck <= m.to }
else
klass = duck.is_a?(Class) ? duck : duck.class
mapping = Devise::Mapping.find_by_class(klass)
raise "Could not find a valid mapping for #{duck}" unless mapping
mapping.name
Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
end
raise "Could not find a valid mapping for #{duck}"
end
# Default url options which can be used as prefix.
@@ -62,11 +58,12 @@ module Devise
@as = (options.delete(:as) || name).to_sym
@klass = (options.delete(:class_name) || name.to_s.classify).to_s
@name = (options.delete(:scope) || name.to_s.singularize).to_sym
@path_names = options.delete(:path_names) || {}
@path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
@path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
@route_options = options || {}
setup_path_names
@path_names = Hash.new { |h,k| h[k] = k.to_s }
@path_names.merge!(options.delete(:path_names) || {})
end
# Return modules for the mapping.
@@ -92,23 +89,23 @@ module Devise
self.path_prefix.count("/")
end
# Returns the raw path using the current relative_url_root, path_prefix and as.
# Returns the raw path using path_prefix and as.
def raw_path
ActionController::Base.relative_url_root.to_s + path_prefix + as.to_s
path_prefix + as.to_s
end
# Returns the parsed path. If you need meta information in your path_prefix,
# you should overwrite this method to use it. The only information supported
# by default is I18n.locale.
#
# Returns the parsed path taking into account the relative url root and raw path.
def parsed_path
returning raw_path do |path|
returning (ActionController::Base.relative_url_root.to_s + raw_path) do |path|
self.class.default_url_options.each do |key, value|
path.gsub!(key.inspect, value.to_param)
end
end
end
def authenticatable?
@authenticatable ||= self.for.any? { |m| m.to_s =~ /authenticatable/ }
end
# Create magic predicates for verifying what module is activated by this map.
# Example:
@@ -119,7 +116,7 @@ module Devise
#
def self.register(*modules)
modules.each do |m|
class_eval <<-METHOD, __FILE__, __LINE__
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{m}?
self.for.include?(:#{m})
end
@@ -127,15 +124,5 @@ module Devise
end
end
Devise::Mapping.register *ALL
private
# Configure default path names, allowing the user overwrite defaults by
# passing a hash in :path_names.
def setup_path_names
[:sign_in, :sign_out, :password, :confirmation].each do |path_name|
@path_names[path_name] ||= path_name.to_s
end
end
end
end

View File

@@ -1,11 +1,12 @@
module Devise
module Models
autoload :Activatable, 'devise/models/activatable'
autoload :Authenticatable, 'devise/models/authenticatable'
autoload :DatabaseAuthenticatable, 'devise/models/database_authenticatable'
autoload :Confirmable, 'devise/models/confirmable'
autoload :Lockable, 'devise/models/lockable'
autoload :Recoverable, 'devise/models/recoverable'
autoload :Rememberable, 'devise/models/rememberable'
autoload :Registerable, 'devise/models/registerable'
autoload :Timeoutable, 'devise/models/timeoutable'
autoload :Trackable, 'devise/models/trackable'
autoload :Validatable, 'devise/models/validatable'
@@ -28,7 +29,7 @@ module Devise
#
def self.config(mod, *accessors) #:nodoc:
accessors.each do |accessor|
mod.class_eval <<-METHOD, __FILE__, __LINE__
mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{accessor}
if defined?(@#{accessor})
@#{accessor}
@@ -50,26 +51,23 @@ module Devise
#
# devise :authenticatable, :confirmable, :recoverable
#
# You can also give the following configuration values in a hash: :pepper,
# :stretches, :confirm_within and :remember_for. Please check your Devise
# initialiazer for a complete description on those values.
# You can also give any of the devise configuration values in form of a hash,
# with specific values for this model. Please check your Devise initializer
# for a complete description on those values.
#
def devise(*modules)
raise "You need to give at least one Devise module" if modules.empty?
options = modules.extract_options!
options = modules.extract_options!
# TODO Remove me
if modules.delete(:all)
ActiveSupport::Deprecation.warn "devise :all is deprecated. List your modules instead", caller
modules += Devise.all
if modules.delete(:authenticatable)
ActiveSupport::Deprecation.warn ":authenticatable as module is deprecated. Please give :database_authenticatable instead.", caller
modules << :database_authenticatable
end
modules -= Array(options.delete(:except))
modules = Devise::ALL & modules.uniq
@devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
Devise.orm_class.included_modules_hook(self, modules) do
modules.each do |m|
devise_modules << m.to_sym
Devise.orm_class.included_modules_hook(self) do
devise_modules.each do |m|
include Devise::Models.const_get(m.to_s.classify)
end
@@ -116,4 +114,4 @@ module Devise
end
end
end
end
end

View File

@@ -57,18 +57,13 @@ module Devise
# Send confirmation instructions by email
def send_confirmation_instructions
generate_confirmation_token if self.confirmation_token.nil?
::DeviseMailer.deliver_confirmation_instructions(self)
end
# Remove confirmation date and send confirmation instructions, to ensure
# after sending these instructions the user won't be able to sign in without
# confirming it's account
def resend_confirmation!
unless_confirmed do
generate_confirmation_token
save(false)
send_confirmation_instructions
end
# Resend confirmation token. This method does not need to generate a new token.
def resend_confirmation_token
unless_confirmed { send_confirmation_instructions }
end
# Overwrites active? from Devise::Models::Activatable for confirmation
@@ -76,16 +71,12 @@ module Devise
# is already confirmed, it should never be blocked. Otherwise we need to
# calculate if the confirm time has not expired for this user.
def active?
super && (confirmed? || confirmation_period_valid?)
super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
end
# The message to be shown if the account is inactive.
def inactive_message
if !confirmed?
:unconfirmed
else
super
end
!confirmed? ? :unconfirmed : super
end
# If you don't want confirmation to be sent on create, neither a code
@@ -151,7 +142,7 @@ module Devise
# Options must contain the user email
def send_confirmation_instructions(attributes={})
confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
confirmable.resend_confirmation! unless confirmable.new_record?
confirmable.resend_confirmation_token unless confirmable.new_record?
confirmable
end
@@ -159,8 +150,8 @@ module Devise
# 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
def confirm!(attributes={})
confirmable = find_or_initialize_with_error_by(:confirmation_token, attributes[:confirmation_token])
def confirm_by_token(confirmation_token)
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
confirmable.confirm! unless confirmable.new_record?
confirmable
end

View File

@@ -1,4 +1,4 @@
require 'devise/strategies/authenticatable'
require 'devise/strategies/database_authenticatable'
module Devise
module Models
@@ -26,17 +26,24 @@ module Devise
# User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
# User.find(1).valid_password?('password123') # returns true/false
#
module Authenticatable
module DatabaseAuthenticatable
def self.included(base)
base.class_eval do
extend ClassMethods
attr_reader :password, :old_password
attr_reader :password, :current_password
attr_accessor :password_confirmation
end
end
# Regenerates password salt and encrypted password each time password is set.
# TODO Remove me in next release
def old_password
ActiveSupport::Deprecation.warn "old_password is deprecated, please use current_password instead", caller
@old_password
end
# Regenerates password salt and encrypted password each time password is set,
# and then trigger any "after_changed_password"-callbacks.
def password=(new_password)
@password = new_password
@@ -48,7 +55,7 @@ module Devise
# Verifies whether an incoming_password (ie from sign in) is the user password.
def valid_password?(incoming_password)
password_digest(incoming_password) == encrypted_password
password_digest(incoming_password) == self.encrypted_password
end
# Checks if a resource is valid upon authentication.
@@ -56,28 +63,54 @@ module Devise
valid_password?(attributes[:password])
end
# Update record attributes when :old_password matches, otherwise returns
# error on :old_password.
# Set password and password confirmation to nil
def clean_up_passwords
self.password = self.password_confirmation = nil
end
# Update record attributes when :current_password matches, otherwise returns
# error on :current_password. It also automatically rejects :password and
# :password_confirmation if they are blank.
def update_with_password(params={})
if valid_password?(params[:old_password])
current_password = params.delete(:current_password)
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
result = if valid_password?(current_password)
update_attributes(params)
else
self.class.add_error_on(self, :old_password, :invalid, false)
message = current_password.blank? ? :blank : :invalid
self.class.add_error_on(self, :current_password, message, false)
self.attributes = params
false
end
clean_up_passwords unless result
result
end
protected
# Checks whether a password is needed or not. For validations only.
# Passwords are always required if it's a new record, or if the password
# or confirmation are being set somewhere.
def password_required?
new_record? || !password.nil? || !password_confirmation.nil?
end
# Digests the password using the configured encryptor.
def password_digest(password)
self.class.encryptor_class.digest(password, self.class.stretches, password_salt, self.class.pepper)
self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
end
module ClassMethods
Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
# Authenticate a user based on configured attribute keys. Returns the
# authenticated user if it's valid or nil. Attributes are by default
# :email and :password, but the latter is always required.
# authenticated user if it's valid or nil.
def authenticate(attributes={})
return unless authentication_keys.all? { |k| attributes[k].present? }
conditions = attributes.slice(*authentication_keys)
@@ -105,8 +138,6 @@ module Devise
def find_for_authentication(conditions)
find(:first, :conditions => conditions)
end
Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
end
end
end

View File

@@ -0,0 +1,21 @@
require 'devise/strategies/http_authenticatable'
module Devise
module Models
# Adds HttpAuthenticatable behavior to your model. It expects that your
# model class responds to authenticate and authentication_keys methods
# (which for example are defined in authenticatable).
module HttpAuthenticatable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# Authenticate an user using http.
def authenticate_with_http(username, password)
authenticate(authentication_keys.first => username, :password => password)
end
end
end
end
end

View File

@@ -3,9 +3,22 @@ require 'devise/models/activatable'
module Devise
module Models
# 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
# 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.
#
# Configuration:
#
# maximum_attempts: how many attempts should be accepted before blocking the user.
# unlock_strategy: unlock the user account by :time, :email or :both.
# unlock_in: the time you want to lock the user after to lock happens. Only
# available when unlock_strategy is :time or :both.
#
module Lockable
include Devise::Models::Activatable
include Devise::Models::Authenticatable
def self.included(base)
base.class_eval do
@@ -14,23 +27,21 @@ module Devise
end
# Lock an user setting it's locked_at to actual time.
def lock
def lock_access!
return true if access_locked?
self.locked_at = Time.now
if [:both, :email].include?(self.class.unlock_strategy)
generate_unlock_token
self.send_unlock_instructions
end
end
# calls lock and save the model
def lock!
self.lock
if self.class.unlock_strategy_enabled?(:email)
generate_unlock_token
send_unlock_instructions
end
save(false)
end
# Unlock an user by cleaning locket_at and failed_attempts
def unlock!
if_locked do
# Unlock an user by cleaning locket_at and failed_attempts.
def unlock_access!
if_access_locked do
self.locked_at = nil
self.failed_attempts = 0
self.unlock_token = nil
@@ -38,9 +49,9 @@ module Devise
end
end
# Verifies whether a user is locked or not
def locked?
self.locked_at && !lock_expired?
# Verifies whether a user is locked or not.
def access_locked?
locked_at && !lock_expired?
end
# Send unlock instructions by email
@@ -48,19 +59,21 @@ module Devise
::DeviseMailer.deliver_unlock_instructions(self)
end
# Resend the unlock instructions if the user is locked
def resend_unlock!
if_locked do
generate_unlock_token unless self.unlock_token.present?
save(false)
send_unlock_instructions
end
# Resend the unlock instructions if the user is locked.
def resend_unlock_token
if_access_locked { send_unlock_instructions }
end
# Overwrites active? from Devise::Models::Activatable for locking purposes
# by verifying whether an user is active to sign in or not based on locked?
def active?
super && !locked?
super && !access_locked?
end
# Overwrites invalid_message from Devise::Models::Authenticatable to define
# the correct reason for blocking the sign in.
def inactive_message
access_locked? ? :locked : super
end
# Overwrites valid_for_authentication? from Devise::Models::Authenticatable
@@ -71,22 +84,12 @@ module Devise
self.failed_attempts = 0
else
self.failed_attempts += 1
self.lock if self.failed_attempts > self.class.maximum_attempts
lock_access! if failed_attempts > self.class.maximum_attempts
end
save(false) if changed?
result
end
# Overwrites invalid_message from Devise::Models::Authenticatable to define
# the correct reason for blocking the sign in.
def inactive_message
if locked?
:locked
else
super
end
end
protected
# Generates unlock token
@@ -96,8 +99,8 @@ module Devise
# Tells if the lock is expired if :time unlock strategy is active
def lock_expired?
if [:both, :time].include?(self.class.unlock_strategy)
self.locked_at && self.locked_at < self.class.unlock_in.ago
if self.class.unlock_strategy_enabled?(:time)
locked_at && locked_at < self.class.unlock_in.ago
else
false
end
@@ -105,8 +108,8 @@ module Devise
# Checks whether the record is locked or not, yielding to the block
# if it's locked, otherwise adds an error to email.
def if_locked
if locked?
def if_access_locked
if access_locked?
yield
else
self.class.add_error_on(self, :email, :not_locked)
@@ -121,7 +124,7 @@ module Devise
# Options must contain the user email
def send_unlock_instructions(attributes={})
lockable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
lockable.resend_unlock! unless lockable.new_record?
lockable.resend_unlock_token unless lockable.new_record?
lockable
end
@@ -129,14 +132,19 @@ module Devise
# 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
def unlock!(attributes={})
lockable = find_or_initialize_with_error_by(:unlock_token, attributes[:unlock_token])
lockable.unlock! unless lockable.new_record?
def unlock_access_by_token(unlock_token)
lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
lockable.unlock_access! unless lockable.new_record?
lockable
end
# Is the unlock enabled for the given unlock strategy?
def unlock_strategy_enabled?(strategy)
[:both, strategy].include?(self.unlock_strategy)
end
Devise::Models.config(self, :maximum_attempts, :unlock_strategy, :unlock_in)
end
end
end
end
end

View File

@@ -69,7 +69,7 @@ module Devise
# try saving the record. If not user is found, returns a new user
# containing an error in reset_password_token attribute.
# Attributes must contain reset_password_token, password and confirmation
def reset_password!(attributes={})
def reset_password_by_token(attributes={})
recoverable = find_or_initialize_with_error_by(:reset_password_token, attributes[:reset_password_token])
recoverable.reset_password!(attributes[:password], attributes[:password_confirmation]) unless recoverable.new_record?
recoverable

View File

@@ -0,0 +1,8 @@
module Devise
module Models
# Registerable is responsible for everything related to registering a new
# resource (ie user sign up).
module Registerable
end
end
end

View File

@@ -9,7 +9,7 @@ module Devise
#
# Configuration:
#
# timeout: the time you want to timeout the user session without activity.
# timeout_in: the time you want to timeout the user session without activity.
module Timeoutable
def self.included(base)
base.extend ClassMethods

View File

@@ -0,0 +1,89 @@
require 'devise/strategies/token_authenticatable'
module Devise
module Models
# Token Authenticatable Module, responsible for generate authentication token and validating
# authenticity of a user while signing in using an authentication token (say follows an URL).
#
# == Configuration:
#
# You can overwrite configuration values by setting in globally in Devise (+Devise.setup+),
# using devise method, or overwriting the respective instance method.
#
# +token_authentication_key+ - Defines name of the authentication token params key. E.g. /users/sign_in?some_key=...
#
# == Examples:
#
# User.authenticate_with_token(:auth_token => '123456789') # returns authenticated user or nil
# User.find(1).valid_authentication_token?('rI1t6PKQ8yP7VetgwdybB') # returns true/false
#
module TokenAuthenticatable
def self.included(base)
base.class_eval do
extend ClassMethods
before_save :ensure_authentication_token
end
end
# Generate new authentication token (a.k.a. "single access token").
def reset_authentication_token
self.authentication_token = self.class.authentication_token
end
# Generate new authentication token and save the record.
def reset_authentication_token!
reset_authentication_token
self.save
end
# Generate authentication token unless already exists.
def ensure_authentication_token
self.reset_authentication_token if self.authentication_token.blank?
end
# Generate authentication token unless already exists and save the record.
def ensure_authentication_token!
self.reset_authentication_token! if self.authentication_token.blank?
end
# Verifies whether an +incoming_authentication_token+ (i.e. from single access URL)
# is the user authentication token.
def valid_authentication_token?(incoming_auth_token)
incoming_auth_token.present? && incoming_auth_token == self.authentication_token
end
module ClassMethods
::Devise::Models.config(self, :token_authentication_key)
# Authenticate a user based on authentication token.
def authenticate_with_token(attributes)
token = attributes[self.token_authentication_key]
resource = self.find_for_token_authentication(token)
resource if resource.try(:valid_authentication_token?, token)
end
def authentication_token
::Devise.friendly_token
end
protected
# Find first record based on conditions given (ie by the sign in form).
# Overwrite to add customized conditions, create a join, or maybe use a
# namedscope to filter records while authenticating.
#
# == Example:
#
# def self.find_for_token_authentication(token, conditions = {})
# conditions = {:active => true}
# self.find_by_authentication_token(token, :conditions => conditions)
# end
#
def find_for_token_authentication(token)
self.find(:first, :conditions => { :authentication_token => token})
end
end
end
end
end

View File

@@ -34,15 +34,6 @@ module Devise
"to the following methods: #{unavailable_validations.to_sentence}."
end
end
protected
# Checks whether a password is needed or not. For validations only.
# Passwords are always required if it's a new record, or if the password
# or confirmation are being set somewhere.
def password_required?
new_record? || !password.nil? || !password_confirmation.nil?
end
end
end
end

View File

@@ -20,7 +20,7 @@ module Devise
#
module ActiveRecord
# Required ORM hook. Just yield the given block in ActiveRecord.
def self.included_modules_hook(klass, modules)
def self.included_modules_hook(klass)
yield
end
@@ -36,5 +36,6 @@ end
if defined?(ActiveRecord)
ActiveRecord::Base.extend Devise::Models
ActiveRecord::ConnectionAdapters::Table.send :include, Devise::Orm::ActiveRecord
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Orm::ActiveRecord
end
end

View File

@@ -11,13 +11,13 @@ module Devise
end
end
def self.included_modules_hook(klass, modules)
def self.included_modules_hook(klass)
klass.send :extend, self
klass.send :include, InstanceMethods
yield
modules.each do |mod|
klass.devise_modules.each do |mod|
klass.send(mod) if klass.respond_to?(mod)
end
end
@@ -80,4 +80,4 @@ module Devise
end
end
DataMapper::Model.send(:include, Devise::Models)
DataMapper::Model.send(:include, Devise::Models)

View File

@@ -11,16 +11,25 @@ module Devise
end
end
def self.included_modules_hook(klass, modules)
def self.included_modules_hook(klass)
klass.send :extend, self
klass.send :include, InstanceMethods
yield
modules.each do |mod|
klass.devise_modules.each do |mod|
klass.send(mod) if klass.respond_to?(mod)
end
end
def find(*args)
case args.first
when :first, :all
send(args.shift, *args)
else
super
end
end
include Devise::Schema
# Tell how to apply schema methods. This automatically converts DateTime
@@ -34,5 +43,10 @@ module Devise
end
end
MongoMapper::Document::ClassMethods.send(:include, Devise::Models)
MongoMapper::EmbeddedDocument::ClassMethods.send(:include, Devise::Models)
if MongoMapper::Version >= "0.8.0"
MongoMapper::Plugins::Document::ClassMethods.send(:include, Devise::Models)
MongoMapper::Plugins::EmbeddedDocument::ClassMethods.send(:include, Devise::Models)
else
MongoMapper::Document::ClassMethods.send(:include, Devise::Models)
MongoMapper::EmbeddedDocument::ClassMethods.send(:include, Devise::Models)
end

View File

@@ -88,8 +88,8 @@ module ActionController::Routing
route_options = mapping.route_options.merge(:path_prefix => mapping.raw_path, :name_prefix => "#{mapping.name}_")
with_options(route_options) do |routes|
mapping.for.each do |strategy|
send(strategy, routes, mapping) if self.respond_to?(strategy, true)
mapping.for.each do |mod|
send(mod, routes, mapping) if self.respond_to?(mod, true)
end
end
end
@@ -97,7 +97,7 @@ module ActionController::Routing
protected
def authenticatable(routes, mapping)
def database_authenticatable(routes, mapping)
routes.with_options(:controller => 'sessions', :name_prefix => nil) do |session|
session.send(:"new_#{mapping.name}_session", mapping.path_names[:sign_in], :action => 'new', :conditions => { :method => :get })
session.send(:"#{mapping.name}_session", mapping.path_names[:sign_in], :action => 'create', :conditions => { :method => :post })
@@ -105,10 +105,6 @@ module ActionController::Routing
end
end
def recoverable(routes, mapping)
routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
end
def confirmable(routes, mapping)
routes.resource :confirmation, :only => [:new, :create, :show], :as => mapping.path_names[:confirmation]
end
@@ -117,6 +113,13 @@ module ActionController::Routing
routes.resource :unlock, :only => [:new, :create, :show], :as => mapping.path_names[:unlock]
end
def recoverable(routes, mapping)
routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
end
def registerable(routes, mapping)
routes.resource :registration, :only => [:new, :create, :edit, :update, :destroy], :as => mapping.raw_path[1..-1], :path_prefix => nil, :path_names => { :new => mapping.path_names[:sign_up] }
end
end
end
end

View File

@@ -3,42 +3,55 @@ module Devise
# and overwrite the apply_schema method.
module Schema
def authenticatable(*args)
ActiveSupport::Deprecation.warn "t.authenticatable in migrations is deprecated. Please use t.database_authenticatable instead.", caller
database_authenticatable(*args)
end
# Creates email, encrypted_password and password_salt.
#
# == Options
# * :null - When true, allow columns to be null.
# * :encryptor - The encryptor going to be used, necessary for setting the proper encrypter password length.
def authenticatable(options={})
null = options[:null] || false
encryptor = options[:encryptor] || (respond_to?(:encryptor) ? self.encryptor : :sha1)
def database_authenticatable(options={})
null = options[:null] || false
default = options[:default] || ""
apply_schema :email, String, :null => null, :limit => 100
apply_schema :encrypted_password, String, :null => null, :limit => Devise::ENCRYPTORS_LENGTH[encryptor]
apply_schema :password_salt, String, :null => null, :limit => 20
if options.delete(:encryptor)
ActiveSupport::Deprecation.warn ":encryptor as option is deprecated, simply remove it."
end
apply_schema :email, String, :null => null, :default => default
apply_schema :encrypted_password, String, :null => null, :default => default, :limit => 128
apply_schema :password_salt, String, :null => null, :default => default
end
# Creates authentication_token.
def token_authenticatable
apply_schema :authentication_token, String
end
# Creates confirmation_token, confirmed_at and confirmation_sent_at.
def confirmable
apply_schema :confirmation_token, String, :limit => 20
apply_schema :confirmation_token, String
apply_schema :confirmed_at, DateTime
apply_schema :confirmation_sent_at, DateTime
end
# Creates reset_password_token.
def recoverable
apply_schema :reset_password_token, String, :limit => 20
apply_schema :reset_password_token, String
end
# Creates remember_token and remember_created_at.
def rememberable
apply_schema :remember_token, String, :limit => 20
apply_schema :remember_token, String
apply_schema :remember_created_at, DateTime
end
# Creates sign_in_count, current_sign_in_at, last_sign_in_at,
# current_sign_in_ip, last_sign_in_ip.
def trackable
apply_schema :sign_in_count, Integer
apply_schema :sign_in_count, Integer, :default => 0
apply_schema :current_sign_in_at, DateTime
apply_schema :last_sign_in_at, DateTime
apply_schema :current_sign_in_ip, String
@@ -48,7 +61,7 @@ module Devise
# Creates failed_attempts, unlock_token and locked_at
def lockable
apply_schema :failed_attempts, Integer, :default => 0
apply_schema :unlock_token, String, :limit => 20
apply_schema :unlock_token, String
apply_schema :locked_at, DateTime
end

View File

@@ -2,22 +2,14 @@ module Devise
module Strategies
# Base strategy for Devise. Responsible for verifying correct scope and mapping.
class Base < ::Warden::Strategies::Base
# Validate strategy. By default will raise an error if no scope or an
# invalid mapping is found.
def valid?
raise "Could not find mapping for #{scope}" unless mapping
mapping.for.include?(klass_type)
end
# Checks if a valid scope was given for devise and find mapping based on
# this scope.
def mapping
Devise.mappings[scope]
end
# Store this class type.
def klass_type
@klass_type ||= self.class.name.split("::").last.underscore.to_sym
@mapping ||= begin
mapping = Devise.mappings[scope]
raise "Could not find mapping for #{scope}" unless mapping
mapping
end
end
end
end

View File

@@ -4,9 +4,9 @@ module Devise
module Strategies
# Default strategy for signing in a user, based on his email and password.
# Redirects to sign_in page if it's not authenticated
class Authenticatable < Base
class DatabaseAuthenticatable < Base
def valid?
super && params[scope] && params[scope][:password].present?
valid_controller? && valid_params? && mapping.to.respond_to?(:authenticate)
end
# Authenticate a user based on email and password params, returning to warden
@@ -16,11 +16,21 @@ module Devise
if resource = mapping.to.authenticate(params[scope])
success!(resource)
else
fail!(:invalid)
fail(:invalid)
end
end
protected
def valid_controller?
params[:controller] =~ /sessions$/
end
def valid_params?
params[scope] && params[scope][:password].present?
end
end
end
end
Warden::Strategies.add(:authenticatable, Devise::Strategies::Authenticatable)
Warden::Strategies.add(:database_authenticatable, Devise::Strategies::DatabaseAuthenticatable)

View File

@@ -0,0 +1,59 @@
require 'devise/strategies/base'
module Devise
module Strategies
# Sign in an user using HTTP authentication.
class HttpAuthenticatable < Base
def valid?
http_authentication? && mapping.to.respond_to?(:authenticate_with_http)
end
def authenticate!
username, password = username_and_password
if resource = mapping.to.authenticate_with_http(username, password)
success!(resource)
else
custom!([401, custom_headers, [response_body]])
end
end
private
def username_and_password
decode_credentials(request).split(/:/, 2)
end
def response_body
body = "HTTP Basic: Access denied."
method = :"to_#{request_format.to_sym}"
{}.respond_to?(method) ? { :error => body }.send(method) : body
end
def http_authentication
request.env['HTTP_AUTHORIZATION'] ||
request.env['X-HTTP_AUTHORIZATION'] ||
request.env['X_HTTP_AUTHORIZATION'] ||
request.env['REDIRECT_X_HTTP_AUTHORIZATION']
end
alias :http_authentication? :http_authentication
def decode_credentials(request)
ActiveSupport::Base64.decode64(http_authentication.split(' ', 2).last || '')
end
def custom_headers
{
"Content-Type" => request_format.to_s,
"WWW-Authenticate" => %(Basic realm="#{Devise.http_authentication_realm.gsub(/"/, "")}")
}
end
def request_format
@request_format ||= Mime::Type.lookup_by_extension(request.template_format.to_s)
end
end
end
end
Warden::Strategies.add(:http_authenticatable, Devise::Strategies::HttpAuthenticatable)

View File

@@ -10,7 +10,7 @@ module Devise
# A valid strategy for rememberable needs a remember token in the cookies.
def valid?
super && remember_me_cookie.present?
remember_me_cookie.present? && mapping.to.respond_to?(:serialize_from_cookie)
end
# To authenticate a user we deserialize the cookie and attempt finding

View File

@@ -0,0 +1,37 @@
require 'devise/strategies/base'
module Devise
module Strategies
# Strategy for signing in a user, based on a authenticatable token.
# Redirects to sign_in page if it's not authenticated.
class TokenAuthenticatable < Base
def valid?
mapping.to.respond_to?(:authenticate_with_token) && authentication_token(scope).present?
end
# Authenticate a user based on authenticatable token params, returning to warden
# success and the authenticated user if everything is okay. Otherwise redirect
# to sign in page.
def authenticate!
if resource = mapping.to.authenticate_with_token(params[scope] || params)
success!(resource)
else
fail!(:invalid_token)
end
end
private
# Detect authentication token in params: scoped or not.
def authentication_token(scope)
if params[scope]
params[scope][mapping.to.token_authentication_key]
else
params[mapping.to.token_authentication_key]
end
end
end
end
end
Warden::Strategies.add(:token_authenticatable, Devise::Strategies::TokenAuthenticatable)

View File

@@ -24,6 +24,10 @@ module Devise
catch_with_redirect { super }
end
def user(*args)
catch_with_redirect { super }
end
def catch_with_redirect(&block)
result = catch(:warden, &block)

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "0.9.0".freeze
VERSION = "1.0.8".freeze
end

View File

@@ -36,6 +36,13 @@ class ControllerAuthenticableTest < ActionController::TestCase
@controller.signed_in?(:my_scope)
end
test 'proxy anybody_signed_in? to signed_in?' do
Devise.mappings.keys.each { |scope| # :user, :admin, :manager
@controller.expects(:signed_in?).with(scope)
}
@controller.anybody_signed_in?
end
test 'proxy current_admin to authenticate with admin scope' do
@mock_warden.expects(:authenticate).with(:scope => :admin)
@controller.current_admin

View File

@@ -25,7 +25,7 @@ class DeviseTest < ActiveSupport::TestCase
Devise.configure_warden(config)
assert_equal Devise::FailureApp, config.failure_app
assert_equal [:rememberable, :authenticatable], config.default_strategies
assert_equal [:rememberable, :http_authenticatable, :token_authenticatable, :database_authenticatable], config.default_strategies
assert_equal :user, config.default_scope
assert config.silence_missing_strategies?
end
@@ -63,6 +63,11 @@ class DeviseTest < ActiveSupport::TestCase
Devise::ALL.delete(:kivi)
Devise::CONTROLLERS.delete(:fruits)
assert_nothing_raised(Exception) { Devise.add_module(:carrot, :route => :vegetable) }
assert_equal 1, Devise::ROUTES.select { |v| v == :vegetable }.size
Devise::ALL.delete(:carrot)
Devise::ROUTES.delete(:vegetable)
assert_nothing_raised(Exception) { Devise.add_module(:authenticatable_again, :model => 'devise/model/authenticatable') }
assert defined?(Devise::Models::AuthenticatableAgain)
end

View File

@@ -1,8 +1,7 @@
require 'test/test_helper'
class AuthenticationTest < ActionController::IntegrationTest
test 'home should be accessible without signed in' do
class AuthenticationSanityTest < ActionController::IntegrationTest
test 'home should be accessible without sign in' do
visit '/'
assert_response :success
assert_template 'home/index'
@@ -76,43 +75,6 @@ class AuthenticationTest < ActionController::IntegrationTest
assert_contain 'Welcome Admin'
end
test 'sign in as user should not authenticate if not using proper authentication keys' do
swap Devise, :authentication_keys => [:username] do
sign_in_as_user
assert_not warden.authenticated?(:user)
end
end
test 'admin signing in with invalid email should return to sign in form with error message' do
sign_in_as_admin do
fill_in 'email', :with => 'wrongemail@test.com'
end
assert_contain 'Invalid email or password'
assert_not warden.authenticated?(:admin)
end
test 'admin signing in with invalid pasword should return to sign in form with error message' do
sign_in_as_admin do
fill_in 'password', :with => 'abcdef'
end
assert_contain 'Invalid email or password'
assert_not warden.authenticated?(:admin)
end
test 'error message is configurable by resource name' do
store_translations :en, :devise => {
:sessions => { :admin => { :invalid => "Invalid credentials" } }
} do
sign_in_as_admin do
fill_in 'password', :with => 'abcdef'
end
assert_contain 'Invalid credentials'
end
end
test 'authenticated admin should not be able to sign as admin again' do
sign_in_as_admin
get new_admin_session_path
@@ -143,6 +105,45 @@ class AuthenticationTest < ActionController::IntegrationTest
get root_path
assert_not_contain 'Signed out successfully'
end
end
class AuthenticationTest < ActionController::IntegrationTest
test 'sign in should not authenticate if not using proper authentication keys' do
swap Devise, :authentication_keys => [:username] do
sign_in_as_user
assert_not warden.authenticated?(:user)
end
end
test 'sign in with invalid email should return to sign in form with error message' do
sign_in_as_admin do
fill_in 'email', :with => 'wrongemail@test.com'
end
assert_contain 'Invalid email or password'
assert_not warden.authenticated?(:admin)
end
test 'sign in with invalid pasword should return to sign in form with error message' do
sign_in_as_admin do
fill_in 'password', :with => 'abcdef'
end
assert_contain 'Invalid email or password'
assert_not warden.authenticated?(:admin)
end
test 'error message is configurable by resource name' do
store_translations :en, :devise => {
:sessions => { :admin => { :invalid => "Invalid credentials" } }
} do
sign_in_as_admin do
fill_in 'password', :with => 'abcdef'
end
assert_contain 'Invalid credentials'
end
end
test 'redirect from warden shows sign in or sign up message' do
get admins_path
@@ -154,43 +155,61 @@ class AuthenticationTest < ActionController::IntegrationTest
assert_contain 'You need to sign in or sign up before continuing.'
end
test 'return to default url if no other was requested' do
test 'redirect to default url if no other was configured' do
sign_in_as_user
assert_template 'home/index'
assert_nil session[:return_to]
assert_nil session[:"user.return_to"]
end
test 'return to given url after sign in' do
test 'redirect to requested url after sign in' do
get users_path
assert_redirected_to new_user_session_path(:unauthenticated => true)
assert_equal users_path, session[:"user.return_to"]
follow_redirect!
follow_redirect!
sign_in_as_user :visit => false
assert_template 'users/index'
assert_nil session[:"user.return_to"]
end
test 'return to configured home path after sign in' do
test 'redirect to last requested url overwriting the stored return_to option' do
get expire_user_path(create_user)
assert_redirected_to new_user_session_path(:unauthenticated => true)
assert_equal expire_user_path(create_user), session[:"user.return_to"]
get users_path
assert_redirected_to new_user_session_path(:unauthenticated => true)
assert_equal users_path, session[:"user.return_to"]
follow_redirect!
sign_in_as_user :visit => false
assert_template 'users/index'
assert_nil session[:"user.return_to"]
end
test 'redirect to configured home path for a given scope after sign in' do
sign_in_as_admin
assert_equal "/admin_area/home", @request.path
end
test 'destroyed account is signed out' do
sign_in_as_user
visit 'users/index'
User.destroy_all
visit 'users/index'
assert_redirected_to '/users/sign_in?unauthenticated=true'
end
test 'allows session to be set by a given scope' do
sign_in_as_user
visit 'users/index'
assert_equal "Cart", @controller.user_session[:cart]
end
test 'destroyed account is logged out' do
sign_in_as_user
visit 'users/index'
User.destroy_all
visit 'users/index'
assert_redirected_to '/users/sign_in?unauthenticated=true'
end
test 'renders the scoped view if turned on and view is available' do
swap Devise, :scoped_views => true do
assert_raise Webrat::NotFoundError do
@@ -200,6 +219,20 @@ class AuthenticationTest < ActionController::IntegrationTest
end
end
test 'renders the scoped view if turned on in an specific controller' do
begin
SessionsController.scoped_views = true
assert_raise Webrat::NotFoundError do
sign_in_as_user
end
assert_match /Special user view/, response.body
assert !PasswordsController.scoped_views
ensure
SessionsController.send :remove_instance_variable, :@scoped_views
end
end
test 'does not render the scoped view if turned off' do
swap Devise, :scoped_views => false do
assert_nothing_raised do

View File

@@ -0,0 +1,52 @@
require 'test/test_helper'
class HttpAuthenticationTest < ActionController::IntegrationTest
test 'sign in should authenticate with http' do
sign_in_as_new_user_with_http
assert_response :success
assert_template 'users/index'
assert_contain 'Welcome'
assert warden.authenticated?(:user)
end
test 'returns a custom response with www-authenticate header on failures' do
sign_in_as_new_user_with_http("unknown")
assert_equal 401, status
assert_equal 'Basic realm="Application"', headers["WWW-Authenticate"]
end
test 'uses the request format as response content type' do
sign_in_as_new_user_with_http("unknown", "123456", :xml)
assert_equal 401, status
assert_equal "application/xml", headers["Content-Type"]
# Cannot assert this due to a bug between integration tests and rack on 2.3
# assert response.body.include?("<error>HTTP Basic: Access denied.</error>")
end
test 'returns a custom response with www-authenticate and chosen realm' do
swap Devise, :http_authentication_realm => "MyApp" do
sign_in_as_new_user_with_http("unknown")
assert_equal 401, status
assert_equal 'Basic realm="MyApp"', headers["WWW-Authenticate"]
end
end
test 'sign in should authenticate with http even with specific authentication keys' do
swap Devise, :authentication_keys => [:username] do
sign_in_as_new_user_with_http "usertest"
assert_response :success
assert_template 'users/index'
assert_contain 'Welcome'
assert warden.authenticated?(:user)
end
end
private
def sign_in_as_new_user_with_http(username="user@test.com", password="123456", format=:html)
user = create_user
get users_path(:format => format), {}, :authorization => "Basic #{ActiveSupport::Base64.encode64("#{username}:#{password}")}"
user
end
end

View File

@@ -36,6 +36,16 @@ class LockTest < ActionController::IntegrationTest
assert_equal 0, ActionMailer::Base.deliveries.size
end
test 'unlocked pages should not be available if email strategy is disabled' do
visit new_user_unlock_path
assert_response :success
swap Devise, :unlock_strategy => :time do
visit new_user_unlock_path
assert_response :not_found
end
end
test 'user with invalid unlock token should not be able to unlock an account' do
visit_user_unlock_with_token('invalid_token')
@@ -47,20 +57,19 @@ class LockTest < ActionController::IntegrationTest
test "locked user should be able to unlock account" do
user = create_user(:locked => true)
assert user.locked?
assert user.access_locked?
visit_user_unlock_with_token(user.unlock_token)
assert_template 'home/index'
assert_contain 'Your account was successfully unlocked.'
assert_not user.reload.locked?
assert_not user.reload.access_locked?
end
test "sign in user automatically after unlocking it's account" do
user = create_user(:locked => true)
visit_user_unlock_with_token(user.unlock_token)
assert warden.authenticated?(:user)
end
@@ -71,6 +80,16 @@ class LockTest < ActionController::IntegrationTest
assert_not warden.authenticated?(:user)
end
test "user should not send a new e-mail if already locked" do
user = create_user(:locked => true)
user.update_attribute(:failed_attempts, User.maximum_attempts + 1)
ActionMailer::Base.deliveries.clear
sign_in_as_user(:password => "invalid")
assert_contain 'Invalid email or password.'
assert ActionMailer::Base.deliveries.empty?
end
test 'error message is configurable by resource name' do
store_translations :en, :devise => {
:sessions => { :admin => { :locked => "You are locked!" } }

View File

@@ -0,0 +1,47 @@
require "test/test_helper"
require "rack/test"
class RackMiddlewareTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
ActionController::Dispatcher.new
end
def warden
last_request.env['warden']
end
def with_custom_strategy
get '/'
Warden::Strategies.add(:custom_test) do
def valid?
true
end
def authenticate!
custom! [599, {
"X-Custom-Response" => "Custom response test",
"Content-type" => "text/plain"
}, "Custom response test"]
end
end
#ActionController::Dispatcher.middleware.use CustomStrategyInterceptor
default_strategies = warden.manager.config.default_strategies
warden.manager.config.default_strategies :custom_test
yield
warden.manager.config.default_strategies default_strategies
end
def test_custom_strategy_response
with_custom_strategy do
post('/users/sign_in')
assert_equal 599, last_response.status
assert_equal "Custom response test", last_response.body
assert_equal "Custom response test", last_response.headers["X-Custom-Response"]
end
end
end

View File

@@ -128,4 +128,14 @@ 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)
request_forgot_password
reset_password :reset_password_token => user.reload.reset_password_token
assert_redirected_to new_user_session_path(:unconfirmed => true)
assert !warden.authenticated?(:user)
end
end

View File

@@ -0,0 +1,144 @@
require 'test/test_helper'
class RegistrationTest < ActionController::IntegrationTest
test 'a guest admin should be able to sign in successfully' do
visit new_admin_session_path
click_link 'Sign up'
assert_template 'registrations/new'
fill_in 'email', :with => 'new_user@test.com'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user123'
click_button 'Sign up'
assert_contain 'You have signed up successfully.'
assert warden.authenticated?(:admin)
admin = Admin.last
assert_equal admin.email, 'new_user@test.com'
end
test 'a guest user should be able to sign up successfully and be blocked by confirmation' do
visit new_user_registration_path
fill_in 'email', :with => 'new_user@test.com'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user123'
click_button 'Sign up'
assert_equal "You have signed up successfully. If enabled, a confirmation was sent to your e-mail.", @controller.send(:flash)[:notice]
# For some reason flash is not being set correctly, so instead of getting the
# "signed_up" message we get the unconfirmed one. Seems to be an issue with
# the internal redirect by the hook and the tests.
# follow_redirect!
# assert_contain 'You have signed up successfully.'
# assert_not_contain 'confirm your account'
follow_redirect!
assert_contain 'Sign in'
assert_not warden.authenticated?(:user)
user = User.last
assert_equal user.email, 'new_user@test.com'
assert_not user.confirmed?
end
test 'a guest user cannot sign up with invalid information' do
visit 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_template 'registrations/new'
assert_have_selector '#errorExplanation'
assert_contain "Email is invalid"
assert_contain "Password doesn't match confirmation"
assert_nil User.first
assert_not warden.authenticated?(:user)
end
test 'a guest should not sign up with email/password that already exists' do
user = create_user
visit new_user_registration_path
fill_in 'email', :with => 'user@test.com'
fill_in 'password', :with => '123456'
fill_in 'password confirmation', :with => '123456'
click_button 'Sign up'
assert_template 'registrations/new'
assert_contain 'Email has already been taken'
assert_not warden.authenticated?(:user)
end
test 'a guest should not be able to change account' do
visit edit_user_registration_path
follow_redirect!
assert_template 'sessions/new'
end
test 'a signed in user should not be able to access sign up' do
sign_in_as_user
visit new_user_registration_path
assert_template 'home/index'
end
test 'a signed in user should be able to edit his account' do
sign_in_as_user
visit edit_user_registration_path
fill_in 'email', :with => 'user.new@email.com'
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_template 'home/index'
assert_contain 'You updated your account successfully.'
assert_equal "user.new@email.com", User.first.email
end
test 'a signed in user should be able to edit his password' do
sign_in_as_user
visit edit_user_registration_path
fill_in 'password', :with => 'pas123'
fill_in 'password confirmation', :with => 'pas123'
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_template 'home/index'
assert_contain 'You updated your account successfully.'
assert User.first.valid_password?('pas123')
end
test 'a signed in user should not be able to edit his password with invalid confirmation' do
sign_in_as_user
get edit_user_registration_path
fill_in 'password', :with => 'pas123'
fill_in 'password confirmation', :with => ''
fill_in 'current password', :with => '123456'
click_button 'Update'
assert_contain "Password doesn't match confirmation"
assert_not User.first.valid_password?('pas123')
end
test 'a signed in user should be able to cancel his account' do
sign_in_as_user
visit edit_user_registration_path
click_link "Cancel my account"
assert_contain "Bye! Your account was successfully cancelled. We hope to see you again soon."
assert User.all.empty?
end
end

View File

@@ -28,6 +28,14 @@ class RememberMeTest < ActionController::IntegrationTest
assert warden.user(:user) == user
end
test 'does not remember other scopes' do
user = create_user_and_remember
get root_path
assert_response :success
assert warden.authenticated?(:user)
assert_not warden.authenticated?(:admin)
end
test 'do not remember with invalid token' do
user = create_user_and_remember('add')
get users_path

View File

@@ -0,0 +1,55 @@
require 'test/test_helper'
class TokenAuthenticationTest < ActionController::IntegrationTest
test 'sign in should authenticate with valid authentication token and proper authentication token key' do
swap Devise, :token_authentication_key => :secret_token do
sign_in_as_new_user_with_token(:auth_token_key => :secret_token)
assert_response :success
assert_template 'users/index'
assert_contain 'Welcome'
assert warden.authenticated?(:user)
end
end
test 'signing in with valid authentication token - but improper authentication token key - return to sign in form with error message' do
swap Devise, :token_authentication_key => :donald_duck_token do
sign_in_as_new_user_with_token(:auth_token_key => :secret_token)
assert_redirected_to new_user_session_path(:unauthenticated => true)
follow_redirect!
assert_contain 'You need to sign in or sign up before continuing'
assert_contain 'Sign in'
assert_not warden.authenticated?(:user)
end
end
test 'signing in with invalid authentication token should return to sign in form with error message' do
store_translations :en, :devise => {:sessions => {:invalid_token => 'LOL, that was not a single character correct.'}} do
sign_in_as_new_user_with_token(:auth_token => '*** INVALID TOKEN ***')
assert_redirected_to new_user_session_path(:invalid_token => true)
follow_redirect!
assert_response :success
assert_contain 'LOL, that was not a single character correct.'
assert_contain 'Sign in'
assert_not warden.authenticated?(:user)
end
end
private
def sign_in_as_new_user_with_token(options = {})
options[:auth_token_key] ||= Devise.token_authentication_key
options[:auth_token] ||= VALID_AUTHENTICATION_TOKEN
user = create_user(options)
user.authentication_token = VALID_AUTHENTICATION_TOKEN
user.save
visit users_path(options[:auth_token_key].to_sym => options[:auth_token])
user
end
end

View File

@@ -39,7 +39,7 @@ class TrackableHooksTest < ActionController::IntegrationTest
test "increase sign in count" do
user = create_user
assert_nil user.sign_in_count
assert_equal 0, user.sign_in_count
sign_in_as_user
user.reload

View File

@@ -63,6 +63,21 @@ class ConfirmationInstructionsTest < ActionMailer::TestCase
end
end
test 'content type should be set to plain when manually configured' do
swap Devise, :mailer_content_type => "text/plain" do
assert_equal "text/plain", mail.content_type
end
end
test 'renders a scoped if scoped_views is set in the mailer class' do
begin
DeviseMailer.scoped_views = true
assert_equal user.email, mail.body
ensure
DeviseMailer.send :remove_instance_variable, :@scoped_views
end
end
test 'mailer sender accepts a proc' do
swap Devise, :mailer_sender => lambda { "another@example.com" } do
assert_equal ['another@example.com'], mail.from

View File

@@ -10,7 +10,7 @@ class UnlockInstructionsTest < ActionMailer::TestCase
def user
@user ||= begin
user = create_user
user.lock!
user.lock_access!
user
end
end

View File

@@ -39,22 +39,17 @@ class MappingTest < ActiveSupport::TestCase
assert_equal Devise.mappings[:admin], Devise::Mapping.find_by_path("/admin_area/session")
end
test 'find mapping by class' do
assert_nil Devise::Mapping.find_by_class(String)
assert_equal Devise.mappings[:user], Devise::Mapping.find_by_class(User)
end
test 'find mapping by class works with single table inheritance' do
klass = Class.new(User)
assert_equal Devise.mappings[:user], Devise::Mapping.find_by_class(klass)
end
test 'find scope for a given object' do
assert_equal :user, Devise::Mapping.find_scope!(User)
assert_equal :user, Devise::Mapping.find_scope!(:user)
assert_equal :user, Devise::Mapping.find_scope!(User.new)
end
test 'find scope works with single table inheritance' do
assert_equal :user, Devise::Mapping.find_scope!(Class.new(User))
assert_equal :user, Devise::Mapping.find_scope!(Class.new(User).new)
end
test 'find scope raises an error if cannot be found' do
assert_raise RuntimeError do
Devise::Mapping.find_scope!(String)
@@ -63,18 +58,22 @@ class MappingTest < ActiveSupport::TestCase
test 'return default path names' do
mapping = Devise.mappings[:user]
assert_equal 'sign_in', mapping.path_names[:sign_in]
assert_equal 'sign_out', mapping.path_names[:sign_out]
assert_equal 'password', mapping.path_names[:password]
assert_equal 'sign_in', mapping.path_names[:sign_in]
assert_equal 'sign_out', mapping.path_names[:sign_out]
assert_equal 'password', mapping.path_names[:password]
assert_equal 'confirmation', mapping.path_names[:confirmation]
assert_equal 'sign_up', mapping.path_names[:sign_up]
assert_equal 'unlock', mapping.path_names[:unlock]
end
test 'allow custom path names to be given' do
mapping = Devise.mappings[:manager]
assert_equal 'login', mapping.path_names[:sign_in]
assert_equal 'logout', mapping.path_names[:sign_out]
assert_equal 'secret', mapping.path_names[:password]
assert_equal 'login', mapping.path_names[:sign_in]
assert_equal 'logout', mapping.path_names[:sign_out]
assert_equal 'secret', mapping.path_names[:password]
assert_equal 'verification', mapping.path_names[:confirmation]
assert_equal 'register', mapping.path_names[:sign_up]
assert_equal 'unblock', mapping.path_names[:unlock]
end
test 'has an empty path as default path prefix' do
@@ -86,7 +85,7 @@ class MappingTest < ActiveSupport::TestCase
mapping = Devise.mappings[:manager]
assert_equal '/:locale/', mapping.path_prefix
end
test 'retrieve as from the proper position' do
assert_equal 1, Devise.mappings[:user].as_position
assert_equal 2, Devise.mappings[:manager].as_position
@@ -96,15 +95,9 @@ class MappingTest < ActiveSupport::TestCase
assert_equal '/users', Devise.mappings[:user].raw_path
assert_equal '/:locale/accounts', Devise.mappings[:manager].raw_path
end
test 'raw path adds in the relative_url_root' do
swap ActionController::Base, :relative_url_root => '/abc' do
assert_equal '/abc/users', Devise.mappings[:user].raw_path
end
end
test 'raw path deals with a nil relative_url_root' do
swap ActionController::Base, :relative_url_root => nil do
test 'raw path ignores the relative_url_root' do
swap ActionController::Base, :relative_url_root => "/abc" do
assert_equal '/users', Devise.mappings[:user].raw_path
end
end
@@ -118,13 +111,19 @@ class MappingTest < ActiveSupport::TestCase
Devise.default_url_options {{ }}
end
end
test 'parsed path deals with non-standard relative_url_roots' do
swap ActionController::Base, :relative_url_root => "/abc" do
test 'parsed path adds in the relative_url_root' do
swap ActionController::Base, :relative_url_root => '/abc' do
assert_equal '/abc/users', Devise.mappings[:user].parsed_path
end
end
test 'parsed path deals with a nil relative_url_root' do
swap ActionController::Base, :relative_url_root => nil do
assert_equal '/users', Devise.mappings[:user].raw_path
end
end
test 'should have default route options' do
assert_equal({}, Devise.mappings[:user].route_options)
end

View File

@@ -119,7 +119,7 @@ class AuthenticatableTest < ActiveSupport::TestCase
test 'should use authentication keys to retrieve users' do
swap Devise, :authentication_keys => [:username] do
user = create_user(:username => "josevalim")
user = create_user
assert_nil User.authenticate(:email => user.email, :password => user.password)
assert_not_nil User.authenticate(:username => user.username, :password => user.password)
end
@@ -130,29 +130,51 @@ class AuthenticatableTest < ActiveSupport::TestCase
assert_not_nil Admin.authenticate(:email => admin.email, :password => admin.password)
end
test 'should respond to old password' do
assert new_user.respond_to?(:old_password)
test 'should respond to current password' do
assert new_user.respond_to?(:current_password)
end
test 'should update password with valid old password' do
test 'should update password with valid current password' do
user = create_user
assert user.update_with_password(:old_password => '123456',
assert user.update_with_password(:current_password => '123456',
:password => 'pass321', :password_confirmation => 'pass321')
assert user.reload.valid_password?('pass321')
end
test 'should add an error to old password when it is invalid' do
test 'should add an error to current password when it is invalid' do
user = create_user
assert_not user.update_with_password(:old_password => 'other',
assert_not user.update_with_password(:current_password => 'other',
:password => 'pass321', :password_confirmation => 'pass321')
assert user.reload.valid_password?('123456')
assert_match /invalid/, user.errors[:old_password]
assert_match /invalid/, user.errors[:current_password]
end
test 'should add an error to current password when it is blank' do
user = create_user
assert_not user.update_with_password(:password => 'pass321',
:password_confirmation => 'pass321')
assert user.reload.valid_password?('123456')
assert_match /blank/, user.errors[:current_password]
end
test 'should ignore password and its confirmation if they are blank' do
user = create_user
assert user.update_with_password(:current_password => '123456', :email => "new@email.com")
assert_equal "new@email.com", user.email
end
test 'should not update password with invalid confirmation' do
user = create_user
assert_not user.update_with_password(:old_password => '123456',
assert_not user.update_with_password(:current_password => '123456',
:password => 'pass321', :password_confirmation => 'other')
assert user.reload.valid_password?('123456')
end
test 'should clean up password fields on failure' do
user = create_user
assert_not user.update_with_password(:current_password => '123456',
:password => 'pass321', :password_confirmation => 'other')
assert user.password.blank?
assert user.password_confirmation.blank?
end
end

View File

@@ -11,15 +11,6 @@ class ConfirmableTest < ActiveSupport::TestCase
assert_not_nil create_user.confirmation_token
end
test 'should regenerate confirmation token each time' do
user = create_user
3.times do
token = user.confirmation_token
user.resend_confirmation!
assert_not_equal token, user.confirmation_token
end
end
test 'should never generate the same confirmation token for different users' do
confirmation_tokens = []
3.times do
@@ -62,19 +53,19 @@ class ConfirmableTest < ActiveSupport::TestCase
test 'should find and confirm an user automatically' do
user = create_user
confirmed_user = User.confirm!(:confirmation_token => user.confirmation_token)
confirmed_user = User.confirm_by_token(user.confirmation_token)
assert_equal confirmed_user, user
assert user.reload.confirmed?
end
test 'should return a new record with errors when a invalid token is given' do
confirmed_user = User.confirm!(:confirmation_token => 'invalid_confirmation_token')
confirmed_user = User.confirm_by_token('invalid_confirmation_token')
assert confirmed_user.new_record?
assert_match /invalid/, confirmed_user.errors[:confirmation_token]
end
test 'should return a new record with errors when a blank token is given' do
confirmed_user = User.confirm!(:confirmation_token => '')
confirmed_user = User.confirm_by_token('')
assert confirmed_user.new_record?
assert_match /blank/, confirmed_user.errors[:confirmation_token]
end
@@ -83,7 +74,7 @@ class ConfirmableTest < ActiveSupport::TestCase
user = create_user
user.confirmed_at = Time.now
user.save
confirmed_user = User.confirm!(:confirmation_token => user.confirmation_token)
confirmed_user = User.confirm_by_token(user.confirmation_token)
assert confirmed_user.confirmed?
assert confirmed_user.errors[:email]
end
@@ -137,19 +128,20 @@ class ConfirmableTest < ActiveSupport::TestCase
assert_equal 'not found', confirmation_user.errors[:email]
end
test 'should generate a confirmation token before send the confirmation instructions email' do
user = create_user
token = user.confirmation_token
confirmation_user = User.send_confirmation_instructions(:email => user.email)
assert_not_equal token, user.reload.confirmation_token
end
test 'should send email instructions for the user confirm it\'s email' do
user = create_user
assert_email_sent do
User.send_confirmation_instructions(:email => user.email)
end
end
test 'should always have confirmation token when email is sent' do
user = new_user
user.instance_eval { def confirmation_required?; false end }
user.save
user.send_confirmation_instructions
assert_not_nil user.confirmation_token
end
test 'should not resend email instructions if the user change his email' do
user = create_user
@@ -173,7 +165,7 @@ class ConfirmableTest < ActiveSupport::TestCase
test 'should not be able to send instructions if the user is already confirmed' do
user = create_user
user.confirm!
assert_not user.resend_confirmation!
assert_not user.resend_confirmation_token
assert user.confirmed?
assert_equal 'already confirmed', user.errors[:email]
end
@@ -216,7 +208,7 @@ class ConfirmableTest < ActiveSupport::TestCase
Devise.confirm_within = 0.days
user = create_user
user.confirmation_sent_at = Date.today
assert_not user.active?
assert_not user.reload.active?
end
test 'should not be active without confirmation' do
@@ -225,4 +217,12 @@ class ConfirmableTest < ActiveSupport::TestCase
user.save
assert_not user.reload.active?
end
test 'should be active without confirmation when confirmation is not required' do
user = create_user
user.instance_eval { def confirmation_required?; false end }
user.confirmation_sent_at = nil
user.save
assert user.reload.active?
end
end

View File

@@ -1,33 +1,33 @@
require 'test/test_helper'
class LockableTest < ActiveSupport::TestCase
def setup
setup_mailer
end
test "should increment failed attempts on unsuccessful authentication" do
user = create_user
assert_equal 0, user.failed_attempts
authenticated_user = User.authenticate(:email => user.email, :password => "anotherpassword")
assert_equal 1, user.reload.failed_attempts
end
test "should lock account base on maximum_attempts" do
user = create_user
attempts = Devise.maximum_attempts + 1
attempts.times { authenticated_user = User.authenticate(:email => user.email, :password => "anotherpassword") }
assert user.reload.locked?
assert user.reload.access_locked?
end
test "should respect maximum attempts configuration" do
user = create_user
swap Devise, :maximum_attempts => 2 do
3.times { authenticated_user = User.authenticate(:email => user.email, :password => "anotherpassword") }
assert user.reload.locked?
assert user.reload.access_locked?
end
end
test "should clear failed_attempts on successfull sign in" do
user = create_user
User.authenticate(:email => user.email, :password => "anotherpassword")
@@ -36,40 +36,50 @@ class LockableTest < ActiveSupport::TestCase
assert_equal 0, user.reload.failed_attempts
end
test "should verify wheter a user is locked or not" do
test "should verify whether a user is locked or not" do
user = create_user
assert_not user.locked?
user.lock!
assert user.locked?
assert_not user.access_locked?
user.lock_access!
assert user.access_locked?
end
test "active? should be the opposite of locked?" do
user = create_user
user.confirm!
assert user.active?
user.lock!
user.lock_access!
assert_not user.active?
end
test "should unlock an user by cleaning locked_at, falied_attempts and unlock_token" do
user = create_user
user.lock!
user.lock_access!
assert_not_nil user.reload.locked_at
assert_not_nil user.reload.unlock_token
user.unlock!
user.unlock_access!
assert_nil user.reload.locked_at
assert_nil user.reload.unlock_token
assert 0, user.reload.failed_attempts
end
test 'should not unlcok an unlocked user' do
test "should not lock a locked account" do
user = create_user
assert_not user.unlock!
user.lock_access!
assert_no_difference "ActionMailer::Base.deliveries.size" do
user.lock_access!
end
end
test 'should not unlock an unlocked user' do
user = create_user
assert_not user.unlock_access!
assert_match /not locked/, user.errors[:email]
end
test "new user should not be locked and should have zero failed_attempts" do
assert_not new_user.locked?
assert_not new_user.access_locked?
assert_equal 0, create_user.failed_attempts
end
@@ -77,10 +87,10 @@ class LockableTest < ActiveSupport::TestCase
swap Devise, :unlock_in => 3.hours do
user = new_user
user.locked_at = 2.hours.ago
assert user.locked?
assert user.access_locked?
Devise.unlock_in = 1.hour
assert_not user.locked?
assert_not user.access_locked?
end
end
@@ -88,32 +98,22 @@ class LockableTest < ActiveSupport::TestCase
swap Devise, :unlock_strategy => :email do
user = new_user
user.locked_at = 2.hours.ago
assert user.locked?
assert user.access_locked?
end
end
test "should set unlock_token when locking" do
user = create_user
assert_nil user.unlock_token
user.lock!
user.lock_access!
assert_not_nil user.unlock_token
end
test 'should not regenerate unlock token if it already exists' do
user = create_user
user.lock!
3.times do
token = user.unlock_token
user.resend_unlock!
assert_equal token, user.unlock_token
end
end
test "should never generate the same unlock token for different users" do
unlock_tokens = []
3.times do
user = create_user
user.lock!
user.lock_access!
token = user.unlock_token
assert !unlock_tokens.include?(token)
unlock_tokens << token
@@ -123,7 +123,7 @@ class LockableTest < ActiveSupport::TestCase
test "should not generate unlock_token when :email is not an unlock strategy" do
swap Devise, :unlock_strategy => :time do
user = create_user
user.lock!
user.lock_access!
assert_nil user.unlock_token
end
end
@@ -132,7 +132,7 @@ class LockableTest < ActiveSupport::TestCase
swap Devise, :unlock_strategy => :email do
user = create_user
assert_email_sent do
user.lock!
user.lock_access!
end
end
end
@@ -141,42 +141,42 @@ class LockableTest < ActiveSupport::TestCase
swap Devise, :unlock_strategy => :time do
user = create_user
assert_email_not_sent do
user.lock!
user.lock_access!
end
end
end
test 'should find and unlock an user automatically' do
user = create_user
user.lock!
locked_user = User.unlock!(:unlock_token => user.unlock_token)
user.lock_access!
locked_user = User.unlock_access_by_token(user.unlock_token)
assert_equal locked_user, user
assert_not user.reload.locked?
assert_not user.reload.access_locked?
end
test 'should return a new record with errors when a invalid token is given' do
locked_user = User.unlock!(:unlock_token => 'invalid_token')
locked_user = User.unlock_access_by_token('invalid_token')
assert locked_user.new_record?
assert_match /invalid/, locked_user.errors[:unlock_token]
end
test 'should return a new record with errors when a blank token is given' do
locked_user = User.unlock!(:unlock_token => '')
locked_user = User.unlock_access_by_token('')
assert locked_user.new_record?
assert_match /blank/, locked_user.errors[:unlock_token]
end
test 'should authenticate a unlocked user' do
user = create_user
user.lock!
user.unlock!
user.lock_access!
user.unlock_access!
authenticated_user = User.authenticate(:email => user.email, :password => user.password)
assert_equal authenticated_user, user
end
test 'should find a user to send unlock instructions' do
user = create_user
user.lock!
user.lock_access!
unlock_user = User.send_unlock_instructions(:email => user.email)
assert_equal unlock_user, user
end
@@ -194,9 +194,9 @@ class LockableTest < ActiveSupport::TestCase
test 'should not be able to send instructions if the user is not locked' do
user = create_user
assert_not user.resend_unlock!
assert_not user.locked?
assert_not user.resend_unlock_token
assert_not user.access_locked?
assert_equal 'not locked', user.errors[:email]
end
end
end

View File

@@ -104,18 +104,18 @@ class RecoverableTest < ActiveSupport::TestCase
user = create_user
user.send :generate_reset_password_token!
reset_password_user = User.reset_password!(:reset_password_token => user.reset_password_token)
reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token)
assert_equal reset_password_user, user
end
test 'should a new record with errors if no reset_password_token is found' do
reset_password_user = User.reset_password!(:reset_password_token => 'invalid_token')
reset_password_user = User.reset_password_by_token(:reset_password_token => 'invalid_token')
assert reset_password_user.new_record?
assert_match /invalid/, reset_password_user.errors[:reset_password_token]
end
test 'should a new record with errors if reset_password_token is blank' do
reset_password_user = User.reset_password!(:reset_password_token => '')
reset_password_user = User.reset_password_by_token(:reset_password_token => '')
assert reset_password_user.new_record?
assert_match /blank/, reset_password_user.errors[:reset_password_token]
end
@@ -125,7 +125,7 @@ class RecoverableTest < ActiveSupport::TestCase
old_password = user.password
user.send :generate_reset_password_token!
reset_password_user = User.reset_password!(
reset_password_user = User.reset_password_by_token(
:reset_password_token => user.reset_password_token,
:password => 'new_password',
:password_confirmation => 'new_password'

View File

@@ -0,0 +1,51 @@
require 'test/test_helper'
class TokenAuthenticatableTest < ActiveSupport::TestCase
test 'should generate friendly authentication token on create' do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
assert_present user.authentication_token
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
end
test 'should reset authentication token' do
user = new_user
user.reset_authentication_token
previous_token = user.authentication_token
user.reset_authentication_token
assert_not_equal previous_token, user.authentication_token
end
test 'should ensure authentication token' do
user = new_user
user.ensure_authentication_token
previous_token = user.authentication_token
user.ensure_authentication_token
assert_equal previous_token, user.authentication_token
end
test 'should test for a valid authentication token' do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
assert user.valid_authentication_token?(VALID_AUTHENTICATION_TOKEN)
assert_not user.valid_authentication_token?(VALID_AUTHENTICATION_TOKEN.reverse)
end
test 'should authenticate a valid user with authentication token and return it' do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
user.confirm!
authenticated_user = User.authenticate_with_token(:auth_token => user.authentication_token)
assert_equal authenticated_user, user
end
test 'should return nil when authenticating an invalid user by authentication token' do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
user.confirm!
authenticated_user = User.authenticate_with_token(:auth_token => user.authentication_token.reverse)
assert_nil authenticated_user
end
end

View File

@@ -23,7 +23,21 @@ class ActiveRecordTest < ActiveSupport::TestCase
end
test 'add modules cherry pick' do
assert_include_modules Admin, :authenticatable, :timeoutable
assert_include_modules Admin, :database_authenticatable, :registerable, :timeoutable
end
test 'order of module inclusion' do
correct_module_order = [:database_authenticatable, :registerable, :timeoutable]
incorrect_module_order = [:database_authenticatable, :timeoutable, :registerable]
assert_include_modules Admin, *incorrect_module_order
# get module constants from symbol list
module_constants = correct_module_order.collect { |mod| Devise::Models::const_get(mod.to_s.classify) }
# confirm that they adhere to the order in ALL
# get included modules, filter out the noise, and reverse the order
assert_equal module_constants, (Admin.included_modules & module_constants).reverse
end
test 'set a default value for stretches' do

View File

@@ -17,6 +17,7 @@ ActiveRecord::Schema.define(:version => 1) do
t.rememberable
t.trackable
t.lockable
t.token_authenticatable
end
t.timestamps

View File

@@ -1,5 +1,5 @@
class Admin < ActiveRecord::Base
devise :authenticatable, :timeoutable
devise :authenticatable, :registerable, :timeoutable
def self.find_for_authentication(conditions)
last(:conditions => conditions)

View File

@@ -1,5 +1,7 @@
class User < ActiveRecord::Base
devise :authenticatable, :confirmable, :recoverable, :rememberable, :trackable,
:validatable, :timeoutable, :lockable
devise :authenticatable, :http_authenticatable, :confirmable, :lockable, :recoverable,
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable
attr_accessible :username, :email, :password, :password_confirmation
end

View File

@@ -7,4 +7,6 @@ class ApplicationController < ActionController::Base
# Scrub sensitive parameters from your log
filter_parameter_logging :password
before_filter :current_user
end

View File

@@ -1,9 +1,13 @@
class Admin
include MongoMapper::Document
devise :authenticatable, :timeoutable
devise :authenticatable, :registerable, :timeoutable
def self.find_for_authentication(conditions)
last(:conditions => conditions, :order => "email")
last(:conditions => conditions)
end
def self.last(options={})
options.merge!(:order => 'email')
super options
end
end

View File

@@ -1,7 +1,14 @@
class User
include MongoMapper::Document
key :created_at, DateTime
devise :authenticatable, :confirmable, :recoverable, :rememberable, :trackable,
:validatable, :timeoutable, :lockable
devise :authenticatable, :http_authenticatable, :confirmable, :lockable, :recoverable,
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable
# attr_accessible :username, :email, :password, :password_confirmation
def self.last(options={})
options.merge!(:order => 'email')
super options
end
end

View File

@@ -7,9 +7,13 @@
<body>
<div id="container">
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
<%- end -%>
<% if user_signed_in? -%>
<p>Hello User! You are signed in!</p>
<% end -%>
<%= yield %>
</div>
</body>

View File

@@ -35,6 +35,9 @@ Devise.setup do |config|
# Configure the e-mail address which will be shown in DeviseMailer.
config.mailer_sender = "please-change-me-omg@yourapp.com"
# Configure the content type of DeviseMailer mails (defaults to text/html")
# config.mailer_content_type = "text/plain"
# Load and configure the ORM. Supports :active_record, :data_mapper and :mongo_mapper.
require "devise/orm/#{DEVISE_ORM}"

View File

@@ -3,8 +3,9 @@ ActionController::Routing::Routes.draw do |map|
map.devise_for :admin, :as => 'admin_area'
map.devise_for :accounts, :scope => 'manager', :path_prefix => ':locale',
:class_name => "User", :requirements => { :extra => 'value' }, :path_names => {
:sign_in => 'login', :sign_out => 'logout', :password => 'secret',
:confirmation => 'verification', :unlock => 'unblock'
:sign_in => 'login', :sign_out => 'logout',
:password => 'secret', :confirmation => 'verification',
:unlock => 'unblock', :sign_up => 'register'
}
map.resources :users, :only => [:index], :member => { :expire => :get }

View File

@@ -42,6 +42,38 @@ class MapRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'passwords', :action => 'update'}, {:path => 'users/password', :method => :put})
end
test 'map new user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'new'}, 'users/unlock/new')
end
test 'map create user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'create'}, {:path => 'users/unlock', :method => :post})
end
test 'map show user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'show'}, {:path => 'users/unlock', :method => :get})
end
test 'map new user registration' do
assert_recognizes({:controller => 'registrations', :action => 'new'}, 'users/sign_up')
end
test 'map create user registration' do
assert_recognizes({:controller => 'registrations', :action => 'create'}, {:path => 'users', :method => :post})
end
test 'map edit user registration' do
assert_recognizes({:controller => 'registrations', :action => 'edit'}, {:path => 'users/edit', :method => :get})
end
test 'map update user registration' do
assert_recognizes({:controller => 'registrations', :action => 'update'}, {:path => 'users', :method => :put})
end
test 'map destroy user registration' do
assert_recognizes({:controller => 'registrations', :action => 'destroy'}, {:path => 'users', :method => :delete})
end
test 'map admin session with :as option' do
assert_recognizes({:controller => 'sessions', :action => 'new'}, {:path => 'admin_area/sign_in', :method => :get})
end
@@ -72,4 +104,7 @@ class MapRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'unlocks', :action => 'new', :locale => 'en', :extra => 'value'}, '/en/accounts/unblock/new')
end
test 'map account with custom path name for registration' do
assert_recognizes({:controller => 'registrations', :action => 'new', :locale => 'en', :extra => 'value'}, '/en/accounts/register')
end
end

View File

@@ -7,10 +7,14 @@ class ActionController::IntegrationTest
def create_user(options={})
@user ||= begin
user = User.create!(
:email => 'user@test.com', :password => '123456', :password_confirmation => '123456', :created_at => Time.now.utc
:username => 'usertest',
:email => 'user@test.com',
:password => '123456',
:password_confirmation => '123456',
:created_at => Time.now.utc
)
user.confirm! unless options[:confirm] == false
user.lock! if options[:locked] == true
user.lock_access! if options[:locked] == true
user
end
end
@@ -28,7 +32,7 @@ class ActionController::IntegrationTest
user = create_user(options)
visit new_user_session_path unless options[:visit] == false
fill_in 'email', :with => 'user@test.com'
fill_in 'password', :with => '123456'
fill_in 'password', :with => options[:password] || '123456'
check 'remember me' if options[:remember_me] == true
yield if block_given?
click_button 'Sign In'

View File

@@ -1,4 +1,6 @@
class ActiveSupport::TestCase
VALID_AUTHENTICATION_TOKEN = 'AbCdEfGhIjKlMnOpQrSt'.freeze
def setup_mailer
ActionMailer::Base.deliveries = []
end
@@ -21,7 +23,8 @@ class ActiveSupport::TestCase
end
def valid_attributes(attributes={})
{ :email => generate_unique_email,
{ :username => "usertest",
:email => generate_unique_email,
:password => '123456',
:password_confirmation => '123456' }.update(attributes)
end
@@ -33,4 +36,4 @@ class ActiveSupport::TestCase
def create_user(attributes={})
User.create!(valid_attributes(attributes))
end
end
end