Files
ubicloud/spec/routes/web/auth_spec.rb
Jeremy Evans 4b819d3cb2 Change all create_with_id to create
It hasn't been necessary to use create_with_id since
ebc79622df, in December 2024.

I have plans to introduce:

```ruby
def create_with_id(id, values)
  obj = new(values)
  obj.id = id
  obj.save_changes
end
```

This will make it easier to use the same id when creating
multiple objects.  The first step is removing the existing
uses of create_with_id.
2025-08-06 01:55:51 +09:00

1056 lines
40 KiB
Ruby

# frozen_string_literal: true
require_relative "spec_helper"
RSpec.describe Clover, "auth" do
it "redirects root to login" do
visit "/"
expect(page).to have_current_path("/login")
end
it "can not login new account without verification" do
visit "/create-account"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Full Name", with: "John Doe"
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
click_button "Create Account"
expect(Mail::TestMailer.deliveries.length).to eq 1
expect(page.title).to eq("Ubicloud - Login")
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_flash_error("The account you tried to login with is currently awaiting verification")
expect(page.title).to eq("Ubicloud - Resend Verification")
end
it "can not create new account with invalid name" do
visit "/create-account"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Full Name", with: "Click here http://example.com"
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
click_button "Create Account"
expect(page.title).to eq("Ubicloud - Create Account")
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_content("Name must only contain letters, numbers, spaces, and hyphens and have max length 63.")
end
it "can send email verification email again after 300 seconds" do
visit "/create-account"
fill_in "Full Name", with: "John Doe"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
click_button "Create Account"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_flash_error("The account you tried to login with is currently awaiting verification")
expect(page).to have_content("You need to wait at least 5 minutes before sending another verification email. If you did not receive the email, please check your spam folder.")
DB[:account_verification_keys].update(email_last_sent: Time.now - 310)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_flash_error("The account you tried to login with is currently awaiting verification")
DB.transaction(rollback: :always) do
click_button "Send Verification Again"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 2
end
visit "/verify-account-resend"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Send Verification Again"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 3
end
it "can create new account, verify it, and visit project which invited" do
p = Project.create(name: "Invited-project")
p.add_invitation(email: TEST_USER_EMAIL, inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
visit "/create-account"
fill_in "Full Name", with: "John Doe"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
expect(page).to have_no_content "By using Ubicloud console you agree to our"
click_button "Create Account"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-account.+?)"/)[1]
visit verify_link
expect(page.title).to eq("Ubicloud - Verify Account")
click_button "Verify Account"
expect(page.title).to eq("Ubicloud - Default Dashboard")
visit "#{p.path}/dashboard"
expect(page.title).to eq("Ubicloud - #{p.name} Dashboard")
end
it "can not create new account without cloudflare turnstile key if turnstile usage enabled" do
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return("cf_site_key").thrice
visit "/create-account"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Full Name", with: "Joe User"
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
click_button "Create Account"
expect(page.title).to eq("Ubicloud - Create Account")
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_content("Could not create account. Please ensure JavaScript is enabled and access to Cloudflare is not blocked, then try again.")
end
it "can create new account, verify it, and visit project which invited with default policy" do
p = Project.create(name: "Invited-project")
subject_id = SubjectTag.create(project_id: p.id, name: "Admin").id
AccessControlEntry.create(project_id: p.id, subject_id:, action_id: ActionType::NAME_MAP["Project:view"])
p.add_invitation(email: TEST_USER_EMAIL, policy: "Admin", inviter_id: "bd3479c6-5ee3-894c-8694-5190b76f84cf", expires_at: Time.now + 7 * 24 * 60 * 60)
expect(Config).to receive(:managed_service).and_return(true).at_least(:once)
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return("1")
visit "/create-account"
expect(page.find(".cf-turnstile")["data-sitekey"]).to eq "1"
expect(Config).to receive(:cloudflare_turnstile_site_key).and_return(nil).at_least(:once)
fill_in "Full Name", with: "John Doe"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
expect(page).to have_content "By using Ubicloud console you agree to our"
click_button "Create Account"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-account.+?)"/)[1]
visit verify_link
expect(page.title).to eq("Ubicloud - Verify Account")
click_button "Verify Account"
expect(page.title).to eq("Ubicloud - Default Dashboard")
visit p.path
expect(page.title).to eq("Ubicloud - #{p.name}")
end
it "can remember login" do
account = create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
check "Remember me"
click_button "Sign in"
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
expect(DB[:account_remember_keys].first(id: account.id)).not_to be_nil
end
it "has correct current user when logged in via remember token" do
account = create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
check "Remember me"
click_button "Sign in"
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
page.driver.browser.rack_mock_session.cookie_jar.delete("_Clover.session")
page.refresh
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
end
it "can reset password" do
create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
click_link "Forgot your password?"
click_button "Request Password Reset"
expect(page).to have_flash_notice("An email has been sent to you with a link to reset the password for your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
reset_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/reset-password.+?)"/)[1]
visit reset_link
expect(page.title).to eq("Ubicloud - Reset Password")
fill_in "Password", with: "#{TEST_USER_PASSWORD}_new"
fill_in "Password Confirmation", with: "#{TEST_USER_PASSWORD}_new"
click_button "Reset Password"
expect(page.title).to eq("Ubicloud - Login")
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: "#{TEST_USER_PASSWORD}_new"
click_button "Sign in"
end
it "can not reset password if password disabled" do
account = create_account
DB[:account_password_hashes].where(id: account.id).delete
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_no_content("Forget your password?")
visit "/reset-password-request"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Request Password Reset"
expect(page).to have_flash_error(/Login with password is not enabled for this account.*/)
expect(DB[:account_password_reset_keys].count).to eq 0
end
it "can login to an account when there are no omniauth_providers" do
create_account(with_project: false)
expect(Config).to receive(:omniauth_google_id).and_return(nil)
expect(Config).to receive(:omniauth_github_id).and_return(nil)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - Projects")
end
it "can login to an account without projects" do
create_account(with_project: false)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - Projects")
end
it "can not login if the account is suspended" do
account = create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
account.suspend
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error(/Your account has been suspended.*/)
end
it "can not login if the account is suspended via remember token" do
account = create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
check "Remember me"
click_button "Sign in"
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
page.driver.browser.rack_mock_session.cookie_jar.delete("_Clover.session")
account.suspend
page.refresh
expect(page.title).to eq("Ubicloud - Login")
end
it "redirects to otp page if the otp is only 2FA method" do
create_account(enable_otp: true)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - 2FA - One-Time Password")
end
it "redirects to webauthn page if the webauthn is only 2FA method" do
create_account(enable_webauthn: true)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - 2FA - Security Keys")
end
it "shows 2FA method list if there are multiple 2FA methods" do
create_account(enable_otp: true, enable_webauthn: true)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - Two-factor Authentication")
end
it "shows enter recovery codes page" do
create_account(enable_otp: true)
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
click_link "Enter a recovery code"
expect(page.title).to eq("Ubicloud - 2FA - Recovery Codes")
end
describe "authenticated" do
before do
create_account
login
end
it "redirects root to dashboard" do
visit "/dashboard"
expect(page).to have_current_path("/dashboard")
end
it "can logout" do
visit "/dashboard"
click_button "Log out"
expect(page.title).to eq("Ubicloud - Login")
end
[true, false].each do |logged_in|
it "can change email, verifying when #{"not " unless logged_in}logged in" do
visit "/clear-last-password-entry" if logged_in
new_email = "new@example.com"
visit "/account/change-login"
fill_in "New Email Address", with: new_email
fill_in "Password", with: TEST_USER_PASSWORD if logged_in
click_button "Change Email"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your login change")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-login-change.+?)"/)[1]
click_button "Log out" unless logged_in
visit verify_link
expect(page.title).to eq("Ubicloud - Verify New Email")
expect(page).to have_content("Verify your new email") unless logged_in
click_button "Click to Verify New Email"
expect(page).to have_flash_notice "Your login change has been verified"
if logged_in
expect(page.title).to eq("Ubicloud - Default Dashboard")
click_button "Log out"
end
expect(page.title).to eq("Ubicloud - Login")
fill_in "Email Address", with: new_email
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page).to have_flash_notice "You have been logged in"
end
end
it "can create password for accounts that do not have a password" do
DB[:account_password_hashes].delete
visit "/account/change-password"
expect(page.title).to eq("Ubicloud - Create Password")
fill_in "New Password", with: "#{TEST_USER_PASSWORD}_new"
fill_in "New Password Confirmation", with: "#{TEST_USER_PASSWORD}_new"
click_button "Create Password"
expect(page.title).to eq("Ubicloud - Change Password")
click_button "Log out"
expect(page.title).to eq("Ubicloud - Login")
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: "#{TEST_USER_PASSWORD}_new"
click_button "Sign in"
end
[true, false].each do |clear_last_password_entry|
it "can change password when password entry is #{"not " unless clear_last_password_entry}required" do
visit "/clear-last-password-entry" if clear_last_password_entry
visit "/account/change-password"
fill_in "Current Password", with: TEST_USER_PASSWORD if clear_last_password_entry
fill_in "New Password", with: "#{TEST_USER_PASSWORD}_new"
fill_in "New Password Confirmation", with: "#{TEST_USER_PASSWORD}_new"
click_button "Change Password"
expect(page.title).to eq("Ubicloud - Change Password")
click_button "Log out"
expect(page.title).to eq("Ubicloud - Login")
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
fill_in "Password", with: "#{TEST_USER_PASSWORD}_new"
click_button "Sign in"
end
it "can close account when password entry is #{"not " unless clear_last_password_entry}required" do
visit "/clear-last-password-entry" if clear_last_password_entry
account = Account[email: TEST_USER_EMAIL]
UsageAlert.create(project_id: account.projects.first.id, user_id: account.id, name: "test", limit: 100)
visit "/account/close-account"
fill_in "Password", with: TEST_USER_PASSWORD if clear_last_password_entry
click_button "Close Account"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_notice("Your account has been closed")
expect(Account[email: TEST_USER_EMAIL]).to be_nil
expect(DB[:access_tag].where(hyper_tag_id: account.id).count).to eq 0
end
end
it "can not close account if the project has some resources" do
vm = create_vm
project = Account[email: TEST_USER_EMAIL].projects.first
vm.update(project_id: project.id)
visit "/account/close-account"
click_button "Close Account"
expect(page.title).to eq("Ubicloud - Close Account")
expect(page).to have_flash_error("'#{project.name}' project has some resources. Delete all related resources first.")
end
end
describe "social login" do
def mock_provider(provider, email = TEST_USER_EMAIL, name: "John Doe", mock_config: true)
expect(Config).to receive("omniauth_#{provider}_id").and_return("12345").at_least(:once) if mock_config
OmniAuth.config.add_mock(provider, {
provider: provider,
uid: "123456790",
info: {
name:,
email: email
}
})
end
let(:oidc_provider) do
OidcProvider.create(
display_name: "TestOIDC",
client_id: "123",
client_secret: "456",
url: "http://example.com",
authorization_endpoint: "/auth",
token_endpoint: "/tok",
userinfo_endpoint: "/ui",
jwks_uri: "https://host/jw"
)
end
before do
OmniAuth.config.logger = Logger.new(IO::NULL)
OmniAuth.config.test_mode = true
end
[true, false].each do |locked_domain|
it "can login via OIDC flow using separate login page#{" when domain is locked" if locked_domain}" do
visit "/auth/#{OidcProvider.generate_ubid}"
expect(page.status_code).to eq 404
provider = oidc_provider
provider.add_locked_domain(domain: "Example.com") if locked_domain
omniauth_key = provider.ubid.to_sym
visit "/auth/#{provider.ubid}"
expect(page.status_code).to eq 200
expect(page.title).to eq "Ubicloud - Login to TestOIDC via OIDC"
OmniAuth.config.mock_auth[omniauth_key] = :invalid_credentials
click_button "Login"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error("There was an error logging in with the external provider")
visit "/auth/#{provider.ubid}"
OmniAuth.config.add_mock(omniauth_key, provider: provider.ubid, uid: "789",
info: {email: "user@example.com"})
click_button "Login"
account = Account.first
expect(account.email).to eq "user@example.com"
expect(AccountIdentity.select_hash(:account_id, :provider)).to eq(account.id => provider.ubid)
expect(page.title).to eq("Ubicloud - Default Dashboard")
expect(page).to have_flash_notice("You have been logged in")
end
end
it "cannot login to an account via password when domain is locked" do
oidc_provider.add_locked_domain(domain: "Example.com")
account = create_account
visit "/login"
fill_in "Email Address", with: account.email
click_button "Sign in"
fill_in "Password", with: TEST_USER_PASSWORD
click_button "Sign in"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error("Login via username and password is not supported for the example.com domain. You must authenticate using TestOIDC.")
end
it "disallow attempting to verify an account in a locked domain" do
visit "/create-account"
fill_in "Full Name", with: "John Doe"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
expect(page).to have_no_content "By using Ubicloud console you agree to our"
click_button "Create Account"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-account.+?)"/)[1]
oidc_provider.add_locked_domain(domain: "Example.com")
visit verify_link
click_button "Verify Account"
expect(page).to have_flash_error("Verifying accounts is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
expect(Account.all).to eq []
end
it "attempting to create an account in a locked domain redirects to required OIDC login page" do
oidc_provider.add_locked_domain(domain: "Example.com")
visit "/create-account"
fill_in "Email Address", with: TEST_USER_EMAIL
fill_in "Full Name", with: "John Doe"
fill_in "Password", with: TEST_USER_PASSWORD
fill_in "Password Confirmation", with: TEST_USER_PASSWORD
click_button "Create Account"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Creating accounts with a password is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
end
it "attempting to reset the password for an account in a locked domain redirects to required OIDC login page" do
create_account
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
click_link "Forgot your password?"
click_button "Request Password Reset"
expect(page).to have_flash_notice("An email has been sent to you with a link to reset the password for your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
reset_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/reset-password.+?)"/)[1]
oidc_provider.add_locked_domain(domain: "Example.com")
visit reset_link
fill_in "Password", with: "#{TEST_USER_PASSWORD}_new"
fill_in "Password Confirmation", with: "#{TEST_USER_PASSWORD}_new"
click_button "Reset Password"
expect(page).to have_flash_error("Resetting passwords is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
end
it "requesting a password reset for an account in a locked domain redirects to required OIDC login page" do
oidc_provider.add_locked_domain(domain: "Example.com")
create_account
visit "/login"
click_link "Forgot your password?"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Request Password Reset"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Resetting passwords is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
end
it "attempting to unlock an account in a locked domain redirects to required OIDC login page" do
account = create_account
DB[:account_lockouts].insert(id: account.id, key: SecureRandom.urlsafe_base64(32))
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
click_button "Request Account Unlock"
expect(page).to have_flash_notice("An email has been sent to you with a link to unlock your account")
expect(Mail::TestMailer.deliveries.length).to eq 1
unlock_link = Mail::TestMailer.deliveries.first.body.match(/(\/unlock-account[^ ]+)/)[1]
oidc_provider.add_locked_domain(domain: "Example.com")
visit unlock_link
click_button "Unlock Account"
expect(page).to have_flash_error("Unlocking accounts is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
end
it "requesting an account unlock for an account in a locked domain redirects to required OIDC login page" do
oidc_provider.add_locked_domain(domain: "Example.com")
account = create_account
DB[:account_lockouts].insert(id: account.id, key: SecureRandom.urlsafe_base64(32))
visit "/login"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
click_button "Request Account Unlock"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Unlocking accounts is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(page).to have_current_path "/auth/#{oidc_provider.ubid}"
end
it "cannot login to an account via an omniauth provider when domain is locked to a different provider" do
provider = OidcProvider.create(
display_name: "TestOIDC2",
client_id: "123",
client_secret: "456",
url: "http://example.com",
authorization_endpoint: "/auth",
token_endpoint: "/tok",
userinfo_endpoint: "/ui",
jwks_uri: "https://host/jw"
)
visit "/auth/#{provider.ubid}"
OmniAuth.config.add_mock(provider.ubid.to_sym, provider: provider.ubid, uid: "789",
info: {email: "user@example.com"})
click_button "Login"
account = Account.first
expect(account.email).to eq "user@example.com"
expect(AccountIdentity.select_hash(:account_id, :provider)).to eq(account.id => provider.ubid)
click_button "Log out"
oidc_provider.add_locked_domain(domain: "Example.com")
visit "/auth/#{provider.ubid}"
click_button "Login"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error("Login via TestOIDC2 is not supported for the example.com domain. You must authenticate using TestOIDC.")
end
it "cannot create account via an omniauth provider when domain is locked to a different provider" do
oidc_provider.add_locked_domain(domain: "Example.com")
provider = OidcProvider.create(
display_name: "TestOIDC2",
client_id: "123",
client_secret: "456",
url: "http://example.com",
authorization_endpoint: "/auth",
token_endpoint: "/tok",
userinfo_endpoint: "/ui",
jwks_uri: "https://host/jw"
)
visit "/auth/#{provider.ubid}"
OmniAuth.config.add_mock(provider.ubid.to_sym, provider: provider.ubid, uid: "789",
info: {email: "user@example.com"})
click_button "Login"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error("Creating an account via authentication through TestOIDC2 is not supported for the example.com domain. You must authenticate using TestOIDC.")
expect(Account.all).to eq []
expect(AccountIdentity.all).to eq []
end
it "shows error message if attempting to create an account where social login has no email" do
mock_provider(:github, nil)
visit "/login"
click_button "GitHub"
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error(/Social login is only allowed if social login provider provides email/)
end
it "can create new account even if social account doesn't have a name" do
mock_provider(:github, name: nil)
visit "/login"
click_button "GitHub"
expect(Account[email: TEST_USER_EMAIL].name).to eq "User"
end
it "can create new account" do
mock_provider(:github)
visit "/login"
click_button "GitHub"
account = Account[email: TEST_USER_EMAIL]
expect(account).not_to be_nil
expect(account.identities_dataset.first(provider: "github", uid: "123456790")).not_to be_nil
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
end
it "can login existing account" do
mock_provider(:google)
account = create_account
account.add_identity(provider: "google", uid: "123456790")
visit "/login"
click_button "Google"
expect(Account.count).to eq(1)
expect(AccountIdentity.count).to eq(1)
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - #{account.projects.first.name} Dashboard")
end
it "can not login existing account before linking it" do
mock_provider(:github)
create_account
visit "/login"
click_button "GitHub"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Login")
expect(page).to have_flash_error(/There is already an account with this email address.*/)
end
describe "authenticated" do
let(:account) { create_account }
before do
login(account.email)
end
it "can connect and disconnect existing account to OIDC provider" do
provider = oidc_provider
omniauth_key = provider.ubid.to_sym
visit "/account/login-method/oidc"
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_error("No valid OIDC provider with that ID")
visit "/account/login-method"
within "#login-method-connect-oidc" do
fill_in "OIDC Provider ID", with: provider.ubid
click_button "Connect"
end
expect(AccountIdentity).to be_empty
OmniAuth.config.mock_auth[omniauth_key] = :invalid_credentials
click_button "Connect"
expect(AccountIdentity).to be_empty
expect(page.title).to eq("Ubicloud - Default Dashboard")
expect(page).to have_flash_error("There was an error logging in with the external provider")
visit "/account/login-method"
within "#login-method-connect-oidc" do
fill_in "OIDC Provider ID", with: provider.ubid
click_button "Connect"
end
OmniAuth.config.add_mock(omniauth_key, provider: provider.ubid, uid: "789",
info: {email: account.email})
click_button "Connect"
expect(AccountIdentity.select_hash(:account_id, :provider)).to eq(account.id => provider.ubid)
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_notice("You have successfully connected your account with TestOIDC.")
within "#login-method-disconnect-oidc-#{provider.ubid}" do
click_button "Disconnect"
end
expect(AccountIdentity).to be_empty
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_notice("Your account has been disconnected from TestOIDC")
ensure
OmniAuth.config.mock_auth[omniauth_key] = nil
end
[true, false].each do |locked_domain|
it "can login via OIDC flow with already connected account using normal login page#{" when domain is locked" if locked_domain}" do
omniauth_key = oidc_provider.ubid.to_sym
oidc_provider.add_locked_domain(domain: "Example.com") if locked_domain
AccountIdentity.create(account_id: Account.first.id, provider: oidc_provider.ubid, uid: "789")
OmniAuth.config.add_mock(omniauth_key, provider: oidc_provider.ubid, uid: "789",
info: {email: "user@example.com"})
click_button "Log out"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_content("Password")
expect(page).to have_content("Or login with:")
click_button "TestOIDC"
expect(page.title).to eq("Ubicloud - Default Dashboard")
expect(page).to have_flash_notice("You have been logged in")
end
it "can login via OIDC flow with already connected account using normal login page when account does not have password#{" when domain is locked" if locked_domain}" do
omniauth_key = oidc_provider.ubid.to_sym
oidc_provider.add_locked_domain(domain: "Example.com") if locked_domain
account_id = Account.first.id
AccountIdentity.create(account_id:, provider: oidc_provider.ubid, uid: "789")
AccountIdentity.create(account_id:, provider: "google", uid: "123")
mock_provider(:google, "uSer@example.com")
OmniAuth.config.add_mock(omniauth_key, provider: oidc_provider.ubid, uid: "789",
info: {email: "user@example.com"})
DB[:account_password_hashes].delete
click_button "Log out"
fill_in "Email Address", with: TEST_USER_EMAIL
click_button "Sign in"
expect(page).to have_no_content("Password")
expect(page).to have_content("Login with:")
expect(page).to have_content("Google")
click_button "TestOIDC"
expect(page.title).to eq("Ubicloud - Default Dashboard")
expect(page).to have_flash_notice("You have been logged in")
end
end
it "can connect to existing account" do
mock_provider(:github, "uSer@example.com")
visit "/account/login-method"
within "#login-method-github" do
click_button "Connect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_notice("You have successfully connected your account with GitHub.")
end
it "can disconnect from existing account" do
account.add_identity(provider: "google", uid: "123456790")
account.add_identity(provider: "github", uid: "123456790")
visit "/account/login-method"
within "#login-method-github" do
click_button "Disconnect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_notice("Your account has been disconnected from GitHub")
end
it "can delete password if another login method is available" do
account.add_identity(provider: "google", uid: "123456790")
visit "/account/login-method"
within "#login-method-password" do
click_button "Delete"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_notice("Your password has been deleted")
end
it "can not disconnect the last login method if has no password" do
DB[:account_password_hashes].where(id: account.id).delete
account.add_identity(provider: "github", uid: "123456790")
visit "/account/login-method"
within "#login-method-github" do
click_button "Disconnect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_error("You must have at least one login method")
end
it "can not disconnect if it's already disconnected" do
account.add_identity(provider: "google", uid: "123456790")
account.add_identity(provider: "github", uid: "123456790")
visit "/account/login-method"
account.identities_dataset.first(provider: "github").update(uid: "0987654321")
within "#login-method-github" do
click_button "Disconnect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_error("Your account already has been disconnected from GitHub")
end
it "can not connect an account with different email" do
mock_provider(:github, "user2@example.com")
visit "/account/login-method"
within "#login-method-github" do
click_button "Connect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_error("Your account's email address is different from the email address associated with the GitHub account.")
end
it "can not connect a social account with multiple accounts" do
create_account("user2@example.com")
mock_provider(:github, "user2@example.com")
visit "/account/login-method"
within "#login-method-github" do
click_button "Connect"
end
expect(page.title).to eq("Ubicloud - Login Methods")
expect(page).to have_flash_error("Your account's email address is different from the email address associated with the GitHub account.")
end
it "disallows changing password for an account in a locked domain" do
oidc_provider.add_locked_domain(domain: "Example.com")
visit "/account/change-password"
fill_in "New Password", with: "#{TEST_USER_PASSWORD}_new"
fill_in "New Password Confirmation", with: "#{TEST_USER_PASSWORD}_new"
click_button "Change Password"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Changing passwords is not supported for the example.com domain.")
expect(page.title).to eq("Ubicloud - Default Dashboard")
end
it "disallows requesting a login change for an account in a locked domain" do
oidc_provider.add_locked_domain(domain: "Example.com")
visit "/account/change-login"
fill_in "New Email Address", with: "user@other-example.com"
click_button "Change Email"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Changing email addresses is not supported for the example.com domain.")
expect(page.title).to eq("Ubicloud - Default Dashboard")
end
it "disallows changing login for an account in a locked domain" do
visit "/account/change-login"
fill_in "New Email Address", with: "user@other-example.com"
click_button "Change Email"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your login change")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-login-change.+?)"/)[1]
oidc_provider.add_locked_domain(domain: "Example.com")
visit verify_link
click_button "Click to Verify New Email"
expect(page).to have_flash_error("Changing email addresses is not supported for the example.com domain.")
expect(page.title).to eq("Ubicloud - Default Dashboard")
end
it "disallows requesting a login change for an account to a locked domain" do
oidc_provider.add_locked_domain(domain: "other-example.com")
visit "/account/change-login"
fill_in "New Email Address", with: "user@other-example.com"
click_button "Change Email"
expect(Mail::TestMailer.deliveries.length).to eq 0
expect(page).to have_flash_error("Changing email addresses is not supported for the other-example.com domain.")
expect(page.title).to eq("Ubicloud - Default Dashboard")
end
it "disallows changing login for an account to a locked domain" do
visit "/account/change-login"
fill_in "New Email Address", with: "user@other-example.com"
click_button "Change Email"
expect(page).to have_flash_notice("An email has been sent to you with a link to verify your login change")
expect(Mail::TestMailer.deliveries.length).to eq 1
verify_link = Mail::TestMailer.deliveries.first.html_part.body.match(/(\/verify-login-change.+?)"/)[1]
oidc_provider.add_locked_domain(domain: "other-example.com")
visit verify_link
click_button "Click to Verify New Email"
expect(page).to have_flash_error("Changing email addresses is not supported for the other-example.com domain.")
expect(page.title).to eq("Ubicloud - Default Dashboard")
end
it "hides login methods, change password, and change emails options on My Account page" do
oidc_provider.add_locked_domain(domain: "Example.com")
visit "/account"
expect(page).to have_no_content("Login Methods")
expect(page).to have_no_content("Change Password")
expect(page).to have_no_content("Change Email")
end
end
end
end