From 8773d5d1573375d5b881c50a67485487e92ef8cf Mon Sep 17 00:00:00 2001 From: Timshel Date: Mon, 2 Jun 2025 17:40:55 +0200 Subject: [PATCH] Fix enforceOnLogin org policies --- playwright/global-utils.ts | 8 +++++++ playwright/tests/login.spec.ts | 6 +----- playwright/tests/setups/orgs.ts | 14 +++++++++++- playwright/tests/sso_organization.spec.ts | 26 +++++++++++++++++++++++ src/api/core/accounts.rs | 6 +----- src/api/mod.rs | 6 +++--- 6 files changed, 52 insertions(+), 14 deletions(-) diff --git a/playwright/global-utils.ts b/playwright/global-utils.ts index 38aa2226..4177e258 100644 --- a/playwright/global-utils.ts +++ b/playwright/global-utils.ts @@ -225,3 +225,11 @@ export async function cleanLanding(page: Page) { await page.getByRole('button', { name: 'Log out' }).click(); } } + +export async function logout(test: Test, page: Page, user: { name: string }) { + await test.step('logout', async () => { + await page.getByRole('button', { name: user.name, exact: true }).click(); + await page.getByRole('menuitem', { name: 'Log out' }).click(); + await expect(page.getByRole('heading', { name: 'Log in' })).toBeVisible(); + }); +} diff --git a/playwright/tests/login.spec.ts b/playwright/tests/login.spec.ts index ba21cc00..aaac4708 100644 --- a/playwright/tests/login.spec.ts +++ b/playwright/tests/login.spec.ts @@ -29,11 +29,7 @@ test('Authenticator 2fa', async ({ page }) => { let totp = await activateTOTP(test, page, users.user1); - await test.step('logout', async () => { - await page.getByRole('button', { name: users.user1.name }).click(); - await page.getByRole('menuitem', { name: 'Log out' }).click(); - await expect(page.getByRole('heading', { name: 'Log in' })).toBeVisible(); - }); + await utils.logout(test, page, users.user1); await test.step('login', async () => { let timestamp = Date.now(); // Needed to use the next token diff --git a/playwright/tests/setups/orgs.ts b/playwright/tests/setups/orgs.ts index e38cb278..ba61b46a 100644 --- a/playwright/tests/setups/orgs.ts +++ b/playwright/tests/setups/orgs.ts @@ -14,8 +14,20 @@ export async function create(test, page: Page, name: string) { }); } +export async function policies(test, page: Page, name: string) { + await test.step(`Navigate to ${name} policies`, async () => { + await page.locator('a').filter({ hasText: 'Admin Console' }).first().click(); + await page.locator('org-switcher').getByLabel(/Toggle collapse/).click(); + await page.locator('org-switcher').getByRole('link', { name: `${name}` }).first().click(); + await expect(page.getByRole('heading', { name: `${name} collections` })).toBeVisible(); + await page.getByRole('button', { name: 'Toggle collapse Settings' }).click(); + await page.getByRole('link', { name: 'Policies' }).click(); + await expect(page.getByRole('heading', { name: 'Policies' })).toBeVisible(); + }); +} + export async function members(test, page: Page, name: string) { - await test.step(`Navigate to ${name}`, async () => { + await test.step(`Navigate to ${name} members`, async () => { await page.locator('a').filter({ hasText: 'Admin Console' }).first().click(); await page.locator('org-switcher').getByLabel(/Toggle collapse/).click(); await page.locator('org-switcher').getByRole('link', { name: `${name}` }).first().click(); diff --git a/playwright/tests/sso_organization.spec.ts b/playwright/tests/sso_organization.spec.ts index 68ead019..c215ce3b 100644 --- a/playwright/tests/sso_organization.spec.ts +++ b/playwright/tests/sso_organization.spec.ts @@ -48,3 +48,29 @@ test('Organization is visible', async ({ page }) => { await page.getByLabel('vault: /Test').click(); await expect(page.getByLabel('Filter: Default collection')).toBeVisible(); }); + +test('Enforce password policy', async ({ page }) => { + await logUser(test, page, users.user1); + await orgs.policies(test, page, '/Test'); + + await test.step(`Set master password policy`, async () => { + await page.getByRole('button', { name: 'Master password requirements' }).click(); + await page.getByRole('checkbox', { name: 'Turn on' }).check(); + await page.getByRole('checkbox', { name: 'Require existing members to' }).check(); + await page.getByRole('spinbutton', { name: 'Minimum length' }).fill('42'); + await page.getByRole('button', { name: 'Save' }).click(); + await utils.checkNotification(page, 'Edited policy Master password requirements.'); + }); + + await utils.logout(test, page, users.user1); + + await test.step(`Unlock trigger policy`, async () => { + await page.getByRole('textbox', { name: 'Email address (required)' }).fill(users.user1.email); + await page.getByRole('button', { name: 'Use single sign-on' }).click(); + + await page.getByRole('textbox', { name: 'Master password (required)' }).fill(users.user1.password); + await page.getByRole('button', { name: 'Unlock' }).click(); + + await expect(page.getByRole('heading', { name: 'Update master password' })).toBeVisible(); + }); +}); diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index bb7dbbd3..4aea371e 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -1163,8 +1163,6 @@ pub async fn kdf_upgrade(user: &mut User, pwd_hash: &str, conn: &mut DbConn) -> Ok(()) } -// It appears that at the moment the return policy is required but ignored. -// As such the `enforceOnLogin` part is not working. #[post("/accounts/verify-password", data = "")] async fn verify_password(data: Json, headers: Headers, mut conn: DbConn) -> JsonResult { let data: SecretVerificationRequest = data.into_inner(); @@ -1176,9 +1174,7 @@ async fn verify_password(data: Json, headers: Headers kdf_upgrade(&mut user, &data.master_password_hash, &mut conn).await?; - Ok(Json(json!({ - "MasterPasswordPolicy": master_password_policy(&user, &conn).await, - }))) + Ok(Json(master_password_policy(&user, &conn).await)) } async fn _api_key(data: Json, rotate: bool, headers: Headers, mut conn: DbConn) -> JsonResult { diff --git a/src/api/mod.rs b/src/api/mod.rs index 1686671f..558e94ee 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -73,11 +73,11 @@ impl PasswordOrOtpData { } } -#[derive(Default, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct MasterPasswordPolicy { - min_complexity: u8, - min_length: u32, + min_complexity: Option, + min_length: Option, require_lower: bool, require_upper: bool, require_numbers: bool,