mirror of
https://github.com/heartcombo/devise.git
synced 2026-01-09 23:58:06 -05:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c8c0f8dba | ||
|
|
66afa2a2bf | ||
|
|
747751a20f | ||
|
|
8e0327e203 | ||
|
|
d88b3cedfb | ||
|
|
c4d5a3fdaa | ||
|
|
11a77055f1 | ||
|
|
9bc8e1211b | ||
|
|
cd1bc53947 | ||
|
|
fffbeb5cc7 | ||
|
|
7539d31a05 | ||
|
|
78fedd6c10 | ||
|
|
14a0cfefaa | ||
|
|
b7e67115fc | ||
|
|
b7bc8dec12 | ||
|
|
b8ed2f3160 | ||
|
|
b46b7e3736 | ||
|
|
c5ef39f087 | ||
|
|
cfb8228de9 | ||
|
|
4e0c598de1 | ||
|
|
fed6a92d55 | ||
|
|
2c1ca126f4 | ||
|
|
36fb1ecf4d | ||
|
|
08986dbd4b | ||
|
|
b69d2c412f | ||
|
|
6f5d176e8e | ||
|
|
57515de5ad | ||
|
|
49aebde435 | ||
|
|
bc598b9da3 | ||
|
|
da0323e591 | ||
|
|
061e9d7404 | ||
|
|
3c519b13ff | ||
|
|
aeec6138c2 | ||
|
|
75ad4ee87b | ||
|
|
e029ad7b0c | ||
|
|
225afe6813 | ||
|
|
8487637b2f | ||
|
|
8978bd4fe0 | ||
|
|
5926898cf4 | ||
|
|
df2995ce19 | ||
|
|
5717c6f082 | ||
|
|
72cf2481b5 | ||
|
|
5c39a0c6d1 | ||
|
|
56a26bd280 | ||
|
|
b194882b23 | ||
|
|
b5909f9b93 | ||
|
|
3c9cfa50c0 | ||
|
|
e632240aee | ||
|
|
176158a309 | ||
|
|
2503f2d0a8 | ||
|
|
85897b4fe2 | ||
|
|
fdd47d74db | ||
|
|
ae4448403c | ||
|
|
2ead747be6 | ||
|
|
10aadee637 | ||
|
|
7670eb8e98 | ||
|
|
486dc65884 | ||
|
|
17e85aa79d | ||
|
|
10c9a492ab | ||
|
|
b7079c7cf2 | ||
|
|
55e69b18a4 | ||
|
|
020341176c | ||
|
|
a79846730e | ||
|
|
48eea9d325 | ||
|
|
dd7c3ee91f | ||
|
|
4e2cdc2d5b | ||
|
|
545a5cec3b | ||
|
|
ab77e08690 |
@@ -1,8 +1,21 @@
|
||||
== 3.0.0.rc
|
||||
== 3.0.1
|
||||
|
||||
* enhancements
|
||||
* Rails 4 and Strong Parameters compatibility. (@carlosantoniodasilva, @josevalim, @latortuga, @lucasmazza, @nashby, @rafaelfranca, @spastorino)
|
||||
* Drop support for Rails < 3.2 and Ruby < 1.9.3.
|
||||
* Add after_confirmation callback
|
||||
|
||||
* bug fix
|
||||
* When using rails 3.2, the generator adds 'attr_accessible' to the model (by @jcoyne)
|
||||
* Clean up CSRF token after authentication (by @homakov). Notice this change will clean up the CSRF Token after authentication (sign in, sign up, etc). So if you are using AJAX for such features, you will need to fetch a new CSRF token from the server.
|
||||
|
||||
== 3.0.0
|
||||
|
||||
* enhancements
|
||||
* Rails 4 and Strong Parameters compatibility (by @carlosantoniodasilva, @josevalim, @latortuga, @lucasmazza, @nashby, @rafaelfranca, @spastorino)
|
||||
* Drop support for Rails < 3.2 and Ruby < 1.9.3
|
||||
* Enable to skip sending reconfirmation email when reconfirmable is on and skip_confirmation_notification! is invoked (by @tkhr)
|
||||
|
||||
* bug fix
|
||||
* Errors on unlock are now properly reflected on the first `unlock_keys`
|
||||
|
||||
== 2.2.4
|
||||
|
||||
@@ -119,6 +132,7 @@ Security announcement: http://blog.plataformatec.com.br/2013/01/security-announc
|
||||
* Do not accidentally mark `_prefixes` as private
|
||||
* Better support for custom strategies on test helpers (by @mattconnolly)
|
||||
* Return `head :no_content` in SessionsController now that most JS libraries handle it (by @julianvargasalvarez)
|
||||
* Reverted moving devise/shared/_links.erb to devise/_links.erb
|
||||
|
||||
== 2.0.4
|
||||
|
||||
|
||||
2
Gemfile
2
Gemfile
@@ -2,7 +2,7 @@ source "https://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
gem "rails", "~> 4.0.0.rc1"
|
||||
gem "rails", "~> 4.0.0"
|
||||
gem "omniauth", "~> 1.0.0"
|
||||
gem "omniauth-oauth2", "~> 1.0.0"
|
||||
gem "rdoc"
|
||||
|
||||
73
Gemfile.lock
73
Gemfile.lock
@@ -12,64 +12,63 @@ GIT
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
devise (3.0.0.rc)
|
||||
devise (3.0.0)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
warden (~> 1.2.1)
|
||||
warden (~> 1.2.3)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionmailer (4.0.0.rc1)
|
||||
actionpack (= 4.0.0.rc1)
|
||||
actionmailer (4.0.0)
|
||||
actionpack (= 4.0.0)
|
||||
mail (~> 2.5.3)
|
||||
actionpack (4.0.0.rc1)
|
||||
activesupport (= 4.0.0.rc1)
|
||||
actionpack (4.0.0)
|
||||
activesupport (= 4.0.0)
|
||||
builder (~> 3.1.0)
|
||||
erubis (~> 2.7.0)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
activemodel (4.0.0.rc1)
|
||||
activesupport (= 4.0.0.rc1)
|
||||
activemodel (4.0.0)
|
||||
activesupport (= 4.0.0)
|
||||
builder (~> 3.1.0)
|
||||
activerecord (4.0.0.rc1)
|
||||
activemodel (= 4.0.0.rc1)
|
||||
activerecord (4.0.0)
|
||||
activemodel (= 4.0.0)
|
||||
activerecord-deprecated_finders (~> 1.0.2)
|
||||
activesupport (= 4.0.0.rc1)
|
||||
activesupport (= 4.0.0)
|
||||
arel (~> 4.0.0)
|
||||
activerecord-deprecated_finders (1.0.2)
|
||||
activesupport (4.0.0.rc1)
|
||||
activerecord-deprecated_finders (1.0.3)
|
||||
activesupport (4.0.0)
|
||||
i18n (~> 0.6, >= 0.6.4)
|
||||
minitest (~> 4.2)
|
||||
multi_json (~> 1.3)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 0.3.37)
|
||||
arel (4.0.0)
|
||||
atomic (1.1.8)
|
||||
bcrypt-ruby (3.0.1)
|
||||
atomic (1.1.10)
|
||||
bcrypt-ruby (3.1.1)
|
||||
builder (3.1.4)
|
||||
erubis (2.7.0)
|
||||
faraday (0.8.7)
|
||||
multipart-post (~> 1.1)
|
||||
hashie (1.2.0)
|
||||
hike (1.2.2)
|
||||
hike (1.2.3)
|
||||
httpauth (0.2.0)
|
||||
i18n (0.6.4)
|
||||
json (1.7.7)
|
||||
jwt (0.1.8)
|
||||
multi_json (>= 1.5)
|
||||
mail (2.5.3)
|
||||
i18n (>= 0.4.0)
|
||||
mail (2.5.4)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
metaclass (0.0.1)
|
||||
mime-types (1.23)
|
||||
minitest (4.7.4)
|
||||
minitest (4.7.5)
|
||||
mocha (0.13.3)
|
||||
metaclass (~> 0.0.1)
|
||||
moped (1.4.5)
|
||||
multi_json (1.7.2)
|
||||
multi_json (1.7.7)
|
||||
multipart-post (1.2.0)
|
||||
nokogiri (1.5.9)
|
||||
oauth2 (0.8.1)
|
||||
@@ -98,29 +97,29 @@ GEM
|
||||
ruby-openid (>= 2.1.8)
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rails (4.0.0.rc1)
|
||||
actionmailer (= 4.0.0.rc1)
|
||||
actionpack (= 4.0.0.rc1)
|
||||
activerecord (= 4.0.0.rc1)
|
||||
activesupport (= 4.0.0.rc1)
|
||||
rails (4.0.0)
|
||||
actionmailer (= 4.0.0)
|
||||
actionpack (= 4.0.0)
|
||||
activerecord (= 4.0.0)
|
||||
activesupport (= 4.0.0)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.0.0.rc1)
|
||||
sprockets-rails (~> 2.0.0.rc4)
|
||||
railties (4.0.0.rc1)
|
||||
actionpack (= 4.0.0.rc1)
|
||||
activesupport (= 4.0.0.rc1)
|
||||
railties (= 4.0.0)
|
||||
sprockets-rails (~> 2.0.0)
|
||||
railties (4.0.0)
|
||||
actionpack (= 4.0.0)
|
||||
activesupport (= 4.0.0)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (10.0.4)
|
||||
rake (10.1.0)
|
||||
rdoc (4.0.1)
|
||||
json (~> 1.4)
|
||||
ruby-openid (2.2.3)
|
||||
sprockets (2.9.3)
|
||||
sprockets (2.10.0)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sprockets-rails (2.0.0.rc4)
|
||||
sprockets-rails (2.0.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (~> 2.8)
|
||||
@@ -128,12 +127,12 @@ GEM
|
||||
thor (0.18.1)
|
||||
thread_safe (0.1.0)
|
||||
atomic
|
||||
tilt (1.4.0)
|
||||
treetop (1.4.12)
|
||||
tilt (1.4.1)
|
||||
treetop (1.4.14)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.37)
|
||||
warden (1.2.1)
|
||||
warden (1.2.3)
|
||||
rack (>= 1.0)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
@@ -154,7 +153,7 @@ DEPENDENCIES
|
||||
omniauth-facebook
|
||||
omniauth-oauth2 (~> 1.0.0)
|
||||
omniauth-openid (~> 1.0.1)
|
||||
rails (~> 4.0.0.rc1)
|
||||
rails (~> 4.0.0)
|
||||
rdoc
|
||||
sqlite3
|
||||
webrat (= 0.7.3)
|
||||
|
||||
70
README.md
70
README.md
@@ -12,7 +12,7 @@ Devise is a flexible authentication solution for Rails based on Warden. It:
|
||||
|
||||
* Is Rack based;
|
||||
* Is a complete MVC solution based on Rails engines;
|
||||
* Allows you to have multiple roles (or models/scopes) signed in at the same time;
|
||||
* Allows you to have multiple models signed in at the same time;
|
||||
* Is based on a modularity concept: use just what you really need.
|
||||
|
||||
It's composed of 11 modules:
|
||||
@@ -29,6 +29,8 @@ It's composed of 11 modules:
|
||||
* [Validatable](http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Validatable): provides validations of email and password. It's optional and can be customized, so you're able to define your own validations.
|
||||
* [Lockable](http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Lockable): locks an account after a specified number of failed sign-in attempts. Can unlock via email or after a specified time period.
|
||||
|
||||
Devise is guaranteed to be thread-safe on YARV. Thread-safety support on JRuby is on progress.
|
||||
|
||||
## Information
|
||||
|
||||
### The Devise wiki
|
||||
@@ -110,7 +112,7 @@ The generator will install an initializer which describes ALL Devise's configura
|
||||
rails generate devise MODEL
|
||||
```
|
||||
|
||||
Replace MODEL by the class name used for the applications users, it's frequently 'User' but could also be 'Admin'. This will create a model (if one does not exist) and configure it with default Devise modules. Next, you'll usually run "rake db:migrate" as the generator will have created a migration file (if your ORM supports them). This generator also configures your config/routes.rb file to point to the Devise controller.
|
||||
Replace MODEL by the class name used for the applications users, it's frequently `User` but could also be `Admin`. This will create a model (if one does not exist) and configure it with default Devise modules. Next, you'll usually run `rake db:migrate` as the generator will have created a migration file (if your ORM supports them). This generator also configures your config/routes.rb file to point to the Devise controller.
|
||||
|
||||
Note that you should re-start your app here if you've already started it. Otherwise you'll run into strange errors like users being unable to login and the route helpers being undefined.
|
||||
|
||||
@@ -200,6 +202,32 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
```
|
||||
|
||||
If you have multiple Devise models, you may want to set up different parameter sanitizer per model. In this case, we recommend inheriting from `Devise::ParameterSanitizer` and add your own logic:
|
||||
|
||||
```ruby
|
||||
class User::ParameterSanitizer < Devise::ParameterSanitizer
|
||||
def sign_in
|
||||
default_params.permit(:username, :email)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And then configure your controllers to use it:
|
||||
|
||||
```ruby
|
||||
class ApplicationController < ActionController::Base
|
||||
protected
|
||||
|
||||
def devise_parameter_sanitizer
|
||||
if resource_class == User
|
||||
User::ParameterSanitizer.new(User, :user, params)
|
||||
else
|
||||
super # Use the default one
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The example above overrides the permitted parameters for the user to be both `:username` and `:email`. The non-lazy way to configure parameters would be by defining the before filter above in a custom controller. We detail how to configure and customize controllers in some sections below.
|
||||
|
||||
### Configuring views
|
||||
@@ -212,7 +240,7 @@ Since Devise is an engine, all its views are packaged inside the gem. These view
|
||||
rails generate devise:views
|
||||
```
|
||||
|
||||
If you have more than one role in your application (such as "User" and "Admin"), you will notice that Devise uses the same views for all roles. Fortunately, Devise offers an easy way to customize views. All you need to do is set "config.scoped_views = true" inside "config/initializers/devise.rb".
|
||||
If you have more than one Devise model in your application (such as "User" and "Admin"), you will notice that Devise uses the same views for all models. Fortunately, Devise offers an easy way to customize views. All you need to do is set "config.scoped_views = true" inside "config/initializers/devise.rb".
|
||||
|
||||
After doing so, you will be able to have views based on the role like "users/sessions/new" and "admins/sessions/new". If no view is found within the scope, Devise will use the default view at "devise/sessions/new". You can also use the generator to generate scoped views:
|
||||
|
||||
@@ -224,22 +252,24 @@ rails generate devise:views users
|
||||
|
||||
If the customization at the views level is not enough, you can customize each controller by following these steps:
|
||||
|
||||
1) Create your custom controller, for example a Admins::SessionsController:
|
||||
1. Create your custom controller, for example a `Admins::SessionsController`:
|
||||
|
||||
```ruby
|
||||
class Admins::SessionsController < Devise::SessionsController
|
||||
end
|
||||
```
|
||||
```ruby
|
||||
class Admins::SessionsController < Devise::SessionsController
|
||||
end
|
||||
```
|
||||
|
||||
2) Tell the router to use this controller:
|
||||
Note that in the above example, the controller needs to be created in the `app/controller/admins/` directory.
|
||||
|
||||
```ruby
|
||||
devise_for :admins, :controllers => { :sessions => "admins/sessions" }
|
||||
```
|
||||
2. Tell the router to use this controller:
|
||||
|
||||
3) And since we changed the controller, it won't use the "devise/sessions" views, so remember to copy "devise/sessions" to "admin/sessions".
|
||||
```ruby
|
||||
devise_for :admins, :controllers => { :sessions => "admins/sessions" }
|
||||
```
|
||||
|
||||
Remember that Devise uses flash messages to let users know if sign in was successful or failed. Devise expects your application to call "flash[:notice]" and "flash[:alert]" as appropriate. Do not print the entire flash hash, print specific keys or at least remove the `:timedout` key from the hash as Devise adds this key in some circumstances, this key is not meant for display.
|
||||
3. And since we changed the controller, it won't use the `"devise/sessions"` views, so remember to copy `"devise/sessions"` to `"admin/sessions"`.
|
||||
|
||||
Remember that Devise uses flash messages to let users know if sign in was successful or failed. Devise expects your application to call `"flash[:notice]"` and `"flash[:alert]"` as appropriate. Do not print the entire flash hash, print specific keys or at least remove the `:timedout` key from the hash as Devise adds this key in some circumstances, this key is not meant for display.
|
||||
|
||||
### Configuring routes
|
||||
|
||||
@@ -331,12 +361,14 @@ sign_out @user # sign_out(resource)
|
||||
|
||||
There are two things that is important to keep in mind:
|
||||
|
||||
1) These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;
|
||||
1. These helpers are not going to work for integration tests driven by Capybara or Webrat. They are meant to be used with functional tests only. Instead, fill in the form or explicitly set the user in session;
|
||||
|
||||
2) If you are testing Devise internal controllers or a controller that inherits from Devise's, you need to tell Devise which mapping should be used before a request. This is necessary because Devise gets this information from router, but since functional tests do not pass through the router, it needs to be told explicitly. For example, if you are testing the user scope, simply do:
|
||||
2. If you are testing Devise internal controllers or a controller that inherits from Devise's, you need to tell Devise which mapping should be used before a request. This is necessary because Devise gets this information from router, but since functional tests do not pass through the router, it needs to be told explicitly. For example, if you are testing the user scope, simply do:
|
||||
|
||||
```ruby
|
||||
@request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
get :new
|
||||
```
|
||||
|
||||
### Omniauth
|
||||
|
||||
@@ -352,7 +384,7 @@ You can read more about Omniauth support in the wiki:
|
||||
|
||||
### Configuring multiple models
|
||||
|
||||
Devise allows you to set up as many roles as you want. For example, you may have a User model and also want an Admin model with just authentication and timeoutable features. If so, just follow these steps:
|
||||
Devise allows you to set up as many Devise models as you want. If you want to have an Admin model with just authentication and timeout features, in addition to the User model above, just run:
|
||||
|
||||
```ruby
|
||||
# Create a migration with the required fields
|
||||
@@ -377,7 +409,9 @@ current_admin
|
||||
admin_session
|
||||
```
|
||||
|
||||
On the other hand, you can simply run the generator!
|
||||
Alternatively, you can simply run the Devise generator.
|
||||
|
||||
Keep in mind that those models will have completely different routes. They **do not** and **cannot** share the same controller for sign in, sign out and so on. In case you want to have different roles sharing the same actions, we recommend you to use a role-based approach, by either providing a role column or using [CanCan](https://github.com/ryanb/cancan).
|
||||
|
||||
### Other ORMs
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class Devise::PasswordsController < DeviseController
|
||||
# Check if a reset_password_token is provided in the request
|
||||
def assert_reset_token_passed
|
||||
if params[:reset_password_token].blank?
|
||||
set_flash_message(:error, :no_token)
|
||||
set_flash_message(:alert, :no_token)
|
||||
redirect_to new_session_path(resource_name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,7 +10,7 @@ class Devise::RegistrationsController < DeviseController
|
||||
|
||||
# POST /resource
|
||||
def create
|
||||
self.resource = build_resource(sign_up_params)
|
||||
build_resource(sign_up_params)
|
||||
|
||||
if resource.save
|
||||
if resource.active_for_authentication?
|
||||
|
||||
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
||||
s.test_files = `git ls-files -- test/*`.split("\n")
|
||||
s.require_paths = ["lib"]
|
||||
|
||||
s.add_dependency("warden", "~> 1.2.1")
|
||||
s.add_dependency("warden", "~> 1.2.3")
|
||||
s.add_dependency("orm_adapter", "~> 0.1")
|
||||
s.add_dependency("bcrypt-ruby", "~> 3.0")
|
||||
s.add_dependency("railties", ">= 3.2.6", "< 5")
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
PATH
|
||||
remote: ..
|
||||
specs:
|
||||
devise (3.0.0.rc)
|
||||
devise (3.0.0)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
warden (~> 1.2.1)
|
||||
warden (~> 1.2.3)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
@@ -38,7 +38,7 @@ GEM
|
||||
i18n (= 0.6.1)
|
||||
multi_json (~> 1.0)
|
||||
arel (3.0.2)
|
||||
bcrypt-ruby (3.0.1)
|
||||
bcrypt-ruby (3.1.1)
|
||||
builder (3.0.4)
|
||||
erubis (2.7.0)
|
||||
faraday (0.8.7)
|
||||
@@ -129,7 +129,7 @@ GEM
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.37)
|
||||
warden (1.2.1)
|
||||
warden (1.2.3)
|
||||
rack (>= 1.0)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
|
||||
@@ -9,7 +9,7 @@ module Devise
|
||||
autoload :Delegator, 'devise/delegator'
|
||||
autoload :FailureApp, 'devise/failure_app'
|
||||
autoload :OmniAuth, 'devise/omniauth'
|
||||
autoload :ParamFilter, 'devise/param_filter'
|
||||
autoload :ParameterFilter, 'devise/parameter_filter'
|
||||
autoload :BaseSanitizer, 'devise/parameter_sanitizer'
|
||||
autoload :ParameterSanitizer, 'devise/parameter_sanitizer'
|
||||
autoload :TestHelpers, 'devise/test_helpers'
|
||||
@@ -223,6 +223,10 @@ module Devise
|
||||
mattr_accessor :omniauth_path_prefix
|
||||
@@omniauth_path_prefix = nil
|
||||
|
||||
# Set if we should clean up the CSRF Token on authentication
|
||||
mattr_accessor :clean_up_csrf_token_on_authentication
|
||||
@@clean_up_csrf_token_on_authentication = true
|
||||
|
||||
def self.encryptor=(value)
|
||||
warn "\n[DEVISE] To select a encryption which isn't bcrypt, you should use devise-encryptable gem.\n"
|
||||
end
|
||||
|
||||
@@ -78,7 +78,14 @@ module Devise
|
||||
def redirect_url
|
||||
if warden_message == :timeout
|
||||
flash[:timedout] = true
|
||||
attempted_path || scope_path
|
||||
|
||||
path = if request.get?
|
||||
attempted_path
|
||||
else
|
||||
request.referrer
|
||||
end
|
||||
|
||||
path || scope_path
|
||||
else
|
||||
scope_path
|
||||
end
|
||||
|
||||
5
lib/devise/hooks/csrf_cleaner.rb
Normal file
5
lib/devise/hooks/csrf_cleaner.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
Warden::Manager.after_authentication do |record, warden, options|
|
||||
if Devise.clean_up_csrf_token_on_authentication
|
||||
warden.request.session.try(:delete, :_csrf_token)
|
||||
end
|
||||
end
|
||||
@@ -2,6 +2,6 @@
|
||||
# This is only triggered when the user is explicitly set (with set_user)
|
||||
Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
|
||||
if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
|
||||
record.update_attribute(:failed_attempts, 0)
|
||||
record.update_attribute(:failed_attempts, 0) unless record.failed_attempts.zero?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,17 +29,17 @@ module Devise
|
||||
|
||||
# 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
|
||||
def self.find_scope!(obj)
|
||||
case obj
|
||||
when String, Symbol
|
||||
return duck
|
||||
return obj
|
||||
when Class
|
||||
Devise.mappings.each_value { |m| return m.name if duck <= m.to }
|
||||
Devise.mappings.each_value { |m| return m.name if obj <= m.to }
|
||||
else
|
||||
Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
|
||||
Devise.mappings.each_value { |m| return m.name if obj.is_a?(m.to) }
|
||||
end
|
||||
|
||||
raise "Could not find a valid mapping for #{duck.inspect}"
|
||||
raise "Could not find a valid mapping for #{obj.inspect}"
|
||||
end
|
||||
|
||||
def self.find_by_path!(path, path_type=:fullpath)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'devise/hooks/activatable'
|
||||
require 'devise/hooks/csrf_cleaner'
|
||||
|
||||
module Devise
|
||||
module Models
|
||||
@@ -21,7 +22,7 @@ module Devise
|
||||
# as key on authentication. This can also be a hash where the value is a boolean specifying
|
||||
# if the value is required or not.
|
||||
#
|
||||
# * +http_authenticatable+: if this model allows http authentication. By default true.
|
||||
# * +http_authenticatable+: if this model allows http authentication. By default false.
|
||||
# It also accepts an array specifying the strategies that should allow http.
|
||||
#
|
||||
# * +params_authenticatable+: if this model allows authentication through request params. By default true.
|
||||
@@ -243,7 +244,7 @@ module Devise
|
||||
end
|
||||
|
||||
def find_first_by_auth_conditions(tainted_conditions, opts={})
|
||||
to_adapter.find_first(devise_param_filter.filter(tainted_conditions).merge(opts))
|
||||
to_adapter.find_first(devise_parameter_filter.filter(tainted_conditions).merge(opts))
|
||||
end
|
||||
|
||||
# Find an initialize a record setting an error if it can't be found.
|
||||
@@ -275,8 +276,8 @@ module Devise
|
||||
|
||||
protected
|
||||
|
||||
def devise_param_filter
|
||||
@devise_param_filter ||= Devise::ParamFilter.new(case_insensitive_keys, strip_whitespace_keys)
|
||||
def devise_parameter_filter
|
||||
@devise_parameter_filter ||= Devise::ParameterFilter.new(case_insensitive_keys, strip_whitespace_keys)
|
||||
end
|
||||
|
||||
# Generate a token by looping and ensuring does not already exist.
|
||||
|
||||
@@ -35,8 +35,8 @@ module Devise
|
||||
included do
|
||||
before_create :generate_confirmation_token, :if => :confirmation_required?
|
||||
after_create :send_on_create_confirmation_instructions, :if => :send_confirmation_notification?
|
||||
before_update :postpone_email_change_until_confirmation, :if => :postpone_email_change?
|
||||
after_update :send_confirmation_instructions, :if => :reconfirmation_required?
|
||||
before_update :postpone_email_change_until_confirmation_and_regenerate_confirmation_token, :if => :postpone_email_change?
|
||||
after_update :send_reconfirmation_instructions, :if => :reconfirmation_required?
|
||||
end
|
||||
|
||||
def initialize(*args, &block)
|
||||
@@ -66,7 +66,7 @@ module Devise
|
||||
self.confirmation_token = nil
|
||||
self.confirmed_at = Time.now.utc
|
||||
|
||||
if self.class.reconfirmable && unconfirmed_email.present?
|
||||
saved = if self.class.reconfirmable && unconfirmed_email.present?
|
||||
skip_reconfirmation!
|
||||
self.email = unconfirmed_email
|
||||
self.unconfirmed_email = nil
|
||||
@@ -76,6 +76,9 @@ module Devise
|
||||
else
|
||||
save(:validate => false)
|
||||
end
|
||||
|
||||
after_confirmation if saved
|
||||
saved
|
||||
end
|
||||
end
|
||||
|
||||
@@ -90,23 +93,34 @@ module Devise
|
||||
|
||||
# Send confirmation instructions by email
|
||||
def send_confirmation_instructions
|
||||
self.confirmation_token = nil if reconfirmation_required?
|
||||
@reconfirmation_required = false
|
||||
|
||||
generate_confirmation_token! if self.confirmation_token.blank?
|
||||
ensure_confirmation_token!
|
||||
|
||||
opts = pending_reconfirmation? ? { :to => unconfirmed_email } : { }
|
||||
send_devise_notification(:confirmation_instructions, opts)
|
||||
end
|
||||
|
||||
# Resend confirmation token. This method does not need to generate a new token.
|
||||
def resend_confirmation_token
|
||||
pending_any_confirmation do
|
||||
self.confirmation_token = nil if confirmation_period_expired?
|
||||
def send_reconfirmation_instructions
|
||||
@reconfirmation_required = false
|
||||
|
||||
unless @skip_confirmation_notification
|
||||
send_confirmation_instructions
|
||||
end
|
||||
end
|
||||
|
||||
# Resend confirmation token.
|
||||
# Regenerates the token if the period is expired.
|
||||
def resend_confirmation_token
|
||||
pending_any_confirmation do
|
||||
regenerate_confirmation_token! if confirmation_period_expired?
|
||||
send_confirmation_instructions
|
||||
end
|
||||
end
|
||||
|
||||
# Generate a confirmation token unless already exists and save the record.
|
||||
def ensure_confirmation_token!
|
||||
generate_confirmation_token! if should_generate_confirmation_token?
|
||||
end
|
||||
|
||||
# Overwrites active_for_authentication? for confirmation
|
||||
# by verifying whether a user is active to sign in or not. If the user
|
||||
# is already confirmed, it should never be blocked. Otherwise we need to
|
||||
@@ -126,7 +140,7 @@ module Devise
|
||||
self.confirmed_at = Time.now.utc
|
||||
end
|
||||
|
||||
# Skips sending the confirmation notification email after_create. Unlike
|
||||
# Skips sending the confirmation/reconfirmation notification email after_create/after_update. Unlike
|
||||
# #skip_confirmation!, record still requires confirmation.
|
||||
def skip_confirmation_notification!
|
||||
@skip_confirmation_notification = true
|
||||
@@ -139,6 +153,9 @@ module Devise
|
||||
end
|
||||
|
||||
protected
|
||||
def should_generate_confirmation_token?
|
||||
confirmation_token.nil? || confirmation_period_expired?
|
||||
end
|
||||
|
||||
# A callback method used to deliver confirmation
|
||||
# instructions on creation. This can be overriden
|
||||
@@ -215,29 +232,42 @@ module Devise
|
||||
generate_confirmation_token && save(:validate => false)
|
||||
end
|
||||
|
||||
# Regenerates a new token.
|
||||
def regenerate_confirmation_token
|
||||
generate_confirmation_token
|
||||
end
|
||||
|
||||
def regenerate_confirmation_token!
|
||||
regenerate_confirmation_token && save(:validate => false)
|
||||
end
|
||||
|
||||
def after_password_reset
|
||||
super
|
||||
confirm! unless confirmed?
|
||||
end
|
||||
|
||||
def postpone_email_change_until_confirmation
|
||||
def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
|
||||
@reconfirmation_required = true
|
||||
self.unconfirmed_email = self.email
|
||||
self.email = self.email_was
|
||||
regenerate_confirmation_token
|
||||
end
|
||||
|
||||
def postpone_email_change?
|
||||
postpone = self.class.reconfirmable && email_changed? && !@bypass_postpone
|
||||
postpone = self.class.reconfirmable && email_changed? && !@bypass_postpone && !self.email.blank?
|
||||
@bypass_postpone = false
|
||||
postpone
|
||||
end
|
||||
|
||||
def reconfirmation_required?
|
||||
self.class.reconfirmable && @reconfirmation_required
|
||||
self.class.reconfirmable && @reconfirmation_required && !self.email.blank?
|
||||
end
|
||||
|
||||
def send_confirmation_notification?
|
||||
confirmation_required? && !@skip_confirmation_notification
|
||||
confirmation_required? && !@skip_confirmation_notification && !self.email.blank?
|
||||
end
|
||||
|
||||
def after_confirmation
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
@@ -81,7 +81,7 @@ module Devise
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# def update_without_password(params={})
|
||||
# def update_without_password(params, *options)
|
||||
# params.delete(:email)
|
||||
# super(params)
|
||||
# end
|
||||
|
||||
@@ -55,7 +55,7 @@ module Devise
|
||||
|
||||
# Verifies whether a user is locked or not.
|
||||
def access_locked?
|
||||
locked_at && !lock_expired?
|
||||
!!locked_at && !lock_expired?
|
||||
end
|
||||
|
||||
# Send unlock instructions by email
|
||||
@@ -146,16 +146,16 @@ module Devise
|
||||
if access_locked?
|
||||
yield
|
||||
else
|
||||
self.errors.add(:email, :not_locked)
|
||||
self.errors.add(Devise.unlock_keys.first, :not_locked)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Attempt to find a user by its email. If a record is found, send new
|
||||
# Attempt to find a user by its unlock keys. If a record is found, send new
|
||||
# unlock instructions to it. If not user is found, returns a new user
|
||||
# with an email not found error.
|
||||
# Options must contain the user email
|
||||
# Options must contain the user's unlock keys
|
||||
def send_unlock_instructions(attributes={})
|
||||
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
|
||||
lockable.resend_unlock_token if lockable.persisted?
|
||||
|
||||
@@ -44,10 +44,15 @@ module Devise
|
||||
|
||||
# Resets reset password token and send reset password instructions by email
|
||||
def send_reset_password_instructions
|
||||
generate_reset_password_token! if should_generate_reset_token?
|
||||
ensure_reset_password_token!
|
||||
send_devise_notification(:reset_password_instructions)
|
||||
end
|
||||
|
||||
|
||||
# Generate reset password token unless already exists and save the record.
|
||||
def ensure_reset_password_token!
|
||||
generate_reset_password_token! if should_generate_reset_token?
|
||||
end
|
||||
|
||||
# Checks if the reset password token sent is within the limit time.
|
||||
# We do this by calculating if the difference between today and the
|
||||
# sending date does not exceed the confirm in time configured.
|
||||
|
||||
@@ -50,7 +50,7 @@ module Devise
|
||||
def remember_me!(extend_period=false)
|
||||
self.remember_token = self.class.remember_token if generate_remember_token?
|
||||
self.remember_created_at = Time.now.utc if generate_remember_timestamp?(extend_period)
|
||||
save(:validate => false)
|
||||
save(:validate => false) if self.changed?
|
||||
end
|
||||
|
||||
# If the record is persisted, remove the remember token (but only if
|
||||
|
||||
@@ -37,7 +37,7 @@ module Devise
|
||||
private
|
||||
|
||||
def remember_exists_and_not_expired?
|
||||
return false unless respond_to?(:remember_created_at)
|
||||
return false unless respond_to?(:remember_created_at) && respond_to?(:remember_expired?)
|
||||
remember_created_at && !remember_expired?
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Devise
|
||||
class ParamFilter
|
||||
class ParameterFilter
|
||||
def initialize(case_insensitive_keys, strip_whitespace_keys)
|
||||
@case_insensitive_keys = case_insensitive_keys || []
|
||||
@strip_whitespace_keys = strip_whitespace_keys || []
|
||||
@@ -40,20 +40,24 @@ module Devise
|
||||
end
|
||||
end
|
||||
|
||||
# These are the params used to sign in a user so we don't need to
|
||||
# mass-assign the password param in order to authenticate. Excluding it
|
||||
# here allows us to construct a new user without sensitive information if
|
||||
# authentication fails.
|
||||
def sign_in
|
||||
default_params.permit(auth_keys)
|
||||
default_params.permit(*auth_keys + [:password, :remember_me])
|
||||
end
|
||||
|
||||
def sign_up
|
||||
default_params.permit(auth_keys + [:password, :password_confirmation])
|
||||
default_params.permit(*(auth_keys + [:password, :password_confirmation]))
|
||||
end
|
||||
|
||||
def account_update
|
||||
default_params.permit(auth_keys + [:password, :password_confirmation, :current_password])
|
||||
default_params.permit(*(auth_keys + [:password, :password_confirmation, :current_password]))
|
||||
end
|
||||
|
||||
def auth_keys
|
||||
resource_class.authentication_keys
|
||||
resource_class.authentication_keys.respond_to?(:keys) ? resource_class.authentication_keys.keys : resource_class.authentication_keys
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module Devise
|
||||
VERSION = "3.0.0.rc".freeze
|
||||
VERSION = "3.0.1".freeze
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ module Devise
|
||||
module Generators
|
||||
module OrmHelpers
|
||||
def model_contents
|
||||
<<-CONTENT
|
||||
buffer = <<-CONTENT
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :confirmable,
|
||||
# :lockable, :timeoutable and :omniauthable
|
||||
@@ -10,16 +10,36 @@ module Devise
|
||||
:recoverable, :rememberable, :trackable, :validatable
|
||||
|
||||
CONTENT
|
||||
buffer += <<-CONTENT if needs_attr_accessible?
|
||||
# Setup accessible (or protected) attributes for your model
|
||||
attr_accessible :email, :password, :password_confirmation, :remember_me
|
||||
|
||||
CONTENT
|
||||
buffer
|
||||
end
|
||||
|
||||
def needs_attr_accessible?
|
||||
rails_3? && !strong_parameters_enabled?
|
||||
end
|
||||
|
||||
def rails_3?
|
||||
Rails::VERSION::MAJOR == 3
|
||||
end
|
||||
|
||||
def strong_parameters_enabled?
|
||||
defined?(ActionController::StrongParameters)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_exists?
|
||||
File.exists?(File.join(destination_root, model_path))
|
||||
end
|
||||
|
||||
|
||||
def migration_exists?(table_name)
|
||||
Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
|
||||
end
|
||||
|
||||
|
||||
def migration_path
|
||||
@migration_path ||= File.join("db", "migrate")
|
||||
end
|
||||
@@ -29,4 +49,4 @@ CONTENT
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,7 +22,7 @@ module Mongoid
|
||||
## Database authenticatable
|
||||
field :email, :type => String, :default => ""
|
||||
field :encrypted_password, :type => String, :default => ""
|
||||
|
||||
|
||||
## Recoverable
|
||||
field :reset_password_token, :type => String
|
||||
field :reset_password_sent_at, :type => Time
|
||||
@@ -54,4 +54,4 @@ RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ Some setup you must do manually if you haven't yet:
|
||||
<p class="notice"><%= notice %></p>
|
||||
<p class="alert"><%= alert %></p>
|
||||
|
||||
4. If you are deploying Rails 3.1+ on Heroku, you may want to set:
|
||||
4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:
|
||||
|
||||
config.assets.initialize_on_precompile = false
|
||||
|
||||
|
||||
@@ -76,6 +76,12 @@ Devise.setup do |config|
|
||||
# passing :skip => :sessions to `devise_for` in your config/routes.rb
|
||||
config.skip_session_storage = [:http_auth]
|
||||
|
||||
# By default, Devise cleans up the CSRF token on authentication to
|
||||
# avoid CSRF token fixation attacks. This means that, when using AJAX
|
||||
# requests for sign in and sign up, you need to get a new CSRF token
|
||||
# from the server. You can disable this option at your own risk.
|
||||
# config.clean_up_csrf_token_on_authentication = true
|
||||
|
||||
# ==> Configuration for :database_authenticatable
|
||||
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
|
||||
# using other encryptors, it sets how many times you want the password re-encrypted.
|
||||
|
||||
@@ -4,6 +4,20 @@ class SessionsControllerTest < ActionController::TestCase
|
||||
tests Devise::SessionsController
|
||||
include Devise::TestHelpers
|
||||
|
||||
test "#create doesn't raise unpermitted params when sign in fails" do
|
||||
ActiveSupport::Notifications.subscribe /unpermitted_parameters/ do |name, start, finish, id, payload|
|
||||
flunk "Unpermitted params: #{payload}"
|
||||
end
|
||||
request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
request.session["user_return_to"] = 'foo.bar'
|
||||
user = create_user
|
||||
post :create, :user => {
|
||||
:email => "wrong@email.com",
|
||||
:password => "wrongpassword"
|
||||
}
|
||||
assert_equal 200, @response.status
|
||||
end
|
||||
|
||||
test "#create works even with scoped views" do
|
||||
swap Devise, :scoped_views => true do
|
||||
request.env["devise.mapping"] = Devise.mappings[:user]
|
||||
|
||||
@@ -62,11 +62,41 @@ if DEVISE_ORM == :active_record
|
||||
destination File.expand_path("../../tmp", __FILE__)
|
||||
setup :prepare_destination
|
||||
|
||||
test "all files are properly created" do
|
||||
test "all files are properly created in rails 4.0" do
|
||||
ActiveRecord::Generators::DeviseGenerator.any_instance.stubs(:rails_3?).returns(false)
|
||||
simulate_inside_engine(RailsEngine::Engine, RailsEngine) do
|
||||
run_generator ["monster"]
|
||||
|
||||
assert_file "app/models/rails_engine/monster.rb", /devise/
|
||||
assert_file "app/models/rails_engine/monster.rb" do |content|
|
||||
assert_no_match /attr_accessible :email/, content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "all files are properly created in rails 3.2 when strong_parameters gem is not installed" do
|
||||
ActiveRecord::Generators::DeviseGenerator.any_instance.stubs(:rails_3?).returns(true)
|
||||
ActiveRecord::Generators::DeviseGenerator.any_instance.stubs(:strong_parameters_enabled?).returns(false)
|
||||
simulate_inside_engine(RailsEngine::Engine, RailsEngine) do
|
||||
run_generator ["monster"]
|
||||
|
||||
assert_file "app/models/rails_engine/monster.rb", /devise/
|
||||
assert_file "app/models/rails_engine/monster.rb" do |content|
|
||||
assert_match /attr_accessible :email/, content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "all files are properly created in rails 3.2 when strong_parameters gem is installed" do
|
||||
ActiveRecord::Generators::DeviseGenerator.any_instance.stubs(:rails_3?).returns(true)
|
||||
ActiveRecord::Generators::DeviseGenerator.any_instance.stubs(:strong_parameters_enabled?).returns(true)
|
||||
simulate_inside_engine(RailsEngine::Engine, RailsEngine) do
|
||||
run_generator ["monster"]
|
||||
|
||||
assert_file "app/models/rails_engine/monster.rb", /devise/
|
||||
assert_file "app/models/rails_engine/monster.rb" do |content|
|
||||
assert_no_match /attr_accessible :email/, content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -327,6 +327,20 @@ class AuthenticationSessionTest < ActionDispatch::IntegrationTest
|
||||
assert_redirected_to new_user_session_path
|
||||
end
|
||||
|
||||
test 'refreshes _csrf_token' do
|
||||
ApplicationController.allow_forgery_protection = true
|
||||
|
||||
begin
|
||||
get new_user_session_path
|
||||
token = request.session[:_csrf_token]
|
||||
|
||||
sign_in_as_user
|
||||
assert_not_equal request.session[:_csrf_token], token
|
||||
ensure
|
||||
ApplicationController.allow_forgery_protection = false
|
||||
end
|
||||
end
|
||||
|
||||
test 'allows session to be set for a given scope' do
|
||||
sign_in_as_user
|
||||
get '/users'
|
||||
|
||||
@@ -45,6 +45,16 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
|
||||
assert_not warden.authenticated?(:user)
|
||||
end
|
||||
|
||||
test 'time out user session after deault limit time and redirect to latest get request' do
|
||||
user = sign_in_as_user
|
||||
visit edit_form_user_path(user)
|
||||
|
||||
click_button 'Update'
|
||||
sign_in_as_user
|
||||
|
||||
assert_equal edit_form_user_url(user), current_url
|
||||
end
|
||||
|
||||
test 'time out is not triggered on sign out' do
|
||||
user = sign_in_as_user
|
||||
get expire_user_path(user)
|
||||
|
||||
@@ -114,6 +114,14 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not send confirmation when no email is provided' do
|
||||
assert_email_not_sent do
|
||||
user = new_user
|
||||
user.email = ''
|
||||
user.save(:validate => false)
|
||||
end
|
||||
end
|
||||
|
||||
test 'should find a user to send confirmation instructions' do
|
||||
user = create_user
|
||||
confirmation_user = User.send_confirmation_instructions(:email => user.email)
|
||||
@@ -286,6 +294,45 @@ class ConfirmableTest < ActiveSupport::TestCase
|
||||
assert_not_equal user.confirmation_token, old
|
||||
end
|
||||
end
|
||||
|
||||
test 'should generate a new token when a valid one does not exist' do
|
||||
swap Devise, :confirm_within => 3.days do
|
||||
user = create_user
|
||||
user.update_attribute(:confirmation_sent_at, 4.days.ago)
|
||||
old = user.confirmation_token
|
||||
user.ensure_confirmation_token!
|
||||
assert_not_equal user.confirmation_token, old
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not generate a new token when a valid one exists' do
|
||||
user = create_user
|
||||
assert_not_nil user.confirmation_token
|
||||
old = user.confirmation_token
|
||||
user.ensure_confirmation_token!
|
||||
assert_equal user.confirmation_token, old
|
||||
end
|
||||
|
||||
test 'should call after_confirmation if confirmed' do
|
||||
user = create_user
|
||||
user.define_singleton_method :after_confirmation do
|
||||
self.username = self.username.to_s + 'updated'
|
||||
end
|
||||
old = user.username
|
||||
assert user.confirm!
|
||||
assert_not_equal user.username, old
|
||||
end
|
||||
|
||||
test 'should not call after_confirmation if not confirmed' do
|
||||
user = create_user
|
||||
assert user.confirm!
|
||||
user.define_singleton_method :after_confirmation do
|
||||
self.username = self.username.to_s + 'updated'
|
||||
end
|
||||
old = user.username
|
||||
assert_not user.confirm!
|
||||
assert_equal user.username, old
|
||||
end
|
||||
end
|
||||
|
||||
class ReconfirmableTest < ActiveSupport::TestCase
|
||||
@@ -311,6 +358,15 @@ class ReconfirmableTest < ActiveSupport::TestCase
|
||||
assert_nil admin.confirmation_token
|
||||
end
|
||||
|
||||
test 'should skip sending reconfirmation email when email is changed and skip_confirmation_notification! is invoked' do
|
||||
admin = create_admin
|
||||
admin.skip_confirmation_notification!
|
||||
|
||||
assert_email_not_sent do
|
||||
admin.update_attributes(:email => 'new_test@example.com')
|
||||
end
|
||||
end
|
||||
|
||||
test 'should regenerate confirmation token after changing email' do
|
||||
admin = create_admin
|
||||
assert admin.confirm!
|
||||
@@ -337,6 +393,15 @@ class ReconfirmableTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not send confirmation by email after changing to a blank email' do
|
||||
admin = create_admin
|
||||
assert admin.confirm!
|
||||
assert_email_not_sent do
|
||||
admin.email = ''
|
||||
admin.save(:validate => false)
|
||||
end
|
||||
end
|
||||
|
||||
test 'should stay confirmed when email is changed' do
|
||||
admin = create_admin
|
||||
assert admin.confirm!
|
||||
|
||||
@@ -48,19 +48,19 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
|
||||
|
||||
test "param filter should not convert booleans and integer to strings" do
|
||||
conditions = { "login" => "foo@bar.com", "bool1" => true, "bool2" => false, "fixnum" => 123, "will_be_converted" => (1..10) }
|
||||
conditions = Devise::ParamFilter.new([], []).filter(conditions)
|
||||
conditions = Devise::ParameterFilter.new([], []).filter(conditions)
|
||||
assert_equal( { "login" => "foo@bar.com", "bool1" => "true", "bool2" => "false", "fixnum" => "123", "will_be_converted" => "1..10" }, conditions)
|
||||
end
|
||||
|
||||
test 'param filter should filter case_insensitive_keys as insensitive' do
|
||||
conditions = {'insensitive' => 'insensitive_VAL', 'sensitive' => 'sensitive_VAL'}
|
||||
conditions = Devise::ParamFilter.new(['insensitive'], []).filter(conditions)
|
||||
conditions = Devise::ParameterFilter.new(['insensitive'], []).filter(conditions)
|
||||
assert_equal( {'insensitive' => 'insensitive_val', 'sensitive' => 'sensitive_VAL'}, conditions )
|
||||
end
|
||||
|
||||
test 'param filter should filter strip_whitespace_keys stripping whitespaces' do
|
||||
conditions = {'strip_whitespace' => ' strip_whitespace_val ', 'do_not_strip_whitespace' => ' do_not_strip_whitespace_val '}
|
||||
conditions = Devise::ParamFilter.new([], ['strip_whitespace']).filter(conditions)
|
||||
conditions = Devise::ParameterFilter.new([], ['strip_whitespace']).filter(conditions)
|
||||
assert_equal( {'strip_whitespace' => 'strip_whitespace_val', 'do_not_strip_whitespace' => ' do_not_strip_whitespace_val '}, conditions )
|
||||
end
|
||||
|
||||
|
||||
@@ -185,12 +185,12 @@ class LockableTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test 'should require all unlock_keys' do
|
||||
swap Devise, :unlock_keys => [:username, :email] do
|
||||
user = create_user
|
||||
unlock_user = User.send_unlock_instructions(:email => user.email)
|
||||
assert_not unlock_user.persisted?
|
||||
assert_equal "can't be blank", unlock_user.errors[:username].join
|
||||
end
|
||||
swap Devise, :unlock_keys => [:username, :email] do
|
||||
user = create_user
|
||||
unlock_user = User.send_unlock_instructions(:email => user.email)
|
||||
assert_not unlock_user.persisted?
|
||||
assert_equal "can't be blank", unlock_user.errors[:username].join
|
||||
end
|
||||
end
|
||||
|
||||
test 'should not be able to send instructions if the user is not locked' do
|
||||
@@ -200,6 +200,15 @@ class LockableTest < ActiveSupport::TestCase
|
||||
assert_equal 'was not locked', user.errors[:email].join
|
||||
end
|
||||
|
||||
test 'should not be able to send instructions if the user if not locked and have username as unlock key' do
|
||||
swap Devise, :unlock_keys => [:username] do
|
||||
user = create_user
|
||||
assert_not user.resend_unlock_token
|
||||
assert_not user.access_locked?
|
||||
assert_equal 'was not locked', user.errors[:username].join
|
||||
end
|
||||
end
|
||||
|
||||
test 'should unlock account if lock has expired and increase attempts on failure' do
|
||||
swap Devise, :unlock_in => 1.minute do
|
||||
user = create_user
|
||||
|
||||
@@ -110,7 +110,7 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
|
||||
test 'should find a user to reset his password based on reset_password_token' do
|
||||
user = create_user
|
||||
user.send :generate_reset_password_token!
|
||||
user.ensure_reset_password_token!
|
||||
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token)
|
||||
assert_equal reset_password_user, user
|
||||
@@ -130,7 +130,7 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
|
||||
test 'should return a new record with errors if password is blank' do
|
||||
user = create_user
|
||||
user.send :generate_reset_password_token!
|
||||
user.ensure_reset_password_token!
|
||||
|
||||
reset_password_user = User.reset_password_by_token(:reset_password_token => user.reset_password_token, :password => '')
|
||||
assert_not reset_password_user.errors.empty?
|
||||
@@ -140,7 +140,7 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
test 'should reset successfully user password given the new password and confirmation' do
|
||||
user = create_user
|
||||
old_password = user.password
|
||||
user.send :generate_reset_password_token!
|
||||
user.ensure_reset_password_token!
|
||||
|
||||
User.reset_password_by_token(
|
||||
:reset_password_token => user.reset_password_token,
|
||||
@@ -179,7 +179,7 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
swap Devise, :reset_password_within => 1.hour do
|
||||
user = create_user
|
||||
old_password = user.password
|
||||
user.send :generate_reset_password_token!
|
||||
user.ensure_reset_password_token!
|
||||
user.reset_password_sent_at = 2.days.ago
|
||||
user.save!
|
||||
|
||||
@@ -202,4 +202,21 @@ class RecoverableTest < ActiveSupport::TestCase
|
||||
:reset_password_token
|
||||
]
|
||||
end
|
||||
|
||||
test 'should generate a new token when a valid one does not exist' do
|
||||
user = create_user
|
||||
assert_nil user.reset_password_token
|
||||
|
||||
user.ensure_reset_password_token!
|
||||
assert_not_nil user.reset_password_token
|
||||
end
|
||||
|
||||
test 'should not generate a new token when a valid one exists' do
|
||||
user = create_user
|
||||
user.send :generate_reset_password_token!
|
||||
assert_not_nil user.reset_password_token
|
||||
old = user.reset_password_token
|
||||
user.ensure_reset_password_token!
|
||||
assert_equal user.reset_password_token, old
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,4 +43,9 @@ class TimeoutableTest < ActiveSupport::TestCase
|
||||
test 'required_fields should contain the fields that Devise uses' do
|
||||
assert_same_content Devise::Models::Timeoutable.required_fields(User), []
|
||||
end
|
||||
|
||||
test 'should not raise error if remember_created_at is not empty and rememberable is disabled' do
|
||||
user = create_admin(remember_created_at: Time.current)
|
||||
assert user.timedout?(31.minutes.ago)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,8 +21,15 @@ if defined?(ActionController::StrongParameters)
|
||||
end
|
||||
|
||||
test 'filters some parameters on sign in by default' do
|
||||
sanitizer = sanitizer(user: { "email" => "jose", "password" => "invalid" })
|
||||
assert_equal({ "email" => "jose" }, sanitizer.for(:sign_in))
|
||||
sanitizer = sanitizer(user: { "email" => "jose", "password" => "invalid", "remember_me" => "1" })
|
||||
assert_equal({ "email" => "jose", "password" => "invalid", "remember_me" => "1" }, sanitizer.for(:sign_in))
|
||||
end
|
||||
|
||||
test 'handles auth keys as a hash' do
|
||||
swap Devise, :authentication_keys => {:email => true} do
|
||||
sanitizer = sanitizer(user: { "email" => "jose", "password" => "invalid" })
|
||||
assert_equal({ "email" => "jose", "password" => "invalid" }, sanitizer.for(:sign_in))
|
||||
end
|
||||
end
|
||||
|
||||
test 'filters some parameters on sign up by default' do
|
||||
|
||||
@@ -8,6 +8,14 @@ class UsersController < ApplicationController
|
||||
respond_with(current_user)
|
||||
end
|
||||
|
||||
def edit_form
|
||||
user_session['last_request_at'] = 31.minutes.ago.utc
|
||||
end
|
||||
|
||||
def update_form
|
||||
render :text => 'Update'
|
||||
end
|
||||
|
||||
def accept
|
||||
@current_user = current_user
|
||||
end
|
||||
|
||||
1
test/rails_app/app/views/users/edit_form.html.erb
Normal file
1
test/rails_app/app/views/users/edit_form.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= button_to 'Update', update_form_user_path(current_user), method: 'put' %>
|
||||
@@ -1,8 +1,12 @@
|
||||
Rails.application.routes.draw do
|
||||
# Resources for testing
|
||||
resources :users, :only => [:index] do
|
||||
get :expire, :on => :member
|
||||
get :accept, :on => :member
|
||||
member do
|
||||
get :expire
|
||||
get :accept
|
||||
get :edit_form
|
||||
put :update_form
|
||||
end
|
||||
|
||||
authenticate do
|
||||
post :exhibit, :on => :member
|
||||
|
||||
@@ -63,7 +63,7 @@ class ActiveSupport::TestCase
|
||||
def clear_cached_variables(options)
|
||||
if options.key?(:case_insensitive_keys) || options.key?(:strip_whitespace_keys)
|
||||
Devise.mappings.each do |_, mapping|
|
||||
mapping.to.instance_variable_set(:@devise_param_filter, nil)
|
||||
mapping.to.instance_variable_set(:@devise_parameter_filter, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user