├── test
├── rails_app
│ ├── public
│ │ ├── favicon.ico
│ │ ├── 422.html
│ │ ├── 404.html
│ │ └── 500.html
│ ├── app
│ │ ├── views
│ │ │ ├── home
│ │ │ │ ├── join.html.erb
│ │ │ │ ├── index.html.erb
│ │ │ │ ├── private.html.erb
│ │ │ │ ├── user_dashboard.html.erb
│ │ │ │ └── admin_dashboard.html.erb
│ │ │ ├── admins
│ │ │ │ ├── index.html.erb
│ │ │ │ └── sessions
│ │ │ │ │ └── new.html.erb
│ │ │ ├── users
│ │ │ │ ├── sessions
│ │ │ │ │ └── new.html.erb
│ │ │ │ ├── index.html.erb
│ │ │ │ └── mailer
│ │ │ │ │ └── confirmation_instructions.erb
│ │ │ └── layouts
│ │ │ │ └── application.html.erb
│ │ ├── active_record
│ │ │ ├── shim.rb
│ │ │ ├── admin.rb
│ │ │ └── user.rb
│ │ ├── controllers
│ │ │ ├── publisher
│ │ │ │ ├── sessions_controller.rb
│ │ │ │ └── registrations_controller.rb
│ │ │ ├── admins
│ │ │ │ └── sessions_controller.rb
│ │ │ ├── admins_controller.rb
│ │ │ ├── application_controller.rb
│ │ │ ├── home_controller.rb
│ │ │ ├── users
│ │ │ │ └── omniauth_callbacks_controller.rb
│ │ │ └── users_controller.rb
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── mailers
│ │ │ └── users
│ │ │ │ └── mailer.rb
│ │ └── mongoid
│ │ │ ├── shim.rb
│ │ │ ├── admin.rb
│ │ │ └── user.rb
│ ├── config
│ │ ├── initializers
│ │ │ ├── inflections.rb
│ │ │ ├── secret_token.rb
│ │ │ └── backtrace_silencers.rb
│ │ ├── environment.rb
│ │ ├── boot.rb
│ │ ├── database.yml
│ │ ├── environments
│ │ │ ├── development.rb
│ │ │ ├── production.rb
│ │ │ └── test.rb
│ │ ├── application.rb
│ │ └── routes.rb
│ ├── config.ru
│ ├── Rakefile
│ ├── script
│ │ └── rails
│ ├── lib
│ │ ├── shared_admin.rb
│ │ └── shared_user.rb
│ └── db
│ │ ├── migrate
│ │ └── 20100401102949_create_tables.rb
│ │ └── schema.rb
├── support
│ ├── locale
│ │ └── en.yml
│ ├── webrat
│ │ └── integrations
│ │ │ └── rails.rb
│ ├── assertions.rb
│ ├── helpers.rb
│ └── integration.rb
├── models
│ ├── omniauthable_test.rb
│ ├── registerable_test.rb
│ ├── trackable_test.rb
│ ├── authenticatable_test.rb
│ ├── timeoutable_test.rb
│ ├── serializable_test.rb
│ ├── token_authenticatable_test.rb
│ └── validatable_test.rb
├── orm
│ ├── mongoid.rb
│ └── active_record.rb
├── generators
│ ├── install_generator_test.rb
│ ├── mongoid_generator_test.rb
│ ├── devise_generator_test.rb
│ ├── views_generator_test.rb
│ └── active_record_generator_test.rb
├── delegator_test.rb
├── test_helper.rb
├── test_models.rb
├── helpers
│ └── devise_helper_test.rb
├── omniauth
│ ├── url_helpers_test.rb
│ └── config_test.rb
├── controllers
│ ├── custom_strategy_test.rb
│ ├── url_helpers_test.rb
│ ├── sessions_controller_test.rb
│ └── internal_helpers_test.rb
├── mailers
│ ├── unlock_instructions_test.rb
│ ├── reset_password_instructions_test.rb
│ └── confirmation_instructions_test.rb
├── integration
│ ├── trackable_test.rb
│ ├── database_authenticatable_test.rb
│ ├── timeoutable_test.rb
│ └── omniauthable_test.rb
├── devise_test.rb
└── test_helpers_test.rb
├── devise.png
├── lib
├── devise
│ ├── version.rb
│ ├── orm
│ │ ├── active_record.rb
│ │ └── mongoid.rb
│ ├── hooks
│ │ ├── rememberable.rb
│ │ ├── lockable.rb
│ │ ├── forgetable.rb
│ │ ├── trackable.rb
│ │ ├── activatable.rb
│ │ └── timeoutable.rb
│ ├── rails
│ │ └── warden_compat.rb
│ ├── time_inflector.rb
│ ├── controllers
│ │ ├── scoped_views.rb
│ │ ├── rememberable.rb
│ │ └── url_helpers.rb
│ ├── delegator.rb
│ ├── omniauth
│ │ ├── url_helpers.rb
│ │ └── config.rb
│ ├── strategies
│ │ ├── base.rb
│ │ ├── database_authenticatable.rb
│ │ ├── rememberable.rb
│ │ └── token_authenticatable.rb
│ ├── models
│ │ ├── omniauthable.rb
│ │ ├── registerable.rb
│ │ ├── timeoutable.rb
│ │ ├── trackable.rb
│ │ ├── validatable.rb
│ │ └── token_authenticatable.rb
│ ├── omniauth.rb
│ ├── param_filter.rb
│ ├── modules.rb
│ ├── rails.rb
│ └── mailers
│ │ └── helpers.rb
└── generators
│ ├── templates
│ ├── markerb
│ │ ├── confirmation_instructions.markerb
│ │ ├── unlock_instructions.markerb
│ │ └── reset_password_instructions.markerb
│ ├── simple_form_for
│ │ ├── passwords
│ │ │ ├── new.html.erb
│ │ │ └── edit.html.erb
│ │ ├── sessions
│ │ │ └── new.html.erb
│ │ ├── unlocks
│ │ │ └── new.html.erb
│ │ ├── registrations
│ │ │ ├── new.html.erb
│ │ │ └── edit.html.erb
│ │ └── confirmations
│ │ │ └── new.html.erb
│ └── README
│ ├── devise
│ ├── install_generator.rb
│ ├── devise_generator.rb
│ ├── orm_helpers.rb
│ └── views_generator.rb
│ ├── active_record
│ ├── templates
│ │ ├── migration.rb
│ │ └── migration_existing.rb
│ └── devise_generator.rb
│ └── mongoid
│ └── devise_generator.rb
├── .gitignore
├── app
├── views
│ └── devise
│ │ ├── _links.erb
│ │ ├── mailer
│ │ ├── confirmation_instructions.html.erb
│ │ ├── unlock_instructions.html.erb
│ │ └── reset_password_instructions.html.erb
│ │ ├── unlocks
│ │ └── new.html.erb
│ │ ├── passwords
│ │ ├── new.html.erb
│ │ └── edit.html.erb
│ │ ├── confirmations
│ │ └── new.html.erb
│ │ ├── sessions
│ │ └── new.html.erb
│ │ ├── registrations
│ │ ├── new.html.erb
│ │ └── edit.html.erb
│ │ └── shared
│ │ └── _links.erb
├── mailers
│ └── devise
│ │ └── mailer.rb
├── helpers
│ └── devise_helper.rb
└── controllers
│ └── devise
│ ├── omniauth_callbacks_controller.rb
│ ├── unlocks_controller.rb
│ ├── confirmations_controller.rb
│ ├── sessions_controller.rb
│ ├── passwords_controller.rb
│ └── registrations_controller.rb
├── Gemfile
├── gemfiles
├── Gemfile.rails-3.1.x
└── Gemfile.rails-3.1.x.lock
├── .travis.yml
├── devise.gemspec
├── CONTRIBUTING.md
├── Rakefile
├── MIT-LICENSE
├── config
└── locales
│ └── en.yml
└── Gemfile.lock
/test/rails_app/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/home/join.html.erb:
--------------------------------------------------------------------------------
1 | Join
--------------------------------------------------------------------------------
/test/rails_app/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | Home!
--------------------------------------------------------------------------------
/test/rails_app/app/active_record/shim.rb:
--------------------------------------------------------------------------------
1 | module Shim
2 | end
--------------------------------------------------------------------------------
/test/rails_app/app/views/home/private.html.erb:
--------------------------------------------------------------------------------
1 | Private!
2 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/admins/index.html.erb:
--------------------------------------------------------------------------------
1 | Welcome Admin!
2 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/home/user_dashboard.html.erb:
--------------------------------------------------------------------------------
1 | User dashboard
--------------------------------------------------------------------------------
/devise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snitko/devise/master/devise.png
--------------------------------------------------------------------------------
/test/rails_app/app/views/home/admin_dashboard.html.erb:
--------------------------------------------------------------------------------
1 | Admin dashboard
--------------------------------------------------------------------------------
/lib/devise/version.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | VERSION = "2.2.3".freeze
3 | end
4 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/users/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | Special user view
2 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/users/index.html.erb:
--------------------------------------------------------------------------------
1 | Welcome User #<%= current_user.id %>!
2 |
--------------------------------------------------------------------------------
/test/rails_app/app/views/users/mailer/confirmation_instructions.erb:
--------------------------------------------------------------------------------
1 | <%= @resource.email %>
--------------------------------------------------------------------------------
/test/support/locale/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | errors:
3 | messages:
4 | taken: "has already been taken"
5 |
--------------------------------------------------------------------------------
/test/rails_app/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | ActiveSupport::Inflector.inflections do |inflect|
2 | end
3 |
--------------------------------------------------------------------------------
/lib/devise/orm/active_record.rb:
--------------------------------------------------------------------------------
1 | require 'orm_adapter/adapters/active_record'
2 |
3 | ActiveRecord::Base.extend Devise::Models
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/publisher/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class Publisher::SessionsController < ApplicationController
2 | end
--------------------------------------------------------------------------------
/test/rails_app/app/views/admins/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | Welcome to "sessions/new" view!
2 | <%= render :file => "devise/sessions/new" %>
--------------------------------------------------------------------------------
/lib/devise/orm/mongoid.rb:
--------------------------------------------------------------------------------
1 | require 'orm_adapter/adapters/mongoid'
2 |
3 | Mongoid::Document::ClassMethods.send :include, Devise::Models
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/publisher/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | class Publisher::RegistrationsController < ApplicationController
2 | end
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test/rails_app/log/*
2 | test/rails_app/tmp/*
3 | *~
4 | coverage/*
5 | *.sqlite3
6 | .bundle
7 | rdoc/*
8 | pkg
9 | log
10 | test/tmp/*
11 |
--------------------------------------------------------------------------------
/test/rails_app/app/active_record/admin.rb:
--------------------------------------------------------------------------------
1 | require 'shared_admin'
2 |
3 | class Admin < ActiveRecord::Base
4 | include Shim
5 | include SharedAdmin
6 | end
7 |
--------------------------------------------------------------------------------
/test/rails_app/app/active_record/user.rb:
--------------------------------------------------------------------------------
1 | require 'shared_user'
2 |
3 | class User < ActiveRecord::Base
4 | include Shim
5 | include SharedUser
6 | end
7 |
--------------------------------------------------------------------------------
/test/rails_app/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | # Methods added to this helper will be available to all templates in the application.
2 | module ApplicationHelper
3 | end
4 |
--------------------------------------------------------------------------------
/test/rails_app/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require ::File.expand_path('../config/environment', __FILE__)
4 | run RailsApp::Application
5 |
--------------------------------------------------------------------------------
/test/rails_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the rails application
2 | require File.expand_path('../application', __FILE__)
3 |
4 | # Initialize the rails application
5 | RailsApp::Application.initialize!
6 |
--------------------------------------------------------------------------------
/app/views/devise/_links.erb:
--------------------------------------------------------------------------------
1 | <% ActiveSupport::Deprecation.warn "Rendering partials devise/_links.erb is deprecated" \
2 | "please use devise/shared/_links.erb instead."%>
3 | <%= render "shared/links" %>
4 |
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/admins/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class Admins::SessionsController < Devise::SessionsController
2 | def new
3 | flash[:special] = "Welcome to #{controller_path.inspect} controller!"
4 | super
5 | end
6 | end
--------------------------------------------------------------------------------
/test/rails_app/config/boot.rb:
--------------------------------------------------------------------------------
1 | unless defined?(DEVISE_ORM)
2 | DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
3 | end
4 |
5 | require 'rubygems'
6 | require 'bundler/setup'
7 |
8 | $:.unshift File.expand_path('../../../../lib', __FILE__)
--------------------------------------------------------------------------------
/lib/generators/templates/markerb/confirmation_instructions.markerb:
--------------------------------------------------------------------------------
1 | Welcome <%= @email %>!
2 |
3 | You can confirm your account through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
6 |
--------------------------------------------------------------------------------
/test/models/omniauthable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class OmniauthableTest < ActiveSupport::TestCase
4 | test 'required_fields should contain the fields that Devise uses' do
5 | assert_same_content Devise::Models::Omniauthable.required_fields(User), []
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/registerable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class RegisterableTest < ActiveSupport::TestCase
4 | test 'required_fields should contain the fields that Devise uses' do
5 | assert_same_content Devise::Models::Registerable.required_fields(User), []
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/rails_app/config/initializers/secret_token.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.secret_token = 'ea942c41850d502f2c8283e26bdc57829f471bb18224ddff0a192c4f32cdf6cb5aa0d82b3a7a7adbeb640c4b06f3aa1cd5f098162d8240f669b39d6b49680571'
2 | Rails.application.config.session_store :cookie_store, :key => "_my_app"
--------------------------------------------------------------------------------
/app/views/devise/mailer/confirmation_instructions.html.erb:
--------------------------------------------------------------------------------
1 |
Welcome <%= @email %>!
2 |
3 | You can confirm your account email through the link below:
4 |
5 | <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
6 |
--------------------------------------------------------------------------------
/test/rails_app/app/mailers/users/mailer.rb:
--------------------------------------------------------------------------------
1 | class Users::Mailer < Devise::Mailer
2 | default :from => 'custom@example.com'
3 | end
4 |
5 | class Users::ReplyToMailer < Devise::Mailer
6 | default :from => 'custom@example.com'
7 | default :reply_to => 'custom_reply_to@example.com'
8 | end
--------------------------------------------------------------------------------
/test/orm/mongoid.rb:
--------------------------------------------------------------------------------
1 | require 'mongoid/version'
2 |
3 | Mongoid.configure do |config|
4 | config.connect_to("devise-test-suite")
5 | config.use_utc = true
6 | config.include_root_in_json = true
7 | end
8 |
9 | class ActiveSupport::TestCase
10 | setup do
11 | Mongoid.purge!
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/devise/hooks/rememberable.rb:
--------------------------------------------------------------------------------
1 | Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
2 | scope = options[:scope]
3 | if record.respond_to?(:remember_me) && record.remember_me && warden.authenticated?(scope)
4 | Devise::Controllers::Rememberable::Proxy.new(warden).remember_me(record)
5 | end
6 | end
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/admins_controller.rb:
--------------------------------------------------------------------------------
1 | class AdminsController < ApplicationController
2 | before_filter :authenticate_admin!
3 |
4 | def index
5 | end
6 |
7 | def expire
8 | admin_session['last_request_at'] = 31.minutes.ago.utc
9 | render :text => 'Admin will be expired on next request'
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/devise/rails/warden_compat.rb:
--------------------------------------------------------------------------------
1 | module Warden::Mixins::Common
2 | def request
3 | @request ||= ActionDispatch::Request.new(env)
4 | end
5 |
6 | # This is called internally by Warden on logout
7 | def reset_session!
8 | request.reset_session
9 | end
10 |
11 | def cookies
12 | request.cookie_jar
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/generators/templates/markerb/unlock_instructions.markerb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | Click the link below to unlock your account:
6 |
7 | <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
8 |
--------------------------------------------------------------------------------
/lib/devise/time_inflector.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/module/delegation"
2 |
3 | module Devise
4 | class TimeInflector
5 | include ActionView::Helpers::DateHelper
6 |
7 | class << self
8 | attr_reader :instance
9 | delegate :time_ago_in_words, :to => :instance
10 | end
11 |
12 | @instance = new
13 | end
14 | end
--------------------------------------------------------------------------------
/test/orm/active_record.rb:
--------------------------------------------------------------------------------
1 | ActiveRecord::Migration.verbose = false
2 | ActiveRecord::Base.logger = Logger.new(nil)
3 |
4 | ActiveRecord::Migrator.migrate(File.expand_path("../../rails_app/db/migrate/", __FILE__))
5 |
6 | class ActiveSupport::TestCase
7 | self.use_transactional_fixtures = true
8 | self.use_instantiated_fixtures = false
9 | end
10 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/unlock_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 |
5 | Click the link below to unlock your account:
6 |
7 | <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
8 |
--------------------------------------------------------------------------------
/test/rails_app/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('../config/application', __FILE__)
5 |
6 | require 'rake'
7 | require 'rake/testtask'
8 | require 'rake/rdoctask'
9 |
10 | Rails.application.load_tasks
11 |
--------------------------------------------------------------------------------
/lib/devise/hooks/lockable.rb:
--------------------------------------------------------------------------------
1 | # After each sign in, if resource responds to failed_attempts, sets it to 0
2 | # This is only triggered when the user is explicitly set (with set_user)
3 | Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
4 | if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
5 | record.update_attribute(:failed_attempts, 0)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/trackable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TrackableTest < ActiveSupport::TestCase
4 | test 'required_fields should contain the fields that Devise uses' do
5 | assert_same_content Devise::Models::Trackable.required_fields(User), [
6 | :current_sign_in_at,
7 | :current_sign_in_ip,
8 | :last_sign_in_at,
9 | :last_sign_in_ip,
10 | :sign_in_count
11 | ]
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/devise/controllers/scoped_views.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Controllers
3 | module ScopedViews
4 | extend ActiveSupport::Concern
5 |
6 | module ClassMethods
7 | def scoped_views?
8 | defined?(@scoped_views) ? @scoped_views : Devise.scoped_views
9 | end
10 |
11 | def scoped_views=(value)
12 | @scoped_views = value
13 | end
14 | end
15 | end
16 | end
17 | end
--------------------------------------------------------------------------------
/app/views/devise/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend unlock instructions
2 |
3 | <%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= devise_error_messages! %>
5 |
6 | <%= f.label :email %>
7 | <%= f.email_field :email, :autofocus => true %>
8 |
9 | <%= f.submit "Resend unlock instructions" %>
10 | <% end %>
11 |
12 | <%= render "devise/shared/links" %>
13 |
--------------------------------------------------------------------------------
/test/rails_app/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/lib/generators/templates/markerb/reset_password_instructions.markerb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password, and you can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/test/generators/install_generator_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class InstallGeneratorTest < Rails::Generators::TestCase
4 | tests Devise::Generators::InstallGenerator
5 | destination File.expand_path("../../tmp", __FILE__)
6 | setup :prepare_destination
7 |
8 | test "Assert all files are properly created" do
9 | run_generator
10 | assert_file "config/initializers/devise.rb"
11 | assert_file "config/locales/devise.en.yml"
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 | Forgot your password?
2 |
3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= devise_error_messages! %>
5 |
6 | <%= f.label :email %>
7 | <%= f.email_field :email, :autofocus => true %>
8 |
9 | <%= f.submit "Send me reset password instructions" %>
10 | <% end %>
11 |
12 | <%= render "devise/shared/links" %>
13 |
--------------------------------------------------------------------------------
/lib/devise/delegator.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | # Checks the scope in the given environment and returns the associated failure app.
3 | class Delegator
4 | def call(env)
5 | failure_app(env).call(env)
6 | end
7 |
8 | def failure_app(env)
9 | app = env["warden.options"] &&
10 | (scope = env["warden.options"][:scope]) &&
11 | Devise.mappings[scope.to_sym].failure_app
12 |
13 | app || Devise::FailureApp
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | # Filters added to this controller apply to all controllers in the application.
2 | # Likewise, all the methods added will be available for all controllers.
3 |
4 | class ApplicationController < ActionController::Base
5 | protect_from_forgery
6 | before_filter :current_user, :unless => :devise_controller?
7 | before_filter :authenticate_user!, :if => :devise_controller?
8 | respond_to *Mime::SET.map(&:to_sym)
9 | end
10 |
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def index
3 | end
4 |
5 | def private
6 | end
7 |
8 | def user_dashboard
9 | end
10 |
11 | def admin_dashboard
12 | end
13 |
14 | def join
15 | end
16 |
17 | def set
18 | session["devise.foo_bar"] = "something"
19 | head :ok
20 | end
21 |
22 | def unauthenticated
23 | render :text => "unauthenticated", :status => :unauthorized
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
2 | def facebook
3 | data = env["omniauth.auth"]
4 | session["devise.facebook_data"] = data["extra"]["user_hash"]
5 | render :json => data
6 | end
7 |
8 | def sign_in_facebook
9 | user = User.find_by_email('user@test.com')
10 | user.remember_me = true
11 | sign_in user
12 | render :text => ""
13 | end
14 | end
--------------------------------------------------------------------------------
/app/views/devise/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= devise_error_messages! %>
5 |
6 | <%= f.label :email %>
7 | <%= f.email_field :email, :autofocus => true %>
8 |
9 | <%= f.submit "Resend confirmation instructions" %>
10 | <% end %>
11 |
12 | <%= render "devise/shared/links" %>
13 |
--------------------------------------------------------------------------------
/app/views/devise/mailer/reset_password_instructions.html.erb:
--------------------------------------------------------------------------------
1 | Hello <%= @resource.email %>!
2 |
3 | Someone has requested a link to change your password. You can do this through the link below.
4 |
5 | <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
6 |
7 | If you didn't request this, please ignore this email.
8 | Your password won't change until you access the link above and create a new one.
9 |
--------------------------------------------------------------------------------
/test/rails_app/script/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3 |
4 | ENV_PATH = File.expand_path('../../config/environment', __FILE__)
5 | BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
6 | APP_PATH = File.expand_path('../../config/application', __FILE__)
7 | ROOT_PATH = File.expand_path('../..', __FILE__)
8 |
9 | require BOOT_PATH
10 | require 'rails/commands'
11 |
--------------------------------------------------------------------------------
/app/mailers/devise/mailer.rb:
--------------------------------------------------------------------------------
1 | class Devise::Mailer < Devise.parent_mailer.constantize
2 | include Devise::Mailers::Helpers
3 |
4 | def confirmation_instructions(record, opts={})
5 | devise_mail(record, :confirmation_instructions, opts)
6 | end
7 |
8 | def reset_password_instructions(record, opts={})
9 | devise_mail(record, :reset_password_instructions, opts)
10 | end
11 |
12 | def unlock_instructions(record, opts={})
13 | devise_mail(record, :unlock_instructions, opts)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/devise/hooks/forgetable.rb:
--------------------------------------------------------------------------------
1 | # Before logout hook to forget the user in the given scope, if it responds
2 | # to forget_me! Also clear remember token to ensure the user won't be
3 | # remembered again. Notice that we forget the user unless the record is not persisted.
4 | # This avoids forgetting deleted users.
5 | Warden::Manager.before_logout do |record, warden, options|
6 | if record.respond_to?(:forget_me!)
7 | Devise::Controllers::Rememberable::Proxy.new(warden).forget_me(record)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/devise/hooks/trackable.rb:
--------------------------------------------------------------------------------
1 | # After each sign in, update sign in time, sign in count and sign in IP.
2 | # This is only triggered when the user is explicitly set (with set_user)
3 | # and on authentication. Retrieving the user from session (:fetch) does
4 | # not trigger it.
5 | Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
6 | if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && !warden.request.env['devise.skip_trackable']
7 | record.update_tracked_fields!(warden.request)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/passwords/new.html.erb:
--------------------------------------------------------------------------------
1 | Forgot your password?
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 |
7 | <%= f.input :email, :required => true, :autofocus => true %>
8 |
9 |
10 |
11 | <%= f.button :submit, "Send me reset password instructions" %>
12 |
13 | <% end %>
14 |
15 | <%= render "devise/shared/links" %>
16 |
--------------------------------------------------------------------------------
/test/rails_app/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3-ruby (not necessary on OS X Leopard)
3 | development:
4 | adapter: sqlite3
5 | database: db/development.sqlite3
6 | pool: 5
7 | timeout: 5000
8 |
9 | # Warning: The database defined as "test" will be erased and
10 | # re-generated from your development database when you run "rake".
11 | # Do not set this db to the same as development or production.
12 | test:
13 | adapter: sqlite3
14 | database: ":memory:"
15 |
16 | production:
17 | adapter: sqlite3
18 | database: ":memory:"
19 |
--------------------------------------------------------------------------------
/test/rails_app/lib/shared_admin.rb:
--------------------------------------------------------------------------------
1 | module SharedAdmin
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | devise :database_authenticatable, :registerable,
6 | :timeoutable, :recoverable, :lockable, :confirmable,
7 | :unlock_strategy => :time, :lock_strategy => :none,
8 | :allow_unconfirmed_access_for => 2.weeks, :reconfirmable => true
9 |
10 | validates_length_of :reset_password_token, :minimum => 3, :allow_blank => true
11 | validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?
12 | end
13 |
14 | end
15 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | Sign in
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
4 |
5 | <%= f.input :email, :required => false, :autofocus => true %>
6 | <%= f.input :password, :required => false %>
7 | <%= f.input :remember_me, :as => :boolean if devise_mapping.rememberable? %>
8 |
9 |
10 |
11 | <%= f.button :submit, "Sign in" %>
12 |
13 | <% end %>
14 |
15 | <%= render "devise/shared/links" %>
16 |
--------------------------------------------------------------------------------
/app/views/devise/sessions/new.html.erb:
--------------------------------------------------------------------------------
1 | Sign in
2 |
3 | <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
4 | <%= f.label :email %>
5 | <%= f.email_field :email, :autofocus => true %>
6 |
7 | <%= f.label :password %>
8 | <%= f.password_field :password %>
9 |
10 | <% if devise_mapping.rememberable? -%>
11 | <%= f.check_box :remember_me %> <%= f.label :remember_me %>
12 | <% end -%>
13 |
14 | <%= f.submit "Sign in" %>
15 | <% end %>
16 |
17 | <%= render "devise/shared/links" %>
18 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/unlocks/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend unlock instructions
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= f.error_notification %>
5 | <%= f.full_error :unlock_token %>
6 |
7 |
8 | <%= f.input :email, :required => true, :autofocus => true %>
9 |
10 |
11 |
12 | <%= f.button :submit, "Resend unlock instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/lib/devise/omniauth/url_helpers.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module OmniAuth
3 | module UrlHelpers
4 | def self.define_helpers(mapping)
5 | end
6 |
7 | def omniauth_authorize_path(resource_or_scope, *args)
8 | scope = Devise::Mapping.find_scope!(resource_or_scope)
9 | send("#{scope}_omniauth_authorize_path", *args)
10 | end
11 |
12 | def omniauth_callback_path(resource_or_scope, *args)
13 | scope = Devise::Mapping.find_scope!(resource_or_scope)
14 | send("#{scope}_omniauth_callback_path", *args)
15 | end
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 | Sign up
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
4 | <%= f.error_notification %>
5 |
6 |
7 | <%= f.input :email, :required => true, :autofocus => true %>
8 | <%= f.input :password, :required => true %>
9 | <%= f.input :password_confirmation, :required => true %>
10 |
11 |
12 |
13 | <%= f.button :submit, "Sign up" %>
14 |
15 | <% end %>
16 |
17 | <%= render "devise/shared/links" %>
18 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/new.html.erb:
--------------------------------------------------------------------------------
1 | Sign up
2 |
3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
4 | <%= devise_error_messages! %>
5 |
6 | <%= f.label :email %>
7 | <%= f.email_field :email, :autofocus => true %>
8 |
9 | <%= f.label :password %>
10 | <%= f.password_field :password %>
11 |
12 | <%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 |
15 | <%= f.submit "Sign up" %>
16 | <% end %>
17 |
18 | <%= render "devise/shared/links" %>
19 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/confirmations/new.html.erb:
--------------------------------------------------------------------------------
1 | Resend confirmation instructions
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
4 | <%= f.error_notification %>
5 | <%= f.full_error :confirmation_token %>
6 |
7 |
8 | <%= f.input :email, :required => true, :autofocus => true %>
9 |
10 |
11 |
12 | <%= f.button :submit, "Resend confirmation instructions" %>
13 |
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/test/support/webrat/integrations/rails.rb:
--------------------------------------------------------------------------------
1 | require 'webrat/core/elements/form'
2 | require 'action_dispatch/testing/integration'
3 |
4 | module Webrat
5 | Form.class_eval do
6 | def self.parse_rails_request_params(params)
7 | Rack::Utils.parse_nested_query(params)
8 | end
9 | end
10 |
11 | module Logging
12 | # Avoid RAILS_DEFAULT_LOGGER deprecation warning
13 | def logger # :nodoc:
14 | ::Rails.logger
15 | end
16 | end
17 | end
18 |
19 | module ActionDispatch #:nodoc:
20 | IntegrationTest.class_eval do
21 | include Webrat::Methods
22 | include Webrat::Matchers
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/rails_app/app/mongoid/shim.rb:
--------------------------------------------------------------------------------
1 | module Shim
2 | extend ::ActiveSupport::Concern
3 |
4 | included do
5 | include ::Mongoid::Timestamps
6 | field :created_at, :type => DateTime
7 | end
8 |
9 | module ClassMethods
10 | def last(options = {})
11 | options.delete(:order) if options[:order] == "id"
12 | where(options).last
13 | end
14 |
15 | def find_by_email(email)
16 | find_by(:email => email)
17 | end
18 | end
19 |
20 | # overwrite equality (because some devise tests use this for asserting model equality)
21 | def ==(other)
22 | other.is_a?(self.class) && _id == other._id
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/models/authenticatable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AuthenticatableTest < ActiveSupport::TestCase
4 | test 'required_fields should be an empty array' do
5 | assert_equal Devise::Models::Validatable.required_fields(User), []
6 | end
7 |
8 | test 'find_first_by_auth_conditions allows custom filtering parameters' do
9 | user = User.create!(:email => "example@example.com", :password => "123456")
10 | assert_equal User.find_first_by_auth_conditions({ :email => "example@example.com" }), user
11 | assert_nil User.find_first_by_auth_conditions({ :email => "example@example.com" }, :id => user.id.to_s.next)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/views/devise/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
4 | <%= devise_error_messages! %>
5 | <%= f.hidden_field :reset_password_token %>
6 |
7 | <%= f.label :password, "New password" %>
8 | <%= f.password_field :password, :autofocus => true %>
9 |
10 | <%= f.label :password_confirmation, "Confirm new password" %>
11 | <%= f.password_field :password_confirmation %>
12 |
13 | <%= f.submit "Change my password" %>
14 | <% end %>
15 |
16 | <%= render "devise/shared/links" %>
17 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec
4 |
5 | gem "rails", "~> 3.2.6"
6 | gem "omniauth", "~> 1.0.0"
7 | gem "omniauth-oauth2", "~> 1.0.0"
8 | gem "rdoc"
9 |
10 | group :test do
11 | gem "omniauth-facebook"
12 | gem "omniauth-openid", "~> 1.0.1"
13 | gem "webrat", "0.7.2", :require => false
14 | gem "mocha", "0.10.0", :require => false
15 | end
16 |
17 | platforms :jruby do
18 | gem "activerecord-jdbc-adapter"
19 | gem "activerecord-jdbcsqlite3-adapter"
20 | gem "jruby-openssl"
21 | end
22 |
23 | platforms :ruby do
24 | gem "sqlite3"
25 | end
26 |
27 |
28 | platforms :mri_19 do
29 | group :mongoid do
30 | gem "mongoid", "~> 3.0"
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/devise/strategies/base.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Strategies
3 | # Base strategy for Devise. Responsible for verifying correct scope and mapping.
4 | class Base < ::Warden::Strategies::Base
5 | # Whenever CSRF cannot be verified, we turn off any kind of storage
6 | def store?
7 | !env["devise.skip_storage"]
8 | end
9 |
10 | # Checks if a valid scope was given for devise and find mapping based on this scope.
11 | def mapping
12 | @mapping ||= begin
13 | mapping = Devise.mappings[scope]
14 | raise "Could not find mapping for #{scope}" unless mapping
15 | mapping
16 | end
17 | end
18 | end
19 | end
20 | end
--------------------------------------------------------------------------------
/test/rails_app/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | prepend_before_filter :current_user, :only => :exhibit
3 | before_filter :authenticate_user!, :except => [:accept, :exhibit]
4 | respond_to :html, :xml
5 |
6 | def index
7 | user_session[:cart] = "Cart"
8 | respond_with(current_user)
9 | end
10 |
11 | def accept
12 | @current_user = current_user
13 | end
14 |
15 | def exhibit
16 | render :text => current_user ? "User is authenticated" : "User is not authenticated"
17 | end
18 |
19 | def expire
20 | user_session['last_request_at'] = 31.minutes.ago.utc
21 | render :text => 'User will be expired on next request'
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/test/delegator_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class DelegatorTest < ActiveSupport::TestCase
4 | def delegator
5 | Devise::Delegator.new
6 | end
7 |
8 | test 'failure_app returns default failure app if no warden options in env' do
9 | assert_equal Devise::FailureApp, delegator.failure_app({})
10 | end
11 |
12 | test 'failure_app returns default failure app if no scope in warden options' do
13 | assert_equal Devise::FailureApp, delegator.failure_app({"warden.options" => {}})
14 | end
15 |
16 | test 'failure_app returns associated failure app by scope in the given environment' do
17 | assert_kind_of Proc, delegator.failure_app({"warden.options" => {:scope => "manager"}})
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/generators/devise/install_generator.rb:
--------------------------------------------------------------------------------
1 | require 'securerandom'
2 |
3 | module Devise
4 | module Generators
5 | class InstallGenerator < Rails::Generators::Base
6 | source_root File.expand_path("../../templates", __FILE__)
7 |
8 | desc "Creates a Devise initializer and copy locale files to your application."
9 | class_option :orm
10 |
11 | def copy_initializer
12 | template "devise.rb", "config/initializers/devise.rb"
13 | end
14 |
15 | def copy_locale
16 | copy_file "../../../config/locales/en.yml", "config/locales/devise.en.yml"
17 | end
18 |
19 | def show_readme
20 | readme "README" if behavior == :invoke
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/devise/hooks/activatable.rb:
--------------------------------------------------------------------------------
1 | # Deny user access whenever his account is not active yet. All strategies that inherits from
2 | # Devise::Strategies::Authenticatable and uses the validate already check if the user is active_for_authentication?
3 | # before actively signing him in. However, we need this as hook to validate the user activity
4 | # in each request and in case the user is using other strategies beside Devise ones.
5 | Warden::Manager.after_set_user do |record, warden, options|
6 | if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
7 | scope = options[:scope]
8 | warden.logout(scope)
9 | throw :warden, :scope => scope, :message => record.inactive_message
10 | end
11 | end
--------------------------------------------------------------------------------
/test/generators/mongoid_generator_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | if DEVISE_ORM == :mongoid
4 | require "generators/mongoid/devise_generator"
5 |
6 | class MongoidGeneratorTest < Rails::Generators::TestCase
7 | tests Mongoid::Generators::DeviseGenerator
8 | destination File.expand_path("../../tmp", __FILE__)
9 | setup :prepare_destination
10 |
11 | test "all files are properly created" do
12 | run_generator %w(monster)
13 | assert_file "app/models/monster.rb", /devise/
14 | end
15 |
16 | test "all files are properly deleted" do
17 | run_generator %w(monster)
18 | run_generator %w(monster), :behavior => :revoke
19 | assert_no_file "app/models/monster.rb"
20 | end
21 | end
22 | end
23 |
24 |
--------------------------------------------------------------------------------
/lib/devise/models/omniauthable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/omniauth'
2 |
3 | module Devise
4 | module Models
5 | # Adds OmniAuth support to your model.
6 | #
7 | # == Options
8 | #
9 | # Oauthable adds the following options to devise_for:
10 | #
11 | # * +omniauth_providers+: Which providers are avaialble to this model. It expects an array:
12 | #
13 | # devise_for :database_authenticatable, :omniauthable, :omniauth_providers => [:twitter]
14 | #
15 | module Omniauthable
16 | extend ActiveSupport::Concern
17 |
18 | def self.required_fields(klass)
19 | []
20 | end
21 |
22 | module ClassMethods
23 | Devise::Models.config(self, :omniauth_providers)
24 | end
25 | end
26 | end
27 | end
--------------------------------------------------------------------------------
/test/rails_app/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | Devise Test App
6 |
7 |
8 |
9 | <%- flash.each do |name, msg| -%>
10 | <%= content_tag :div, msg, :id => "flash_#{name}" %>
11 | <%- end -%>
12 |
13 | <% if user_signed_in? -%>
14 |
Hello User <%= current_user.email %>! You are signed in!
15 | <% end -%>
16 |
17 | <% if admin_signed_in? -%>
18 |
Hello Admin <%= current_admin.email %>! You are signed in!
19 | <% end -%>
20 |
21 | <%= yield %>
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/lib/generators/active_record/templates/migration.rb:
--------------------------------------------------------------------------------
1 | class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
2 | def change
3 | create_table(:<%= table_name %>) do |t|
4 | <%= migration_data -%>
5 |
6 | <% attributes.each do |attribute| -%>
7 | t.<%= attribute.type %> :<%= attribute.name %>
8 | <% end -%>
9 |
10 | t.timestamps
11 | end
12 |
13 | add_index :<%= table_name %>, :email, :unique => true
14 | add_index :<%= table_name %>, :reset_password_token, :unique => true
15 | # add_index :<%= table_name %>, :confirmation_token, :unique => true
16 | # add_index :<%= table_name %>, :unlock_token, :unique => true
17 | # add_index :<%= table_name %>, :authentication_token, :unique => true
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/passwords/edit.html.erb:
--------------------------------------------------------------------------------
1 | Change your password
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 | <%= f.input :reset_password_token, :as => :hidden %>
7 | <%= f.full_error :reset_password_token %>
8 |
9 |
10 | <%= f.input :password, :label => "New password", :required => true, :autofocus => true %>
11 | <%= f.input :password_confirmation, :label => "Confirm your new password", :required => true %>
12 |
13 |
14 |
15 | <%= f.button :submit, "Change my password" %>
16 |
17 | <% end %>
18 |
19 | <%= render "devise/shared/links" %>
20 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-3.1.x:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | gem "devise", :path => ".."
4 |
5 | gem "rails", "~> 3.1.0"
6 | gem "omniauth", "~> 1.0.0"
7 | gem "omniauth-oauth2", "~> 1.0.0"
8 | gem "rdoc"
9 |
10 | group :test do
11 | gem "omniauth-facebook"
12 | gem "omniauth-openid", "~> 1.0.1"
13 | gem "webrat", "0.7.2", :require => false
14 | gem "mocha", "0.10.0", :require => false
15 |
16 | platforms :mri_18 do
17 | gem "ruby-debug", ">= 0.10.3"
18 | end
19 | end
20 |
21 | platforms :jruby do
22 | gem "activerecord-jdbc-adapter"
23 | gem "activerecord-jdbcsqlite3-adapter"
24 | gem "jruby-openssl"
25 | end
26 |
27 | platforms :ruby do
28 | gem "sqlite3"
29 | end
30 |
31 | platforms :mri_19 do
32 | group :mongoid do
33 | gem "mongoid", "~> 3.0"
34 | end
35 | end
--------------------------------------------------------------------------------
/lib/devise/strategies/database_authenticatable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/strategies/authenticatable'
2 |
3 | module Devise
4 | module Strategies
5 | # Default strategy for signing in a user, based on his email and password in the database.
6 | class DatabaseAuthenticatable < Authenticatable
7 | def authenticate!
8 | resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9 | return fail(:not_found_in_database) unless resource
10 |
11 | if validate(resource){ resource.valid_password?(password) }
12 | resource.after_database_authentication
13 | success!(resource)
14 | end
15 | end
16 | end
17 | end
18 | end
19 |
20 | Warden::Strategies.add(:database_authenticatable, Devise::Strategies::DatabaseAuthenticatable)
21 |
--------------------------------------------------------------------------------
/test/rails_app/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | RailsApp::Application.configure do
2 | # Settings specified here will take precedence over those in config/environment.rb
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the webserver when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Log error messages when you accidentally call methods on nil.
10 | config.whiny_nils = true
11 |
12 | # Show full error reports and disable caching
13 | config.consider_all_requests_local = true
14 | config.action_controller.perform_caching = false
15 |
16 | # Don't care if the mailer can't send
17 | config.action_mailer.raise_delivery_errors = false
18 | end
19 |
--------------------------------------------------------------------------------
/test/rails_app/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The change you wanted was rejected.
23 |
Maybe you tried to change something you didn't have access to.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/devise/models/registerable.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Models
3 | # Registerable is responsible for everything related to registering a new
4 | # resource (ie user sign up).
5 | module Registerable
6 | extend ActiveSupport::Concern
7 |
8 | def self.required_fields(klass)
9 | []
10 | end
11 |
12 | module ClassMethods
13 | # A convenience method that receives both parameters and session to
14 | # initialize a user. This can be used by OAuth, for example, to send
15 | # in the user token and be stored on initialization.
16 | #
17 | # By default discards all information sent by the session by calling
18 | # new with params.
19 | def new_with_session(params, session)
20 | new(params)
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/test/rails_app/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
The page you were looking for doesn't exist.
23 |
You may have mistyped the address or the page may have moved.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/test/rails_app/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
17 |
18 |
19 |
20 |
21 |
22 |
We're sorry, but something went wrong.
23 |
We've been notified about this issue and we'll take a look at it shortly.
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/test/rails_app/app/mongoid/admin.rb:
--------------------------------------------------------------------------------
1 | require 'shared_admin'
2 |
3 | class Admin
4 | include Mongoid::Document
5 | include Shim
6 | include SharedAdmin
7 |
8 | ## Database authenticatable
9 | field :email, :type => String
10 | field :encrypted_password, :type => String
11 |
12 | ## Recoverable
13 | field :reset_password_token, :type => String
14 | field :reset_password_sent_at, :type => Time
15 |
16 | ## Rememberable
17 | field :remember_created_at, :type => Time
18 |
19 | ## Confirmable
20 | field :confirmation_token, :type => String
21 | field :confirmed_at, :type => Time
22 | field :confirmation_sent_at, :type => Time
23 | field :unconfirmed_email, :type => String # Only if using reconfirmable
24 |
25 | ## Lockable
26 | field :locked_at, :type => Time
27 |
28 | field :active, :type => Boolean, :default => false
29 | end
30 |
--------------------------------------------------------------------------------
/test/rails_app/lib/shared_user.rb:
--------------------------------------------------------------------------------
1 | module SharedUser
2 | extend ActiveSupport::Concern
3 |
4 | included do
5 | devise :database_authenticatable, :confirmable, :lockable, :recoverable,
6 | :registerable, :rememberable, :timeoutable, :token_authenticatable,
7 | :trackable, :validatable, :omniauthable
8 |
9 | attr_accessor :other_key
10 | attr_accessible :username, :email, :password, :password_confirmation, :remember_me, :confirmation_sent_at
11 |
12 | # They need to be included after Devise is called.
13 | extend ExtendMethods
14 | end
15 |
16 | module ExtendMethods
17 | def new_with_session(params, session)
18 | super.tap do |user|
19 | if data = session["devise.facebook_data"]
20 | user.email = data["email"]
21 | user.confirmed_at = Time.now
22 | end
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] = "test"
2 | DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
3 |
4 | $:.unshift File.dirname(__FILE__)
5 | puts "\n==> Devise.orm = #{DEVISE_ORM.inspect}"
6 |
7 | require "rails_app/config/environment"
8 | require "rails/test_help"
9 | require "orm/#{DEVISE_ORM}"
10 |
11 | I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__)
12 |
13 | require 'mocha'
14 | require 'webrat'
15 | Webrat.configure do |config|
16 | config.mode = :rails
17 | config.open_error_files = false
18 | end
19 |
20 | # Add support to load paths so we can overwrite broken webrat setup
21 | $:.unshift File.expand_path('../support', __FILE__)
22 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
23 |
24 | # For generators
25 | require "rails/generators/test_case"
26 | require "generators/devise/install_generator"
27 | require "generators/devise/views_generator"
28 |
--------------------------------------------------------------------------------
/lib/generators/devise/devise_generator.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Generators
3 | class DeviseGenerator < Rails::Generators::NamedBase
4 | include Rails::Generators::ResourceHelpers
5 |
6 | namespace "devise"
7 | source_root File.expand_path("../templates", __FILE__)
8 |
9 | desc "Generates a model with the given NAME (if one does not exist) with devise " <<
10 | "configuration plus a migration file and devise routes."
11 |
12 | hook_for :orm
13 |
14 | class_option :routes, :desc => "Generate routes", :type => :boolean, :default => true
15 |
16 | def add_devise_routes
17 | devise_route = "devise_for :#{plural_name}"
18 | devise_route << %Q(, :class_name => "#{class_name}") if class_name.include?("::")
19 | devise_route << %Q(, :skip => :all) unless options.routes?
20 | route devise_route
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/test_models.rb:
--------------------------------------------------------------------------------
1 | class Configurable < User
2 | devise :database_authenticatable, :confirmable, :rememberable, :timeoutable, :lockable,
3 | :stretches => 15, :pepper => 'abcdef', :allow_unconfirmed_access_for => 5.days,
4 | :remember_for => 7.days, :timeout_in => 15.minutes, :unlock_in => 10.days
5 | end
6 |
7 | class WithValidation < Admin
8 | devise :database_authenticatable, :validatable, :password_length => 2..6
9 | end
10 |
11 | class UserWithValidation < User
12 | validates_presence_of :username
13 | end
14 |
15 | class UserWithVirtualAttributes < User
16 | devise :case_insensitive_keys => [ :email, :email_confirmation ]
17 | validates :email, :presence => true, :confirmation => {:on => :create}
18 | attr_accessible :email, :email_confirmation
19 | end
20 |
21 | class Several < Admin
22 | devise :validatable
23 | devise :lockable
24 | end
25 |
26 | class Inheritable < Admin
27 | end
28 |
--------------------------------------------------------------------------------
/app/helpers/devise_helper.rb:
--------------------------------------------------------------------------------
1 | module DeviseHelper
2 | # A simple way to show error messages for the current devise resource. If you need
3 | # to customize this method, you can either overwrite it in your application helpers or
4 | # copy the views to your application.
5 | #
6 | # This method is intended to stay simple and it is unlikely that we are going to change
7 | # it to add more behavior or options.
8 | def devise_error_messages!
9 | return "" if resource.errors.empty?
10 |
11 | messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
12 | sentence = I18n.t("errors.messages.not_saved",
13 | :count => resource.errors.count,
14 | :resource => resource.class.model_name.human.downcase)
15 |
16 | html = <<-HTML
17 |
18 |
#{sentence}
19 |
20 |
21 | HTML
22 |
23 | html.html_safe
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | script: "bundle exec rake test"
3 | rvm:
4 | - 1.8.7
5 | - 1.9.2
6 | - 1.9.3
7 | env:
8 | - DEVISE_ORM=mongoid
9 | - DEVISE_ORM=active_record
10 | matrix:
11 | exclude:
12 | - rvm: 1.8.7
13 | env: DEVISE_ORM=mongoid
14 | gemfile: Gemfile
15 | - rvm: 1.8.7
16 | env: DEVISE_ORM=mongoid
17 | gemfile: gemfiles/Gemfile.rails-3.1.x
18 | - rvm: 1.9.2
19 | env: DEVISE_ORM=mongoid
20 | gemfile: Gemfile
21 | - rvm: 1.9.2
22 | env: DEVISE_ORM=mongoid
23 | gemfile: gemfiles/Gemfile.rails-3.1.x
24 | gemfile:
25 | - gemfiles/Gemfile.rails-3.1.x
26 | - Gemfile
27 | services:
28 | - mongodb
29 | notifications:
30 | email: false
31 | campfire:
32 | on_success: change
33 | on_failure: always
34 | rooms:
35 | - secure: "TRiqvuM4i/QmRDWjUSNitE5/P91BOzDkNl53+bZjjtxcISCswZtmECWBR7n9\n3xwqCOU1o2lfohxZ32OHOj/Nj7o+90zWJfWxcv+if0hIXRiil62M5pg0lZUd\nyJ4M5VQ0lSWo5he1OUrXhSabPJeaK3B8yT/tdh+qO5yzR+vb/jc="
36 |
--------------------------------------------------------------------------------
/devise.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path("../lib", __FILE__)
3 | require "devise/version"
4 |
5 | Gem::Specification.new do |s|
6 | s.name = "devise"
7 | s.version = Devise::VERSION.dup
8 | s.platform = Gem::Platform::RUBY
9 | s.license = "MIT"
10 | s.summary = "Flexible authentication solution for Rails with Warden"
11 | s.email = "contact@plataformatec.com.br"
12 | s.homepage = "http://github.com/plataformatec/devise"
13 | s.description = "Flexible authentication solution for Rails with Warden"
14 | s.authors = ['José Valim', 'Carlos Antônio']
15 |
16 | s.rubyforge_project = "devise"
17 |
18 | s.files = `git ls-files`.split("\n")
19 | s.test_files = `git ls-files -- test/*`.split("\n")
20 | s.require_paths = ["lib"]
21 |
22 | s.add_dependency("warden", "~> 1.2.1")
23 | s.add_dependency("orm_adapter", "~> 0.1")
24 | s.add_dependency("bcrypt-ruby", "~> 3.0")
25 | s.add_dependency("railties", "~> 3.1")
26 | end
27 |
--------------------------------------------------------------------------------
/lib/generators/devise/orm_helpers.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Generators
3 | module OrmHelpers
4 | def model_contents
5 | <<-CONTENT
6 | # Include default devise modules. Others available are:
7 | # :token_authenticatable, :confirmable,
8 | # :lockable, :timeoutable and :omniauthable
9 | devise :database_authenticatable, :registerable,
10 | :recoverable, :rememberable, :trackable, :validatable
11 |
12 | CONTENT
13 | end
14 |
15 | def model_exists?
16 | File.exists?(File.join(destination_root, model_path))
17 | end
18 |
19 | def migration_exists?(table_name)
20 | Dir.glob("#{File.join(destination_root, migration_path)}/[0-9]*_*.rb").grep(/\d+_add_devise_to_#{table_name}.rb$/).first
21 | end
22 |
23 | def migration_path
24 | @migration_path ||= File.join("db", "migrate")
25 | end
26 |
27 | def model_path
28 | @model_path ||= File.join("app", "models", "#{file_path}.rb")
29 | end
30 | end
31 | end
32 | end
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ### Please read before contributing
2 |
3 | 1) Do not post questions in the issues tracker. If you have any questions about Devise, search the [Wiki](https://github.com/plataformatec/devise/wiki) or use the [Mailing List](https://groups.google.com/group/plataformatec-devise) or [Stack Overflow](http://stackoverflow.com/questions/tagged/devise).
4 |
5 | 2) If you find a security bug, **DO NOT** submit an issue here. Please send an e-mail to [developers@plataformatec.com.br](mailto:developers@plataformatec.com.br) instead.
6 |
7 | 3) Do a small search on the issues tracker before submitting your issue to see if it was already reported / fixed.
8 |
9 | 4) When reporting an issue, include Rails, Devise and Warden versions. If you are getting exceptions, please include the full backtrace.
10 |
11 | That's it! The more information you give, the easier it becomes for us to track it down and fix it.
12 | Ideally, you should provide an application that reproduces the error or a test case to Devise's suite.
13 |
14 | Thanks!
15 |
--------------------------------------------------------------------------------
/app/controllers/devise/omniauth_callbacks_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::OmniauthCallbacksController < DeviseController
2 | prepend_before_filter { request.env["devise.skip_timeout"] = true }
3 |
4 | def passthru
5 | render :status => 404, :text => "Not found. Authentication passthru."
6 | end
7 |
8 | def failure
9 | set_flash_message :alert, :failure, :kind => OmniAuth::Utils.camelize(failed_strategy.name), :reason => failure_message
10 | redirect_to after_omniauth_failure_path_for(resource_name)
11 | end
12 |
13 | protected
14 |
15 | def failed_strategy
16 | env["omniauth.error.strategy"]
17 | end
18 |
19 | def failure_message
20 | exception = env["omniauth.error"]
21 | error = exception.error_reason if exception.respond_to?(:error_reason)
22 | error ||= exception.error if exception.respond_to?(:error)
23 | error ||= env["omniauth.error.type"].to_s
24 | error.to_s.humanize if error
25 | end
26 |
27 | def after_omniauth_failure_path_for(scope)
28 | new_session_path(scope)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require "bundler/gem_tasks"
3 | require 'rake/testtask'
4 | require 'rdoc/task'
5 |
6 | desc 'Default: run tests for all ORMs.'
7 | task :default => :test
8 |
9 | desc 'Run Devise tests for all ORMs.'
10 | task :pre_commit do
11 | Dir[File.join(File.dirname(__FILE__), 'test', 'orm', '*.rb')].each do |file|
12 | orm = File.basename(file).split(".").first
13 | # "Some day, my son, rake's inner wisdom will reveal itself. Until then,
14 | # take this `system` -- may its brute force protect you well."
15 | exit 1 unless system "rake test DEVISE_ORM=#{orm}"
16 | end
17 | end
18 |
19 | desc 'Run Devise unit tests.'
20 | Rake::TestTask.new(:test) do |t|
21 | t.libs << 'lib'
22 | t.libs << 'test'
23 | t.pattern = 'test/**/*_test.rb'
24 | t.verbose = true
25 | end
26 |
27 | desc 'Generate documentation for Devise.'
28 | Rake::RDocTask.new(:rdoc) do |rdoc|
29 | rdoc.rdoc_dir = 'rdoc'
30 | rdoc.title = 'Devise'
31 | rdoc.options << '--line-numbers' << '--inline-source'
32 | rdoc.rdoc_files.include('README.md')
33 | rdoc.rdoc_files.include('lib/**/*.rb')
34 | end
35 |
--------------------------------------------------------------------------------
/lib/devise/omniauth.rb:
--------------------------------------------------------------------------------
1 | begin
2 | require "omniauth"
3 | require "omniauth/version"
4 | rescue LoadError
5 | warn "Could not load 'omniauth'. Please ensure you have the omniauth gem >= 1.0.0 installed and listed in your Gemfile."
6 | raise
7 | end
8 |
9 | unless OmniAuth::VERSION =~ /^1\./
10 | raise "You are using an old OmniAuth version, please ensure you have 1.0.0.pr2 version or later installed."
11 | end
12 |
13 | # Clean up the default path_prefix. It will be automatically set by Devise.
14 | OmniAuth.config.path_prefix = nil
15 |
16 | OmniAuth.config.on_failure = Proc.new do |env|
17 | env['devise.mapping'] = Devise::Mapping.find_by_path!(env['PATH_INFO'], :path)
18 | controller_name = ActiveSupport::Inflector.camelize(env['devise.mapping'].controllers[:omniauth_callbacks])
19 | controller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller")
20 | controller_klass.action(:failure).call(env)
21 | end
22 |
23 | module Devise
24 | module OmniAuth
25 | autoload :Config, "devise/omniauth/config"
26 | autoload :UrlHelpers, "devise/omniauth/url_helpers"
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2009-2013 Plataformatec. http://plataformatec.com.br
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lib/generators/active_record/templates/migration_existing.rb:
--------------------------------------------------------------------------------
1 | class AddDeviseTo<%= table_name.camelize %> < ActiveRecord::Migration
2 | def self.up
3 | change_table(:<%= table_name %>) do |t|
4 | <%= migration_data -%>
5 |
6 | <% attributes.each do |attribute| -%>
7 | t.<%= attribute.type %> :<%= attribute.name %>
8 | <% end -%>
9 |
10 | # Uncomment below if timestamps were not included in your original model.
11 | # t.timestamps
12 | end
13 |
14 | add_index :<%= table_name %>, :email, :unique => true
15 | add_index :<%= table_name %>, :reset_password_token, :unique => true
16 | # add_index :<%= table_name %>, :confirmation_token, :unique => true
17 | # add_index :<%= table_name %>, :unlock_token, :unique => true
18 | # add_index :<%= table_name %>, :authentication_token, :unique => true
19 | end
20 |
21 | def self.down
22 | # By default, we don't want to make any assumption about how to roll back a migration when your
23 | # model already existed. Please edit below which fields you would like to remove in this migration.
24 | raise ActiveRecord::IrreversibleMigration
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/devise/hooks/timeoutable.rb:
--------------------------------------------------------------------------------
1 | # Each time a record is set we check whether its session has already timed out
2 | # or not, based on last request time. If so, the record is logged out and
3 | # redirected to the sign in page. Also, each time the request comes and the
4 | # record is set, we set the last request time inside its scoped session to
5 | # verify timeout in the following request.
6 | Warden::Manager.after_set_user do |record, warden, options|
7 | scope = options[:scope]
8 | env = warden.request.env
9 |
10 | if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false
11 | last_request_at = warden.session(scope)['last_request_at']
12 |
13 | if record.timedout?(last_request_at) && !env['devise.skip_timeout']
14 | warden.logout(scope)
15 | if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout
16 | record.reset_authentication_token!
17 | end
18 | throw :warden, :scope => scope, :message => :timeout
19 | end
20 |
21 | unless env['devise.skip_trackable']
22 | warden.session(scope)['last_request_at'] = Time.now.utc
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/app/views/devise/shared/_links.erb:
--------------------------------------------------------------------------------
1 | <%- if controller_name != 'sessions' %>
2 | <%= link_to "Sign in", new_session_path(resource_name) %>
3 | <% end -%>
4 |
5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%>
8 |
9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%>
12 |
13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%>
16 |
17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%>
20 |
21 | <%- if devise_mapping.omniauthable? %>
22 | <%- resource_class.omniauth_providers.each do |provider| %>
23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%>
25 | <% end -%>
--------------------------------------------------------------------------------
/lib/devise/param_filter.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | class ParamFilter
3 | def initialize(case_insensitive_keys, strip_whitespace_keys)
4 | @case_insensitive_keys = case_insensitive_keys || []
5 | @strip_whitespace_keys = strip_whitespace_keys || []
6 | end
7 |
8 | def filter(conditions)
9 | conditions = stringify_params(conditions.dup)
10 |
11 | @case_insensitive_keys.each do |k|
12 | value = conditions[k]
13 | next unless value.respond_to?(:downcase)
14 | conditions[k] = value.downcase
15 | end
16 |
17 | @strip_whitespace_keys.each do |k|
18 | value = conditions[k]
19 | next unless value.respond_to?(:strip)
20 | conditions[k] = value.strip
21 | end
22 |
23 | conditions
24 | end
25 |
26 | # Force keys to be string to avoid injection on mongoid related database.
27 | def stringify_params(conditions)
28 | return conditions unless conditions.is_a?(Hash)
29 | conditions.each do |k, v|
30 | conditions[k] = v.to_s if param_requires_string_conversion?(v)
31 | end
32 | end
33 |
34 | private
35 |
36 | def param_requires_string_conversion?(value)
37 | true
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/generators/templates/simple_form_for/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= resource_name.to_s.humanize %>
2 |
3 | <%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
4 | <%= f.error_notification %>
5 |
6 |
17 |
18 |
19 | <%= f.button :submit, "Update" %>
20 |
21 | <% end %>
22 |
23 | Cancel my account
24 |
25 | Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.
26 |
27 | <%= link_to "Back", :back %>
28 |
--------------------------------------------------------------------------------
/test/generators/devise_generator_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | require "generators/devise/devise_generator"
4 |
5 | class DeviseGeneratorTest < Rails::Generators::TestCase
6 | tests Devise::Generators::DeviseGenerator
7 | destination File.expand_path("../../tmp", __FILE__)
8 |
9 | setup do
10 | prepare_destination
11 | copy_routes
12 | end
13 |
14 | test "route generation for simple model names" do
15 | run_generator %w(monster name:string)
16 | assert_file "config/routes.rb", /devise_for :monsters/
17 | end
18 |
19 | test "route generation for namespaced model names" do
20 | run_generator %w(monster/goblin name:string)
21 | match = /devise_for :goblins, :class_name => "Monster::Goblin"/
22 | assert_file "config/routes.rb", match
23 | end
24 |
25 | test "route generation with skip routes" do
26 | run_generator %w(monster name:string --skip-routes)
27 | match = /devise_for :monsters, :skip => :all/
28 | assert_file "config/routes.rb", match
29 | end
30 |
31 | def copy_routes
32 | routes = File.expand_path("../../rails_app/config/routes.rb", __FILE__)
33 | destination = File.join(destination_root, "config")
34 |
35 | FileUtils.mkdir_p(destination)
36 | FileUtils.cp routes, destination
37 | end
38 |
39 | end
40 |
--------------------------------------------------------------------------------
/app/views/devise/registrations/edit.html.erb:
--------------------------------------------------------------------------------
1 | Edit <%= resource_name.to_s.humanize %>
2 |
3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
4 | <%= devise_error_messages! %>
5 |
6 | <%= f.label :email %>
7 | <%= f.email_field :email, :autofocus => true %>
8 |
9 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
10 | Currently waiting confirmation for: <%= resource.unconfirmed_email %>
11 | <% end %>
12 |
13 | <%= f.label :password %> (leave blank if you don't want to change it)
14 | <%= f.password_field :password, :autocomplete => "off" %>
15 |
16 | <%= f.label :password_confirmation %>
17 | <%= f.password_field :password_confirmation %>
18 |
19 | <%= f.label :current_password %> (we need your current password to confirm your changes)
20 | <%= f.password_field :current_password %>
21 |
22 | <%= f.submit "Update" %>
23 | <% end %>
24 |
25 | Cancel my account
26 |
27 | Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.
28 |
29 | <%= link_to "Back", :back %>
30 |
--------------------------------------------------------------------------------
/test/rails_app/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | RailsApp::Application.configure do
2 | # Settings specified here will take precedence over those in config/environment.rb
3 |
4 | # The production environment is meant for finished, "live" apps.
5 | # Code is not reloaded between requests
6 | config.cache_classes = true
7 |
8 | # Full error reports are disabled and caching is turned on
9 | config.consider_all_requests_local = false
10 | config.action_controller.perform_caching = true
11 |
12 | # See everything in the log (default is :info)
13 | # config.log_level = :debug
14 |
15 | # Use a different logger for distributed setups
16 | # config.logger = SyslogLogger.new
17 |
18 | # Use a different cache store in production
19 | # config.cache_store = :mem_cache_store
20 |
21 | # Disable Rails's static asset server
22 | # In production, Apache or nginx will already do this
23 | config.serve_static_assets = false
24 |
25 | # Enable serving of images, stylesheets, and javascripts from an asset server
26 | # config.action_controller.asset_host = "http://assets.example.com"
27 |
28 | # Disable delivery errors, bad email addresses will be ignored
29 | # config.action_mailer.raise_delivery_errors = false
30 |
31 | # Enable threaded mode
32 | # config.threadsafe!
33 | end
34 |
--------------------------------------------------------------------------------
/test/support/assertions.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/test_case'
2 |
3 | class ActiveSupport::TestCase
4 | def assert_not(assertion)
5 | assert !assertion
6 | end
7 |
8 | def assert_blank(assertion)
9 | assert assertion.blank?
10 | end
11 |
12 | def assert_not_blank(assertion)
13 | assert !assertion.blank?
14 | end
15 | alias :assert_present :assert_not_blank
16 |
17 | def assert_email_sent(address = nil, &block)
18 | assert_difference('ActionMailer::Base.deliveries.size', &block)
19 | if address.present?
20 | assert_equal address, ActionMailer::Base.deliveries.last['to'].to_s
21 | end
22 | end
23 |
24 | def assert_email_not_sent(&block)
25 | assert_no_difference('ActionMailer::Base.deliveries.size', &block)
26 | end
27 |
28 | def assert_same_content(result, expected)
29 | assert expected.size == result.size, "the arrays doesn't have the same size"
30 | expected.each do |element|
31 | assert result.include?(element), "The array doesn't include '#{element}'."
32 | end
33 | end
34 |
35 | def assert_raise_with_message(exception_klass, message, &block)
36 | exception = assert_raise exception_klass, &block
37 | assert_equal exception.message, message,
38 | "The expected message was #{message} but your exception throwed #{exception.message}"
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/devise/modules.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/core_ext/object/with_options'
2 |
3 | Devise.with_options :model => true do |d|
4 | # Strategies first
5 | d.with_options :strategy => true do |s|
6 | routes = [nil, :new, :destroy]
7 | s.add_module :database_authenticatable, :controller => :sessions, :route => { :session => routes }
8 | s.add_module :token_authenticatable, :controller => :sessions, :route => { :session => routes }, :no_input => true
9 | s.add_module :rememberable, :no_input => true
10 | end
11 |
12 | # Other authentications
13 | d.add_module :omniauthable, :controller => :omniauth_callbacks, :route => :omniauth_callback
14 |
15 | # Misc after
16 | routes = [nil, :new, :edit]
17 | d.add_module :recoverable, :controller => :passwords, :route => { :password => routes }
18 | d.add_module :registerable, :controller => :registrations, :route => { :registration => (routes << :cancel) }
19 | d.add_module :validatable
20 |
21 | # The ones which can sign out after
22 | routes = [nil, :new]
23 | d.add_module :confirmable, :controller => :confirmations, :route => { :confirmation => routes }
24 | d.add_module :lockable, :controller => :unlocks, :route => { :unlock => routes }
25 | d.add_module :timeoutable
26 |
27 | # Stats for last, so we make sure the user is really signed in
28 | d.add_module :trackable
29 | end
--------------------------------------------------------------------------------
/lib/generators/templates/README:
--------------------------------------------------------------------------------
1 | ===============================================================================
2 |
3 | Some setup you must do manually if you haven't yet:
4 |
5 | 1. Ensure you have defined default url options in your environments files. Here
6 | is an example of default_url_options appropriate for a development environment
7 | in config/environments/development.rb:
8 |
9 | config.action_mailer.default_url_options = { :host => 'localhost:3000' }
10 |
11 | In production, :host should be set to the actual host of your application.
12 |
13 | 2. Ensure you have defined root_url to *something* in your config/routes.rb.
14 | For example:
15 |
16 | root :to => "home#index"
17 |
18 | 3. Ensure you have flash messages in app/views/layouts/application.html.erb.
19 | For example:
20 |
21 | <%= notice %>
22 | <%= alert %>
23 |
24 | 4. If you are deploying Rails 3.1+ on Heroku, you may want to set:
25 |
26 | config.assets.initialize_on_precompile = false
27 |
28 | On config/application.rb forcing your application to not access the DB
29 | or load models when precompiling your assets.
30 |
31 | 5. You can copy Devise views (for customization) to your app by running:
32 |
33 | rails g devise:views
34 |
35 | ===============================================================================
36 |
--------------------------------------------------------------------------------
/app/controllers/devise/unlocks_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::UnlocksController < DeviseController
2 | prepend_before_filter :require_no_authentication
3 |
4 | # GET /resource/unlock/new
5 | def new
6 | build_resource({})
7 | end
8 |
9 | # POST /resource/unlock
10 | def create
11 | self.resource = resource_class.send_unlock_instructions(resource_params)
12 |
13 | if successfully_sent?(resource)
14 | respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
15 | else
16 | respond_with(resource)
17 | end
18 | end
19 |
20 | # GET /resource/unlock?unlock_token=abcdef
21 | def show
22 | self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
23 |
24 | if resource.errors.empty?
25 | set_flash_message :notice, :unlocked if is_navigational_format?
26 | respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) }
27 | else
28 | respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
29 | end
30 | end
31 |
32 | protected
33 |
34 | # The path used after sending unlock password instructions
35 | def after_sending_unlock_instructions_path_for(resource)
36 | new_session_path(resource)
37 | end
38 |
39 | # The path used after unlocking the resource
40 | def after_unlock_path_for(resource)
41 | new_session_path(resource)
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/test/rails_app/config/application.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../boot', __FILE__)
2 |
3 | require "action_controller/railtie"
4 | require "action_mailer/railtie"
5 | require "active_resource/railtie"
6 | require "rails/test_unit/railtie"
7 |
8 | Bundler.require :default, DEVISE_ORM
9 |
10 | begin
11 | require "#{DEVISE_ORM}/railtie"
12 | rescue LoadError
13 | end
14 |
15 | require "devise"
16 |
17 | module RailsApp
18 | class Application < Rails::Application
19 | # Add additional load paths for your own custom dirs
20 | config.autoload_paths.reject!{ |p| p =~ /\/app\/(\w+)$/ && !%w(controllers helpers views).include?($1) }
21 | config.autoload_paths += [ "#{config.root}/app/#{DEVISE_ORM}" ]
22 |
23 | # Configure generators values. Many other options are available, be sure to check the documentation.
24 | # config.generators do |g|
25 | # g.orm :active_record
26 | # g.template_engine :erb
27 | # g.test_framework :test_unit, :fixture => true
28 | # end
29 |
30 | # Configure sensitive parameters which will be filtered from the log file.
31 | config.filter_parameters << :password
32 | config.assets.enabled = false
33 |
34 | config.action_mailer.default_url_options = { :host => "localhost:3000" }
35 |
36 | # This was used to break devise in some situations
37 | config.to_prepare do
38 | Devise::SessionsController.layout "application"
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/test/models/timeoutable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TimeoutableTest < ActiveSupport::TestCase
4 |
5 | test 'should be expired' do
6 | assert new_user.timedout?(31.minutes.ago)
7 | end
8 |
9 | test 'should not be expired' do
10 | assert_not new_user.timedout?(29.minutes.ago)
11 | end
12 |
13 | test 'should not be expired when params is nil' do
14 | assert_not new_user.timedout?(nil)
15 | end
16 |
17 | test 'should use timeout_in method' do
18 | user = new_user
19 | user.instance_eval { def timeout_in; 10.minutes end }
20 |
21 | assert user.timedout?(12.minutes.ago)
22 | assert_not user.timedout?(8.minutes.ago)
23 | end
24 |
25 | test 'should not be expired when timeout_in method returns nil' do
26 | user = new_user
27 | user.instance_eval { def timeout_in; nil end }
28 | assert_not user.timedout?(10.hours.ago)
29 | end
30 |
31 | test 'fallback to Devise config option' do
32 | swap Devise, :timeout_in => 1.minute do
33 | user = new_user
34 | assert user.timedout?(2.minutes.ago)
35 | assert_not user.timedout?(30.seconds.ago)
36 |
37 | Devise.timeout_in = 5.minutes
38 | assert_not user.timedout?(2.minutes.ago)
39 | assert user.timedout?(6.minutes.ago)
40 | end
41 | end
42 |
43 | test 'required_fields should contain the fields that Devise uses' do
44 | assert_same_content Devise::Models::Timeoutable.required_fields(User), []
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/test/rails_app/app/mongoid/user.rb:
--------------------------------------------------------------------------------
1 | require 'shared_user'
2 |
3 | class User
4 | include Mongoid::Document
5 | include Shim
6 | include SharedUser
7 |
8 | field :username, :type => String
9 | field :facebook_token, :type => String
10 |
11 | ## Database authenticatable
12 | field :email, :type => String, :default => ""
13 | field :encrypted_password, :type => String, :default => ""
14 |
15 | ## Recoverable
16 | field :reset_password_token, :type => String
17 | field :reset_password_sent_at, :type => Time
18 |
19 | ## Rememberable
20 | field :remember_created_at, :type => Time
21 |
22 | ## Trackable
23 | field :sign_in_count, :type => Integer, :default => 0
24 | field :current_sign_in_at, :type => Time
25 | field :last_sign_in_at, :type => Time
26 | field :current_sign_in_ip, :type => String
27 | field :last_sign_in_ip, :type => String
28 |
29 | ## Confirmable
30 | field :confirmation_token, :type => String
31 | field :confirmed_at, :type => Time
32 | field :confirmation_sent_at, :type => Time
33 | # field :unconfirmed_email, :type => String # Only if using reconfirmable
34 |
35 | ## Lockable
36 | field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts
37 | field :unlock_token, :type => String # Only if unlock strategy is :email or :both
38 | field :locked_at, :type => Time
39 |
40 | ## Token authenticatable
41 | field :authentication_token, :type => String
42 | end
43 |
--------------------------------------------------------------------------------
/lib/devise/models/timeoutable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/hooks/timeoutable'
2 |
3 | module Devise
4 | module Models
5 | # Timeoutable takes care of veryfing whether a user session has already
6 | # expired or not. When a session expires after the configured time, the user
7 | # will be asked for credentials again, it means, he/she will be redirected
8 | # to the sign in page.
9 | #
10 | # == Options
11 | #
12 | # Timeoutable adds the following options to devise_for:
13 | #
14 | # * +timeout_in+: the interval to timeout the user session without activity.
15 | #
16 | # == Examples
17 | #
18 | # user.timedout?(30.minutes.ago)
19 | #
20 | module Timeoutable
21 | extend ActiveSupport::Concern
22 |
23 | def self.required_fields(klass)
24 | []
25 | end
26 |
27 | # Checks whether the user session has expired based on configured time.
28 | def timedout?(last_access)
29 | return false if remember_exists_and_not_expired?
30 | !timeout_in.nil? && last_access && last_access <= timeout_in.ago
31 | end
32 |
33 | def timeout_in
34 | self.class.timeout_in
35 | end
36 |
37 | private
38 |
39 | def remember_exists_and_not_expired?
40 | return false unless respond_to?(:remember_created_at)
41 | remember_created_at && !remember_expired?
42 | end
43 |
44 | module ClassMethods
45 | Devise::Models.config(self, :timeout_in)
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/app/controllers/devise/confirmations_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::ConfirmationsController < DeviseController
2 | # GET /resource/confirmation/new
3 | def new
4 | build_resource({})
5 | end
6 |
7 | # POST /resource/confirmation
8 | def create
9 | self.resource = resource_class.send_confirmation_instructions(resource_params)
10 |
11 | if successfully_sent?(resource)
12 | respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
13 | else
14 | respond_with(resource)
15 | end
16 | end
17 |
18 | # GET /resource/confirmation?confirmation_token=abcdef
19 | def show
20 | self.resource = resource_class.confirm_by_token(params[:confirmation_token])
21 |
22 | if resource.errors.empty?
23 | set_flash_message(:notice, :confirmed) if is_navigational_format?
24 | sign_in(resource_name, resource)
25 | respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
26 | else
27 | respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
28 | end
29 | end
30 |
31 | protected
32 |
33 | # The path used after resending confirmation instructions.
34 | def after_resending_confirmation_instructions_path_for(resource_name)
35 | new_session_path(resource_name)
36 | end
37 |
38 | # The path used after confirmation.
39 | def after_confirmation_path_for(resource_name, resource)
40 | after_sign_in_path_for(resource)
41 | end
42 |
43 | end
44 |
--------------------------------------------------------------------------------
/lib/devise/models/trackable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/hooks/trackable'
2 |
3 | module Devise
4 | module Models
5 | # Track information about your user sign in. It tracks the following columns:
6 | #
7 | # * sign_in_count - Increased every time a sign in is made (by form, openid, oauth)
8 | # * current_sign_in_at - A timestamp updated when the user signs in
9 | # * last_sign_in_at - Holds the timestamp of the previous sign in
10 | # * current_sign_in_ip - The remote ip updated when the user sign in
11 | # * last_sign_in_ip - Holds the remote ip of the previous sign in
12 | #
13 | module Trackable
14 | def self.required_fields(klass)
15 | [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
16 | end
17 |
18 | def update_tracked_fields!(request)
19 | old_current, new_current = self.current_sign_in_at, Time.now.utc
20 | self.last_sign_in_at = old_current || new_current
21 | self.current_sign_in_at = new_current
22 |
23 | old_current, new_current = self.current_sign_in_ip, request.remote_ip
24 | self.last_sign_in_ip = old_current || new_current
25 | self.current_sign_in_ip = new_current
26 |
27 | self.sign_in_count ||= 0
28 | self.sign_in_count += 1
29 |
30 | save(:validate => false) or raise "Devise trackable could not save #{inspect}." \
31 | "Please make sure a model using trackable can be saved at sign in."
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/test/rails_app/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | RailsApp::Application.configure do
2 | # Settings specified here will take precedence over those in config/environment.rb
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Log error messages when you accidentally call methods on nil.
11 | config.whiny_nils = true
12 |
13 | # Show full error reports and disable caching
14 | config.consider_all_requests_local = true
15 | config.action_controller.perform_caching = false
16 |
17 | # Disable request forgery protection in test environment
18 | config.action_controller.allow_forgery_protection = false
19 |
20 | # Tell Action Mailer not to deliver emails to the real world.
21 | # The :test delivery method accumulates sent emails in the
22 | # ActionMailer::Base.deliveries array.
23 | config.action_mailer.delivery_method = :test
24 |
25 | # Use SQL instead of Active Record's schema dumper when creating the test database.
26 | # This is necessary if your schema can't be completely dumped by the schema dumper,
27 | # like if you have constraints or database-specific column types
28 | # config.active_record.schema_format = :sql
29 |
30 | config.action_dispatch.show_exceptions = false
31 |
32 | config.active_support.deprecation = :stderr
33 | end
34 |
--------------------------------------------------------------------------------
/lib/devise/omniauth/config.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module OmniAuth
3 | class StrategyNotFound < NameError
4 | def initialize(strategy)
5 | @strategy = strategy
6 | super("Could not find a strategy with name `#{strategy}'. " \
7 | "Please ensure it is required or explicitly set it using the :strategy_class option.")
8 | end
9 | end
10 |
11 | class Config
12 | attr_accessor :strategy
13 | attr_reader :args, :options, :provider, :strategy_name
14 |
15 | def initialize(provider, args)
16 | @provider = provider
17 | @args = args
18 | @options = @args.last.is_a?(Hash) ? @args.last : {}
19 | @strategy = nil
20 | @strategy_name = options[:name] || @provider
21 | @strategy_class = options.delete(:strategy_class)
22 | end
23 |
24 | def strategy_class
25 | @strategy_class ||= find_strategy || autoload_strategy
26 | end
27 |
28 | def find_strategy
29 | ::OmniAuth.strategies.find do |strategy_class|
30 | strategy_class.to_s =~ /#{::OmniAuth::Utils.camelize(strategy_name)}$/ ||
31 | strategy_class.default_options[:name] == strategy_name
32 | end
33 | end
34 |
35 | def autoload_strategy
36 | name = ::OmniAuth::Utils.camelize(provider.to_s)
37 | if ::OmniAuth::Strategies.const_defined?(name)
38 | ::OmniAuth::Strategies.const_get(name)
39 | else
40 | raise StrategyNotFound, name
41 | end
42 | end
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/lib/devise/strategies/rememberable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/strategies/authenticatable'
2 |
3 | module Devise
4 | module Strategies
5 | # Remember the user through the remember token. This strategy is responsible
6 | # to verify whether there is a cookie with the remember token, and to
7 | # recreate the user from this cookie if it exists. Must be called *before*
8 | # authenticatable.
9 | class Rememberable < Authenticatable
10 | # A valid strategy for rememberable needs a remember token in the cookies.
11 | def valid?
12 | @remember_cookie = nil
13 | remember_cookie.present?
14 | end
15 |
16 | # To authenticate a user we deserialize the cookie and attempt finding
17 | # the record in the database. If the attempt fails, we pass to another
18 | # strategy handle the authentication.
19 | def authenticate!
20 | resource = mapping.to.serialize_from_cookie(*remember_cookie)
21 |
22 | unless resource
23 | cookies.delete(remember_key)
24 | return pass
25 | end
26 |
27 | if validate(resource)
28 | success!(resource)
29 | end
30 | end
31 |
32 | private
33 |
34 | def decorate(resource)
35 | super
36 | resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
37 | end
38 |
39 | def remember_me?
40 | true
41 | end
42 |
43 | def remember_key
44 | mapping.to.rememberable_options.fetch(:key, "remember_#{scope}_token")
45 | end
46 |
47 | def remember_cookie
48 | @remember_cookie ||= cookies.signed[remember_key]
49 | end
50 |
51 | end
52 | end
53 | end
54 |
55 | Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)
56 |
--------------------------------------------------------------------------------
/test/helpers/devise_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class DeviseHelperTest < ActionDispatch::IntegrationTest
4 | setup do
5 | model_labels = { :models => { :user => "utilisateur" } }
6 |
7 | I18n.backend.store_translations :fr,
8 | {
9 | :errors => { :messages => { :not_saved => {
10 | :one => "Erreur lors de l'enregistrement de '%{resource}': 1 erreur.",
11 | :other => "Erreur lors de l'enregistrement de '%{resource}': %{count} erreurs."
12 | } } },
13 | :activerecord => model_labels,
14 | :mongoid => model_labels
15 | }
16 |
17 | I18n.locale = 'fr'
18 | end
19 |
20 | teardown do
21 | I18n.locale = 'en'
22 | end
23 |
24 | test 'test errors.messages.not_saved with single error from i18n' do
25 | get new_user_registration_path
26 |
27 | fill_in 'password', :with => 'new_user123'
28 | fill_in 'password confirmation', :with => 'new_user123'
29 | click_button 'Sign up'
30 |
31 | assert_have_selector '#error_explanation'
32 | assert_contain "Erreur lors de l'enregistrement de 'utilisateur': 1 erreur"
33 | end
34 |
35 | test 'test errors.messages.not_saved with multiple errors from i18n' do
36 | # Dirty tracking behavior prevents email validations from being applied:
37 | # https://github.com/mongoid/mongoid/issues/756
38 | (pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
39 |
40 | get new_user_registration_path
41 |
42 | fill_in 'email', :with => 'invalid_email'
43 | fill_in 'password', :with => 'new_user123'
44 | fill_in 'password confirmation', :with => 'new_user321'
45 | click_button 'Sign up'
46 |
47 | assert_have_selector '#error_explanation'
48 | assert_contain "Erreur lors de l'enregistrement de 'utilisateur': 2 erreurs"
49 | end
50 | end
51 |
52 |
--------------------------------------------------------------------------------
/app/controllers/devise/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::SessionsController < DeviseController
2 | prepend_before_filter :require_no_authentication, :only => [ :new, :create ]
3 | prepend_before_filter :allow_params_authentication!, :only => :create
4 | prepend_before_filter { request.env["devise.skip_timeout"] = true }
5 |
6 | # GET /resource/sign_in
7 | def new
8 | self.resource = build_resource(nil, :unsafe => true)
9 | clean_up_passwords(resource)
10 | respond_with(resource, serialize_options(resource))
11 | end
12 |
13 | # POST /resource/sign_in
14 | def create
15 | self.resource = warden.authenticate!(auth_options)
16 | set_flash_message(:notice, :signed_in) if is_navigational_format?
17 | sign_in(resource_name, resource)
18 | respond_with resource, :location => after_sign_in_path_for(resource)
19 | end
20 |
21 | # DELETE /resource/sign_out
22 | def destroy
23 | redirect_path = after_sign_out_path_for(resource_name)
24 | signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
25 | set_flash_message :notice, :signed_out if signed_out && is_navigational_format?
26 |
27 | # We actually need to hardcode this as Rails default responder doesn't
28 | # support returning empty response on GET request
29 | respond_to do |format|
30 | format.all { head :no_content }
31 | format.any(*navigational_formats) { redirect_to redirect_path }
32 | end
33 | end
34 |
35 | protected
36 |
37 | def serialize_options(resource)
38 | methods = resource_class.authentication_keys.dup
39 | methods = methods.keys if methods.is_a?(Hash)
40 | methods << :password if resource.respond_to?(:password)
41 | { :methods => methods, :only => [:password] }
42 | end
43 |
44 | def auth_options
45 | { :scope => resource_name, :recall => "#{controller_path}#new" }
46 | end
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/test/models/serializable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SerializableTest < ActiveSupport::TestCase
4 | setup do
5 | @user = create_user
6 | end
7 |
8 | test 'should not include unsafe keys on XML' do
9 | assert_match /email/, @user.to_xml
10 | assert_no_match /confirmation-token/, @user.to_xml
11 | end
12 |
13 | test 'should not include unsafe keys on XML even if a new except is provided' do
14 | assert_no_match /email/, @user.to_xml(:except => :email)
15 | assert_no_match /confirmation-token/, @user.to_xml(:except => :email)
16 | end
17 |
18 | test 'should include unsafe keys on XML if a force_except is provided' do
19 | assert_no_match / :email)
20 | assert_match /confirmation-token/, @user.to_xml(:force_except => :email)
21 | end
22 |
23 | test 'should not include unsafe keys on JSON' do
24 | keys = from_json().keys.select{ |key| !key.include?("id") }
25 | assert_equal %w(created_at email facebook_token updated_at username), keys.sort
26 | end
27 |
28 | test 'should not include unsafe keys on JSON even if a new except is provided' do
29 | assert_no_key "email", from_json(:except => :email)
30 | assert_no_key "confirmation_token", from_json(:except => :email)
31 | end
32 |
33 | test 'should include unsafe keys on JSON if a force_except is provided' do
34 | assert_no_key "email", from_json(:force_except => :email)
35 | assert_key "confirmation_token", from_json(:force_except => :email)
36 | end
37 |
38 | def assert_key(key, subject)
39 | assert subject.key?(key), "Expected #{subject.inspect} to have key #{key.inspect}"
40 | end
41 |
42 | def assert_no_key(key, subject)
43 | assert !subject.key?(key), "Expected #{subject.inspect} to not have key #{key.inspect}"
44 | end
45 |
46 | def from_json(options=nil)
47 | ActiveSupport::JSON.decode(@user.to_json(options))["user"]
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/devise/strategies/token_authenticatable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/strategies/base'
2 |
3 | module Devise
4 | module Strategies
5 | # Strategy for signing in a user, based on a authenticatable token. This works for both params
6 | # and http. For the former, all you need to do is to pass the params in the URL:
7 | #
8 | # http://myapp.example.com/?user_token=SECRET
9 | #
10 | # For HTTP, you can pass the token as username and blank password. Since some clients may require
11 | # a password, you can pass "X" as password and it will simply be ignored.
12 | class TokenAuthenticatable < Authenticatable
13 | def store?
14 | super && !mapping.to.skip_session_storage.include?(:token_auth)
15 | end
16 |
17 | def authenticate!
18 | resource = mapping.to.find_for_token_authentication(authentication_hash)
19 | return fail(:invalid_token) unless resource
20 |
21 | if validate(resource)
22 | resource.after_token_authentication
23 | success!(resource)
24 | end
25 | end
26 |
27 | private
28 |
29 | # Token Authenticatable can be authenticated with params in any controller and any verb.
30 | def valid_params_request?
31 | true
32 | end
33 |
34 | # Do not use remember_me behavior with token.
35 | def remember_me?
36 | false
37 | end
38 |
39 | # Try both scoped and non scoped keys.
40 | def params_auth_hash
41 | if params[scope].kind_of?(Hash) && params[scope].has_key?(authentication_keys.first)
42 | params[scope]
43 | else
44 | params
45 | end
46 | end
47 |
48 | # Overwrite authentication keys to use token_authentication_key.
49 | def authentication_keys
50 | @authentication_keys ||= [mapping.to.token_authentication_key]
51 | end
52 | end
53 | end
54 | end
55 |
56 | Warden::Strategies.add(:token_authenticatable, Devise::Strategies::TokenAuthenticatable)
57 |
--------------------------------------------------------------------------------
/test/models/token_authenticatable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TokenAuthenticatableTest < ActiveSupport::TestCase
4 |
5 | test 'should reset authentication token' do
6 | user = new_user
7 | user.reset_authentication_token
8 | previous_token = user.authentication_token
9 | user.reset_authentication_token
10 | assert_not_equal previous_token, user.authentication_token
11 | end
12 |
13 | test 'should ensure authentication token' do
14 | user = new_user
15 | user.ensure_authentication_token
16 | previous_token = user.authentication_token
17 | user.ensure_authentication_token
18 | assert_equal previous_token, user.authentication_token
19 | end
20 |
21 | test 'should authenticate a valid user with authentication token and return it' do
22 | user = create_user
23 | user.ensure_authentication_token!
24 | user.confirm!
25 | authenticated_user = User.find_for_token_authentication(:auth_token => user.authentication_token)
26 | assert_equal authenticated_user, user
27 | end
28 |
29 | test 'should return nil when authenticating an invalid user by authentication token' do
30 | user = create_user
31 | user.ensure_authentication_token!
32 | user.confirm!
33 | authenticated_user = User.find_for_token_authentication(:auth_token => user.authentication_token.reverse)
34 | assert_nil authenticated_user
35 | end
36 |
37 | test 'should not be subject to injection' do
38 | user1 = create_user
39 | user1.ensure_authentication_token!
40 | user1.confirm!
41 |
42 | user2 = create_user
43 | user2.ensure_authentication_token!
44 | user2.confirm!
45 |
46 | user = User.find_for_token_authentication(:auth_token => {'$ne' => user1.authentication_token})
47 | assert_nil user
48 | end
49 |
50 | test 'required_fields should contain the fields that Devise uses' do
51 | assert_same_content Devise::Models::TokenAuthenticatable.required_fields(User), [
52 | :authentication_token
53 | ]
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/test/omniauth/url_helpers_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class OmniAuthRoutesTest < ActionController::TestCase
4 | tests ApplicationController
5 |
6 | def assert_path(action, provider, with_param=true)
7 | # Resource param
8 | assert_equal @controller.send(action, :user, provider),
9 | @controller.send("user_#{action}", provider)
10 |
11 | # With an object
12 | assert_equal @controller.send(action, User.new, provider),
13 | @controller.send("user_#{action}", provider)
14 |
15 | if with_param
16 | # Default url params
17 | assert_equal @controller.send(action, :user, provider, :param => 123),
18 | @controller.send("user_#{action}", provider, :param => 123)
19 | end
20 | end
21 |
22 | test 'should alias omniauth_callback to mapped user auth_callback' do
23 | assert_path :omniauth_callback_path, :facebook
24 | end
25 |
26 | test 'should alias omniauth_authorize to mapped user auth_authorize' do
27 | assert_path :omniauth_authorize_path, :facebook, false
28 | end
29 |
30 | test 'should generate authorization path' do
31 | assert_match "/users/auth/facebook", @controller.omniauth_authorize_path(:user, :facebook)
32 |
33 | assert_raise ActionController::RoutingError do
34 | @controller.omniauth_authorize_path(:user, :github)
35 | end
36 | end
37 |
38 | test 'should generate authorization path for named open_id omniauth' do
39 | assert_match "/users/auth/google", @controller.omniauth_authorize_path(:user, :google)
40 | end
41 |
42 | test 'should generate authorization path with params' do
43 | assert_match "/users/auth/openid?openid_url=http%3A%2F%2Fyahoo.com",
44 | @controller.omniauth_authorize_path(:user, :openid, :openid_url => "http://yahoo.com")
45 | end
46 |
47 | test 'should not add a "?" if no param was sent' do
48 | assert_equal "/users/auth/openid",
49 | @controller.omniauth_authorize_path(:user, :openid)
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/devise/controllers/rememberable.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Controllers
3 | # A module that may be optionally included in a controller in order
4 | # to provide remember me behavior.
5 | module Rememberable
6 | # Return default cookie values retrieved from session options.
7 | def self.cookie_values
8 | Rails.configuration.session_options.slice(:path, :domain, :secure)
9 | end
10 |
11 | # A small warden proxy so we can remember and forget uses from hooks.
12 | class Proxy #:nodoc:
13 | include Devise::Controllers::Rememberable
14 |
15 | delegate :cookies, :env, :to => :@warden
16 |
17 | def initialize(warden)
18 | @warden = warden
19 | end
20 | end
21 |
22 | # Remembers the given resource by setting up a cookie
23 | def remember_me(resource)
24 | scope = Devise::Mapping.find_scope!(resource)
25 | resource.remember_me!(resource.extend_remember_period)
26 | cookies.signed[remember_key(resource, scope)] = remember_cookie_values(resource)
27 | end
28 |
29 | # Forgets the given resource by deleting a cookie
30 | def forget_me(resource)
31 | scope = Devise::Mapping.find_scope!(resource)
32 | resource.forget_me!
33 | cookies.delete(remember_key(resource, scope), forget_cookie_values(resource))
34 | end
35 |
36 | protected
37 |
38 | def forget_cookie_values(resource)
39 | Devise::Controllers::Rememberable.cookie_values.merge!(resource.rememberable_options)
40 | end
41 |
42 | def remember_cookie_values(resource)
43 | options = { :httponly => true }
44 | options.merge!(forget_cookie_values(resource))
45 | options.merge!(
46 | :value => resource.class.serialize_into_cookie(resource),
47 | :expires => resource.remember_expires_at
48 | )
49 | end
50 |
51 | def remember_key(resource, scope)
52 | resource.rememberable_options.fetch(:key, "remember_#{scope}_token")
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/generators/mongoid/devise_generator.rb:
--------------------------------------------------------------------------------
1 | require 'generators/devise/orm_helpers'
2 |
3 | module Mongoid
4 | module Generators
5 | class DeviseGenerator < Rails::Generators::NamedBase
6 | include Devise::Generators::OrmHelpers
7 |
8 | def generate_model
9 | invoke "mongoid:model", [name] unless model_exists? && behavior == :invoke
10 | end
11 |
12 | def inject_field_types
13 | inject_into_file model_path, migration_data, :after => "include Mongoid::Document\n" if model_exists?
14 | end
15 |
16 | def inject_devise_content
17 | inject_into_file model_path, model_contents, :after => "include Mongoid::Document\n" if model_exists?
18 | end
19 |
20 | def migration_data
21 | < String, :default => ""
24 | field :encrypted_password, :type => String, :default => ""
25 |
26 | ## Recoverable
27 | field :reset_password_token, :type => String
28 | field :reset_password_sent_at, :type => Time
29 |
30 | ## Rememberable
31 | field :remember_created_at, :type => Time
32 |
33 | ## Trackable
34 | field :sign_in_count, :type => Integer, :default => 0
35 | field :current_sign_in_at, :type => Time
36 | field :last_sign_in_at, :type => Time
37 | field :current_sign_in_ip, :type => String
38 | field :last_sign_in_ip, :type => String
39 |
40 | ## Confirmable
41 | # field :confirmation_token, :type => String
42 | # field :confirmed_at, :type => Time
43 | # field :confirmation_sent_at, :type => Time
44 | # field :unconfirmed_email, :type => String # Only if using reconfirmable
45 |
46 | ## Lockable
47 | # field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts
48 | # field :unlock_token, :type => String # Only if unlock strategy is :email or :both
49 | # field :locked_at, :type => Time
50 |
51 | ## Token authenticatable
52 | # field :authentication_token, :type => String
53 | RUBY
54 | end
55 | end
56 | end
57 | end
--------------------------------------------------------------------------------
/lib/devise/rails.rb:
--------------------------------------------------------------------------------
1 | require 'devise/rails/routes'
2 | require 'devise/rails/warden_compat'
3 |
4 | module Devise
5 | class Engine < ::Rails::Engine
6 | config.devise = Devise
7 |
8 | # Initialize Warden and copy its configurations.
9 | config.app_middleware.use Warden::Manager do |config|
10 | Devise.warden_config = config
11 | end
12 |
13 | # Force routes to be loaded if we are doing any eager load.
14 | config.before_eager_load { |app| app.reload_routes! }
15 |
16 | initializer "devise.url_helpers" do
17 | Devise.include_helpers(Devise::Controllers)
18 | end
19 |
20 | initializer "devise.omniauth" do |app|
21 | Devise.omniauth_configs.each do |provider, config|
22 | app.middleware.use config.strategy_class, *config.args do |strategy|
23 | config.strategy = strategy
24 | end
25 | end
26 |
27 | if Devise.omniauth_configs.any?
28 | Devise.include_helpers(Devise::OmniAuth)
29 | end
30 | end
31 |
32 | initializer "devise.mongoid_version_warning" do
33 | if defined?(Mongoid)
34 | require 'mongoid/version'
35 | if Mongoid::VERSION.to_f < 2.1
36 | puts "\n[DEVISE] Please note that Mongoid versions prior to 2.1 handle dirty model " \
37 | "object attributes in such a way that the Devise `validatable` module will not apply " \
38 | "its usual uniqueness and format validations for the email field. It is recommended " \
39 | "that you upgrade to Mongoid 2.1+ for this and other fixes, but if for some reason you " \
40 | "are unable to do so, you should add these validations manually.\n"
41 | end
42 | end
43 | end
44 |
45 | initializer "devise.fix_routes_proxy_missing_respond_to_bug" do
46 | # We can get rid of this once we support only Rails > 3.2
47 | ActionDispatch::Routing::RoutesProxy.class_eval do
48 | def respond_to?(method, include_private = false)
49 | super || routes.url_helpers.respond_to?(method)
50 | end
51 | end
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/test/controllers/custom_strategy_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 | require 'ostruct'
3 | require 'warden/strategies/base'
4 | require 'devise/test_helpers'
5 |
6 | class CustomStrategyController < ActionController::Base
7 | def new
8 | warden.authenticate!(:custom_strategy)
9 | end
10 | end
11 |
12 | # These tests are to prove that a warden strategy can successfully
13 | # return a custom response, including a specific status code and
14 | # custom http response headers. This does work in production,
15 | # however, at the time of writing this, the Devise test helpers do
16 | # not recognise the custom response and proceed to calling the
17 | # Failure App. This makes it impossible to write tests for a
18 | # strategy that return a custom response with Devise.
19 | class CustomStrategy < Warden::Strategies::Base
20 | def authenticate!
21 | custom_headers = { "X-FOO" => "BAR" }
22 | response = Rack::Response.new("BAD REQUEST", 400, custom_headers)
23 | custom! response.finish
24 | end
25 | end
26 |
27 | class CustomStrategyTest < ActionController::TestCase
28 | tests CustomStrategyController
29 |
30 | include Devise::TestHelpers
31 |
32 | setup do
33 | Warden::Strategies.add(:custom_strategy, CustomStrategy)
34 | end
35 |
36 | teardown do
37 | Warden::Strategies._strategies.delete(:custom_strategy)
38 | end
39 |
40 | test "custom strategy can return its own status code" do
41 | ret = get :new
42 |
43 | # check the returned rack array
44 | assert ret.is_a?(Array)
45 | assert_equal 400, ret.first
46 |
47 | # check the saved response as well. This is purely so that the response is available to the testing framework
48 | # for verification. In production, the above array would be delivered directly to Rack.
49 | assert_response 400
50 | end
51 |
52 | test "custom strategy can return custom headers" do
53 | ret = get :new
54 |
55 | # check the returned rack array
56 | assert ret.is_a?(Array)
57 | assert_equal ret.third['X-FOO'], 'BAR'
58 |
59 | # check the saved response headers as well.
60 | assert_equal response.headers['X-FOO'], 'BAR'
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/test/omniauth/config_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class OmniAuthConfigTest < ActiveSupport::TestCase
4 | class MyStrategy
5 | include OmniAuth::Strategy
6 | end
7 |
8 | test 'strategy_name returns provider if no options given' do
9 | config = Devise::OmniAuth::Config.new :facebook, [{}]
10 | assert_equal :facebook, config.strategy_name
11 | end
12 |
13 | test 'strategy_name returns provider if no name option are given' do
14 | config = Devise::OmniAuth::Config.new :facebook, [{ :other => :option }]
15 | assert_equal :facebook, config.strategy_name
16 | end
17 |
18 | test 'returns name option when have a name' do
19 | config = Devise::OmniAuth::Config.new :facebook, [{ :name => :github }]
20 | assert_equal :github, config.strategy_name
21 | end
22 |
23 | test "finds contrib strategies" do
24 | config = Devise::OmniAuth::Config.new :facebook, [{}]
25 | assert_equal OmniAuth::Strategies::Facebook, config.strategy_class
26 | end
27 |
28 | test "finds the strategy in OmniAuth's list by name" do
29 | NamedTestStrategy = Class.new
30 | NamedTestStrategy.send :include, OmniAuth::Strategy
31 | NamedTestStrategy.option :name, :the_one
32 |
33 | config = Devise::OmniAuth::Config.new :the_one, [{}]
34 | assert_equal NamedTestStrategy, config.strategy_class
35 | end
36 |
37 | test "finds the strategy in OmniAuth's list by class name" do
38 | UnNamedTestStrategy = Class.new
39 | UnNamedTestStrategy.send :include, OmniAuth::Strategy
40 |
41 | config = Devise::OmniAuth::Config.new :un_named_test_strategy, [{}]
42 | assert_equal UnNamedTestStrategy, config.strategy_class
43 | end
44 |
45 | test 'raises an error if strategy cannot be found' do
46 | config = Devise::OmniAuth::Config.new :my_other_strategy, [{}]
47 | assert_raise Devise::OmniAuth::StrategyNotFound do
48 | config.strategy_class
49 | end
50 | end
51 |
52 | test 'allows the user to define a custom require path' do
53 | config = Devise::OmniAuth::Config.new :my_strategy, [{:strategy_class => MyStrategy}]
54 | config_class = config.strategy_class
55 | assert_equal MyStrategy, config_class
56 | end
57 | end
--------------------------------------------------------------------------------
/test/rails_app/db/migrate/20100401102949_create_tables.rb:
--------------------------------------------------------------------------------
1 | class CreateTables < ActiveRecord::Migration
2 | def self.up
3 | create_table :users do |t|
4 | t.string :username
5 | t.string :facebook_token
6 |
7 | ## Database authenticatable
8 | t.string :email, :null => false, :default => ""
9 | t.string :encrypted_password, :null => false, :default => ""
10 |
11 | ## Recoverable
12 | t.string :reset_password_token
13 | t.datetime :reset_password_sent_at
14 |
15 | ## Rememberable
16 | t.datetime :remember_created_at
17 |
18 | ## Trackable
19 | t.integer :sign_in_count, :default => 0
20 | t.datetime :current_sign_in_at
21 | t.datetime :last_sign_in_at
22 | t.string :current_sign_in_ip
23 | t.string :last_sign_in_ip
24 |
25 | ## Confirmable
26 | t.string :confirmation_token
27 | t.datetime :confirmed_at
28 | t.datetime :confirmation_sent_at
29 | # t.string :unconfirmed_email # Only if using reconfirmable
30 |
31 | ## Lockable
32 | t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
33 | t.string :unlock_token # Only if unlock strategy is :email or :both
34 | t.datetime :locked_at
35 |
36 | ## Token authenticatable
37 | t.string :authentication_token
38 |
39 | t.timestamps
40 | end
41 |
42 | create_table :admins do |t|
43 | ## Database authenticatable
44 | t.string :email, :null => true
45 | t.string :encrypted_password, :null => true
46 |
47 | ## Recoverable
48 | t.string :reset_password_token
49 | t.datetime :reset_password_sent_at
50 |
51 | ## Rememberable
52 | t.datetime :remember_created_at
53 |
54 | ## Confirmable
55 | t.string :confirmation_token
56 | t.datetime :confirmed_at
57 | t.datetime :confirmation_sent_at
58 | t.string :unconfirmed_email # Only if using reconfirmable
59 |
60 | ## Lockable
61 | t.datetime :locked_at
62 |
63 | ## Attribute for testing route blocks
64 | t.boolean :active, :default => false
65 |
66 | t.timestamps
67 | end
68 | end
69 |
70 | def self.down
71 | drop_table :users
72 | drop_table :admins
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/test/rails_app/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended to check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(:version => 20100401102949) do
14 |
15 | create_table "admins", :force => true do |t|
16 | t.string "email"
17 | t.string "encrypted_password", :limit => 128
18 | t.string "password_salt"
19 | t.string "remember_token"
20 | t.datetime "remember_created_at"
21 | t.string "reset_password_token"
22 | t.integer "failed_attempts", :default => 0
23 | t.string "unlock_token"
24 | t.datetime "locked_at"
25 | t.datetime "created_at"
26 | t.datetime "updated_at"
27 | end
28 |
29 | create_table "users", :force => true do |t|
30 | t.string "username"
31 | t.string "facebook_token"
32 | t.string "email", :default => "", :null => false
33 | t.string "encrypted_password", :limit => 128, :default => "", :null => false
34 | t.string "confirmation_token"
35 | t.datetime "confirmed_at"
36 | t.datetime "confirmation_sent_at"
37 | t.string "reset_password_token"
38 | t.datetime "remember_created_at"
39 | t.integer "sign_in_count", :default => 0
40 | t.datetime "current_sign_in_at"
41 | t.datetime "last_sign_in_at"
42 | t.string "current_sign_in_ip"
43 | t.string "last_sign_in_ip"
44 | t.integer "failed_attempts", :default => 0
45 | t.string "unlock_token"
46 | t.datetime "locked_at"
47 | t.string "authentication_token"
48 | t.datetime "created_at"
49 | t.datetime "updated_at"
50 | end
51 |
52 | end
53 |
--------------------------------------------------------------------------------
/test/controllers/url_helpers_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class RoutesTest < ActionController::TestCase
4 | tests ApplicationController
5 |
6 | def assert_path_and_url(name, prepend_path=nil)
7 | @request.path = '/users/session'
8 | prepend_path = "#{prepend_path}_" if prepend_path
9 |
10 | # Resource param
11 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", :user),
12 | send(:"#{prepend_path}user_#{name}_path")
13 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", :user),
14 | send(:"#{prepend_path}user_#{name}_url")
15 |
16 | # Default url params
17 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", :user, :param => 123),
18 | send(:"#{prepend_path}user_#{name}_path", :param => 123)
19 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", :user, :param => 123),
20 | send(:"#{prepend_path}user_#{name}_url", :param => 123)
21 |
22 | @request.path = nil
23 | # With an object
24 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", User.new),
25 | send(:"#{prepend_path}user_#{name}_path")
26 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", User.new),
27 | send(:"#{prepend_path}user_#{name}_url")
28 | end
29 |
30 |
31 | test 'should alias session to mapped user session' do
32 | assert_path_and_url :session
33 | assert_path_and_url :session, :new
34 | assert_path_and_url :session, :destroy
35 | end
36 |
37 | test 'should alias password to mapped user password' do
38 | assert_path_and_url :password
39 | assert_path_and_url :password, :new
40 | assert_path_and_url :password, :edit
41 | end
42 |
43 | test 'should alias confirmation to mapped user confirmation' do
44 | assert_path_and_url :confirmation
45 | assert_path_and_url :confirmation, :new
46 | end
47 |
48 | test 'should alias unlock to mapped user unlock' do
49 | assert_path_and_url :unlock
50 | assert_path_and_url :unlock, :new
51 | end
52 |
53 | test 'should alias registration to mapped user registration' do
54 | assert_path_and_url :registration
55 | assert_path_and_url :registration, :new
56 | assert_path_and_url :registration, :edit
57 | assert_path_and_url :registration, :cancel
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/app/controllers/devise/passwords_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::PasswordsController < DeviseController
2 | prepend_before_filter :require_no_authentication
3 | # Render the #edit only if coming from a reset password email link
4 | append_before_filter :assert_reset_token_passed, :only => :edit
5 |
6 | # GET /resource/password/new
7 | def new
8 | build_resource({})
9 | end
10 |
11 | # POST /resource/password
12 | def create
13 | self.resource = resource_class.send_reset_password_instructions(resource_params)
14 |
15 | if successfully_sent?(resource)
16 | respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
17 | else
18 | respond_with(resource)
19 | end
20 | end
21 |
22 | # GET /resource/password/edit?reset_password_token=abcdef
23 | def edit
24 | self.resource = resource_class.new
25 | resource.reset_password_token = params[:reset_password_token]
26 | end
27 |
28 | # PUT /resource/password
29 | def update
30 | self.resource = resource_class.reset_password_by_token(resource_params)
31 |
32 | if resource.errors.empty?
33 | resource.unlock_access! if unlockable?(resource)
34 | flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
35 | set_flash_message(:notice, flash_message) if is_navigational_format?
36 | sign_in(resource_name, resource)
37 | respond_with resource, :location => after_sign_in_path_for(resource)
38 | else
39 | respond_with resource
40 | end
41 | end
42 |
43 | protected
44 |
45 | # The path used after sending reset password instructions
46 | def after_sending_reset_password_instructions_path_for(resource_name)
47 | new_session_path(resource_name)
48 | end
49 |
50 | # Check if a reset_password_token is provided in the request
51 | def assert_reset_token_passed
52 | if params[:reset_password_token].blank?
53 | set_flash_message(:error, :no_token)
54 | redirect_to new_session_path(resource_name)
55 | end
56 | end
57 |
58 | # Check if proper Lockable module methods are present & unlock strategy
59 | # allows to unlock resource on password reset
60 | def unlockable?(resource)
61 | resource.respond_to?(:unlock_access!) &&
62 | resource.respond_to?(:unlock_strategy_enabled?) &&
63 | resource.unlock_strategy_enabled?(:email)
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/test/mailers/unlock_instructions_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UnlockInstructionsTest < ActionMailer::TestCase
4 |
5 | def setup
6 | setup_mailer
7 | Devise.mailer = 'Devise::Mailer'
8 | Devise.mailer_sender = 'test@example.com'
9 | end
10 |
11 | def teardown
12 | Devise.mailer = 'Devise::Mailer'
13 | Devise.mailer_sender = 'please-change-me@config-initializers-devise.com'
14 | end
15 |
16 | def user
17 | @user ||= begin
18 | user = create_user
19 | user.lock_access!
20 | user
21 | end
22 | end
23 |
24 | def mail
25 | @mail ||= begin
26 | user
27 | ActionMailer::Base.deliveries.last
28 | end
29 | end
30 |
31 | test 'email sent after locking the user' do
32 | assert_not_nil mail
33 | end
34 |
35 | test 'content type should be set to html' do
36 | assert mail.content_type.include?('text/html')
37 | end
38 |
39 | test 'send unlock instructions to the user email' do
40 | assert_equal [user.email], mail.to
41 | end
42 |
43 | test 'setup sender from configuration' do
44 | assert_equal ['test@example.com'], mail.from
45 | end
46 |
47 | test 'setup sender from custom mailer defaults' do
48 | Devise.mailer = 'Users::Mailer'
49 | assert_equal ['custom@example.com'], mail.from
50 | end
51 |
52 | test 'custom mailer renders parent mailer template' do
53 | Devise.mailer = 'Users::Mailer'
54 | assert_not_blank mail.body.encoded
55 | end
56 |
57 | test 'setup reply to as copy from sender' do
58 | assert_equal ['test@example.com'], mail.reply_to
59 | end
60 |
61 | test 'setup subject from I18n' do
62 | store_translations :en, :devise => { :mailer => { :unlock_instructions => { :subject => 'Yo unlock instructions' } } } do
63 | assert_equal 'Yo unlock instructions', mail.subject
64 | end
65 | end
66 |
67 | test 'subject namespaced by model' do
68 | store_translations :en, :devise => { :mailer => { :unlock_instructions => { :user_subject => 'User Unlock Instructions' } } } do
69 | assert_equal 'User Unlock Instructions', mail.subject
70 | end
71 | end
72 |
73 | test 'body should have user info' do
74 | assert_match user.email, mail.body.encoded
75 | end
76 |
77 | test 'body should have link to unlock the account' do
78 | host = ActionMailer::Base.default_url_options[:host]
79 | unlock_url_regexp = %r{}
80 | assert_match unlock_url_regexp, mail.body.encoded
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/test/support/helpers.rb:
--------------------------------------------------------------------------------
1 | require 'active_support/test_case'
2 |
3 | class ActiveSupport::TestCase
4 | VALID_AUTHENTICATION_TOKEN = 'AbCdEfGhIjKlMnOpQrSt'.freeze
5 |
6 | def setup_mailer
7 | ActionMailer::Base.deliveries = []
8 | end
9 |
10 | def store_translations(locale, translations, &block)
11 | begin
12 | I18n.backend.store_translations(locale, translations)
13 | yield
14 | ensure
15 | I18n.reload!
16 | end
17 | end
18 |
19 | def generate_unique_email
20 | @@email_count ||= 0
21 | @@email_count += 1
22 | "test#{@@email_count}@example.com"
23 | end
24 |
25 | def valid_attributes(attributes={})
26 | { :username => "usertest",
27 | :email => generate_unique_email,
28 | :password => '12345678',
29 | :password_confirmation => '12345678' }.update(attributes)
30 | end
31 |
32 | def new_user(attributes={})
33 | User.new(valid_attributes(attributes))
34 | end
35 |
36 | def create_user(attributes={})
37 | User.create!(valid_attributes(attributes))
38 | end
39 |
40 | def create_admin(attributes={})
41 | valid_attributes = valid_attributes(attributes)
42 | valid_attributes.delete(:username)
43 | Admin.create!(valid_attributes)
44 | end
45 |
46 | # Execute the block setting the given values and restoring old values after
47 | # the block is executed.
48 | def swap(object, new_values)
49 | old_values = {}
50 | new_values.each do |key, value|
51 | old_values[key] = object.send key
52 | object.send :"#{key}=", value
53 | end
54 | clear_cached_variables(new_values)
55 | yield
56 | ensure
57 | clear_cached_variables(new_values)
58 | old_values.each do |key, value|
59 | object.send :"#{key}=", value
60 | end
61 | end
62 |
63 | def clear_cached_variables(options)
64 | if options.key?(:case_insensitive_keys) || options.key?(:strip_whitespace_keys)
65 | Devise.mappings.each do |_, mapping|
66 | mapping.to.instance_variable_set(:@devise_param_filter, nil)
67 | end
68 | end
69 | end
70 |
71 | def swap_module_method_existence(klass, method)
72 | klass.module_eval %Q[
73 | class << self
74 | alias #{method}_referenced #{method}
75 | undef #{method}
76 | end
77 | ]
78 |
79 | begin
80 | yield if block_given?
81 | ensure
82 |
83 | klass.module_eval %Q[
84 | class << self
85 | alias #{method} #{method}_referenced
86 | undef #{method}_referenced
87 | end
88 | ]
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/lib/devise/controllers/url_helpers.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Controllers
3 | # Create url helpers to be used with resource/scope configuration. Acts as
4 | # proxies to the generated routes created by devise.
5 | # Resource param can be a string or symbol, a class, or an instance object.
6 | # Example using a :user resource:
7 | #
8 | # new_session_path(:user) => new_user_session_path
9 | # session_path(:user) => user_session_path
10 | # destroy_session_path(:user) => destroy_user_session_path
11 | #
12 | # new_password_path(:user) => new_user_password_path
13 | # password_path(:user) => user_password_path
14 | # edit_password_path(:user) => edit_user_password_path
15 | #
16 | # new_confirmation_path(:user) => new_user_confirmation_path
17 | # confirmation_path(:user) => user_confirmation_path
18 | #
19 | # Those helpers are included by default to ActionController::Base.
20 | #
21 | # In case you want to add such helpers to another class, you can do
22 | # that as long as this new class includes both url_helpers and
23 | # mounted_helpers. Example:
24 | #
25 | # include Rails.application.routes.url_helpers
26 | # include Rails.application.routes.mounted_helpers
27 | #
28 | module UrlHelpers
29 | def self.remove_helpers!
30 | self.instance_methods.map(&:to_s).grep(/_(url|path)$/).each do |method|
31 | remove_method method
32 | end
33 | end
34 |
35 | def self.generate_helpers!(routes=nil)
36 | routes ||= begin
37 | mappings = Devise.mappings.values.map(&:used_helpers).flatten.uniq
38 | Devise::URL_HELPERS.slice(*mappings)
39 | end
40 |
41 | routes.each do |module_name, actions|
42 | [:path, :url].each do |path_or_url|
43 | actions.each do |action|
44 | action = action ? "#{action}_" : ""
45 | method = "#{action}#{module_name}_#{path_or_url}"
46 |
47 | class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
48 | def #{method}(resource_or_scope, *args)
49 | scope = Devise::Mapping.find_scope!(resource_or_scope)
50 | _devise_route_context.send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
51 | end
52 | URL_HELPERS
53 | end
54 | end
55 | end
56 | end
57 |
58 | generate_helpers!(Devise::URL_HELPERS)
59 |
60 | private
61 |
62 | def _devise_route_context
63 | @_devise_route_context ||= send(Devise.available_router_name)
64 | end
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/lib/devise/models/validatable.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Models
3 | # Validatable creates all needed validations for a user email and password.
4 | # It's optional, given you may want to create the validations by yourself.
5 | # Automatically validate if the email is present, unique and its format is
6 | # valid. Also tests presence of password, confirmation and length.
7 | #
8 | # == Options
9 | #
10 | # Validatable adds the following options to devise_for:
11 | #
12 | # * +email_regexp+: the regular expression used to validate e-mails;
13 | # * +password_length+: a range expressing password length. Defaults to 8..128.
14 | #
15 | module Validatable
16 | # All validations used by this module.
17 | VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
18 | :validates_confirmation_of, :validates_length_of ].freeze
19 |
20 | def self.required_fields(klass)
21 | []
22 | end
23 |
24 | def self.included(base)
25 | base.extend ClassMethods
26 | assert_validations_api!(base)
27 |
28 | base.class_eval do
29 | validates_presence_of :email, :if => :email_required?
30 | validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?
31 | validates_format_of :email, :with => email_regexp, :allow_blank => true, :if => :email_changed?
32 |
33 | validates_presence_of :password, :if => :password_required?
34 | validates_confirmation_of :password, :if => :password_required?
35 | validates_length_of :password, :within => password_length, :allow_blank => true
36 | end
37 | end
38 |
39 | def self.assert_validations_api!(base) #:nodoc:
40 | unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
41 |
42 | unless unavailable_validations.empty?
43 | raise "Could not use :validatable module since #{base} does not respond " <<
44 | "to the following methods: #{unavailable_validations.to_sentence}."
45 | end
46 | end
47 |
48 | protected
49 |
50 | # Checks whether a password is needed or not. For validations only.
51 | # Passwords are always required if it's a new record, or if the password
52 | # or confirmation are being set somewhere.
53 | def password_required?
54 | !persisted? || !password.nil? || !password_confirmation.nil?
55 | end
56 |
57 | def email_required?
58 | true
59 | end
60 |
61 | module ClassMethods
62 | Devise::Models.config(self, :email_regexp, :password_length)
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/test/integration/trackable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TrackableHooksTest < ActionDispatch::IntegrationTest
4 |
5 | test "current and last sign in timestamps are updated on each sign in" do
6 | user = create_user
7 | assert_nil user.current_sign_in_at
8 | assert_nil user.last_sign_in_at
9 |
10 | sign_in_as_user
11 | user.reload
12 |
13 | assert_kind_of Time, user.current_sign_in_at
14 | assert_kind_of Time, user.last_sign_in_at
15 |
16 | assert_equal user.current_sign_in_at, user.last_sign_in_at
17 | assert user.current_sign_in_at >= user.created_at
18 |
19 | visit destroy_user_session_path
20 | new_time = 2.seconds.from_now
21 | Time.stubs(:now).returns(new_time)
22 |
23 | sign_in_as_user
24 | user.reload
25 | assert user.current_sign_in_at > user.last_sign_in_at
26 | end
27 |
28 | test "current and last sign in remote ip are updated on each sign in" do
29 | user = create_user
30 | assert_nil user.current_sign_in_ip
31 | assert_nil user.last_sign_in_ip
32 |
33 | sign_in_as_user
34 | user.reload
35 |
36 | assert_equal "127.0.0.1", user.current_sign_in_ip
37 | assert_equal "127.0.0.1", user.last_sign_in_ip
38 | end
39 |
40 | test "current remote ip returns original ip behind a non transparent proxy" do
41 | user = create_user
42 |
43 | arbitrary_ip = '200.121.1.69'
44 | sign_in_as_user do
45 | header 'HTTP_X_FORWARDED_FOR', arbitrary_ip
46 | end
47 | user.reload
48 | assert_equal arbitrary_ip, user.current_sign_in_ip
49 | end
50 |
51 | test "increase sign in count" do
52 | user = create_user
53 | assert_equal 0, user.sign_in_count
54 |
55 | sign_in_as_user
56 | user.reload
57 | assert_equal 1, user.sign_in_count
58 |
59 | visit destroy_user_session_path
60 | sign_in_as_user
61 | user.reload
62 | assert_equal 2, user.sign_in_count
63 | end
64 |
65 | test "does not update anything if user has signed out along the way" do
66 | swap Devise, :allow_unconfirmed_access_for => 0 do
67 | user = create_user(:confirm => false)
68 | sign_in_as_user
69 |
70 | user.reload
71 | assert_nil user.current_sign_in_at
72 | assert_nil user.last_sign_in_at
73 | end
74 | end
75 |
76 | test "do not track if devise.skip_trackable is set" do
77 | user = create_user
78 | sign_in_as_user do
79 | header 'devise.skip_trackable', '1'
80 | end
81 | user.reload
82 | assert_equal 0, user.sign_in_count
83 | visit destroy_user_session_path
84 |
85 | sign_in_as_user do
86 | header 'devise.skip_trackable', false
87 | end
88 | user.reload
89 | assert_equal 1, user.sign_in_count
90 | end
91 |
92 | end
93 |
--------------------------------------------------------------------------------
/test/generators/views_generator_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ViewsGeneratorTest < Rails::Generators::TestCase
4 | tests Devise::Generators::ViewsGenerator
5 | destination File.expand_path("../../tmp", __FILE__)
6 | setup :prepare_destination
7 |
8 | test "Assert all views are properly created with no params" do
9 | run_generator
10 | assert_files
11 | assert_shared_links
12 | end
13 |
14 | test "Assert all views are properly created with scope param" do
15 | run_generator %w(users)
16 | assert_files "users"
17 | assert_shared_links "users"
18 |
19 | run_generator %w(admins)
20 | assert_files "admins"
21 | assert_shared_links "admins"
22 | end
23 |
24 | test "Assert views with simple form" do
25 | run_generator %w(-b simple_form_for)
26 | assert_files
27 | assert_file "app/views/devise/confirmations/new.html.erb", /simple_form_for/
28 |
29 | run_generator %w(users -b simple_form_for)
30 | assert_files "users"
31 | assert_file "app/views/users/confirmations/new.html.erb", /simple_form_for/
32 | end
33 |
34 | test "Assert views with markerb" do
35 | run_generator %w(--markerb)
36 | assert_files nil, :mail_template_engine => "markerb"
37 | end
38 |
39 | def assert_files(scope = nil, options={})
40 | scope = "devise" if scope.nil?
41 | mail_template_engine = options[:mail_template_engine] || "html.erb"
42 |
43 | assert_file "app/views/#{scope}/confirmations/new.html.erb"
44 | assert_file "app/views/#{scope}/mailer/confirmation_instructions.#{mail_template_engine}"
45 | assert_file "app/views/#{scope}/mailer/reset_password_instructions.#{mail_template_engine}"
46 | assert_file "app/views/#{scope}/mailer/unlock_instructions.#{mail_template_engine}"
47 | assert_file "app/views/#{scope}/passwords/edit.html.erb"
48 | assert_file "app/views/#{scope}/passwords/new.html.erb"
49 | assert_file "app/views/#{scope}/registrations/new.html.erb"
50 | assert_file "app/views/#{scope}/registrations/edit.html.erb"
51 | assert_file "app/views/#{scope}/sessions/new.html.erb"
52 | assert_file "app/views/#{scope}/shared/_links.erb"
53 | assert_file "app/views/#{scope}/unlocks/new.html.erb"
54 | end
55 |
56 | def assert_shared_links(scope = nil)
57 | scope = "devise" if scope.nil?
58 | link = /<%= render \"#{scope}\/shared\/links\" %>/
59 |
60 | assert_file "app/views/#{scope}/passwords/edit.html.erb", link
61 | assert_file "app/views/#{scope}/passwords/new.html.erb", link
62 | assert_file "app/views/#{scope}/confirmations/new.html.erb", link
63 | assert_file "app/views/#{scope}/registrations/new.html.erb", link
64 | assert_file "app/views/#{scope}/sessions/new.html.erb", link
65 | assert_file "app/views/#{scope}/unlocks/new.html.erb", link
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/test/mailers/reset_password_instructions_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ResetPasswordInstructionsTest < ActionMailer::TestCase
4 | def setup
5 | setup_mailer
6 | Devise.mailer = 'Devise::Mailer'
7 | Devise.mailer_sender = 'test@example.com'
8 | end
9 |
10 | def teardown
11 | Devise.mailer = 'Devise::Mailer'
12 | Devise.mailer_sender = 'please-change-me@config-initializers-devise.com'
13 | end
14 |
15 | def user
16 | @user ||= begin
17 | user = create_user
18 | user.send_reset_password_instructions
19 | user
20 | end
21 | end
22 |
23 | def mail
24 | @mail ||= begin
25 | user
26 | ActionMailer::Base.deliveries.last
27 | end
28 | end
29 |
30 | test 'email sent after reseting the user password' do
31 | assert_not_nil mail
32 | end
33 |
34 | test 'content type should be set to html' do
35 | assert mail.content_type.include?('text/html')
36 | end
37 |
38 | test 'send confirmation instructions to the user email' do
39 | assert_equal [user.email], mail.to
40 | end
41 |
42 | test 'setup sender from configuration' do
43 | assert_equal ['test@example.com'], mail.from
44 | end
45 |
46 | test 'setup sender from custom mailer defaults' do
47 | Devise.mailer = 'Users::Mailer'
48 | assert_equal ['custom@example.com'], mail.from
49 | end
50 |
51 | test 'custom mailer renders parent mailer template' do
52 | Devise.mailer = 'Users::Mailer'
53 | assert_not_blank mail.body.encoded
54 | end
55 |
56 | test 'setup reply to as copy from sender' do
57 | assert_equal ['test@example.com'], mail.reply_to
58 | end
59 |
60 | test 'setup subject from I18n' do
61 | store_translations :en, :devise => { :mailer => { :reset_password_instructions => { :subject => 'Reset instructions' } } } do
62 | assert_equal 'Reset instructions', mail.subject
63 | end
64 | end
65 |
66 | test 'subject namespaced by model' do
67 | store_translations :en, :devise => { :mailer => { :reset_password_instructions => { :user_subject => 'User Reset Instructions' } } } do
68 | assert_equal 'User Reset Instructions', mail.subject
69 | end
70 | end
71 |
72 | test 'body should have user info' do
73 | assert_match user.email, mail.body.encoded
74 | end
75 |
76 | test 'body should have link to confirm the account' do
77 | host = ActionMailer::Base.default_url_options[:host]
78 | reset_url_regexp = %r{}
79 | assert_match reset_url_regexp, mail.body.encoded
80 | end
81 |
82 | test 'mailer sender accepts a proc' do
83 | swap Devise, :mailer_sender => proc { "another@example.com" } do
84 | assert_equal ['another@example.com'], mail.from
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/test/generators/active_record_generator_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | if DEVISE_ORM == :active_record
4 | require "generators/active_record/devise_generator"
5 |
6 | class ActiveRecordGeneratorTest < Rails::Generators::TestCase
7 | tests ActiveRecord::Generators::DeviseGenerator
8 | destination File.expand_path("../../tmp", __FILE__)
9 | setup :prepare_destination
10 |
11 | test "all files are properly created with rails31 migration syntax" do
12 | run_generator %w(monster)
13 | assert_file "app/models/monster.rb", /devise/, /attr_accessible (:[a-z_]+(, )?)+/
14 | assert_migration "db/migrate/devise_create_monsters.rb", /def change/
15 | end
16 |
17 | test "all files for namespaced model are properly created" do
18 | run_generator %w(admin/monster)
19 | assert_file "app/models/admin/monster.rb", /devise/, /attr_accessible (:[a-z_]+(, )?)+/
20 | assert_migration "db/migrate/devise_create_admin_monsters.rb", /def change/
21 | end
22 |
23 | test "update model migration when model exists" do
24 | run_generator %w(monster)
25 | assert_file "app/models/monster.rb"
26 | run_generator %w(monster)
27 | assert_migration "db/migrate/add_devise_to_monsters.rb"
28 | end
29 |
30 | test "all files are properly deleted" do
31 | run_generator %w(monster)
32 | run_generator %w(monster)
33 | assert_migration "db/migrate/devise_create_monsters.rb"
34 | assert_migration "db/migrate/add_devise_to_monsters.rb"
35 | run_generator %w(monster), :behavior => :revoke
36 | assert_no_migration "db/migrate/add_devise_to_monsters.rb"
37 | assert_migration "db/migrate/devise_create_monsters.rb"
38 | run_generator %w(monster), :behavior => :revoke
39 | assert_no_file "app/models/monster.rb"
40 | assert_no_migration "db/migrate/devise_create_monsters.rb"
41 | end
42 | end
43 |
44 | module RailsEngine
45 | class Engine < Rails::Engine
46 | isolate_namespace RailsEngine
47 | end
48 | end
49 |
50 | def simulate_inside_engine(engine, namespace)
51 | if Rails::Generators.respond_to?(:namespace=)
52 | swap Rails::Generators, :namespace => namespace do
53 | yield
54 | end
55 | else
56 | swap Rails, :application => engine.instance do
57 | yield
58 | end
59 | end
60 | end
61 |
62 | class ActiveRecordEngineGeneratorTest < Rails::Generators::TestCase
63 | tests ActiveRecord::Generators::DeviseGenerator
64 | destination File.expand_path("../../tmp", __FILE__)
65 | setup :prepare_destination
66 |
67 | test "all files are properly created" do
68 | simulate_inside_engine(RailsEngine::Engine, RailsEngine) do
69 | run_generator ["monster"]
70 |
71 | assert_file "app/models/rails_engine/monster.rb", /devise/,/attr_accessible (:[a-z_]+(, )?)+/
72 | end
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/generators/active_record/devise_generator.rb:
--------------------------------------------------------------------------------
1 | require 'rails/generators/active_record'
2 | require 'generators/devise/orm_helpers'
3 |
4 | module ActiveRecord
5 | module Generators
6 | class DeviseGenerator < ActiveRecord::Generators::Base
7 | argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
8 |
9 | include Devise::Generators::OrmHelpers
10 | source_root File.expand_path("../templates", __FILE__)
11 |
12 | def copy_devise_migration
13 | if (behavior == :invoke && model_exists?) || (behavior == :revoke && migration_exists?(table_name))
14 | migration_template "migration_existing.rb", "db/migrate/add_devise_to_#{table_name}"
15 | else
16 | migration_template "migration.rb", "db/migrate/devise_create_#{table_name}"
17 | end
18 | end
19 |
20 | def generate_model
21 | invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
22 | end
23 |
24 | def inject_devise_content
25 | content = model_contents + < false, :default => ""
46 | t.string :encrypted_password, :null => false, :default => ""
47 |
48 | ## Recoverable
49 | t.string :reset_password_token
50 | t.datetime :reset_password_sent_at
51 |
52 | ## Rememberable
53 | t.datetime :remember_created_at
54 |
55 | ## Trackable
56 | t.integer :sign_in_count, :default => 0
57 | t.datetime :current_sign_in_at
58 | t.datetime :last_sign_in_at
59 | t.string :current_sign_in_ip
60 | t.string :last_sign_in_ip
61 |
62 | ## Confirmable
63 | # t.string :confirmation_token
64 | # t.datetime :confirmed_at
65 | # t.datetime :confirmation_sent_at
66 | # t.string :unconfirmed_email # Only if using reconfirmable
67 |
68 | ## Lockable
69 | # t.integer :failed_attempts, :default => 0 # Only if lock strategy is :failed_attempts
70 | # t.string :unlock_token # Only if unlock strategy is :email or :both
71 | # t.datetime :locked_at
72 |
73 | ## Token authenticatable
74 | # t.string :authentication_token
75 | RUBY
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/test/integration/database_authenticatable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class DatabaseAuthenticationTest < ActionDispatch::IntegrationTest
4 | test 'sign in with email of different case should succeed when email is in the list of case insensitive keys' do
5 | create_user(:email => 'Foo@Bar.com')
6 |
7 | sign_in_as_user do
8 | fill_in 'email', :with => 'foo@bar.com'
9 | end
10 |
11 | assert warden.authenticated?(:user)
12 | end
13 |
14 | test 'sign in with email of different case should fail when email is NOT the list of case insensitive keys' do
15 | swap Devise, :case_insensitive_keys => [] do
16 | create_user(:email => 'Foo@Bar.com')
17 |
18 | sign_in_as_user do
19 | fill_in 'email', :with => 'foo@bar.com'
20 | end
21 |
22 | assert_not warden.authenticated?(:user)
23 | end
24 | end
25 |
26 | test 'sign in with email including extra spaces should succeed when email is in the list of strip whitespace keys' do
27 | create_user(:email => ' foo@bar.com ')
28 |
29 | sign_in_as_user do
30 | fill_in 'email', :with => 'foo@bar.com'
31 | end
32 |
33 | assert warden.authenticated?(:user)
34 | end
35 |
36 | test 'sign in with email including extra spaces should fail when email is NOT the list of strip whitespace keys' do
37 | swap Devise, :strip_whitespace_keys => [] do
38 | create_user(:email => 'foo@bar.com')
39 |
40 | sign_in_as_user do
41 | fill_in 'email', :with => ' foo@bar.com '
42 | end
43 |
44 | assert_not warden.authenticated?(:user)
45 | end
46 | end
47 |
48 | test 'sign in should not authenticate if not using proper authentication keys' do
49 | swap Devise, :authentication_keys => [:username] do
50 | sign_in_as_user
51 | assert_not warden.authenticated?(:user)
52 | end
53 | end
54 |
55 | test 'sign in with invalid email should return to sign in form with error message' do
56 | store_translations :en, :devise => { :failure => { :admin => { :not_found_in_database => 'Invalid email address' } } } do
57 | sign_in_as_admin do
58 | fill_in 'email', :with => 'wrongemail@test.com'
59 | end
60 |
61 | assert_contain 'Invalid email address'
62 | assert_not warden.authenticated?(:admin)
63 | end
64 | end
65 |
66 | test 'sign in with invalid pasword should return to sign in form with error message' do
67 | sign_in_as_admin do
68 | fill_in 'password', :with => 'abcdef'
69 | end
70 |
71 | assert_contain 'Invalid email or password'
72 | assert_not warden.authenticated?(:admin)
73 | end
74 |
75 | test 'error message is configurable by resource name' do
76 | store_translations :en, :devise => { :failure => { :admin => { :invalid => "Invalid credentials" } } } do
77 | sign_in_as_admin do
78 | fill_in 'password', :with => 'abcdef'
79 | end
80 |
81 | assert_contain 'Invalid credentials'
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/test/support/integration.rb:
--------------------------------------------------------------------------------
1 | require 'action_dispatch/testing/integration'
2 |
3 | class ActionDispatch::IntegrationTest
4 | def warden
5 | request.env['warden']
6 | end
7 |
8 | def create_user(options={})
9 | @user ||= begin
10 | user = User.create!(
11 | :username => 'usertest',
12 | :email => options[:email] || 'user@test.com',
13 | :password => options[:password] || '12345678',
14 | :password_confirmation => options[:password] || '12345678',
15 | :created_at => Time.now.utc
16 | )
17 | user.update_attribute(:confirmation_sent_at, options[:confirmation_sent_at]) if options[:confirmation_sent_at]
18 | user.confirm! unless options[:confirm] == false
19 | user.lock_access! if options[:locked] == true
20 | user
21 | end
22 | end
23 |
24 | def create_admin(options={})
25 | @admin ||= begin
26 | admin = Admin.create!(
27 | :email => options[:email] || 'admin@test.com',
28 | :password => '123456', :password_confirmation => '123456',
29 | :active => options[:active]
30 | )
31 | admin.confirm! unless options[:confirm] == false
32 | admin
33 | end
34 | end
35 |
36 | def sign_in_as_user(options={}, &block)
37 | user = create_user(options)
38 | visit_with_option options[:visit], new_user_session_path
39 | fill_in 'email', :with => options[:email] || 'user@test.com'
40 | fill_in 'password', :with => options[:password] || '12345678'
41 | check 'remember me' if options[:remember_me] == true
42 | yield if block_given?
43 | click_button 'Sign In'
44 | user
45 | end
46 |
47 | def sign_in_as_admin(options={}, &block)
48 | admin = create_admin(options)
49 | visit_with_option options[:visit], new_admin_session_path
50 | fill_in 'email', :with => 'admin@test.com'
51 | fill_in 'password', :with => '123456'
52 | yield if block_given?
53 | click_button 'Sign In'
54 | admin
55 | end
56 |
57 | # Fix assert_redirect_to in integration sessions because they don't take into
58 | # account Middleware redirects.
59 | #
60 | def assert_redirected_to(url)
61 | assert [301, 302].include?(@integration_session.status),
62 | "Expected status to be 301 or 302, got #{@integration_session.status}"
63 |
64 | assert_url url, @integration_session.headers["Location"]
65 | end
66 |
67 | def assert_current_url(expected)
68 | assert_url expected, current_url
69 | end
70 |
71 | def assert_url(expected, actual)
72 | assert_equal prepend_host(expected), prepend_host(actual)
73 | end
74 |
75 | protected
76 |
77 | def visit_with_option(given, default)
78 | case given
79 | when String
80 | visit given
81 | when FalseClass
82 | # Do nothing
83 | else
84 | visit default
85 | end
86 | end
87 |
88 | def prepend_host(url)
89 | url = "http://#{request.host}#{url}" if url[0] == ?/
90 | url
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/test/devise_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | module Devise
4 | def self.yield_and_restore
5 | @@warden_configured = nil
6 | c, b = @@warden_config, @@warden_config_block
7 | yield
8 | ensure
9 | @@warden_config, @@warden_config_block = c, b
10 | end
11 | end
12 |
13 | class DeviseTest < ActiveSupport::TestCase
14 | test 'model options can be configured through Devise' do
15 | swap Devise, :allow_unconfirmed_access_for => 113, :pepper => "foo" do
16 | assert_equal 113, Devise.allow_unconfirmed_access_for
17 | assert_equal "foo", Devise.pepper
18 | end
19 | end
20 |
21 | test 'setup block yields self' do
22 | Devise.setup do |config|
23 | assert_equal Devise, config
24 | end
25 | end
26 |
27 | test 'stores warden configuration' do
28 | assert_kind_of Devise::Delegator, Devise.warden_config.failure_app
29 | assert_equal :user, Devise.warden_config.default_scope
30 | end
31 |
32 | test 'warden manager user configuration through a block' do
33 | Devise.yield_and_restore do
34 | @executed = false
35 | Devise.warden do |config|
36 | @executed = true
37 | assert_kind_of Warden::Config, config
38 | end
39 |
40 | Devise.configure_warden!
41 | assert @executed
42 | end
43 | end
44 |
45 | test 'add new module using the helper method' do
46 | assert_nothing_raised(Exception) { Devise.add_module(:coconut) }
47 | assert_equal 1, Devise::ALL.select { |v| v == :coconut }.size
48 | assert_not Devise::STRATEGIES.include?(:coconut)
49 | assert_not defined?(Devise::Models::Coconut)
50 | Devise::ALL.delete(:coconut)
51 |
52 | assert_nothing_raised(Exception) { Devise.add_module(:banana, :strategy => :fruits) }
53 | assert_equal :fruits, Devise::STRATEGIES[:banana]
54 | Devise::ALL.delete(:banana)
55 | Devise::STRATEGIES.delete(:banana)
56 |
57 | assert_nothing_raised(Exception) { Devise.add_module(:kivi, :controller => :fruits) }
58 | assert_equal :fruits, Devise::CONTROLLERS[:kivi]
59 | Devise::ALL.delete(:kivi)
60 | Devise::CONTROLLERS.delete(:kivi)
61 | end
62 |
63 | test 'should complain when comparing empty or different sized passes' do
64 | [nil, ""].each do |empty|
65 | assert_not Devise.secure_compare(empty, "something")
66 | assert_not Devise.secure_compare("something", empty)
67 | assert_not Devise.secure_compare(empty, empty)
68 | end
69 | assert_not Devise.secure_compare("size_1", "size_four")
70 | end
71 |
72 | test 'Devise.email_regexp should match valid email addresses' do
73 | valid_emails = ["test@example.com", "jo@jo.co", "f4$_m@you.com", "testing.example@example.com.ua"]
74 | non_valid_emails = ["rex", "test@go,com", "test user@example.com", "test_user@example server.com"]
75 |
76 | valid_emails.each do |email|
77 | assert_match Devise.email_regexp, email
78 | end
79 | non_valid_emails.each do |email|
80 | assert_no_match Devise.email_regexp, email
81 | end
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/lib/devise/mailers/helpers.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Mailers
3 | module Helpers
4 | extend ActiveSupport::Concern
5 |
6 | included do
7 | include Devise::Controllers::ScopedViews
8 | attr_reader :scope_name, :resource
9 | end
10 |
11 | protected
12 |
13 | # Configure default email options
14 | def devise_mail(record, action, opts={})
15 | initialize_from_record(record)
16 | mail headers_for(action, opts)
17 | end
18 |
19 | def initialize_from_record(record)
20 | @scope_name = Devise::Mapping.find_scope!(record)
21 | @resource = instance_variable_set("@#{devise_mapping.name}", record)
22 | end
23 |
24 | def devise_mapping
25 | @devise_mapping ||= Devise.mappings[scope_name]
26 | end
27 |
28 | def headers_for(action, opts)
29 | headers = {
30 | :subject => subject_for(action),
31 | :to => resource.email,
32 | :from => mailer_sender(devise_mapping),
33 | :reply_to => mailer_reply_to(devise_mapping),
34 | :template_path => template_paths,
35 | :template_name => action
36 | }.merge(opts)
37 |
38 | if resource.respond_to?(:headers_for)
39 | ActiveSupport::Deprecation.warn "Calling headers_for in the model is no longer supported. " <<
40 | "Please customize your mailer instead."
41 | headers.merge!(resource.headers_for(action))
42 | end
43 |
44 | @email = headers[:to]
45 | headers
46 | end
47 |
48 | def mailer_reply_to(mapping)
49 | mailer_sender(mapping, :reply_to)
50 | end
51 |
52 | def mailer_from(mapping)
53 | mailer_sender(mapping, :from)
54 | end
55 |
56 | def mailer_sender(mapping, sender = :from)
57 | if default_params[sender].present?
58 | default_params[sender]
59 | elsif Devise.mailer_sender.is_a?(Proc)
60 | Devise.mailer_sender.call(mapping.name)
61 | else
62 | Devise.mailer_sender
63 | end
64 | end
65 |
66 | def template_paths
67 | template_path = _prefixes.dup
68 | template_path.unshift "#{@devise_mapping.scoped_path}/mailer" if self.class.scoped_views?
69 | template_path
70 | end
71 |
72 | # Setup a subject doing an I18n lookup. At first, it attemps to set a subject
73 | # based on the current mapping:
74 | #
75 | # en:
76 | # devise:
77 | # mailer:
78 | # confirmation_instructions:
79 | # user_subject: '...'
80 | #
81 | # If one does not exist, it fallbacks to ActionMailer default:
82 | #
83 | # en:
84 | # devise:
85 | # mailer:
86 | # confirmation_instructions:
87 | # subject: '...'
88 | #
89 | def subject_for(key)
90 | I18n.t(:"#{devise_mapping.name}_subject", :scope => [:devise, :mailer, key],
91 | :default => [:subject, key.to_s.humanize])
92 | end
93 | end
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/test/controllers/sessions_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SessionsControllerTest < ActionController::TestCase
4 | tests Devise::SessionsController
5 | include Devise::TestHelpers
6 |
7 | test "#create works even with scoped views" do
8 | swap Devise, :scoped_views => true do
9 | request.env["devise.mapping"] = Devise.mappings[:user]
10 | post :create
11 | assert_equal 200, @response.status
12 | assert_template "users/sessions/new"
13 | end
14 | end
15 |
16 | test "#create delete the url stored in the session if the requested format is navigational" do
17 | request.env["devise.mapping"] = Devise.mappings[:user]
18 | request.session["user_return_to"] = 'foo.bar'
19 |
20 | user = create_user
21 | user.confirm!
22 | post :create, :user => {
23 | :email => user.email,
24 | :password => user.password
25 | }
26 |
27 | assert_nil request.session["user_return_to"]
28 | end
29 |
30 | test "#create doesn't delete the url stored in the session if the requested format is not navigational" do
31 | request.env["devise.mapping"] = Devise.mappings[:user]
32 | request.session["user_return_to"] = 'foo.bar'
33 |
34 | user = create_user
35 | user.confirm!
36 | post :create, :format => 'json', :user => {
37 | :email => user.email,
38 | :password => user.password
39 | }
40 |
41 | assert_equal 'foo.bar', request.session["user_return_to"]
42 | end
43 |
44 | test "#create doesn't raise exception after Warden authentication fails when TestHelpers included" do
45 | request.env["devise.mapping"] = Devise.mappings[:user]
46 | post :create, :user => {
47 | :email => "nosuchuser@example.com",
48 | :password => "wevdude"
49 | }
50 | assert_equal 200, @response.status
51 | assert_template "devise/sessions/new"
52 | end
53 |
54 | test "#destroy doesn't set the flash if the requested format is not navigational" do
55 | request.env["devise.mapping"] = Devise.mappings[:user]
56 | user = create_user
57 | user.confirm!
58 | post :create, :format => 'json', :user => {
59 | :email => user.email,
60 | :password => user.password
61 | }
62 |
63 | delete :destroy, :format => 'json'
64 | assert flash[:notice].blank?, "flash[:notice] should be blank, not #{flash[:notice].inspect}"
65 | assert_equal 204, @response.status
66 | end
67 |
68 | if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:mass_assignment_sanitizer)
69 | test "#new doesn't raise mass-assignment exception even if sign-in key is attr_protected" do
70 | request.env["devise.mapping"] = Devise.mappings[:user]
71 |
72 | ActiveRecord::Base.mass_assignment_sanitizer = :strict
73 | User.class_eval { attr_protected :email }
74 |
75 | begin
76 | assert_nothing_raised ActiveModel::MassAssignmentSecurity::Error do
77 | get :new, :user => { :email => "allez viens!" }
78 | end
79 | ensure
80 | ActiveRecord::Base.mass_assignment_sanitizer = :logger
81 | User.class_eval { attr_accessible :email }
82 | end
83 | end
84 | end
85 | end
86 |
--------------------------------------------------------------------------------
/lib/devise/models/token_authenticatable.rb:
--------------------------------------------------------------------------------
1 | require 'devise/strategies/token_authenticatable'
2 |
3 | module Devise
4 | module Models
5 | # The TokenAuthenticatable module is responsible for generating an authentication token and
6 | # validating the authenticity of the same while signing in.
7 | #
8 | # This module only provides a few helpers to help you manage the token, but it is up to you
9 | # to choose how to use it. For example, if you want to have a new token every time the user
10 | # saves his account, you can do the following:
11 | #
12 | # before_save :reset_authentication_token
13 | #
14 | # On the other hand, if you want to generate token unless one exists, you should use instead:
15 | #
16 | # before_save :ensure_authentication_token
17 | #
18 | # If you want to delete the token after it is used, you can do so in the
19 | # after_token_authentication callback.
20 | #
21 | # == APIs
22 | #
23 | # If you are using token authentication with APIs and using trackable. Every
24 | # request will be considered as a new sign in (since there is no session in
25 | # APIs). You can disable this by creating a before filter as follow:
26 | #
27 | # before_filter :skip_trackable
28 | #
29 | # def skip_trackable
30 | # request.env['devise.skip_trackable'] = true
31 | # end
32 | #
33 | # == Options
34 | #
35 | # TokenAuthenticatable adds the following options to devise_for:
36 | #
37 | # * +token_authentication_key+: Defines name of the authentication token params key. E.g. /users/sign_in?some_key=...
38 | #
39 | module TokenAuthenticatable
40 | extend ActiveSupport::Concern
41 |
42 | def self.required_fields(klass)
43 | [:authentication_token]
44 | end
45 |
46 | # Generate new authentication token (a.k.a. "single access token").
47 | def reset_authentication_token
48 | self.authentication_token = self.class.authentication_token
49 | end
50 |
51 | # Generate new authentication token and save the record.
52 | def reset_authentication_token!
53 | reset_authentication_token
54 | save(:validate => false)
55 | end
56 |
57 | # Generate authentication token unless already exists.
58 | def ensure_authentication_token
59 | reset_authentication_token if authentication_token.blank?
60 | end
61 |
62 | # Generate authentication token unless already exists and save the record.
63 | def ensure_authentication_token!
64 | reset_authentication_token! if authentication_token.blank?
65 | end
66 |
67 | # Hook called after token authentication.
68 | def after_token_authentication
69 | end
70 |
71 | def expire_auth_token_on_timeout
72 | self.class.expire_auth_token_on_timeout
73 | end
74 |
75 | module ClassMethods
76 | def find_for_token_authentication(conditions)
77 | find_for_authentication(:authentication_token => conditions[token_authentication_key])
78 | end
79 |
80 | # Generate a token checking if one does not already exist in the database.
81 | def authentication_token
82 | generate_token(:authentication_token)
83 | end
84 |
85 | Devise::Models.config(self, :token_authentication_key, :expire_auth_token_on_timeout)
86 | end
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/test/mailers/confirmation_instructions_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ConfirmationInstructionsTest < ActionMailer::TestCase
4 |
5 | def setup
6 | setup_mailer
7 | Devise.mailer = 'Devise::Mailer'
8 | Devise.mailer_sender = 'test@example.com'
9 | end
10 |
11 | def teardown
12 | Devise.mailer = 'Devise::Mailer'
13 | Devise.mailer_sender = 'please-change-me@config-initializers-devise.com'
14 | end
15 |
16 | def user
17 | @user ||= create_user
18 | end
19 |
20 | def mail
21 | @mail ||= begin
22 | user
23 | ActionMailer::Base.deliveries.first
24 | end
25 | end
26 |
27 | test 'email sent after creating the user' do
28 | assert_not_nil mail
29 | end
30 |
31 | test 'content type should be set to html' do
32 | assert mail.content_type.include?('text/html')
33 | end
34 |
35 | test 'send confirmation instructions to the user email' do
36 | mail
37 | assert_equal [user.email], mail.to
38 | end
39 |
40 | test 'setup sender from configuration' do
41 | assert_equal ['test@example.com'], mail.from
42 | end
43 |
44 | test 'setup sender from custom mailer defaults' do
45 | Devise.mailer = 'Users::Mailer'
46 | assert_equal ['custom@example.com'], mail.from
47 | end
48 |
49 | test 'custom mailer renders parent mailer template' do
50 | Devise.mailer = 'Users::Mailer'
51 | assert_not_blank mail.body.encoded
52 | end
53 |
54 | test 'setup reply to as copy from sender' do
55 | assert_equal ['test@example.com'], mail.reply_to
56 | end
57 |
58 | test 'setup reply to as different if set in defaults' do
59 | Devise.mailer = 'Users::ReplyToMailer'
60 | assert_equal ['custom@example.com'], mail.from
61 | assert_equal ['custom_reply_to@example.com'], mail.reply_to
62 | end
63 |
64 | test 'setup subject from I18n' do
65 | store_translations :en, :devise => { :mailer => { :confirmation_instructions => { :subject => 'Account Confirmation' } } } do
66 | assert_equal 'Account Confirmation', mail.subject
67 | end
68 | end
69 |
70 | test 'subject namespaced by model' do
71 | store_translations :en, :devise => { :mailer => { :confirmation_instructions => { :user_subject => 'User Account Confirmation' } } } do
72 | assert_equal 'User Account Confirmation', mail.subject
73 | end
74 | end
75 |
76 | test 'body should have user info' do
77 | assert_match user.email, mail.body.encoded
78 | end
79 |
80 | test 'body should have link to confirm the account' do
81 | host = ActionMailer::Base.default_url_options[:host]
82 | confirmation_url_regexp = %r{}
83 | assert_match confirmation_url_regexp, mail.body.encoded
84 | end
85 |
86 | test 'renders a scoped if scoped_views is set to true' do
87 | swap Devise, :scoped_views => true do
88 | assert_equal user.email, mail.body.decoded
89 | end
90 | end
91 |
92 | test 'renders a scoped if scoped_views is set in the mailer class' do
93 | begin
94 | Devise::Mailer.scoped_views = true
95 | assert_equal user.email, mail.body.decoded
96 | ensure
97 | Devise::Mailer.send :remove_instance_variable, :@scoped_views
98 | end
99 | end
100 |
101 | test 'mailer sender accepts a proc' do
102 | swap Devise, :mailer_sender => proc { "another@example.com" } do
103 | assert_equal ['another@example.com'], mail.from
104 | end
105 | end
106 | end
107 |
--------------------------------------------------------------------------------
/test/rails_app/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # Resources for testing
3 | resources :users, :only => [:index] do
4 | get :expire, :on => :member
5 | get :accept, :on => :member
6 |
7 | authenticate do
8 | post :exhibit, :on => :member
9 | end
10 | end
11 |
12 | resources :admins, :only => [:index] do
13 | get :expire, :on => :member
14 | end
15 |
16 | # Users scope
17 | devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
18 |
19 | as :user do
20 | get "/as/sign_in", :to => "devise/sessions#new"
21 | end
22 |
23 | get "/sign_in", :to => "devise/sessions#new"
24 |
25 | # Admin scope
26 | devise_for :admin, :path => "admin_area", :controllers => { :sessions => :"admins/sessions" }, :skip => :passwords
27 |
28 | get "/admin_area/home", :to => "admins#index", :as => :admin_root
29 | get "/anywhere", :to => "foo#bar", :as => :new_admin_password
30 |
31 | authenticate(:admin) do
32 | get "/private", :to => "home#private", :as => :private
33 | end
34 |
35 | authenticate(:admin, lambda { |admin| admin.active? }) do
36 | get "/private/active", :to => "home#private", :as => :private_active
37 | end
38 |
39 | authenticated :admin do
40 | get "/dashboard", :to => "home#admin_dashboard"
41 | end
42 |
43 | authenticated :admin, lambda { |admin| admin.active? } do
44 | get "/dashboard/active", :to => "home#admin_dashboard"
45 | end
46 |
47 | authenticated do
48 | get "/dashboard", :to => "home#user_dashboard"
49 | end
50 |
51 | unauthenticated do
52 | get "/join", :to => "home#join"
53 | end
54 |
55 | # Routes for constraints testing
56 | devise_for :headquarters_admin, :class_name => "Admin", :path => "headquarters", :constraints => {:host => /192\.168\.1\.\d\d\d/}
57 |
58 | constraints(:host => /192\.168\.1\.\d\d\d/) do
59 | devise_for :homebase_admin, :class_name => "Admin", :path => "homebase"
60 | end
61 |
62 | devise_for :skip_admin, :class_name => "Admin", :skip => :all
63 |
64 | # Routes for format=false testing
65 | devise_for :htmlonly_admin, :class_name => "Admin", :skip => [:confirmations, :unlocks], :path => "htmlonly_admin", :format => false, :skip_helpers => [:confirmations, :unlocks]
66 | devise_for :htmlonly_users, :class_name => "User", :only => [:confirmations, :unlocks], :path => "htmlonly_users", :format => false, :skip_helpers => true
67 |
68 | # Other routes for routing_test.rb
69 | devise_for :reader, :class_name => "User", :only => :passwords
70 |
71 | scope :host => "sub.example.com" do
72 | devise_for :sub_admin, :class_name => "Admin"
73 | end
74 |
75 | namespace :publisher, :path_names => { :sign_in => "i_dont_care", :sign_out => "get_out" } do
76 | devise_for :accounts, :class_name => "Admin", :path_names => { :sign_in => "get_in" }
77 | end
78 |
79 | scope ":locale", :module => :invalid do
80 | devise_for :accounts, :singular => "manager", :class_name => "Admin",
81 | :path_names => {
82 | :sign_in => "login", :sign_out => "logout",
83 | :password => "secret", :confirmation => "verification",
84 | :unlock => "unblock", :sign_up => "register",
85 | :registration => "management", :cancel => "giveup"
86 | }, :failure_app => lambda { |env| [404, {"Content-Type" => "text/plain"}, ["Oops, not found"]] }, :module => :devise
87 | end
88 |
89 | namespace :sign_out_via, :module => "devise" do
90 | devise_for :deletes, :sign_out_via => :delete, :class_name => "Admin"
91 | devise_for :posts, :sign_out_via => :post, :class_name => "Admin"
92 | devise_for :delete_or_posts, :sign_out_via => [:delete, :post], :class_name => "Admin"
93 | end
94 |
95 | get "/set", :to => "home#set"
96 | get "/unauthenticated", :to => "home#unauthenticated"
97 | get "/custom_strategy/new"
98 |
99 | root :to => "home#index"
100 | end
101 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2 |
3 | en:
4 | devise:
5 | confirmations:
6 | confirmed: "Your account was successfully confirmed. You are now signed in."
7 | send_instructions: "You will receive an email with instructions about how to confirm your account in a few minutes."
8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions about how to confirm your account in a few minutes."
9 | failure:
10 | already_authenticated: "You are already signed in."
11 | inactive: "Your account was not activated yet."
12 | invalid: "Invalid email or password."
13 | invalid_token: "Invalid authentication token."
14 | locked: "Your account is locked."
15 | not_found_in_database: "Invalid email or password."
16 | timeout: "Your session expired, please sign in again to continue."
17 | unauthenticated: "You need to sign in or sign up before continuing."
18 | unconfirmed: "You have to confirm your account before continuing."
19 | mailer:
20 | confirmation_instructions:
21 | subject: "Confirmation instructions"
22 | reset_password_instructions:
23 | subject: "Reset password instructions"
24 | unlock_instructions:
25 | subject: "Unlock Instructions"
26 | omniauth_callbacks:
27 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"."
28 | success: "Successfully authenticated from %{kind} account."
29 | passwords:
30 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
31 | send_instructions: "You will receive an email with instructions about how to reset your password in a few minutes."
32 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
33 | updated: "Your password was changed successfully. You are now signed in."
34 | updated_not_active: "Your password was changed successfully."
35 | registrations:
36 | destroyed: "Bye! Your account was successfully cancelled. We hope to see you again soon."
37 | signed_up: "Welcome! You have signed up successfully."
38 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
39 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
40 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please open the link to activate your account."
41 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and click on the confirm link to finalize confirming your new email address."
42 | updated: "You updated your account successfully."
43 | sessions:
44 | signed_in: "Signed in successfully."
45 | signed_out: "Signed out successfully."
46 | unlocks:
47 | send_instructions: "You will receive an email with instructions about how to unlock your account in a few minutes."
48 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes."
49 | unlocked: "Your account has been unlocked successfully. Please sign in to continue."
50 | errors:
51 | messages:
52 | already_confirmed: "was already confirmed, please try signing in"
53 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one"
54 | expired: "has expired, please request a new one"
55 | not_found: "not found"
56 | not_locked: "was not locked"
57 | not_saved:
58 | one: "1 error prohibited this %{resource} from being saved:"
59 | other: "%{count} errors prohibited this %{resource} from being saved:"
60 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | devise (2.2.3)
5 | bcrypt-ruby (~> 3.0)
6 | orm_adapter (~> 0.1)
7 | railties (~> 3.1)
8 | warden (~> 1.2.1)
9 |
10 | GEM
11 | remote: http://rubygems.org/
12 | specs:
13 | actionmailer (3.2.11)
14 | actionpack (= 3.2.11)
15 | mail (~> 2.4.4)
16 | actionpack (3.2.11)
17 | activemodel (= 3.2.11)
18 | activesupport (= 3.2.11)
19 | builder (~> 3.0.0)
20 | erubis (~> 2.7.0)
21 | journey (~> 1.0.4)
22 | rack (~> 1.4.0)
23 | rack-cache (~> 1.2)
24 | rack-test (~> 0.6.1)
25 | sprockets (~> 2.2.1)
26 | activemodel (3.2.11)
27 | activesupport (= 3.2.11)
28 | builder (~> 3.0.0)
29 | activerecord (3.2.11)
30 | activemodel (= 3.2.11)
31 | activesupport (= 3.2.11)
32 | arel (~> 3.0.2)
33 | tzinfo (~> 0.3.29)
34 | activeresource (3.2.11)
35 | activemodel (= 3.2.11)
36 | activesupport (= 3.2.11)
37 | activesupport (3.2.11)
38 | i18n (~> 0.6)
39 | multi_json (~> 1.0)
40 | arel (3.0.2)
41 | bcrypt-ruby (3.0.1)
42 | builder (3.0.4)
43 | erubis (2.7.0)
44 | faraday (0.8.4)
45 | multipart-post (~> 1.1)
46 | hashie (1.2.0)
47 | hike (1.2.1)
48 | httpauth (0.2.0)
49 | i18n (0.6.1)
50 | journey (1.0.4)
51 | json (1.7.6)
52 | jwt (0.1.5)
53 | multi_json (>= 1.0)
54 | mail (2.4.4)
55 | i18n (>= 0.4.0)
56 | mime-types (~> 1.16)
57 | treetop (~> 1.4.8)
58 | metaclass (0.0.1)
59 | mime-types (1.19)
60 | mocha (0.10.0)
61 | metaclass (~> 0.0.1)
62 | mongoid (3.0.16)
63 | activemodel (~> 3.1)
64 | moped (~> 1.1)
65 | origin (~> 1.0)
66 | tzinfo (~> 0.3.22)
67 | moped (1.3.2)
68 | multi_json (1.5.0)
69 | multipart-post (1.1.5)
70 | nokogiri (1.5.5)
71 | oauth2 (0.8.0)
72 | faraday (~> 0.8)
73 | httpauth (~> 0.1)
74 | jwt (~> 0.1.4)
75 | multi_json (~> 1.0)
76 | rack (~> 1.2)
77 | omniauth (1.0.3)
78 | hashie (~> 1.2)
79 | rack
80 | omniauth-facebook (1.4.0)
81 | omniauth-oauth2 (~> 1.0.2)
82 | omniauth-oauth2 (1.0.3)
83 | oauth2 (~> 0.8.0)
84 | omniauth (~> 1.0)
85 | omniauth-openid (1.0.1)
86 | omniauth (~> 1.0)
87 | rack-openid (~> 1.3.1)
88 | origin (1.0.11)
89 | orm_adapter (0.4.0)
90 | polyglot (0.3.3)
91 | rack (1.4.3)
92 | rack-cache (1.2)
93 | rack (>= 0.4)
94 | rack-openid (1.3.1)
95 | rack (>= 1.1.0)
96 | ruby-openid (>= 2.1.8)
97 | rack-ssl (1.3.2)
98 | rack
99 | rack-test (0.6.2)
100 | rack (>= 1.0)
101 | rails (3.2.11)
102 | actionmailer (= 3.2.11)
103 | actionpack (= 3.2.11)
104 | activerecord (= 3.2.11)
105 | activeresource (= 3.2.11)
106 | activesupport (= 3.2.11)
107 | bundler (~> 1.0)
108 | railties (= 3.2.11)
109 | railties (3.2.11)
110 | actionpack (= 3.2.11)
111 | activesupport (= 3.2.11)
112 | rack-ssl (~> 1.3.2)
113 | rake (>= 0.8.7)
114 | rdoc (~> 3.4)
115 | thor (>= 0.14.6, < 2.0)
116 | rake (10.0.3)
117 | rdoc (3.12)
118 | json (~> 1.4)
119 | ruby-openid (2.2.2)
120 | sprockets (2.2.2)
121 | hike (~> 1.2)
122 | multi_json (~> 1.0)
123 | rack (~> 1.0)
124 | tilt (~> 1.1, != 1.3.0)
125 | sqlite3 (1.3.6)
126 | thor (0.16.0)
127 | tilt (1.3.3)
128 | treetop (1.4.12)
129 | polyglot
130 | polyglot (>= 0.3.1)
131 | tzinfo (0.3.35)
132 | warden (1.2.1)
133 | rack (>= 1.0)
134 | webrat (0.7.2)
135 | nokogiri (>= 1.2.0)
136 | rack (>= 1.0)
137 | rack-test (>= 0.5.3)
138 |
139 | PLATFORMS
140 | ruby
141 |
142 | DEPENDENCIES
143 | activerecord-jdbc-adapter
144 | activerecord-jdbcsqlite3-adapter
145 | devise!
146 | jruby-openssl
147 | mocha (= 0.10.0)
148 | mongoid (~> 3.0)
149 | omniauth (~> 1.0.0)
150 | omniauth-facebook
151 | omniauth-oauth2 (~> 1.0.0)
152 | omniauth-openid (~> 1.0.1)
153 | rails (~> 3.2.6)
154 | rdoc
155 | sqlite3
156 | webrat (= 0.7.2)
157 |
--------------------------------------------------------------------------------
/test/integration/timeoutable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SessionTimeoutTest < ActionDispatch::IntegrationTest
4 |
5 | def last_request_at
6 | @controller.user_session['last_request_at']
7 | end
8 |
9 | test 'set last request at in user session after each request' do
10 | sign_in_as_user
11 | old_last_request = last_request_at
12 | assert_not_nil last_request_at
13 |
14 | get users_path
15 | assert_not_nil last_request_at
16 | assert_not_equal old_last_request, last_request_at
17 | end
18 |
19 | test 'set last request at in user session after each request is skipped if tracking is disabled' do
20 | sign_in_as_user
21 | old_last_request = last_request_at
22 | assert_not_nil last_request_at
23 |
24 | get users_path, {}, 'devise.skip_trackable' => true
25 | assert_equal old_last_request, last_request_at
26 | end
27 |
28 | test 'does not time out user session before default limit time' do
29 | sign_in_as_user
30 | assert_response :success
31 | assert warden.authenticated?(:user)
32 |
33 | get users_path
34 | assert_response :success
35 | assert warden.authenticated?(:user)
36 | end
37 |
38 | test 'time out user session after default limit time' do
39 | user = sign_in_as_user
40 | get expire_user_path(user)
41 | assert_not_nil last_request_at
42 |
43 | get users_path
44 | assert_redirected_to users_path
45 | assert_not warden.authenticated?(:user)
46 | end
47 |
48 | test 'time out is not triggered on sign out' do
49 | user = sign_in_as_user
50 | get expire_user_path(user)
51 |
52 | get destroy_user_session_path
53 |
54 | assert_response :redirect
55 | assert_redirected_to root_path
56 | follow_redirect!
57 | assert_contain 'Signed out successfully'
58 | end
59 |
60 | test 'time out is not triggered on sign in' do
61 | user = sign_in_as_user
62 | get expire_user_path(user)
63 |
64 | post "/users/sign_in", :email => user.email, :password => "123456"
65 |
66 | assert_response :redirect
67 | follow_redirect!
68 | assert_contain 'You are signed in'
69 | end
70 |
71 | test 'admin does not explode on time out' do
72 | admin = sign_in_as_admin
73 | get expire_admin_path(admin)
74 |
75 | Admin.send :define_method, :reset_authentication_token! do
76 | nil
77 | end
78 |
79 | begin
80 | get admins_path
81 | assert_redirected_to admins_path
82 | assert_not warden.authenticated?(:admin)
83 | ensure
84 | Admin.send(:remove_method, :reset_authentication_token!)
85 | end
86 | end
87 |
88 | test 'user configured timeout limit' do
89 | swap Devise, :timeout_in => 8.minutes do
90 | user = sign_in_as_user
91 |
92 | get users_path
93 | assert_not_nil last_request_at
94 | assert_response :success
95 | assert warden.authenticated?(:user)
96 |
97 | get expire_user_path(user)
98 | get users_path
99 | assert_redirected_to users_path
100 | assert_not warden.authenticated?(:user)
101 | end
102 | end
103 |
104 | test 'error message with i18n' do
105 | store_translations :en, :devise => {
106 | :failure => { :user => { :timeout => 'Session expired!' } }
107 | } do
108 | user = sign_in_as_user
109 |
110 | get expire_user_path(user)
111 | get root_path
112 | follow_redirect!
113 | assert_contain 'Session expired!'
114 | end
115 | end
116 |
117 | test 'error message with i18n with double redirect' do
118 | store_translations :en, :devise => {
119 | :failure => { :user => { :timeout => 'Session expired!' } }
120 | } do
121 | user = sign_in_as_user
122 |
123 | get expire_user_path(user)
124 | get users_path
125 | follow_redirect!
126 | follow_redirect!
127 | assert_contain 'Session expired!'
128 | end
129 | end
130 |
131 | test 'time out not triggered if remembered' do
132 | user = sign_in_as_user :remember_me => true
133 | get expire_user_path(user)
134 | assert_not_nil last_request_at
135 |
136 | get users_path
137 | assert_response :success
138 | assert warden.authenticated?(:user)
139 | end
140 | end
141 |
--------------------------------------------------------------------------------
/test/test_helpers_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TestHelpersTest < ActionController::TestCase
4 | tests UsersController
5 | include Devise::TestHelpers
6 |
7 | class CustomFailureApp < Devise::FailureApp
8 | def redirect
9 | self.status = 306
10 | end
11 | end
12 |
13 | test "redirects if attempting to access a page unauthenticated" do
14 | get :index
15 | assert_redirected_to new_user_session_path
16 | assert_equal "You need to sign in or sign up before continuing.", flash[:alert]
17 | end
18 |
19 | test "redirects if attempting to access a page with an unconfirmed account" do
20 | swap Devise, :allow_unconfirmed_access_for => 0 do
21 | user = create_user
22 | assert !user.active_for_authentication?
23 |
24 | sign_in user
25 | get :index
26 | assert_redirected_to new_user_session_path
27 | end
28 | end
29 |
30 | test "returns nil if accessing current_user with an unconfirmed account" do
31 | swap Devise, :allow_unconfirmed_access_for => 0 do
32 | user = create_user
33 | assert !user.active_for_authentication?
34 |
35 | sign_in user
36 | get :accept, :id => user
37 | assert_nil assigns(:current_user)
38 | end
39 | end
40 |
41 | test "does not redirect with valid user" do
42 | user = create_user
43 | user.confirm!
44 |
45 | sign_in user
46 | get :index
47 | assert_response :success
48 | end
49 |
50 | test "does not redirect with valid user after failed first attempt" do
51 | get :index
52 | assert_response :redirect
53 |
54 | user = create_user
55 | user.confirm!
56 |
57 | sign_in user
58 | get :index
59 | assert_response :success
60 | end
61 |
62 | test "redirects if valid user signed out" do
63 | user = create_user
64 | user.confirm!
65 |
66 | sign_in user
67 | get :index
68 |
69 | sign_out user
70 | get :index
71 | assert_redirected_to new_user_session_path
72 | end
73 |
74 | test "respects custom failure app" do
75 | begin
76 | Devise.warden_config.failure_app = CustomFailureApp
77 | get :index
78 | assert_response 306
79 | ensure
80 | Devise.warden_config.failure_app = Devise::FailureApp
81 | end
82 | end
83 |
84 | test "returns the body of a failure app" do
85 | get :index
86 | assert_equal response.body, "You are being redirected."
87 | end
88 |
89 | test "defined Warden after_authentication callback should not be called when sign_in is called" do
90 | begin
91 | Warden::Manager.after_authentication do |user, auth, opts|
92 | flunk "callback was called while it should not"
93 | end
94 |
95 | user = create_user
96 | user.confirm!
97 | sign_in user
98 | ensure
99 | Warden::Manager._after_set_user.pop
100 | end
101 | end
102 |
103 | test "defined Warden before_logout callback should not be called when sign_out is called" do
104 | begin
105 | Warden::Manager.before_logout do |user, auth, opts|
106 | flunk "callback was called while it should not"
107 | end
108 | user = create_user
109 | user.confirm!
110 |
111 | sign_in user
112 | sign_out user
113 | ensure
114 | Warden::Manager._before_logout.pop
115 | end
116 | end
117 |
118 | test "before_failure call should work" do
119 | begin
120 | executed = false
121 | Warden::Manager.before_failure do |env,opts|
122 | executed = true
123 | end
124 |
125 | user = create_user
126 | sign_in user
127 |
128 | get :index
129 | assert executed
130 | ensure
131 | Warden::Manager._before_failure.pop
132 | end
133 | end
134 |
135 | test "allows to sign in with different users" do
136 | first_user = create_user
137 | first_user.confirm!
138 |
139 | sign_in first_user
140 | get :index
141 | assert_match /User ##{first_user.id}/, @response.body
142 | sign_out first_user
143 |
144 | second_user = create_user
145 | second_user.confirm!
146 |
147 | sign_in second_user
148 | get :index
149 | assert_match /User ##{second_user.id}/, @response.body
150 | end
151 | end
152 |
--------------------------------------------------------------------------------
/lib/generators/devise/views_generator.rb:
--------------------------------------------------------------------------------
1 | module Devise
2 | module Generators
3 | # Include this module in your generator to generate Devise views.
4 | # `copy_views` is the main method and by default copies all views
5 | # with forms.
6 | module ViewPathTemplates #:nodoc:
7 | extend ActiveSupport::Concern
8 |
9 | included do
10 | argument :scope, :required => false, :default => nil,
11 | :desc => "The scope to copy views to"
12 |
13 | # Le sigh, ensure Thor won't handle opts as args
14 | # It should be fixed in future Rails releases
15 | class_option :form_builder, :aliases => "-b"
16 | class_option :markerb
17 |
18 | public_task :copy_views
19 | end
20 |
21 | # TODO: Add this to Rails itself
22 | module ClassMethods
23 | def hide!
24 | Rails::Generators.hide_namespace self.namespace
25 | end
26 | end
27 |
28 | def copy_views
29 | view_directory :confirmations
30 | view_directory :passwords
31 | view_directory :registrations
32 | view_directory :sessions
33 | view_directory :unlocks
34 | end
35 |
36 | protected
37 |
38 | def view_directory(name, _target_path = nil)
39 | directory name.to_s, _target_path || "#{target_path}/#{name}" do |content|
40 | if scope
41 | content.gsub "devise/shared/links", "#{scope}/shared/links"
42 | else
43 | content
44 | end
45 | end
46 | end
47 |
48 | def target_path
49 | @target_path ||= "app/views/#{scope || :devise}"
50 | end
51 | end
52 |
53 | class SharedViewsGenerator < Rails::Generators::Base #:nodoc:
54 | include ViewPathTemplates
55 | source_root File.expand_path("../../../../app/views/devise", __FILE__)
56 | desc "Copies shared Devise views to your application."
57 | hide!
58 |
59 | # Override copy_views to just copy mailer and shared.
60 | def copy_views
61 | view_directory :shared
62 | end
63 | end
64 |
65 | class FormForGenerator < Rails::Generators::Base #:nodoc:
66 | include ViewPathTemplates
67 | source_root File.expand_path("../../../../app/views/devise", __FILE__)
68 | desc "Copies default Devise views to your application."
69 | hide!
70 | end
71 |
72 | class SimpleFormForGenerator < Rails::Generators::Base #:nodoc:
73 | include ViewPathTemplates
74 | source_root File.expand_path("../../templates/simple_form_for", __FILE__)
75 | desc "Copies simple form enabled views to your application."
76 | hide!
77 | end
78 |
79 | class ErbGenerator < Rails::Generators::Base #:nodoc:
80 | include ViewPathTemplates
81 | source_root File.expand_path("../../../../app/views/devise", __FILE__)
82 | desc "Copies Devise mail erb views to your application."
83 | hide!
84 |
85 | def copy_views
86 | view_directory :mailer
87 | end
88 | end
89 |
90 | class MarkerbGenerator < Rails::Generators::Base #:nodoc:
91 | include ViewPathTemplates
92 | source_root File.expand_path("../../templates", __FILE__)
93 | desc "Copies Devise mail markerb views to your application."
94 | hide!
95 |
96 | def copy_views
97 | view_directory :markerb, target_path
98 | end
99 |
100 | def target_path
101 | "app/views/#{scope || :devise}/mailer"
102 | end
103 | end
104 |
105 | class ViewsGenerator < Rails::Generators::Base
106 | desc "Copies Devise views to your application."
107 |
108 | argument :scope, :required => false, :default => nil,
109 | :desc => "The scope to copy views to"
110 |
111 | invoke SharedViewsGenerator
112 |
113 | hook_for :form_builder, :aliases => "-b",
114 | :desc => "Form builder to be used",
115 | :default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
116 |
117 | hook_for :markerb, :desc => "Generate markerb instead of erb mail views",
118 | :default => defined?(Markerb) ? :markerb : :erb,
119 | :type => :boolean
120 | end
121 | end
122 | end
123 |
--------------------------------------------------------------------------------
/test/integration/omniauthable_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 |
4 | class OmniauthableIntegrationTest < ActionDispatch::IntegrationTest
5 | FACEBOOK_INFO = {
6 | "id" => '12345',
7 | "link" => 'http://facebook.com/josevalim',
8 | "email" => 'user@example.com',
9 | "first_name" => 'Jose',
10 | "last_name" => 'Valim',
11 | "website" => 'http://blog.plataformatec.com.br'
12 | }
13 |
14 | setup do
15 | OmniAuth.config.test_mode = true
16 | OmniAuth.config.mock_auth[:facebook] = {
17 | "uid" => '12345',
18 | "provider" => 'facebook',
19 | "user_info" => {"nickname" => 'josevalim'},
20 | "credentials" => {"token" => 'plataformatec'},
21 | "extra" => {"user_hash" => FACEBOOK_INFO}
22 | }
23 | end
24 |
25 | teardown do
26 | OmniAuth.config.test_mode = false
27 | end
28 |
29 | def stub_action!(name)
30 | Users::OmniauthCallbacksController.class_eval do
31 | alias_method :__old_facebook, :facebook
32 | alias_method :facebook, name
33 | end
34 | yield
35 | ensure
36 | Users::OmniauthCallbacksController.class_eval do
37 | alias_method :facebook, :__old_facebook
38 | end
39 | end
40 |
41 | test "can access omniauth.auth in the env hash" do
42 | visit "/users/sign_in"
43 | click_link "Sign in with Facebook"
44 |
45 | json = ActiveSupport::JSON.decode(response.body)
46 |
47 | assert_equal "12345", json["uid"]
48 | assert_equal "facebook", json["provider"]
49 | assert_equal "josevalim", json["user_info"]["nickname"]
50 | assert_equal FACEBOOK_INFO, json["extra"]["user_hash"]
51 | assert_equal "plataformatec", json["credentials"]["token"]
52 | end
53 |
54 | test "cleans up session on sign up" do
55 | assert_no_difference "User.count" do
56 | visit "/users/sign_in"
57 | click_link "Sign in with Facebook"
58 | end
59 |
60 | assert session["devise.facebook_data"]
61 |
62 | assert_difference "User.count" do
63 | visit "/users/sign_up"
64 | fill_in "Password", :with => "12345678"
65 | fill_in "Password confirmation", :with => "12345678"
66 | click_button "Sign up"
67 | end
68 |
69 | assert_current_url "/"
70 | assert_contain "You have signed up successfully."
71 | assert_contain "Hello User user@example.com"
72 | assert_not session["devise.facebook_data"]
73 | end
74 |
75 | test "cleans up session on cancel" do
76 | assert_no_difference "User.count" do
77 | visit "/users/sign_in"
78 | click_link "Sign in with Facebook"
79 | end
80 |
81 | assert session["devise.facebook_data"]
82 | visit "/users/cancel"
83 | assert !session["devise.facebook_data"]
84 | end
85 |
86 | test "cleans up session on sign in" do
87 | assert_no_difference "User.count" do
88 | visit "/users/sign_in"
89 | click_link "Sign in with Facebook"
90 | end
91 |
92 | assert session["devise.facebook_data"]
93 | user = sign_in_as_user
94 | assert !session["devise.facebook_data"]
95 | end
96 |
97 | test "sign in and send remember token if configured" do
98 | visit "/users/sign_in"
99 | click_link "Sign in with Facebook"
100 | assert_nil warden.cookies["remember_user_token"]
101 |
102 | stub_action!(:sign_in_facebook) do
103 | create_user
104 | visit "/users/sign_in"
105 | click_link "Sign in with Facebook"
106 | assert warden.authenticated?(:user)
107 | assert warden.cookies["remember_user_token"]
108 | end
109 | end
110 |
111 | test "generates a proper link when SCRIPT_NAME is set" do
112 | header 'SCRIPT_NAME', '/q'
113 | visit "/users/sign_in"
114 | assert_select "a", :href => "/q/users/auth/facebook"
115 | end
116 |
117 | test "handles callback error parameter according to the specification" do
118 | OmniAuth.config.mock_auth[:facebook] = :access_denied
119 | visit "/users/auth/facebook/callback?error=access_denied"
120 | assert_current_url "/users/sign_in"
121 | assert_contain 'Could not authenticate you from Facebook because "Access denied".'
122 | end
123 |
124 | test "handles other exceptions from omniauth" do
125 | OmniAuth.config.mock_auth[:facebook] = :invalid_credentials
126 |
127 | visit "/users/sign_in"
128 | click_link "Sign in with Facebook"
129 |
130 | assert_current_url "/users/sign_in"
131 | assert_contain 'Could not authenticate you from Facebook because "Invalid credentials".'
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/test/models/validatable_test.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require 'test_helper'
3 |
4 | class ValidatableTest < ActiveSupport::TestCase
5 | test 'should require email to be set' do
6 | user = new_user(:email => nil)
7 | assert user.invalid?
8 | assert user.errors[:email]
9 | assert_equal 'can\'t be blank', user.errors[:email].join
10 | end
11 |
12 | test 'should require uniqueness of email if email has changed, allowing blank' do
13 | existing_user = create_user
14 |
15 | user = new_user(:email => '')
16 | assert user.invalid?
17 | assert_no_match(/taken/, user.errors[:email].join)
18 |
19 | user.email = existing_user.email
20 | assert user.invalid?
21 | assert_match(/taken/, user.errors[:email].join)
22 |
23 | user.save(:validate => false)
24 | assert user.valid?
25 | end
26 |
27 | test 'should require correct email format if email has changed, allowing blank' do
28 | user = new_user(:email => '')
29 | assert user.invalid?
30 | assert_not_equal 'is invalid', user.errors[:email].join
31 |
32 | %w{invalid_email_format 123 $$$ () ☃ bla@bla.}.each do |email|
33 | user.email = email
34 | assert user.invalid?, 'should be invalid with email ' << email
35 | assert_equal 'is invalid', user.errors[:email].join
36 | end
37 |
38 | user.save(:validate => false)
39 | assert user.valid?
40 | end
41 |
42 | test 'should accept valid emails' do
43 | %w(a.b.c@example.com test_mail@gmail.com any@any.net email@test.br 123@mail.test 1☃3@mail.test).each do |email|
44 | user = new_user(:email => email)
45 | assert user.valid?, 'should be valid with email ' << email
46 | assert_blank user.errors[:email]
47 | end
48 | end
49 |
50 | test 'should require password to be set when creating a new record' do
51 | user = new_user(:password => '', :password_confirmation => '')
52 | assert user.invalid?
53 | assert_equal 'can\'t be blank', user.errors[:password].join
54 | end
55 |
56 | test 'should require confirmation to be set when creating a new record' do
57 | user = new_user(:password => 'new_password', :password_confirmation => 'blabla')
58 | assert user.invalid?
59 | assert_equal 'doesn\'t match confirmation', user.errors[:password].join
60 | end
61 |
62 | test 'should require password when updating/reseting password' do
63 | user = create_user
64 |
65 | user.password = ''
66 | user.password_confirmation = ''
67 |
68 | assert user.invalid?
69 | assert_equal 'can\'t be blank', user.errors[:password].join
70 | end
71 |
72 | test 'should require confirmation when updating/reseting password' do
73 | user = create_user
74 | user.password_confirmation = 'another_password'
75 | assert user.invalid?
76 | assert_equal 'doesn\'t match confirmation', user.errors[:password].join
77 | end
78 |
79 | test 'should require a password with minimum of 6 characters' do
80 | user = new_user(:password => '12345', :password_confirmation => '12345')
81 | assert user.invalid?
82 | assert_equal 'is too short (minimum is 6 characters)', user.errors[:password].join
83 | end
84 |
85 | test 'should require a password with maximum of 128 characters long' do
86 | user = new_user(:password => 'x'*129, :password_confirmation => 'x'*129)
87 | assert user.invalid?
88 | assert_equal 'is too long (maximum is 128 characters)', user.errors[:password].join
89 | end
90 |
91 | test 'should not require password length when it\'s not changed' do
92 | user = create_user.reload
93 | user.password = user.password_confirmation = nil
94 | assert user.valid?
95 |
96 | user.password_confirmation = 'confirmation'
97 | assert user.invalid?
98 | assert_not (user.errors[:password].join =~ /is too long/)
99 | end
100 |
101 | test 'should complain about length even if possword is not required' do
102 | user = new_user(:password => 'x'*129, :password_confirmation => 'x'*129)
103 | user.stubs(:password_required?).returns(false)
104 | assert user.invalid?
105 | assert_equal 'is too long (maximum is 128 characters)', user.errors[:password].join
106 | end
107 |
108 | test 'should not be included in objects with invalid API' do
109 | assert_raise RuntimeError do
110 | Class.new.send :include, Devise::Models::Validatable
111 | end
112 | end
113 |
114 | test 'required_fields should be an empty array' do
115 | assert_equal Devise::Models::Validatable.required_fields(User), []
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/gemfiles/Gemfile.rails-3.1.x.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: ..
3 | specs:
4 | devise (2.2.0)
5 | bcrypt-ruby (~> 3.0)
6 | orm_adapter (~> 0.1)
7 | railties (~> 3.1)
8 | warden (~> 1.2.1)
9 |
10 | GEM
11 | remote: http://rubygems.org/
12 | specs:
13 | actionmailer (3.1.10)
14 | actionpack (= 3.1.10)
15 | mail (~> 2.3.3)
16 | actionpack (3.1.10)
17 | activemodel (= 3.1.10)
18 | activesupport (= 3.1.10)
19 | builder (~> 3.0.0)
20 | erubis (~> 2.7.0)
21 | i18n (~> 0.6)
22 | rack (~> 1.3.6)
23 | rack-cache (~> 1.2)
24 | rack-mount (~> 0.8.2)
25 | rack-test (~> 0.6.1)
26 | sprockets (~> 2.0.4)
27 | activemodel (3.1.10)
28 | activesupport (= 3.1.10)
29 | builder (~> 3.0.0)
30 | i18n (~> 0.6)
31 | activerecord (3.1.10)
32 | activemodel (= 3.1.10)
33 | activesupport (= 3.1.10)
34 | arel (~> 2.2.3)
35 | tzinfo (~> 0.3.29)
36 | activeresource (3.1.10)
37 | activemodel (= 3.1.10)
38 | activesupport (= 3.1.10)
39 | activesupport (3.1.10)
40 | multi_json (>= 1.0, < 1.3)
41 | arel (2.2.3)
42 | bcrypt-ruby (3.0.1)
43 | builder (3.0.4)
44 | columnize (0.3.6)
45 | erubis (2.7.0)
46 | faraday (0.8.4)
47 | multipart-post (~> 1.1)
48 | hashie (1.2.0)
49 | hike (1.2.1)
50 | httpauth (0.2.0)
51 | i18n (0.6.1)
52 | json (1.7.6)
53 | jwt (0.1.5)
54 | multi_json (>= 1.0)
55 | linecache (0.46)
56 | rbx-require-relative (> 0.0.4)
57 | mail (2.3.3)
58 | i18n (>= 0.4.0)
59 | mime-types (~> 1.16)
60 | treetop (~> 1.4.8)
61 | metaclass (0.0.1)
62 | mime-types (1.19)
63 | mocha (0.10.0)
64 | metaclass (~> 0.0.1)
65 | mongoid (3.0.16)
66 | activemodel (~> 3.1)
67 | moped (~> 1.1)
68 | origin (~> 1.0)
69 | tzinfo (~> 0.3.22)
70 | moped (1.3.2)
71 | multi_json (1.2.0)
72 | multipart-post (1.1.5)
73 | nokogiri (1.5.6)
74 | oauth2 (0.8.0)
75 | faraday (~> 0.8)
76 | httpauth (~> 0.1)
77 | jwt (~> 0.1.4)
78 | multi_json (~> 1.0)
79 | rack (~> 1.2)
80 | omniauth (1.0.3)
81 | hashie (~> 1.2)
82 | rack
83 | omniauth-facebook (1.4.0)
84 | omniauth-oauth2 (~> 1.0.2)
85 | omniauth-oauth2 (1.0.3)
86 | oauth2 (~> 0.8.0)
87 | omniauth (~> 1.0)
88 | omniauth-openid (1.0.1)
89 | omniauth (~> 1.0)
90 | rack-openid (~> 1.3.1)
91 | origin (1.0.11)
92 | orm_adapter (0.4.0)
93 | polyglot (0.3.3)
94 | rack (1.3.8)
95 | rack-cache (1.2)
96 | rack (>= 0.4)
97 | rack-mount (0.8.3)
98 | rack (>= 1.0.0)
99 | rack-openid (1.3.1)
100 | rack (>= 1.1.0)
101 | ruby-openid (>= 2.1.8)
102 | rack-ssl (1.3.2)
103 | rack
104 | rack-test (0.6.2)
105 | rack (>= 1.0)
106 | rails (3.1.10)
107 | actionmailer (= 3.1.10)
108 | actionpack (= 3.1.10)
109 | activerecord (= 3.1.10)
110 | activeresource (= 3.1.10)
111 | activesupport (= 3.1.10)
112 | bundler (~> 1.0)
113 | railties (= 3.1.10)
114 | railties (3.1.10)
115 | actionpack (= 3.1.10)
116 | activesupport (= 3.1.10)
117 | rack-ssl (~> 1.3.2)
118 | rake (>= 0.8.7)
119 | rdoc (~> 3.4)
120 | thor (~> 0.14.6)
121 | rake (10.0.3)
122 | rbx-require-relative (0.0.9)
123 | rdoc (3.12)
124 | json (~> 1.4)
125 | ruby-debug (0.10.4)
126 | columnize (>= 0.1)
127 | ruby-debug-base (~> 0.10.4.0)
128 | ruby-debug-base (0.10.4)
129 | linecache (>= 0.3)
130 | ruby-openid (2.2.2)
131 | sprockets (2.0.4)
132 | hike (~> 1.2)
133 | rack (~> 1.0)
134 | tilt (~> 1.1, != 1.3.0)
135 | sqlite3 (1.3.6)
136 | thor (0.14.6)
137 | tilt (1.3.3)
138 | treetop (1.4.12)
139 | polyglot
140 | polyglot (>= 0.3.1)
141 | tzinfo (0.3.35)
142 | warden (1.2.1)
143 | rack (>= 1.0)
144 | webrat (0.7.2)
145 | nokogiri (>= 1.2.0)
146 | rack (>= 1.0)
147 | rack-test (>= 0.5.3)
148 |
149 | PLATFORMS
150 | ruby
151 |
152 | DEPENDENCIES
153 | activerecord-jdbc-adapter
154 | activerecord-jdbcsqlite3-adapter
155 | devise!
156 | jruby-openssl
157 | mocha (= 0.10.0)
158 | mongoid (~> 3.0)
159 | omniauth (~> 1.0.0)
160 | omniauth-facebook
161 | omniauth-oauth2 (~> 1.0.0)
162 | omniauth-openid (~> 1.0.1)
163 | rails (~> 3.1.0)
164 | rdoc
165 | ruby-debug (>= 0.10.3)
166 | sqlite3
167 | webrat (= 0.7.2)
168 |
--------------------------------------------------------------------------------
/app/controllers/devise/registrations_controller.rb:
--------------------------------------------------------------------------------
1 | class Devise::RegistrationsController < DeviseController
2 | prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
3 | prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]
4 |
5 | # GET /resource/sign_up
6 | def new
7 | resource = build_resource({})
8 | respond_with resource
9 | end
10 |
11 | # POST /resource
12 | def create
13 | build_resource
14 |
15 | if resource.save
16 | if resource.active_for_authentication?
17 | set_flash_message :notice, :signed_up if is_navigational_format?
18 | sign_up(resource_name, resource)
19 | respond_with resource, :location => after_sign_up_path_for(resource)
20 | else
21 | set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
22 | expire_session_data_after_sign_in!
23 | respond_with resource, :location => after_inactive_sign_up_path_for(resource)
24 | end
25 | else
26 | clean_up_passwords resource
27 | respond_with resource
28 | end
29 | end
30 |
31 | # GET /resource/edit
32 | def edit
33 | render :edit
34 | end
35 |
36 | # PUT /resource
37 | # We need to use a copy of the resource because we don't want to change
38 | # the current user in place.
39 | def update
40 | self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
41 | prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
42 |
43 | if resource.update_with_password(resource_params)
44 | if is_navigational_format?
45 | flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
46 | :update_needs_confirmation : :updated
47 | set_flash_message :notice, flash_key
48 | end
49 | sign_in resource_name, resource, :bypass => true
50 | respond_with resource, :location => after_update_path_for(resource)
51 | else
52 | clean_up_passwords resource
53 | respond_with resource
54 | end
55 | end
56 |
57 | # DELETE /resource
58 | def destroy
59 | resource.destroy
60 | Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
61 | set_flash_message :notice, :destroyed if is_navigational_format?
62 | respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
63 | end
64 |
65 | # GET /resource/cancel
66 | # Forces the session data which is usually expired after sign
67 | # in to be expired now. This is useful if the user wants to
68 | # cancel oauth signing in/up in the middle of the process,
69 | # removing all OAuth session data.
70 | def cancel
71 | expire_session_data_after_sign_in!
72 | redirect_to new_registration_path(resource_name)
73 | end
74 |
75 | protected
76 |
77 | def update_needs_confirmation?(resource, previous)
78 | resource.respond_to?(:pending_reconfirmation?) &&
79 | resource.pending_reconfirmation? &&
80 | previous != resource.unconfirmed_email
81 | end
82 |
83 | # Build a devise resource passing in the session. Useful to move
84 | # temporary session data to the newly created user.
85 | def build_resource(hash=nil)
86 | hash ||= resource_params || {}
87 | self.resource = resource_class.new_with_session(hash, session)
88 | end
89 |
90 | # Signs in a user on sign up. You can overwrite this method in your own
91 | # RegistrationsController.
92 | def sign_up(resource_name, resource)
93 | sign_in(resource_name, resource)
94 | end
95 |
96 | # The path used after sign up. You need to overwrite this method
97 | # in your own RegistrationsController.
98 | def after_sign_up_path_for(resource)
99 | after_sign_in_path_for(resource)
100 | end
101 |
102 | # The path used after sign up for inactive accounts. You need to overwrite
103 | # this method in your own RegistrationsController.
104 | def after_inactive_sign_up_path_for(resource)
105 | respond_to?(:root_path) ? root_path : "/"
106 | end
107 |
108 | # The default url to be used after updating a resource. You need to overwrite
109 | # this method in your own RegistrationsController.
110 | def after_update_path_for(resource)
111 | signed_in_root_path(resource)
112 | end
113 |
114 | # Authenticates the current scope and gets the current resource from the session.
115 | def authenticate_scope!
116 | send(:"authenticate_#{resource_name}!", :force => true)
117 | self.resource = send(:"current_#{resource_name}")
118 | end
119 | end
120 |
--------------------------------------------------------------------------------
/test/controllers/internal_helpers_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class MyController < DeviseController
4 | end
5 |
6 | class HelpersTest < ActionController::TestCase
7 | tests MyController
8 |
9 | def setup
10 | @mock_warden = OpenStruct.new
11 | @controller.request.env['warden'] = @mock_warden
12 | @controller.request.env['devise.mapping'] = Devise.mappings[:user]
13 | end
14 |
15 | test 'get resource name from env' do
16 | assert_equal :user, @controller.resource_name
17 | end
18 |
19 | test 'get resource class from env' do
20 | assert_equal User, @controller.resource_class
21 | end
22 |
23 | test 'get resource instance variable from env' do
24 | @controller.instance_variable_set(:@user, user = User.new)
25 | assert_equal user, @controller.resource
26 | end
27 |
28 | test 'set resource instance variable from env' do
29 | user = @controller.send(:resource_class).new
30 | @controller.send(:resource=, user)
31 |
32 | assert_equal user, @controller.send(:resource)
33 | assert_equal user, @controller.instance_variable_get(:@user)
34 | end
35 |
36 | test 'get resource params from request params using resource name as key' do
37 | user_params = {'name' => 'Shirley Templar'}
38 | @controller.stubs(:params).returns(HashWithIndifferentAccess.new({'user' => user_params}))
39 |
40 | assert_equal user_params, @controller.resource_params
41 | end
42 |
43 | test 'resources methods are not controller actions' do
44 | assert @controller.class.action_methods.empty?
45 | end
46 |
47 | test 'require no authentication tests current mapping' do
48 | @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
49 | @mock_warden.expects(:user).with(:user).returns(User.new)
50 | @controller.expects(:redirect_to).with(root_path)
51 | @controller.send :require_no_authentication
52 | end
53 |
54 | test 'require no authentication only checks if already authenticated if no inputs strategies are available' do
55 | Devise.mappings[:user].expects(:no_input_strategies).returns([])
56 | @mock_warden.expects(:authenticate?).never
57 | @mock_warden.expects(:authenticated?).with(:user).once.returns(true)
58 | @mock_warden.expects(:user).with(:user).returns(User.new)
59 | @controller.expects(:redirect_to).with(root_path)
60 | @controller.send :require_no_authentication
61 | end
62 |
63 | test 'require no authentication sets a flash message' do
64 | @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true)
65 | @mock_warden.expects(:user).with(:user).returns(User.new)
66 | @controller.expects(:redirect_to).with(root_path)
67 | @controller.send :require_no_authentication
68 | assert flash[:alert] == I18n.t("devise.failure.already_authenticated")
69 | end
70 |
71 | test 'signed in resource returns signed in resource for current scope' do
72 | @mock_warden.expects(:authenticate).with(:scope => :user).returns(User.new)
73 | assert_kind_of User, @controller.signed_in_resource
74 | end
75 |
76 | test 'is a devise controller' do
77 | assert @controller.devise_controller?
78 | end
79 |
80 | test 'does not issue blank flash messages' do
81 | I18n.stubs(:t).returns(' ')
82 | @controller.send :set_flash_message, :notice, :send_instructions
83 | assert flash[:notice].nil?
84 | end
85 |
86 | test 'issues non-blank flash messages normally' do
87 | I18n.stubs(:t).returns('non-blank')
88 | @controller.send :set_flash_message, :notice, :send_instructions
89 | assert_equal 'non-blank', flash[:notice]
90 | end
91 |
92 | test 'uses custom i18n options' do
93 | @controller.stubs(:devise_i18n_options).returns(:default => "devise custom options")
94 | @controller.send :set_flash_message, :notice, :invalid_i18n_messagesend_instructions
95 | assert_equal 'devise custom options', flash[:notice]
96 | end
97 |
98 | test 'allows custom i18n options to override resource_name' do
99 | I18n.expects(:t).with("custom_resource_name.confirmed", anything)
100 | @controller.stubs(:devise_i18n_options).returns(:resource_name => "custom_resource_name")
101 | @controller.send :set_flash_message, :notice, :confirmed
102 | end
103 |
104 | test 'navigational_formats not returning a wild card' do
105 | MyController.send(:public, :navigational_formats)
106 | Devise.navigational_formats = [:"*/*", :html]
107 | assert_not @controller.navigational_formats.include?(:"*/*")
108 | MyController.send(:protected, :navigational_formats)
109 | end
110 | end
111 |
--------------------------------------------------------------------------------