From 4911613e0023140c8f84694751dc6aa673c6dcef Mon Sep 17 00:00:00 2001 From: Daria Mayorova Date: Tue, 25 Jun 2024 17:55:49 +0200 Subject: [PATCH 1/3] Do not reveal that email doesn't exist on Forgot password form --- features/old/accounts/buyers/buyer_password_reset.feature | 7 ++++--- .../admin/account/passwords_controller.rb | 8 ++++---- .../developer_portal/passwords_controller_test.rb | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/features/old/accounts/buyers/buyer_password_reset.feature b/features/old/accounts/buyers/buyer_password_reset.feature index 07d1b93a1a..b28308fa1b 100644 --- a/features/old/accounts/buyers/buyer_password_reset.feature +++ b/features/old/accounts/buyers/buyer_password_reset.feature @@ -14,7 +14,7 @@ Feature: Buyer password reset Given I follow "Forgot password?" And I fill in "Email" with "zed@3scale.localhost" And I press "Send instructions" - Then I should see "A password reset link has been emailed to you." + Then I should see "A password reset link has been emailed to: zed@3scale.localhost." When I follow the link found in the password reset email send to "zed@3scale.localhost" And I fill in "Password" with "monkey" And I fill in "Password confirmation" with "monkey" @@ -32,7 +32,8 @@ Feature: Buyer password reset And I follow "Forgot password?" And I fill in "Email" with "bob@3scale.localhost" And I press "Send instructions" - Then I should see "Email not found." + Then I should see "A password reset link has been emailed to: bob@3scale.localhost." + And "bob@3scale.localhost" should receive no emails Scenario: Wrong confirmation Given I follow "Forgot password?" @@ -65,4 +66,4 @@ Feature: Buyer password reset When I follow "Forgot password?" And I fill in "Email" with "zed@3scale.localhost" And I press "Send instructions" - Then I should see "A password reset link has been emailed to you." + Then I should see "A password reset link has been emailed to: zed@3scale.localhost." diff --git a/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb b/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb index 89470b4f03..5dd934db1c 100644 --- a/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb +++ b/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb @@ -12,11 +12,11 @@ class DeveloperPortal::Admin::Account::PasswordsController < ::DeveloperPortal:: def create return redirect_to_request_password('Bot protection failed.') unless bot_check({ flash: false }) - user = @provider.buyer_users.find_by_email(params[:email]) - return redirect_to_request_password('Email not found.') unless user + email = params[:email] + user = @provider.buyer_users.find_by(email: email) + user&.generate_lost_password_token! - user.generate_lost_password_token! - flash[:notice] = 'A password reset link has been emailed to you.' + flash[:notice] = "A password reset link has been emailed to: #{email}." redirect_to login_url end diff --git a/test/integration/developer_portal/passwords_controller_test.rb b/test/integration/developer_portal/passwords_controller_test.rb index 86831f729e..c8e970ade1 100644 --- a/test/integration/developer_portal/passwords_controller_test.rb +++ b/test/integration/developer_portal/passwords_controller_test.rb @@ -78,14 +78,14 @@ def test_update_password post developer_portal.admin_account_password_path(email: user.email) end assert_in_delta Time.now, user.reload.lost_password_token_generated_at, 2.seconds - assert_equal 'A password reset link has been emailed to you.', flash[:notice] + assert_equal "A password reset link has been emailed to: #{user.email}.", flash[:notice] assert_redirected_to developer_portal.login_path end test 'create renders the right error message when the email is not found' do post developer_portal.admin_account_password_path(email: 'fake@example.com') - assert_equal 'Email not found.', flash[:error] - assert_redirected_to developer_portal.new_admin_account_password_path(request_password_reset: true) + assert_equal "A password reset link has been emailed to: fake@example.com.", flash[:notice] + assert_redirected_to developer_portal.login_path end private From a97e02f471ea8e35a744c6f570fc44674b521ce2 Mon Sep 17 00:00:00 2001 From: Daria Mayorova Date: Wed, 26 Jun 2024 11:25:36 +0200 Subject: [PATCH 2/3] Some fixes --- features/developer_portal/buyer_password_reset.feature | 6 +++--- features/old/accounts/buyers/buyer_password_reset.feature | 6 +++--- .../developer_portal/admin/account/passwords_controller.rb | 2 +- .../developer_portal/passwords_controller_test.rb | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/features/developer_portal/buyer_password_reset.feature b/features/developer_portal/buyer_password_reset.feature index 8a4de454e1..10f75f1890 100644 --- a/features/developer_portal/buyer_password_reset.feature +++ b/features/developer_portal/buyer_password_reset.feature @@ -12,14 +12,14 @@ Feature: Buyer signup @recaptcha Scenario: Bot protection doesn't detect the client as a bot - And the client will be marked as a bot + Given the client will be marked as a bot When the buyer wants to reset their password And the buyer fills in the form Then the page should contain "Bot protection failed." @recaptcha Scenario: Bot protection doesn't detect the client as a bot - And the client won't be marked as a bot + Given the client won't be marked as a bot When the buyer wants to reset their password And the buyer fills in the form - Then the page should contain "Email not found." + Then the page should contain "A password reset link will be sent" diff --git a/features/old/accounts/buyers/buyer_password_reset.feature b/features/old/accounts/buyers/buyer_password_reset.feature index b28308fa1b..e46f0f511a 100644 --- a/features/old/accounts/buyers/buyer_password_reset.feature +++ b/features/old/accounts/buyers/buyer_password_reset.feature @@ -14,7 +14,7 @@ Feature: Buyer password reset Given I follow "Forgot password?" And I fill in "Email" with "zed@3scale.localhost" And I press "Send instructions" - Then I should see "A password reset link has been emailed to: zed@3scale.localhost." + Then I should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." When I follow the link found in the password reset email send to "zed@3scale.localhost" And I fill in "Password" with "monkey" And I fill in "Password confirmation" with "monkey" @@ -32,7 +32,7 @@ Feature: Buyer password reset And I follow "Forgot password?" And I fill in "Email" with "bob@3scale.localhost" And I press "Send instructions" - Then I should see "A password reset link has been emailed to: bob@3scale.localhost." + Then I should see "A password reset link will be sent to bob@3scale.localhost if a user exists with this email." And "bob@3scale.localhost" should receive no emails Scenario: Wrong confirmation @@ -66,4 +66,4 @@ Feature: Buyer password reset When I follow "Forgot password?" And I fill in "Email" with "zed@3scale.localhost" And I press "Send instructions" - Then I should see "A password reset link has been emailed to: zed@3scale.localhost." + Then I should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." diff --git a/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb b/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb index 5dd934db1c..7d74be3fb1 100644 --- a/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb +++ b/lib/developer_portal/app/controllers/developer_portal/admin/account/passwords_controller.rb @@ -16,7 +16,7 @@ def create user = @provider.buyer_users.find_by(email: email) user&.generate_lost_password_token! - flash[:notice] = "A password reset link has been emailed to: #{email}." + flash[:notice] = "A password reset link will be sent to #{email} if a user exists with this email." redirect_to login_url end diff --git a/test/integration/developer_portal/passwords_controller_test.rb b/test/integration/developer_portal/passwords_controller_test.rb index c8e970ade1..aa51d81b3b 100644 --- a/test/integration/developer_portal/passwords_controller_test.rb +++ b/test/integration/developer_portal/passwords_controller_test.rb @@ -78,13 +78,13 @@ def test_update_password post developer_portal.admin_account_password_path(email: user.email) end assert_in_delta Time.now, user.reload.lost_password_token_generated_at, 2.seconds - assert_equal "A password reset link has been emailed to: #{user.email}.", flash[:notice] + assert_equal "A password reset link will be sent to #{user.email} if a user exists with this email.", flash[:notice] assert_redirected_to developer_portal.login_path end test 'create renders the right error message when the email is not found' do post developer_portal.admin_account_password_path(email: 'fake@example.com') - assert_equal "A password reset link has been emailed to: fake@example.com.", flash[:notice] + assert_equal "A password reset link will be sent to fake@example.com if a user exists with this email.", flash[:notice] assert_redirected_to developer_portal.login_path end From 5226edaa91e388ee3501705dff781d8bb31a4862 Mon Sep 17 00:00:00 2001 From: Daria Mayorova Date: Thu, 4 Jul 2024 12:16:22 +0200 Subject: [PATCH 3/3] Reorganize cucumbers for password reset --- .../buyer_password_reset.feature | 101 ++++++++++++++---- .../buyers/buyer_password_reset.feature | 69 ------------ features/step_definitions/password_steps.rb | 4 +- features/step_definitions/session_steps.rb | 2 +- 4 files changed, 85 insertions(+), 91 deletions(-) delete mode 100644 features/old/accounts/buyers/buyer_password_reset.feature diff --git a/features/developer_portal/buyer_password_reset.feature b/features/developer_portal/buyer_password_reset.feature index 10f75f1890..ccb9d8570c 100644 --- a/features/developer_portal/buyer_password_reset.feature +++ b/features/developer_portal/buyer_password_reset.feature @@ -1,25 +1,88 @@ -Feature: Buyer signup +Feature: Buyer password reset I want to reset my password as a buyer Background: Given a provider exists - And the default product of provider "master" has name "Master API" - And the following application plan: - | Product | Name | - | Master API | enterprise | - And the provider has bot protection enabled - And the provider account allows signups - @recaptcha - Scenario: Bot protection doesn't detect the client as a bot - Given the client will be marked as a bot - When the buyer wants to reset their password - And the buyer fills in the form - Then the page should contain "Bot protection failed." + Rule: ReCAPTCHA protects from bots + Background: + Given the provider has bot protection enabled - @recaptcha - Scenario: Bot protection doesn't detect the client as a bot - Given the client won't be marked as a bot - When the buyer wants to reset their password - And the buyer fills in the form - Then the page should contain "A password reset link will be sent" + @recaptcha + Scenario: Bot protection doesn't detect the client as a bot + Given the client will be marked as a bot + When the buyer wants to reset their password + And the buyer fills in the form + Then the page should contain "Bot protection failed." + + @recaptcha + Scenario: Bot protection doesn't detect the client as a bot + Given the client won't be marked as a bot + When the buyer wants to reset their password + And the buyer fills in the form + Then the page should contain "A password reset link will be sent" + + Rule: Reset password flow for different scenarios + Background: + Given a buyer "bob" signed up to the provider + And an active user "zed" of account "bob" with email "zed@3scale.localhost" + And the current domain is foo.3scale.localhost + And they go to the login page + + Scenario: Reset password of an existing user + Given they follow "Forgot password?" + And they fill in "Email" with "zed@3scale.localhost" + And they press "Send instructions" + Then they should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." + When they follow the link found in the password reset email send to "zed@3scale.localhost" + And they fill in "Password" with "monkey" + And they fill in "Password confirmation" with "monkey" + And they press "Change Password" + Then they should see "The password has been changed" + + When they go to the login page + And they fill in "Username" with "zed@3scale.localhost" + And they fill in "Password" with "monkey" + And they press "Sign in" + Then they should be logged in as "zed" + + Scenario: Invalid email + Given no user exists with an email of "bob@3scale.localhost" + And they follow "Forgot password?" + And they fill in "Email" with "bob@3scale.localhost" + And they press "Send instructions" + Then they should see "A password reset link will be sent to bob@3scale.localhost if a user exists with this email." + And "bob@3scale.localhost" should receive no emails + + Scenario: Wrong confirmation + Given they follow "Forgot password?" + And they fill in "Email" with "zed@3scale.localhost" + And they press "Send instructions" + And they follow the link found in the password reset email send to "zed@3scale.localhost" + And they fill in "Password" with "monkey" + And they fill in "Password confirmation" with "donkey" + And they press "Change Password" + Then they should see the password confirmation error + And the password of user "zed" should not be "monkey" + + Scenario: Blank passwords + When they follow "Forgot password?" + And they fill in "Email" with "zed@3scale.localhost" + And they press "Send instructions" + And they follow the link found in the password reset email send to "zed@3scale.localhost" + And they press "Change Password" + Then they should see "The password is invalid" + + Scenario: Invalid token + When they go to the password page with invalid password reset token + Then they should see "The password reset token is invalid" + + Scenario: Attempt to login with invalid credentials, then reset password + Given they fill in "Username" with "zed@3scale.localhost" + And they fill in "Password" with "ihavenoclue" + And they press "Sign in" + Then they should see "Incorrect email or password. Please try again." + When they follow "Forgot password?" + And they fill in "Email" with "zed@3scale.localhost" + And they press "Send instructions" + Then they should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." diff --git a/features/old/accounts/buyers/buyer_password_reset.feature b/features/old/accounts/buyers/buyer_password_reset.feature deleted file mode 100644 index e46f0f511a..0000000000 --- a/features/old/accounts/buyers/buyer_password_reset.feature +++ /dev/null @@ -1,69 +0,0 @@ -Feature: Buyer password reset - In order to sign in even if I forgot my password - As a user - I should be able to reset it - - Background: - Given a provider "foo.3scale.localhost" - And a buyer "bob" signed up to provider "foo.3scale.localhost" - And an active user "zed" of account "bob" with email "zed@3scale.localhost" - When the current domain is foo.3scale.localhost - And I go to the login page - - Scenario: Reset password - Given I follow "Forgot password?" - And I fill in "Email" with "zed@3scale.localhost" - And I press "Send instructions" - Then I should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." - When I follow the link found in the password reset email send to "zed@3scale.localhost" - And I fill in "Password" with "monkey" - And I fill in "Password confirmation" with "monkey" - And I press "Change Password" - Then I should see "The password has been changed" - - When I go to the login page - And I fill in "Username" with "zed@3scale.localhost" - And I fill in "Password" with "monkey" - And I press "Sign in" - Then I should be logged in as "zed" - - Scenario: Invalid email - Given no user exists with an email of "bob@3scale.localhost" - And I follow "Forgot password?" - And I fill in "Email" with "bob@3scale.localhost" - And I press "Send instructions" - Then I should see "A password reset link will be sent to bob@3scale.localhost if a user exists with this email." - And "bob@3scale.localhost" should receive no emails - - Scenario: Wrong confirmation - Given I follow "Forgot password?" - And I fill in "Email" with "zed@3scale.localhost" - And I press "Send instructions" - And I follow the link found in the password reset email send to "zed@3scale.localhost" - And I fill in "Password" with "monkey" - And I fill in "Password confirmation" with "donkey" - And I press "Change Password" - Then I should see the password confirmation error - And the password of user "zed" should not be "monkey" - - Scenario: Blank passwords - When I follow "Forgot password?" - And I fill in "Email" with "zed@3scale.localhost" - And I press "Send instructions" - And I follow the link found in the password reset email send to "zed@3scale.localhost" - And I press "Change Password" - Then I should see "The password is invalid" - - Scenario: Invalid token - When I go to the password page with invalid password reset token - Then I should see "The password reset token is invalid" - - Scenario: Attempt to login with invalid credentials, then reset password - Given I fill in "Username" with "zed@3scale.localhost" - And I fill in "Password" with "ihavenoclue" - And I press "Sign in" - Then I should see "Incorrect email or password. Please try again." - When I follow "Forgot password?" - And I fill in "Email" with "zed@3scale.localhost" - And I press "Send instructions" - Then I should see "A password reset link will be sent to zed@3scale.localhost if a user exists with this email." diff --git a/features/step_definitions/password_steps.rb b/features/step_definitions/password_steps.rb index 964a19bce9..63b53ae521 100644 --- a/features/step_definitions/password_steps.rb +++ b/features/step_definitions/password_steps.rb @@ -14,7 +14,7 @@ assert !user.authenticated?(password) end -When /^I follow the link found in the password reset email send to "([^"]*)"$/ do |email| +When /^(?:|I |they )follow the link found in the password reset email send to "([^"]*)"$/ do |email| visit_url_in_email(email, /Lost password recovery/) end @@ -34,7 +34,7 @@ def visit_url_in_email(email, subject) visit url end -Then 'I should see the password confirmation error' do +Then /^(?:|I |they )should see the password confirmation error$/ do %q(I should see error "doesn't match Password" for field "Password confirmation") end diff --git a/features/step_definitions/session_steps.rb b/features/step_definitions/session_steps.rb index 63259d47e2..00d0ad6d14 100644 --- a/features/step_definitions/session_steps.rb +++ b/features/step_definitions/session_steps.rb @@ -113,7 +113,7 @@ click_button('Sign in') end -Then /^I should be logged in as "([^"]*)"$/ do |username| +Then /^(?:|I |they )should be logged in as "([^"]*)"$/ do |username| assert_current_user(username) end