<%= I18n.t("errors.messages.not_saved",
count: resource.errors.count,
diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb
index 084af701c..21cf422d5 100644
--- a/app/views/devise/shared/_links.html.erb
+++ b/app/views/devise/shared/_links.html.erb
@@ -1,25 +1,25 @@
<%- if controller_name != 'sessions' %>
- <%= link_to "Log in", new_session_path(resource_name) %>
+
<%= link_to "Log in", new_session_path(resource_name) %>
<% end %>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
- <%= link_to "Sign up", new_registration_path(resource_name) %>
+
<%= link_to "Sign up", new_registration_path(resource_name) %>
<% end %>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
- <%= link_to "Forgot your password?", new_password_path(resource_name) %>
+
<%= link_to "Forgot your password?", new_password_path(resource_name) %>
<% end %>
<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
- <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
+
<%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
- <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
+
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
<% end %>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
- <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
+
<%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %>
<% end %>
<% end %>
diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb
index ffc34de8d..6b68d724c 100644
--- a/app/views/devise/unlocks/new.html.erb
+++ b/app/views/devise/unlocks/new.html.erb
@@ -4,8 +4,8 @@
<%= render "devise/shared/error_messages", resource: resource %>
- <%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
<%= f.label :email %>
+
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
diff --git a/bin/test b/bin/test
index 3670b19da..28141f2c7 100755
--- a/bin/test
+++ b/bin/test
@@ -1,17 +1,12 @@
#!/usr/bin/env ruby
$: << File.expand_path(File.expand_path('../../test', __FILE__))
-# Remove this begin/rescue once Rails 4 support is removed.
-begin
- require 'bundler/setup'
- require 'rails/test_unit/runner'
- require 'rails/test_unit/reporter'
- require 'rails/test_unit/line_filtering'
+require 'bundler/setup'
+require 'rails/test_unit/runner'
+require 'rails/test_unit/reporter'
+require 'rails/test_unit/line_filtering'
- Rails::TestUnitReporter.executable = 'bin/test'
+Rails::TestUnitReporter.executable = 'bin/test'
- Rails::TestUnit::Runner.parse_options(ARGV)
- Rails::TestUnit::Runner.run(ARGV)
-rescue LoadError
- exec 'rake'
-end
+Rails::TestUnit::Runner.parse_options(ARGV)
+Rails::TestUnit::Runner.run(ARGV)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 55617bdf3..260e1c4ba 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1,4 +1,4 @@
-# Additional translations at https://github.com/plataformatec/devise/wiki/I18n
+# Additional translations at https://github.com/heartcombo/devise/wiki/I18n
en:
devise:
@@ -42,9 +42,9 @@ en:
signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated."
signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked."
signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
- update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address."
+ update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address."
updated: "Your account has been updated successfully."
- updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again"
+ updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again."
sessions:
signed_in: "Signed in successfully."
signed_out: "Signed out successfully."
diff --git a/devise.gemspec b/devise.gemspec
index 5df410bae..1caa6aeb3 100644
--- a/devise.gemspec
+++ b/devise.gemspec
@@ -10,18 +10,26 @@ Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.licenses = ["MIT"]
s.summary = "Flexible authentication solution for Rails with Warden"
- s.email = "contact@plataformatec.com.br"
- s.homepage = "https://github.com/plataformatec/devise"
+ s.email = "heartcombo.oss@gmail.com"
+ s.homepage = "https://github.com/heartcombo/devise"
s.description = "Flexible authentication solution for Rails with Warden"
s.authors = ['José Valim', 'Carlos Antônio']
+ s.metadata = {
+ "homepage_uri" => "https://github.com/heartcombo/devise",
+ "documentation_uri" => "https://rubydoc.info/github/heartcombo/devise",
+ "changelog_uri" => "https://github.com/heartcombo/devise/blob/main/CHANGELOG.md",
+ "source_code_uri" => "https://github.com/heartcombo/devise",
+ "bug_tracker_uri" => "https://github.com/heartcombo/devise/issues",
+ "wiki_uri" => "https://github.com/heartcombo/devise/wiki"
+ }
s.files = Dir["{app,config,lib}/**/*", "CHANGELOG.md", "MIT-LICENSE", "README.md"]
s.require_paths = ["lib"]
- s.required_ruby_version = '>= 2.1.0'
+ s.required_ruby_version = '>= 2.7.0'
s.add_dependency("warden", "~> 1.2.3")
s.add_dependency("orm_adapter", "~> 0.1")
s.add_dependency("bcrypt", "~> 3.0")
- s.add_dependency("railties", ">= 4.1.0", "< 6.0")
+ s.add_dependency("railties", ">= 7.0")
s.add_dependency("responders")
end
diff --git a/gemfiles/Gemfile.rails-5.2-stable b/gemfiles/Gemfile-rails-7-0
similarity index 54%
rename from gemfiles/Gemfile.rails-5.2-stable
rename to gemfiles/Gemfile-rails-7-0
index 3fc1bb7ca..a397d04fc 100644
--- a/gemfiles/Gemfile.rails-5.2-stable
+++ b/gemfiles/Gemfile-rails-7-0
@@ -2,26 +2,30 @@ source "https://rubygems.org"
gemspec path: ".."
-gem "rails", '~> 5.2'
+gem "rails", "~> 7.0.0"
gem "omniauth"
gem "omniauth-oauth2"
gem "rdoc"
-gem "activemodel-serializers-xml", github: "rails/activemodel-serializers-xml"
-
gem "rails-controller-testing"
-gem "responders", "~> 2.1"
+gem "responders", "~> 3.1"
group :test do
gem "omniauth-facebook"
gem "omniauth-openid"
+ gem "rexml"
gem "timecop"
gem "webrat", "0.7.3", require: false
- gem "mocha", "~> 1.1", require: false
- gem 'test_after_commit', require: false
+ gem "mocha", "~> 2.1", require: false
+ gem "minitest", "< 6"
+ gem "ostruct"
end
platforms :ruby do
- gem "sqlite3", "~> 1.3.6"
+ gem "sqlite3", "~> 1.4"
+end
+
+group :mongoid do
+ gem "mongoid", "~> 7.5"
end
diff --git a/gemfiles/Gemfile-rails-7-1 b/gemfiles/Gemfile-rails-7-1
new file mode 100644
index 000000000..41caefa80
--- /dev/null
+++ b/gemfiles/Gemfile-rails-7-1
@@ -0,0 +1,31 @@
+source "https://rubygems.org"
+
+gemspec path: ".."
+
+gem "rails", "~> 7.1.0"
+gem "omniauth"
+gem "omniauth-oauth2"
+gem "rdoc"
+
+gem "rails-controller-testing"
+
+gem "responders", "~> 3.1"
+
+group :test do
+ gem "omniauth-facebook"
+ gem "omniauth-openid"
+ gem "rexml"
+ gem "timecop"
+ gem "webrat"
+ gem "mocha", "~> 2.1", require: false
+ gem "minitest", "< 6"
+ gem "ostruct"
+end
+
+platforms :ruby do
+ gem "sqlite3", "~> 1.4"
+end
+
+group :mongoid do
+ gem "mongoid", "~> 8.1"
+end
diff --git a/gemfiles/Gemfile-rails-7-2 b/gemfiles/Gemfile-rails-7-2
new file mode 100644
index 000000000..deecdfe9c
--- /dev/null
+++ b/gemfiles/Gemfile-rails-7-2
@@ -0,0 +1,31 @@
+source "https://rubygems.org"
+
+gemspec path: ".."
+
+gem "rails", "~> 7.2.0"
+gem "omniauth"
+gem "omniauth-oauth2"
+gem "rdoc"
+
+gem "rails-controller-testing"
+
+gem "responders", "~> 3.1"
+
+group :test do
+ gem "omniauth-facebook"
+ gem "omniauth-openid"
+ gem "rexml"
+ gem "timecop"
+ gem "webrat", require: false
+ gem "mocha", "~> 2.1", require: false
+ gem "minitest", "< 6"
+ gem "ostruct"
+end
+
+platforms :ruby do
+ gem "sqlite3"
+end
+
+group :mongoid do
+ gem "mongoid", "~> 8.1"
+end
diff --git a/gemfiles/Gemfile-rails-8-0 b/gemfiles/Gemfile-rails-8-0
new file mode 100644
index 000000000..23954ff58
--- /dev/null
+++ b/gemfiles/Gemfile-rails-8-0
@@ -0,0 +1,31 @@
+source "https://rubygems.org"
+
+gemspec path: ".."
+
+gem "rails", "~> 8.0.0"
+gem "omniauth"
+gem "omniauth-oauth2"
+gem "rdoc"
+
+gem "rails-controller-testing"
+
+gem "responders", "~> 3.1"
+
+group :test do
+ gem "omniauth-facebook"
+ gem "omniauth-openid"
+ gem "rexml"
+ gem "timecop"
+ gem 'webrat'
+ gem "mocha", "~> 2.1", require: false
+ gem "minitest", "< 6"
+ gem "ostruct"
+end
+
+platforms :ruby do
+ gem "sqlite3"
+end
+
+group :mongoid do
+ gem "mongoid", "~> 8.1"
+end
diff --git a/gemfiles/Gemfile.rails-6.0-beta b/gemfiles/Gemfile-rails-main
similarity index 51%
rename from gemfiles/Gemfile.rails-6.0-beta
rename to gemfiles/Gemfile-rails-main
index 6b11adb97..57ed95a1b 100644
--- a/gemfiles/Gemfile.rails-6.0-beta
+++ b/gemfiles/Gemfile-rails-main
@@ -2,26 +2,30 @@ source "https://rubygems.org"
gemspec path: ".."
-gem 'rails', '~> 6.0.0.beta3'
+gem "rails", github: "rails/rails", branch: "main"
gem "omniauth"
gem "omniauth-oauth2"
gem "rdoc"
-gem "activemodel-serializers-xml", github: "rails/activemodel-serializers-xml"
-
gem "rails-controller-testing"
-gem "responders", "~> 2.4"
+gem "responders", "~> 3.1"
group :test do
gem "omniauth-facebook"
gem "omniauth-openid"
+ gem "rexml"
gem "timecop"
gem "webrat", "0.7.3", require: false
- gem "mocha", "~> 1.1", require: false
- gem 'test_after_commit', require: false
+ gem "mocha", "~> 2.1", require: false
+ gem "minitest", "< 6"
+ gem "ostruct"
end
platforms :ruby do
- gem "sqlite3", "~> 1.3.6"
+ gem "sqlite3"
+end
+
+group :mongoid do
+ gem "mongoid", github: "mongodb/mongoid", branch: "master"
end
diff --git a/gemfiles/Gemfile.rails-4.1-stable b/gemfiles/Gemfile.rails-4.1-stable
deleted file mode 100644
index 44a1c98aa..000000000
--- a/gemfiles/Gemfile.rails-4.1-stable
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-gemspec path: ".."
-
-gem "rails", github: "rails/rails", branch: "4-1-stable"
-gem "omniauth"
-gem "omniauth-oauth2"
-gem "rdoc", "~> 5.1"
-# Force this version because it's breaking on CI since a higher nokogiri version requires Ruby 2.3+.
-gem "nokogiri", "1.9.1"
-
-group :test do
- gem "omniauth-facebook"
- gem "omniauth-openid"
- gem "timecop"
- gem "webrat", "0.7.3", require: false
- gem "mocha", "~> 1.1", require: false
- gem 'test_after_commit', require: false
-end
-
-platforms :jruby do
- gem "activerecord-jdbc-adapter"
- gem "activerecord-jdbcsqlite3-adapter"
- gem "jruby-openssl"
-end
-
-platforms :ruby do
- gem "sqlite3", "~> 1.3.6"
-end
-
-group :mongoid do
- gem "mongoid", "~> 4.0"
-end
diff --git a/gemfiles/Gemfile.rails-4.1-stable.lock b/gemfiles/Gemfile.rails-4.1-stable.lock
deleted file mode 100644
index fbf188839..000000000
--- a/gemfiles/Gemfile.rails-4.1-stable.lock
+++ /dev/null
@@ -1,174 +0,0 @@
-GIT
- remote: git://github.com/rails/rails.git
- revision: 0cad778c2605a5204a05a9f1dbd3344e39f248d8
- branch: 4-1-stable
- specs:
- actionmailer (4.1.16)
- actionpack (= 4.1.16)
- actionview (= 4.1.16)
- mail (~> 2.5, >= 2.5.4)
- rails (4.1.16)
- actionmailer (= 4.1.16)
- actionpack (= 4.1.16)
- actionview (= 4.1.16)
- activemodel (= 4.1.16)
- activerecord (= 4.1.16)
- activesupport (= 4.1.16)
- bundler (>= 1.3.0, < 2.0)
- railties (= 4.1.16)
- sprockets-rails (~> 2.0)
-
-PATH
- remote: ..
- specs:
- devise (4.6.2)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
-
-GEM
- remote: https://rubygems.org/
- specs:
- actionpack (4.1.16)
- actionview (= 4.1.16)
- activesupport (= 4.1.16)
- rack (~> 1.5.2)
- rack-test (~> 0.6.2)
- actionview (4.1.16)
- activesupport (= 4.1.16)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- activemodel (4.1.16)
- activesupport (= 4.1.16)
- builder (~> 3.1)
- activerecord (4.1.16)
- activemodel (= 4.1.16)
- activesupport (= 4.1.16)
- arel (~> 5.0.0)
- activesupport (4.1.16)
- i18n (~> 0.6, >= 0.6.9)
- json (~> 1.7, >= 1.7.7)
- minitest (~> 5.1)
- thread_safe (~> 0.1)
- tzinfo (~> 1.1)
- arel (5.0.1.20140414130214)
- bcrypt (3.1.12)
- bson (3.2.6)
- builder (3.2.3)
- concurrent-ruby (1.0.5)
- connection_pool (2.2.1)
- erubis (2.7.0)
- faraday (0.11.0)
- multipart-post (>= 1.2, < 3)
- hashie (3.5.5)
- i18n (0.8.1)
- json (1.8.6)
- jwt (1.5.6)
- mail (2.6.4)
- mime-types (>= 1.16, < 4)
- metaclass (0.0.4)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mini_portile2 (2.4.0)
- minitest (5.10.1)
- mocha (1.2.1)
- metaclass (~> 0.0.1)
- mongoid (4.0.2)
- activemodel (~> 4.0)
- moped (~> 2.0.0)
- origin (~> 2.1)
- tzinfo (>= 0.3.37)
- moped (2.0.7)
- bson (~> 3.0)
- connection_pool (~> 2.0)
- optionable (~> 0.2.0)
- multi_json (1.12.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- nokogiri (1.9.1)
- mini_portile2 (~> 2.4.0)
- oauth2 (1.3.1)
- faraday (>= 0.8, < 0.12)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (1.4.2)
- hashie (>= 1.2, < 4)
- rack (>= 1.0, < 3)
- omniauth-facebook (4.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-oauth2 (1.4.0)
- oauth2 (~> 1.0)
- omniauth (~> 1.2)
- omniauth-openid (1.0.1)
- omniauth (~> 1.0)
- rack-openid (~> 1.3.1)
- optionable (0.2.0)
- origin (2.3.0)
- orm_adapter (0.5.0)
- rack (1.5.5)
- rack-openid (1.3.1)
- rack (>= 1.1.0)
- ruby-openid (>= 2.1.8)
- rack-test (0.6.3)
- rack (>= 1.0)
- railties (4.1.16)
- actionpack (= 4.1.16)
- activesupport (= 4.1.16)
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
- rake (12.0.0)
- rdoc (5.1.0)
- responders (1.1.2)
- railties (>= 3.2, < 4.2)
- ruby-openid (2.7.0)
- sprockets (3.7.1)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (2.3.3)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- sprockets (>= 2.8, < 4.0)
- sqlite3 (1.3.13)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
- thor (0.19.4)
- thread_safe (0.3.6)
- timecop (0.8.1)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
- warden (1.2.7)
- rack (>= 1.0)
- webrat (0.7.3)
- nokogiri (>= 1.2.0)
- rack (>= 1.0)
- rack-test (>= 0.5.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- activerecord-jdbc-adapter
- activerecord-jdbcsqlite3-adapter
- devise!
- jruby-openssl
- mocha (~> 1.1)
- mongoid (~> 4.0)
- nokogiri (= 1.9.1)
- omniauth
- omniauth-facebook
- omniauth-oauth2
- omniauth-openid
- rails!
- rdoc (~> 5.1)
- sqlite3 (~> 1.3.6)
- test_after_commit
- timecop
- webrat (= 0.7.3)
-
-BUNDLED WITH
- 1.17.3
diff --git a/gemfiles/Gemfile.rails-4.2-stable b/gemfiles/Gemfile.rails-4.2-stable
deleted file mode 100644
index bb35c5f2f..000000000
--- a/gemfiles/Gemfile.rails-4.2-stable
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-gemspec path: ".."
-
-gem "rails", github: "rails/rails", branch: "4-2-stable"
-gem "omniauth"
-gem "omniauth-oauth2"
-gem "rdoc", "~> 5.1"
-gem "nokogiri", "1.9.1"
-
-group :test do
- gem "omniauth-facebook"
- gem "omniauth-openid"
- gem "timecop"
- gem "webrat", "0.7.3", require: false
- gem "mocha", "~> 1.1", require: false
- gem 'test_after_commit', require: false
-end
-
-platforms :jruby do
- gem "activerecord-jdbc-adapter"
- gem "activerecord-jdbcsqlite3-adapter"
- gem "jruby-openssl"
-end
-
-platforms :ruby do
- gem "sqlite3", "~> 1.3.6"
-end
-
-group :mongoid do
- gem "mongoid", "~> 4.0"
-end
diff --git a/gemfiles/Gemfile.rails-4.2-stable.lock b/gemfiles/Gemfile.rails-4.2-stable.lock
deleted file mode 100644
index de76d2106..000000000
--- a/gemfiles/Gemfile.rails-4.2-stable.lock
+++ /dev/null
@@ -1,195 +0,0 @@
-GIT
- remote: git://github.com/rails/rails.git
- revision: dc3ae21802c316e1639239d28202db7aa7fb7cac
- branch: 4-2-stable
- specs:
- actionmailer (4.2.8)
- actionpack (= 4.2.8)
- actionview (= 4.2.8)
- activejob (= 4.2.8)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (4.2.8)
- actionview (= 4.2.8)
- activesupport (= 4.2.8)
- rack (~> 1.6)
- rack-test (~> 0.6.2)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (4.2.8)
- activesupport (= 4.2.8)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (4.2.8)
- activesupport (= 4.2.8)
- globalid (>= 0.3.0)
- activemodel (4.2.8)
- activesupport (= 4.2.8)
- builder (~> 3.1)
- activerecord (4.2.8)
- activemodel (= 4.2.8)
- activesupport (= 4.2.8)
- arel (~> 6.0)
- activesupport (4.2.8)
- i18n (~> 0.7)
- minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
- tzinfo (~> 1.1)
- rails (4.2.8)
- actionmailer (= 4.2.8)
- actionpack (= 4.2.8)
- actionview (= 4.2.8)
- activejob (= 4.2.8)
- activemodel (= 4.2.8)
- activerecord (= 4.2.8)
- activesupport (= 4.2.8)
- bundler (>= 1.3.0, < 2.0)
- railties (= 4.2.8)
- sprockets-rails
- railties (4.2.8)
- actionpack (= 4.2.8)
- activesupport (= 4.2.8)
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
-
-PATH
- remote: ..
- specs:
- devise (4.6.2)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
-
-GEM
- remote: https://rubygems.org/
- specs:
- arel (6.0.4)
- bcrypt (3.1.12)
- bson (3.2.6)
- builder (3.2.3)
- concurrent-ruby (1.0.5)
- connection_pool (2.2.1)
- erubis (2.7.0)
- faraday (0.11.0)
- multipart-post (>= 1.2, < 3)
- globalid (0.3.7)
- activesupport (>= 4.1.0)
- hashie (3.5.5)
- i18n (0.8.1)
- jwt (1.5.6)
- loofah (2.0.3)
- nokogiri (>= 1.5.9)
- mail (2.6.4)
- mime-types (>= 1.16, < 4)
- metaclass (0.0.4)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mini_portile2 (2.4.0)
- minitest (5.10.1)
- mocha (1.2.1)
- metaclass (~> 0.0.1)
- mongoid (4.0.2)
- activemodel (~> 4.0)
- moped (~> 2.0.0)
- origin (~> 2.1)
- tzinfo (>= 0.3.37)
- moped (2.0.7)
- bson (~> 3.0)
- connection_pool (~> 2.0)
- optionable (~> 0.2.0)
- multi_json (1.12.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- nokogiri (1.9.1)
- mini_portile2 (~> 2.4.0)
- oauth2 (1.3.1)
- faraday (>= 0.8, < 0.12)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (1.6.1)
- hashie (>= 3.4.6, < 3.6.0)
- rack (>= 1.6.2, < 3)
- omniauth-facebook (4.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-oauth2 (1.4.0)
- oauth2 (~> 1.0)
- omniauth (~> 1.2)
- omniauth-openid (1.0.1)
- omniauth (~> 1.0)
- rack-openid (~> 1.3.1)
- optionable (0.2.0)
- origin (2.3.0)
- orm_adapter (0.5.0)
- rack (1.6.5)
- rack-openid (1.3.1)
- rack (>= 1.1.0)
- ruby-openid (>= 2.1.8)
- rack-test (0.6.3)
- rack (>= 1.0)
- rails-deprecated_sanitizer (1.0.3)
- activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.8)
- activesupport (>= 4.2.0.beta, < 5.0)
- nokogiri (~> 1.6)
- rails-deprecated_sanitizer (>= 1.0.1)
- rails-html-sanitizer (1.0.3)
- loofah (~> 2.0)
- rake (12.0.0)
- rdoc (5.1.0)
- responders (2.4.1)
- actionpack (>= 4.2.0, < 6.0)
- railties (>= 4.2.0, < 6.0)
- ruby-openid (2.7.0)
- sprockets (3.7.1)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.0)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- sqlite3 (1.3.13)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
- thor (0.19.4)
- thread_safe (0.3.6)
- timecop (0.8.1)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
- warden (1.2.7)
- rack (>= 1.0)
- webrat (0.7.3)
- nokogiri (>= 1.2.0)
- rack (>= 1.0)
- rack-test (>= 0.5.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- activerecord-jdbc-adapter
- activerecord-jdbcsqlite3-adapter
- devise!
- jruby-openssl
- mocha (~> 1.1)
- mongoid (~> 4.0)
- nokogiri (= 1.9.1)
- omniauth
- omniauth-facebook
- omniauth-oauth2
- omniauth-openid
- rails!
- rdoc (~> 5.1)
- sqlite3 (~> 1.3.6)
- test_after_commit
- timecop
- webrat (= 0.7.3)
-
-BUNDLED WITH
- 1.17.3
diff --git a/gemfiles/Gemfile.rails-5.0-stable b/gemfiles/Gemfile.rails-5.0-stable
deleted file mode 100644
index 902ef21a5..000000000
--- a/gemfiles/Gemfile.rails-5.0-stable
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-gemspec path: ".."
-
-gem "rails", '~> 5.0.0'
-gem "omniauth"
-gem "omniauth-oauth2"
-gem "rdoc"
-
-gem "activemodel-serializers-xml", github: "rails/activemodel-serializers-xml"
-
-gem "rails-controller-testing"
-
-gem "responders", "~> 2.1"
-
-group :test do
- gem "omniauth-facebook"
- gem "omniauth-openid"
- gem "timecop"
- gem "webrat", "0.7.3", require: false
- gem "mocha", "~> 1.1", require: false
- gem 'test_after_commit', require: false
-end
-
-platforms :ruby do
- gem "sqlite3", "~> 1.3.6"
-end
-
-# TODO:
-# group :mongoid do
-# gem "mongoid", "~> 4.0.0"
-# end
diff --git a/gemfiles/Gemfile.rails-5.0-stable.lock b/gemfiles/Gemfile.rails-5.0-stable.lock
deleted file mode 100644
index f4a97f8e0..000000000
--- a/gemfiles/Gemfile.rails-5.0-stable.lock
+++ /dev/null
@@ -1,194 +0,0 @@
-GIT
- remote: git://github.com/rails/activemodel-serializers-xml.git
- revision: dd9c0acf26aab111ebc647cd8deb99ebc6946531
- specs:
- activemodel-serializers-xml (1.0.1)
- activemodel (> 5.x)
- activesupport (> 5.x)
- builder (~> 3.1)
-
-PATH
- remote: ..
- specs:
- devise (4.6.2)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
-
-GEM
- remote: https://rubygems.org/
- specs:
- actioncable (5.0.2)
- actionpack (= 5.0.2)
- nio4r (>= 1.2, < 3.0)
- websocket-driver (~> 0.6.1)
- actionmailer (5.0.2)
- actionpack (= 5.0.2)
- actionview (= 5.0.2)
- activejob (= 5.0.2)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 2.0)
- actionpack (5.0.2)
- actionview (= 5.0.2)
- activesupport (= 5.0.2)
- rack (~> 2.0)
- rack-test (~> 0.6.3)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.0.2)
- activesupport (= 5.0.2)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (5.0.2)
- activesupport (= 5.0.2)
- globalid (>= 0.3.6)
- activemodel (5.0.2)
- activesupport (= 5.0.2)
- activerecord (5.0.2)
- activemodel (= 5.0.2)
- activesupport (= 5.0.2)
- arel (~> 7.0)
- activesupport (5.0.2)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (~> 0.7)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- arel (7.1.4)
- bcrypt (3.1.12)
- builder (3.2.3)
- concurrent-ruby (1.0.5)
- erubis (2.7.0)
- faraday (0.11.0)
- multipart-post (>= 1.2, < 3)
- globalid (0.4.0)
- activesupport (>= 4.2.0)
- hashie (3.5.5)
- i18n (0.8.1)
- jwt (1.5.6)
- loofah (2.0.3)
- nokogiri (>= 1.5.9)
- mail (2.6.5)
- mime-types (>= 1.16, < 4)
- metaclass (0.0.4)
- method_source (0.8.2)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mini_portile2 (2.1.0)
- minitest (5.10.1)
- mocha (1.2.1)
- metaclass (~> 0.0.1)
- multi_json (1.12.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- nio4r (2.0.0)
- nokogiri (1.7.2)
- mini_portile2 (~> 2.1.0)
- oauth2 (1.3.1)
- faraday (>= 0.8, < 0.12)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (1.6.1)
- hashie (>= 3.4.6, < 3.6.0)
- rack (>= 1.6.2, < 3)
- omniauth-facebook (4.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-oauth2 (1.4.0)
- oauth2 (~> 1.0)
- omniauth (~> 1.2)
- omniauth-openid (1.0.1)
- omniauth (~> 1.0)
- rack-openid (~> 1.3.1)
- orm_adapter (0.5.0)
- rack (2.0.2)
- rack-openid (1.3.1)
- rack (>= 1.1.0)
- ruby-openid (>= 2.1.8)
- rack-test (0.6.3)
- rack (>= 1.0)
- rails (5.0.2)
- actioncable (= 5.0.2)
- actionmailer (= 5.0.2)
- actionpack (= 5.0.2)
- actionview (= 5.0.2)
- activejob (= 5.0.2)
- activemodel (= 5.0.2)
- activerecord (= 5.0.2)
- activesupport (= 5.0.2)
- bundler (>= 1.3.0, < 2.0)
- railties (= 5.0.2)
- sprockets-rails (>= 2.0.0)
- rails-controller-testing (1.0.1)
- actionpack (~> 5.x)
- actionview (~> 5.x)
- activesupport (~> 5.x)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
- nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.3)
- loofah (~> 2.0)
- railties (5.0.2)
- actionpack (= 5.0.2)
- activesupport (= 5.0.2)
- method_source
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
- rake (12.0.0)
- rdoc (5.1.0)
- responders (2.4.0)
- actionpack (>= 4.2.0, < 5.3)
- railties (>= 4.2.0, < 5.3)
- ruby-openid (2.7.0)
- sprockets (3.7.1)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.0)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- sqlite3 (1.3.13)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
- thor (0.19.4)
- thread_safe (0.3.6)
- timecop (0.8.1)
- tzinfo (1.2.3)
- thread_safe (~> 0.1)
- warden (1.2.7)
- rack (>= 1.0)
- webrat (0.7.3)
- nokogiri (>= 1.2.0)
- rack (>= 1.0)
- rack-test (>= 0.5.3)
- websocket-driver (0.6.5)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.2)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- activemodel-serializers-xml!
- devise!
- mocha (~> 1.1)
- omniauth
- omniauth-facebook
- omniauth-oauth2
- omniauth-openid
- rails (~> 5.0.0)
- rails-controller-testing
- rdoc
- responders (~> 2.1)
- sqlite3 (~> 1.3.6)
- test_after_commit
- timecop
- webrat (= 0.7.3)
-
-BUNDLED WITH
- 1.17.1
diff --git a/gemfiles/Gemfile.rails-5.2-stable.lock b/gemfiles/Gemfile.rails-5.2-stable.lock
deleted file mode 100644
index c9541696b..000000000
--- a/gemfiles/Gemfile.rails-5.2-stable.lock
+++ /dev/null
@@ -1,203 +0,0 @@
-GIT
- remote: git://github.com/rails/activemodel-serializers-xml.git
- revision: f744aeca2747ed3134e492249c4ee39b548efdf6
- specs:
- activemodel-serializers-xml (1.0.2)
- activemodel (> 5.x)
- activesupport (> 5.x)
- builder (~> 3.1)
-
-PATH
- remote: ..
- specs:
- devise (4.6.2)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
-
-GEM
- remote: https://rubygems.org/
- specs:
- actioncable (5.2.1)
- actionpack (= 5.2.1)
- nio4r (~> 2.0)
- websocket-driver (>= 0.6.1)
- actionmailer (5.2.1)
- actionpack (= 5.2.1)
- actionview (= 5.2.1)
- activejob (= 5.2.1)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 2.0)
- actionpack (5.2.1)
- actionview (= 5.2.1)
- activesupport (= 5.2.1)
- rack (~> 2.0)
- rack-test (>= 0.6.3)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (5.2.1)
- activesupport (= 5.2.1)
- builder (~> 3.1)
- erubi (~> 1.4)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (5.2.1)
- activesupport (= 5.2.1)
- globalid (>= 0.3.6)
- activemodel (5.2.1)
- activesupport (= 5.2.1)
- activerecord (5.2.1)
- activemodel (= 5.2.1)
- activesupport (= 5.2.1)
- arel (>= 9.0)
- activestorage (5.2.1)
- actionpack (= 5.2.1)
- activerecord (= 5.2.1)
- marcel (~> 0.3.1)
- activesupport (5.2.1)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- arel (9.0.0)
- bcrypt (3.1.12)
- builder (3.2.3)
- concurrent-ruby (1.0.5)
- crass (1.0.4)
- erubi (1.7.1)
- faraday (0.12.2)
- multipart-post (>= 1.2, < 3)
- globalid (0.4.1)
- activesupport (>= 4.2.0)
- hashie (3.5.7)
- i18n (1.1.0)
- concurrent-ruby (~> 1.0)
- jwt (1.5.6)
- loofah (2.2.2)
- crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.0)
- mini_mime (>= 0.1.1)
- marcel (0.3.3)
- mimemagic (~> 0.3.2)
- metaclass (0.0.4)
- method_source (0.9.0)
- mimemagic (0.3.2)
- mini_mime (1.0.1)
- mini_portile2 (2.3.0)
- minitest (5.11.3)
- mocha (1.7.0)
- metaclass (~> 0.0.1)
- multi_json (1.13.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- nio4r (2.3.1)
- nokogiri (1.8.5)
- mini_portile2 (~> 2.3.0)
- oauth2 (1.4.0)
- faraday (>= 0.8, < 0.13)
- jwt (~> 1.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (1.8.1)
- hashie (>= 3.4.6, < 3.6.0)
- rack (>= 1.6.2, < 3)
- omniauth-facebook (5.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-oauth2 (1.5.0)
- oauth2 (~> 1.1)
- omniauth (~> 1.2)
- omniauth-openid (1.0.1)
- omniauth (~> 1.0)
- rack-openid (~> 1.3.1)
- orm_adapter (0.5.0)
- rack (2.0.5)
- rack-openid (1.3.1)
- rack (>= 1.1.0)
- ruby-openid (>= 2.1.8)
- rack-test (1.1.0)
- rack (>= 1.0, < 3)
- rails (5.2.1)
- actioncable (= 5.2.1)
- actionmailer (= 5.2.1)
- actionpack (= 5.2.1)
- actionview (= 5.2.1)
- activejob (= 5.2.1)
- activemodel (= 5.2.1)
- activerecord (= 5.2.1)
- activestorage (= 5.2.1)
- activesupport (= 5.2.1)
- bundler (>= 1.3.0)
- railties (= 5.2.1)
- sprockets-rails (>= 2.0.0)
- rails-controller-testing (1.0.2)
- actionpack (~> 5.x, >= 5.0.1)
- actionview (~> 5.x, >= 5.0.1)
- activesupport (~> 5.x)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
- nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.4)
- loofah (~> 2.2, >= 2.2.2)
- railties (5.2.1)
- actionpack (= 5.2.1)
- activesupport (= 5.2.1)
- method_source
- rake (>= 0.8.7)
- thor (>= 0.19.0, < 2.0)
- rake (12.3.1)
- rdoc (6.0.4)
- responders (2.4.0)
- actionpack (>= 4.2.0, < 5.3)
- railties (>= 4.2.0, < 5.3)
- ruby-openid (2.7.0)
- sprockets (3.7.2)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.1)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- sqlite3 (1.3.13)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
- thor (0.20.0)
- thread_safe (0.3.6)
- timecop (0.9.1)
- tzinfo (1.2.5)
- thread_safe (~> 0.1)
- warden (1.2.7)
- rack (>= 1.0)
- webrat (0.7.3)
- nokogiri (>= 1.2.0)
- rack (>= 1.0)
- rack-test (>= 0.5.3)
- websocket-driver (0.7.0)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- activemodel-serializers-xml!
- devise!
- mocha (~> 1.1)
- omniauth
- omniauth-facebook
- omniauth-oauth2
- omniauth-openid
- rails (~> 5.2)
- rails-controller-testing
- rdoc
- responders (~> 2.1)
- sqlite3 (~> 1.3.6)
- test_after_commit
- timecop
- webrat (= 0.7.3)
-
-BUNDLED WITH
- 1.17.1
diff --git a/gemfiles/Gemfile.rails-6.0-beta.lock b/gemfiles/Gemfile.rails-6.0-beta.lock
deleted file mode 100644
index 1c4207e46..000000000
--- a/gemfiles/Gemfile.rails-6.0-beta.lock
+++ /dev/null
@@ -1,218 +0,0 @@
-GIT
- remote: git://github.com/rails/activemodel-serializers-xml.git
- revision: 93689638c28525acc65afb638fce866826532641
- specs:
- activemodel-serializers-xml (1.0.2)
- activemodel (>= 5.0.0.a)
- activesupport (>= 5.0.0.a)
- builder (~> 3.1)
-
-PATH
- remote: ..
- specs:
- devise (4.6.2)
- bcrypt (~> 3.0)
- orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 6.0)
- responders
- warden (~> 1.2.3)
-
-GEM
- remote: https://rubygems.org/
- specs:
- actioncable (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- nio4r (~> 2.0)
- websocket-driver (>= 0.6.1)
- actionmailbox (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- mail (>= 2.7.1)
- actionmailer (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 2.0)
- actionpack (6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- rack (~> 2.0)
- rack-test (>= 0.6.3)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actiontext (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- nokogiri (>= 1.8.5)
- actionview (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- builder (~> 3.1)
- erubi (~> 1.4)
- rails-dom-testing (~> 2.0)
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
- activejob (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- globalid (>= 0.3.6)
- activemodel (6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- activerecord (6.0.0.beta3)
- activemodel (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- activestorage (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- marcel (~> 0.3.1)
- activesupport (6.0.0.beta3)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 1.3, >= 1.3.1)
- bcrypt (3.1.12)
- builder (3.2.3)
- concurrent-ruby (1.1.5)
- crass (1.0.4)
- erubi (1.8.0)
- faraday (0.15.4)
- multipart-post (>= 1.2, < 3)
- globalid (0.4.2)
- activesupport (>= 4.2.0)
- hashie (3.6.0)
- i18n (1.6.0)
- concurrent-ruby (~> 1.0)
- jwt (2.1.0)
- loofah (2.2.3)
- crass (~> 1.0.2)
- nokogiri (>= 1.5.9)
- mail (2.7.1)
- mini_mime (>= 0.1.1)
- marcel (0.3.3)
- mimemagic (~> 0.3.2)
- metaclass (0.0.4)
- method_source (0.9.2)
- mimemagic (0.3.3)
- mini_mime (1.0.1)
- mini_portile2 (2.4.0)
- minitest (5.11.3)
- mocha (1.8.0)
- metaclass (~> 0.0.1)
- multi_json (1.13.1)
- multi_xml (0.6.0)
- multipart-post (2.0.0)
- nio4r (2.3.1)
- nokogiri (1.10.2)
- mini_portile2 (~> 2.4.0)
- oauth2 (1.4.1)
- faraday (>= 0.8, < 0.16.0)
- jwt (>= 1.0, < 3.0)
- multi_json (~> 1.3)
- multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (1.9.0)
- hashie (>= 3.4.6, < 3.7.0)
- rack (>= 1.6.2, < 3)
- omniauth-facebook (5.0.0)
- omniauth-oauth2 (~> 1.2)
- omniauth-oauth2 (1.6.0)
- oauth2 (~> 1.1)
- omniauth (~> 1.9)
- omniauth-openid (1.0.1)
- omniauth (~> 1.0)
- rack-openid (~> 1.3.1)
- orm_adapter (0.5.0)
- rack (2.0.7)
- rack-openid (1.3.1)
- rack (>= 1.1.0)
- ruby-openid (>= 2.1.8)
- rack-test (1.1.0)
- rack (>= 1.0, < 3)
- rails (6.0.0.beta3)
- actioncable (= 6.0.0.beta3)
- actionmailbox (= 6.0.0.beta3)
- actionmailer (= 6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- actiontext (= 6.0.0.beta3)
- actionview (= 6.0.0.beta3)
- activejob (= 6.0.0.beta3)
- activemodel (= 6.0.0.beta3)
- activerecord (= 6.0.0.beta3)
- activestorage (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- bundler (>= 1.3.0)
- railties (= 6.0.0.beta3)
- sprockets-rails (>= 2.0.0)
- rails-controller-testing (1.0.4)
- actionpack (>= 5.0.1.x)
- actionview (>= 5.0.1.x)
- activesupport (>= 5.0.1.x)
- rails-dom-testing (2.0.3)
- activesupport (>= 4.2.0)
- nokogiri (>= 1.6)
- rails-html-sanitizer (1.0.4)
- loofah (~> 2.2, >= 2.2.2)
- railties (6.0.0.beta3)
- actionpack (= 6.0.0.beta3)
- activesupport (= 6.0.0.beta3)
- method_source
- rake (>= 0.8.7)
- thor (>= 0.20.3, < 2.0)
- rake (12.3.2)
- rdoc (6.1.1)
- responders (2.4.1)
- actionpack (>= 4.2.0, < 6.0)
- railties (>= 4.2.0, < 6.0)
- ruby-openid (2.7.0)
- sprockets (3.7.2)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.1)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- sqlite3 (1.3.13)
- test_after_commit (1.1.0)
- activerecord (>= 3.2)
- thor (0.20.3)
- thread_safe (0.3.6)
- timecop (0.9.1)
- tzinfo (1.2.5)
- thread_safe (~> 0.1)
- warden (1.2.8)
- rack (>= 2.0.6)
- webrat (0.7.3)
- nokogiri (>= 1.2.0)
- rack (>= 1.0)
- rack-test (>= 0.5.3)
- websocket-driver (0.7.0)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.3)
- zeitwerk (1.4.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- activemodel-serializers-xml!
- devise!
- mocha (~> 1.1)
- omniauth
- omniauth-facebook
- omniauth-oauth2
- omniauth-openid
- rails (~> 6.0.0.beta3)
- rails-controller-testing
- rdoc
- responders (~> 2.4)
- sqlite3 (~> 1.3.6)
- test_after_commit
- timecop
- webrat (= 0.7.3)
-
-BUNDLED WITH
- 1.17.2
diff --git a/lib/devise.rb b/lib/devise.rb
old mode 100755
new mode 100644
index dceee0890..0904778fd
--- a/lib/devise.rb
+++ b/lib/devise.rb
@@ -13,16 +13,17 @@ module Devise
autoload :Encryptor, 'devise/encryptor'
autoload :FailureApp, 'devise/failure_app'
autoload :OmniAuth, 'devise/omniauth'
+ autoload :Orm, 'devise/orm'
autoload :ParameterFilter, 'devise/parameter_filter'
autoload :ParameterSanitizer, 'devise/parameter_sanitizer'
- autoload :TestHelpers, 'devise/test_helpers'
autoload :TimeInflector, 'devise/time_inflector'
autoload :TokenGenerator, 'devise/token_generator'
- autoload :SecretKeyFinder, 'devise/secret_key_finder'
module Controllers
+ autoload :Generator, 'devise/controllers/generator'
autoload :Helpers, 'devise/controllers/helpers'
autoload :Rememberable, 'devise/controllers/rememberable'
+ autoload :Responder, 'devise/controllers/responder'
autoload :ScopedViews, 'devise/controllers/scoped_views'
autoload :SignInOut, 'devise/controllers/sign_in_out'
autoload :StoreLocation, 'devise/controllers/store_location'
@@ -37,6 +38,16 @@ module Mailers
autoload :Helpers, 'devise/mailers/helpers'
end
+ module Mixins
+ autoload :Base, 'devise/mixins/base'
+ autoload :Confirmation, 'devise/mixins/confirmation'
+ autoload :OmniauthCallback, 'devise/mixins/omniauth_callback'
+ autoload :Password, 'devise/mixins/password'
+ autoload :Registration, 'devise/mixins/registration'
+ autoload :Session, 'devise/mixins/session'
+ autoload :Unlock, 'devise/mixins/unlock'
+ end
+
module Strategies
autoload :Base, 'devise/strategies/base'
autoload :Authenticatable, 'devise/strategies/authenticatable'
@@ -59,7 +70,7 @@ module Test
NO_INPUT = []
# True values used to check params
- TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
+ TRUE_VALUES = [true, 1, '1', 'on', 'ON', 't', 'T', 'true', 'TRUE']
# Secret key used by the key generator
mattr_accessor :secret_key
@@ -71,7 +82,7 @@ module Test
# The number of times to hash the password.
mattr_accessor :stretches
- @@stretches = 11
+ @@stretches = 12
# The default key used when authenticating over http auth.
mattr_accessor :http_authentication_key
@@ -217,7 +228,16 @@ module Test
# Which formats should be treated as navigational.
mattr_accessor :navigational_formats
- @@navigational_formats = ["*/*", :html]
+ @@navigational_formats = ["*/*", :html, :turbo_stream]
+
+ # The default responder used by Devise, used to customize status codes with:
+ #
+ # `config.responder.error_status`
+ # `config.responder.redirect_status`
+ #
+ # Can be replaced by a custom application responder.
+ mattr_accessor :responder
+ @@responder = Devise::Controllers::Responder
# When set to true, signing out a user signs out all other scopes.
mattr_accessor :sign_out_all_scopes
@@ -264,8 +284,14 @@ module Test
# PRIVATE CONFIGURATION
# Store scopes mappings.
- mattr_reader :mappings
@@mappings = {}
+ def self.mappings
+ # Starting from Rails 8.0, routes are lazy-loaded by default in test and development environments.
+ # However, Devise's mappings are built during the routes loading phase.
+ # To ensure it works correctly, we need to load the routes first before accessing @@mappings.
+ Rails.application.try(:reload_routes_unless_loaded)
+ @@mappings
+ end
# OmniAuth configurations.
mattr_reader :omniauth_configs
@@ -293,18 +319,17 @@ module Test
mattr_accessor :token_generator
@@token_generator = nil
+ # If within the same application, Devise is to be mounted on different engines.
+ # Each scope listed here will get a generated `
::Devise::*Controller`
+ # set inheriting from `::ApplicationController`, allowing per-engine
+ # helpers/callbacks/concerns to be available to Devise actions.
+ mattr_accessor :controller_scopes
+ @@controller_scopes = [:devise]
+
# When set to false, changing a password does not automatically sign in a user
mattr_accessor :sign_in_after_change_password
@@sign_in_after_change_password = true
- def self.rails51? # :nodoc:
- Rails.gem_version >= Gem::Version.new("5.1.x")
- end
-
- def self.activerecord51? # :nodoc:
- defined?(ActiveRecord) && ActiveRecord.gem_version >= Gem::Version.new("5.1.x")
- end
-
# Default way to set up Devise. Run rails generate devise_install to create
# a fresh initializer with all configuration values.
def self.setup
@@ -317,12 +342,20 @@ def initialize(name)
end
def get
- ActiveSupport::Dependencies.constantize(@name)
+ # TODO: Remove AS::Dependencies usage when dropping support to Rails < 7.
+ if ActiveSupport::Dependencies.respond_to?(:constantize)
+ ActiveSupport::Dependencies.constantize(@name)
+ else
+ @name.constantize
+ end
end
end
def self.ref(arg)
- ActiveSupport::Dependencies.reference(arg)
+ # TODO: Remove AS::Dependencies usage when dropping support to Rails < 7.
+ if ActiveSupport::Dependencies.respond_to?(:reference)
+ ActiveSupport::Dependencies.reference(arg)
+ end
Getter.new(arg)
end
@@ -430,9 +463,9 @@ def self.add_module(module_name, options = {})
# Devise.setup do |config|
# config.allow_unconfirmed_access_for = 2.days
#
- # config.warden do |manager|
+ # config.warden do |warden_config|
# # Configure warden to use other strategies, like oauth.
- # manager.oauth(:twitter)
+ # warden_config.oauth(:twitter)
# end
# end
def self.warden(&block)
@@ -502,12 +535,12 @@ def self.friendly_token(length = 20)
# constant-time comparison algorithm to prevent timing attacks
def self.secure_compare(a, b)
- return false if a.blank? || b.blank? || a.bytesize != b.bytesize
- l = a.unpack "C#{a.bytesize}"
+ return false if a.nil? || b.nil?
+ ActiveSupport::SecurityUtils.secure_compare(a, b)
+ end
- res = 0
- b.each_byte { |byte| res |= byte ^ l.shift }
- res == 0
+ def self.deprecator
+ @deprecator ||= ActiveSupport::Deprecation.new("5.0", "Devise")
end
end
diff --git a/lib/devise/controllers/generator.rb b/lib/devise/controllers/generator.rb
new file mode 100644
index 000000000..ccae6f6af
--- /dev/null
+++ b/lib/devise/controllers/generator.rb
@@ -0,0 +1,135 @@
+module Devise
+ module Controllers
+ # Generates per-scope Devise controllers at boot for each scope in
+ # `Devise.controller_scopes` (configured per application).
+ #
+ # For a scope `:my_scope`, this creates the constants
+ # MyScope::Devise::BaseController < MyScope::ApplicationController
+ # MyScope::Devise::SessionsController < MyScope::Devise::BaseController
+ # ... (and Passwords/Registrations/Confirmations/Unlocks/OmniauthCallbacks)
+ #
+ # Devise behavior is injected by including the corresponding `Devise::Mixins::*`
+ # module into each generated class. The mixin pattern exists because Ruby has
+ # single inheritance: each generated controller must inherit from the host
+ # engine's `ApplicationController` (so it picks up the engine's layout, helpers,
+ # before_actions, etc.), so Devise's per-controller logic cannot also live in
+ # the parent class — it has to be a module.
+ #
+ # IMPORTANT — lexical constant resolution:
+ # Engine controllers in the host app write
+ # module MyScope
+ # class SessionsController < Devise::SessionsController
+ # ...
+ # end
+ # end
+ # Ruby's lexical lookup finds `Devise` in the enclosing `MyScope` module
+ # first, so the bare reference `Devise::SessionsController` resolves to
+ # `MyScope::Devise::SessionsController` (this generator's output), NOT
+ # to the gem's top-level `Devise::SessionsController`. That is what makes the
+ # engine controller inherit from the engine's `ApplicationController` chain.
+ #
+ # Do not "simplify" by dropping the mixin pattern or this generator — the
+ # engine controllers depend on the lexical-resolution trick to inherit from
+ # `::ApplicationController` instead of the gem's `DeviseController`.
+ class Generator
+
+ AVAILABLE_CONTROLLERS = [:confirmation, :omniauth_callback, :password, :registration, :session, :unlock]
+
+ attr_reader :scope, :controllers
+
+ def initialize(scope = :devise, *controllers)
+ @scope = scope.to_sym
+ @parent = parent_controller
+ @controllers = only_available(controllers)
+ end
+
+ def generate
+ base_controller
+ controllers.each do |controller|
+ devise_module_controller(controller)
+ end
+ end
+
+ class << self
+ def generate(scope = :devise, *controllers)
+ new(scope, *controllers).generate
+ end
+ end
+
+ private
+
+ def only_available(args)
+ return AVAILABLE_CONTROLLERS if args.blank? or args == [:all]
+ AVAILABLE_CONTROLLERS & Array(args)
+ end
+
+ def base_controller_name
+ if scope == :devise
+ "Devise::BaseController"
+ else
+ "#{scope.to_s.classify}::Devise::BaseController"
+ end
+ end
+
+ def parent_controller
+ if scope == :devise
+ Devise.parent_controller.to_s
+ else
+ "#{scope.to_s.classify}::ApplicationController"
+ end.constantize
+ end
+
+ def controller_name(option)
+ "#{option.to_s.classify.pluralize}Controller"
+ end
+
+ def root_module
+ scope.to_s.classify.constantize
+ rescue StandardError
+ Object.const_set(scope.to_s.classify, Module.new)
+ end
+
+ def scoped_module
+ (scope == :devise) ? root_module : "#{root_module}::Devise".constantize
+ rescue StandardError
+ root_module.const_set(:Devise, Module.new)
+ end
+
+ def set_devise_router
+ # The default :devise scope's routes live in the host app's main router,
+ # so Devise.router_name must stay nil (→ Devise.available_router_name == :main_app).
+ # Setting it to :devise would route URL helpers through `view.devise`, which
+ # only exists if something is literally `mount`ed `as: :devise`.
+ return if scope == :devise
+
+ @parent.class_variable_set('@@devise_controller_scope', scope)
+ @parent.class_eval do
+ before_action ->{ Devise.router_name = self.class.class_variable_get('@@devise_controller_scope') }
+ end
+ end
+
+ def base_controller
+ set_devise_router
+ klass = find_or_create_class(:BaseController)
+ klass.send(:include, Devise::Mixins::Base)
+ end
+
+ def devise_module_controller(controller)
+ name = controller_name(controller).to_sym
+ mixin = Devise::Mixins.const_get(controller.to_s.classify)
+ klass = find_or_create_class(name, base_controller_name)
+ klass.send(:include, mixin)
+ end
+
+ def find_or_create_class(name, parent_name = nil)
+ parent = (parent_name || @parent).to_s.constantize
+
+ if scoped_module.constants.include?(name)
+ scoped_module.const_get(name)
+ else
+ scoped_module.const_set(name, Class.new(parent))
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/controllers/helpers.rb b/lib/devise/controllers/helpers.rb
index 7ef8507ff..62d2631d1 100644
--- a/lib/devise/controllers/helpers.rb
+++ b/lib/devise/controllers/helpers.rb
@@ -36,16 +36,17 @@ module ClassMethods
# before_action ->{ authenticate_blogger! :admin } # Redirects to the admin login page
# current_blogger :user # Preferably returns a User if one is signed in
#
- def devise_group(group_name, opts={})
+ def devise_group(group_name, opts = {})
mappings = "[#{ opts[:contains].map { |m| ":#{m}" }.join(',') }]"
class_eval <<-METHODS, __FILE__, __LINE__ + 1
- def authenticate_#{group_name}!(favourite=nil, opts={})
+ def authenticate_#{group_name}!(favorite = nil, opts = {})
unless #{group_name}_signed_in?
mappings = #{mappings}
- mappings.unshift mappings.delete(favourite.to_sym) if favourite
+ mappings.unshift mappings.delete(favorite.to_sym) if favorite
mappings.each do |mapping|
opts[:scope] = mapping
+ opts[:locale] = I18n.locale
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
end
@@ -57,9 +58,9 @@ def #{group_name}_signed_in?
end
end
- def current_#{group_name}(favourite=nil)
+ def current_#{group_name}(favorite = nil)
mappings = #{mappings}
- mappings.unshift mappings.delete(favourite.to_sym) if favourite
+ mappings.unshift mappings.delete(favorite.to_sym) if favorite
mappings.each do |mapping|
current = warden.authenticate(scope: mapping)
return current if current
@@ -113,8 +114,9 @@ def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
- def authenticate_#{mapping}!(opts={})
+ def authenticate_#{mapping}!(opts = {})
opts[:scope] = :#{mapping}
+ opts[:locale] = I18n.locale
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
@@ -149,7 +151,7 @@ def warden
#
# before_action :my_filter, unless: :devise_controller?
def devise_controller?
- is_a?(::DeviseController)
+ is_a?(::DeviseController) || self.class.included_modules.include?(::Devise::Mixins::Base)
end
# Set up a param sanitizer to filter parameters using strong_parameters. See
@@ -252,7 +254,7 @@ def sign_out_and_redirect(resource_or_scope)
# Overwrite Rails' handle unverified request to sign out all scopes,
# clear run strategies and remove cached variables.
def handle_unverified_request
- super # call the default behaviour which resets/nullifies/raises
+ super # call the default behavior which resets/nullifies/raises
request.env["devise.skip_storage"] = true
sign_out_all_scopes(false)
end
diff --git a/lib/devise/controllers/responder.rb b/lib/devise/controllers/responder.rb
new file mode 100644
index 000000000..8e3858a13
--- /dev/null
+++ b/lib/devise/controllers/responder.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Devise
+ module Controllers
+ # Custom Responder to configure default statuses that only apply to Devise,
+ # and allow to integrate more easily with Hotwire/Turbo.
+ class Responder < ActionController::Responder
+ if respond_to?(:error_status=) && respond_to?(:redirect_status=)
+ self.error_status = :ok
+ self.redirect_status = :found
+ else
+ # TODO: remove this support for older Rails versions, which aren't supported by Turbo
+ # and/or responders. It won't allow configuring a custom response, but it allows Devise
+ # to use these methods and defaults across the implementation more easily.
+ def self.error_status
+ :ok
+ end
+
+ def self.redirect_status
+ :found
+ end
+
+ def self.error_status=(*)
+ warn "[DEVISE] Setting the error status on the Devise responder has no effect with this " \
+ "version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
+ end
+
+ def self.redirect_status=(*)
+ warn "[DEVISE] Setting the redirect status on the Devise responder has no effect with this " \
+ "version of `responders`, please make sure you're using a newer version. Check the changelog for more info."
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/controllers/sign_in_out.rb b/lib/devise/controllers/sign_in_out.rb
index e4d378939..7e053d112 100644
--- a/lib/devise/controllers/sign_in_out.rb
+++ b/lib/devise/controllers/sign_in_out.rb
@@ -10,7 +10,7 @@ module SignInOut
# cause exceptions to be thrown from this method; if you simply want to check
# if a scope has already previously been authenticated without running
# authentication hooks, you can directly call `warden.authenticated?(scope: scope)`
- def signed_in?(scope=nil)
+ def signed_in?(scope = nil)
[scope || Devise.mappings.keys].flatten.any? do |_scope|
warden.authenticate?(scope: _scope)
end
@@ -21,7 +21,7 @@ def signed_in?(scope=nil)
# to the set_user method in warden.
# If you are using a custom warden strategy and the timeoutable module, you have to
# set `env["devise.skip_timeout"] = true` in the request to use this method, like we do
- # in the sessions controller: https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb#L7
+ # in the sessions controller: https://github.com/heartcombo/devise/blob/main/app/controllers/devise/sessions_controller.rb#L7
#
# Examples:
#
@@ -37,16 +37,7 @@ def sign_in(resource_or_scope, *args)
expire_data_after_sign_in!
- if options[:bypass]
- ActiveSupport::Deprecation.warn(<<-DEPRECATION.strip_heredoc, caller)
- [Devise] bypass option is deprecated and it will be removed in future version of Devise.
- Please use bypass_sign_in method instead.
- Example:
-
- bypass_sign_in(user)
- DEPRECATION
- warden.session_serializer.store(resource, scope)
- elsif warden.user(scope) == resource && !options.delete(:force)
+ if warden.user(scope) == resource && !options.delete(:force)
# Do nothing. User already signed in and we are not forcing it.
true
else
@@ -77,7 +68,7 @@ def bypass_sign_in(resource, scope: nil)
# sign_out :user # sign_out(scope)
# sign_out @user # sign_out(resource)
#
- def sign_out(resource_or_scope=nil)
+ def sign_out(resource_or_scope = nil)
return sign_out_all_scopes unless resource_or_scope
scope = Devise::Mapping.find_scope!(resource_or_scope)
user = warden.user(scope: scope, run_callbacks: false) # If there is no user
@@ -92,7 +83,7 @@ def sign_out(resource_or_scope=nil)
# Sign out all active users or scopes. This helper is useful for signing out all roles
# in one click. This signs out ALL scopes in warden. Returns true if there was at least one logout
# and false if there was no user logged in on all scopes.
- def sign_out_all_scopes(lock=true)
+ def sign_out_all_scopes(lock = true)
users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) }
warden.logout
@@ -106,10 +97,6 @@ def sign_out_all_scopes(lock=true)
private
def expire_data_after_sign_in!
- # session.keys will return an empty array if the session is not yet loaded.
- # This is a bug in both Rack and Rails.
- # A call to #empty? forces the session to be loaded.
- session.empty?
session.keys.grep(/^devise\./).each { |k| session.delete(k) }
end
diff --git a/lib/devise/controllers/store_location.rb b/lib/devise/controllers/store_location.rb
index d2e431e80..1ae46f734 100644
--- a/lib/devise/controllers/store_location.rb
+++ b/lib/devise/controllers/store_location.rb
@@ -56,7 +56,7 @@ def stored_location_key_for(resource_or_scope)
def extract_path_from_location(location)
uri = parse_uri(location)
- if uri
+ if uri && uri.path
path = remove_domain_from_uri(uri)
path = add_fragment_back_to_path(uri, path)
diff --git a/lib/devise/controllers/url_helpers.rb b/lib/devise/controllers/url_helpers.rb
index 3da36423e..2792a07c8 100644
--- a/lib/devise/controllers/url_helpers.rb
+++ b/lib/devise/controllers/url_helpers.rb
@@ -34,7 +34,7 @@ def self.remove_helpers!
end
end
- def self.generate_helpers!(routes=nil)
+ def self.generate_helpers!(routes = nil)
routes ||= begin
mappings = Devise.mappings.values.map(&:used_helpers).flatten.uniq
Devise::URL_HELPERS.slice(*mappings)
diff --git a/lib/devise/failure_app.rb b/lib/devise/failure_app.rb
index 7f80733c8..70cf6d2f3 100644
--- a/lib/devise/failure_app.rb
+++ b/lib/devise/failure_app.rb
@@ -18,6 +18,11 @@ class FailureApp < ActionController::Metal
delegate :flash, to: :request
+ include AbstractController::Callbacks
+ around_action do |failure_app, action|
+ I18n.with_locale(failure_app.i18n_locale, &action)
+ end
+
def self.call(env)
@respond ||= action(:respond)
@respond.call(env)
@@ -71,8 +76,11 @@ def recall
end
flash.now[:alert] = i18n_message(:invalid) if is_flashing_format?
- # self.response = recall_app(warden_options[:recall]).call(env)
- self.response = recall_app(warden_options[:recall]).call(request.env)
+ self.response = recall_app(warden_options[:recall]).call(request.env).tap { |response|
+ status = response[0].in?(300..399) ? Devise.responder.redirect_status : Devise.responder.error_status
+ # Avoid warnings translating status to code using Rails if available (e.g. `unprocessable_entity` => `unprocessable_content`)
+ response[0] = ActionDispatch::Response.try(:rack_status_code, status) || Rack::Utils.status_code(status)
+ }
end
def redirect
@@ -103,16 +111,27 @@ def i18n_message(default = nil)
options[:scope] = "devise.failure"
options[:default] = [message]
auth_keys = scope_class.authentication_keys
- keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key| scope_class.human_attribute_name(key) }
- options[:authentication_keys] = keys.join(I18n.translate(:"support.array.words_connector"))
+ human_keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key|
+ # TODO: Remove the fallback and just use `downcase_first` once we drop support for Rails 7.0.
+ human_key = scope_class.human_attribute_name(key)
+ human_key.respond_to?(:downcase_first) ? human_key.downcase_first : human_key[0].downcase + human_key[1..]
+ }
+ options[:authentication_keys] = human_keys.join(I18n.t(:"support.array.words_connector"))
options = i18n_options(options)
- I18n.t(:"#{scope}.#{message}", options)
+ I18n.t(:"#{scope}.#{message}", **options).then { |msg|
+ # Ensure that auth keys at the start of the translated string are properly cased.
+ msg.start_with?(human_keys.first) ? msg.upcase_first : msg
+ }
else
message.to_s
end
end
+ def i18n_locale
+ warden_options[:locale]
+ end
+
def redirect_url
if warden_message == :timeout
flash[:timedout] = true if is_flashing_format?
@@ -120,7 +139,7 @@ def redirect_url
path = if request.get?
attempted_path
else
- request.referrer
+ extract_path_from_location(request.referrer)
end
path || scope_url
@@ -137,7 +156,7 @@ def scope_url
opts = {}
# Initialize script_name with nil to prevent infinite loops in
- # authenticated mounted engines in rails 4.2 and 5.0
+ # authenticated mounted engines
opts[:script_name] = nil
route = route(scope)
@@ -149,13 +168,6 @@ def scope_url
if relative_url_root?
opts[:script_name] = relative_url_root
-
- # We need to add the rootpath to `script_name` manually for applications that use a Rails
- # version lower than 5.1. Otherwise, it is going to generate a wrong path for Engines
- # that use Devise. Remove it when the support of Rails 5.0 is droped.
- elsif root_path_defined?(context) && !rails_51_and_up?
- rootpath = context.routes.url_helpers.root_path
- opts[:script_name] = rootpath.chomp('/') if rootpath.length > 1
end
if context.respond_to?(route)
@@ -168,7 +180,7 @@ def scope_url
end
def skip_format?
- %w(html */*).include? request_format.to_s
+ %w(html */* turbo_stream).include? request_format.to_s
end
# Choose whether we should respond in an HTTP authentication fashion,
@@ -271,15 +283,5 @@ def relative_url_root?
end
ActiveSupport.run_load_hooks(:devise_failure_app, self)
-
- private
-
- def root_path_defined?(context)
- defined?(context.routes) && context.routes.url_helpers.respond_to?(:root_path)
- end
-
- def rails_51_and_up?
- Rails.gem_version >= Gem::Version.new("5.1")
- end
end
end
diff --git a/lib/devise/hooks/activatable.rb b/lib/devise/hooks/activatable.rb
index b2eaea199..9feb96307 100644
--- a/lib/devise/hooks/activatable.rb
+++ b/lib/devise/hooks/activatable.rb
@@ -7,6 +7,6 @@
if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
scope = options[:scope]
warden.logout(scope)
- throw :warden, scope: scope, message: record.inactive_message
+ throw :warden, scope: scope, message: record.inactive_message, locale: options.fetch(:locale, I18n.locale)
end
end
diff --git a/lib/devise/hooks/csrf_cleaner.rb b/lib/devise/hooks/csrf_cleaner.rb
index d725fbc42..4a6473955 100644
--- a/lib/devise/hooks/csrf_cleaner.rb
+++ b/lib/devise/hooks/csrf_cleaner.rb
@@ -4,6 +4,11 @@
clean_up_for_winning_strategy = !warden.winning_strategy.respond_to?(:clean_up_csrf?) ||
warden.winning_strategy.clean_up_csrf?
if Devise.clean_up_csrf_token_on_authentication && clean_up_for_winning_strategy
- warden.request.session.try(:delete, :_csrf_token)
+ if warden.request.respond_to?(:reset_csrf_token)
+ # Rails 7.1+
+ warden.request.reset_csrf_token
+ else
+ warden.request.session.try(:delete, :_csrf_token)
+ end
end
end
diff --git a/lib/devise/hooks/lockable.rb b/lib/devise/hooks/lockable.rb
index a73a1752e..b11db1e87 100644
--- a/lib/devise/hooks/lockable.rb
+++ b/lib/devise/hooks/lockable.rb
@@ -3,10 +3,7 @@
# After each sign in, if resource responds to failed_attempts, sets it to 0
# This is only triggered when the user is explicitly set (with set_user)
Warden::Manager.after_set_user except: :fetch do |record, warden, options|
- if record.respond_to?(:failed_attempts) && warden.authenticated?(options[:scope])
- unless record.failed_attempts.to_i.zero?
- record.failed_attempts = 0
- record.save(validate: false)
- end
+ if record.respond_to?(:reset_failed_attempts!) && warden.authenticated?(options[:scope])
+ record.reset_failed_attempts!
end
end
diff --git a/lib/devise/hooks/timeoutable.rb b/lib/devise/hooks/timeoutable.rb
index 41b1fde80..f1e7f6d57 100644
--- a/lib/devise/hooks/timeoutable.rb
+++ b/lib/devise/hooks/timeoutable.rb
@@ -21,11 +21,11 @@
proxy = Devise::Hooks::Proxy.new(warden)
- if record.timedout?(last_request_at) &&
- !env['devise.skip_timeout'] &&
+ if !env['devise.skip_timeout'] &&
+ record.timedout?(last_request_at) &&
!proxy.remember_me_is_active?(record)
Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
- throw :warden, scope: scope, message: :timeout
+ throw :warden, scope: scope, message: :timeout, locale: options.fetch(:locale, I18n.locale)
end
unless env['devise.skip_trackable']
diff --git a/lib/devise/mailers/helpers.rb b/lib/devise/mailers/helpers.rb
index f6997462d..29a491970 100644
--- a/lib/devise/mailers/helpers.rb
+++ b/lib/devise/mailers/helpers.rb
@@ -33,28 +33,22 @@ def headers_for(action, opts)
subject: subject_for(action),
to: resource.email,
from: mailer_sender(devise_mapping),
- reply_to: mailer_reply_to(devise_mapping),
+ reply_to: mailer_sender(devise_mapping),
template_path: template_paths,
template_name: action
- }.merge(opts)
+ }
+ # Give priority to the mailer's default if they exists.
+ headers.delete(:from) if default_params[:from]
+ headers.delete(:reply_to) if default_params[:reply_to]
+
+ headers.merge!(opts)
@email = headers[:to]
headers
end
- def mailer_reply_to(mapping)
- mailer_sender(mapping, :reply_to)
- end
-
- def mailer_from(mapping)
- mailer_sender(mapping, :from)
- end
-
- def mailer_sender(mapping, sender = :from)
- default_sender = default_params[sender]
- if default_sender.present?
- default_sender.respond_to?(:to_proc) ? instance_eval(&default_sender) : default_sender
- elsif Devise.mailer_sender.is_a?(Proc)
+ def mailer_sender(mapping)
+ if Devise.mailer_sender.is_a?(Proc)
Devise.mailer_sender.call(mapping.name)
else
Devise.mailer_sender
diff --git a/lib/devise/mapping.rb b/lib/devise/mapping.rb
index 7692020ff..8b1f94ced 100644
--- a/lib/devise/mapping.rb
+++ b/lib/devise/mapping.rb
@@ -30,7 +30,7 @@ class Mapping #:nodoc:
alias :name :singular
- # Receives an object and find a scope for it. If a scope cannot be found,
+ # Receives an object and finds a scope for it. If a scope cannot be found,
# raises an error. If a symbol is given, it's considered to be the scope.
def self.find_scope!(obj)
obj = obj.devise_scope if obj.respond_to?(:devise_scope)
@@ -46,7 +46,7 @@ def self.find_scope!(obj)
raise "Could not find a valid mapping for #{obj.inspect}"
end
- def self.find_by_path!(path, path_type=:fullpath)
+ def self.find_by_path!(path, path_type = :fullpath)
Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
raise "Could not find a valid mapping for path #{path.inspect}"
end
diff --git a/lib/devise/mixins/base.rb b/lib/devise/mixins/base.rb
new file mode 100644
index 000000000..7eb39dcbc
--- /dev/null
+++ b/lib/devise/mixins/base.rb
@@ -0,0 +1,217 @@
+module Devise
+ module Mixins
+ module Base
+ extend ActiveSupport::Concern
+
+ included do
+ include Devise::Controllers::ScopedViews
+
+ if respond_to?(:helper)
+ helper DeviseHelper
+ end
+
+ if respond_to?(:helper_method)
+ helpers = %w(resource scope_name resource_name signed_in_resource
+ resource_class resource_params devise_mapping)
+ helper_method(*helpers)
+ end
+
+ prepend_before_action :assert_is_devise_resource!
+ respond_to :html if mimes_for_respond_to.empty?
+
+ # Override prefixes to consider the scoped view.
+ # Notice we need to check for the request due to a bug in
+ # Action Controller tests that forces _prefixes to be
+ # loaded before even having a request object.
+ #
+ # This method should be public as it is in ActionPack
+ # itself. Changing its visibility may break other gems.
+ def _prefixes #:nodoc:
+ @_prefixes ||= if self.class.scoped_views? && request && devise_mapping
+ ["#{devise_mapping.scoped_path}/#{controller_name}"] + super
+ else
+ super
+ end
+ end
+
+ protected
+
+ # Gets the actual resource stored in the instance variable
+ def resource
+ instance_variable_get(:"@#{resource_name}")
+ end
+
+ # Proxy to devise map name
+ def resource_name
+ devise_mapping.name
+ end
+ alias :scope_name :resource_name
+
+ # Proxy to devise map class
+ def resource_class
+ devise_mapping.to
+ end
+
+ # Returns a signed in resource from session (if one exists)
+ def signed_in_resource
+ warden.authenticate(scope: resource_name)
+ end
+
+ # Attempt to find the mapped route for devise based on request path
+ def devise_mapping
+ @devise_mapping ||= request.env["devise.mapping"]
+ end
+
+ # Checks whether it's a devise mapped resource or not.
+ def assert_is_devise_resource! #:nodoc:
+ unknown_action! <<-MESSAGE unless devise_mapping
+ Could not find devise mapping for path #{request.fullpath.inspect}.
+ This may happen for two reasons:
+
+ 1) You forgot to wrap your route inside the scope block. For example:
+
+ devise_scope :user do
+ get "/some/route" => "some_devise_controller"
+ end
+
+ 2) You are testing a Devise controller bypassing the router.
+ If so, you can explicitly tell Devise which mapping to use:
+
+ @request.env["devise.mapping"] = Devise.mappings[:user]
+
+ MESSAGE
+ end
+
+ # Returns real navigational formats which are supported by Rails
+ def navigational_formats
+ @navigational_formats ||= Devise.navigational_formats.select { |format| Mime::EXTENSION_LOOKUP[format.to_s] }
+ end
+
+ def unknown_action!(msg)
+ logger.debug "[Devise] #{msg}" if logger
+ raise AbstractController::ActionNotFound, msg
+ end
+
+ # Sets the resource creating an instance variable
+ def resource=(new_resource)
+ instance_variable_set(:"@#{resource_name}", new_resource)
+ end
+
+ # Helper for use in before_actions where no authentication is required.
+ #
+ # Example:
+ # before_action :require_no_authentication, only: :new
+ def require_no_authentication
+ assert_is_devise_resource!
+ return unless is_navigational_format?
+ no_input = devise_mapping.no_input_strategies
+
+ authenticated = if no_input.present?
+ args = no_input.dup.push scope: resource_name
+ warden.authenticate?(*args)
+ else
+ warden.authenticated?(resource_name)
+ end
+
+ if authenticated && resource = warden.user(resource_name)
+ flash[:alert] = I18n.t("devise.failure.already_authenticated")
+ redirect_to after_sign_in_path_for(resource)
+ end
+ end
+
+ # Helper for use after calling send_*_instructions methods on a resource.
+ # If we are in paranoid mode, we always act as if the resource was valid
+ # and instructions were sent.
+ def successfully_sent?(resource)
+ notice = if Devise.paranoid
+ resource.errors.clear
+ :send_paranoid_instructions
+ elsif resource.errors.empty?
+ :send_instructions
+ end
+
+ if notice
+ set_flash_message! :notice, notice
+ true
+ end
+ end
+
+ # Sets the flash message with :key, using I18n. By default you are able
+ # to set up your messages using specific resource scope, and if no message is
+ # found we look to the default scope. Set the "now" options key to a true
+ # value to populate the flash.now hash in lieu of the default flash hash (so
+ # the flash message will be available to the current action instead of the
+ # next action).
+ # Example (i18n locale file):
+ #
+ # en:
+ # devise:
+ # passwords:
+ # #default_scope_messages - only if resource_scope is not found
+ # user:
+ # #resource_scope_messages
+ #
+ # Please refer to README or en.yml locale file to check what messages are
+ # available.
+ def set_flash_message(key, kind, options = {})
+ message = find_message(kind, options)
+ if options[:now]
+ flash.now[key] = message if message.present?
+ else
+ flash[key] = message if message.present?
+ end
+ end
+
+ # Sets flash message if is_flashing_format? equals true
+ def set_flash_message!(key, kind, options = {})
+ if is_flashing_format?
+ set_flash_message(key, kind, options)
+ end
+ end
+
+ # Sets minimum password length to show to user
+ def set_minimum_password_length
+ if devise_mapping.validatable?
+ @minimum_password_length = resource_class.password_length.min
+ end
+ end
+
+ def devise_i18n_options(options)
+ options
+ end
+
+ # Get message for given
+ def find_message(kind, options = {})
+ options[:scope] ||= translation_scope
+ options[:default] = Array(options[:default]).unshift(kind.to_sym)
+ options[:resource_name] = resource_name
+ options = devise_i18n_options(options)
+ I18n.t("#{options[:resource_name]}.#{kind}", **options)
+ end
+
+ # Controllers inheriting DeviseController are advised to override this
+ # method so that other controllers inheriting from them would use
+ # existing translations.
+ def translation_scope
+ "devise.#{controller_name}"
+ end
+
+ def clean_up_passwords(object)
+ object.clean_up_passwords if object.respond_to?(:clean_up_passwords)
+ end
+
+ def respond_with_navigational(*args, &block)
+ respond_with(*args) do |format|
+ format.any(*navigational_formats, &block)
+ end
+ end
+
+ def resource_params
+ params.fetch(resource_name, {})
+ end
+
+ ActiveSupport.run_load_hooks(:devise_controller, self)
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/confirmation.rb b/lib/devise/mixins/confirmation.rb
new file mode 100644
index 000000000..b7959ace7
--- /dev/null
+++ b/lib/devise/mixins/confirmation.rb
@@ -0,0 +1,60 @@
+module Devise
+ module Mixins
+ module Confirmation
+ extend ActiveSupport::Concern
+
+ included do
+ # GET /resource/confirmation/new
+ def new
+ self.resource = resource_class.new
+ end
+
+ # POST /resource/confirmation
+ def create
+ self.resource = resource_class.send_confirmation_instructions(resource_params)
+ yield resource if block_given?
+
+ if successfully_sent?(resource)
+ respond_with({}, location: after_resending_confirmation_instructions_path_for(resource_name))
+ else
+ respond_with(resource)
+ end
+ end
+
+ # GET /resource/confirmation?confirmation_token=abcdef
+ def show
+ self.resource = resource_class.confirm_by_token(params[:confirmation_token])
+ yield resource if block_given?
+
+ if resource.errors.empty?
+ set_flash_message!(:notice, :confirmed)
+ respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
+ else
+ # TODO: use `error_status` when the default changes to `:unprocessable_entity`.
+ respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
+ end
+ end
+
+ protected
+
+ # The path used after resending confirmation instructions.
+ def after_resending_confirmation_instructions_path_for(resource_name)
+ is_navigational_format? ? new_session_path(resource_name) : '/'
+ end
+
+ # The path used after confirmation.
+ def after_confirmation_path_for(resource_name, resource)
+ if signed_in?(resource_name)
+ signed_in_root_path(resource)
+ else
+ new_session_path(resource_name)
+ end
+ end
+
+ def translation_scope
+ 'devise.confirmations'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/omniauth_callback.rb b/lib/devise/mixins/omniauth_callback.rb
new file mode 100644
index 000000000..94b5a375c
--- /dev/null
+++ b/lib/devise/mixins/omniauth_callback.rb
@@ -0,0 +1,42 @@
+module Devise
+ module Mixins
+ module OmniauthCallback
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action { request.env["devise.skip_timeout"] = true }
+
+ def passthru
+ render status: 404, plain: "Not found. Authentication passthru."
+ end
+
+ def failure
+ set_flash_message! :alert, :failure, kind: ::OmniAuth::Utils.camelize(failed_strategy.name), reason: failure_message
+ redirect_to after_omniauth_failure_path_for(resource_name)
+ end
+
+ protected
+
+ def failed_strategy
+ request.respond_to?(:get_header) ? request.get_header("omniauth.error.strategy") : request.env["omniauth.error.strategy"]
+ end
+
+ def failure_message
+ exception = request.respond_to?(:get_header) ? request.get_header("omniauth.error") : request.env["omniauth.error"]
+ error = exception.error_reason if exception.respond_to?(:error_reason)
+ error ||= exception.error if exception.respond_to?(:error)
+ error ||= (request.respond_to?(:get_header) ? request.get_header("omniauth.error.type") : request.env["omniauth.error.type"]).to_s
+ error.to_s.humanize if error
+ end
+
+ def after_omniauth_failure_path_for(scope)
+ new_session_path(scope)
+ end
+
+ def translation_scope
+ 'devise.omniauth_callbacks'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/password.rb b/lib/devise/mixins/password.rb
new file mode 100644
index 000000000..34972a0e3
--- /dev/null
+++ b/lib/devise/mixins/password.rb
@@ -0,0 +1,95 @@
+module Devise
+ module Mixins
+ module Password
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :require_no_authentication
+ # Render the #edit only if coming from a reset password email link
+ append_before_action :assert_reset_token_passed, only: :edit
+
+ # GET /resource/password/new
+ def new
+ self.resource = resource_class.new
+ end
+
+ # POST /resource/password
+ def create
+ self.resource = resource_class.send_reset_password_instructions(resource_params)
+ yield resource if block_given?
+
+ if successfully_sent?(resource)
+ respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
+ else
+ respond_with(resource)
+ end
+ end
+
+ # GET /resource/password/edit?reset_password_token=abcdef
+ def edit
+ self.resource = resource_class.new
+ set_minimum_password_length
+ resource.reset_password_token = params[:reset_password_token]
+ end
+
+ # PUT /resource/password
+ def update
+ self.resource = resource_class.reset_password_by_token(resource_params)
+ yield resource if block_given?
+
+ if resource.errors.empty?
+ resource.unlock_access! if unlockable?(resource)
+ if sign_in_after_reset_password?
+ flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
+ set_flash_message!(:notice, flash_message)
+ resource.after_database_authentication
+ sign_in(resource_name, resource)
+ else
+ set_flash_message!(:notice, :updated_not_active)
+ end
+ respond_with resource, location: after_resetting_password_path_for(resource)
+ else
+ set_minimum_password_length
+ respond_with resource
+ end
+ end
+
+ protected
+
+ def after_resetting_password_path_for(resource)
+ sign_in_after_reset_password? ? after_sign_in_path_for(resource) : new_session_path(resource_name)
+ end
+
+ # The path used after sending reset password instructions
+ def after_sending_reset_password_instructions_path_for(resource_name)
+ new_session_path(resource_name) if is_navigational_format?
+ end
+
+ # Check if a reset_password_token is provided in the request
+ def assert_reset_token_passed
+ if params[:reset_password_token].blank?
+ set_flash_message(:alert, :no_token)
+ redirect_to new_session_path(resource_name)
+ end
+ end
+
+ # Check if the user should be signed in automatically after resetting the password.
+ def sign_in_after_reset_password?
+ resource_class.sign_in_after_reset_password
+ end
+
+ # Check if proper Lockable module methods are present & unlock strategy
+ # allows to unlock resource on password reset
+ def unlockable?(resource)
+ resource.respond_to?(:unlock_access!) &&
+ resource.respond_to?(:unlock_strategy_enabled?) &&
+ resource.unlock_strategy_enabled?(:email)
+ end
+
+ def translation_scope
+ 'devise.passwords'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/registration.rb b/lib/devise/mixins/registration.rb
new file mode 100644
index 000000000..2accbfd56
--- /dev/null
+++ b/lib/devise/mixins/registration.rb
@@ -0,0 +1,175 @@
+module Devise
+ module Mixins
+ module Registration
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :require_no_authentication, only: [:new, :create, :cancel]
+ prepend_before_action :authenticate_scope!, only: [:edit, :update, :destroy]
+ prepend_before_action :set_minimum_password_length, only: [:new, :edit]
+
+ # GET /resource/sign_up
+ def new
+ build_resource
+ yield resource if block_given?
+ respond_with resource
+ end
+
+ # POST /resource
+ def create
+ build_resource(sign_up_params)
+
+ resource.save
+ yield resource if block_given?
+ if resource.persisted?
+ if resource.active_for_authentication?
+ set_flash_message! :notice, :signed_up
+ sign_up(resource_name, resource)
+ respond_with resource, location: after_sign_up_path_for(resource)
+ else
+ set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
+ expire_data_after_sign_in!
+ respond_with resource, location: after_inactive_sign_up_path_for(resource)
+ end
+ else
+ clean_up_passwords resource
+ set_minimum_password_length
+ respond_with resource
+ end
+ end
+
+ # GET /resource/edit
+ def edit
+ render :edit
+ end
+
+ # PUT /resource
+ # We need to use a copy of the resource because we don't want to change
+ # the current user in place.
+ def update
+ self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
+ prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
+
+ resource_updated = update_resource(resource, account_update_params)
+ yield resource if block_given?
+ if resource_updated
+ set_flash_message_for_update(resource, prev_unconfirmed_email)
+ bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?
+
+ respond_with resource, location: after_update_path_for(resource)
+ else
+ clean_up_passwords resource
+ set_minimum_password_length
+ respond_with resource
+ end
+ end
+
+ # DELETE /resource
+ def destroy
+ resource.destroy
+ Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
+ set_flash_message! :notice, :destroyed
+ yield resource if block_given?
+ respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name), status: Devise.responder.redirect_status }
+ end
+
+ # GET /resource/cancel
+ # Forces the session data which is usually expired after sign
+ # in to be expired now. This is useful if the user wants to
+ # cancel oauth signing in/up in the middle of the process,
+ # removing all OAuth session data.
+ def cancel
+ expire_data_after_sign_in!
+ redirect_to new_registration_path(resource_name)
+ end
+
+ protected
+
+ def update_needs_confirmation?(resource, previous)
+ resource.respond_to?(:pending_reconfirmation?) &&
+ resource.pending_reconfirmation? &&
+ previous != resource.unconfirmed_email
+ end
+
+ # By default we want to require a password checks on update.
+ # You can overwrite this method in your own RegistrationsController.
+ def update_resource(resource, params)
+ resource.update_with_password(params)
+ end
+
+ # Build a devise resource passing in the session. Useful to move
+ # temporary session data to the newly created user.
+ def build_resource(hash = {})
+ self.resource = resource_class.new_with_session(hash, session)
+ end
+
+ # Signs in a user on sign up. You can overwrite this method in your own
+ # RegistrationsController.
+ def sign_up(resource_name, resource)
+ sign_in(resource_name, resource)
+ end
+
+ # The path used after sign up. You need to overwrite this method
+ # in your own RegistrationsController.
+ def after_sign_up_path_for(resource)
+ after_sign_in_path_for(resource) if is_navigational_format?
+ end
+
+ # The path used after sign up for inactive accounts. You need to overwrite
+ # this method in your own RegistrationsController.
+ def after_inactive_sign_up_path_for(resource)
+ scope = Devise::Mapping.find_scope!(resource)
+ router_name = Devise.mappings[scope].router_name
+ context = router_name ? send(router_name) : self
+ context.respond_to?(:root_path) ? context.root_path : "/"
+ end
+
+ # The default url to be used after updating a resource. You need to overwrite
+ # this method in your own RegistrationsController.
+ def after_update_path_for(resource)
+ sign_in_after_change_password? ? signed_in_root_path(resource) : new_session_path(resource_name)
+ end
+
+ # Authenticates the current scope and gets the current resource from the session.
+ def authenticate_scope!
+ send(:"authenticate_#{resource_name}!", force: true)
+ self.resource = send(:"current_#{resource_name}")
+ end
+
+ def sign_up_params
+ devise_parameter_sanitizer.sanitize(:sign_up)
+ end
+
+ def account_update_params
+ devise_parameter_sanitizer.sanitize(:account_update)
+ end
+
+ def translation_scope
+ 'devise.registrations'
+ end
+
+ private
+
+ def set_flash_message_for_update(resource, prev_unconfirmed_email)
+ return unless is_flashing_format?
+
+ flash_key = if update_needs_confirmation?(resource, prev_unconfirmed_email)
+ :update_needs_confirmation
+ elsif sign_in_after_change_password?
+ :updated
+ else
+ :updated_but_not_signed_in
+ end
+ set_flash_message :notice, flash_key
+ end
+
+ # Check if the user should be signed in automatically after updating the password.
+ def sign_in_after_change_password?
+ return true if account_update_params[:password].blank?
+
+ resource_class.sign_in_after_change_password
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/session.rb b/lib/devise/mixins/session.rb
new file mode 100644
index 000000000..48a9d9faa
--- /dev/null
+++ b/lib/devise/mixins/session.rb
@@ -0,0 +1,89 @@
+module Devise
+ module Mixins
+ module Session
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :require_no_authentication, only: [:new, :create]
+ prepend_before_action :allow_params_authentication!, only: :create
+ prepend_before_action :verify_signed_out_user, only: :destroy
+ prepend_before_action(only: [:create, :destroy]) { request.env["devise.skip_timeout"] = true }
+
+ # GET /resource/sign_in
+ def new
+ self.resource = resource_class.new(sign_in_params)
+ clean_up_passwords(resource)
+ yield resource if block_given?
+ respond_with(resource, serialize_options(resource))
+ end
+
+ # POST /resource/sign_in
+ def create
+ self.resource = warden.authenticate!(auth_options)
+ set_flash_message!(:notice, :signed_in)
+ sign_in(resource_name, resource)
+ yield resource if block_given?
+ respond_with resource, location: after_sign_in_path_for(resource)
+ end
+
+ # DELETE /resource/sign_out
+ def destroy
+ signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
+ set_flash_message! :notice, :signed_out if signed_out
+ yield if block_given?
+ respond_to_on_destroy(non_navigational_status: :no_content)
+ end
+
+ protected
+
+ def sign_in_params
+ devise_parameter_sanitizer.sanitize(:sign_in)
+ end
+
+ def serialize_options(resource)
+ methods = resource_class.authentication_keys.dup
+ methods = methods.keys if methods.is_a?(Hash)
+ methods << :password if resource.respond_to?(:password)
+ { methods: methods, only: [:password] }
+ end
+
+ def auth_options
+ { scope: resource_name, recall: "#{controller_path}#new", locale: I18n.locale }
+ end
+
+ def translation_scope
+ 'devise.sessions'
+ end
+
+ private
+
+ # Check if there is no signed in user before doing the sign out.
+ #
+ # If there is no signed in user, it will set the flash message and redirect
+ # to the after_sign_out path.
+ def verify_signed_out_user
+ if all_signed_out?
+ set_flash_message! :notice, :already_signed_out
+
+ respond_to_on_destroy(non_navigational_status: :unauthorized)
+ end
+ end
+
+ def all_signed_out?
+ users = Devise.mappings.keys.map { |s| warden.user(scope: s, run_callbacks: false) }
+
+ users.all?(&:blank?)
+ end
+
+ def respond_to_on_destroy(non_navigational_status: :no_content)
+ # We actually need to hardcode this as Rails default responder doesn't
+ # support returning empty response on GET request
+ respond_to do |format|
+ format.all { head non_navigational_status }
+ format.any(*navigational_formats) { redirect_to after_sign_out_path_for(resource_name), status: Devise.responder.redirect_status }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/mixins/unlock.rb b/lib/devise/mixins/unlock.rb
new file mode 100644
index 000000000..7449b406b
--- /dev/null
+++ b/lib/devise/mixins/unlock.rb
@@ -0,0 +1,57 @@
+module Devise
+ module Mixins
+ module Unlock
+ extend ActiveSupport::Concern
+
+ included do
+ prepend_before_action :require_no_authentication
+
+ # GET /resource/unlock/new
+ def new
+ self.resource = resource_class.new
+ end
+
+ # POST /resource/unlock
+ def create
+ self.resource = resource_class.send_unlock_instructions(resource_params)
+ yield resource if block_given?
+
+ if successfully_sent?(resource)
+ respond_with({}, location: after_sending_unlock_instructions_path_for(resource))
+ else
+ respond_with(resource)
+ end
+ end
+
+ # GET /resource/unlock?unlock_token=abcdef
+ def show
+ self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
+ yield resource if block_given?
+
+ if resource.errors.empty?
+ set_flash_message! :notice, :unlocked
+ respond_with_navigational(resource){ redirect_to after_unlock_path_for(resource) }
+ else
+ respond_with_navigational(resource.errors, status: :unprocessable_entity){ render :new }
+ end
+ end
+
+ protected
+
+ # The path used after sending unlock password instructions
+ def after_sending_unlock_instructions_path_for(resource)
+ new_session_path(resource) if is_navigational_format?
+ end
+
+ # The path used after unlocking the resource
+ def after_unlock_path_for(resource)
+ new_session_path(resource) if is_navigational_format?
+ end
+
+ def translation_scope
+ 'devise.unlocks'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/devise/models.rb b/lib/devise/models.rb
index 4d50fa245..fb7dd89b0 100644
--- a/lib/devise/models.rb
+++ b/lib/devise/models.rb
@@ -84,6 +84,7 @@ def devise(*modules)
end
devise_modules_hook! do
+ include Devise::Orm
include Devise::Models::Authenticatable
selected_modules.each do |m|
diff --git a/lib/devise/models/authenticatable.rb b/lib/devise/models/authenticatable.rb
index d4d72e909..df964537e 100644
--- a/lib/devise/models/authenticatable.rb
+++ b/lib/devise/models/authenticatable.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
-require 'active_model/version'
require 'devise/hooks/activatable'
require 'devise/hooks/csrf_cleaner'
@@ -10,7 +9,7 @@ module Models
#
# == Options
#
- # Authenticatable adds the following options to devise_for:
+ # Authenticatable adds the following options to +devise+:
#
# * +authentication_keys+: parameters used for authentication. By default [:email].
#
@@ -56,7 +55,7 @@ module Models
module Authenticatable
extend ActiveSupport::Concern
- BLACKLIST_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
+ UNSAFE_ATTRIBUTES_FOR_SERIALIZATION = [:encrypted_password, :reset_password_token, :reset_password_sent_at,
:remember_created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip,
:last_sign_in_ip, :password_salt, :confirmation_token, :confirmed_at, :confirmation_sent_at,
:remember_token, :unconfirmed_email, :failed_attempts, :unlock_token, :locked_at]
@@ -105,12 +104,12 @@ def authenticatable_salt
# given to :except will simply add names to exempt to Devise internal list.
def serializable_hash(options = nil)
options = options.try(:dup) || {}
- options[:except] = Array(options[:except])
+ options[:except] = Array(options[:except]).dup
if options[:force_except]
options[:except].concat Array(options[:force_except])
else
- options[:except].concat BLACKLIST_FOR_SERIALIZATION
+ options[:except].concat UNSAFE_ATTRIBUTES_FOR_SERIALIZATION
end
super(options)
@@ -153,7 +152,8 @@ def devise_mailer
# # If the record is new or changed then delay the
# # delivery until the after_commit callback otherwise
# # send now because after_commit will not be called.
- # if new_record? || changed?
+ # # For Rails < 6 use `changed?` instead of `saved_changes?`.
+ # if new_record? || saved_changes?
# pending_devise_notifications << [notification, args]
# else
# render_and_send_devise_message(notification, *args)
@@ -183,11 +183,8 @@ def devise_mailer
# # Deliver later with Active Job's `deliver_later`
# if message.respond_to?(:deliver_later)
# message.deliver_later
- # # Remove once we move to Rails 4.2+ only, as `deliver` is deprecated.
- # elsif message.respond_to?(:deliver_now)
- # message.deliver_now
# else
- # message.deliver
+ # message.deliver_now
# end
# end
#
@@ -195,12 +192,7 @@ def devise_mailer
#
def send_devise_notification(notification, *args)
message = devise_mailer.send(notification, self, *args)
- # Remove once we move to Rails 4.2+ only.
- if message.respond_to?(:deliver_now)
- message.deliver_now
- else
- message.deliver
- end
+ message.deliver_now
end
def downcase_keys
@@ -272,17 +264,17 @@ def find_for_authentication(tainted_conditions)
find_first_by_auth_conditions(tainted_conditions)
end
- def find_first_by_auth_conditions(tainted_conditions, opts={})
+ def find_first_by_auth_conditions(tainted_conditions, opts = {})
to_adapter.find_first(devise_parameter_filter.filter(tainted_conditions).merge(opts))
end
# Find or initialize a record setting an error if it can't be found.
- def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
+ def find_or_initialize_with_error_by(attribute, value, error = :invalid) #:nodoc:
find_or_initialize_with_errors([attribute], { attribute => value }, error)
end
# Find or initialize a record with group of attributes based on a list of required attributes.
- def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
+ def find_or_initialize_with_errors(required_attributes, attributes, error = :invalid) #:nodoc:
attributes.try(:permit!)
attributes = attributes.to_h.with_indifferent_access
.slice(*required_attributes)
diff --git a/lib/devise/models/confirmable.rb b/lib/devise/models/confirmable.rb
index dbf6d0ffc..1930086aa 100644
--- a/lib/devise/models/confirmable.rb
+++ b/lib/devise/models/confirmable.rb
@@ -48,7 +48,7 @@ module Confirmable
included do
before_create :generate_confirmation_token, if: :confirmation_required?
after_create :skip_reconfirmation_in_callback!, if: :send_confirmation_notification?
- if defined?(ActiveRecord) && self < ActiveRecord::Base # ActiveRecord
+ if Devise::Orm.active_record?(self) # ActiveRecord
after_commit :send_on_create_confirmation_instructions, on: :create, if: :send_confirmation_notification?
after_commit :send_reconfirmation_instructions, on: :update, if: :reconfirmation_required?
else # Mongoid
@@ -76,7 +76,7 @@ def self.required_fields(klass)
# Confirm a user by setting it's confirmed_at to actual time. If the user
# is already confirmed, add an error to email field. If the user is invalid
# add errors
- def confirm(args={})
+ def confirm(args = {})
pending_any_confirmation do
if confirmation_period_expired?
self.errors.add(:email, :confirmation_period_expired,
@@ -258,44 +258,25 @@ def generate_confirmation_token!
generate_confirmation_token && save(validate: false)
end
- if Devise.activerecord51?
- def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
- @reconfirmation_required = true
- self.unconfirmed_email = self.email
- self.email = self.email_in_database
- self.confirmation_token = nil
- generate_confirmation_token
- end
- else
- def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
- @reconfirmation_required = true
- self.unconfirmed_email = self.email
- self.email = self.email_was
- self.confirmation_token = nil
- generate_confirmation_token
- end
+ def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
+ @reconfirmation_required = true
+ # Force unconfirmed_email to be updated, even if the value hasn't changed, to prevent a
+ # race condition which could allow an attacker to confirm an email they don't own. See #5783.
+ devise_unconfirmed_email_will_change!
+ self.unconfirmed_email = self.email
+ self.email = self.devise_email_in_database
+ self.confirmation_token = nil
+ generate_confirmation_token
end
- if Devise.activerecord51?
- def postpone_email_change?
- postpone = self.class.reconfirmable &&
- will_save_change_to_email? &&
- !@bypass_confirmation_postpone &&
- self.email.present? &&
- (!@skip_reconfirmation_in_callback || !self.email_in_database.nil?)
- @bypass_confirmation_postpone = false
- postpone
- end
- else
- def postpone_email_change?
- postpone = self.class.reconfirmable &&
- email_changed? &&
- !@bypass_confirmation_postpone &&
- self.email.present? &&
- (!@skip_reconfirmation_in_callback || !self.email_was.nil?)
- @bypass_confirmation_postpone = false
- postpone
- end
+ def postpone_email_change?
+ postpone = self.class.reconfirmable &&
+ devise_will_save_change_to_email? &&
+ !@bypass_confirmation_postpone &&
+ self.email.present? &&
+ (!@skip_reconfirmation_in_callback || !self.devise_email_in_database.nil?)
+ @bypass_confirmation_postpone = false
+ postpone
end
def reconfirmation_required?
@@ -334,7 +315,7 @@ module ClassMethods
# confirmation instructions to it. If not, try searching for a user by unconfirmed_email
# field. If no user is found, returns a new user with an email not found error.
# Options must contain the user email
- def send_confirmation_instructions(attributes={})
+ def send_confirmation_instructions(attributes = {})
confirmable = find_by_unconfirmed_email_with_errors(attributes) if reconfirmable
unless confirmable.try(:persisted?)
confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
@@ -348,7 +329,19 @@ def send_confirmation_instructions(attributes={})
# If the user is already confirmed, create an error for the user
# Options must have the confirmation_token
def confirm_by_token(confirmation_token)
+ # When the `confirmation_token` parameter is blank, if there are any users with a blank
+ # `confirmation_token` in the database, the first one would be confirmed here.
+ # The error is being manually added here to ensure no users are confirmed by mistake.
+ # This was done in the model for convenience, since validation errors are automatically
+ # displayed in the view.
+ if confirmation_token.blank?
+ confirmable = new
+ confirmable.errors.add(:confirmation_token, :blank)
+ return confirmable
+ end
+
confirmable = find_first_by_auth_conditions(confirmation_token: confirmation_token)
+
unless confirmable
confirmation_digest = Devise.token_generator.digest(self, :confirmation_token, confirmation_token)
confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_digest)
diff --git a/lib/devise/models/database_authenticatable.rb b/lib/devise/models/database_authenticatable.rb
index ffb3ec606..e16b7d845 100644
--- a/lib/devise/models/database_authenticatable.rb
+++ b/lib/devise/models/database_authenticatable.rb
@@ -7,9 +7,13 @@ module Models
# Authenticatable Module, responsible for hashing the password and
# validating the authenticity of a user while signing in.
#
+ # This module defines a `password=` method. This method will hash the argument
+ # and store it in the `encrypted_password` column, bypassing any pre-existing
+ # `password` column if it exists.
+ #
# == Options
#
- # DatabaseAuthenticatable adds the following options to devise_for:
+ # DatabaseAuthenticatable adds the following options to +devise+:
#
# * +pepper+: a random string used to provide a more secure hash. Use
# `rails secret` to generate new keys.
@@ -38,7 +42,7 @@ module DatabaseAuthenticatable
def initialize(*args, &block)
@skip_email_changed_notification = false
@skip_password_change_notification = false
- super
+ super
end
# Skips sending the email changed notification after_update
@@ -80,16 +84,7 @@ def clean_up_passwords
# users to change relevant information like the e-mail without changing
# their password). In case the password field is rejected, the confirmation
# is also rejected as long as it is also blank.
- def update_with_password(params, *options)
- if options.present?
- ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
- [Devise] The second argument of `DatabaseAuthenticatable#update_with_password`
- (`options`) is deprecated and it will be removed in the next major version.
- It was added to support a feature deprecated in Rails 4, so you can safely remove it
- from your code.
- DEPRECATION
- end
-
+ def update_with_password(params)
current_password = params.delete(:current_password)
if params[:password].blank?
@@ -98,9 +93,9 @@ def update_with_password(params, *options)
end
result = if valid_password?(current_password)
- update(params, *options)
+ update(params)
else
- assign_attributes(params, *options)
+ assign_attributes(params)
valid?
errors.add(:current_password, current_password.blank? ? :blank : :invalid)
false
@@ -117,25 +112,16 @@ def update_with_password(params, *options)
#
# Example:
#
- # def update_without_password(params, *options)
+ # def update_without_password(params)
# params.delete(:email)
# super(params)
# end
#
- def update_without_password(params, *options)
- if options.present?
- ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
- [Devise] The second argument of `DatabaseAuthenticatable#update_without_password`
- (`options`) is deprecated and it will be removed in the next major version.
- It was added to support a feature deprecated in Rails 4, so you can safely remove it
- from your code.
- DEPRECATION
- end
-
+ def update_without_password(params)
params.delete(:password)
params.delete(:password_confirmation)
- result = update(params, *options)
+ result = update(params)
clean_up_passwords
result
end
@@ -173,16 +159,9 @@ def authenticatable_salt
encrypted_password[0,29] if encrypted_password
end
- if Devise.activerecord51?
- # Send notification to user when email changes.
- def send_email_changed_notification
- send_devise_notification(:email_changed, to: email_before_last_save)
- end
- else
- # Send notification to user when email changes.
- def send_email_changed_notification
- send_devise_notification(:email_changed, to: email_was)
- end
+ # Send notification to user when email changes.
+ def send_email_changed_notification
+ send_devise_notification(:email_changed, to: devise_email_before_last_save)
end
# Send notification to user when password changes.
@@ -195,30 +174,18 @@ def send_password_change_notification
# Hashes the password using bcrypt. Custom hash functions should override
# this method to apply their own algorithm.
#
- # See https://github.com/plataformatec/devise-encryptable for examples
+ # See https://github.com/heartcombo/devise-encryptable for examples
# of other hashing engines.
def password_digest(password)
Devise::Encryptor.digest(self.class, password)
end
- if Devise.activerecord51?
- def send_email_changed_notification?
- self.class.send_email_changed_notification && saved_change_to_email? && !@skip_email_changed_notification
- end
- else
- def send_email_changed_notification?
- self.class.send_email_changed_notification && email_changed? && !@skip_email_changed_notification
- end
+ def send_email_changed_notification?
+ self.class.send_email_changed_notification && devise_saved_change_to_email? && !@skip_email_changed_notification
end
- if Devise.activerecord51?
- def send_password_change_notification?
- self.class.send_password_change_notification && saved_change_to_encrypted_password? && !@skip_password_change_notification
- end
- else
- def send_password_change_notification?
- self.class.send_password_change_notification && encrypted_password_changed? && !@skip_password_change_notification
- end
+ def send_password_change_notification?
+ self.class.send_password_change_notification && devise_saved_change_to_encrypted_password? && !@skip_password_change_notification
end
module ClassMethods
diff --git a/lib/devise/models/lockable.rb b/lib/devise/models/lockable.rb
index b8ec4dcaa..6ab0ce747 100644
--- a/lib/devise/models/lockable.rb
+++ b/lib/devise/models/lockable.rb
@@ -18,7 +18,7 @@ module Models
# * +maximum_attempts+: how many attempts should be accepted before blocking the user.
# * +lock_strategy+: lock the user account by :failed_attempts or :none.
# * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
- # * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both.
+ # * +unlock_in+: the time you want to unlock the user after lock happens. Only available when unlock_strategy is :time or :both.
# * +unlock_keys+: the keys you want to use when locking and unlocking an account
#
module Lockable
@@ -57,6 +57,14 @@ def unlock_access!
save(validate: false)
end
+ # Resets failed attempts counter to 0.
+ def reset_failed_attempts!
+ if respond_to?(:failed_attempts) && !failed_attempts.to_i.zero?
+ self.failed_attempts = 0
+ save(validate: false)
+ end
+ end
+
# Verifies whether a user is locked or not.
def access_locked?
!!locked_at && !lock_expired?
@@ -76,7 +84,7 @@ def resend_unlock_instructions
if_access_locked { send_unlock_instructions }
end
- # Overwrites active_for_authentication? from Devise::Models::Activatable for locking purposes
+ # Overwrites active_for_authentication? from Devise::Models::Authenticatable for locking purposes
# by verifying whether a user is active to sign in or not based on locked?
def active_for_authentication?
super && !access_locked?
@@ -110,7 +118,7 @@ def valid_for_authentication?
false
end
end
-
+
def increment_failed_attempts
self.class.increment_counter(:failed_attempts, id)
reload
@@ -168,7 +176,7 @@ module ClassMethods
# unlock instructions to it. If not user is found, returns a new user
# with an email not found error.
# Options must contain the user's unlock keys
- def send_unlock_instructions(attributes={})
+ def send_unlock_instructions(attributes = {})
lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
lockable.resend_unlock_instructions if lockable.persisted?
lockable
diff --git a/lib/devise/models/omniauthable.rb b/lib/devise/models/omniauthable.rb
index c0fe1e547..6f4c8976d 100644
--- a/lib/devise/models/omniauthable.rb
+++ b/lib/devise/models/omniauthable.rb
@@ -8,11 +8,11 @@ module Models
#
# == Options
#
- # Oauthable adds the following options to devise_for:
+ # Oauthable adds the following options to +devise+:
#
# * +omniauth_providers+: Which providers are available to this model. It expects an array:
#
- # devise_for :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
+ # devise :database_authenticatable, :omniauthable, omniauth_providers: [:twitter]
#
module Omniauthable
extend ActiveSupport::Concern
diff --git a/lib/devise/models/recoverable.rb b/lib/devise/models/recoverable.rb
index 75318d503..b17c42aae 100644
--- a/lib/devise/models/recoverable.rb
+++ b/lib/devise/models/recoverable.rb
@@ -7,7 +7,7 @@ module Models
#
# ==Options
#
- # Recoverable adds the following options to devise_for:
+ # Recoverable adds the following options to +devise+:
#
# * +reset_password_keys+: the keys you want to use when recovering the password for an account
# * +reset_password_within+: the time period within which the password must be reset or the token expires.
@@ -99,24 +99,13 @@ def send_reset_password_instructions_notification(token)
send_devise_notification(:reset_password_instructions, token, {})
end
- if Devise.activerecord51?
- def clear_reset_password_token?
- encrypted_password_changed = respond_to?(:will_save_change_to_encrypted_password?) && will_save_change_to_encrypted_password?
- authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
- respond_to?("will_save_change_to_#{attribute}?") && send("will_save_change_to_#{attribute}?")
- end
-
- authentication_keys_changed || encrypted_password_changed
+ def clear_reset_password_token?
+ encrypted_password_changed = devise_respond_to_and_will_save_change_to_attribute?(:encrypted_password)
+ authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
+ devise_respond_to_and_will_save_change_to_attribute?(attribute)
end
- else
- def clear_reset_password_token?
- encrypted_password_changed = respond_to?(:encrypted_password_changed?) && encrypted_password_changed?
- authentication_keys_changed = self.class.authentication_keys.any? do |attribute|
- respond_to?("#{attribute}_changed?") && send("#{attribute}_changed?")
- end
- authentication_keys_changed || encrypted_password_changed
- end
+ authentication_keys_changed || encrypted_password_changed
end
module ClassMethods
@@ -131,7 +120,7 @@ def with_reset_password_token(token)
# password instructions to it. If user is not found, returns a new user
# with an email not found error.
# Attributes must contain the user's email
- def send_reset_password_instructions(attributes={})
+ def send_reset_password_instructions(attributes = {})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
@@ -142,7 +131,7 @@ def send_reset_password_instructions(attributes={})
# try saving the record. If not user is found, returns a new user
# containing an error in reset_password_token attribute.
# Attributes must contain reset_password_token, password and confirmation
- def reset_password_by_token(attributes={})
+ def reset_password_by_token(attributes = {})
original_token = attributes[:reset_password_token]
reset_password_token = Devise.token_generator.digest(self, :reset_password_token, original_token)
diff --git a/lib/devise/models/rememberable.rb b/lib/devise/models/rememberable.rb
index 5a175013c..a66979ad5 100644
--- a/lib/devise/models/rememberable.rb
+++ b/lib/devise/models/rememberable.rb
@@ -15,7 +15,7 @@ module Models
#
# == Options
#
- # Rememberable adds the following options in devise_for:
+ # Rememberable adds the following options to +devise+:
#
# * +remember_for+: the time you want the user will be remembered without
# asking for credentials. After this time the user will be blocked and
@@ -102,7 +102,7 @@ def after_remembered
def remember_me?(token, generated_at)
# TODO: Normalize the JSON type coercion along with the Timeoutable hook
- # in a single place https://github.com/plataformatec/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
+ # in a single place https://github.com/heartcombo/devise/blob/ffe9d6d406e79108cf32a2c6a1d0b3828849c40b/lib/devise/hooks/timeoutable.rb#L14-L18
if generated_at.is_a?(String)
generated_at = time_from_json(generated_at)
end
diff --git a/lib/devise/models/timeoutable.rb b/lib/devise/models/timeoutable.rb
index ee187dbfe..1d3ce2ae9 100644
--- a/lib/devise/models/timeoutable.rb
+++ b/lib/devise/models/timeoutable.rb
@@ -11,7 +11,7 @@ module Models
#
# == Options
#
- # Timeoutable adds the following options to devise_for:
+ # Timeoutable adds the following options to +devise+:
#
# * +timeout_in+: the interval to timeout the user session without activity.
#
diff --git a/lib/devise/models/trackable.rb b/lib/devise/models/trackable.rb
index e450838d3..2328597c3 100644
--- a/lib/devise/models/trackable.rb
+++ b/lib/devise/models/trackable.rb
@@ -33,7 +33,7 @@ def update_tracked_fields(request)
def update_tracked_fields!(request)
# We have to check if the user is already persisted before running
# `save` here because invalid users can be saved if we don't.
- # See https://github.com/plataformatec/devise/issues/4673 for more details.
+ # See https://github.com/heartcombo/devise/issues/4673 for more details.
return if new_record?
update_tracked_fields(request)
diff --git a/lib/devise/models/validatable.rb b/lib/devise/models/validatable.rb
index 40c63de3c..62486cfbe 100644
--- a/lib/devise/models/validatable.rb
+++ b/lib/devise/models/validatable.rb
@@ -9,11 +9,13 @@ module Models
#
# == Options
#
- # Validatable adds the following options to devise_for:
+ # Validatable adds the following options to +devise+:
#
# * +email_regexp+: the regular expression used to validate e-mails;
# * +password_length+: a range expressing password length. Defaults to 6..128.
#
+ # Since +password_length+ is applied in a proc within `validates_length_of` it can be overridden
+ # at runtime.
module Validatable
# All validations used by this module.
VALIDATIONS = [:validates_presence_of, :validates_uniqueness_of, :validates_format_of,
@@ -29,17 +31,12 @@ def self.included(base)
base.class_eval do
validates_presence_of :email, if: :email_required?
- if Devise.activerecord51?
- validates_uniqueness_of :email, allow_blank: true, case_sensitive: true, if: :will_save_change_to_email?
- validates_format_of :email, with: email_regexp, allow_blank: true, if: :will_save_change_to_email?
- else
- validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
- validates_format_of :email, with: email_regexp, allow_blank: true, if: :email_changed?
- end
+ validates_uniqueness_of :email, allow_blank: true, case_sensitive: true, if: :devise_will_save_change_to_email?
+ validates_format_of :email, with: email_regexp, allow_blank: true, if: :devise_will_save_change_to_email?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
- validates_length_of :password, within: password_length, allow_blank: true
+ validates_length_of :password, minimum: proc { password_length.min }, maximum: proc { password_length.max }, allow_blank: true
end
end
@@ -47,7 +44,7 @@ def self.assert_validations_api!(base) #:nodoc:
unavailable_validations = VALIDATIONS.select { |v| !base.respond_to?(v) }
unless unavailable_validations.empty?
- raise "Could not use :validatable module since #{base} does not respond " <<
+ raise "Could not use :validatable module since #{base} does not respond " \
"to the following methods: #{unavailable_validations.to_sentence}."
end
end
diff --git a/lib/devise/omniauth.rb b/lib/devise/omniauth.rb
index 63ea6ca51..f18df3053 100644
--- a/lib/devise/omniauth.rb
+++ b/lib/devise/omniauth.rb
@@ -1,17 +1,14 @@
# frozen_string_literal: true
begin
+ gem "omniauth", ">= 1.0.0"
+
require "omniauth"
- require "omniauth/version"
rescue LoadError
warn "Could not load 'omniauth'. Please ensure you have the omniauth gem >= 1.0.0 installed and listed in your Gemfile."
raise
end
-unless OmniAuth::VERSION =~ /^1\./
- raise "You are using an old OmniAuth version, please ensure you have 1.0.0.pr2 version or later installed."
-end
-
# Clean up the default path_prefix. It will be automatically set by Devise.
OmniAuth.config.path_prefix = nil
diff --git a/lib/devise/orm.rb b/lib/devise/orm.rb
new file mode 100644
index 000000000..f00f397f0
--- /dev/null
+++ b/lib/devise/orm.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module Devise
+ module Orm # :nodoc:
+ def self.active_record?(model)
+ defined?(ActiveRecord) && model < ActiveRecord::Base
+ end
+
+ def self.included(model)
+ if Devise::Orm.active_record?(model)
+ model.include DirtyTrackingActiveRecordMethods
+ else
+ model.include DirtyTrackingMongoidMethods
+ end
+ end
+
+ module DirtyTrackingActiveRecordMethods
+ def devise_email_before_last_save
+ email_before_last_save
+ end
+
+ def devise_email_in_database
+ email_in_database
+ end
+
+ def devise_saved_change_to_email?
+ saved_change_to_email?
+ end
+
+ def devise_saved_change_to_encrypted_password?
+ saved_change_to_encrypted_password?
+ end
+
+ def devise_will_save_change_to_email?
+ will_save_change_to_email?
+ end
+
+ def devise_unconfirmed_email_will_change!
+ unconfirmed_email_will_change!
+ end
+
+ def devise_respond_to_and_will_save_change_to_attribute?(attribute)
+ respond_to?("will_save_change_to_#{attribute}?") && send("will_save_change_to_#{attribute}?")
+ end
+ end
+
+ module DirtyTrackingMongoidMethods
+ def devise_email_before_last_save
+ respond_to?(:email_previously_was) ? email_previously_was : email_was
+ end
+
+ def devise_email_in_database
+ email_was
+ end
+
+ def devise_saved_change_to_email?
+ respond_to?(:email_previously_changed?) ? email_previously_changed? : email_changed?
+ end
+
+ def devise_saved_change_to_encrypted_password?
+ respond_to?(:encrypted_password_previously_changed?) ? encrypted_password_previously_changed? : encrypted_password_changed?
+ end
+
+ def devise_will_save_change_to_email?
+ email_changed?
+ end
+
+ def devise_unconfirmed_email_will_change!
+ # Mongoid's will_change! doesn't force unchanged attributes into updates,
+ # so we override changed_attributes to make it see a difference.
+ unconfirmed_email_will_change!
+ changed_attributes["unconfirmed_email"] = nil
+ end
+
+ def devise_respond_to_and_will_save_change_to_attribute?(attribute)
+ respond_to?("#{attribute}_changed?") && send("#{attribute}_changed?")
+ end
+ end
+ end
+end
diff --git a/lib/devise/parameter_sanitizer.rb b/lib/devise/parameter_sanitizer.rb
index a2d721a1a..6d9523a4f 100644
--- a/lib/devise/parameter_sanitizer.rb
+++ b/lib/devise/parameter_sanitizer.rb
@@ -130,8 +130,7 @@ def permit(action, keys: nil, except: nil, &block)
#
# Returns an +ActiveSupport::HashWithIndifferentAccess+.
def cast_to_hash(params)
- # TODO: Remove the `with_indifferent_access` method call when we only support Rails 5+.
- params && params.to_h.with_indifferent_access
+ params && params.to_h
end
def default_params
diff --git a/lib/devise/rails.rb b/lib/devise/rails.rb
index 5cc5fa6d5..be28938a3 100644
--- a/lib/devise/rails.rb
+++ b/lib/devise/rails.rb
@@ -17,6 +17,17 @@ class Engine < ::Rails::Engine
app.reload_routes! if Devise.reload_routes
end
+ initializer "devise.deprecator" do |app|
+ app.deprecators[:devise] = Devise.deprecator if app.respond_to?(:deprecators)
+ end
+
+ # Generate the Devise's Controllers (per request for development & once for production) for all the specified controller_scopes.
+ config.to_prepare do
+ Devise.controller_scopes.each do |scope|
+ Devise::Controllers::Generator.generate scope
+ end
+ end
+
initializer "devise.url_helpers" do
Devise.include_helpers(Devise::Controllers)
end
@@ -34,7 +45,7 @@ class Engine < ::Rails::Engine
end
initializer "devise.secret_key" do |app|
- Devise.secret_key ||= Devise::SecretKeyFinder.new(app).find
+ Devise.secret_key ||= app.secret_key_base
Devise.token_generator ||=
if secret_key = Devise.secret_key
@@ -43,5 +54,11 @@ class Engine < ::Rails::Engine
)
end
end
+
+ initializer "devise.configure_zeitwerk" do
+ if Rails.autoloaders.zeitwerk_enabled? && !defined?(ActionMailer)
+ Rails.autoloaders.main.ignore("#{root}/app/mailers/devise/mailer.rb")
+ end
+ end
end
end
diff --git a/lib/devise/rails/routes.rb b/lib/devise/rails/routes.rb
index 2d177b252..f43e62fea 100644
--- a/lib/devise/rails/routes.rb
+++ b/lib/devise/rails/routes.rb
@@ -135,10 +135,10 @@ class Mapper
# * failure_app: a rack app which is invoked whenever there is a failure. Strings representing a given
# are also allowed as parameter.
#
- # * sign_out_via: the HTTP method(s) accepted for the :sign_out action (default: :get),
+ # * sign_out_via: the HTTP method(s) accepted for the :sign_out action (default: :delete),
# if you wish to restrict this to accept only :post or :delete requests you should do:
#
- # devise_for :users, sign_out_via: [:post, :delete]
+ # devise_for :users, sign_out_via: [:get, :post]
#
# You need to make sure that your sign_out controls trigger a request with a matching HTTP method.
#
@@ -235,7 +235,6 @@ def devise_for(*resources)
options[:constraints] = (@scope[:constraints] || {}).merge(options[:constraints] || {})
options[:defaults] = (@scope[:defaults] || {}).merge(options[:defaults] || {})
options[:options] = @scope[:options] || {}
- options[:options][:format] = false if options[:format] == false
resources.map!(&:to_sym)
@@ -287,7 +286,7 @@ def devise_for(*resources)
# root to: "admin/dashboard#show", as: :user_root
# end
#
- def authenticate(scope=nil, block=nil)
+ def authenticate(scope = nil, block = nil)
constraints_for(:authenticate!, scope, block) do
yield
end
@@ -311,7 +310,7 @@ def authenticate(scope=nil, block=nil)
#
# root to: 'landing#show'
#
- def authenticated(scope=nil, block=nil)
+ def authenticated(scope = nil, block = nil)
constraints_for(:authenticate?, scope, block) do
yield
end
@@ -328,7 +327,7 @@ def authenticated(scope=nil, block=nil)
#
# root to: 'dashboard#show'
#
- def unauthenticated(scope=nil)
+ def unauthenticated(scope = nil)
constraint = lambda do |request|
not request.env["warden"].authenticate? scope: scope
end
@@ -413,7 +412,7 @@ def devise_registration(mapping, controllers) #:nodoc:
controller: controllers[:registrations]
}
- resource :registration, options do
+ resource :registration, **options do
get :cancel
end
end
@@ -447,7 +446,7 @@ def devise_omniauth_callback(mapping, controllers) #:nodoc:
match "#{path_prefix}/#{provider}",
to: "#{controllers[:omniauth_callbacks]}#passthru",
as: "#{provider}_omniauth_authorize",
- via: [:get, :post]
+ via: OmniAuth.config.allowed_request_methods
match "#{path_prefix}/#{provider}/callback",
to: "#{controllers[:omniauth_callbacks]}##{provider}",
@@ -462,7 +461,7 @@ def with_devise_exclusive_scope(new_path, new_as, options) #:nodoc:
current_scope = @scope.dup
exclusive = { as: new_as, path: new_path, module: nil }
- exclusive.merge!(options.slice(:constraints, :defaults, :options))
+ exclusive.merge!(options.slice(:constraints, :format, :defaults, :options))
if @scope.respond_to? :new
@scope = @scope.new exclusive
@@ -474,7 +473,7 @@ def with_devise_exclusive_scope(new_path, new_as, options) #:nodoc:
@scope = current_scope
end
- def constraints_for(method_to_apply, scope=nil, block=nil)
+ def constraints_for(method_to_apply, scope = nil, block = nil)
constraint = lambda do |request|
request.env['warden'].send(method_to_apply, scope: scope) &&
(block.nil? || block.call(request.env["warden"].user(scope)))
diff --git a/lib/devise/secret_key_finder.rb b/lib/devise/secret_key_finder.rb
deleted file mode 100644
index d07106c44..000000000
--- a/lib/devise/secret_key_finder.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module Devise
- class SecretKeyFinder
- def initialize(application)
- @application = application
- end
-
- def find
- if @application.respond_to?(:credentials) && key_exists?(@application.credentials)
- @application.credentials.secret_key_base
- elsif @application.respond_to?(:secrets) && key_exists?(@application.secrets)
- @application.secrets.secret_key_base
- elsif @application.config.respond_to?(:secret_key_base) && key_exists?(@application.config)
- @application.config.secret_key_base
- elsif @application.respond_to?(:secret_key_base) && key_exists?(@application)
- @application.secret_key_base
- end
- end
-
- private
-
- def key_exists?(object)
- object.secret_key_base.present?
- end
- end
-end
diff --git a/lib/devise/test/controller_helpers.rb b/lib/devise/test/controller_helpers.rb
index cd1a8be8e..d3522a346 100644
--- a/lib/devise/test/controller_helpers.rb
+++ b/lib/devise/test/controller_helpers.rb
@@ -37,6 +37,8 @@ def process(*)
@response
end
+ ruby2_keywords(:process) if respond_to?(:ruby2_keywords, true)
+
# We need to set up the environment variables and the response in the controller.
def setup_controller_for_warden #:nodoc:
@request.env['action_controller.instance'] = @controller
@@ -62,17 +64,7 @@ def warden #:nodoc:
#
# sign_in users(:alice)
# sign_in users(:alice), scope: :admin
- def sign_in(resource, deprecated = nil, scope: nil)
- if deprecated.present?
- scope = resource
- resource = deprecated
-
- ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
- [Devise] sign_in(:#{scope}, resource) on controller tests is deprecated and will be removed from Devise.
- Please use sign_in(resource, scope: :#{scope}) instead.
- DEPRECATION
- end
-
+ def sign_in(resource, scope: nil)
scope ||= Devise::Mapping.find_scope!(resource)
warden.instance_variable_get(:@users).delete(scope)
@@ -139,9 +131,8 @@ def _process_unauthenticated(env, options = {})
status, headers, response = Devise.warden_config[:failure_app].call(env).to_a
@controller.response.headers.merge!(headers)
- @controller.response.content_type = headers["Content-Type"] unless Rails::VERSION::MAJOR >= 5
@controller.status = status
- @controller.response.body = response.body
+ @controller.response_body = response.body
nil # causes process return @response
end
diff --git a/lib/devise/test/integration_helpers.rb b/lib/devise/test/integration_helpers.rb
index 997313896..0c7c910a7 100644
--- a/lib/devise/test/integration_helpers.rb
+++ b/lib/devise/test/integration_helpers.rb
@@ -28,7 +28,7 @@ def self.included(base)
end
end
- # Signs in a specific resource, mimicking a successfull sign in
+ # Signs in a specific resource, mimicking a successful sign in
# operation through +Devise::SessionsController#create+.
#
# * +resource+ - The resource that should be authenticated
diff --git a/lib/devise/test_helpers.rb b/lib/devise/test_helpers.rb
deleted file mode 100644
index c5b52fade..000000000
--- a/lib/devise/test_helpers.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Devise
- module TestHelpers
- def self.included(base)
- base.class_eval do
- ActiveSupport::Deprecation.warn <<-DEPRECATION.strip_heredoc
- [Devise] including `Devise::TestHelpers` is deprecated and will be removed from Devise.
- For controller tests, please include `Devise::Test::ControllerHelpers` instead.
- DEPRECATION
- include Devise::Test::ControllerHelpers
- end
- end
- end
-end
diff --git a/lib/devise/version.rb b/lib/devise/version.rb
index e232ce182..6576edddf 100644
--- a/lib/devise/version.rb
+++ b/lib/devise/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module Devise
- VERSION = "4.6.2".freeze
+ VERSION = "5.0.4".freeze
end
diff --git a/lib/generators/active_record/devise_generator.rb b/lib/generators/active_record/devise_generator.rb
index 7319c47f2..89b2f94dd 100644
--- a/lib/generators/active_record/devise_generator.rb
+++ b/lib/generators/active_record/devise_generator.rb
@@ -5,7 +5,7 @@
module ActiveRecord
module Generators
- class DeviseGenerator < ActiveRecord::Generators::Base
+ class DeviseGenerator < Base
argument :attributes, type: :array, default: [], banner: "field:type field:type"
class_option :primary_key_type, type: :string, desc: "The type for primary key"
@@ -82,23 +82,32 @@ def inet?
postgresql?
end
- def rails5_and_up?
- Rails::VERSION::MAJOR >= 5
+ def rails61_and_up?
+ Rails::VERSION::MAJOR > 6 || (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR >= 1)
end
def postgresql?
- config = ActiveRecord::Base.configurations[Rails.env]
- config && config['adapter'] == 'postgresql'
+ ar_config && ar_config['adapter'] == 'postgresql'
end
- def migration_version
- if rails5_and_up?
- "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
- end
- end
+ def ar_config
+ if ActiveRecord::Base.configurations.respond_to?(:configs_for)
+ if rails61_and_up?
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: "primary").configuration_hash
+ else
+ ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, spec_name: "primary").config
+ end
+ else
+ ActiveRecord::Base.configurations[Rails.env]
+ end
+ end
+
+ def migration_version
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
+ end
def primary_key_type
- primary_key_string if rails5_and_up?
+ primary_key_string
end
def primary_key_string
diff --git a/lib/generators/devise/controllers_generator.rb b/lib/generators/devise/controllers_generator.rb
index caa04f1a5..d96d3d33e 100644
--- a/lib/generators/devise/controllers_generator.rb
+++ b/lib/generators/devise/controllers_generator.rb
@@ -11,7 +11,7 @@ class ControllersGenerator < Rails::Generators::Base
Create inherited Devise controllers in your app/controllers folder.
Use -c to specify which controller you want to overwrite.
- If you do no specify a controller, all controllers will be created.
+ If you do not specify a controller, all controllers will be created.
For example:
rails generate devise:controllers users -c=sessions
diff --git a/lib/generators/devise/devise_generator.rb b/lib/generators/devise/devise_generator.rb
index 79f860ba9..560d0da77 100644
--- a/lib/generators/devise/devise_generator.rb
+++ b/lib/generators/devise/devise_generator.rb
@@ -13,7 +13,7 @@ class DeviseGenerator < Rails::Generators::NamedBase
desc "Generates a model with the given NAME (if one does not exist) with devise " \
"configuration plus a migration file and devise routes."
- hook_for :orm
+ hook_for :orm, required: true
class_option :routes, desc: "Generate routes", type: :boolean, default: true
diff --git a/lib/generators/devise/install_generator.rb b/lib/generators/devise/install_generator.rb
index f81494d6b..78f3303d6 100644
--- a/lib/generators/devise/install_generator.rb
+++ b/lib/generators/devise/install_generator.rb
@@ -11,7 +11,7 @@ class InstallGenerator < Rails::Generators::Base
source_root File.expand_path("../../templates", __FILE__)
desc "Creates a Devise initializer and copy locale files to your application."
- class_option :orm
+ class_option :orm, required: true
def copy_initializer
unless options[:orm]
@@ -37,10 +37,6 @@ def copy_locale
def show_readme
readme "README" if behavior == :invoke
end
-
- def rails_4?
- Rails::VERSION::MAJOR == 4
- end
end
end
end
diff --git a/lib/generators/devise/orm_helpers.rb b/lib/generators/devise/orm_helpers.rb
index 067eb36f5..18c8526a5 100644
--- a/lib/generators/devise/orm_helpers.rb
+++ b/lib/generators/devise/orm_helpers.rb
@@ -25,11 +25,7 @@ def migration_exists?(table_name)
end
def migration_path
- if Rails.version >= '5.0.3'
- db_migrate_path
- else
- @migration_path ||= File.join("db", "migrate")
- end
+ db_migrate_path
end
def model_path
diff --git a/lib/generators/devise/views_generator.rb b/lib/generators/devise/views_generator.rb
index 29bf8feda..bc271743c 100644
--- a/lib/generators/devise/views_generator.rb
+++ b/lib/generators/devise/views_generator.rb
@@ -42,7 +42,7 @@ def copy_views
def view_directory(name, _target_path = nil)
directory name.to_s, _target_path || "#{target_path}/#{name}" do |content|
if scope
- content.gsub "devise/shared/links", "#{plural_scope}/shared/links"
+ content.gsub("devise/shared", "#{plural_scope}/shared")
else
content
end
diff --git a/lib/generators/templates/README b/lib/generators/templates/README
index b76482806..c89920b0c 100644
--- a/lib/generators/templates/README
+++ b/lib/generators/templates/README
@@ -1,6 +1,6 @@
===============================================================================
-Some setup you must do manually if you haven't yet:
+Depending on your application's configuration some manual setup may be required:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
@@ -10,10 +10,14 @@ Some setup you must do manually if you haven't yet:
In production, :host should be set to the actual host of your application.
+ * Required for all applications. *
+
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
+
+ * Not required for API-only Applications *
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
@@ -21,8 +25,12 @@ Some setup you must do manually if you haven't yet:
<%= notice %>
<%= alert %>
+ * Not required for API-only Applications *
+
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
+
+ * Not required *
===============================================================================
diff --git a/lib/generators/templates/controllers/omniauth_callbacks_controller.rb b/lib/generators/templates/controllers/omniauth_callbacks_controller.rb
index 5f95be2d2..29556cf6e 100644
--- a/lib/generators/templates/controllers/omniauth_callbacks_controller.rb
+++ b/lib/generators/templates/controllers/omniauth_callbacks_controller.rb
@@ -9,7 +9,7 @@ class <%= @scope_prefix %>OmniauthCallbacksController < Devise::OmniauthCallback
# end
# More info at:
- # https://github.com/plataformatec/devise#omniauth
+ # https://github.com/heartcombo/devise#omniauth
# GET|POST /resource/auth/twitter
# def passthru
diff --git a/lib/generators/templates/devise.rb b/lib/generators/templates/devise.rb
old mode 100755
new mode 100644
index 5bad7f9af..b36f281f2
--- a/lib/generators/templates/devise.rb
+++ b/lib/generators/templates/devise.rb
@@ -1,5 +1,11 @@
# frozen_string_literal: true
+# Assuming you have not yet modified this file, each configuration option below
+# is set to its default value. Note that some are commented out while others
+# are not: uncommented lines are intended to protect your configuration from
+# breaking changes in upgrades (i.e., in the event that future versions of
+# Devise change the default values for those options).
+#
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
@@ -68,7 +74,10 @@
# Tell if authentication through HTTP Auth is enabled. False by default.
# It can be set to an array that will enable http authentication only for the
# given strategies, for example, `config.http_authenticatable = [:database]` will
- # enable it only for database authentication. The supported strategies are:
+ # enable it only for database authentication.
+ # For API-only applications to support authentication "out-of-the-box", you will likely want to
+ # enable this with :database unless you are using a custom strategy.
+ # The supported strategies are:
# :database = Support basic authentication with authentication key + password
# config.http_authenticatable = false
@@ -103,15 +112,18 @@
# config.reload_routes = true
# ==> Configuration for :database_authenticatable
- # For bcrypt, this is the cost for hashing the password and defaults to 11. If
+ # For bcrypt, this is the cost for hashing the password and defaults to 12. If
# using other algorithms, it sets how many times you want the password to be hashed.
+ # The number of stretches used for generating the hashed password are stored
+ # with the hashed password. This allows you to change the stretches without
+ # invalidating existing passwords.
#
# Limiting the stretches to just one in testing will increase the performance of
# your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
# a value less than 10 in other environments. Note that, for bcrypt (the default
# algorithm), the cost increases exponentially with the number of stretches (e.g.
# a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation).
- config.stretches = Rails.env.test? ? 1 : 11
+ config.stretches = Rails.env.test? ? 1 : 12
# Set up a pepper to generate the hashed password.
# config.pepper = '<%= SecureRandom.hex(64) %>'
@@ -145,6 +157,9 @@
# initial account confirmation) to be applied. Requires additional unconfirmed_email
# db field (see migrations). Until confirmed, new email is stored in
# unconfirmed_email column, and copied to email column on successful confirmation.
+ # Also, when used in conjunction with `send_email_changed_notification`,
+ # the notification is sent to the original email when the change is requested,
+ # not when the unconfirmed email is confirmed.
config.reconfirmable = true
# Defines which key will be used when confirming an account
@@ -244,14 +259,14 @@
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
- # :html, should redirect to the sign in page when the user does not have
+ # :html should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
#
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists.
#
# The "*/*" below is required to match Internet Explorer requests.
- # config.navigational_formats = ['*/*', :html]
+ # config.navigational_formats = ['*/*', :html, :turbo_stream]
# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = :delete
@@ -265,9 +280,9 @@
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
- # config.warden do |manager|
- # manager.intercept_401 = false
- # manager.default_strategies(scope: :user).unshift :some_external_strategy
+ # config.warden do |warden_config|
+ # warden_config.intercept_401 = false
+ # warden_config.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Mountable engine configurations
@@ -284,12 +299,14 @@
# so you need to do it manually. For the users scope, it would be:
# config.omniauth_path_prefix = '/my_engine/users/auth'
- # ==> Turbolinks configuration
- # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly:
- #
- # ActiveSupport.on_load(:devise_failure_app) do
- # include Turbolinks::Controller
- # end
+ # ==> Hotwire/Turbo configuration
+ # When using Devise with Hotwire/Turbo, the http status for error responses
+ # and some redirects must match the following. The default in Devise for existing
+ # apps is `200 OK` and `302 Found` respectively, but new apps are generated with
+ # these new defaults that match Hotwire/Turbo behavior.
+ # Note: These might become the new default in future versions of Devise.
+ config.responder.error_status = <%= Rack::Utils::SYMBOL_TO_STATUS_CODE.key(422).inspect %>
+ config.responder.redirect_status = :see_other
# ==> Configuration for :registerable
diff --git a/lib/generators/templates/simple_form_for/passwords/new.html.erb b/lib/generators/templates/simple_form_for/passwords/new.html.erb
index 01ce0b8b9..9a2c7d39f 100644
--- a/lib/generators/templates/simple_form_for/passwords/new.html.erb
+++ b/lib/generators/templates/simple_form_for/passwords/new.html.erb
@@ -11,7 +11,7 @@
- <%= f.button :submit, "Send me reset password instructions" %>
+ <%= f.button :submit, "Send me password reset instructions" %>
<% end %>
diff --git a/lib/generators/templates/simple_form_for/registrations/edit.html.erb b/lib/generators/templates/simple_form_for/registrations/edit.html.erb
index dfb7eb94e..b3c0089ad 100644
--- a/lib/generators/templates/simple_form_for/registrations/edit.html.erb
+++ b/lib/generators/templates/simple_form_for/registrations/edit.html.erb
@@ -30,6 +30,6 @@
Cancel my account
-
Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
+
Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>
<%= link_to "Back", :back %>
diff --git a/test/controllers/custom_strategy_test.rb b/test/controllers/custom_strategy_test.rb
index c39ac3e61..1c9681264 100644
--- a/test/controllers/custom_strategy_test.rb
+++ b/test/controllers/custom_strategy_test.rb
@@ -3,7 +3,7 @@
require 'test_helper'
require 'ostruct'
require 'warden/strategies/base'
-require 'devise/test_helpers'
+require 'devise/test/controller_helpers'
class CustomStrategyController < ActionController::Base
def new
@@ -42,9 +42,7 @@ class CustomStrategyTest < Devise::ControllerTestCase
test "custom strategy can return its own status code" do
ret = get :new
- # check the returned rack array
- # assert ret.is_a?(Array)
- # assert_equal 400, ret.first
+ # check the returned response
assert ret.is_a?(ActionDispatch::TestResponse)
# check the saved response as well. This is purely so that the response is available to the testing framework
@@ -55,12 +53,10 @@ class CustomStrategyTest < Devise::ControllerTestCase
test "custom strategy can return custom headers" do
ret = get :new
- # check the returned rack array
- # assert ret.is_a?(Array)
- # assert_equal ret.third['X-FOO'], 'BAR'
+ # check the returned response
assert ret.is_a?(ActionDispatch::TestResponse)
# check the saved response headers as well.
- assert_equal response.headers['X-FOO'], 'BAR'
+ assert_equal 'BAR', response.headers['X-FOO']
end
end
diff --git a/test/controllers/helper_methods_test.rb b/test/controllers/helper_methods_test.rb
index 5e20477ca..998cab6fb 100644
--- a/test/controllers/helper_methods_test.rb
+++ b/test/controllers/helper_methods_test.rb
@@ -14,8 +14,8 @@ class HelperMethodsTest < Devise::ControllerTestCase
end
test 'does not respond_to helper or helper_method' do
- refute_respond_to @controller.class, :helper
- refute_respond_to @controller.class, :helper_method
+ assert_not_respond_to @controller.class, :helper
+ assert_not_respond_to @controller.class, :helper_method
end
test 'defines methods like current_user' do
diff --git a/test/controllers/helpers_test.rb b/test/controllers/helpers_test.rb
index b48502643..536b282c2 100644
--- a/test/controllers/helpers_test.rb
+++ b/test/controllers/helpers_test.rb
@@ -64,30 +64,30 @@ def setup
end
test 'proxy authenticate_user! to authenticate with user scope' do
- @mock_warden.expects(:authenticate!).with(scope: :user)
+ @mock_warden.expects(:authenticate!).with({ scope: :user, locale: :en })
@controller.authenticate_user!
end
test 'proxy authenticate_user! options to authenticate with user scope' do
- @mock_warden.expects(:authenticate!).with(scope: :user, recall: "foo")
+ @mock_warden.expects(:authenticate!).with({ scope: :user, recall: "foo", locale: :en })
@controller.authenticate_user!(recall: "foo")
end
test 'proxy authenticate_admin! to authenticate with admin scope' do
- @mock_warden.expects(:authenticate!).with(scope: :admin)
+ @mock_warden.expects(:authenticate!).with({ scope: :admin, locale: :en })
@controller.authenticate_admin!
end
test 'proxy authenticate_[group]! to authenticate!? with each scope' do
[:user, :admin].each do |scope|
- @mock_warden.expects(:authenticate!).with(scope: scope)
+ @mock_warden.expects(:authenticate!).with({ scope: scope, locale: :en })
@mock_warden.expects(:authenticate?).with(scope: scope).returns(false)
end
@controller.authenticate_commenter!
end
test 'proxy authenticate_publisher_account! to authenticate with namespaced publisher account scope' do
- @mock_warden.expects(:authenticate!).with(scope: :publisher_account)
+ @mock_warden.expects(:authenticate!).with({ scope: :publisher_account, locale: :en })
@controller.authenticate_publisher_account!
end
@@ -98,7 +98,7 @@ def setup
test 'proxy admin_signed_in? to authenticatewith admin scope' do
@mock_warden.expects(:authenticate).with(scope: :admin)
- refute @controller.admin_signed_in?
+ assert_not @controller.admin_signed_in?
end
test 'proxy publisher_account_signed_in? to authenticate with namespaced publisher account scope' do
@@ -127,14 +127,14 @@ def setup
test 'sign in proxy to set_user on warden' do
user = User.new
@mock_warden.expects(:user).returns(nil)
- @mock_warden.expects(:set_user).with(user, scope: :user).returns(true)
+ @mock_warden.expects(:set_user).with(user, { scope: :user }).returns(true)
@controller.sign_in(:user, user)
end
test 'sign in accepts a resource as argument' do
user = User.new
@mock_warden.expects(:user).returns(nil)
- @mock_warden.expects(:set_user).with(user, scope: :user).returns(true)
+ @mock_warden.expects(:set_user).with(user, { scope: :user }).returns(true)
@controller.sign_in(user)
end
@@ -148,7 +148,7 @@ def setup
test 'sign in again when the user is already in only if force is given' do
user = User.new
@mock_warden.expects(:user).returns(user)
- @mock_warden.expects(:set_user).with(user, scope: :user).returns(true)
+ @mock_warden.expects(:set_user).with(user, { scope: :user }).returns(true)
@controller.sign_in(user, force: true)
end
@@ -224,10 +224,20 @@ def setup
assert_equal "/foo.bar", @controller.stored_location_for(:user)
end
- test 'store bad location for stores a location to redirect back to' do
- assert_nil @controller.stored_location_for(:user)
- @controller.store_location_for(:user, "/foo.bar\">Carry")
- assert_nil @controller.stored_location_for(:user)
+ test 'store bad location for does not store a location to redirect back to' do
+ bad_locations = [
+ "/foo.bar\">Carry", # unparseable
+ "http://[invalid", # unparseable
+ "javascript:alert(1)", # opaque URI, no path
+ "mailto:foo@example.com", # opaque URI, no path
+ nil,
+ ]
+
+ bad_locations.each do |location|
+ @controller.store_location_for(:user, location)
+ assert_nil @controller.stored_location_for(:user),
+ "expected bad location #{location.inspect} to not be stored"
+ end
end
test 'store location for accepts a resource as argument' do
@@ -269,7 +279,7 @@ def setup
user = User.new
@controller.session[:user_return_to] = "/foo.bar"
@mock_warden.expects(:user).with(:user).returns(nil)
- @mock_warden.expects(:set_user).with(user, scope: :user).returns(true)
+ @mock_warden.expects(:set_user).with(user, { scope: :user }).returns(true)
@controller.expects(:redirect_to).with("/foo.bar")
@controller.sign_in_and_redirect(user)
end
@@ -277,7 +287,7 @@ def setup
test 'sign in and redirect uses the configured after sign in path' do
admin = Admin.new
@mock_warden.expects(:user).with(:admin).returns(nil)
- @mock_warden.expects(:set_user).with(admin, scope: :admin).returns(true)
+ @mock_warden.expects(:set_user).with(admin, { scope: :admin }).returns(true)
@controller.expects(:redirect_to).with(admin_root_path)
@controller.sign_in_and_redirect(admin)
end
@@ -319,10 +329,10 @@ def setup
test 'is_flashing_format? is guarded against flash (middleware) not being loaded' do
@controller.request.expects(:respond_to?).with(:flash).returns(false)
- refute @controller.is_flashing_format?
+ assert_not @controller.is_flashing_format?
end
test 'is not a devise controller' do
- refute @controller.devise_controller?
+ assert_not @controller.devise_controller?
end
end
diff --git a/test/controllers/internal_helpers_test.rb b/test/controllers/internal_helpers_test.rb
index 7710e0d7b..124c8df06 100644
--- a/test/controllers/internal_helpers_test.rb
+++ b/test/controllers/internal_helpers_test.rb
@@ -51,11 +51,11 @@ def setup
end
test 'resources methods are not controller actions' do
- assert @controller.class.action_methods.delete_if { |m| m.include? 'commenter' }.empty?
+ assert_empty @controller.class.action_methods.delete_if { |m| m.include? 'commenter' }
end
test 'require no authentication tests current mapping' do
- @mock_warden.expects(:authenticate?).with(:rememberable, scope: :user).returns(true)
+ @mock_warden.expects(:authenticate?).with(:rememberable, { scope: :user }).returns(true)
@mock_warden.expects(:user).with(:user).returns(User.new)
@controller.expects(:redirect_to).with(root_path)
@controller.send :require_no_authentication
@@ -71,7 +71,7 @@ def setup
end
test 'require no authentication sets a flash message' do
- @mock_warden.expects(:authenticate?).with(:rememberable, scope: :user).returns(true)
+ @mock_warden.expects(:authenticate?).with(:rememberable, { scope: :user }).returns(true)
@mock_warden.expects(:user).with(:user).returns(User.new)
@controller.expects(:redirect_to).with(root_path)
@controller.send :require_no_authentication
@@ -121,7 +121,7 @@ def setup
MyController.send(:public, :navigational_formats)
swap Devise, navigational_formats: ['*/*', :html] do
- refute @controller.navigational_formats.include?("*/*")
+ assert_not @controller.navigational_formats.include?("*/*")
end
MyController.send(:protected, :navigational_formats)
diff --git a/test/controllers/load_hooks_controller_test.rb b/test/controllers/load_hooks_controller_test.rb
index 6387b309a..63720c2e2 100644
--- a/test/controllers/load_hooks_controller_test.rb
+++ b/test/controllers/load_hooks_controller_test.rb
@@ -16,6 +16,6 @@ class LoadHooksControllerTest < Devise::ControllerTestCase
end
test 'load hook called when controller is loaded' do
- assert DeviseController.instance_methods.include? :defined_by_load_hook
+ assert_includes DeviseController.instance_methods, :defined_by_load_hook
end
-end
\ No newline at end of file
+end
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
index e88cf7e90..9c970ab5d 100644
--- a/test/controllers/sessions_controller_test.rb
+++ b/test/controllers/sessions_controller_test.rb
@@ -74,7 +74,7 @@ class SessionsControllerTest < Devise::ControllerTestCase
assert_template "devise/sessions/new"
end
- test "#destroy doesn't set the flash if the requested format is not navigational" do
+ test "#destroy doesn't set the flash and returns 204 status if the requested format is not navigational" do
request.env["devise.mapping"] = Devise.mappings[:user]
user = create_user
user.confirm
@@ -88,21 +88,15 @@ class SessionsControllerTest < Devise::ControllerTestCase
assert_equal 204, @response.status
end
- if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:mass_assignment_sanitizer)
- test "#new doesn't raise mass-assignment exception even if sign-in key is attr_protected" do
- request.env["devise.mapping"] = Devise.mappings[:user]
-
- ActiveRecord::Base.mass_assignment_sanitizer = :strict
- User.class_eval { attr_protected :email }
+ test "#destroy returns 401 status if user is not signed in and the requested format is not navigational" do
+ request.env["devise.mapping"] = Devise.mappings[:user]
+ delete :destroy, format: 'json'
+ assert_equal 401, @response.status
+ end
- begin
- assert_nothing_raised do
- get :new, user: { email: "allez viens!" }
- end
- ensure
- ActiveRecord::Base.mass_assignment_sanitizer = :logger
- User.class_eval { attr_accessible :email }
- end
- end
+ test "#destroy returns 302 status if user is not signed in and the requested format is navigational" do
+ request.env["devise.mapping"] = Devise.mappings[:user]
+ delete :destroy
+ assert_equal 302, @response.status
end
end
diff --git a/test/controllers/url_helpers_test.rb b/test/controllers/url_helpers_test.rb
index d5328a367..e4b6a3093 100644
--- a/test/controllers/url_helpers_test.rb
+++ b/test/controllers/url_helpers_test.rb
@@ -5,7 +5,7 @@
class RoutesTest < Devise::ControllerTestCase
tests ApplicationController
- def assert_path_and_url(name, prepend_path=nil)
+ def assert_path_and_url(name, prepend_path = nil)
@request.path = '/users/session'
prepend_path = "#{prepend_path}_" if prepend_path
diff --git a/test/devise_test.rb b/test/devise_test.rb
old mode 100755
new mode 100644
index 088527feb..a46be0d52
--- a/test/devise_test.rb
+++ b/test/devise_test.rb
@@ -71,8 +71,8 @@ class DeviseTest < ActiveSupport::TestCase
test 'add new module using the helper method' do
Devise.add_module(:coconut)
assert_equal 1, Devise::ALL.select { |v| v == :coconut }.size
- refute Devise::STRATEGIES.include?(:coconut)
- refute defined?(Devise::Models::Coconut)
+ assert_not Devise::STRATEGIES.include?(:coconut)
+ assert_not defined?(Devise::Models::Coconut)
Devise::ALL.delete(:coconut)
Devise.add_module(:banana, strategy: :fruits)
@@ -86,13 +86,18 @@ class DeviseTest < ActiveSupport::TestCase
Devise::CONTROLLERS.delete(:kivi)
end
- test 'should complain when comparing empty or different sized passes' do
+ test 'Devise.secure_compare fails when comparing different strings or nil' do
[nil, ""].each do |empty|
- refute Devise.secure_compare(empty, "something")
- refute Devise.secure_compare("something", empty)
- refute Devise.secure_compare(empty, empty)
+ assert_not Devise.secure_compare(empty, "something")
+ assert_not Devise.secure_compare("something", empty)
end
- refute Devise.secure_compare("size_1", "size_four")
+ assert_not Devise.secure_compare(nil, nil)
+ assert_not Devise.secure_compare("size_1", "size_four")
+ end
+
+ test 'Devise.secure_compare passes when strings are the same, even two empty strings' do
+ assert Devise.secure_compare("", "")
+ assert Devise.secure_compare("something", "something")
end
test 'Devise.email_regexp should match valid email addresses' do
diff --git a/test/failure_app_test.rb b/test/failure_app_test.rb
index af622fff2..c9e4a56ce 100644
--- a/test/failure_app_test.rb
+++ b/test/failure_app_test.rb
@@ -73,13 +73,12 @@ def self.context(name, &block)
instance_eval(&block)
end
- def call_failure(env_params={})
+ def call_failure(env_params = {})
env = {
'REQUEST_URI' => 'http://test.host/',
'HTTP_HOST' => 'test.host',
'REQUEST_METHOD' => 'GET',
'warden.options' => { scope: :user },
- 'rack.session' => {},
'action_dispatch.request.formats' => Array(env_params.delete('formats') || Mime[:html]),
'rack.input' => "",
'warden' => OpenStruct.new(message: nil)
@@ -185,22 +184,44 @@ def call_failure(env_params={})
test 'uses the proxy failure message as symbol' do
call_failure('warden' => OpenStruct.new(message: :invalid))
- assert_equal 'Invalid Email or password.', @request.flash[:alert]
+ assert_equal 'Invalid email or password.', @request.flash[:alert]
assert_equal 'http://test.host/users/sign_in', @response.second["Location"]
end
test 'supports authentication_keys as a Hash for the flash message' do
swap Devise, authentication_keys: { email: true, login: true } do
call_failure('warden' => OpenStruct.new(message: :invalid))
- assert_equal 'Invalid Email, Login or password.', @request.flash[:alert]
+ assert_equal 'Invalid email, login or password.', @request.flash[:alert]
end
end
+ test 'downcases authentication_keys for the flash message' do
+ call_failure('warden' => OpenStruct.new(message: :invalid))
+ assert_equal 'Invalid email or password.', @request.flash[:alert]
+ end
+
+ test 'humanizes the flash message' do
+ call_failure('warden' => OpenStruct.new(message: :invalid))
+ assert_equal @request.flash[:alert], @request.flash[:alert].humanize
+ end
+
test 'uses custom i18n options' do
call_failure('warden' => OpenStruct.new(message: :does_not_exist), app: FailureWithI18nOptions)
assert_equal 'User Steve does not exist', @request.flash[:alert]
end
+ test 'respects the i18n locale passed via warden options when redirecting' do
+ call_failure('warden' => OpenStruct.new(message: :invalid), 'warden.options' => { locale: :"pt-BR" })
+
+ assert_equal 'Email ou senha inválidos.', @request.flash[:alert]
+ assert_equal 'http://test.host/users/sign_in', @response.second["Location"]
+
+ call_failure('warden' => OpenStruct.new(message: :invalid), 'warden.options' => { locale: :de })
+
+ assert_equal 'E-Mail oder Passwort ist ungültig.', @request.flash[:alert]
+ assert_equal 'http://test.host/users/sign_in', @response.second["Location"]
+ end
+
test 'uses the proxy failure message as string' do
call_failure('warden' => OpenStruct.new(message: 'Hello world'))
assert_equal 'Hello world', @request.flash[:alert]
@@ -214,14 +235,18 @@ def call_failure(env_params={})
test 'set up a default message' do
call_failure
- assert_match(/You are being/, @response.last.body)
- assert_match(/redirected/, @response.last.body)
- assert_match(/users\/sign_in/, @response.last.body)
+ if Devise::Test.rails71_and_up?
+ assert_empty @response.last.body
+ else
+ assert_match(/You are being/, @response.last.body)
+ assert_match(/redirected/, @response.last.body)
+ assert_match(/users\/sign_in/, @response.last.body)
+ end
end
test 'works for any navigational format' do
- swap Devise, navigational_formats: [:xml] do
- call_failure('formats' => Mime[:xml])
+ swap Devise, navigational_formats: [:json] do
+ call_failure('formats' => Mime[:json])
assert_equal 302, @response.first
end
end
@@ -236,7 +261,7 @@ def call_failure(env_params={})
context 'For HTTP request' do
test 'return 401 status' do
- call_failure('formats' => Mime[:xml])
+ call_failure('formats' => Mime[:json])
assert_equal 401, @response.first
end
@@ -258,13 +283,13 @@ def call_failure(env_params={})
end
test 'return WWW-authenticate headers if model allows' do
- call_failure('formats' => Mime[:xml])
+ call_failure('formats' => Mime[:json])
assert_equal 'Basic realm="Application"', @response.second["WWW-Authenticate"]
end
test 'does not return WWW-authenticate headers if model does not allow' do
swap Devise, http_authenticatable: false do
- call_failure('formats' => Mime[:xml])
+ call_failure('formats' => Mime[:json])
assert_nil @response.second["WWW-Authenticate"]
end
end
@@ -278,7 +303,13 @@ def call_failure(env_params={})
test 'uses the failure message as response body' do
call_failure('formats' => Mime[:xml], 'warden' => OpenStruct.new(message: :invalid))
- assert_match '
Invalid Email or password.', @response.third.body
+ assert_match '
Invalid email or password.', @response.third.body
+ end
+
+ test 'respects the i18n locale passed via warden options when responding to HTTP request' do
+ call_failure('formats' => Mime[:json], 'warden' => OpenStruct.new(message: :invalid), 'warden.options' => { locale: :"pt-BR" })
+
+ assert_equal %({"error":"Email ou senha inválidos."}), @response.third.body
end
context 'on ajax call' do
@@ -326,8 +357,8 @@ def call_failure(env_params={})
"warden" => stub_everything
}
call_failure(env)
- assert @response.third.body.include?('
Log in
')
- assert @response.third.body.include?('Invalid Email or password.')
+ assert_includes @response.third.body, '
Log in
'
+ assert_includes @response.third.body, 'Invalid email or password.'
end
test 'calls the original controller if not confirmed email' do
@@ -337,8 +368,8 @@ def call_failure(env_params={})
"warden" => stub_everything
}
call_failure(env)
- assert @response.third.body.include?('
Log in
')
- assert @response.third.body.include?('You have to confirm your email address before continuing.')
+ assert_includes @response.third.body, '
Log in
'
+ assert_includes @response.third.body, 'You have to confirm your email address before continuing.'
end
test 'calls the original controller if inactive account' do
@@ -348,8 +379,8 @@ def call_failure(env_params={})
"warden" => stub_everything
}
call_failure(env)
- assert @response.third.body.include?('
Log in
')
- assert @response.third.body.include?('Your account is not activated yet.')
+ assert_includes @response.third.body, '
Log in
'
+ assert_includes @response.third.body, 'Your account is not activated yet.'
end
if Rails.application.config.respond_to?(:relative_url_root)
@@ -361,20 +392,86 @@ def call_failure(env_params={})
"warden" => stub_everything
}
call_failure(env)
- assert @response.third.body.include?('
Log in
')
- assert @response.third.body.include?('Invalid Email or password.')
- assert_equal @request.env["SCRIPT_NAME"], '/sample'
- assert_equal @request.env["PATH_INFO"], '/users/sign_in'
+ assert_includes @response.third.body, '
Log in
'
+ assert_includes @response.third.body, 'Invalid email or password.'
+ assert_equal '/sample', @request.env["SCRIPT_NAME"]
+ assert_equal '/users/sign_in', @request.env["PATH_INFO"]
+ end
+ end
+ end
+
+ test 'respects the i18n locale passed via warden options when recalling original controller' do
+ env = {
+ "warden.options" => { recall: "devise/sessions#new", attempted_path: "/users/sign_in", locale: :"pt-BR" },
+ "devise.mapping" => Devise.mappings[:user],
+ "warden" => stub_everything
+ }
+ call_failure(env)
+
+ assert_includes @response.third.body, '
Log in
'
+ assert_includes @response.third.body, 'Email ou senha inválidos.'
+ end
+
+ # TODO: remove conditional/else when supporting only responders 3.1+
+ if ActionController::Responder.respond_to?(:error_status=)
+ test 'respects the configured responder `error_status` for the status code' do
+ swap Devise.responder, error_status: :unprocessable_entity do
+ env = {
+ "warden.options" => { recall: "devise/sessions#new", attempted_path: "/users/sign_in" },
+ "devise.mapping" => Devise.mappings[:user],
+ "warden" => stub_everything
+ }
+ call_failure(env)
+
+ assert_equal 422, @response.first
+ assert_includes @response.third.body, 'Invalid email or password.'
end
end
+
+ test 'respects the configured responder `redirect_status` if the recall app returns a redirect status code' do
+ swap Devise.responder, redirect_status: :see_other do
+ env = {
+ "warden.options" => { recall: "devise/registrations#cancel", attempted_path: "/users/cancel" },
+ "devise.mapping" => Devise.mappings[:user],
+ "warden" => stub_everything
+ }
+ call_failure(env)
+
+ assert_equal 303, @response.first
+ end
+ end
+ else
+ test 'uses default hardcoded responder `error_status` for the status code since responders version does not support configuring it' do
+ env = {
+ "warden.options" => { recall: "devise/sessions#new", attempted_path: "/users/sign_in" },
+ "devise.mapping" => Devise.mappings[:user],
+ "warden" => stub_everything
+ }
+ call_failure(env)
+
+ assert_equal 200, @response.first
+ assert_includes @response.third.body, 'Invalid email or password.'
+ end
+
+ test 'users default hardcoded responder `redirect_status` for the status code since responders version does not support configuring it' do
+ env = {
+ "warden.options" => { recall: "devise/registrations#cancel", attempted_path: "/users/cancel" },
+ "devise.mapping" => Devise.mappings[:user],
+ "warden" => stub_everything
+ }
+ call_failure(env)
+
+ assert_equal 302, @response.first
+ end
end
end
context "Lazy loading" do
test "loads" do
- assert_equal Devise::FailureApp.new.lazy_loading_works?, "yes it does"
+ assert_equal "yes it does", Devise::FailureApp.new.lazy_loading_works?
end
end
+
context "Without Flash Support" do
test "returns to the default redirect location without a flash message" do
call_failure request_klass: RequestWithoutFlashSupport
diff --git a/test/generators/active_record_generator_test.rb b/test/generators/active_record_generator_test.rb
index 757095edc..1c7d8219f 100644
--- a/test/generators/active_record_generator_test.rb
+++ b/test/generators/active_record_generator_test.rb
@@ -20,11 +20,7 @@ class ActiveRecordGeneratorTest < Rails::Generators::TestCase
Rails.application.config.paths.add "db/migrate", with: "db2/migrate"
run_generator %w(monster)
- if Rails.version >= '5.0.3'
- assert_migration "db2/migrate/devise_create_monsters.rb", /def change/
- else
- assert_migration "db/migrate/devise_create_monsters.rb", /def change/
- end
+ assert_migration "db2/migrate/devise_create_monsters.rb", /def change/
Rails.application.config.paths["db/migrate"] = old_paths
end
@@ -49,11 +45,7 @@ class ActiveRecordGeneratorTest < Rails::Generators::TestCase
assert_file "app/models/monster.rb"
run_generator %w(monster)
- if Rails.version >= '5.0.3'
- assert_migration "db2/migrate/add_devise_to_monsters.rb"
- else
- assert_migration "db/migrate/add_devise_to_monsters.rb"
- end
+ assert_migration "db2/migrate/add_devise_to_monsters.rb"
Rails.application.config.paths["db/migrate"] = old_paths
end
@@ -84,11 +76,7 @@ class ActiveRecordGeneratorTest < Rails::Generators::TestCase
test "add primary key type with rails 5 when specified in rails generator" do
run_generator ["monster", "--primary_key_type=uuid"]
- if Devise::Test.rails5_and_up?
- assert_migration "db/migrate/devise_create_monsters.rb", /create_table :monsters, id: :uuid do/
- else
- assert_migration "db/migrate/devise_create_monsters.rb", /create_table :monsters do/
- end
+ assert_migration "db/migrate/devise_create_monsters.rb", /create_table :monsters, id: :uuid do/
end
end
diff --git a/test/generators/devise_generator_test.rb b/test/generators/devise_generator_test.rb
index 00118c22d..22112c69c 100644
--- a/test/generators/devise_generator_test.rb
+++ b/test/generators/devise_generator_test.rb
@@ -37,5 +37,4 @@ def copy_routes
FileUtils.mkdir_p(destination)
FileUtils.cp routes, destination
end
-
end
diff --git a/test/generators/install_generator_test.rb b/test/generators/install_generator_test.rb
index 45aeddd07..3bb1b00f5 100644
--- a/test/generators/install_generator_test.rb
+++ b/test/generators/install_generator_test.rb
@@ -23,4 +23,12 @@ class InstallGeneratorTest < Rails::Generators::TestCase
assert_no_file "config/initializers/devise.rb"
assert_no_file "config/locales/devise.en.yml"
end
+
+ test "responder error_status based on rack version" do
+ run_generator(["--orm=active_record"])
+
+ error_status = Rack::RELEASE >= "3.1" ? :unprocessable_content : :unprocessable_entity
+
+ assert_file "config/initializers/devise.rb", /config\.responder\.error_status = #{error_status.inspect}/
+ end
end
diff --git a/test/generators/views_generator_test.rb b/test/generators/views_generator_test.rb
index bfb4a7f1b..1f8f90f3c 100644
--- a/test/generators/views_generator_test.rb
+++ b/test/generators/views_generator_test.rb
@@ -11,16 +11,19 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
run_generator
assert_files
assert_shared_links
+ assert_error_messages
end
test "Assert all views are properly created with scope param" do
run_generator %w(users)
assert_files "users"
assert_shared_links "users"
+ assert_error_messages "users"
run_generator %w(admins)
assert_files "admins"
assert_shared_links "admins"
+ assert_error_messages "admins"
end
test "Assert views with simple form" do
@@ -74,7 +77,7 @@ class ViewsGeneratorTest < Rails::Generators::TestCase
assert_file "app/views/devise/mailer/reset_password_instructions.markerb"
end
- def assert_files(scope = nil, options={})
+ def assert_files(scope = nil, options = {})
scope = "devise" if scope.nil?
mail_template_engine = options[:mail_template_engine] || "html.erb"
@@ -88,6 +91,7 @@ def assert_files(scope = nil, options={})
assert_file "app/views/#{scope}/registrations/edit.html.erb"
assert_file "app/views/#{scope}/sessions/new.html.erb"
assert_file "app/views/#{scope}/shared/_links.html.erb"
+ assert_file "app/views/#{scope}/shared/_error_messages.html.erb"
assert_file "app/views/#{scope}/unlocks/new.html.erb"
end
@@ -102,4 +106,16 @@ def assert_shared_links(scope = nil)
assert_file "app/views/#{scope}/sessions/new.html.erb", link
assert_file "app/views/#{scope}/unlocks/new.html.erb", link
end
+
+ def assert_error_messages(scope = nil)
+ scope = "devise" if scope.nil?
+ link = /<%= render \"#{scope}\/shared\/error_messages\", resource: resource %>/
+
+ assert_file "app/views/#{scope}/passwords/edit.html.erb", link
+ assert_file "app/views/#{scope}/passwords/new.html.erb", link
+ assert_file "app/views/#{scope}/confirmations/new.html.erb", link
+ assert_file "app/views/#{scope}/registrations/new.html.erb", link
+ assert_file "app/views/#{scope}/registrations/edit.html.erb", link
+ assert_file "app/views/#{scope}/unlocks/new.html.erb", link
+ end
end
diff --git a/test/helpers/devise_helper_test.rb b/test/helpers/devise_helper_test.rb
index 754e82d81..b9fac7da3 100644
--- a/test/helpers/devise_helper_test.rb
+++ b/test/helpers/devise_helper_test.rb
@@ -34,10 +34,6 @@ class DeviseHelperTest < Devise::IntegrationTest
end
test 'test errors.messages.not_saved with multiple errors from i18n' do
- # Dirty tracking behavior prevents email validations from being applied:
- # https://github.com/mongoid/mongoid/issues/756
- (pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
-
get new_user_registration_path
fill_in 'email', with: 'invalid_email'
diff --git a/test/integration/authenticatable_test.rb b/test/integration/authenticatable_test.rb
index 6b1d5799f..28d00399b 100644
--- a/test/integration/authenticatable_test.rb
+++ b/test/integration/authenticatable_test.rb
@@ -6,7 +6,7 @@ class AuthenticationSanityTest < Devise::IntegrationTest
test 'sign in should not run model validations' do
sign_in_as_user
- refute User.validations_performed
+ assert_not User.validations_performed
end
test 'home should be accessible without sign in' do
@@ -18,13 +18,13 @@ class AuthenticationSanityTest < Devise::IntegrationTest
test 'sign in as user should not authenticate admin scope' do
sign_in_as_user
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
test 'sign in as admin should not authenticate user scope' do
sign_in_as_admin
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'sign in as both user and admin at same time' do
@@ -39,7 +39,7 @@ class AuthenticationSanityTest < Devise::IntegrationTest
sign_in_as_user
sign_in_as_admin
delete destroy_user_session_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert warden.authenticated?(:admin)
end
end
@@ -50,7 +50,7 @@ class AuthenticationSanityTest < Devise::IntegrationTest
sign_in_as_admin
delete destroy_admin_session_path
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
assert warden.authenticated?(:user)
end
end
@@ -61,8 +61,8 @@ class AuthenticationSanityTest < Devise::IntegrationTest
sign_in_as_admin
delete destroy_user_session_path
- refute warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:user)
+ assert_not warden.authenticated?(:admin)
end
end
@@ -72,21 +72,21 @@ class AuthenticationSanityTest < Devise::IntegrationTest
sign_in_as_admin
delete destroy_admin_session_path
- refute warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:user)
end
end
test 'not signed in as admin should not be able to access admins actions' do
get admins_path
assert_redirected_to new_admin_session_path
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
test 'signed in as user should not be able to access admins actions' do
sign_in_as_user
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
get admins_path
assert_redirected_to new_admin_session_path
@@ -95,7 +95,7 @@ class AuthenticationSanityTest < Devise::IntegrationTest
test 'signed in as admin should be able to access admin actions' do
sign_in_as_admin
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
get admins_path
@@ -123,7 +123,7 @@ class AuthenticationSanityTest < Devise::IntegrationTest
get root_path
assert_contain 'Signed out successfully'
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
test 'unauthenticated admin set message on sign out' do
@@ -146,13 +146,13 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'not signed in should not be able to access private route (authenticate denied)' do
get private_path
assert_redirected_to new_admin_session_path
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
test 'signed in as user should not be able to access private route restricted to admins (authenticate denied)' do
sign_in_as_user
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
get private_path
assert_redirected_to new_admin_session_path
end
@@ -160,7 +160,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as admin should be able to access private route restricted to admins (authenticate accepted)' do
sign_in_as_admin
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
get private_path
@@ -172,7 +172,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as inactive admin should not be able to access private/active route restricted to active admins (authenticate denied)' do
sign_in_as_admin(active: false)
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_raises ActionController::RoutingError do
get "/private/active"
@@ -182,7 +182,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as active admin should be able to access private/active route restricted to active admins (authenticate accepted)' do
sign_in_as_admin(active: true)
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
get private_active_path
@@ -194,7 +194,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as admin should get admin dashboard (authenticated accepted)' do
sign_in_as_admin
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
get dashboard_path
@@ -206,7 +206,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as user should get user dashboard (authenticated accepted)' do
sign_in_as_user
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
get dashboard_path
@@ -224,7 +224,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as inactive admin should not be able to access dashboard/active route restricted to active admins (authenticated denied)' do
sign_in_as_admin(active: false)
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_raises ActionController::RoutingError do
get "/dashboard/active"
@@ -234,7 +234,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in as active admin should be able to access dashboard/active route restricted to active admins (authenticated accepted)' do
sign_in_as_admin(active: true)
assert warden.authenticated?(:admin)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
get dashboard_active_path
@@ -246,7 +246,7 @@ class AuthenticationRoutesRestrictions < Devise::IntegrationTest
test 'signed in user should not see unauthenticated page (unauthenticated denied)' do
sign_in_as_user
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
assert_raises ActionController::RoutingError do
get join_path
@@ -273,6 +273,15 @@ class AuthenticationRedirectTest < Devise::IntegrationTest
assert_contain 'You need to sign in or sign up before continuing.'
end
+ test 'redirect from warden respects i18n locale set at the controller' do
+ get admins_path(locale: "pt-BR")
+
+ assert_redirected_to new_admin_session_path
+ follow_redirect!
+
+ assert_contain 'Para continuar, faça login ou registre-se.'
+ end
+
test 'redirect to default url if no other was configured' do
sign_in_as_user
assert_template 'home/index'
@@ -321,7 +330,15 @@ class AuthenticationRedirectTest < Devise::IntegrationTest
test 'require_no_authentication should set the already_authenticated flash message' do
sign_in_as_user
visit new_user_session_path
- assert_equal flash[:alert], I18n.t("devise.failure.already_authenticated")
+ assert_equal I18n.t("devise.failure.already_authenticated"), flash[:alert]
+ end
+
+ test 'require_no_authentication should set the already_authenticated flash message as admin' do
+ store_translations :en, devise: { failure: { admin: { already_authenticated: 'You are already signed in as admin.' } } } do
+ sign_in_as_admin
+ visit new_admin_session_path
+ assert_equal "You are already signed in as admin.", flash[:alert]
+ end
end
end
@@ -336,16 +353,20 @@ class AuthenticationSessionTest < Devise::IntegrationTest
end
test 'refreshes _csrf_token' do
- ApplicationController.allow_forgery_protection = true
-
- begin
+ swap ApplicationController, allow_forgery_protection: true do
get new_user_session_path
- token = request.session[:_csrf_token]
+ token_from_session = request.session[:_csrf_token]
+
+ if Devise::Test.rails71_and_up?
+ token_from_env = request.env["action_controller.csrf_token"]
+ end
sign_in_as_user
- assert_not_equal request.session[:_csrf_token], token
- ensure
- ApplicationController.allow_forgery_protection = false
+ assert_not_equal request.session[:_csrf_token], token_from_session
+
+ if Devise::Test.rails71_and_up?
+ assert_not_equal request.env["action_controller.csrf_token"], token_from_env
+ end
end
end
@@ -385,7 +406,7 @@ class AuthenticationWithScopedViewsTest < Devise::IntegrationTest
end
assert_match %r{Special user view}, response.body
- assert !Devise::PasswordsController.scoped_views?
+ assert_not Devise::PasswordsController.scoped_views?
ensure
Devise::SessionsController.send :remove_instance_variable, :@scoped_views
end
@@ -412,13 +433,13 @@ class AuthenticationOthersTest < Devise::IntegrationTest
test 'handles unverified requests gets rid of caches' do
swap ApplicationController, allow_forgery_protection: true do
post exhibit_user_url(1)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
sign_in_as_user
assert warden.authenticated?(:user)
post exhibit_user_url(1)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_equal "User is not authenticated", response.body
end
end
@@ -454,14 +475,6 @@ class AuthenticationOthersTest < Devise::IntegrationTest
end
end
- test 'sign in stub in xml format' do
- get new_user_session_path(format: 'xml')
- assert_match '', response.body
- assert_match %r{
.*}m, response.body
- assert_match '
', response.body
- assert_match '
\n)
+ assert_includes response.body, '{"user":{'
end
- test 'sign in with xml format is idempotent' do
- get new_user_session_path(format: 'xml')
+ test 'sign in with json format is idempotent' do
+ get new_user_session_path(format: 'json')
assert_response :success
create_user
- post user_session_path(format: 'xml'), params: { user: {email: "user@test.com", password: '12345678'} }
+ post user_session_path(format: 'json'), params: { user: {email: "user@test.com", password: '12345678'} }
assert_response :success
- get new_user_session_path(format: 'xml')
+ get new_user_session_path(format: 'json')
assert_response :success
- post user_session_path(format: 'xml'), params: { user: {email: "user@test.com", password: '12345678'} }
+ post user_session_path(format: 'json'), params: { user: {email: "user@test.com", password: '12345678'} }
assert_response :success
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"user":{'
end
test 'sign out with html redirects' do
@@ -519,18 +532,11 @@ class AuthenticationOthersTest < Devise::IntegrationTest
assert_current_url '/'
end
- test 'sign out with xml format returns no content' do
- sign_in_as_user
- delete destroy_user_session_path(format: 'xml')
- assert_response :no_content
- refute warden.authenticated?(:user)
- end
-
test 'sign out with json format returns no content' do
sign_in_as_user
delete destroy_user_session_path(format: 'json')
assert_response :no_content
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'sign out with non-navigational format via XHR does not redirect' do
@@ -538,7 +544,7 @@ class AuthenticationOthersTest < Devise::IntegrationTest
sign_in_as_admin
get destroy_sign_out_via_get_session_path, xhr: true, headers: { "HTTP_ACCEPT" => "application/json,text/javascript,*/*" } # NOTE: Bug is triggered by combination of XHR and */*.
assert_response :no_content
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -548,7 +554,7 @@ class AuthenticationOthersTest < Devise::IntegrationTest
sign_in_as_user
delete destroy_user_session_path, xhr: true, headers: { "HTTP_ACCEPT" => "text/html,*/*" }
assert_response :redirect
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
end
@@ -557,8 +563,8 @@ class AuthenticationKeysTest < Devise::IntegrationTest
test 'missing authentication keys cause authentication to abort' do
swap Devise, authentication_keys: [:subdomain] do
sign_in_as_user
- assert_contain "Invalid Subdomain or password."
- refute warden.authenticated?(:user)
+ assert_contain "Invalid subdomain or password."
+ assert_not warden.authenticated?(:user)
end
end
@@ -575,7 +581,7 @@ class AuthenticationRequestKeysTest < Devise::IntegrationTest
host! 'foo.bar.baz'
swap Devise, request_keys: [:subdomain] do
- User.expects(:find_for_authentication).with(subdomain: 'foo', email: 'user@test.com').returns(create_user)
+ User.expects(:find_for_authentication).with({ subdomain: 'foo', email: 'user@test.com' }).returns(create_user)
sign_in_as_user
assert warden.authenticated?(:user)
end
@@ -587,7 +593,7 @@ class AuthenticationRequestKeysTest < Devise::IntegrationTest
sign_in_as_user
end
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -596,8 +602,8 @@ class AuthenticationRequestKeysTest < Devise::IntegrationTest
swap Devise, request_keys: [:subdomain] do
sign_in_as_user
- assert_contain "Invalid Email or password."
- refute warden.authenticated?(:user)
+ assert_contain "Invalid email or password."
+ assert_not warden.authenticated?(:user)
end
end
@@ -620,7 +626,7 @@ def sign_in!(scope)
test 'allow sign out via delete when sign_out_via provides only delete' do
sign_in!(:sign_out_via_delete)
delete destroy_sign_out_via_delete_session_path
- refute warden.authenticated?(:sign_out_via_delete)
+ assert_not warden.authenticated?(:sign_out_via_delete)
end
test 'do not allow sign out via get when sign_out_via provides only delete' do
@@ -634,7 +640,7 @@ def sign_in!(scope)
test 'allow sign out via post when sign_out_via provides only post' do
sign_in!(:sign_out_via_post)
post destroy_sign_out_via_post_session_path
- refute warden.authenticated?(:sign_out_via_post)
+ assert_not warden.authenticated?(:sign_out_via_post)
end
test 'do not allow sign out via get when sign_out_via provides only post' do
@@ -648,13 +654,13 @@ def sign_in!(scope)
test 'allow sign out via delete when sign_out_via provides delete and post' do
sign_in!(:sign_out_via_delete_or_post)
delete destroy_sign_out_via_delete_or_post_session_path
- refute warden.authenticated?(:sign_out_via_delete_or_post)
+ assert_not warden.authenticated?(:sign_out_via_delete_or_post)
end
test 'allow sign out via post when sign_out_via provides delete and post' do
sign_in!(:sign_out_via_delete_or_post)
post destroy_sign_out_via_delete_or_post_session_path
- refute warden.authenticated?(:sign_out_via_delete_or_post)
+ assert_not warden.authenticated?(:sign_out_via_delete_or_post)
end
test 'do not allow sign out via get when sign_out_via provides delete and post' do
diff --git a/test/integration/confirmable_test.rb b/test/integration/confirmable_test.rb
index 73563f283..f9185e87f 100644
--- a/test/integration/confirmable_test.rb
+++ b/test/integration/confirmable_test.rb
@@ -43,12 +43,12 @@ def resend_confirmation
test 'user with valid confirmation token should not be able to confirm an account after the token has expired' do
swap Devise, confirm_within: 3.days do
user = create_user(confirm: false, confirmation_sent_at: 4.days.ago)
- refute user.confirmed?
+ assert_not user.confirmed?
visit_user_confirmation_with_token(user.raw_confirmation_token)
assert_have_selector '#error_explanation'
assert_contain %r{needs to be confirmed within 3 days}
- refute user.reload.confirmed?
+ assert_not user.reload.confirmed?
assert_current_url "/users/confirmation?confirmation_token=#{user.raw_confirmation_token}"
end
end
@@ -86,7 +86,7 @@ def resend_confirmation
test 'user with valid confirmation token should be able to confirm an account before the token has expired' do
swap Devise, confirm_within: 3.days do
user = create_user(confirm: false, confirmation_sent_at: 2.days.ago)
- refute user.confirmed?
+ assert_not user.confirmed?
visit_user_confirmation_with_token(user.raw_confirmation_token)
assert_contain 'Your email address has been successfully confirmed.'
@@ -132,7 +132,16 @@ def resend_confirmation
sign_in_as_user(confirm: false)
assert_contain 'You have to confirm your email address before continuing'
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
+ end
+ end
+
+ test 'not confirmed user redirect respects i18n locale set' do
+ swap Devise, allow_unconfirmed_access_for: 0.days do
+ sign_in_as_user(confirm: false, visit: new_user_session_path(locale: "pt-BR"))
+
+ assert_contain 'Você precisa confirmar seu email para continuar'
+ assert_not warden.authenticated?(:user)
end
end
@@ -142,8 +151,8 @@ def resend_confirmation
fill_in 'password', with: 'invalid'
end
- assert_contain 'Invalid Email or password'
- refute warden.authenticated?(:user)
+ assert_contain 'Invalid email or password'
+ assert_not warden.authenticated?(:user)
end
end
@@ -175,6 +184,36 @@ def resend_confirmation
assert_current_url '/users/sign_in'
end
+ test "should not be able to confirm an email with a blank confirmation token" do
+ visit_user_confirmation_with_token("")
+
+ assert_contain %r{Confirmation token can['’]t be blank}
+ end
+
+ test "should not be able to confirm an email with a nil confirmation token" do
+ visit_user_confirmation_with_token(nil)
+
+ assert_contain %r{Confirmation token can['’]t be blank}
+ end
+
+ test "should not be able to confirm user with blank confirmation token" do
+ user = create_user(confirm: false)
+ user.update_attribute(:confirmation_token, "")
+
+ visit_user_confirmation_with_token("")
+
+ assert_contain %r{Confirmation token can['’]t be blank}
+ end
+
+ test "should not be able to confirm user with nil confirmation token" do
+ user = create_user(confirm: false)
+ user.update_attribute(:confirmation_token, nil)
+
+ visit_user_confirmation_with_token(nil)
+
+ assert_contain %r{Confirmation token can['’]t be blank}
+ end
+
test 'error message is configurable by resource name' do
store_translations :en, devise: {
failure: { user: { unconfirmed: "Not confirmed user" } }
@@ -184,40 +223,32 @@ def resend_confirmation
end
end
- test 'resent confirmation token with valid E-Mail in XML format should return valid response' do
+ test 'resent confirmation token with valid e-mail in JSON format should return empty and valid response' do
user = create_user(confirm: false)
- post user_confirmation_path(format: 'xml'), params: { user: { email: user.email } }
+ post user_confirmation_path(format: 'json'), params: { user: { email: user.email } }
assert_response :success
- assert_equal response.body, {}.to_xml
+ assert_equal({}.to_json, response.body)
end
- test 'resent confirmation token with invalid E-Mail in XML format should return invalid response' do
+ test 'resent confirmation token with invalid e-mail in JSON format should return invalid response' do
create_user(confirm: false)
- post user_confirmation_path(format: 'xml'), params: { user: { email: 'invalid.test@test.com' } }
+ post user_confirmation_path(format: 'json'), params: { user: { email: 'invalid.test@test.com' } }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"errors":{'
end
- test 'confirm account with valid confirmation token in XML format should return valid response' do
+ test 'confirm account with valid confirmation token in JSON format should return valid response' do
user = create_user(confirm: false)
- get user_confirmation_path(confirmation_token: user.raw_confirmation_token, format: 'xml')
+ get user_confirmation_path(confirmation_token: user.raw_confirmation_token, format: 'json')
assert_response :success
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"user":{'
end
- test 'confirm account with invalid confirmation token in XML format should return invalid response' do
+ test 'confirm account with invalid confirmation token in JSON format should return invalid response' do
create_user(confirm: false)
- get user_confirmation_path(confirmation_token: 'invalid_confirmation', format: 'xml')
+ get user_confirmation_path(confirmation_token: 'invalid_confirmation', format: 'json')
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
- end
-
- test 'request an account confirmation account with JSON, should return an empty JSON' do
- user = create_user(confirm: false)
-
- post user_confirmation_path, params: { user: { email: user.email }, format: :json }
- assert_response :success
- assert_equal response.body, {}.to_json
+ assert_includes response.body, '{"confirmation_token":['
end
test "when in paranoid mode and with a valid e-mail, should not say that the e-mail is valid" do
@@ -252,7 +283,7 @@ def resend_confirmation
end
class ConfirmationOnChangeTest < Devise::IntegrationTest
- def create_second_admin(options={})
+ def create_second_admin(options = {})
@admin = nil
create_admin(options)
end
@@ -286,7 +317,7 @@ def visit_admin_confirmation_with_token(confirmation_token)
assert_contain 'Your email address has been successfully confirmed.'
assert_current_url '/admin_area/sign_in'
assert admin.reload.confirmed?
- refute admin.reload.pending_reconfirmation?
+ assert_not admin.reload.pending_reconfirmation?
end
test 'admin with previously valid confirmation token should not be able to confirm email after email changed again' do
@@ -308,7 +339,7 @@ def visit_admin_confirmation_with_token(confirmation_token)
assert_contain 'Your email address has been successfully confirmed.'
assert_current_url '/admin_area/sign_in'
assert admin.reload.confirmed?
- refute admin.reload.pending_reconfirmation?
+ assert_not admin.reload.pending_reconfirmation?
end
test 'admin email should be unique also within unconfirmed_email' do
@@ -323,4 +354,32 @@ def visit_admin_confirmation_with_token(confirmation_token)
assert_contain(/Email.*already.*taken/)
assert admin.reload.pending_reconfirmation?
end
+
+ test 'concurrent "update email" requests should not allow confirming a victim email address' do
+ attacker_email = "attacker@example.com"
+ victim_email = "victim@example.com"
+
+ attacker = create_admin
+ # update the email address of the attacker, but do not confirm it yet
+ attacker.update!(email: attacker_email)
+
+ # A new request starts, to update the unconfirmed email again.
+ attacker = Admin.find_by(id: attacker.id)
+
+ # A concurrent request also updates the email address to the victim, while the `attacker` request's model is in memory
+ Admin.where(id: attacker.id).update_all(
+ unconfirmed_email: victim_email,
+ confirmation_token: "different token"
+ )
+
+ # Now the attacker updates to the same prior unconfirmed email address, and confirm.
+ # This should update the `unconfirmed_email` in the database, even though it is unchanged from the models point of view.
+ attacker.update!(email: attacker_email)
+ attacker_token = attacker.raw_confirmation_token
+ visit_admin_confirmation_with_token(attacker_token)
+
+ attacker.reload
+ assert attacker.confirmed?
+ assert_equal attacker_email, attacker.email
+ end
end
diff --git a/test/integration/database_authenticatable_test.rb b/test/integration/database_authenticatable_test.rb
index 64a52b907..08011fe28 100644
--- a/test/integration/database_authenticatable_test.rb
+++ b/test/integration/database_authenticatable_test.rb
@@ -21,7 +21,7 @@ class DatabaseAuthenticationTest < Devise::IntegrationTest
fill_in 'email', with: 'foo@bar.com'
end
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -43,14 +43,14 @@ class DatabaseAuthenticationTest < Devise::IntegrationTest
fill_in 'email', with: ' foo@bar.com '
end
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
test 'sign in should not authenticate if not using proper authentication keys' do
swap Devise, authentication_keys: [:username] do
sign_in_as_user
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -61,17 +61,17 @@ class DatabaseAuthenticationTest < Devise::IntegrationTest
end
assert_contain 'Invalid email address'
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
end
- test 'sign in with invalid pasword should return to sign in form with error message' do
+ test 'sign in with invalid password should return to sign in form with error message' do
sign_in_as_admin do
fill_in 'password', with: 'abcdef'
end
- assert_contain 'Invalid Email or password'
- refute warden.authenticated?(:admin)
+ assert_contain 'Invalid email or password'
+ assert_not warden.authenticated?(:admin)
end
test 'when in paranoid mode and without a valid e-mail' do
@@ -80,9 +80,9 @@ class DatabaseAuthenticationTest < Devise::IntegrationTest
sign_in_as_user do
fill_in 'email', with: 'wrongemail@test.com'
end
-
+
assert_not_contain 'Not found in database'
- assert_contain 'Invalid Email or password.'
+ assert_contain 'Invalid email or password.'
end
end
end
diff --git a/test/integration/http_authenticatable_test.rb b/test/integration/http_authenticatable_test.rb
index 3a52c571f..11e373320 100644
--- a/test/integration/http_authenticatable_test.rb
+++ b/test/integration/http_authenticatable_test.rb
@@ -6,7 +6,7 @@ class HttpAuthenticationTest < Devise::IntegrationTest
test 'sign in with HTTP should not run model validations' do
sign_in_as_new_user_with_http
- refute User.validations_performed
+ assert_not User.validations_performed
end
test 'handles unverified requests gets rid of caches but continues signed in' do
@@ -22,10 +22,10 @@ class HttpAuthenticationTest < Devise::IntegrationTest
swap Devise, skip_session_storage: [] do
sign_in_as_new_user_with_http
assert_response 200
- assert_match 'user@test.com', response.body
+ assert_match '"email":"user@test.com"', response.body
assert warden.authenticated?(:user)
- get users_path(format: :xml)
+ get users_path(format: :json)
assert_response 200
end
end
@@ -34,10 +34,10 @@ class HttpAuthenticationTest < Devise::IntegrationTest
swap Devise, skip_session_storage: [:http_auth] do
sign_in_as_new_user_with_http
assert_response 200
- assert_match 'user@test.com', response.body
+ assert_match '"email":"user@test.com"', response.body
assert warden.authenticated?(:user)
- get users_path(format: :xml)
+ get users_path(format: :json)
assert_response 401
end
end
@@ -51,8 +51,8 @@ class HttpAuthenticationTest < Devise::IntegrationTest
test 'uses the request format as response content type' do
sign_in_as_new_user_with_http("unknown")
assert_equal 401, status
- assert_equal "application/xml; charset=utf-8", headers["Content-Type"]
- assert_match "Invalid Email or password.", response.body
+ assert_equal "application/json; charset=utf-8", headers["Content-Type"]
+ assert_match '"error":"Invalid email or password."', response.body
end
test 'returns a custom response with www-authenticate and chosen realm' do
@@ -67,7 +67,7 @@ class HttpAuthenticationTest < Devise::IntegrationTest
swap Devise, authentication_keys: [:username] do
sign_in_as_new_user_with_http("usertest")
assert_response :success
- assert_match 'user@test.com', response.body
+ assert_match '"email":"user@test.com"', response.body
assert warden.authenticated?(:user)
end
end
@@ -76,7 +76,7 @@ class HttpAuthenticationTest < Devise::IntegrationTest
swap Devise, authentication_keys: { username: false, email: false } do
sign_in_as_new_user_with_http("usertest")
assert_response :success
- assert_match 'user@test.com', response.body
+ assert_match '"email":"user@test.com"', response.body
assert warden.authenticated?(:user)
end
end
@@ -85,7 +85,7 @@ class HttpAuthenticationTest < Devise::IntegrationTest
swap Devise, authentication_keys: { email: false, username: false }, http_authentication_key: :username do
sign_in_as_new_user_with_http("usertest")
assert_response :success
- assert_match 'user@test.com', response.body
+ assert_match '"email":"user@test.com"', response.body
assert warden.authenticated?(:user)
end
end
@@ -99,16 +99,15 @@ class HttpAuthenticationTest < Devise::IntegrationTest
end
private
- def sign_in_as_new_user_with_http(username="user@test.com", password="12345678")
+ def sign_in_as_new_user_with_http(username = "user@test.com", password = "12345678")
user = create_user
- get users_path(format: :xml), headers: { "HTTP_AUTHORIZATION" => "Basic #{Base64.encode64("#{username}:#{password}")}" }
+ get users_path(format: :json), headers: { "HTTP_AUTHORIZATION" => "Basic #{Base64.encode64("#{username}:#{password}")}" }
user
end
# Sign in with oauth2 token. This is just to test that it isn't misinterpreted as basic authentication
def add_oauth2_header
user = create_user
- get users_path(format: :xml), headers: { "HTTP_AUTHORIZATION" => "OAuth #{Base64.encode64("#{user.email}:12345678")}" }
+ get users_path(format: :json), headers: { "HTTP_AUTHORIZATION" => "OAuth #{Base64.encode64("#{user.email}:12345678")}" }
end
-
end
diff --git a/test/integration/lockable_test.rb b/test/integration/lockable_test.rb
index 87e439ef1..e5dd5ee08 100644
--- a/test/integration/lockable_test.rb
+++ b/test/integration/lockable_test.rb
@@ -87,7 +87,7 @@ def send_unlock_request
assert_current_url "/users/sign_in"
assert_contain 'Your account has been unlocked successfully. Please sign in to continue.'
- refute user.reload.access_locked?
+ assert_not user.reload.access_locked?
end
test "user should not send a new e-mail if already locked" do
@@ -99,7 +99,7 @@ def send_unlock_request
sign_in_as_user(password: "invalid")
assert_contain 'Your account is locked.'
- assert ActionMailer::Base.deliveries.empty?
+ assert_empty ActionMailer::Base.deliveries
end
test 'error message is configurable by resource name' do
@@ -130,48 +130,39 @@ def send_unlock_request
end
end
- test 'user should be able to request a new unlock token via XML request' do
+ test 'user should be able to request a new unlock token via JSON request and should return empty and valid response' do
user = create_user(locked: true)
ActionMailer::Base.deliveries.clear
- post user_unlock_path(format: 'xml'), params: { user: {email: user.email} }
+ post user_unlock_path(format: 'json'), params: { user: {email: user.email} }
assert_response :success
- assert_equal response.body, {}.to_xml
-
+ assert_equal({}.to_json, response.body)
assert_equal 1, ActionMailer::Base.deliveries.size
end
- test 'unlocked user should not be able to request a unlock token via XML request' do
+ test 'unlocked user should not be able to request a unlock token via JSON request' do
user = create_user(locked: false)
ActionMailer::Base.deliveries.clear
- post user_unlock_path(format: 'xml'), params: { user: {email: user.email} }
+ post user_unlock_path(format: 'json'), params: { user: {email: user.email} }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"errors":{'
assert_equal 0, ActionMailer::Base.deliveries.size
end
- test 'user with valid unlock token should be able to unlock account via XML request' do
+ test 'user with valid unlock token should be able to unlock account via JSON request' do
user = create_user()
raw = user.lock_access!
assert user.access_locked?
- get user_unlock_path(format: 'xml', unlock_token: raw)
+ get user_unlock_path(format: 'json', unlock_token: raw)
assert_response :success
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"user":{'
end
-
- test 'user with invalid unlock token should not be able to unlock the account via XML request' do
- get user_unlock_path(format: 'xml', unlock_token: 'invalid_token')
+ test 'user with invalid unlock token should not be able to unlock the account via JSON request' do
+ get user_unlock_path(format: 'json', unlock_token: 'invalid_token')
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
- end
-
- test "when using json to ask a unlock request, should not return the user" do
- user = create_user(locked: true)
- post user_unlock_path(format: "json", user: {email: user.email})
- assert_response :success
- assert_equal response.body, {}.to_json
+ assert_includes response.body, '{"unlock_token":['
end
test "in paranoid mode, when trying to unlock a user that exists it should not say that it exists if it is locked" do
diff --git a/test/integration/omniauthable_test.rb b/test/integration/omniauthable_test.rb
index 6c989f0c1..72a59dbfb 100644
--- a/test/integration/omniauthable_test.rb
+++ b/test/integration/omniauthable_test.rb
@@ -23,6 +23,9 @@ class OmniauthableIntegrationTest < Devise::IntegrationTest
"extra" => {"user_hash" => FACEBOOK_INFO}
}
OmniAuth.config.add_camelization 'facebook', 'FaceBook'
+ if OmniAuth.config.respond_to?(:request_validation_phase)
+ OmniAuth.config.request_validation_phase = ->(env) {}
+ end
end
teardown do
@@ -45,17 +48,17 @@ def stub_action!(name)
test "omniauth sign in should not run model validations" do
stub_action!(:sign_in_facebook) do
create_user
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
assert warden.authenticated?(:user)
- refute User.validations_performed
+ assert_not User.validations_performed
end
end
test "can access omniauth.auth in the env hash" do
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
json = ActiveSupport::JSON.decode(response.body)
@@ -68,8 +71,8 @@ def stub_action!(name)
test "cleans up session on sign up" do
assert_no_difference "User.count" do
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
end
assert session["devise.facebook_data"]
@@ -84,49 +87,80 @@ def stub_action!(name)
assert_current_url "/"
assert_contain "You have signed up successfully."
assert_contain "Hello User user@example.com"
- refute session["devise.facebook_data"]
+ assert_not session["devise.facebook_data"]
end
test "cleans up session on cancel" do
assert_no_difference "User.count" do
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
end
assert session["devise.facebook_data"]
visit "/users/cancel"
- assert !session["devise.facebook_data"]
+ assert_not session["devise.facebook_data"]
end
test "cleans up session on sign in" do
assert_no_difference "User.count" do
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
end
assert session["devise.facebook_data"]
sign_in_as_user
- assert !session["devise.facebook_data"]
+ assert_not session["devise.facebook_data"]
end
test "sign in and send remember token if configured" do
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
assert_nil warden.cookies["remember_user_token"]
stub_action!(:sign_in_facebook) do
create_user
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
assert warden.authenticated?(:user)
assert warden.cookies["remember_user_token"]
end
end
+ test "authorization path via GET when Omniauth allowed_request_methods includes GET" do
+ original_allowed = OmniAuth.config.allowed_request_methods
+ OmniAuth.config.allowed_request_methods = [:get, :post]
+
+ get "/users/auth/facebook"
+
+ assert_response(:redirect)
+ ensure
+ OmniAuth.config.allowed_request_methods = original_allowed
+ end
+
+ test "authorization path via GET when Omniauth allowed_request_methods doesn't include GET" do
+ original_allowed = OmniAuth.config.allowed_request_methods
+ OmniAuth.config.allowed_request_methods = [:post]
+
+ assert_raises(ActionController::RoutingError) do
+ get "/users/auth/facebook"
+ end
+ ensure
+ OmniAuth.config.allowed_request_methods = original_allowed
+ end
+
+ test "generates a link to authenticate with provider" do
+ visit "/users/sign_in"
+ assert_select "form[action=?][method=post]", "/users/auth/facebook" do
+ assert_select "input[type=submit][value=?]", "Sign in with FaceBook"
+ end
+ end
+
test "generates a proper link when SCRIPT_NAME is set" do
header 'SCRIPT_NAME', '/q'
visit "/users/sign_in"
- assert_select "a", href: "/q/users/auth/facebook"
+ assert_select "form[action=?][method=post]", "/q/users/auth/facebook" do
+ assert_select "input[type=submit][value=?]", "Sign in with FaceBook"
+ end
end
test "handles callback error parameter according to the specification" do
@@ -139,10 +173,10 @@ def stub_action!(name)
test "handles other exceptions from OmniAuth" do
OmniAuth.config.mock_auth[:facebook] = :invalid_credentials
- visit "/users/sign_in"
- click_link "Sign in with FaceBook"
+ post "/users/auth/facebook"
+ follow_redirect!
+ follow_redirect!
- assert_current_url "/users/sign_in"
assert_contain 'Could not authenticate you from FaceBook because "Invalid credentials".'
end
end
diff --git a/test/integration/recoverable_test.rb b/test/integration/recoverable_test.rb
index c834f1d22..c391b0b2e 100644
--- a/test/integration/recoverable_test.rb
+++ b/test/integration/recoverable_test.rb
@@ -12,16 +12,16 @@ def visit_new_password_path
def request_forgot_password(&block)
visit_new_password_path
assert_response :success
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
fill_in 'email', with: 'user@test.com'
yield if block_given?
Devise.stubs(:friendly_token).returns("abcdef")
- click_button 'Send me reset password instructions'
+ click_button 'Send me password reset instructions'
end
- def reset_password(options={}, &block)
+ def reset_password(options = {}, &block)
unless options[:visit] == false
visit edit_user_password_path(reset_password_token: options[:reset_password_token] || "abcdef")
assert_response :success
@@ -33,6 +33,17 @@ def reset_password(options={}, &block)
click_button 'Change my password'
end
+ test 'reset password should send to user record email and avoid case mapping collisions' do
+ create_user(email: 'user@github.com')
+
+ request_forgot_password do
+ fill_in 'email', with: 'user@gıthub.com'
+ end
+
+ mail = ActionMailer::Base.deliveries.last
+ assert_equal ['user@github.com'], mail.to
+ end
+
test 'reset password with email of different case should succeed when email is in the list of case insensitive keys' do
create_user(email: 'Foo@Bar.com')
@@ -149,7 +160,7 @@ def reset_password(options={}, &block)
assert_current_url '/users/password'
assert_have_selector '#error_explanation'
assert_contain %r{Reset password token(.*)invalid}
- refute user.reload.valid_password?('987654321')
+ assert_not user.reload.valid_password?('987654321')
end
test 'not authenticated user with valid reset password token but invalid password should not be able to change their password' do
@@ -162,8 +173,8 @@ def reset_password(options={}, &block)
assert_response :success
assert_current_url '/users/password'
assert_have_selector '#error_explanation'
- assert_contain "Password confirmation doesn't match Password"
- refute user.reload.valid_password?('987654321')
+ assert_contain %r{Password confirmation doesn['’]t match Password}
+ assert_not user.reload.valid_password?('987654321')
end
test 'not authenticated user with valid data should be able to change their password' do
@@ -183,7 +194,7 @@ def reset_password(options={}, &block)
reset_password { fill_in 'Confirm new password', with: 'other_password' }
assert_response :success
assert_have_selector '#error_explanation'
- refute user.reload.valid_password?('987654321')
+ assert_not user.reload.valid_password?('987654321')
reset_password visit: false
assert_contain 'Your password has been changed successfully.'
@@ -207,7 +218,32 @@ def reset_password(options={}, &block)
assert_contain 'Your password has been changed successfully.'
assert_not_contain 'You are now signed in.'
assert_equal new_user_session_path, @request.path
- assert !warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
+ end
+ end
+
+ test 'does not sign in user automatically after changing its password if resource_class.sign_in_after_reset_password is false' do
+ swap_model_config User, sign_in_after_reset_password: false do
+ create_user
+ request_forgot_password
+ reset_password
+
+ assert_contain 'Your password has been changed successfully'
+ assert_not_contain 'You are now signed in.'
+ assert_equal new_user_session_path, @request.path
+ assert_not warden.authenticated?(:user)
+ end
+ end
+
+ test 'sign in user automatically after changing its password if resource_class.sign_in_after_reset_password is true' do
+ swap Devise, sign_in_after_reset_password: false do
+ swap_model_config User, sign_in_after_reset_password: true do
+ create_user
+ request_forgot_password
+ reset_password
+
+ assert warden.authenticated?(:user)
+ end
end
end
@@ -221,7 +257,7 @@ def reset_password(options={}, &block)
assert_contain 'Your password has been changed successfully.'
assert_not_contain 'You are now signed in.'
assert_equal new_user_session_path, @request.path
- assert !warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
end
@@ -233,7 +269,7 @@ def reset_password(options={}, &block)
reset_password
assert_contain 'Your password has been changed successfully.'
- assert !user.reload.access_locked?
+ assert_not user.reload.access_locked?
assert warden.authenticated?(:user)
end
end
@@ -245,75 +281,65 @@ def reset_password(options={}, &block)
reset_password
assert_contain 'Your password has been changed successfully.'
- assert !user.reload.access_locked?
+ assert_not user.reload.access_locked?
assert warden.authenticated?(:user)
end
end
- test 'reset password request with valid E-Mail in XML format should return valid response' do
+ test 'reset password request with valid e-mail in JSON format should return empty and valid response' do
create_user
- post user_password_path(format: 'xml'), params: { user: {email: "user@test.com"} }
+ post user_password_path(format: 'json'), params: { user: {email: "user@test.com"} }
assert_response :success
- assert_equal response.body, { }.to_xml
+ assert_equal({}.to_json, response.body)
end
- test 'reset password request with invalid E-Mail in XML format should return valid response' do
+ test 'reset password request with invalid e-mail in JSON format should return valid response' do
create_user
- post user_password_path(format: 'xml'), params: { user: {email: "invalid.test@test.com"} }
+ post user_password_path(format: 'json'), params: { user: {email: "invalid.test@test.com"} }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"errors":{'
end
- test 'reset password request with invalid E-Mail in XML format should return empty and valid response' do
+ test 'reset password request with invalid e-mail in JSON format should return empty and valid response in paranoid mode' do
swap Devise, paranoid: true do
create_user
- post user_password_path(format: 'xml'), params: { user: {email: "invalid@test.com"} }
+ post user_password_path(format: 'json'), params: { user: {email: "invalid@test.com"} }
assert_response :success
- assert_equal response.body, { }.to_xml
+ assert_equal({}.to_json, response.body)
end
end
- test 'change password with valid parameters in XML format should return valid response' do
+ test 'change password with valid parameters in JSON format should return valid response' do
create_user
request_forgot_password
- put user_password_path(format: 'xml'), params: { user: {
+ put user_password_path(format: 'json'), params: { user: {
reset_password_token: 'abcdef', password: '987654321', password_confirmation: '987654321'
- }
- }
+ } }
assert_response :success
assert warden.authenticated?(:user)
end
- test 'change password with invalid token in XML format should return invalid response' do
+ test 'change password with invalid token in JSON format should return invalid response' do
create_user
request_forgot_password
- put user_password_path(format: 'xml'), params: { user: {reset_password_token: 'invalid.token', password: '987654321', password_confirmation: '987654321'} }
+ put user_password_path(format: 'json'), params: { user: {reset_password_token: 'invalid.token', password: '987654321', password_confirmation: '987654321'} }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"errors":{'
end
- test 'change password with invalid new password in XML format should return invalid response' do
+ test 'change password with invalid new password in JSON format should return invalid response' do
user = create_user
request_forgot_password
- put user_password_path(format: 'xml'), params: { user: {reset_password_token: user.reload.reset_password_token, password: '', password_confirmation: '987654321'} }
+ put user_password_path(format: 'json'), params: { user: {reset_password_token: user.reload.reset_password_token, password: '', password_confirmation: '987654321'} }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
- end
-
- test "when using json requests to ask a confirmable request, should not return the object" do
- user = create_user(confirm: false)
-
- post user_password_path(format: :json), params: { user: { email: user.email } }
-
- assert_response :success
- assert_equal response.body, "{}"
+ assert_includes response.body, '{"errors":{'
end
test "when in paranoid mode and with an invalid e-mail, asking to reset a password should display a message that does not indicates that the e-mail does not exists in the database" do
swap Devise, paranoid: true do
visit_new_password_path
fill_in "email", with: "arandomemail@test.com"
- click_button 'Send me reset password instructions'
+ click_button 'Send me password reset instructions'
assert_not_contain "1 error prohibited this user from being saved:"
assert_not_contain "Email not found"
@@ -327,7 +353,7 @@ def reset_password(options={}, &block)
user = create_user
visit_new_password_path
fill_in 'email', with: user.email
- click_button 'Send me reset password instructions'
+ click_button 'Send me password reset instructions'
assert_contain "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
assert_current_url "/users/sign_in"
diff --git a/test/integration/registerable_test.rb b/test/integration/registerable_test.rb
index 46f09f37b..9289ac6af 100644
--- a/test/integration/registerable_test.rb
+++ b/test/integration/registerable_test.rb
@@ -20,7 +20,7 @@ class RegistrationTest < Devise::IntegrationTest
assert_current_url "/admin_area/home"
admin = Admin.to_adapter.find_first(order: [:id, :desc])
- assert_equal admin.email, 'new_user@test.com'
+ assert_equal 'new_user@test.com', admin.email
end
test 'a guest admin should be able to sign in and be redirected to a custom location' do
@@ -66,11 +66,11 @@ def user_sign_up
assert_not_contain 'You have to confirm your account before continuing'
assert_current_url "/"
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
user = User.to_adapter.find_first(order: [:id, :desc])
- assert_equal user.email, 'new_user@test.com'
- refute user.confirmed?
+ assert_equal 'new_user@test.com', user.email
+ assert_not user.confirmed?
end
test 'a guest user should receive the confirmation instructions from the default mailer' do
@@ -94,14 +94,10 @@ def user_sign_up
click_button 'Sign up'
assert_current_url "/?custom=1"
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'a guest user cannot sign up with invalid information' do
- # Dirty tracking behavior prevents email validations from being applied:
- # https://github.com/mongoid/mongoid/issues/756
- (pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
-
get new_user_registration_path
fill_in 'email', with: 'invalid_email'
@@ -112,18 +108,14 @@ def user_sign_up
assert_template 'registrations/new'
assert_have_selector '#error_explanation'
assert_contain "Email is invalid"
- assert_contain "Password confirmation doesn't match Password"
+ assert_contain %r{Password confirmation doesn['’]t match Password}
assert_contain "2 errors prohibited"
assert_nil User.to_adapter.find_first
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'a guest should not sign up with email/password that already exists' do
- # Dirty tracking behavior prevents email validations from being applied:
- # https://github.com/mongoid/mongoid/issues/756
- (pending "Fails on Mongoid < 2.1"; break) if defined?(Mongoid) && Mongoid::VERSION.to_f < 2.1
-
create_user
get new_user_registration_path
@@ -135,7 +127,7 @@ def user_sign_up
assert_current_url '/users'
assert_contain(/Email.*already.*taken/)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'a guest should not be able to change account' do
@@ -189,9 +181,25 @@ def user_sign_up
fill_in 'current password', with: '12345678'
click_button 'Update'
- assert_contain 'Your account has been updated successfully, but since your password was changed, you need to sign in again'
+ assert_contain 'Your account has been updated successfully, but since your password was changed, you need to sign in again.'
+ assert_equal new_user_session_path, @request.path
+ assert_not warden.authenticated?(:user)
+ end
+ end
+
+ test 'a signed in user should not be able to use the website after changing their password if resource_class.sign_in_after_change_password is false' do
+ swap_model_config User, sign_in_after_change_password: false do
+ sign_in_as_user
+ get edit_user_registration_path
+
+ fill_in 'password', with: '1234567890'
+ fill_in 'password confirmation', with: '1234567890'
+ fill_in 'current password', with: '12345678'
+ click_button 'Update'
+
+ assert_contain 'Your account has been updated successfully, but since your password was changed, you need to sign in again.'
assert_equal new_user_session_path, @request.path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -251,10 +259,10 @@ def user_sign_up
fill_in 'current password', with: '12345678'
click_button 'Update'
- assert_contain "Password confirmation doesn't match Password"
- refute User.to_adapter.find_first.valid_password?('pas123')
+ assert_contain %r{Password confirmation doesn['’]t match Password}
+ assert_not User.to_adapter.find_first.valid_password?('pas123')
end
-
+
test 'a signed in user should see a warning about minimum password length' do
sign_in_as_user
get edit_user_registration_path
@@ -268,7 +276,7 @@ def user_sign_up
click_button "Cancel my account"
assert_contain "Bye! Your account has been successfully cancelled. We hope to see you again soon."
- assert User.to_adapter.find_all.empty?
+ assert_empty User.to_adapter.find_all
end
test 'a user should be able to cancel sign up by deleting data in the session' do
@@ -283,13 +291,6 @@ def user_sign_up
assert_redirected_to new_user_registration_path
end
- test 'a user with XML sign up stub' do
- get new_user_registration_path(format: 'xml')
- assert_response :success
- assert_match %(\n), response.body
- assert_no_match(/\n)
+ assert_includes response.body, '{"admin":{'
admin = Admin.to_adapter.find_first(order: [:id, :desc])
- assert_equal admin.email, 'new_user@test.com'
+ assert_equal 'new_user@test.com', admin.email
end
- test 'a user sign up with valid information in XML format should return valid response' do
- post user_registration_path(format: 'xml'), params: { user: { email: 'new_user@test.com', password: 'new_user123', password_confirmation: 'new_user123' } }
+ test 'a user sign up with valid information in JSON format should return valid response' do
+ post user_registration_path(format: 'json'), params: { user: { email: 'new_user@test.com', password: 'new_user123', password_confirmation: 'new_user123' } }
assert_response :success
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"user":{'
user = User.to_adapter.find_first(order: [:id, :desc])
- assert_equal user.email, 'new_user@test.com'
+ assert_equal 'new_user@test.com', user.email
end
- test 'a user sign up with invalid information in XML format should return invalid response' do
- post user_registration_path(format: 'xml'), params: { user: { email: 'new_user@test.com', password: 'new_user123', password_confirmation: 'invalid' } }
+ test 'a user sign up with invalid information in JSON format should return invalid response' do
+ post user_registration_path(format: 'json'), params: { user: { email: 'new_user@test.com', password: 'new_user123', password_confirmation: 'invalid' } }
assert_response :unprocessable_entity
- assert response.body.include? %(\n)
+ assert_includes response.body, '{"errors":{'
end
- test 'a user update information with valid data in XML format should return valid response' do
+ test 'a user update information with valid data in JSON format should return valid response' do
user = sign_in_as_user
- put user_registration_path(format: 'xml'), params: { user: { current_password: '12345678', email: 'user.new@test.com' } }
+ put user_registration_path(format: 'json'), params: { user: { current_password: '12345678', email: 'user.new@test.com' } }
assert_response :success
- assert_equal user.reload.email, 'user.new@test.com'
+ assert_equal 'user.new@test.com', user.reload.email
end
- test 'a user update information with invalid data in XML format should return invalid response' do
+ test 'a user update information with invalid data in JSON format should return invalid response' do
user = sign_in_as_user
- put user_registration_path(format: 'xml'), params: { user: { current_password: 'invalid', email: 'user.new@test.com' } }
+ put user_registration_path(format: 'json'), params: { user: { current_password: 'invalid', email: 'user.new@test.com' } }
assert_response :unprocessable_entity
- assert_equal user.reload.email, 'user@test.com'
+ assert_equal 'user@test.com', user.reload.email
end
- test 'a user cancel their account in XML format should return valid response' do
+ test 'a user cancel their account in JSON format should return valid response' do
sign_in_as_user
- delete user_registration_path(format: 'xml')
+ delete user_registration_path(format: 'json')
assert_response :success
- assert_equal User.to_adapter.find_all.size, 0
+ assert_equal 0, User.to_adapter.find_all.size
end
end
diff --git a/test/integration/rememberable_test.rb b/test/integration/rememberable_test.rb
index cd6f2f10e..1fc4e4d58 100644
--- a/test/integration/rememberable_test.rb
+++ b/test/integration/rememberable_test.rb
@@ -3,7 +3,7 @@
require 'test_helper'
class RememberMeTest < Devise::IntegrationTest
- def create_user_and_remember(add_to_token='')
+ def create_user_and_remember(add_to_token = '')
user = create_user
user.remember_me!
raw_cookie = User.serialize_into_cookie(user).tap { |a| a[1] << add_to_token }
@@ -12,13 +12,7 @@ def create_user_and_remember(add_to_token='')
end
def generate_signed_cookie(raw_cookie)
- request = if Devise::Test.rails51? || Devise::Test.rails52_and_up?
- ActionController::TestRequest.create(Class.new) # needs a "controller class"
- elsif Devise::Test.rails5?
- ActionController::TestRequest.create
- else
- ActionController::TestRequest.new
- end
+ request = ActionController::TestRequest.create(Class.new) # needs a "controller class"
request.cookie_jar.signed['raw_cookie'] = raw_cookie
request.cookie_jar['raw_cookie']
end
@@ -41,12 +35,12 @@ def cookie_expires(key)
test 'handle unverified requests gets rid of caches' do
swap ApplicationController, allow_forgery_protection: true do
post exhibit_user_url(1)
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
create_user_and_remember
post exhibit_user_url(1)
assert_equal "User is not authenticated", response.body
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -59,8 +53,8 @@ def cookie_expires(key)
authenticity_token: "oops",
user: { email: "jose.valim@gmail.com", password: "123456", remember_me: "1" }
}
- refute warden.authenticated?(:user)
- refute request.cookies['remember_user_token']
+ assert_not warden.authenticated?(:user)
+ assert_not request.cookies['remember_user_token']
end
end
@@ -140,7 +134,7 @@ def cookie_expires(key)
get root_path
current_remember_token = request.cookies['remember_user_token']
- refute_equal old_remember_token, current_remember_token
+ assert_not_equal old_remember_token, current_remember_token
end
end
@@ -166,13 +160,13 @@ def cookie_expires(key)
get root_path
assert_response :success
assert warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:admin)
end
test 'do not remember with invalid token' do
create_user_and_remember('add')
get users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_redirected_to new_user_session_path
end
@@ -180,7 +174,7 @@ def cookie_expires(key)
create_user_and_remember
swap Devise, remember_for: 0.days do
get users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_redirected_to new_user_session_path
end
end
@@ -191,11 +185,11 @@ def cookie_expires(key)
assert warden.authenticated?(:user)
delete destroy_user_session_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert_nil warden.cookies['remember_user_token']
get users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'changing user password expires remember me token' do
@@ -205,7 +199,7 @@ def cookie_expires(key)
user.save!
get users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test 'valid sign in calls after_remembered callback' do
diff --git a/test/integration/timeoutable_test.rb b/test/integration/timeoutable_test.rb
index ceddd1086..670ddbbe8 100644
--- a/test/integration/timeoutable_test.rb
+++ b/test/integration/timeoutable_test.rb
@@ -58,7 +58,7 @@ def last_request_at
get users_path
assert_redirected_to users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert warden.authenticated?(:admin)
end
end
@@ -72,12 +72,12 @@ def last_request_at
assert_not_nil last_request_at
get root_path
- refute warden.authenticated?(:user)
- refute warden.authenticated?(:admin)
+ assert_not warden.authenticated?(:user)
+ assert_not warden.authenticated?(:admin)
end
end
- test 'time out user session after deault limit time and redirect to latest get request' do
+ test 'time out user session after default limit time and redirect to latest get request' do
user = sign_in_as_user
visit edit_form_user_path(user)
@@ -87,6 +87,26 @@ def last_request_at
assert_equal edit_form_user_url(user), current_url
end
+ test 'time out on non-GET request does not redirect to an external host supplied via the referer' do
+ user = sign_in_as_user
+ get expire_user_path(user)
+
+ put update_form_user_path(user), headers: { 'HTTP_REFERER' => 'http://evil.example/phishing' }
+
+ assert_response :redirect
+ assert_redirected_to '/phishing'
+ end
+
+ test 'time out on non-GET request with an opaque referer falls back to the sign in page' do
+ user = sign_in_as_user
+ get expire_user_path(user)
+
+ put update_form_user_path(user), headers: { 'HTTP_REFERER' => 'javascript:alert(1)' }
+
+ assert_response :redirect
+ assert_redirected_to new_user_session_path
+ end
+
test 'time out is not triggered on sign out' do
user = sign_in_as_user
get expire_user_path(user)
@@ -109,8 +129,8 @@ def last_request_at
follow_redirect!
assert_response :success
- assert_contain 'Sign in'
- refute warden.authenticated?(:user)
+ assert_contain 'Log in'
+ assert_not warden.authenticated?(:user)
end
test 'time out is not triggered on sign in' do
@@ -136,7 +156,7 @@ def last_request_at
get expire_user_path(user)
get users_path
assert_redirected_to users_path
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
end
@@ -167,6 +187,17 @@ def last_request_at
end
end
+ test 'error message redirect respects i18n locale set' do
+ user = sign_in_as_user
+
+ get expire_user_path(user)
+ get root_path(locale: "pt-BR")
+ follow_redirect!
+
+ assert_contain 'Sua sessão expirou. Por favor faça o login novamente para continuar.'
+ assert_not warden.authenticated?(:user)
+ end
+
test 'time out not triggered if remembered' do
user = sign_in_as_user remember_me: true
get expire_user_path(user)
@@ -180,7 +211,9 @@ def last_request_at
test 'does not crash when the last_request_at is a String' do
user = sign_in_as_user
- get edit_form_user_path(user, last_request_at: Time.now.utc.to_s)
- get users_path
+ assert_nothing_raised do
+ get edit_form_user_path(user, last_request_at: Time.now.utc.to_s)
+ get users_path
+ end
end
end
diff --git a/test/integration/trackable_test.rb b/test/integration/trackable_test.rb
index 6695ac52b..f5b6fcda8 100644
--- a/test/integration/trackable_test.rb
+++ b/test/integration/trackable_test.rb
@@ -6,7 +6,7 @@ class TrackableHooksTest < Devise::IntegrationTest
test "trackable should not run model validations" do
sign_in_as_user
- refute User.validations_performed
+ assert_not User.validations_performed
end
test "current and last sign in timestamps are updated on each sign in" do
@@ -44,7 +44,7 @@ class TrackableHooksTest < Devise::IntegrationTest
assert_equal "127.0.0.1", user.last_sign_in_ip
end
- test "current remote ip returns original ip behind a non transparent proxy" do
+ test "current and last sign in remote ip returns original ip behind a non transparent proxy" do
user = create_user
arbitrary_ip = '200.121.1.69'
@@ -53,6 +53,7 @@ class TrackableHooksTest < Devise::IntegrationTest
end
user.reload
assert_equal arbitrary_ip, user.current_sign_in_ip
+ assert_equal arbitrary_ip, user.last_sign_in_ip
end
test "increase sign in count" do
diff --git a/test/mailers/confirmation_instructions_test.rb b/test/mailers/confirmation_instructions_test.rb
index 358b092c1..5b4633121 100644
--- a/test/mailers/confirmation_instructions_test.rb
+++ b/test/mailers/confirmation_instructions_test.rb
@@ -31,7 +31,7 @@ def mail
end
test 'content type should be set to html' do
- assert mail.content_type.include?('text/html')
+ assert_includes mail.content_type, 'text/html'
end
test 'send confirmation instructions to the user email' do
@@ -88,7 +88,7 @@ def mail
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
if mail.body.encoded =~ %r{}
- assert_equal $1, user.confirmation_token
+ assert_equal user.confirmation_token, $1
else
flunk "expected confirmation url regex to match"
end
diff --git a/test/mailers/email_changed_test.rb b/test/mailers/email_changed_test.rb
index c82517f50..f32416545 100644
--- a/test/mailers/email_changed_test.rb
+++ b/test/mailers/email_changed_test.rb
@@ -35,7 +35,7 @@ def mail
end
test 'content type should be set to html' do
- assert mail.content_type.include?('text/html')
+ assert_includes mail.content_type, 'text/html'
end
test 'send email changed to the original user email' do
diff --git a/test/mailers/mailer_test.rb b/test/mailers/mailer_test.rb
index f8369052a..6f9f568e8 100644
--- a/test/mailers/mailer_test.rb
+++ b/test/mailers/mailer_test.rb
@@ -17,4 +17,30 @@ def confirmation_instructions(record, token, opts = {})
assert mail.content_transfer_encoding, "7bit"
end
+
+ test "default values defined as proc with different arity are handled correctly" do
+ class TestMailerWithDefault < Devise::Mailer
+ default from: -> { computed_from }
+ default reply_to: ->(_) { computed_reply_to }
+
+ def confirmation_instructions(record, token, opts = {})
+ @token = token
+ devise_mail(record, :confirmation_instructions, opts)
+ end
+
+ private
+
+ def computed_from
+ "from@example.com"
+ end
+
+ def computed_reply_to
+ "reply_to@example.com"
+ end
+ end
+
+ mail = TestMailerWithDefault.confirmation_instructions(create_user, "confirmation-token")
+ assert mail.from, "from@example.com"
+ assert mail.reply_to, "reply_to@example.com"
+ end
end
diff --git a/test/mailers/reset_password_instructions_test.rb b/test/mailers/reset_password_instructions_test.rb
index f0b458091..5a344cbf0 100644
--- a/test/mailers/reset_password_instructions_test.rb
+++ b/test/mailers/reset_password_instructions_test.rb
@@ -29,12 +29,12 @@ def mail
end
end
- test 'email sent after reseting the user password' do
+ test 'email sent after resetting the user password' do
assert_not_nil mail
end
test 'content type should be set to html' do
- assert mail.content_type.include?('text/html')
+ assert_includes mail.content_type, 'text/html'
end
test 'send confirmation instructions to the user email' do
@@ -84,7 +84,7 @@ def mail
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
if mail.body.encoded =~ %r{}
- assert_equal Devise.token_generator.digest(user.class, :reset_password_token, $1), user.reset_password_token
+ assert_equal user.reset_password_token, Devise.token_generator.digest(user.class, :reset_password_token, $1)
else
flunk "expected reset password url regex to match"
end
diff --git a/test/mailers/unlock_instructions_test.rb b/test/mailers/unlock_instructions_test.rb
index 586799da5..dff580e2e 100644
--- a/test/mailers/unlock_instructions_test.rb
+++ b/test/mailers/unlock_instructions_test.rb
@@ -35,7 +35,7 @@ def mail
end
test 'content type should be set to html' do
- assert mail.content_type.include?('text/html')
+ assert_includes mail.content_type, 'text/html'
end
test 'send unlock instructions to the user email' do
@@ -85,7 +85,7 @@ def mail
host, port = ActionMailer::Base.default_url_options.values_at :host, :port
if mail.body.encoded =~ %r{}
- assert_equal Devise.token_generator.digest(user.class, :unlock_token, $1), user.unlock_token
+ assert_equal user.unlock_token, Devise.token_generator.digest(user.class, :unlock_token, $1)
else
flunk "expected unlock url regex to match"
end
diff --git a/test/mapping_test.rb b/test/mapping_test.rb
index 0fc10c1a6..9d60287cd 100644
--- a/test/mapping_test.rb
+++ b/test/mapping_test.rb
@@ -6,7 +6,7 @@ class FakeRequest < Struct.new(:path_info, :params)
end
class MappingTest < ActiveSupport::TestCase
- def fake_request(path, params={})
+ def fake_request(path, params = {})
FakeRequest.new(path, params)
end
@@ -117,7 +117,7 @@ def user.devise_scope; :special_scope; end
assert mapping.authenticatable?
assert mapping.recoverable?
assert mapping.lockable?
- refute mapping.omniauthable?
+ assert_not mapping.omniauthable?
end
test 'find mapping by path' do
diff --git a/test/models/authenticatable_test.rb b/test/models/authenticatable_test.rb
index 4fc30a810..4d58b1e49 100644
--- a/test/models/authenticatable_test.rb
+++ b/test/models/authenticatable_test.rb
@@ -4,12 +4,12 @@
class AuthenticatableTest < ActiveSupport::TestCase
test 'required_fields should be an empty array' do
- assert_equal Devise::Models::Validatable.required_fields(User), []
+ assert_equal [], Devise::Models::Validatable.required_fields(User)
end
test 'find_first_by_auth_conditions allows custom filtering parameters' do
user = User.create!(email: "example@example.com", password: "1234567")
- assert_equal User.find_first_by_auth_conditions({ email: "example@example.com" }), user
+ assert_equal user, User.find_first_by_auth_conditions({ email: "example@example.com" })
assert_nil User.find_first_by_auth_conditions({ email: "example@example.com" }, id: user.id.to_s.next)
end
@@ -18,24 +18,24 @@ class AuthenticatableTest < ActiveSupport::TestCase
# config.strip_whitespace_keys = [:email]
test 'find_or_initialize_with_errors uses parameter filter on find' do
user = User.create!(email: "example@example.com", password: "1234567")
- assert_equal User.find_or_initialize_with_errors([:email], { email: " EXAMPLE@example.com " }), user
+ assert_equal user, User.find_or_initialize_with_errors([:email], { email: " EXAMPLE@example.com " })
end
# assumes default configuration of
# config.case_insensitive_keys = [:email]
# config.strip_whitespace_keys = [:email]
test 'find_or_initialize_with_errors uses parameter filter on initialize' do
- assert_equal User.find_or_initialize_with_errors([:email], { email: " EXAMPLE@example.com " }).email, "example@example.com"
+ assert_equal "example@example.com", User.find_or_initialize_with_errors([:email], { email: " EXAMPLE@example.com " }).email
end
test 'find_or_initialize_with_errors adds blank error' do
user_with_error = User.find_or_initialize_with_errors([:email], { email: "" })
- assert_equal [:email, "can't be blank"], user_with_error.errors.first
+ assert user_with_error.errors.added?(:email, :blank)
end
test 'find_or_initialize_with_errors adds invalid error' do
user_with_error = User.find_or_initialize_with_errors([:email], { email: "example@example.com" })
- assert_equal [:email, "is invalid"], user_with_error.errors.first
+ assert user_with_error.errors.added?(:email, :invalid)
end
if defined?(ActionController::Parameters)
@@ -43,7 +43,7 @@ class AuthenticatableTest < ActiveSupport::TestCase
user = create_user(email: 'example@example.com')
attributes = ActionController::Parameters.new(email: 'example@example.com')
- User.expects(:find_first_by_auth_conditions).with('email' => 'example@example.com').returns(user)
+ User.expects(:find_first_by_auth_conditions).with({ 'email' => 'example@example.com' }).returns(user)
User.find_or_initialize_with_errors([:email], attributes)
end
end
diff --git a/test/models/confirmable_test.rb b/test/models/confirmable_test.rb
index cab1d4f38..31a955e72 100644
--- a/test/models/confirmable_test.rb
+++ b/test/models/confirmable_test.rb
@@ -28,7 +28,7 @@ def setup
confirmation_tokens = []
3.times do
token = create_user.confirmation_token
- assert !confirmation_tokens.include?(token)
+ assert_not_includes confirmation_tokens, token
confirmation_tokens << token
end
end
@@ -41,9 +41,9 @@ def setup
end
test 'should verify whether a user is confirmed or not' do
- refute new_user.confirmed?
+ assert_not new_user.confirmed?
user = create_user
- refute user.confirmed?
+ assert_not user.confirmed?
user.confirm
assert user.confirmed?
end
@@ -53,7 +53,7 @@ def setup
assert user.confirm
assert_blank user.errors[:email]
- refute user.confirm
+ assert_not user.confirm
assert_equal "was already confirmed, please try signing in", user.errors[:email].join
end
@@ -61,20 +61,38 @@ def setup
user = create_user
raw = user.raw_confirmation_token
confirmed_user = User.confirm_by_token(raw)
- assert_equal confirmed_user, user
+ assert_equal user, confirmed_user
assert user.reload.confirmed?
end
test 'should return a new record with errors when a invalid token is given' do
confirmed_user = User.confirm_by_token('invalid_confirmation_token')
- refute confirmed_user.persisted?
+ assert_not confirmed_user.persisted?
assert_equal "is invalid", confirmed_user.errors[:confirmation_token].join
end
test 'should return a new record with errors when a blank token is given' do
confirmed_user = User.confirm_by_token('')
- refute confirmed_user.persisted?
- assert_equal "can't be blank", confirmed_user.errors[:confirmation_token].join
+ assert_not confirmed_user.persisted?
+ assert confirmed_user.errors.added?(:confirmation_token, :blank)
+ end
+
+ test 'should return a new record with errors when a blank token is given and a record exists on the database' do
+ user = create_user(confirmation_token: '')
+
+ confirmed_user = User.confirm_by_token('')
+
+ assert_not user.reload.confirmed?
+ assert confirmed_user.errors.added?(:confirmation_token, :blank)
+ end
+
+ test 'should return a new record with errors when a nil token is given and a record exists on the database' do
+ user = create_user(confirmation_token: nil)
+
+ confirmed_user = User.confirm_by_token(nil)
+
+ assert_not user.reload.confirmed?
+ assert confirmed_user.errors.added?(:confirmation_token, :blank)
end
test 'should generate errors for a user email if user is already confirmed' do
@@ -127,7 +145,7 @@ def setup
assert_email_not_sent do
user.save!
- refute user.confirmed?
+ assert_not user.confirmed?
end
end
@@ -142,12 +160,12 @@ def setup
test 'should find a user to send confirmation instructions' do
user = create_user
confirmation_user = User.send_confirmation_instructions(email: user.email)
- assert_equal confirmation_user, user
+ assert_equal user, confirmation_user
end
test 'should return a new user if no email was found' do
confirmation_user = User.send_confirmation_instructions(email: "invalid@example.com")
- refute confirmation_user.persisted?
+ assert_not confirmation_user.persisted?
end
test 'should add error to new user email if no email was found' do
@@ -194,7 +212,7 @@ def setup
test 'should not be able to send instructions if the user is already confirmed' do
user = create_user
user.confirm
- refute user.resend_confirmation_instructions
+ assert_not user.resend_confirmation_instructions
assert user.confirmed?
assert_equal 'was already confirmed, please try signing in', user.errors[:email].join
end
@@ -203,7 +221,7 @@ def setup
swap Devise, allow_unconfirmed_access_for: 1.day do
user = create_user
user.confirmation_sent_at = 2.days.ago
- refute user.active_for_authentication?
+ assert_not user.active_for_authentication?
Devise.allow_unconfirmed_access_for = 3.days
assert user.active_for_authentication?
@@ -219,14 +237,14 @@ def setup
assert user.active_for_authentication?
user.confirmation_sent_at = 5.days.ago
- refute user.active_for_authentication?
+ assert_not user.active_for_authentication?
end
end
test 'should be active when already confirmed' do
user = create_user
- refute user.confirmed?
- refute user.active_for_authentication?
+ assert_not user.confirmed?
+ assert_not user.active_for_authentication?
user.confirm
assert user.confirmed?
@@ -237,7 +255,7 @@ def setup
Devise.allow_unconfirmed_access_for = 0.days
user = create_user
user.confirmation_sent_at = Time.zone.today
- refute user.active_for_authentication?
+ assert_not user.active_for_authentication?
end
test 'should not be active when confirm period is set to 0 days' do
@@ -246,7 +264,7 @@ def setup
Timecop.freeze(Time.zone.today) do
user.confirmation_sent_at = Time.zone.today
- refute user.active_for_authentication?
+ assert_not user.active_for_authentication?
end
end
@@ -262,7 +280,7 @@ def setup
user = create_user
user.confirmation_sent_at = nil
user.save
- refute user.reload.active_for_authentication?
+ assert_not user.reload.active_for_authentication?
end
test 'should be active without confirmation when confirmation is not required' do
@@ -287,7 +305,7 @@ def setup
swap Devise, authentication_keys: [:username, :email] do
user = create_user
confirm_user = User.send_confirmation_instructions(email: user.email, username: user.username)
- assert_equal confirm_user, user
+ assert_equal user, confirm_user
end
end
@@ -295,8 +313,8 @@ def setup
swap Devise, confirmation_keys: [:username, :email] do
user = create_user
confirm_user = User.send_confirmation_instructions(email: user.email)
- refute confirm_user.persisted?
- assert_equal "can't be blank", confirm_user.errors[:username].join
+ assert_not confirm_user.persisted?
+ assert confirm_user.errors.added?(:username, :blank)
end
end
@@ -304,7 +322,7 @@ def confirm_user_by_token_with_confirmation_sent_at(confirmation_sent_at)
user = create_user
user.update_attribute(:confirmation_sent_at, confirmation_sent_at)
confirmed_user = User.confirm_by_token(user.raw_confirmation_token)
- assert_equal confirmed_user, user
+ assert_equal user, confirmed_user
user.reload.confirmed?
end
@@ -320,7 +338,7 @@ def confirm_user_by_token_with_confirmation_sent_at(confirmation_sent_at)
test 'should not accept confirmation email token after 4 days when expiration is set to 3 days' do
swap Devise, confirm_within: 3.days do
- refute confirm_user_by_token_with_confirmation_sent_at(4.days.ago)
+ assert_not confirm_user_by_token_with_confirmation_sent_at(4.days.ago)
end
end
@@ -360,14 +378,14 @@ def confirm_user_by_token_with_confirmation_sent_at(confirmation_sent_at)
self.username = self.username.to_s + 'updated'
end
old = user.username
- refute user.confirm
+ assert_not user.confirm
assert_equal user.username, old
end
test 'should always perform validations upon confirm when ensure valid true' do
admin = create_admin
admin.stubs(:valid?).returns(false)
- refute admin.confirm(ensure_valid: true)
+ assert_not admin.confirm(ensure_valid: true)
end
end
@@ -393,7 +411,7 @@ class ReconfirmableTest < ActiveSupport::TestCase
admin.skip_reconfirmation!
assert admin.update(email: 'new_test@example.com')
assert admin.confirmed?
- refute admin.pending_reconfirmation?
+ assert_not admin.pending_reconfirmation?
assert_equal original_token, admin.confirmation_token
end
@@ -479,12 +497,12 @@ class ReconfirmableTest < ActiveSupport::TestCase
assert admin.confirm
assert admin.update(email: 'new_test@example.com')
confirmation_admin = Admin.send_confirmation_instructions(email: admin.unconfirmed_email)
- assert_equal confirmation_admin, admin
+ assert_equal admin, confirmation_admin
end
test 'should return a new admin if no email or unconfirmed_email was found' do
confirmation_admin = Admin.send_confirmation_instructions(email: "invalid@email.com")
- refute confirmation_admin.persisted?
+ assert_not confirmation_admin.persisted?
end
test 'should add error to new admin email if no email or unconfirmed_email was found' do
@@ -502,25 +520,25 @@ class ReconfirmableTest < ActiveSupport::TestCase
end
test 'required_fields should contain the fields that Devise uses' do
- assert_equal Devise::Models::Confirmable.required_fields(User), [
+ assert_equal [
:confirmation_token,
:confirmed_at,
:confirmation_sent_at
- ]
+ ], Devise::Models::Confirmable.required_fields(User)
end
test 'required_fields should also contain unconfirmable when reconfirmable_email is true' do
- assert_equal Devise::Models::Confirmable.required_fields(Admin), [
+ assert_equal [
:confirmation_token,
:confirmed_at,
:confirmation_sent_at,
:unconfirmed_email
- ]
+ ], Devise::Models::Confirmable.required_fields(Admin)
end
test 'should not require reconfirmation after creating a record' do
admin = create_admin
- assert !admin.pending_reconfirmation?
+ assert_not admin.pending_reconfirmation?
end
test 'should not require reconfirmation after creating a record with #save called in callback' do
@@ -529,12 +547,12 @@ class Admin::WithSaveInCallback < Admin
end
admin = Admin::WithSaveInCallback.create(valid_attributes.except(:username))
- assert !admin.pending_reconfirmation?
+ assert_not admin.pending_reconfirmation?
end
test 'should require reconfirmation after creating a record and updating the email' do
admin = create_admin
- assert !admin.instance_variable_get(:@bypass_confirmation_postpone)
+ assert_not admin.instance_variable_get(:@bypass_confirmation_postpone)
admin.email = "new_test@email.com"
admin.save
assert admin.pending_reconfirmation?
diff --git a/test/models/database_authenticatable_test.rb b/test/models/database_authenticatable_test.rb
index 6eb6a0527..909e01045 100644
--- a/test/models/database_authenticatable_test.rb
+++ b/test/models/database_authenticatable_test.rb
@@ -97,8 +97,8 @@ def setup
test 'should respond to password and password confirmation' do
user = new_user
- assert user.respond_to?(:password)
- assert user.respond_to?(:password_confirmation)
+ assert_respond_to user, :password
+ assert_respond_to user, :password_confirmation
end
test 'should generate a hashed password while setting password' do
@@ -108,7 +108,7 @@ def setup
test 'should support custom hashing methods' do
user = UserWithCustomHashing.new(password: '654321')
- assert_equal user.encrypted_password, '123456'
+ assert_equal '123456', user.encrypted_password
end
test 'allow authenticatable_salt to work even with nil hashed password' do
@@ -133,7 +133,7 @@ def setup
test 'should test for a valid password' do
user = create_user
assert user.valid_password?('12345678')
- refute user.valid_password?('654321')
+ assert_not user.valid_password?('654321')
end
test 'should not raise error with an empty password' do
@@ -145,11 +145,11 @@ def setup
test 'should be an invalid password if the user has an empty password' do
user = create_user
user.encrypted_password = ''
- refute user.valid_password?('654321')
+ assert_not user.valid_password?('654321')
end
test 'should respond to current password' do
- assert new_user.respond_to?(:current_password)
+ assert_respond_to new_user, :current_password
end
test 'should update password with valid current password' do
@@ -161,7 +161,7 @@ def setup
test 'should add an error to current password when it is invalid' do
user = create_user
- refute user.update_with_password(current_password: 'other',
+ assert_not user.update_with_password(current_password: 'other',
password: 'pass4321', password_confirmation: 'pass4321')
assert user.reload.valid_password?('12345678')
assert_match "is invalid", user.errors[:current_password].join
@@ -169,19 +169,19 @@ def setup
test 'should add an error to current password when it is blank' do
user = create_user
- refute user.update_with_password(password: 'pass4321',
+ assert_not user.update_with_password(password: 'pass4321',
password_confirmation: 'pass4321')
assert user.reload.valid_password?('12345678')
- assert_match "can't be blank", user.errors[:current_password].join
+ assert user.errors.added?(:current_password, :blank)
end
test 'should run validations even when current password is invalid or blank' do
user = UserWithValidation.create!(valid_attributes)
user.save
assert user.persisted?
- refute user.update_with_password(username: "")
+ assert_not user.update_with_password(username: "")
assert_match "usertest", user.reload.username
- assert_match "can't be blank", user.errors[:username].join
+ assert user.errors.added?(:username, :blank)
end
test 'should ignore password and its confirmation if they are blank' do
@@ -192,14 +192,14 @@ def setup
test 'should not update password with invalid confirmation' do
user = create_user
- refute user.update_with_password(current_password: '12345678',
+ assert_not user.update_with_password(current_password: '12345678',
password: 'pass4321', password_confirmation: 'other')
assert user.reload.valid_password?('12345678')
end
test 'should clean up password fields on failure' do
user = create_user
- refute user.update_with_password(current_password: '12345678',
+ assert_not user.update_with_password(current_password: '12345678',
password: 'pass4321', password_confirmation: 'other')
assert user.password.blank?
assert user.password_confirmation.blank?
@@ -214,28 +214,28 @@ def setup
test 'should not update password without password' do
user = create_user
user.update_without_password(password: 'pass4321', password_confirmation: 'pass4321')
- assert !user.reload.valid_password?('pass4321')
+ assert_not user.reload.valid_password?('pass4321')
assert user.valid_password?('12345678')
end
test 'should destroy user if current password is valid' do
user = create_user
assert user.destroy_with_password('12345678')
- assert !user.persisted?
+ assert_not user.persisted?
end
test 'should not destroy user with invalid password' do
user = create_user
- refute user.destroy_with_password('other')
+ assert_not user.destroy_with_password('other')
assert user.persisted?
assert_match "is invalid", user.errors[:current_password].join
end
test 'should not destroy user with blank password' do
user = create_user
- refute user.destroy_with_password(nil)
+ assert_not user.destroy_with_password(nil)
assert user.persisted?
- assert_match "can't be blank", user.errors[:current_password].join
+ assert user.errors.added?(:current_password, :blank)
end
test 'should not email on password change' do
@@ -289,22 +289,22 @@ def setup
test 'downcase_keys with validation' do
User.create(email: "HEllO@example.com", password: "123456")
user = User.create(email: "HEllO@example.com", password: "123456")
- assert !user.valid?
+ assert_not user.valid?
end
test 'required_fields should be encryptable_password and the email field by default' do
- assert_equal Devise::Models::DatabaseAuthenticatable.required_fields(User), [
+ assert_equal [
:encrypted_password,
:email
- ]
+ ], Devise::Models::DatabaseAuthenticatable.required_fields(User)
end
test 'required_fields should be encryptable_password and the login when the login is on authentication_keys' do
swap Devise, authentication_keys: [:login] do
- assert_equal Devise::Models::DatabaseAuthenticatable.required_fields(User), [
+ assert_equal [
:encrypted_password,
:login
- ]
+ ], Devise::Models::DatabaseAuthenticatable.required_fields(User)
end
end
end
diff --git a/test/models/lockable_test.rb b/test/models/lockable_test.rb
index 644156a4e..b1d8cab0d 100644
--- a/test/models/lockable_test.rb
+++ b/test/models/lockable_test.rb
@@ -34,7 +34,7 @@ def setup
user.confirm
swap Devise, lock_strategy: :none, maximum_attempts: 2 do
3.times { user.valid_for_authentication?{ false } }
- assert !user.access_locked?
+ assert_not user.access_locked?
assert_equal 0, user.failed_attempts
end
end
@@ -50,6 +50,32 @@ def setup
assert_equal initial_failed_attempts + 2, user.reload.failed_attempts
end
+ test "reset_failed_attempts! updates the failed attempts counter back to 0" do
+ user = create_user(failed_attempts: 3)
+ assert_equal 3, user.failed_attempts
+
+ user.reset_failed_attempts!
+ assert_equal 0, user.failed_attempts
+
+ user.reset_failed_attempts!
+ assert_equal 0, user.failed_attempts
+ end
+
+ test "reset_failed_attempts! does not run model validations" do
+ user = create_user(failed_attempts: 1)
+ user.expects(:after_validation_callback).never
+
+ assert user.reset_failed_attempts!
+ assert_equal 0, user.failed_attempts
+ end
+
+ test "reset_failed_attempts! does not try to reset if not using failed attempts strategy" do
+ admin = create_admin
+
+ assert_not_respond_to admin, :failed_attempts
+ assert_not admin.reset_failed_attempts!
+ end
+
test 'should be valid for authentication with a unlocked user' do
user = create_user
user.lock_access!
@@ -59,7 +85,7 @@ def setup
test "should verify whether a user is locked or not" do
user = create_user
- refute user.access_locked?
+ assert_not user.access_locked?
user.lock_access!
assert user.access_locked?
end
@@ -69,7 +95,7 @@ def setup
user.confirm
assert user.active_for_authentication?
user.lock_access!
- refute user.active_for_authentication?
+ assert_not user.active_for_authentication?
end
test "should unlock a user by cleaning locked_at, failed_attempts and unlock_token" do
@@ -85,7 +111,7 @@ def setup
end
test "new user should not be locked and should have zero failed_attempts" do
- refute new_user.access_locked?
+ assert_not new_user.access_locked?
assert_equal 0, create_user.failed_attempts
end
@@ -96,7 +122,7 @@ def setup
assert user.access_locked?
Devise.unlock_in = 1.hour
- refute user.access_locked?
+ assert_not user.access_locked?
end
end
@@ -121,7 +147,7 @@ def setup
user = create_user
user.lock_access!
token = user.unlock_token
- assert !unlock_tokens.include?(token)
+ assert_not_includes unlock_tokens, token
unlock_tokens << token
end
end
@@ -174,32 +200,32 @@ def setup
user = create_user
raw = user.send_unlock_instructions
locked_user = User.unlock_access_by_token(raw)
- assert_equal locked_user, user
- refute user.reload.access_locked?
+ assert_equal user, locked_user
+ assert_not user.reload.access_locked?
end
test 'should return a new record with errors when a invalid token is given' do
locked_user = User.unlock_access_by_token('invalid_token')
- refute locked_user.persisted?
+ assert_not locked_user.persisted?
assert_equal "is invalid", locked_user.errors[:unlock_token].join
end
test 'should return a new record with errors when a blank token is given' do
locked_user = User.unlock_access_by_token('')
- refute locked_user.persisted?
- assert_equal "can't be blank", locked_user.errors[:unlock_token].join
+ assert_not locked_user.persisted?
+ assert locked_user.errors.added?(:unlock_token, :blank)
end
test 'should find a user to send unlock instructions' do
user = create_user
user.lock_access!
unlock_user = User.send_unlock_instructions(email: user.email)
- assert_equal unlock_user, user
+ assert_equal user, unlock_user
end
test 'should return a new user if no email was found' do
unlock_user = User.send_unlock_instructions(email: "invalid@example.com")
- refute unlock_user.persisted?
+ assert_not unlock_user.persisted?
end
test 'should add error to new user email if no email was found' do
@@ -211,7 +237,7 @@ def setup
swap Devise, authentication_keys: [:username, :email] do
user = create_user
unlock_user = User.send_unlock_instructions(email: user.email, username: user.username)
- assert_equal unlock_user, user
+ assert_equal user, unlock_user
end
end
@@ -219,23 +245,23 @@ def setup
swap Devise, unlock_keys: [:username, :email] do
user = create_user
unlock_user = User.send_unlock_instructions(email: user.email)
- refute unlock_user.persisted?
- assert_equal "can't be blank", unlock_user.errors[:username].join
+ assert_not unlock_user.persisted?
+ assert unlock_user.errors.added?(:username, :blank)
end
end
test 'should not be able to send instructions if the user is not locked' do
user = create_user
- refute user.resend_unlock_instructions
- refute user.access_locked?
+ assert_not user.resend_unlock_instructions
+ assert_not user.access_locked?
assert_equal 'was not locked', user.errors[:email].join
end
test 'should not be able to send instructions if the user if not locked and have username as unlock key' do
swap Devise, unlock_keys: [:username] do
user = create_user
- refute user.resend_unlock_instructions
- refute user.access_locked?
+ assert_not user.resend_unlock_instructions
+ assert_not user.access_locked?
assert_equal 'was not locked', user.errors[:username].join
end
end
@@ -270,11 +296,11 @@ def setup
test 'required_fields should contain the all the fields when all the strategies are enabled' do
swap Devise, unlock_strategy: :both do
swap Devise, lock_strategy: :failed_attempts do
- assert_equal Devise::Models::Lockable.required_fields(User), [
- :failed_attempts,
- :locked_at,
- :unlock_token
- ]
+ assert_equal [
+ :failed_attempts,
+ :locked_at,
+ :unlock_token
+ ], Devise::Models::Lockable.required_fields(User)
end
end
end
@@ -282,10 +308,10 @@ def setup
test 'required_fields should contain only failed_attempts and locked_at when the strategies are time and failed_attempts are enabled' do
swap Devise, unlock_strategy: :time do
swap Devise, lock_strategy: :failed_attempts do
- assert_equal Devise::Models::Lockable.required_fields(User), [
- :failed_attempts,
- :locked_at
- ]
+ assert_equal [
+ :failed_attempts,
+ :locked_at
+ ], Devise::Models::Lockable.required_fields(User)
end
end
end
@@ -293,10 +319,10 @@ def setup
test 'required_fields should contain only failed_attempts and unlock_token when the strategies are token and failed_attempts are enabled' do
swap Devise, unlock_strategy: :email do
swap Devise, lock_strategy: :failed_attempts do
- assert_equal Devise::Models::Lockable.required_fields(User), [
- :failed_attempts,
- :unlock_token
- ]
+ assert_equal [
+ :failed_attempts,
+ :unlock_token
+ ], Devise::Models::Lockable.required_fields(User)
end
end
end
diff --git a/test/models/omniauthable_test.rb b/test/models/omniauthable_test.rb
index c22bc4308..22cea976c 100644
--- a/test/models/omniauthable_test.rb
+++ b/test/models/omniauthable_test.rb
@@ -4,6 +4,6 @@
class OmniauthableTest < ActiveSupport::TestCase
test 'required_fields should contain the fields that Devise uses' do
- assert_equal Devise::Models::Omniauthable.required_fields(User), []
+ assert_equal [], Devise::Models::Omniauthable.required_fields(User)
end
end
diff --git a/test/models/recoverable_test.rb b/test/models/recoverable_test.rb
index 919e6e486..b2234ac6a 100644
--- a/test/models/recoverable_test.rb
+++ b/test/models/recoverable_test.rb
@@ -18,7 +18,7 @@ def setup
user = create_user
user.send_reset_password_instructions
token = user.reset_password_token
- assert !reset_password_tokens.include?(token)
+ assert_not_includes reset_password_tokens, token
reset_password_tokens << token
end
end
@@ -34,7 +34,7 @@ def setup
assert create_user.reset_password('123456789', '123456789')
end
- test 'should clear reset password token while reseting the password' do
+ test 'should clear reset password token while resetting the password' do
user = create_user
assert_nil user.reset_password_token
@@ -94,14 +94,14 @@ def setup
user = create_user
user.send_reset_password_instructions
assert_present user.reset_password_token
- refute user.reset_password('123456789', '987654321')
+ assert_not user.reset_password('123456789', '987654321')
assert_present user.reset_password_token
end
test 'should not reset password with invalid data' do
user = create_user
user.stubs(:valid?).returns(false)
- refute user.reset_password('123456789', '987654321')
+ assert_not user.reset_password('123456789', '987654321')
end
test 'should reset reset password token and send instructions by email' do
@@ -116,12 +116,12 @@ def setup
test 'should find a user to send instructions by email' do
user = create_user
reset_password_user = User.send_reset_password_instructions(email: user.email)
- assert_equal reset_password_user, user
+ assert_equal user, reset_password_user
end
test 'should return a new record with errors if user was not found by e-mail' do
reset_password_user = User.send_reset_password_instructions(email: "invalid@example.com")
- refute reset_password_user.persisted?
+ assert_not reset_password_user.persisted?
assert_equal "not found", reset_password_user.errors[:email].join
end
@@ -129,17 +129,17 @@ def setup
swap Devise, authentication_keys: [:username, :email] do
user = create_user
reset_password_user = User.send_reset_password_instructions(email: user.email, username: user.username)
- assert_equal reset_password_user, user
+ assert_equal user, reset_password_user
end
end
test 'should require all reset_password_keys' do
- swap Devise, reset_password_keys: [:username, :email] do
- user = create_user
- reset_password_user = User.send_reset_password_instructions(email: user.email)
- refute reset_password_user.persisted?
- assert_equal "can't be blank", reset_password_user.errors[:username].join
- end
+ swap Devise, reset_password_keys: [:username, :email] do
+ user = create_user
+ reset_password_user = User.send_reset_password_instructions(email: user.email)
+ assert_not reset_password_user.persisted?
+ assert reset_password_user.errors.added?(:username, :blank)
+ end
end
test 'should reset reset_password_token before send the reset instructions email' do
@@ -161,19 +161,19 @@ def setup
raw = user.send_reset_password_instructions
reset_password_user = User.reset_password_by_token(reset_password_token: raw)
- assert_equal reset_password_user, user
+ assert_equal user, reset_password_user
end
test 'should return a new record with errors if no reset_password_token is found' do
reset_password_user = User.reset_password_by_token(reset_password_token: 'invalid_token')
- refute reset_password_user.persisted?
+ assert_not reset_password_user.persisted?
assert_equal "is invalid", reset_password_user.errors[:reset_password_token].join
end
test 'should return a new record with errors if reset_password_token is blank' do
reset_password_user = User.reset_password_by_token(reset_password_token: '')
- refute reset_password_user.persisted?
- assert_match "can't be blank", reset_password_user.errors[:reset_password_token].join
+ assert_not reset_password_user.persisted?
+ assert reset_password_user.errors.added?(:reset_password_token, :blank)
end
test 'should return a new record with errors if password is blank' do
@@ -181,8 +181,8 @@ def setup
raw = user.send_reset_password_instructions
reset_password_user = User.reset_password_by_token(reset_password_token: raw, password: '')
- refute reset_password_user.errors.empty?
- assert_match "can't be blank", reset_password_user.errors[:password].join
+ assert_not reset_password_user.errors.empty?
+ assert reset_password_user.errors.added?(:password, :blank)
assert_equal raw, reset_password_user.reset_password_token
end
@@ -191,8 +191,8 @@ def setup
raw = user.send_reset_password_instructions
reset_password_user = User.reset_password_by_token(reset_password_token: raw)
- refute reset_password_user.errors.empty?
- assert_match "can't be blank", reset_password_user.errors[:password].join
+ assert_not reset_password_user.errors.empty?
+ assert reset_password_user.errors.added?(:password, :blank)
assert_equal raw, reset_password_user.reset_password_token
end
@@ -209,7 +209,7 @@ def setup
assert_nil reset_password_user.reset_password_token
user.reload
- refute user.valid_password?(old_password)
+ assert_not user.valid_password?(old_password)
assert user.valid_password?('new_password')
assert_nil user.reset_password_token
end
@@ -231,29 +231,29 @@ def setup
user.reload
assert user.valid_password?(old_password)
- refute user.valid_password?('new_password')
+ assert_not user.valid_password?('new_password')
assert_equal "has expired, please request a new one", reset_password_user.errors[:reset_password_token].join
end
end
test 'required_fields should contain the fields that Devise uses' do
- assert_equal Devise::Models::Recoverable.required_fields(User), [
+ assert_equal [
:reset_password_sent_at,
:reset_password_token
- ]
+ ], Devise::Models::Recoverable.required_fields(User)
end
test 'should return a user based on the raw token' do
user = create_user
raw = user.send_reset_password_instructions
- assert_equal User.with_reset_password_token(raw), user
+ assert_equal user, User.with_reset_password_token(raw)
end
test 'should return the same reset password token as generated' do
user = create_user
raw = user.send_reset_password_instructions
- assert_equal Devise.token_generator.digest(self.class, :reset_password_token, raw), user.reset_password_token
+ assert_equal user.reset_password_token, Devise.token_generator.digest(self.class, :reset_password_token, raw)
end
test 'should return nil if a user based on the raw token is not found' do
diff --git a/test/models/registerable_test.rb b/test/models/registerable_test.rb
index 254934168..df5cf6fcf 100644
--- a/test/models/registerable_test.rb
+++ b/test/models/registerable_test.rb
@@ -4,6 +4,6 @@
class RegisterableTest < ActiveSupport::TestCase
test 'required_fields should contain the fields that Devise uses' do
- assert_equal Devise::Models::Registerable.required_fields(User), []
+ assert_equal [], Devise::Models::Registerable.required_fields(User)
end
end
diff --git a/test/models/rememberable_test.rb b/test/models/rememberable_test.rb
index 24ebb3feb..8b8317212 100644
--- a/test/models/rememberable_test.rb
+++ b/test/models/rememberable_test.rb
@@ -129,8 +129,8 @@ def user.authenticatable_salt; ""; end
end
test 'should respond to remember_me attribute' do
- assert resource_class.new.respond_to?(:remember_me)
- assert resource_class.new.respond_to?(:remember_me=)
+ assert_respond_to resource_class.new, :remember_me
+ assert_respond_to resource_class.new, :remember_me=
end
test 'forget_me should clear remember_created_at if expire_all_remember_me_on_sign_out is true' do
@@ -177,8 +177,8 @@ def user.authenticatable_salt; ""; end
end
test 'should have the required_fields array' do
- assert_equal Devise::Models::Rememberable.required_fields(User), [
+ assert_equal [
:remember_created_at
- ]
+ ], Devise::Models::Rememberable.required_fields(User)
end
end
diff --git a/test/models/serializable_test.rb b/test/models/serializable_test.rb
index 0ec9e7206..024ccf449 100644
--- a/test/models/serializable_test.rb
+++ b/test/models/serializable_test.rb
@@ -7,21 +7,6 @@ class SerializableTest < ActiveSupport::TestCase
@user = create_user
end
- test 'should not include unsafe keys on XML' do
- assert_match(/email/, @user.to_xml)
- assert_no_match(/confirmation-token/, @user.to_xml)
- end
-
- test 'should not include unsafe keys on XML even if a new except is provided' do
- assert_no_match(/email/, @user.to_xml(except: :email))
- assert_no_match(/confirmation-token/, @user.to_xml(except: :email))
- end
-
- test 'should include unsafe keys on XML if a force_except is provided' do
- assert_no_match(/ ""
+ render body: ""
end
end
diff --git a/test/rails_app/app/controllers/users_controller.rb b/test/rails_app/app/controllers/users_controller.rb
index 384aa67a7..b3b49ee2a 100644
--- a/test/rails_app/app/controllers/users_controller.rb
+++ b/test/rails_app/app/controllers/users_controller.rb
@@ -3,7 +3,8 @@
class UsersController < ApplicationController
prepend_before_action :current_user, only: :exhibit
before_action :authenticate_user!, except: [:accept, :exhibit]
- respond_to :html, :xml
+ clear_respond_to
+ respond_to :html, :json
def index
user_session[:cart] = "Cart"
@@ -15,7 +16,7 @@ def edit_form
end
def update_form
- render (Devise::Test.rails5_and_up? ? :body : :text) => 'Update'
+ render body: 'Update'
end
def accept
@@ -23,11 +24,11 @@ def accept
end
def exhibit
- render (Devise::Test.rails5_and_up? ? :body : :text) => current_user ? "User is authenticated" : "User is not authenticated"
+ render body: current_user ? "User is authenticated" : "User is not authenticated"
end
def expire
user_session['last_request_at'] = 31.minutes.ago.utc
- render (Devise::Test.rails5_and_up? ? :body : :text) => 'User will be expired on next request'
+ render body: 'User will be expired on next request'
end
end
diff --git a/test/rails_app/app/views/admins/sessions/new.html.erb b/test/rails_app/app/views/admins/sessions/new.html.erb
index 75f3b860f..f3be6278e 100644
--- a/test/rails_app/app/views/admins/sessions/new.html.erb
+++ b/test/rails_app/app/views/admins/sessions/new.html.erb
@@ -1,2 +1,2 @@
Welcome to "sessions/new" view!
-<%= render file: "devise/sessions/new" %>
+<%= render template: "devise/sessions/new" %>
diff --git a/test/rails_app/config/application.rb b/test/rails_app/config/application.rb
index d39fa7dd6..fc3b171d0 100644
--- a/test/rails_app/config/application.rb
+++ b/test/rails_app/config/application.rb
@@ -2,6 +2,7 @@
require File.expand_path('../boot', __FILE__)
+require "logger"
require "action_controller/railtie"
require "action_mailer/railtie"
require "rails/test_unit/railtie"
@@ -33,21 +34,20 @@ class Application < Rails::Application
# config.assets.enabled = false
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
- rails_version = Gem::Version.new(Rails.version)
- if DEVISE_ORM == :active_record &&
- rails_version >= Gem::Version.new('4.2.0') &&
- rails_version < Gem::Version.new('5.1.0')
- config.active_record.raise_in_transactional_callbacks = true
- end
# This was used to break devise in some situations
config.to_prepare do
Devise::SessionsController.layout "application"
end
- # Remove this check once Rails 5.0 support is removed.
- if Devise::Test.rails52_and_up?
- Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ if DEVISE_ORM == :active_record
+ if Devise::Test.rails70?
+ config.active_record.legacy_connection_handling = false
+ end
+ end
+
+ if Devise::Test.rails70_and_up?
+ config.active_support.cache_format_version = 7.0
end
end
end
diff --git a/test/rails_app/config/boot.rb b/test/rails_app/config/boot.rb
index 01621de75..e328fce49 100644
--- a/test/rails_app/config/boot.rb
+++ b/test/rails_app/config/boot.rb
@@ -6,26 +6,18 @@
module Devise
module Test
- # Detection for minor differences between Rails 4 and 5, 5.1, and 5.2 in tests.
-
- def self.rails52_and_up?
- Rails::VERSION::MAJOR > 5 || rails52?
- end
-
- def self.rails52?
- Rails.version.start_with? '5.2'
- end
+ # Detection for minor differences between Rails versions in tests.
- def self.rails51?
- Rails.version.start_with? '5.1'
+ def self.rails71_and_up?
+ !rails70? && Rails::VERSION::MAJOR >= 7
end
- def self.rails5_and_up?
- Rails::VERSION::MAJOR >= 5
+ def self.rails70_and_up?
+ Rails::VERSION::MAJOR >= 7
end
- def self.rails5?
- Rails.version.start_with? '5'
+ def self.rails70?
+ Rails.version.start_with? '7.0'
end
end
end
diff --git a/test/rails_app/config/environments/production.rb b/test/rails_app/config/environments/production.rb
index 3a3be4784..17c5b266f 100644
--- a/test/rails_app/config/environments/production.rb
+++ b/test/rails_app/config/environments/production.rb
@@ -22,13 +22,7 @@
# config.action_dispatch.rack_cache = true
# Disable Rails's static asset server (Apache or nginx will already do this).
- if Devise::Test.rails5_and_up?
- config.public_file_server.enabled = false
- elsif Rails.version >= "4.2.0"
- config.serve_static_files = false
- else
- config.serve_static_assets = false
- end
+ config.public_file_server.enabled = false
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
diff --git a/test/rails_app/config/environments/test.rb b/test/rails_app/config/environments/test.rb
index c5d393c5e..acc06f31f 100644
--- a/test/rails_app/config/environments/test.rb
+++ b/test/rails_app/config/environments/test.rb
@@ -16,23 +16,19 @@
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
- if Devise::Test.rails5_and_up?
- config.public_file_server.enabled = true
- config.public_file_server.headers = {'Cache-Control' => 'public, max-age=3600'}
- elsif Rails.version >= "4.2.0"
- config.serve_static_files = true
- config.static_cache_control = "public, max-age=3600"
- else
- config.serve_static_assets = true
- config.static_cache_control = "public, max-age=3600"
- end
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {'Cache-Control' => 'public, max-age=3600'}
# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
# Raise exceptions instead of rendering exception templates.
- config.action_dispatch.show_exceptions = false
+ if Devise::Test.rails71_and_up?
+ config.action_dispatch.show_exceptions = :none
+ else
+ config.action_dispatch.show_exceptions = false
+ end
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
diff --git a/test/rails_app/config/initializers/devise.rb b/test/rails_app/config/initializers/devise.rb
index 0ce41964b..85fdfe0ae 100644
--- a/test/rails_app/config/initializers/devise.rb
+++ b/test/rails_app/config/initializers/devise.rb
@@ -3,6 +3,12 @@
require "omniauth-facebook"
require "omniauth-openid"
+# Assuming you have not yet modified this file, each configuration option below
+# is set to its default value. Note that some are commented out while others
+# are not: uncommented lines are intended to protect your configuration from
+# breaking changes in upgrades (i.e., in the event that future versions of
+# Devise change the default values for those options).
+#
# Use this hook to configure devise mailer, warden hooks and so forth. The first
# four configuration values can also be set straight in your models.
Devise.setup do |config|
@@ -175,9 +181,9 @@
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
- # config.warden do |manager|
- # manager.failure_app = AnotherApp
- # manager.default_strategies(scope: :user).unshift :some_external_strategy
+ # config.warden do |warden_config|
+ # warden_config.failure_app = AnotherApp
+ # warden_config.default_strategies(scope: :user).unshift :some_external_strategy
# end
# ==> Configuration for :registerable
diff --git a/test/rails_app/config/routes.rb b/test/rails_app/config/routes.rb
index 8687dae24..0b748f3fd 100644
--- a/test/rails_app/config/routes.rb
+++ b/test/rails_app/config/routes.rb
@@ -17,6 +17,8 @@
resources :admins, only: [:index]
+ resources :streaming, only: [:index]
+
# Users scope
devise_for :users, controllers: { omniauth_callbacks: "users/omniauth_callbacks" }
diff --git a/test/rails_app/db/migrate/20100401102949_create_tables.rb b/test/rails_app/db/migrate/20100401102949_create_tables.rb
index 43c6d867b..8d46b3e5c 100644
--- a/test/rails_app/db/migrate/20100401102949_create_tables.rb
+++ b/test/rails_app/db/migrate/20100401102949_create_tables.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
-superclass = ActiveRecord::Migration
-# TODO: Inherit from the 5.0 Migration class directly when we drop support for Rails 4.
-superclass = ActiveRecord::Migration[5.0] if superclass.respond_to?(:[])
-
-class CreateTables < superclass
+class CreateTables < ActiveRecord::Migration[5.0]
def self.up
create_table :users do |t|
t.string :username
diff --git a/test/rails_app/lib/shared_admin.rb b/test/rails_app/lib/shared_admin.rb
index 3e6362a78..374666ff5 100644
--- a/test/rails_app/lib/shared_admin.rb
+++ b/test/rails_app/lib/shared_admin.rb
@@ -10,11 +10,7 @@ module SharedAdmin
allow_unconfirmed_access_for: 2.weeks, reconfirmable: true
validates_length_of :reset_password_token, minimum: 3, allow_blank: true
- if Devise::Test.rails51?
- validates_uniqueness_of :email, allow_blank: true, if: :will_save_change_to_email?
- else
- validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
- end
+ validates_uniqueness_of :email, allow_blank: true, if: :devise_will_save_change_to_email?
end
def raw_confirmation_token
diff --git a/test/rails_app/lib/shared_user_without_email.rb b/test/rails_app/lib/shared_user_without_email.rb
index f030c195d..5a86729ff 100644
--- a/test/rails_app/lib/shared_user_without_email.rb
+++ b/test/rails_app/lib/shared_user_without_email.rb
@@ -21,7 +21,7 @@ def email_changed?
raise NoMethodError
end
- def respond_to?(method_name, include_all=false)
+ def respond_to?(method_name, include_all = false)
return false if method_name.to_sym == :email_changed?
super(method_name, include_all)
end
diff --git a/test/rails_test.rb b/test/rails_test.rb
index fdc1612e8..64ff82cda 100644
--- a/test/rails_test.rb
+++ b/test/rails_test.rb
@@ -8,4 +8,10 @@ class RailsTest < ActiveSupport::TestCase
assert_equal :load_config_initializers, initializer.after
assert_equal :build_middleware_stack, initializer.before
end
+
+ if Devise::Test.rails71_and_up?
+ test 'deprecator is added to application deprecators' do
+ assert_not_nil Rails.application.deprecators[:devise]
+ end
+ end
end
diff --git a/test/routes_test.rb b/test/routes_test.rb
index 4f6233afc..20ba31172 100644
--- a/test/routes_test.rb
+++ b/test/routes_test.rb
@@ -2,7 +2,7 @@
require 'test_helper'
-ExpectedRoutingError = MiniTest::Assertion
+ExpectedRoutingError = Minitest::Assertion
class DefaultRoutingTest < ActionController::TestCase
test 'map new user session' do
@@ -205,56 +205,51 @@ class CustomizedRoutingTest < ActionController::TestCase
test 'map with format false for sessions' do
expected_params = {controller: 'devise/sessions', action: 'new'}
- expected_params[:format] = false if Devise::Test.rails5_and_up?
assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_in', method: :get})
assert_raise ExpectedRoutingError do
- assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_in.xml', method: :get})
+ assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_in.json', method: :get})
end
end
test 'map with format false for passwords' do
expected_params = {controller: 'devise/passwords', action: 'create'}
- expected_params[:format] = false if Devise::Test.rails5_and_up?
assert_recognizes(expected_params, {path: '/htmlonly_admin/password', method: :post})
assert_raise ExpectedRoutingError do
- assert_recognizes(expected_params, {path: '/htmlonly_admin/password.xml', method: :post})
+ assert_recognizes(expected_params, {path: '/htmlonly_admin/password.json', method: :post})
end
end
test 'map with format false for registrations' do
expected_params = {controller: 'devise/registrations', action: 'new'}
- expected_params[:format] = false if Devise::Test.rails5_and_up?
assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_up', method: :get})
assert_raise ExpectedRoutingError do
- assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_up.xml', method: :get})
+ assert_recognizes(expected_params, {path: '/htmlonly_admin/sign_up.json', method: :get})
end
end
test 'map with format false for confirmations' do
expected_params = {controller: 'devise/confirmations', action: 'show'}
- expected_params[:format] = false if Devise::Test.rails5_and_up?
assert_recognizes(expected_params, {path: '/htmlonly_users/confirmation', method: :get})
assert_raise ExpectedRoutingError do
- assert_recognizes(expected_params, {path: '/htmlonly_users/confirmation.xml', method: :get})
+ assert_recognizes(expected_params, {path: '/htmlonly_users/confirmation.json', method: :get})
end
end
test 'map with format false for unlocks' do
expected_params = {controller: 'devise/unlocks', action: 'show'}
- expected_params[:format] = false if Devise::Test.rails5_and_up?
assert_recognizes(expected_params, {path: '/htmlonly_users/unlock', method: :get})
assert_raise ExpectedRoutingError do
- assert_recognizes(expected_params, {path: '/htmlonly_users/unlock.xml', method: :get})
+ assert_recognizes(expected_params, {path: '/htmlonly_users/unlock.json', method: :get})
end
end
test 'map with format false is not permanent' do
- assert_equal "/set.xml", @routes.url_helpers.set_path(:xml)
+ assert_equal "/set.json", @routes.url_helpers.set_path(:json)
end
test 'checks if mapping has proper configuration for omniauth callback' do
diff --git a/test/secret_key_finder_test.rb b/test/secret_key_finder_test.rb
deleted file mode 100644
index 434dbc8d1..000000000
--- a/test/secret_key_finder_test.rb
+++ /dev/null
@@ -1,121 +0,0 @@
-# frozen_string_literal: true
-
-require 'test_helper'
-
-class Rails52Credentials
- def credentials
- OpenStruct.new(secret_key_base: 'credentials')
- end
-end
-
-class Rails52Secrets
- def credentials
- OpenStruct.new(secret_key_base: nil)
- end
-
- def secrets
- OpenStruct.new(secret_key_base: 'secrets')
- end
-end
-
-class Rails52Config
- def credentials
- OpenStruct.new(secret_key_base: nil)
- end
-
- def secrets
- OpenStruct.new(secret_key_base: nil)
- end
-
- def config
- OpenStruct.new(secret_key_base: 'config')
- end
-end
-
-class Rails52SecretKeyBase
- def credentials
- OpenStruct.new(secret_key_base: nil)
- end
-
- def secrets
- OpenStruct.new(secret_key_base: nil)
- end
-
- def config
- OpenStruct.new(secret_key_base: nil)
- end
-
- def secret_key_base
- 'secret_key_base'
- end
-end
-
-class Rails41Secrets
- def secrets
- OpenStruct.new(secret_key_base: 'secrets')
- end
-
- def config
- OpenStruct.new(secret_key_base: nil)
- end
-end
-
-class Rails41Config
- def secrets
- OpenStruct.new(secret_key_base: nil)
- end
-
- def config
- OpenStruct.new(secret_key_base: 'config')
- end
-end
-
-class Rails40Config
- def config
- OpenStruct.new(secret_key_base: 'config')
- end
-end
-
-class SecretKeyFinderTest < ActiveSupport::TestCase
- test "rails 5.2 uses credentials when they're available" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails52Credentials.new)
-
- assert_equal 'credentials', secret_key_finder.find
- end
-
- test "rails 5.2 uses secrets when credentials are empty" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails52Secrets.new)
-
- assert_equal 'secrets', secret_key_finder.find
- end
-
- test "rails 5.2 uses config when secrets are empty" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails52Config.new)
-
- assert_equal 'config', secret_key_finder.find
- end
-
- test "rails 5.2 uses secret_key_base when config is empty" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails52SecretKeyBase.new)
-
- assert_equal 'secret_key_base', secret_key_finder.find
- end
-
- test "rails 4.1 uses secrets" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails41Secrets.new)
-
- assert_equal 'secrets', secret_key_finder.find
- end
-
- test "rails 4.1 uses config when secrets are empty" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails41Config.new)
-
- assert_equal 'config', secret_key_finder.find
- end
-
- test "rails 4.0 uses config" do
- secret_key_finder = Devise::SecretKeyFinder.new(Rails40Config.new)
-
- assert_equal 'config', secret_key_finder.find
- end
-end
diff --git a/test/support/helpers.rb b/test/support/helpers.rb
index fe7cf1b57..01dc6aa56 100644
--- a/test/support/helpers.rb
+++ b/test/support/helpers.rb
@@ -3,8 +3,6 @@
require 'active_support/test_case'
class ActiveSupport::TestCase
- VALID_AUTHENTICATION_TOKEN = 'AbCdEfGhIjKlMnOpQrSt'.freeze
-
def setup_mailer
ActionMailer::Base.deliveries = []
end
@@ -27,32 +25,32 @@ def generate_unique_email
"test#{@@email_count}@example.com"
end
- def valid_attributes(attributes={})
+ def valid_attributes(attributes = {})
{ username: "usertest",
email: generate_unique_email,
password: '12345678',
password_confirmation: '12345678' }.update(attributes)
end
- def new_user(attributes={})
+ def new_user(attributes = {})
User.new(valid_attributes(attributes))
end
- def create_user(attributes={})
+ def create_user(attributes = {})
User.create!(valid_attributes(attributes))
end
- def create_admin(attributes={})
+ def create_admin(attributes = {})
valid_attributes = valid_attributes(attributes)
valid_attributes.delete(:username)
Admin.create!(valid_attributes)
end
- def create_user_without_email(attributes={})
+ def create_user_without_email(attributes = {})
UserWithoutEmail.create!(valid_attributes(attributes))
end
- def create_user_with_validations(attributes={})
+ def create_user_with_validations(attributes = {})
UserWithValidations.create!(valid_attributes(attributes))
end
@@ -73,6 +71,17 @@ def swap(object, new_values)
end
end
+ def swap_model_config(model, new_values)
+ new_values.each do |key, value|
+ model.send :"#{key}=", value
+ end
+ yield
+ ensure
+ new_values.each_key do |key|
+ model.remove_instance_variable :"@#{key}"
+ end
+ end
+
def clear_cached_variables(options)
if options.key?(:case_insensitive_keys) || options.key?(:strip_whitespace_keys)
Devise.mappings.each do |_, mapping|
diff --git a/test/support/http_method_compatibility.rb b/test/support/http_method_compatibility.rb
index de207087f..44d80dffd 100644
--- a/test/support/http_method_compatibility.rb
+++ b/test/support/http_method_compatibility.rb
@@ -2,52 +2,8 @@
module Devise
class IntegrationTest < ActionDispatch::IntegrationTest
- # %w( get post patch put head delete xml_http_request
- # xhr get_via_redirect post_via_redirect
- # ).each do |method|
- %w( get post put ).each do |method|
- if Devise::Test.rails5_and_up?
- define_method(method) do |url, options={}|
- if options.empty?
- super url
- else
- super url, options
- end
- end
- else
- define_method(method) do |url, options={}|
- if options[:xhr]==true
- xml_http_request __method__, url, options[:params] || {}, options[:headers]
- else
- super url, options[:params] || {}, options[:headers]
- end
- end
- end
- end
end
class ControllerTestCase < ActionController::TestCase
- # %w( get post patch put head delete xml_http_request
- # xhr get_via_redirect post_via_redirect
- # ).each do |method|
- %w( get post put ).each do |method|
- if Devise::Test.rails5_and_up?
- define_method(method) do |action, options={}|
- if options.empty?
- super action
- else
- super action, options
- end
- end
- else
- define_method(method) do |action, options={}|
- if options[:xhr]==true
- xml_http_request __method__, action, options[:params] || {}, options[:headers]
- else
- super action, options[:params] || {}, options[:headers]
- end
- end
- end
- end
end
end
diff --git a/test/support/integration.rb b/test/support/integration.rb
index 2dccccf11..3ed0d85aa 100644
--- a/test/support/integration.rb
+++ b/test/support/integration.rb
@@ -7,7 +7,7 @@ def warden
request.env['warden']
end
- def create_user(options={})
+ def create_user(options = {})
@user ||= begin
user = User.create!(
username: 'usertest',
@@ -24,7 +24,7 @@ def create_user(options={})
end
end
- def create_admin(options={})
+ def create_admin(options = {})
@admin ||= begin
admin = Admin.create!(
email: options[:email] || 'admin@test.com',
@@ -36,7 +36,7 @@ def create_admin(options={})
end
end
- def sign_in_as_user(options={}, &block)
+ def sign_in_as_user(options = {}, &block)
user = create_user(options)
visit_with_option options[:visit], new_user_session_path
fill_in 'email', with: options[:email] || 'user@test.com'
@@ -47,7 +47,7 @@ def sign_in_as_user(options={}, &block)
user
end
- def sign_in_as_admin(options={}, &block)
+ def sign_in_as_admin(options = {}, &block)
admin = create_admin(options)
visit_with_option options[:visit], new_admin_session_path
fill_in 'email', with: 'admin@test.com'
@@ -61,8 +61,8 @@ def sign_in_as_admin(options={}, &block)
# account Middleware redirects.
#
def assert_redirected_to(url)
- assert [301, 302].include?(@integration_session.status),
- "Expected status to be 301 or 302, got #{@integration_session.status}"
+ assert_includes [301, 302, 303], @integration_session.status,
+ "Expected status to be 301, 302, or 303, got #{@integration_session.status}"
assert_url url, @integration_session.headers["Location"]
end
diff --git a/test/support/locale/de.yml b/test/support/locale/de.yml
new file mode 100644
index 000000000..b60457162
--- /dev/null
+++ b/test/support/locale/de.yml
@@ -0,0 +1,12 @@
+de:
+ activerecord:
+ attributes:
+ user:
+ email: E-Mail
+ mongoid:
+ attributes:
+ user:
+ email: E-Mail
+ devise:
+ failure:
+ invalid: "%{authentication_keys} oder Passwort ist ungültig."
diff --git a/test/support/locale/pt-BR.yml b/test/support/locale/pt-BR.yml
new file mode 100644
index 000000000..687cc87ae
--- /dev/null
+++ b/test/support/locale/pt-BR.yml
@@ -0,0 +1,7 @@
+pt-BR:
+ devise:
+ failure:
+ invalid: "%{authentication_keys} ou senha inválidos."
+ unauthenticated: "Para continuar, faça login ou registre-se."
+ timeout: "Sua sessão expirou. Por favor faça o login novamente para continuar."
+ unconfirmed: "Você precisa confirmar seu email para continuar."
diff --git a/test/support/mongoid.yml b/test/support/mongoid.yml
index cfd0124e2..d55de9250 100644
--- a/test/support/mongoid.yml
+++ b/test/support/mongoid.yml
@@ -1,5 +1,5 @@
test:
- <%= Mongoid::VERSION.to_i > 4 ? 'clients' : 'sessions' %>:
+ clients:
default:
database: devise-test-suite
hosts:
diff --git a/test/support/webrat/matchers.rb b/test/support/webrat/matchers.rb
new file mode 100644
index 000000000..24d71df3d
--- /dev/null
+++ b/test/support/webrat/matchers.rb
@@ -0,0 +1,16 @@
+# Monkey patch for Nokogiri changes - https://github.com/sparklemotion/nokogiri/issues/2469
+module Webrat
+ module Matchers
+ class HaveSelector
+ def query
+ Nokogiri::CSS::Parser.new.parse(@expected.to_s).map do |ast|
+ if ::Gem::Version.new(Nokogiri::VERSION) < ::Gem::Version.new('1.17.2')
+ ast.to_xpath('//', Nokogiri::CSS::XPathVisitor.new)
+ else
+ ast.to_xpath(Nokogiri::CSS::XPathVisitor.new)
+ end
+ end.first
+ end
+ end
+ end
+end
diff --git a/test/test/controller_helpers_test.rb b/test/test/controller_helpers_test.rb
index 7855621c1..a158e8753 100644
--- a/test/test/controller_helpers_test.rb
+++ b/test/test/controller_helpers_test.rb
@@ -15,7 +15,7 @@ class TestControllerHelpersTest < Devise::ControllerTestCase
test "redirects if attempting to access a page with an unconfirmed account" do
swap Devise, allow_unconfirmed_access_for: 0.days do
user = create_user
- assert !user.active_for_authentication?
+ assert_not user.active_for_authentication?
sign_in user
get :index
@@ -26,7 +26,7 @@ class TestControllerHelpersTest < Devise::ControllerTestCase
test "returns nil if accessing current_user with an unconfirmed account" do
swap Devise, allow_unconfirmed_access_for: 0.days do
user = create_user
- assert !user.active_for_authentication?
+ assert_not user.active_for_authentication?
sign_in user
get :accept, params: { id: user }
@@ -97,16 +97,22 @@ def respond
test "returns the body of a failure app" do
get :index
- assert_equal response.body, "You are being redirected."
+
+ if Devise::Test.rails71_and_up?
+ assert_empty response.body
+ else
+ assert_equal "You are being redirected.", response.body
+ end
end
test "returns the content type of a failure app" do
- get :index, params: { format: :xml }
- assert response.content_type.include?('application/xml')
+ get :index, params: { format: :json }
+
+ assert_includes response.media_type, 'application/json'
end
test "defined Warden after_authentication callback should not be called when sign_in is called" do
- begin
+ assert_nothing_raised do
Warden::Manager.after_authentication do |user, auth, opts|
flunk "callback was called while it should not"
end
@@ -120,7 +126,7 @@ def respond
end
test "defined Warden before_logout callback should not be called when sign_out is called" do
- begin
+ assert_nothing_raised do
Warden::Manager.before_logout do |user, auth, opts|
flunk "callback was called while it should not"
end
@@ -171,13 +177,7 @@ def respond
test "creates a new warden proxy if the request object has changed" do
old_warden_proxy = warden
- @request = if Devise::Test.rails51? || Devise::Test.rails52_and_up?
- ActionController::TestRequest.create(Class.new) # needs a "controller class"
- elsif Devise::Test.rails5?
- ActionController::TestRequest.create
- else
- ActionController::TestRequest.new
- end
+ @request = ActionController::TestRequest.create(Class.new) # needs a "controller class"
new_warden_proxy = warden
@@ -191,3 +191,18 @@ def respond
assert_equal old_warden_proxy, new_warden_proxy
end
end
+
+class TestControllerHelpersForStreamingControllerTest < Devise::ControllerTestCase
+ tests StreamingController
+ include Devise::Test::ControllerHelpers
+
+ test "doesn't hang when sending an authentication error response body" do
+ get :index
+
+ if Devise::Test.rails71_and_up?
+ assert_empty response.body
+ else
+ assert_equal "You are being redirected.", response.body
+ end
+ end
+end
diff --git a/test/test/integration_helpers_test.rb b/test/test/integration_helpers_test.rb
index 131593c1d..7f579a8da 100644
--- a/test/test/integration_helpers_test.rb
+++ b/test/test/integration_helpers_test.rb
@@ -18,7 +18,7 @@ class TestIntegrationsHelpersTest < Devise::IntegrationTest
sign_out user
visit '/'
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
end
test '#sign_out does not signs out other scopes' do
@@ -28,7 +28,7 @@ class TestIntegrationsHelpersTest < Devise::IntegrationTest
visit '/'
- refute warden.authenticated?(:user)
+ assert_not warden.authenticated?(:user)
assert warden.authenticated?(:admin)
end
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 378c9b110..e6df81203 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -10,9 +10,9 @@
require "rails/test_help"
require "orm/#{DEVISE_ORM}"
-I18n.load_path << File.expand_path("../support/locale/en.yml", __FILE__)
+I18n.load_path.concat Dir["#{File.dirname(__FILE__)}/support/locale/*.yml"]
-require 'mocha/setup'
+require 'mocha/minitest'
require 'timecop'
require 'webrat'
Webrat.configure do |config|