mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-14 12:32:44 +00:00
fix(frontend): mfa test reauth dialogs (#13273)
* fix the stepts that need a password now * use different selectors to make more robust * fix popup breaking flow * support password reauth in reauth
This commit is contained in:
1 parent
435cdaafbc
commit
de77fc4293
4 files changed
+58
-20
No files matched your search
@@ -25,10 +25,22 @@ import { useServerApiState } from '../../states/ServerApiState';
|
||||
export function getReauthFlow(err: any): FlowEnum | null {
|
||||
const flows: any = err.response?.data?.data?.flows ?? [];
|
||||
|
||||
if (flows.find((flow: any) => flow.id == FlowEnum.MfaReauthenticate)) {
|
||||
const mfaFlow = flows.find(
|
||||
(flow: any) => flow.id == FlowEnum.MfaReauthenticate
|
||||
);
|
||||
const mfaSupported = mfaFlow?.types.includes('totp');
|
||||
const passwordFlow = flows.find(
|
||||
(flow: any) => flow.id == FlowEnum.Reauthenticate
|
||||
);
|
||||
|
||||
if (mfaSupported) {
|
||||
return FlowEnum.MfaReauthenticate;
|
||||
} else if (flows.find((flow: any) => flow.id == FlowEnum.Reauthenticate)) {
|
||||
// this can happen if you only have webauthn and mail but not totp/recovery
|
||||
} else if (passwordFlow) {
|
||||
return FlowEnum.Reauthenticate;
|
||||
// fallback for other flows
|
||||
} else if (mfaFlow) {
|
||||
return FlowEnum.MfaReauthenticate;
|
||||
} else {
|
||||
// No match found
|
||||
return null;
|
||||
|
||||
@@ -291,3 +291,27 @@ export const waitForDataOutput = async (
|
||||
page.off('popup', closePopup);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a re-authentication (password) prompt, if one is displayed.
|
||||
*/
|
||||
// Handle a re-authentication prompt (if displayed)
|
||||
export const reauthenticate = async (
|
||||
page: Page,
|
||||
password: string,
|
||||
time = 3000
|
||||
) => {
|
||||
const dialog = page.getByRole('dialog', { name: 'Reauthenticate' });
|
||||
const visible = await dialog
|
||||
.waitFor({ timeout: time })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (visible) {
|
||||
await dialog.locator('input[type="password"]').fill(password);
|
||||
await dialog.getByRole('button', { name: 'Submit', exact: true }).click();
|
||||
await dialog.waitFor({ state: 'hidden' });
|
||||
}
|
||||
|
||||
return visible;
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import type { Locator, Page } from '@playwright/test';
|
||||
import { TOTP } from 'otpauth';
|
||||
import { expect, test } from './baseFixtures.js';
|
||||
import { engineeruser, logoutUrl, mfauser, noaccessuser } from './defaults.js';
|
||||
import { navigate, openDetailAction } from './helpers.js';
|
||||
import { navigate, openDetailAction, reauthenticate } from './helpers.js';
|
||||
import { doLogin } from './login.js';
|
||||
|
||||
const stripQueryAndHash = (url: string): string => {
|
||||
@@ -487,10 +487,21 @@ test('Login - MFA - TOTP', async ({ page }) => {
|
||||
|
||||
await page.getByRole('textbox', { name: 'text-input-otp-code' }).fill(token);
|
||||
await page.getByRole('button', { name: 'Submit', exact: true }).click();
|
||||
|
||||
// re-authentication; needs a new login
|
||||
const submitButton = page.getByRole('button', {
|
||||
name: 'Submit',
|
||||
exact: true
|
||||
});
|
||||
if (await reauthenticate(page, noaccessuser.testcred ?? '', 5000)) {
|
||||
await submitButton.click();
|
||||
}
|
||||
|
||||
await page.getByText('TOTP token registered successfully').waitFor();
|
||||
|
||||
// View recovery codes
|
||||
await page.getByRole('button', { name: 'view-recovery-codes' }).click();
|
||||
await reauthenticate(page, noaccessuser.testcred ?? '');
|
||||
await page
|
||||
.getByText('The following one time recovery codes are available')
|
||||
.waitFor();
|
||||
@@ -499,6 +510,7 @@ test('Login - MFA - TOTP', async ({ page }) => {
|
||||
// Remove TOTP token
|
||||
await page.getByRole('button', { name: 'remove-totp' }).click();
|
||||
await page.getByRole('button', { name: 'Remove', exact: true }).click();
|
||||
await reauthenticate(page, noaccessuser.testcred ?? '');
|
||||
|
||||
await page.getByText('TOTP token removed successfully').waitFor();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { Page } from '@playwright/test';
|
||||
import { createApi } from './api.js';
|
||||
import { expect, generateUUID, test } from './baseFixtures.js';
|
||||
import { logoutUrl } from './defaults.js';
|
||||
import { navigate } from './helpers.js';
|
||||
import { navigate, reauthenticate } from './helpers.js';
|
||||
import { doLogin } from './login.js';
|
||||
|
||||
// Attach a virtual authenticator to the provided page
|
||||
@@ -51,20 +51,6 @@ const createUser = async (username: string, password: string) => {
|
||||
return user.pk;
|
||||
};
|
||||
|
||||
// Handle a re-authentication prompt (if displayed)
|
||||
const reauthenticate = async (page: Page, password: string) => {
|
||||
const field = page.getByLabel('text-field-password');
|
||||
const visible = await field
|
||||
.waitFor({ timeout: 3000 })
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (visible) {
|
||||
await field.fill(password);
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
}
|
||||
};
|
||||
|
||||
test('Login - MFA - WebAuthn', async ({ page, browserName }) => {
|
||||
// The virtual authenticator requires the Chrome DevTools Protocol
|
||||
test.skip(
|
||||
@@ -97,7 +83,9 @@ test('Login - MFA - WebAuthn', async ({ page, browserName }) => {
|
||||
.getByRole('button', { name: 'Multi-Factor Authentication' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'add-factor-webauthn' }).click();
|
||||
await reauthenticate(page, password);
|
||||
if (await reauthenticate(page, password)) {
|
||||
await page.getByRole('button', { name: 'add-factor-webauthn' }).click();
|
||||
}
|
||||
await page
|
||||
.getByText('WebAuthn credential registered successfully')
|
||||
.waitFor();
|
||||
@@ -125,7 +113,9 @@ test('Login - MFA - WebAuthn', async ({ page, browserName }) => {
|
||||
await page.getByRole('button', { name: 'remove-webauthn' }).click();
|
||||
await page.getByText('Remove WebAuthn Credential').waitFor();
|
||||
await page.getByRole('button', { name: 'Remove', exact: true }).click();
|
||||
await reauthenticate(page, password);
|
||||
if (await reauthenticate(page, password)) {
|
||||
await page.getByRole('button', { name: 'Remove', exact: true }).click();
|
||||
}
|
||||
await page.getByText('WebAuthn credential removed successfully').waitFor();
|
||||
} finally {
|
||||
const api = await createApi({});
|
||||
|
||||
Reference in new issue
Block a user