From d69f28ba996ab33e6c457fc58ef7375d94c61315 Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Fri, 26 Jan 2024 14:39:29 +0100 Subject: [PATCH 01/10] Evaluate callable redirects in context of controller (#203) * Evaluate callable redirects in context of controller * Use instance_exec * Optionally pass authenticatable as argument --- .../passwordless/sessions_controller.rb | 25 +++++++++++++++---- .../passwordless/sessions_controller_test.rb | 16 ++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/app/controllers/passwordless/sessions_controller.rb b/app/controllers/passwordless/sessions_controller.rb index a7c7e41..0e4b680 100644 --- a/app/controllers/passwordless/sessions_controller.rb +++ b/app/controllers/passwordless/sessions_controller.rb @@ -119,8 +119,15 @@ def passwordless_query_redirect_path nil end - def passwordless_success_redirect_path - success_redirect_path = call_or_return(Passwordless.config.success_redirect_path) + def passwordless_success_redirect_path(authenticatable) + success_redirect_path = Passwordless.config.success_redirect_path + + if success_redirect_path.respond_to?(:call) + success_redirect_path = call_or_return( + success_redirect_path, + *[authenticatable].first(success_redirect_path.arity) + ) + end if Passwordless.config.redirect_back_after_sign_in session_redirect_url = reset_passwordless_redirect_location!(authenticatable_class) @@ -142,7 +149,11 @@ def artificially_slow_down_brute_force_attacks(token) def authenticate_and_sign_in(session, token) if session.authenticate(token) sign_in(session) - redirect_to(passwordless_success_redirect_path, status: :see_other, **redirect_to_options) + redirect_to( + passwordless_success_redirect_path(session.authenticatable), + status: :see_other, + **redirect_to_options + ) else flash[:error] = I18n.t("passwordless.sessions.errors.invalid_token") render(status: :forbidden, action: "show") @@ -168,8 +179,12 @@ def authenticatable_class authenticatable_type.constantize end - def call_or_return(value) - value.respond_to?(:call) ? value.call : value + def call_or_return(value, *args) + if value.respond_to?(:call) + instance_exec(*args, &value) + else + value + end end def find_authenticatable diff --git a/test/controllers/passwordless/sessions_controller_test.rb b/test/controllers/passwordless/sessions_controller_test.rb index 044670d..62cbb6e 100644 --- a/test/controllers/passwordless/sessions_controller_test.rb +++ b/test/controllers/passwordless/sessions_controller_test.rb @@ -145,20 +145,26 @@ class << User assert_equal pwless_session(User), Session.last!.id end - test("PATCH /:passwordless_for/sign_in/:id -> SUCCESS / callable success path") do + test("PATCH /:passwordless_for/sign_in/:id -> SUCCESS / callable success path with no args") do passwordless_session = create_pwless_session(token: "hi") with_config(success_redirect_path: lambda { "/" }) do patch("/users/sign_in/#{passwordless_session.identifier}", params: {passwordless: {token: "hi"}}) end - assert_equal 303, status - follow_redirect! - assert_equal 200, status assert_equal "/", path + end - assert_equal pwless_session(User), Session.last!.id + test("PATCH /:passwordless_for/sign_in/:id -> SUCCESS / callable success path with 1 arg") do + passwordless_session = create_pwless_session(token: "hi") + + with_config(success_redirect_path: lambda { |user| "/#{user.id}" }) do + patch("/users/sign_in/#{passwordless_session.identifier}", params: {passwordless: {token: "hi"}}) + end + + follow_redirect! + assert_equal "/#{passwordless_session.authenticatable.id}", path end test("PATCH /:passwordless_for/sign_in/:id -> ERROR") do From 09ed49293bef7e993bec7c0b205a82d224075f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9as=20K=C3=BCndig?= Date: Thu, 15 Feb 2024 09:46:04 +0100 Subject: [PATCH 02/10] Add url_options param to sign_in email (#208) --- app/mailers/passwordless/mailer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/mailers/passwordless/mailer.rb b/app/mailers/passwordless/mailer.rb index 0c627b8..8cace33 100644 --- a/app/mailers/passwordless/mailer.rb +++ b/app/mailers/passwordless/mailer.rb @@ -10,7 +10,7 @@ class Mailer < Passwordless.config.parent_mailer.constantize # @param session [Session] An instance of Passwordless::Session # @param token [String] The token in plaintext. Falls back to `session.token` hoping it # is still in memory (optional) - def sign_in(session, token = nil) + def sign_in(session, token = nil, url_options = {}) @token = token || session.token @magic_link = Passwordless.context.url_for( @@ -18,6 +18,7 @@ def sign_in(session, token = nil) action: "confirm", id: session.to_param, token: @token, + **url_options, **default_url_options ) From 04b1a4e75df764214a32c69b7b9ff3e762955485 Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Mon, 11 Mar 2024 10:42:47 +0100 Subject: [PATCH 03/10] Include TestHelpers in ActionDispatch::IntegrationTest (#211) --- lib/passwordless/test_helpers.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/passwordless/test_helpers.rb b/lib/passwordless/test_helpers.rb index 4e96f8d..fffb185 100644 --- a/lib/passwordless/test_helpers.rb +++ b/lib/passwordless/test_helpers.rb @@ -75,6 +75,10 @@ def passwordless_sign_in(resource, only_path: false) ActionDispatch::SystemTestCase.send(:include, ::Passwordless::TestHelpers::SystemTestCase) end +if defined?(ActionDispatch::IntegrationTest) + ActionDispatch::IntegrationTest.send(:include, ::Passwordless::TestHelpers::RequestTestCase) +end + if defined?(RSpec) RSpec.configure do |config| config.include(::Passwordless::TestHelpers::ControllerTestCase, type: :controller) From c7f84b3ea6cd65bbc3837bcdf49e2be2e8c4bd2b Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Mon, 11 Mar 2024 11:04:42 +0100 Subject: [PATCH 04/10] Update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47d0a5c..c98686f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## Unreleased + +### Changed + +- Evaluate callable redirects in context of controller (#203) + +### Added + +- Add url_options param to sign_in email (#208) +- Include TestHelpers in ActionDispatch::IntegrationTest (#211) + ## 1.4.0 ### Changed From 28e5c36693bbd7cfee05928168f624c41f5f8d97 Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Mon, 11 Mar 2024 11:05:35 +0100 Subject: [PATCH 05/10] v1.5.0 --- CHANGELOG.md | 2 +- lib/passwordless/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c98686f..cb9ae77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 1.5.0 ### Changed diff --git a/lib/passwordless/version.rb b/lib/passwordless/version.rb index b6ff6f5..cc2b73c 100644 --- a/lib/passwordless/version.rb +++ b/lib/passwordless/version.rb @@ -2,5 +2,5 @@ module Passwordless # :nodoc: - VERSION = "1.4.0" + VERSION = "1.5.0" end From 0aceaa0c10cbe9eefdff33bff0c564952d26379a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9as=20K=C3=BCndig?= Date: Wed, 3 Apr 2024 09:29:43 +0200 Subject: [PATCH 06/10] Add "token" on show page to locale definition (#214) --- app/views/passwordless/sessions/show.html.erb | 2 +- config/locales/en.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/passwordless/sessions/show.html.erb b/app/views/passwordless/sessions/show.html.erb index c59dd64..5ebb0e7 100644 --- a/app/views/passwordless/sessions/show.html.erb +++ b/app/views/passwordless/sessions/show.html.erb @@ -1,5 +1,5 @@ <%= form_with(model: @session, url: url_for(action: 'update'), scope: 'passwordless', method: 'patch', data: { turbo: false }) do |f| %> - <%= f.label :token %> + <%= f.label :token, t(".token") %> <%= f.text_field :token, required: true, autofocus: true, diff --git a/config/locales/en.yml b/config/locales/en.yml index d813971..091ee62 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -12,6 +12,7 @@ en: not_found: "We couldn't find a user with that email address" error: "An error occured" show: + token: "Token" confirm: "Confirm" errors: invalid_token: "Token is invalid" From bc715eb0a6ca330bf930c15dba20b6d70a7edca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9as=20K=C3=BCndig?= Date: Fri, 5 Apr 2024 13:45:32 +0200 Subject: [PATCH 07/10] Add support for scoped routes (#209) --- .../passwordless/sessions_controller.rb | 3 +- .../passwordless/sessions_controller_test.rb | 66 +++++++++++++++++++ test/dummy/config/application.rb | 1 + test/dummy/config/locales/test.yml | 7 ++ test/dummy/config/routes.rb | 4 ++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/dummy/config/locales/test.yml diff --git a/app/controllers/passwordless/sessions_controller.rb b/app/controllers/passwordless/sessions_controller.rb index 0e4b680..47165fa 100644 --- a/app/controllers/passwordless/sessions_controller.rb +++ b/app/controllers/passwordless/sessions_controller.rb @@ -30,7 +30,8 @@ def create Passwordless.context.path_for( @session, id: @session.to_param, - action: "show" + action: "show", + **default_url_options ), flash: {notice: I18n.t("passwordless.sessions.create.email_sent")} ) diff --git a/test/controllers/passwordless/sessions_controller_test.rb b/test/controllers/passwordless/sessions_controller_test.rb index 62cbb6e..1d9f1ce 100644 --- a/test/controllers/passwordless/sessions_controller_test.rb +++ b/test/controllers/passwordless/sessions_controller_test.rb @@ -17,6 +17,7 @@ def create_pwless_session(attrs = {}) assert_equal 200, status assert_template "passwordless/sessions/new" + assert_match "E-mail address", response.body end test("GET /:passwordless_for/sign_in/:id") do @@ -266,3 +267,68 @@ def pwless_session(cls) end end end + +module Passwordless + class SessionsControllerParamsTest < ActionDispatch::IntegrationTest + def create_user(attrs = {}) + attrs.reverse_merge!(email: next_email) + User.create!(attrs) + end + + def create_pwless_session(attrs = {}) + attrs[:authenticatable] = create_user unless attrs.key?(:authenticatable) + Session.create!(attrs) + end + + class Passwordless::LocaleParentController < ActionController::Base + around_action :switch_locale + + def default_url_options + {locale: I18n.locale} + end + + def switch_locale(&action) + locale = params[:locale] || I18n.default_locale + I18n.with_locale(locale, &action) + end + end + + setup do + with_config({parent_controller: "Passwordless::LocaleParentController"}) do + reload_controller! + end + end + + teardown do + reload_controller! + end + + test("GET /locale/en/:passwordless_for/sign_in") do + get "/locale/en/users/sign_in" + + assert_match "E-mail address", response.body + end + + test("GET /locale/test/:passwordless_for/sign_in") do + get "/locale/test/users/sign_in" + + assert_match "Gimme dat email", response.body + end + + test("GET /locale/test/:passwordless_for/sign_in/:id") do + passwordless_session = create_pwless_session + + get("/locale/test/users/sign_in/#{passwordless_session.identifier}") + + assert_equal "/locale/test/users/sign_in/#{passwordless_session.to_param}", path + end + + test("POST /locale/test/:passwordless_for/sign_in -> SUCCESS ") do + create_user(email: "a@a") + + post("/locale/test/users/sign_in", params: {passwordless: {email: "a@a"}}) + + assert_equal 302, status + end + end +end diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb index 8415321..1850537 100644 --- a/test/dummy/config/application.rb +++ b/test/dummy/config/application.rb @@ -28,5 +28,6 @@ class Application < Rails::Application config.action_mailer.default_url_options = {host: "localhost", port: "3000"} routes.default_url_options[:host] = "localhost:3000" + config.i18n.available_locales = %i[en test] end end diff --git a/test/dummy/config/locales/test.yml b/test/dummy/config/locales/test.yml new file mode 100644 index 0000000..4f28b7c --- /dev/null +++ b/test/dummy/config/locales/test.yml @@ -0,0 +1,7 @@ +--- +test: + passwordless: + sessions: + new: + email: + label: "Gimme dat email" diff --git a/test/dummy/config/routes.rb b/test/dummy/config/routes.rb index 82982cc..630d571 100644 --- a/test/dummy/config/routes.rb +++ b/test/dummy/config/routes.rb @@ -12,4 +12,8 @@ get("/secret-alt", to: "secrets#index") root(to: "users#index") + + scope("/locale/:locale") do + passwordless_for(:users, as: :locale_user) + end end From 754f3b7e92027e65dcff4647059cb49a7752187a Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Fri, 5 Apr 2024 14:43:28 +0200 Subject: [PATCH 08/10] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb9ae77..1594ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Add support for scoped routes with `default_url_options` (#209) + ## 1.5.0 ### Changed From 5a29d12211048066f9663f794b4da870378d3e50 Mon Sep 17 00:00:00 2001 From: Luis Romero Date: Sat, 6 Apr 2024 08:55:07 -0500 Subject: [PATCH 09/10] Use flash.alert (#215) --- app/controllers/passwordless/sessions_controller.rb | 10 +++++----- .../passwordless/sessions_controller_test.rb | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/passwordless/sessions_controller.rb b/app/controllers/passwordless/sessions_controller.rb index 47165fa..ce4face 100644 --- a/app/controllers/passwordless/sessions_controller.rb +++ b/app/controllers/passwordless/sessions_controller.rb @@ -36,14 +36,14 @@ def create flash: {notice: I18n.t("passwordless.sessions.create.email_sent")} ) else - flash[:error] = I18n.t("passwordless.sessions.create.error") + flash.alert = I18n.t("passwordless.sessions.create.error") render(:new, status: :unprocessable_entity) end rescue ActiveRecord::RecordNotFound @session = Session.new - flash[:error] = I18n.t("passwordless.sessions.create.not_found") + flash.alert = I18n.t("passwordless.sessions.create.not_found") render(:new, status: :not_found) end @@ -156,15 +156,15 @@ def authenticate_and_sign_in(session, token) **redirect_to_options ) else - flash[:error] = I18n.t("passwordless.sessions.errors.invalid_token") + flash.alert = I18n.t("passwordless.sessions.errors.invalid_token") render(status: :forbidden, action: "show") end rescue Errors::TokenAlreadyClaimedError - flash[:error] = I18n.t("passwordless.sessions.errors.token_claimed") + flash.alert = I18n.t("passwordless.sessions.errors.token_claimed") redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options) rescue Errors::SessionTimedOutError - flash[:error] = I18n.t("passwordless.sessions.errors.session_expired") + flash.alert = I18n.t("passwordless.sessions.errors.session_expired") redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options) end diff --git a/test/controllers/passwordless/sessions_controller_test.rb b/test/controllers/passwordless/sessions_controller_test.rb index 1d9f1ce..53a9dcd 100644 --- a/test/controllers/passwordless/sessions_controller_test.rb +++ b/test/controllers/passwordless/sessions_controller_test.rb @@ -114,7 +114,7 @@ class << User assert_equal 0, ActionMailer::Base.deliveries.size assert_template "passwordless/sessions/new" - assert_match "We couldn't find a user with that email address", flash[:error] + assert_match "We couldn't find a user with that email address", flash.alert end test("POST /:passwordless_for/sign_in -> ERROR / other error") do @@ -129,7 +129,7 @@ class << User assert_equal 0, ActionMailer::Base.deliveries.size assert_template "passwordless/sessions/new" - assert_match "An error occured", flash[:error] + assert_match "An error occured", flash.alert end test("PATCH /:passwordless_for/sign_in/:id -> SUCCESS") do @@ -196,7 +196,7 @@ class << User follow_redirect! assert_equal 200, status assert_equal "/", path - assert_match "This link has already been used, try requesting the link again", flash[:error] + assert_match "This link has already been used, try requesting the link again", flash.alert assert_nil pwless_session(User) end @@ -215,7 +215,7 @@ class << User follow_redirect! assert_equal 200, status assert_equal "/", path - assert_match "Your session has expired", flash[:error] + assert_match "Your session has expired", flash.alert assert_nil pwless_session(User) end From 971093ee814df13ec0b485840cb8fbd55d2179db Mon Sep 17 00:00:00 2001 From: Mikkel Malmberg Date: Sat, 6 Apr 2024 15:56:27 +0200 Subject: [PATCH 10/10] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1594ea2..1580840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,14 @@ ## Unreleased +### Added + - Add support for scoped routes with `default_url_options` (#209) +### Fixed + +- Use flash.alert of flash[:error] (#215) + ## 1.5.0 ### Changed