├── 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 │ │ │ │ └── edit_form.html.erb │ │ │ └── layouts │ │ │ │ └── application.html.erb │ │ ├── active_record │ │ │ ├── shim.rb │ │ │ ├── admin.rb │ │ │ ├── user.rb │ │ │ ├── user_on_engine.rb │ │ │ ├── user_on_main_app.rb │ │ │ └── user_without_email.rb │ │ ├── mailers │ │ │ └── users │ │ │ │ ├── mailer.rb │ │ │ │ ├── from_proc_mailer.rb │ │ │ │ └── reply_to_mailer.rb │ │ ├── controllers │ │ │ ├── publisher │ │ │ │ ├── sessions_controller.rb │ │ │ │ └── registrations_controller.rb │ │ │ ├── admins_controller.rb │ │ │ ├── admins │ │ │ │ └── sessions_controller.rb │ │ │ ├── application_controller.rb │ │ │ ├── home_controller.rb │ │ │ ├── users │ │ │ │ └── omniauth_callbacks_controller.rb │ │ │ ├── custom │ │ │ │ └── registrations_controller.rb │ │ │ ├── application_with_fake_engine.rb │ │ │ └── users_controller.rb │ │ ├── helpers │ │ │ └── application_helper.rb │ │ └── mongoid │ │ │ ├── shim.rb │ │ │ ├── admin.rb │ │ │ ├── user_without_email.rb │ │ │ ├── user.rb │ │ │ ├── user_on_engine.rb │ │ │ └── user_on_main_app.rb │ ├── config │ │ ├── initializers │ │ │ ├── inflections.rb │ │ │ ├── session_store.rb │ │ │ ├── secret_token.rb │ │ │ └── backtrace_silencers.rb │ │ ├── environment.rb │ │ ├── boot.rb │ │ ├── database.yml │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── test.rb │ │ │ └── production.rb │ │ └── application.rb │ ├── bin │ │ ├── rake │ │ ├── bundle │ │ └── rails │ ├── config.ru │ ├── Rakefile │ ├── lib │ │ ├── shared_user_without_omniauth.rb │ │ ├── shared_admin.rb │ │ ├── shared_user.rb │ │ └── shared_user_without_email.rb │ └── db │ │ ├── schema.rb │ │ └── migrate │ │ └── 20100401102949_create_tables.rb ├── support │ ├── locale │ │ └── en.yml │ ├── mongoid.yml │ ├── action_controller │ │ └── record_identifier.rb │ ├── webrat │ │ └── integrations │ │ │ └── rails.rb │ ├── assertions.rb │ ├── http_method_compatibility.rb │ ├── helpers.rb │ └── integration.rb ├── models │ ├── omniauthable_test.rb │ ├── registerable_test.rb │ ├── authenticatable_test.rb │ ├── trackable_test.rb │ ├── timeoutable_test.rb │ └── serializable_test.rb ├── orm │ ├── mongoid.rb │ └── active_record.rb ├── rails_test.rb ├── controllers │ ├── load_hooks_controller_test.rb │ ├── helper_methods_test.rb │ ├── passwords_controller_test.rb │ ├── inherited_controller_i18n_messages_test.rb │ ├── custom_registrations_controller_test.rb │ ├── custom_strategy_test.rb │ └── url_helpers_test.rb ├── mailers │ ├── mailer_test.rb │ ├── unlock_instructions_test.rb │ ├── reset_password_instructions_test.rb │ └── confirmation_instructions_test.rb ├── delegator_test.rb ├── generators │ ├── mongoid_generator_test.rb │ ├── install_generator_test.rb │ ├── devise_generator_test.rb │ ├── controllers_generator_test.rb │ └── active_record_generator_test.rb ├── test │ └── integration_helpers_test.rb ├── test_models.rb ├── test_helper.rb ├── integration │ ├── mounted_engine_test.rb │ ├── trackable_test.rb │ └── database_authenticatable_test.rb ├── helpers │ └── devise_helper_test.rb ├── omniauth │ ├── url_helpers_test.rb │ └── config_test.rb ├── parameter_sanitizer_test.rb └── devise_test.rb ├── devise.png ├── lib ├── devise │ ├── version.rb │ ├── orm │ │ ├── active_record.rb │ │ └── mongoid.rb │ ├── rails │ │ └── warden_compat.rb │ ├── hooks │ │ ├── rememberable.rb │ │ ├── csrf_cleaner.rb │ │ ├── forgetable.rb │ │ ├── lockable.rb │ │ ├── activatable.rb │ │ ├── proxy.rb │ │ ├── trackable.rb │ │ └── timeoutable.rb │ ├── time_inflector.rb │ ├── controllers │ │ ├── scoped_views.rb │ │ ├── store_location.rb │ │ ├── rememberable.rb │ │ └── url_helpers.rb │ ├── delegator.rb │ ├── test_helpers.rb │ ├── strategies │ │ ├── base.rb │ │ ├── database_authenticatable.rb │ │ └── rememberable.rb │ ├── encryptor.rb │ ├── models │ │ ├── omniauthable.rb │ │ ├── registerable.rb │ │ ├── timeoutable.rb │ │ ├── trackable.rb │ │ └── validatable.rb │ ├── token_generator.rb │ ├── omniauth │ │ ├── url_helpers.rb │ │ └── config.rb │ ├── omniauth.rb │ ├── modules.rb │ ├── parameter_filter.rb │ ├── rails.rb │ ├── test │ │ └── integration_helpers.rb │ └── mailers │ │ └── helpers.rb └── generators │ ├── templates │ ├── markerb │ │ ├── password_change.markerb │ │ ├── confirmation_instructions.markerb │ │ ├── unlock_instructions.markerb │ │ └── reset_password_instructions.markerb │ ├── controllers │ │ ├── README │ │ ├── sessions_controller.rb │ │ ├── unlocks_controller.rb │ │ ├── confirmations_controller.rb │ │ ├── passwords_controller.rb │ │ ├── omniauth_callbacks_controller.rb │ │ └── registrations_controller.rb │ ├── simple_form_for │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ └── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ └── README │ ├── active_record │ ├── templates │ │ ├── migration.rb │ │ └── migration_existing.rb │ └── devise_generator.rb │ ├── devise │ ├── devise_generator.rb │ ├── orm_helpers.rb │ ├── install_generator.rb │ └── controllers_generator.rb │ └── mongoid │ └── devise_generator.rb ├── .gitignore ├── .yardopts ├── app ├── views │ └── devise │ │ ├── mailer │ │ ├── password_change.html.erb │ │ ├── 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.html.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 ├── bin └── test ├── gemfiles ├── Gemfile.rails-4.1-stable └── Gemfile.rails-4.2-stable ├── Gemfile ├── .travis.yml ├── devise.gemspec ├── Rakefile ├── MIT-LICENSE ├── CODE_OF_CONDUCT.md └── guides └── bug_report_templates └── integration_test.rb /test/rails_app/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/rails_app/app/views/home/join.html.erb: -------------------------------------------------------------------------------- 1 | Join 2 | -------------------------------------------------------------------------------- /test/rails_app/app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 | Home! 2 | -------------------------------------------------------------------------------- /test/rails_app/app/views/home/private.html.erb: -------------------------------------------------------------------------------- 1 | Private! 2 | -------------------------------------------------------------------------------- /devise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zog/devise/master/devise.png -------------------------------------------------------------------------------- /test/rails_app/app/active_record/shim.rb: -------------------------------------------------------------------------------- 1 | module Shim 2 | end 3 | -------------------------------------------------------------------------------- /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 2 | -------------------------------------------------------------------------------- /lib/devise/version.rb: -------------------------------------------------------------------------------- 1 | module Devise 2 | VERSION = "4.2.0".freeze 3 | end 4 | -------------------------------------------------------------------------------- /test/rails_app/app/views/home/admin_dashboard.html.erb: -------------------------------------------------------------------------------- 1 | Admin dashboard 2 | -------------------------------------------------------------------------------- /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/rails_app/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | ActiveSupport::Inflector.inflections do |inflect| 2 | end 3 | -------------------------------------------------------------------------------- /test/rails_app/app/mailers/users/mailer.rb: -------------------------------------------------------------------------------- 1 | class Users::Mailer < Devise::Mailer 2 | default from: 'custom@example.com' 3 | end 4 | -------------------------------------------------------------------------------- /test/rails_app/app/views/users/edit_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= button_to 'Update', update_form_user_path(current_user), method: 'put' %> 2 | -------------------------------------------------------------------------------- /test/rails_app/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/rails_app/app/controllers/publisher/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Publisher::SessionsController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /test/rails_app/app/views/admins/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | Welcome to "sessions/new" view! 2 | <%= render file: "devise/sessions/new" %> 3 | -------------------------------------------------------------------------------- /test/rails_app/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | RailsApp::Application.config.session_store :cookie_store, key: '_rails_app_session' 2 | -------------------------------------------------------------------------------- /test/rails_app/app/controllers/publisher/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Publisher::RegistrationsController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --protected 2 | --no-private 3 | --embed-mixin ClassMethods 4 | - 5 | README.md 6 | CHANGELOG.rdoc 7 | CONTRIBUTING.md 8 | MIT-LICENSE 9 | 10 | -------------------------------------------------------------------------------- /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/mailers/users/from_proc_mailer.rb: -------------------------------------------------------------------------------- 1 | class Users::FromProcMailer < Devise::Mailer 2 | default from: proc { 'custom@example.com' } 3 | end 4 | -------------------------------------------------------------------------------- /lib/devise/orm/active_record.rb: -------------------------------------------------------------------------------- 1 | require 'orm_adapter/adapters/active_record' 2 | 3 | ActiveSupport.on_load(:active_record) do 4 | extend Devise::Models 5 | end 6 | -------------------------------------------------------------------------------- /test/rails_app/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |
Hello <%= @resource.email %>!
2 | 3 |We're contacting you to notify you that your password has been changed.
4 | -------------------------------------------------------------------------------- /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/app/controllers/admins_controller.rb: -------------------------------------------------------------------------------- 1 | class AdminsController < ApplicationController 2 | before_action :authenticate_admin! 3 | 4 | def index 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/generators/templates/markerb/password_change.markerb: -------------------------------------------------------------------------------- 1 |Hello <%= @resource.email %>!
2 | 3 |We're contacting you to notify you that your password has been changed.
4 | -------------------------------------------------------------------------------- /test/rails_app/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /lib/devise/orm/mongoid.rb: -------------------------------------------------------------------------------- 1 | ActiveSupport.on_load(:mongoid) do 2 | require 'orm_adapter/adapters/mongoid' 3 | 4 | Mongoid::Document::ClassMethods.send :include, Devise::Models 5 | end 6 | -------------------------------------------------------------------------------- /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/app/mailers/users/reply_to_mailer.rb: -------------------------------------------------------------------------------- 1 | class Users::ReplyToMailer < Devise::Mailer 2 | default from: 'custom@example.com' 3 | default reply_to: 'custom_reply_to@example.com' 4 | end 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 | -------------------------------------------------------------------------------- /test/support/locale/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | devise: 3 | failure: 4 | user: 5 | does_not_exist: "User %{name} does not exist" 6 | errors: 7 | messages: 8 | taken: "has already been taken" 9 | -------------------------------------------------------------------------------- /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 | include ActiveModel::Serializers::Xml if Devise.rails5? 7 | end 8 | -------------------------------------------------------------------------------- /test/support/mongoid.yml: -------------------------------------------------------------------------------- 1 | test: 2 | <%= Mongoid::VERSION.to_i > 4 ? 'clients' : 'sessions' %>: 3 | default: 4 | database: devise-test-suite 5 | hosts: 6 | - localhost:<%= ENV['MONGODB_PORT'] || '27017' %> 7 | -------------------------------------------------------------------------------- /test/rails_app/app/active_record/user_on_engine.rb: -------------------------------------------------------------------------------- 1 | require 'shared_user_without_omniauth' 2 | 3 | class UserOnEngine < ActiveRecord::Base 4 | self.table_name = 'users' 5 | include Shim 6 | include SharedUserWithoutOmniauth 7 | end 8 | -------------------------------------------------------------------------------- /test/rails_app/app/active_record/user_on_main_app.rb: -------------------------------------------------------------------------------- 1 | require 'shared_user_without_omniauth' 2 | 3 | class UserOnMainApp < ActiveRecord::Base 4 | self.table_name = 'users' 5 | include Shim 6 | include SharedUserWithoutOmniauth 7 | end 8 | -------------------------------------------------------------------------------- /test/rails_app/app/active_record/user_without_email.rb: -------------------------------------------------------------------------------- 1 | require "shared_user_without_email" 2 | 3 | class UserWithoutEmail < ActiveRecord::Base 4 | self.table_name = 'users' 5 | include Shim 6 | include SharedUserWithoutEmail 7 | end 8 | 9 | -------------------------------------------------------------------------------- /lib/generators/templates/markerb/confirmation_instructions.markerb: -------------------------------------------------------------------------------- 1 | Welcome <%= @email %>! 2 | 3 | You can confirm your account through the link below: 4 | 5 | [Confirm my account](<%= confirmation_url(@resource, confirmation_token: @token) %>) 6 | -------------------------------------------------------------------------------- /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 7 | -------------------------------------------------------------------------------- /test/rails_app/config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | config = Rails.application.config 2 | 3 | config.secret_key_base = 'd588e99efff13a86461fd6ab82327823ad2f8feb5dc217ce652cdd9f0dfc5eb4b5a62a92d24d2574d7d51dfb1ea8dd453ea54e00cf672159a13104a135422a10' 4 | -------------------------------------------------------------------------------- /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: @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_equal 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_equal Devise::Models::Registerable.required_fields(User), [] 6 | end 7 | end 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 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /lib/devise/rails/warden_compat.rb: -------------------------------------------------------------------------------- 1 | module Warden::Mixins::Common 2 | def request 3 | @request ||= ActionDispatch::Request.new(env) 4 | end 5 | 6 | def reset_session! 7 | request.reset_session 8 | end 9 | 10 | def cookies 11 | request.cookie_jar 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/orm/mongoid.rb: -------------------------------------------------------------------------------- 1 | require 'mongoid/version' 2 | 3 | Mongoid.configure do |config| 4 | config.load!('test/support/mongoid.yml') 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/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 | [Unlock my account](<%= unlock_url(@resource, unlock_token: @token) %>) 8 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path(File.expand_path('../../test', __FILE__)) 3 | 4 | require 'bundler/setup' 5 | begin 6 | require 'rails/test_unit/minitest_plugin' 7 | rescue LoadError 8 | exec 'rake' 9 | end 10 | 11 | Rails::TestUnitReporter.executable = 'bin/test' 12 | 13 | exit Minitest.run(ARGV) 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) && options[:store] != false && 4 | record.remember_me && warden.authenticated?(scope) 5 | Devise::Hooks::Proxy.new(warden).remember_me(record) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /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: @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 15 | -------------------------------------------------------------------------------- /test/rails_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RailsTest < ActiveSupport::TestCase 4 | test 'correct initializer position' do 5 | initializer = Devise::Engine.initializers.detect { |i| i.name == 'devise.omniauth' } 6 | assert_equal :load_config_initializers, initializer.after 7 | assert_equal :build_middleware_stack, initializer.before 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/support/action_controller/record_identifier.rb: -------------------------------------------------------------------------------- 1 | # Since webrat uses ActionController::RecordIdentifier class that was moved to 2 | # ActionView namespace in Rails 4.1+ 3 | 4 | unless defined?(ActionController::RecordIdentifier) 5 | require 'action_view/record_identifier' 6 | 7 | module ActionController 8 | RecordIdentifier = ActionView::RecordIdentifier 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/devise/hooks/csrf_cleaner.rb: -------------------------------------------------------------------------------- 1 | Warden::Manager.after_authentication do |record, warden, options| 2 | clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) || 3 | warden.winning_strategy.clean_up_csrf? 4 | if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy 5 | warden.request.session.try(:delete, :_csrf_token) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/rails_app/lib/shared_user_without_omniauth.rb: -------------------------------------------------------------------------------- 1 | module SharedUserWithoutOmniauth 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | devise :database_authenticatable, :confirmable, :lockable, :recoverable, 6 | :registerable, :rememberable, :timeoutable, 7 | :trackable, :validatable, reconfirmable: false 8 | end 9 | 10 | def raw_confirmation_token 11 | @raw_confirmation_token 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /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 | [Change my password](<%= edit_password_url(@resource, reset_password_token: @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 | -------------------------------------------------------------------------------- /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 18 | -------------------------------------------------------------------------------- /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: @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/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/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 | -------------------------------------------------------------------------------- /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::Hooks::Proxy.new(warden).forget_me(record) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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 | unless record.failed_attempts.to_i.zero? 6 | record.failed_attempts = 0 7 | record.save(validate: false) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /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 | module Devise 6 | # Detection for minor differences between Rails 4 and 5 in tests. 7 | def self.rails5? 8 | Rails.version.start_with? '5' 9 | end 10 | end 11 | 12 | # Set up gems listed in the Gemfile. 13 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 14 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 15 | -------------------------------------------------------------------------------- /lib/devise/test_helpers.rb: -------------------------------------------------------------------------------- 1 | module Devise 2 | module TestHelpers 3 | def self.included(base) 4 | base.class_eval do 5 | ActiveSupport::Deprecation.warn <<-DEPRECATION 6 | [Devise] including `Devise::TestHelpers` is deprecated and will be removed from Devise. 7 | For controller tests, please include `Devise::Test::ControllerHelpers` instead. 8 | DEPRECATION 9 | include Devise::Test::ControllerHelpers 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |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 |Maybe you tried to change something you didn't have access to.
24 |You may have mistyped the address or the page may have moved.
24 |We've been notified about this issue and we'll take a look at it shortly.
24 |<%= notice %>
22 |<%= alert %>
23 | 24 | 4. You can copy Devise views (for customization) to your app by running: 25 | 26 | rails g devise:views 27 | 28 | =============================================================================== 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rake/testtask' 5 | require 'rdoc/task' 6 | 7 | desc 'Default: run tests for all ORMs.' 8 | task default: :test 9 | 10 | desc 'Run Devise tests for all ORMs.' 11 | task :pre_commit do 12 | Dir[File.join(File.dirname(__FILE__), 'test', 'orm', '*.rb')].each do |file| 13 | orm = File.basename(file).split(".").first 14 | # "Some day, my son, rake's inner wisdom will reveal itself. Until then, 15 | # take this `system` -- may its brute force protect you well." 16 | exit 1 unless system "rake test DEVISE_ORM=#{orm}" 17 | end 18 | end 19 | 20 | desc 'Run Devise unit tests.' 21 | Rake::TestTask.new(:test) do |t| 22 | t.libs << 'lib' 23 | t.libs << 'test' 24 | t.pattern = 'test/**/*_test.rb' 25 | t.verbose = true 26 | t.warning = false 27 | end 28 | 29 | desc 'Generate documentation for Devise.' 30 | Rake::RDocTask.new(:rdoc) do |rdoc| 31 | rdoc.rdoc_dir = 'rdoc' 32 | rdoc.title = 'Devise' 33 | rdoc.options << '--line-numbers' << '--inline-source' 34 | rdoc.rdoc_files.include('README.md') 35 | rdoc.rdoc_files.include('lib/**/*.rb') 36 | end 37 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2009-2016 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 | -------------------------------------------------------------------------------- /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: "1234567") 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 | 14 | if defined?(ActionController::Parameters) 15 | test 'does not passes an ActionController::Parameters to find_first_by_auth_conditions through find_or_initialize_with_errors' do 16 | user = create_user(email: 'example@example.com') 17 | attributes = ActionController::Parameters.new(email: 'example@example.com') 18 | 19 | User.expects(:find_first_by_auth_conditions).with('email' => 'example@example.com').returns(user) 20 | User.find_or_initialize_with_errors([:email], attributes) 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/controllers/passwords_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordsControllerTest < Devise::ControllerTestCase 4 | tests Devise::PasswordsController 5 | include Devise::Test::ControllerHelpers 6 | 7 | setup do 8 | request.env["devise.mapping"] = Devise.mappings[:user] 9 | @user = create_user.tap(&:confirm) 10 | @raw = @user.send_reset_password_instructions 11 | end 12 | 13 | def put_update_with_params 14 | put :update, params: { "user" => { 15 | "reset_password_token" => @raw, "password" => "1234567", "password_confirmation" => "1234567" 16 | } 17 | } 18 | end 19 | 20 | test 'redirect to after_sign_in_path_for if after_resetting_password_path_for is not overridden' do 21 | put_update_with_params 22 | assert_redirected_to "http://test.host/" 23 | end 24 | 25 | test 'redirect accordingly if after_resetting_password_path_for is overridden' do 26 | custom_path = "http://custom.path/" 27 | Devise::PasswordsController.any_instance.stubs(:after_resetting_password_path_for).with(@user).returns(custom_path) 28 | 29 | put_update_with_params 30 | assert_redirected_to custom_path 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /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 :rememberable, no_input: true 9 | end 10 | 11 | # Other authentications 12 | d.add_module :omniauthable, controller: :omniauth_callbacks, route: :omniauth_callback 13 | 14 | # Misc after 15 | routes = [nil, :new, :edit] 16 | d.add_module :recoverable, controller: :passwords, route: { password: routes } 17 | d.add_module :registerable, controller: :registrations, route: { registration: (routes << :cancel) } 18 | d.add_module :validatable 19 | 20 | # The ones which can sign out after 21 | routes = [nil, :new] 22 | d.add_module :confirmable, controller: :confirmations, route: { confirmation: routes } 23 | d.add_module :lockable, controller: :unlocks, route: { unlock: routes } 24 | d.add_module :timeoutable 25 | 26 | # Stats for last, so we make sure the user is really signed in 27 | d.add_module :trackable 28 | end 29 | -------------------------------------------------------------------------------- /lib/generators/templates/simple_form_for/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |Currently waiting confirmation for: <%= resource.unconfirmed_email %>
11 | <% end %> 12 | 13 | <%= f.input :password, autocomplete: "off", hint: "leave it blank if you don't want to change it", required: false %> 14 | <%= f.input :password_confirmation, required: false %> 15 | <%= f.input :current_password, hint: "we need your current password to confirm your changes", required: true %> 16 |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/integration/mounted_engine_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MyMountableEngine 4 | def self.call(env) 5 | ['200', { 'Content-Type' => 'text/html' }, ['Rendered content of MyMountableEngine']] 6 | end 7 | end 8 | 9 | # If disable_clear_and_finalize is set to true, Rails will not clear other routes when calling 10 | # again the draw method. Look at the source code at: 11 | # http://www.rubydoc.info/docs/rails/ActionDispatch/Routing/RouteSet:draw 12 | Rails.application.routes.disable_clear_and_finalize = true 13 | 14 | Rails.application.routes.draw do 15 | authenticate(:user) do 16 | mount MyMountableEngine, at: '/mountable_engine' 17 | end 18 | end 19 | 20 | class AuthenticatedMountedEngineTest < Devise::IntegrationTest 21 | test 'redirects to the sign in page when not authenticated' do 22 | get '/mountable_engine' 23 | follow_redirect! 24 | 25 | assert_response :ok 26 | assert_contain 'You need to sign in or sign up before continuing.' 27 | end 28 | 29 | test 'renders the mounted engine when authenticated' do 30 | sign_in_as_user 31 | get '/mountable_engine' 32 | 33 | assert_response :success 34 | assert_contain 'Rendered content of MyMountableEngine' 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
42 | 43 | <%= link_to "Back", :back %> 44 | -------------------------------------------------------------------------------- /app/controllers/devise/unlocks_controller.rb: -------------------------------------------------------------------------------- 1 | class Devise::UnlocksController < DeviseController 2 | prepend_before_action :require_no_authentication 3 | 4 | # GET /resource/unlock/new 5 | def new 6 | self.resource = resource_class.new 7 | end 8 | 9 | # POST /resource/unlock 10 | def create 11 | self.resource = resource_class.send_unlock_instructions(resource_params) 12 | yield resource if block_given? 13 | 14 | if successfully_sent?(resource) 15 | respond_with({}, location: after_sending_unlock_instructions_path_for(resource)) 16 | else 17 | respond_with(resource) 18 | end 19 | end 20 | 21 | # GET /resource/unlock?unlock_token=abcdef 22 | def show 23 | self.resource = resource_class.unlock_access_by_token(params[:unlock_token]) 24 | yield resource if block_given? 25 | 26 | if resource.errors.empty? 27 | set_flash_message! :notice, :unlocked 28 | respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) } 29 | else 30 | respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new } 31 | end 32 | end 33 | 34 | protected 35 | 36 | # The path used after sending unlock password instructions 37 | def after_sending_unlock_instructions_path_for(resource) 38 | new_session_path(resource) if is_navigational_format? 39 | end 40 | 41 | # The path used after unlocking the resource 42 | def after_unlock_path_for(resource) 43 | new_session_path(resource) if is_navigational_format? 44 | end 45 | 46 | def translation_scope 47 | 'devise.unlocks' 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /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 | refute new_user.timedout?(29.minutes.ago) 11 | end 12 | 13 | test 'should not be expired when params is nil' do 14 | refute 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 | refute 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 | refute 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 | refute user.timedout?(30.seconds.ago) 36 | 37 | Devise.timeout_in = 5.minutes 38 | refute 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_equal Devise::Models::Timeoutable.required_fields(User), [] 45 | end 46 | 47 | test 'should not raise error if remember_created_at is not empty and rememberable is disabled' do 48 | user = create_admin(remember_created_at: Time.current) 49 | assert user.timedout?(31.minutes.ago) 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/controllers/custom_registrations_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CustomRegistrationsControllerTest < Devise::ControllerTestCase 4 | tests Custom::RegistrationsController 5 | 6 | include Devise::Test::ControllerHelpers 7 | 8 | setup do 9 | request.env["devise.mapping"] = Devise.mappings[:user] 10 | @password = 'password' 11 | @user = create_user(password: @password, password_confirmation: @password).tap(&:confirm) 12 | end 13 | 14 | test "yield resource to block on create success" do 15 | post :create, params: { user: { email: "user@example.org", password: "password", password_confirmation: "password" } } 16 | assert @controller.create_block_called?, "create failed to yield resource to provided block" 17 | end 18 | 19 | test "yield resource to block on create failure" do 20 | post :create, params: { user: { } } 21 | assert @controller.create_block_called?, "create failed to yield resource to provided block" 22 | end 23 | 24 | test "yield resource to block on update success" do 25 | sign_in @user 26 | put :update, params: { user: { current_password: @password } } 27 | assert @controller.update_block_called?, "update failed to yield resource to provided block" 28 | end 29 | 30 | test "yield resource to block on update failure" do 31 | sign_in @user 32 | put :update, params: { user: { } } 33 | assert @controller.update_block_called?, "update failed to yield resource to provided block" 34 | end 35 | 36 | test "yield resource to block on new" do 37 | get :new 38 | assert @controller.new_block_called?, "new failed to yield resource to provided block" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /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 do |app| 15 | app.reload_routes! if Devise.reload_routes 16 | end 17 | 18 | initializer "devise.url_helpers" do 19 | Devise.include_helpers(Devise::Controllers) 20 | end 21 | 22 | initializer "devise.omniauth", after: :load_config_initializers, before: :build_middleware_stack do |app| 23 | Devise.omniauth_configs.each do |provider, config| 24 | app.middleware.use config.strategy_class, *config.args do |strategy| 25 | config.strategy = strategy 26 | end 27 | end 28 | 29 | if Devise.omniauth_configs.any? 30 | Devise.include_helpers(Devise::OmniAuth) 31 | end 32 | end 33 | 34 | initializer "devise.secret_key" do |app| 35 | if app.respond_to?(:secrets) 36 | Devise.secret_key ||= app.secrets.secret_key_base 37 | elsif app.config.respond_to?(:secret_key_base) 38 | Devise.secret_key ||= app.config.secret_key_base 39 | end 40 | 41 | Devise.token_generator ||= 42 | if secret_key = Devise.secret_key 43 | Devise::TokenGenerator.new( 44 | ActiveSupport::CachingKeyGenerator.new(ActiveSupport::KeyGenerator.new(secret_key)) 45 | ) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/generators/templates/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class <%= @scope_prefix %>RegistrationsController < Devise::RegistrationsController 2 | # before_action :configure_sign_up_params, only: [:create] 3 | # before_action :configure_account_update_params, only: [:update] 4 | 5 | # GET /resource/sign_up 6 | # def new 7 | # super 8 | # end 9 | 10 | # POST /resource 11 | # def create 12 | # super 13 | # end 14 | 15 | # GET /resource/edit 16 | # def edit 17 | # super 18 | # end 19 | 20 | # PUT /resource 21 | # def update 22 | # super 23 | # end 24 | 25 | # DELETE /resource 26 | # def destroy 27 | # super 28 | # end 29 | 30 | # GET /resource/cancel 31 | # Forces the session data which is usually expired after sign 32 | # in to be expired now. This is useful if the user wants to 33 | # cancel oauth signing in/up in the middle of the process, 34 | # removing all OAuth session data. 35 | # def cancel 36 | # super 37 | # end 38 | 39 | # protected 40 | 41 | # If you have extra params to permit, append them to the sanitizer. 42 | # def configure_sign_up_params 43 | # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) 44 | # end 45 | 46 | # If you have extra params to permit, append them to the sanitizer. 47 | # def configure_account_update_params 48 | # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) 49 | # end 50 | 51 | # The path used after sign up. 52 | # def after_sign_up_path_for(resource) 53 | # super(resource) 54 | # end 55 | 56 | # The path used after sign up for inactive accounts. 57 | # def after_inactive_sign_up_path_for(resource) 58 | # super(resource) 59 | # end 60 | end 61 | -------------------------------------------------------------------------------- /test/helpers/devise_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DeviseHelperTest < Devise::IntegrationTest 4 | setup do 5 | model_labels = { models: { user: "the user" } } 6 | translations = { 7 | errors: { messages: { not_saved: { 8 | one: "Can't save %{resource} because of 1 error", 9 | other: "Can't save %{resource} because of %{count} errors", 10 | } } }, 11 | activerecord: model_labels, 12 | mongoid: model_labels 13 | } 14 | 15 | I18n.available_locales 16 | I18n.backend.store_translations(:en, translations) 17 | end 18 | 19 | teardown do 20 | I18n.reload! 21 | end 22 | 23 | test 'test errors.messages.not_saved with single error from i18n' do 24 | get new_user_registration_path 25 | 26 | fill_in 'password', with: 'new_user123' 27 | fill_in 'password confirmation', with: 'new_user123' 28 | click_button 'Sign up' 29 | 30 | assert_have_selector '#error_explanation' 31 | assert_contain "Can't save the user because of 1 error" 32 | end 33 | 34 | test 'test errors.messages.not_saved with multiple errors from i18n' do 35 | # Dirty tracking behavior prevents email validations from being applied: 36 | # https://github.com/mongoid/mongoid/issues/756 37 | (pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1 38 | 39 | get new_user_registration_path 40 | 41 | fill_in 'email', with: 'invalid_email' 42 | fill_in 'password', with: 'new_user123' 43 | fill_in 'password confirmation', with: 'new_user321' 44 | click_button 'Sign up' 45 | 46 | assert_have_selector '#error_explanation' 47 | assert_contain "Can't save the user because of 2 errors" 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/generators/devise/controllers_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | 3 | module Devise 4 | module Generators 5 | class ControllersGenerator < Rails::Generators::Base 6 | CONTROLLERS = %w(confirmations passwords registrations sessions unlocks omniauth_callbacks).freeze 7 | 8 | desc <<-DESC.strip_heredoc 9 | Create inherited Devise controllers in your app/controllers folder. 10 | 11 | Use -c to specify which controller you want to overwrite. 12 | If you do no specify a controller, all controllers will be created. 13 | For example: 14 | 15 | rails generate devise:controllers users -c=sessions 16 | 17 | This will create a controller class at app/controllers/users/sessions_controller.rb like this: 18 | 19 | class Users::ConfirmationsController < Devise::ConfirmationsController 20 | content... 21 | end 22 | DESC 23 | 24 | source_root File.expand_path("../../templates/controllers", __FILE__) 25 | argument :scope, required: true, 26 | desc: "The scope to create controllers in, e.g. users, admins" 27 | class_option :controllers, aliases: "-c", type: :array, 28 | desc: "Select specific controllers to generate (#{CONTROLLERS.join(', ')})" 29 | 30 | def create_controllers 31 | @scope_prefix = scope.blank? ? '' : (scope.camelize + '::') 32 | controllers = options[:controllers] || CONTROLLERS 33 | controllers.each do |name| 34 | template "#{name}_controller.rb", 35 | "app/controllers/#{scope}/#{name}_controller.rb" 36 | end 37 | end 38 | 39 | def show_readme 40 | readme "README" if behavior == :invoke 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /app/controllers/devise/confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | class Devise::ConfirmationsController < DeviseController 2 | # GET /resource/confirmation/new 3 | def new 4 | self.resource = resource_class.new 5 | end 6 | 7 | # POST /resource/confirmation 8 | def create 9 | self.resource = resource_class.send_confirmation_instructions(resource_params) 10 | yield resource if block_given? 11 | 12 | if successfully_sent?(resource) 13 | respond_with({}, location: after_resending_confirmation_instructions_path_for(resource_name)) 14 | else 15 | respond_with(resource) 16 | end 17 | end 18 | 19 | # GET /resource/confirmation?confirmation_token=abcdef 20 | def show 21 | self.resource = resource_class.confirm_by_token(params[:confirmation_token]) 22 | yield resource if block_given? 23 | 24 | if resource.errors.empty? 25 | set_flash_message!(:notice, :confirmed) 26 | respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, 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 resending confirmation instructions. 35 | def after_resending_confirmation_instructions_path_for(resource_name) 36 | is_navigational_format? ? new_session_path(resource_name) : '/' 37 | end 38 | 39 | # The path used after confirmation. 40 | def after_confirmation_path_for(resource_name, resource) 41 | if signed_in?(resource_name) 42 | signed_in_root_path(resource) 43 | else 44 | new_session_path(resource_name) 45 | end 46 | end 47 | 48 | def translation_scope 49 | 'devise.confirmations' 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/support/http_method_compatibility.rb: -------------------------------------------------------------------------------- 1 | module Devise 2 | class IntegrationTest < ActionDispatch::IntegrationTest 3 | # %w( get post patch put head delete xml_http_request 4 | # xhr get_via_redirect post_via_redirect 5 | # ).each do |method| 6 | %w( get post put ).each do |method| 7 | if Rails.version >= '5.0.0' 8 | define_method(method) do |url, options={}| 9 | if options.empty? 10 | super url 11 | else 12 | super url, options 13 | end 14 | end 15 | else 16 | define_method(method) do |url, options={}| 17 | if options[:xhr]==true 18 | xml_http_request __method__, url, options[:params] || {}, options[:headers] 19 | else 20 | super url, options[:params] || {}, options[:headers] 21 | end 22 | end 23 | end 24 | end 25 | end 26 | 27 | class ControllerTestCase < ActionController::TestCase 28 | # %w( get post patch put head delete xml_http_request 29 | # xhr get_via_redirect post_via_redirect 30 | # ).each do |method| 31 | %w( get post put ).each do |method| 32 | if Rails.version >= '5.0.0' 33 | define_method(method) do |action, options={}| 34 | if options.empty? 35 | super action 36 | else 37 | super action, options 38 | end 39 | end 40 | else 41 | define_method(method) do |action, options={}| 42 | if options[:xhr]==true 43 | xml_http_request __method__, action, options[:params] || {}, options[:headers] 44 | else 45 | super action, options[:params] || {}, options[:headers] 46 | end 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/generators/mongoid/devise_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/named_base' 2 | require 'generators/devise/orm_helpers' 3 | 4 | module Mongoid 5 | module Generators 6 | class DeviseGenerator < Rails::Generators::NamedBase 7 | include Devise::Generators::OrmHelpers 8 | 9 | def generate_model 10 | invoke "mongoid:model", [name] unless model_exists? && behavior == :invoke 11 | end 12 | 13 | def inject_field_types 14 | inject_into_file model_path, migration_data, after: "include Mongoid::Document\n" if model_exists? 15 | end 16 | 17 | def inject_devise_content 18 | inject_into_file model_path, model_contents, after: "include Mongoid::Document\n" if model_exists? 19 | end 20 | 21 | def migration_data 22 | <= "5.0.0" 18 | config.public_file_server.enabled = true 19 | config.public_file_server.headers = {'Cache-Control' => 'public, max-age=3600'} 20 | elsif Rails.version >= "4.2.0" 21 | config.serve_static_files = true 22 | config.static_cache_control = "public, max-age=3600" 23 | else 24 | config.serve_static_assets = true 25 | config.static_cache_control = "public, max-age=3600" 26 | end 27 | 28 | # Show full error reports and disable caching. 29 | config.consider_all_requests_local = true 30 | config.action_controller.perform_caching = false 31 | 32 | # Raise exceptions instead of rendering exception templates. 33 | config.action_dispatch.show_exceptions = false 34 | 35 | # Disable request forgery protection in test environment. 36 | config.action_controller.allow_forgery_protection = false 37 | 38 | # Tell Action Mailer not to deliver emails to the real world. 39 | # The :test delivery method accumulates sent emails in the 40 | # ActionMailer::Base.deliveries array. 41 | config.action_mailer.delivery_method = :test 42 | 43 | # Print deprecation notices to the stderr. 44 | config.active_support.deprecation = :stderr 45 | end 46 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic addresses, without explicit permission 14 | * Other unethical or unprofessional conduct. 15 | 16 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 17 | 18 | This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by sending an email to [conduct@plataformatec.com.br](conduct@plataformatec.com.br) or contacting one or more of the project maintainers. 21 | 22 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 23 | -------------------------------------------------------------------------------- /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. Useful when signing in is done 5 | # through a callback, like in OmniAuth. 6 | module Rememberable 7 | # Return default cookie values retrieved from session options. 8 | def self.cookie_values 9 | Rails.configuration.session_options.slice(:path, :domain, :secure) 10 | end 11 | 12 | def remember_me_is_active?(resource) 13 | return false unless resource.respond_to?(:remember_me) 14 | scope = Devise::Mapping.find_scope!(resource) 15 | _, token, generated_at = cookies.signed[remember_key(resource, scope)] 16 | resource.remember_me?(token, generated_at) 17 | end 18 | 19 | # Remembers the given resource by setting up a cookie 20 | def remember_me(resource) 21 | return if request.env["devise.skip_storage"] 22 | scope = Devise::Mapping.find_scope!(resource) 23 | resource.remember_me! 24 | cookies.signed[remember_key(resource, scope)] = remember_cookie_values(resource) 25 | end 26 | 27 | # Forgets the given resource by deleting a cookie 28 | def forget_me(resource) 29 | scope = Devise::Mapping.find_scope!(resource) 30 | resource.forget_me! 31 | cookies.delete(remember_key(resource, scope), forget_cookie_values(resource)) 32 | end 33 | 34 | protected 35 | 36 | def forget_cookie_values(resource) 37 | Devise::Controllers::Rememberable.cookie_values.merge!(resource.rememberable_options) 38 | end 39 | 40 | def remember_cookie_values(resource) 41 | options = { httponly: true } 42 | options.merge!(forget_cookie_values(resource)) 43 | options.merge!( 44 | value: resource.class.serialize_into_cookie(resource), 45 | expires: resource.remember_expires_at 46 | ) 47 | end 48 | 49 | def remember_key(resource, scope) 50 | resource.rememberable_options.fetch(:key, "remember_#{scope}_token") 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /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 58 | -------------------------------------------------------------------------------- /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 | remember_me(resource) if extend_remember_me?(resource) 29 | resource.after_remembered 30 | success!(resource) 31 | end 32 | end 33 | 34 | # No need to clean up the CSRF when using rememberable. 35 | # In fact, cleaning it up here would be a bug because 36 | # rememberable is triggered on GET requests which means 37 | # we would render a page on first access with all csrf 38 | # tokens expired. 39 | def clean_up_csrf? 40 | false 41 | end 42 | 43 | private 44 | 45 | def extend_remember_me?(resource) 46 | resource.respond_to?(:extend_remember_period) && resource.extend_remember_period 47 | end 48 | 49 | def remember_me? 50 | true 51 | end 52 | 53 | def remember_key 54 | mapping.to.rememberable_options.fetch(:key, "remember_#{scope}_token") 55 | end 56 | 57 | def remember_cookie 58 | @remember_cookie ||= cookies.signed[remember_key] 59 | end 60 | 61 | end 62 | end 63 | end 64 | 65 | Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable) 66 | -------------------------------------------------------------------------------- /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 < Devise::ControllerTestCase 28 | tests CustomStrategyController 29 | 30 | include Devise::Test::ControllerHelpers 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 | assert ret.is_a?(ActionDispatch::TestResponse) 47 | 48 | # check the saved response as well. This is purely so that the response is available to the testing framework 49 | # for verification. In production, the above array would be delivered directly to Rack. 50 | assert_response 400 51 | end 52 | 53 | test "custom strategy can return custom headers" do 54 | ret = get :new 55 | 56 | # check the returned rack array 57 | # assert ret.is_a?(Array) 58 | # assert_equal ret.third['X-FOO'], 'BAR' 59 | assert ret.is_a?(ActionDispatch::TestResponse) 60 | 61 | # check the saved response headers as well. 62 | assert_equal response.headers['X-FOO'], 'BAR' 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/rails_app/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20100401102949) do 15 | 16 | create_table "admins", force: true do |t| 17 | t.string "email" 18 | t.string "encrypted_password" 19 | t.string "reset_password_token" 20 | t.datetime "reset_password_sent_at" 21 | t.datetime "remember_created_at" 22 | t.string "confirmation_token" 23 | t.datetime "confirmed_at" 24 | t.datetime "confirmation_sent_at" 25 | t.string "unconfirmed_email" 26 | t.datetime "locked_at" 27 | t.boolean "active", default: false 28 | t.datetime "created_at" 29 | t.datetime "updated_at" 30 | end 31 | 32 | create_table "users", force: true do |t| 33 | t.string "username" 34 | t.string "facebook_token" 35 | t.string "email", default: "", null: false 36 | t.string "encrypted_password", default: "", null: false 37 | t.string "reset_password_token" 38 | t.datetime "reset_password_sent_at" 39 | t.datetime "remember_created_at" 40 | t.integer "sign_in_count", default: 0 41 | t.datetime "current_sign_in_at" 42 | t.datetime "last_sign_in_at" 43 | t.string "current_sign_in_ip" 44 | t.string "last_sign_in_ip" 45 | t.string "confirmation_token" 46 | t.datetime "confirmed_at" 47 | t.datetime "confirmation_sent_at" 48 | t.integer "failed_attempts", default: 0 49 | t.string "unlock_token" 50 | t.datetime "locked_at" 51 | t.datetime "created_at" 52 | t.datetime "updated_at" 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /test/rails_app/db/migrate/20100401102949_create_tables.rb: -------------------------------------------------------------------------------- 1 | superclass = ActiveRecord::Migration 2 | # TODO: Inherit from the 5.0 Migration class directly when we drop support for Rails 4. 3 | superclass = ActiveRecord::Migration[5.0] if superclass.respond_to?(:[]) 4 | 5 | class CreateTables < superclass 6 | def self.up 7 | create_table :users do |t| 8 | t.string :username 9 | t.string :facebook_token 10 | 11 | ## Database authenticatable 12 | t.string :email, null: false, default: "" 13 | t.string :encrypted_password, null: false, default: "" 14 | 15 | ## Recoverable 16 | t.string :reset_password_token 17 | t.datetime :reset_password_sent_at 18 | 19 | ## Rememberable 20 | t.datetime :remember_created_at 21 | 22 | ## Trackable 23 | t.integer :sign_in_count, default: 0 24 | t.datetime :current_sign_in_at 25 | t.datetime :last_sign_in_at 26 | t.string :current_sign_in_ip 27 | t.string :last_sign_in_ip 28 | 29 | ## Confirmable 30 | t.string :confirmation_token 31 | t.datetime :confirmed_at 32 | t.datetime :confirmation_sent_at 33 | # t.string :unconfirmed_email # Only if using reconfirmable 34 | 35 | ## Lockable 36 | t.integer :failed_attempts, default: 0 # Only if lock strategy is :failed_attempts 37 | t.string :unlock_token # Only if unlock strategy is :email or :both 38 | t.datetime :locked_at 39 | 40 | t.timestamps null: false 41 | end 42 | 43 | create_table :admins do |t| 44 | ## Database authenticatable 45 | t.string :email, null: true 46 | t.string :encrypted_password, null: true 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 | ## Confirmable 56 | t.string :confirmation_token 57 | t.datetime :confirmed_at 58 | t.datetime :confirmation_sent_at 59 | t.string :unconfirmed_email # Only if using reconfirmable 60 | 61 | ## Lockable 62 | t.datetime :locked_at 63 | 64 | ## Attribute for testing route blocks 65 | t.boolean :active, default: false 66 | 67 | t.timestamps null: false 68 | end 69 | end 70 | 71 | def self.down 72 | drop_table :users 73 | drop_table :admins 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /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 | # Calling 'available_locales' before storing the translations to ensure 12 | # that the I18n backend will be initialized before we store our custom 13 | # translations, so they will always override the translations for the 14 | # YML file. 15 | I18n.available_locales 16 | I18n.backend.store_translations(locale, translations) 17 | yield 18 | ensure 19 | I18n.reload! 20 | end 21 | 22 | def generate_unique_email 23 | @@email_count ||= 0 24 | @@email_count += 1 25 | "test#{@@email_count}@example.com" 26 | end 27 | 28 | def valid_attributes(attributes={}) 29 | { username: "usertest", 30 | email: generate_unique_email, 31 | password: '12345678', 32 | password_confirmation: '12345678' }.update(attributes) 33 | end 34 | 35 | def new_user(attributes={}) 36 | User.new(valid_attributes(attributes)) 37 | end 38 | 39 | def create_user(attributes={}) 40 | User.create!(valid_attributes(attributes)) 41 | end 42 | 43 | def create_admin(attributes={}) 44 | valid_attributes = valid_attributes(attributes) 45 | valid_attributes.delete(:username) 46 | Admin.create!(valid_attributes) 47 | end 48 | 49 | def create_user_without_email(attributes={}) 50 | UserWithoutEmail.create!(valid_attributes(attributes)) 51 | end 52 | 53 | # Execute the block setting the given values and restoring old values after 54 | # the block is executed. 55 | def swap(object, new_values) 56 | old_values = {} 57 | new_values.each do |key, value| 58 | old_values[key] = object.send key 59 | object.send :"#{key}=", value 60 | end 61 | clear_cached_variables(new_values) 62 | yield 63 | ensure 64 | clear_cached_variables(new_values) 65 | old_values.each do |key, value| 66 | object.send :"#{key}=", value 67 | end 68 | end 69 | 70 | def clear_cached_variables(options) 71 | if options.key?(:case_insensitive_keys) || options.key?(:strip_whitespace_keys) 72 | Devise.mappings.each do |_, mapping| 73 | mapping.to.instance_variable_set(:@devise_parameter_filter, nil) 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /test/controllers/url_helpers_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RoutesTest < Devise::ControllerTestCase 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 | # With string 17 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", "user"), 18 | send(:"#{prepend_path}user_#{name}_path") 19 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", "user"), 20 | send(:"#{prepend_path}user_#{name}_url") 21 | 22 | # Default url params 23 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", :user, param: 123), 24 | send(:"#{prepend_path}user_#{name}_path", param: 123) 25 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", :user, param: 123), 26 | send(:"#{prepend_path}user_#{name}_url", param: 123) 27 | 28 | @request.path = nil 29 | # With an object 30 | assert_equal @controller.send(:"#{prepend_path}#{name}_path", User.new), 31 | send(:"#{prepend_path}user_#{name}_path") 32 | assert_equal @controller.send(:"#{prepend_path}#{name}_url", User.new), 33 | send(:"#{prepend_path}user_#{name}_url") 34 | end 35 | 36 | 37 | test 'should alias session to mapped user session' do 38 | assert_path_and_url :session 39 | assert_path_and_url :session, :new 40 | assert_path_and_url :session, :destroy 41 | end 42 | 43 | test 'should alias password to mapped user password' do 44 | assert_path_and_url :password 45 | assert_path_and_url :password, :new 46 | assert_path_and_url :password, :edit 47 | end 48 | 49 | test 'should alias confirmation to mapped user confirmation' do 50 | assert_path_and_url :confirmation 51 | assert_path_and_url :confirmation, :new 52 | end 53 | 54 | test 'should alias unlock to mapped user unlock' do 55 | assert_path_and_url :unlock 56 | assert_path_and_url :unlock, :new 57 | end 58 | 59 | test 'should alias registration to mapped user registration' do 60 | assert_path_and_url :registration 61 | assert_path_and_url :registration, :new 62 | assert_path_and_url :registration, :edit 63 | assert_path_and_url :registration, :cancel 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /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..72. 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 < Devise::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 user.current_sign_in_at.acts_like?(:time) 14 | assert user.last_sign_in_at.acts_like?(:time) 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 | delete 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 | delete 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.days 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 | delete 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 | -------------------------------------------------------------------------------- /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 | define_method method do |resource_or_scope, *args| 48 | scope = Devise::Mapping.find_scope!(resource_or_scope) 49 | router_name = Devise.mappings[scope].router_name 50 | context = router_name ? send(router_name) : _devise_route_context 51 | context.send("#{action}#{scope}_#{module_name}_#{path_or_url}", *args) 52 | end 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 | -------------------------------------------------------------------------------- /test/parameter_sanitizer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'devise/parameter_sanitizer' 3 | 4 | class ParameterSanitizerTest < ActiveSupport::TestCase 5 | def sanitizer(params) 6 | params = ActionController::Parameters.new(params) 7 | Devise::ParameterSanitizer.new(User, :user, params) 8 | end 9 | 10 | test 'permits the default parameters for sign in' do 11 | sanitizer = sanitizer('user' => { 'email' => 'jose' }) 12 | sanitized = sanitizer.sanitize(:sign_in) 13 | 14 | assert_equal({ 'email' => 'jose' }, sanitized) 15 | end 16 | 17 | test 'permits the default parameters for sign up' do 18 | sanitizer = sanitizer('user' => { 'email' => 'jose', 'role' => 'invalid' }) 19 | sanitized = sanitizer.sanitize(:sign_up) 20 | 21 | assert_equal({ 'email' => 'jose' }, sanitized) 22 | end 23 | 24 | test 'permits the default parameters for account update' do 25 | sanitizer = sanitizer('user' => { 'email' => 'jose', 'role' => 'invalid' }) 26 | sanitized = sanitizer.sanitize(:account_update) 27 | 28 | assert_equal({ 'email' => 'jose' }, sanitized) 29 | end 30 | 31 | test 'permits news parameters for an existing action' do 32 | sanitizer = sanitizer('user' => { 'username' => 'jose' }) 33 | sanitizer.permit(:sign_in, keys: [:username]) 34 | sanitized = sanitizer.sanitize(:sign_in) 35 | 36 | assert_equal({ 'username' => 'jose' }, sanitized) 37 | end 38 | 39 | test 'permits news parameters for an existing action with a block' do 40 | sanitizer = sanitizer('user' => { 'username' => 'jose' }) 41 | sanitizer.permit(:sign_in) do |user| 42 | user.permit(:username) 43 | end 44 | 45 | sanitized = sanitizer.sanitize(:sign_in) 46 | 47 | assert_equal({ 'username' => 'jose' }, sanitized) 48 | end 49 | 50 | test 'permit parameters for new actions' do 51 | sanitizer = sanitizer('user' => { 'email' => 'jose@omglol', 'name' => 'Jose' }) 52 | sanitizer.permit(:invite_user, keys: [:email, :name]) 53 | 54 | sanitized = sanitizer.sanitize(:invite_user) 55 | 56 | assert_equal({ 'email' => 'jose@omglol', 'name' => 'Jose' }, sanitized) 57 | end 58 | 59 | test 'fails when we do not have any permitted parameters for the action' do 60 | sanitizer = sanitizer('user' => { 'email' => 'jose', 'password' => 'invalid' }) 61 | 62 | assert_raise NotImplementedError do 63 | sanitizer.sanitize(:unknown) 64 | end 65 | end 66 | 67 | test 'removes permitted parameters' do 68 | sanitizer = sanitizer('user' => { 'email' => 'jose@omglol', 'username' => 'jose' }) 69 | 70 | sanitizer.permit(:sign_in, keys: [:username], except: [:email]) 71 | sanitized = sanitizer.sanitize(:sign_in) 72 | 73 | assert_equal({ 'username' => 'jose' }, sanitized) 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /app/controllers/devise/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Devise::SessionsController < DeviseController 2 | prepend_before_action :require_no_authentication, only: [:new, :create] 3 | prepend_before_action :allow_params_authentication!, only: :create 4 | prepend_before_action :verify_signed_out_user, only: :destroy 5 | prepend_before_action only: [:create, :destroy] { request.env["devise.skip_timeout"] = true } 6 | 7 | # GET /resource/sign_in 8 | def new 9 | self.resource = resource_class.new(sign_in_params) 10 | clean_up_passwords(resource) 11 | yield resource if block_given? 12 | respond_with(resource, serialize_options(resource)) 13 | end 14 | 15 | # POST /resource/sign_in 16 | def create 17 | self.resource = warden.authenticate!(auth_options) 18 | set_flash_message!(:notice, :signed_in) 19 | sign_in(resource_name, resource) 20 | yield resource if block_given? 21 | respond_with resource, location: after_sign_in_path_for(resource) 22 | end 23 | 24 | # DELETE /resource/sign_out 25 | def destroy 26 | signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)) 27 | set_flash_message! :notice, :signed_out if signed_out 28 | yield if block_given? 29 | respond_to_on_destroy 30 | end 31 | 32 | protected 33 | 34 | def sign_in_params 35 | devise_parameter_sanitizer.sanitize(:sign_in) 36 | end 37 | 38 | def serialize_options(resource) 39 | methods = resource_class.authentication_keys.dup 40 | methods = methods.keys if methods.is_a?(Hash) 41 | methods << :password if resource.respond_to?(:password) 42 | { methods: methods, only: [:password] } 43 | end 44 | 45 | def auth_options 46 | { scope: resource_name, recall: "#{controller_path}#new" } 47 | end 48 | 49 | def translation_scope 50 | 'devise.sessions' 51 | end 52 | 53 | private 54 | 55 | # Check if there is no signed in user before doing the sign out. 56 | # 57 | # If there is no signed in user, it will set the flash message and redirect 58 | # to the after_sign_out path. 59 | def verify_signed_out_user 60 | if all_signed_out? 61 | set_flash_message! :notice, :already_signed_out 62 | 63 | respond_to_on_destroy 64 | end 65 | end 66 | 67 | def all_signed_out? 68 | users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) } 69 | 70 | users.all?(&:blank?) 71 | end 72 | 73 | def respond_to_on_destroy 74 | # We actually need to hardcode this as Rails default responder doesn't 75 | # support returning empty response on GET request 76 | respond_to do |format| 77 | format.all { head :no_content } 78 | format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name) } 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /guides/bug_report_templates/integration_test.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | # Activate the gem you are reporting the issue against. 11 | gem 'rails', '~> 4.2.0' 12 | gem 'devise', '~> 4.0' 13 | gem 'sqlite3' 14 | gem 'byebug' 15 | end 16 | 17 | require 'rack/test' 18 | require 'action_controller/railtie' 19 | require 'active_record' 20 | require 'devise/rails/routes' 21 | require 'devise/rails/warden_compat' 22 | 23 | ActiveRecord::Base.establish_connection( adapter: :sqlite3, database: ':memory:') 24 | 25 | class DeviseCreateUsers < ActiveRecord::Migration 26 | def change 27 | create_table(:users) do |t| 28 | t.string :email, null: false 29 | t.string :encrypted_password, null: true 30 | t.timestamps null: false 31 | end 32 | 33 | end 34 | end 35 | 36 | Devise.setup do |config| 37 | require 'devise/orm/active_record' 38 | config.secret_key = 'secret_key_base' 39 | end 40 | 41 | class TestApp < Rails::Application 42 | config.root = File.dirname(__FILE__) 43 | config.session_store :cookie_store, key: 'cookie_store_key' 44 | secrets.secret_token = 'secret_token' 45 | secrets.secret_key_base = 'secret_key_base' 46 | config.eager_load = false 47 | 48 | config.middleware.use Warden::Manager do |config| 49 | Devise.warden_config = config 50 | end 51 | 52 | config.logger = Logger.new($stdout) 53 | Rails.logger = config.logger 54 | 55 | end 56 | 57 | Rails.application.initialize! 58 | 59 | DeviseCreateUsers.migrate(:up) 60 | 61 | class User < ActiveRecord::Base 62 | devise :database_authenticatable 63 | end 64 | 65 | Rails.application.routes.draw do 66 | devise_for :users 67 | 68 | get '/' => 'test#index' 69 | end 70 | 71 | class ApplicationController < ActionController::Base 72 | end 73 | 74 | class TestController < ApplicationController 75 | include Rails.application.routes.url_helpers 76 | 77 | before_filter :authenticate_user! 78 | 79 | def index 80 | render plain: 'Home' 81 | end 82 | end 83 | 84 | require 'minitest/autorun' 85 | 86 | class BugTest < ActionDispatch::IntegrationTest 87 | include Rack::Test::Methods 88 | include Warden::Test::Helpers 89 | 90 | def test_returns_success 91 | Warden.test_mode! 92 | 93 | login_as User.create!(email: 'test@test.com', password: 'test123456', password_confirmation: 'test123456') 94 | 95 | get '/' 96 | assert last_response.ok? 97 | end 98 | 99 | private 100 | 101 | def app 102 | Rails.application 103 | end 104 | end 105 | -------------------------------------------------------------------------------- /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 'set up sender from configuration' do 44 | assert_equal ['test@example.com'], mail.from 45 | end 46 | 47 | test 'set up sender from custom mailer defaults' do 48 | Devise.mailer = 'Users::Mailer' 49 | assert_equal ['custom@example.com'], mail.from 50 | end 51 | 52 | test 'set up sender from custom mailer defaults with proc' do 53 | Devise.mailer = 'Users::FromProcMailer' 54 | assert_equal ['custom@example.com'], mail.from 55 | end 56 | 57 | test 'custom mailer renders parent mailer template' do 58 | Devise.mailer = 'Users::Mailer' 59 | assert_present mail.body.encoded 60 | end 61 | 62 | test 'set up reply to as copy from sender' do 63 | assert_equal ['test@example.com'], mail.reply_to 64 | end 65 | 66 | test 'set up subject from I18n' do 67 | store_translations :en, devise: { mailer: { unlock_instructions: { subject: 'Yo unlock instructions' } } } do 68 | assert_equal 'Yo unlock instructions', mail.subject 69 | end 70 | end 71 | 72 | test 'subject namespaced by model' do 73 | store_translations :en, devise: { mailer: { unlock_instructions: { user_subject: 'User Unlock Instructions' } } } do 74 | assert_equal 'User Unlock Instructions', mail.subject 75 | end 76 | end 77 | 78 | test 'body should have user info' do 79 | assert_match user.email, mail.body.encoded 80 | end 81 | 82 | test 'body should have link to unlock the account' do 83 | host, port = ActionMailer::Base.default_url_options.values_at :host, :port 84 | 85 | if mail.body.encoded =~ %r{} 86 | assert_equal Devise.token_generator.digest(user.class, :unlock_token, $1), user.unlock_token 87 | else 88 | flunk "expected unlock url regex to match" 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /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 | end 9 | 10 | protected 11 | 12 | attr_reader :scope_name, :resource 13 | 14 | # Configure default email options 15 | def devise_mail(record, action, opts = {}, &block) 16 | initialize_from_record(record) 17 | mail headers_for(action, opts), &block 18 | end 19 | 20 | def initialize_from_record(record) 21 | @scope_name = Devise::Mapping.find_scope!(record) 22 | @resource = instance_variable_set("@#{devise_mapping.name}", record) 23 | end 24 | 25 | def devise_mapping 26 | @devise_mapping ||= Devise.mappings[scope_name] 27 | end 28 | 29 | def headers_for(action, opts) 30 | headers = { 31 | subject: subject_for(action), 32 | to: resource.email, 33 | from: mailer_sender(devise_mapping), 34 | reply_to: mailer_reply_to(devise_mapping), 35 | template_path: template_paths, 36 | template_name: action 37 | }.merge(opts) 38 | 39 | @email = headers[:to] 40 | headers 41 | end 42 | 43 | def mailer_reply_to(mapping) 44 | mailer_sender(mapping, :reply_to) 45 | end 46 | 47 | def mailer_from(mapping) 48 | mailer_sender(mapping, :from) 49 | end 50 | 51 | def mailer_sender(mapping, sender = :from) 52 | default_sender = default_params[sender] 53 | if default_sender.present? 54 | default_sender.respond_to?(:to_proc) ? instance_eval(&default_sender) : default_sender 55 | elsif Devise.mailer_sender.is_a?(Proc) 56 | Devise.mailer_sender.call(mapping.name) 57 | else 58 | Devise.mailer_sender 59 | end 60 | end 61 | 62 | def template_paths 63 | template_path = _prefixes.dup 64 | template_path.unshift "#{@devise_mapping.scoped_path}/mailer" if self.class.scoped_views? 65 | template_path 66 | end 67 | 68 | # Set up a subject doing an I18n lookup. At first, it attempts to set a subject 69 | # based on the current mapping: 70 | # 71 | # en: 72 | # devise: 73 | # mailer: 74 | # confirmation_instructions: 75 | # user_subject: '...' 76 | # 77 | # If one does not exist, it fallbacks to ActionMailer default: 78 | # 79 | # en: 80 | # devise: 81 | # mailer: 82 | # confirmation_instructions: 83 | # subject: '...' 84 | # 85 | def subject_for(key) 86 | I18n.t(:"#{devise_mapping.name}_subject", scope: [:devise, :mailer, key], 87 | default: [:subject, key.to_s.humanize]) 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /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 'Log 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 'Log 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 | -------------------------------------------------------------------------------- /app/controllers/devise/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class Devise::PasswordsController < DeviseController 2 | prepend_before_action :require_no_authentication 3 | # Render the #edit only if coming from a reset password email link 4 | append_before_action :assert_reset_token_passed, only: :edit 5 | 6 | # GET /resource/password/new 7 | def new 8 | self.resource = resource_class.new 9 | end 10 | 11 | # POST /resource/password 12 | def create 13 | self.resource = resource_class.send_reset_password_instructions(resource_params) 14 | yield resource if block_given? 15 | 16 | if successfully_sent?(resource) 17 | respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) 18 | else 19 | respond_with(resource) 20 | end 21 | end 22 | 23 | # GET /resource/password/edit?reset_password_token=abcdef 24 | def edit 25 | self.resource = resource_class.new 26 | set_minimum_password_length 27 | resource.reset_password_token = params[:reset_password_token] 28 | end 29 | 30 | # PUT /resource/password 31 | def update 32 | self.resource = resource_class.reset_password_by_token(resource_params) 33 | yield resource if block_given? 34 | 35 | if resource.errors.empty? 36 | resource.unlock_access! if unlockable?(resource) 37 | if Devise.sign_in_after_reset_password 38 | flash_message = resource.active_for_authentication? ? :updated : :updated_not_active 39 | set_flash_message!(:notice, flash_message) 40 | sign_in(resource_name, resource) 41 | else 42 | set_flash_message!(:notice, :updated_not_active) 43 | end 44 | respond_with resource, location: after_resetting_password_path_for(resource) 45 | else 46 | set_minimum_password_length 47 | respond_with resource 48 | end 49 | end 50 | 51 | protected 52 | def after_resetting_password_path_for(resource) 53 | Devise.sign_in_after_reset_password ? after_sign_in_path_for(resource) : new_session_path(resource_name) 54 | end 55 | 56 | # The path used after sending reset password instructions 57 | def after_sending_reset_password_instructions_path_for(resource_name) 58 | new_session_path(resource_name) if is_navigational_format? 59 | end 60 | 61 | # Check if a reset_password_token is provided in the request 62 | def assert_reset_token_passed 63 | if params[:reset_password_token].blank? 64 | set_flash_message(:alert, :no_token) 65 | redirect_to new_session_path(resource_name) 66 | end 67 | end 68 | 69 | # Check if proper Lockable module methods are present & unlock strategy 70 | # allows to unlock resource on password reset 71 | def unlockable?(resource) 72 | resource.respond_to?(:unlock_access!) && 73 | resource.respond_to?(:unlock_strategy_enabled?) && 74 | resource.unlock_strategy_enabled?(:email) 75 | end 76 | 77 | def translation_scope 78 | 'devise.passwords' 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /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 'set up sender from configuration' do 43 | assert_equal ['test@example.com'], mail.from 44 | end 45 | 46 | test 'set up sender from custom mailer defaults' do 47 | Devise.mailer = 'Users::Mailer' 48 | assert_equal ['custom@example.com'], mail.from 49 | end 50 | 51 | test 'set up sender from custom mailer defaults with proc' do 52 | Devise.mailer = 'Users::FromProcMailer' 53 | assert_equal ['custom@example.com'], mail.from 54 | end 55 | 56 | test 'custom mailer renders parent mailer template' do 57 | Devise.mailer = 'Users::Mailer' 58 | assert_present mail.body.encoded 59 | end 60 | 61 | test 'set up reply to as copy from sender' do 62 | assert_equal ['test@example.com'], mail.reply_to 63 | end 64 | 65 | test 'set up subject from I18n' do 66 | store_translations :en, devise: { mailer: { reset_password_instructions: { subject: 'Reset instructions' } } } do 67 | assert_equal 'Reset instructions', mail.subject 68 | end 69 | end 70 | 71 | test 'subject namespaced by model' do 72 | store_translations :en, devise: { mailer: { reset_password_instructions: { user_subject: 'User Reset Instructions' } } } do 73 | assert_equal 'User Reset Instructions', mail.subject 74 | end 75 | end 76 | 77 | test 'body should have user info' do 78 | assert_match user.email, mail.body.encoded 79 | end 80 | 81 | test 'body should have link to confirm the account' do 82 | host, port = ActionMailer::Base.default_url_options.values_at :host, :port 83 | 84 | if mail.body.encoded =~ %r{} 85 | assert_equal Devise.token_generator.digest(user.class, :reset_password_token, $1), user.reset_password_token 86 | else 87 | flunk "expected reset password url regex to match" 88 | end 89 | end 90 | 91 | test 'mailer sender accepts a proc' do 92 | swap Devise, mailer_sender: proc { "another@example.com" } do 93 | assert_equal ['another@example.com'], mail.from 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /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_migration "db/migrate/devise_create_monsters.rb", /def change/ 14 | end 15 | 16 | test "all files for namespaced model are properly created" do 17 | run_generator %w(admin/monster) 18 | assert_migration "db/migrate/devise_create_admin_monsters.rb", /def change/ 19 | end 20 | 21 | test "update model migration when model exists" do 22 | run_generator %w(monster) 23 | assert_file "app/models/monster.rb" 24 | run_generator %w(monster) 25 | assert_migration "db/migrate/add_devise_to_monsters.rb" 26 | end 27 | 28 | test "all files are properly deleted" do 29 | run_generator %w(monster) 30 | run_generator %w(monster) 31 | assert_migration "db/migrate/devise_create_monsters.rb" 32 | assert_migration "db/migrate/add_devise_to_monsters.rb" 33 | run_generator %w(monster), behavior: :revoke 34 | assert_no_migration "db/migrate/add_devise_to_monsters.rb" 35 | assert_migration "db/migrate/devise_create_monsters.rb" 36 | run_generator %w(monster), behavior: :revoke 37 | assert_no_file "app/models/monster.rb" 38 | assert_no_migration "db/migrate/devise_create_monsters.rb" 39 | end 40 | 41 | test "use string column type for ip addresses" do 42 | run_generator %w(monster) 43 | assert_migration "db/migrate/devise_create_monsters.rb", /t.string :current_sign_in_ip/ 44 | assert_migration "db/migrate/devise_create_monsters.rb", /t.string :last_sign_in_ip/ 45 | end 46 | end 47 | 48 | module RailsEngine 49 | class Engine < Rails::Engine 50 | isolate_namespace RailsEngine 51 | end 52 | end 53 | 54 | def simulate_inside_engine(engine, namespace) 55 | if Rails::Generators.respond_to?(:namespace=) 56 | swap Rails::Generators, namespace: namespace do 57 | yield 58 | end 59 | else 60 | swap Rails, application: engine.instance do 61 | yield 62 | end 63 | end 64 | end 65 | 66 | class ActiveRecordEngineGeneratorTest < Rails::Generators::TestCase 67 | tests ActiveRecord::Generators::DeviseGenerator 68 | destination File.expand_path("../../tmp", __FILE__) 69 | setup :prepare_destination 70 | 71 | test "all files are properly created in rails 4.0" do 72 | simulate_inside_engine(RailsEngine::Engine, RailsEngine) do 73 | run_generator ["monster"] 74 | 75 | assert_file "app/models/rails_engine/monster.rb", /devise/ 76 | assert_file "app/models/rails_engine/monster.rb" do |content| 77 | assert_no_match %r{attr_accessible :email}, content 78 | end 79 | end 80 | end 81 | 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /test/integration/database_authenticatable_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DatabaseAuthenticationTest < Devise::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 | refute 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 | refute 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 | refute 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 | refute 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 | refute 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 | 85 | test 'valid sign in calls after_database_authentication callback' do 86 | user = create_user(email: ' foo@bar.com ') 87 | 88 | User.expects(:find_for_database_authentication).returns user 89 | user.expects :after_database_authentication 90 | 91 | sign_in_as_user do 92 | fill_in 'email', with: 'foo@bar.com' 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /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}.rb", migration_version: migration_version 15 | else 16 | migration_template "migration.rb", "db/migrate/devise_create_#{table_name}.rb", migration_version: migration_version 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 26 | 27 | class_path = if namespaced? 28 | class_name.to_s.split("::") 29 | else 30 | [class_name] 31 | end 32 | 33 | indent_depth = class_path.size - 1 34 | content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n" 35 | 36 | inject_into_class(model_path, class_path.last, content) if model_exists? 37 | end 38 | 39 | def migration_data 40 | <= "5.0.0" 24 | config.public_file_server.enabled = false 25 | elsif Rails.version >= "4.2.0" 26 | config.serve_static_files = false 27 | else 28 | config.serve_static_assets = false 29 | end 30 | 31 | # Compress JavaScripts and CSS. 32 | config.assets.js_compressor = :uglifier 33 | # config.assets.css_compressor = :sass 34 | 35 | # Whether to fallback to assets pipeline if a precompiled asset is missed. 36 | config.assets.compile = false 37 | 38 | # Generate digests for assets URLs. 39 | config.assets.digest = true 40 | 41 | # Version of your assets, change this if you want to expire all your assets. 42 | config.assets.version = '1.0' 43 | 44 | # Specifies the header that your server uses for sending files. 45 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 46 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 47 | 48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 49 | # config.force_ssl = true 50 | 51 | # Set to :debug to see everything in the log. 52 | config.log_level = :info 53 | 54 | # Prepend all log lines with the following tags. 55 | # config.log_tags = [:subdomain, :uuid] 56 | 57 | # Use a different logger for distributed setups. 58 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 59 | 60 | # Use a different cache store in production. 61 | # config.cache_store = :mem_cache_store 62 | 63 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 64 | # config.action_controller.asset_host = "http://assets.example.com" 65 | 66 | # Precompile additional assets. 67 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 68 | # config.assets.precompile += %w( search.js ) 69 | 70 | # Ignore bad email addresses and do not raise email delivery errors. 71 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 72 | # config.action_mailer.raise_delivery_errors = false 73 | 74 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 75 | # the I18n.default_locale when a translation can not be found). 76 | config.i18n.fallbacks = true 77 | 78 | # Send deprecation notices to registered listeners. 79 | config.active_support.deprecation = :notify 80 | 81 | # Disable automatic flushing of the log to improve performance. 82 | # config.autoflush_log = false 83 | 84 | # Use default logging formatter so that PID and timestamp are not suppressed. 85 | config.log_formatter = ::Logger::Formatter.new 86 | end 87 | -------------------------------------------------------------------------------- /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_blocks 7 | yield 8 | ensure 9 | @@warden_config, @@warden_config_blocks = c, b 10 | end 11 | end 12 | 13 | class DeviseTest < ActiveSupport::TestCase 14 | test 'bcrypt on the class' do 15 | password = "super secret" 16 | klass = Struct.new(:pepper, :stretches).new("blahblah", 2) 17 | hash = Devise::Encryptor.digest(klass, password) 18 | assert_equal ::BCrypt::Password.create(hash), hash 19 | 20 | klass = Struct.new(:pepper, :stretches).new("bla", 2) 21 | hash = Devise::Encryptor.digest(klass, password) 22 | assert_not_equal ::BCrypt::Password.new(hash), hash 23 | end 24 | 25 | test 'model options can be configured through Devise' do 26 | swap Devise, allow_unconfirmed_access_for: 113, pepper: "foo" do 27 | assert_equal 113, Devise.allow_unconfirmed_access_for 28 | assert_equal "foo", Devise.pepper 29 | end 30 | end 31 | 32 | test 'setup block yields self' do 33 | Devise.setup do |config| 34 | assert_equal Devise, config 35 | end 36 | end 37 | 38 | test 'stores warden configuration' do 39 | assert_kind_of Devise::Delegator, Devise.warden_config.failure_app 40 | assert_equal :user, Devise.warden_config.default_scope 41 | end 42 | 43 | test 'warden manager user configuration through a block' do 44 | Devise.yield_and_restore do 45 | executed = false 46 | Devise.warden do |config| 47 | executed = true 48 | assert_kind_of Warden::Config, config 49 | end 50 | 51 | Devise.configure_warden! 52 | assert executed 53 | end 54 | end 55 | 56 | test 'warden manager user configuration through multiple blocks' do 57 | Devise.yield_and_restore do 58 | executed = 0 59 | 60 | 3.times do 61 | Devise.warden { |config| executed += 1 } 62 | end 63 | 64 | Devise.configure_warden! 65 | assert_equal 3, executed 66 | end 67 | end 68 | 69 | test 'add new module using the helper method' do 70 | Devise.add_module(:coconut) 71 | assert_equal 1, Devise::ALL.select { |v| v == :coconut }.size 72 | refute Devise::STRATEGIES.include?(:coconut) 73 | refute defined?(Devise::Models::Coconut) 74 | Devise::ALL.delete(:coconut) 75 | 76 | Devise.add_module(:banana, strategy: :fruits) 77 | assert_equal :fruits, Devise::STRATEGIES[:banana] 78 | Devise::ALL.delete(:banana) 79 | Devise::STRATEGIES.delete(:banana) 80 | 81 | Devise.add_module(:kivi, controller: :fruits) 82 | assert_equal :fruits, Devise::CONTROLLERS[:kivi] 83 | Devise::ALL.delete(:kivi) 84 | Devise::CONTROLLERS.delete(:kivi) 85 | end 86 | 87 | test 'should complain when comparing empty or different sized passes' do 88 | [nil, ""].each do |empty| 89 | refute Devise.secure_compare(empty, "something") 90 | refute Devise.secure_compare("something", empty) 91 | refute Devise.secure_compare(empty, empty) 92 | end 93 | refute Devise.secure_compare("size_1", "size_four") 94 | end 95 | 96 | test 'Devise.email_regexp should match valid email addresses' do 97 | valid_emails = ["test@example.com", "jo@jo.co", "f4$_m@you.com", "testing.example@example.com.ua", "test@tt", "test@valid---domain.com"] 98 | non_valid_emails = ["rex", "test user@example.com", "test_user@example server.com"] 99 | 100 | valid_emails.each do |email| 101 | assert_match Devise.email_regexp, email 102 | end 103 | non_valid_emails.each do |email| 104 | assert_no_match Devise.email_regexp, email 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /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 'set up sender from configuration' do 41 | assert_equal ['test@example.com'], mail.from 42 | end 43 | 44 | test 'set up sender from custom mailer defaults' do 45 | Devise.mailer = 'Users::Mailer' 46 | assert_equal ['custom@example.com'], mail.from 47 | end 48 | 49 | test 'set up sender from custom mailer defaults with proc' do 50 | Devise.mailer = 'Users::FromProcMailer' 51 | assert_equal ['custom@example.com'], mail.from 52 | end 53 | 54 | test 'custom mailer renders parent mailer template' do 55 | Devise.mailer = 'Users::Mailer' 56 | assert_present mail.body.encoded 57 | end 58 | 59 | test 'set up reply to as copy from sender' do 60 | assert_equal ['test@example.com'], mail.reply_to 61 | end 62 | 63 | test 'set up reply to as different if set in defaults' do 64 | Devise.mailer = 'Users::ReplyToMailer' 65 | assert_equal ['custom@example.com'], mail.from 66 | assert_equal ['custom_reply_to@example.com'], mail.reply_to 67 | end 68 | 69 | test 'set up subject from I18n' do 70 | store_translations :en, devise: { mailer: { confirmation_instructions: { subject: 'Account Confirmation' } } } do 71 | assert_equal 'Account Confirmation', mail.subject 72 | end 73 | end 74 | 75 | test 'subject namespaced by model' do 76 | store_translations :en, devise: { mailer: { confirmation_instructions: { user_subject: 'User Account Confirmation' } } } do 77 | assert_equal 'User Account Confirmation', mail.subject 78 | end 79 | end 80 | 81 | test 'body should have user info' do 82 | assert_match user.email, mail.body.encoded 83 | end 84 | 85 | test 'body should have link to confirm the account' do 86 | host, port = ActionMailer::Base.default_url_options.values_at :host, :port 87 | 88 | if mail.body.encoded =~ %r{} 89 | assert_equal $1, user.confirmation_token 90 | else 91 | flunk "expected confirmation url regex to match" 92 | end 93 | end 94 | 95 | test 'renders a scoped if scoped_views is set to true' do 96 | swap Devise, scoped_views: true do 97 | assert_equal user.email, mail.body.decoded 98 | end 99 | end 100 | 101 | test 'renders a scoped if scoped_views is set in the mailer class' do 102 | begin 103 | Devise::Mailer.scoped_views = true 104 | assert_equal user.email, mail.body.decoded 105 | ensure 106 | Devise::Mailer.send :remove_instance_variable, :@scoped_views 107 | end 108 | end 109 | 110 | test 'mailer sender accepts a proc' do 111 | swap Devise, mailer_sender: proc { "another@example.com" } do 112 | assert_equal ['another@example.com'], mail.from 113 | end 114 | end 115 | end 116 | --------------------------------------------------------------------------------