Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions e2e/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export const routes = {
overview: '/admin/overview',
settings: '/admin/settings',
devices: '/admin/devices',
groups: '/admin/groups',
webhooks: '/admin/webhooks'
},
authorize: '/api/v1/oauth/authorize',
};
Expand Down
133 changes: 128 additions & 5 deletions e2e/tests/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createUser } from '../utils/controllers/createUser';
import { loginBasic, loginRecoveryCodes, loginTOTP } from '../utils/controllers/login';
import { logout } from '../utils/controllers/logout';
import { enableEmailMFA } from '../utils/controllers/mfa/enableEmail';
import { enableSecurityKey } from '../utils/controllers/mfa/enableSecurityKey';
import { enableTOTP } from '../utils/controllers/mfa/enableTOTP';
import { changePassword, changePasswordByAdmin } from '../utils/controllers/profile';
import { disableUser } from '../utils/controllers/toggleUserState';
Expand All @@ -30,15 +31,15 @@ test.describe('Test user authentication', () => {
expect(page.url()).toBe(routes.base + routes.admin.wizard);
});

test('Create user and login as him', async ({ page, browser }) => {
test('Create user and log in as him', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser);
await loginBasic(page, testUser);
await waitForRoute(page, routes.me);
expect(page.url()).toBe(routes.base + routes.me);
});

test('Login with admin user TOTP', async ({ page, browser }) => {
test('Log in with admin user TOTP', async ({ page, browser }) => {
await waitForBase(page);
await loginBasic(page, defaultUserAdmin);
const { secret } = await enableTOTP(browser, defaultUserAdmin);
Expand All @@ -48,7 +49,7 @@ test.describe('Test user authentication', () => {
await waitForRoute(page, routes.admin.wizard);
});

test('Login with user TOTP', async ({ page, browser }) => {
test('Log in with user TOTP', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser);
const { secret } = await enableTOTP(browser, testUser);
Expand All @@ -69,7 +70,7 @@ test.describe('Test user authentication', () => {
expect(page.url()).toBe(routes.base + routes.me);
});

test('Login with Email TOTP', async ({ page, browser }) => {
test('Log in with Email TOTP', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser);
const { secret } = await enableEmailMFA(browser, testUser);
Expand All @@ -84,7 +85,7 @@ test.describe('Test user authentication', () => {
await waitForRoute(page, routes.me);
});

test('Login as disabled user', async ({ page, browser }) => {
test('Log in as disabled user', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser);
await disableUser(browser, testUser);
Expand All @@ -110,6 +111,28 @@ test.describe('Test user authentication', () => {
await page.locator('a[href="/me"]').click();
await responsePromise;
});
test('Disable user MFA and log in', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser);
await enableTOTP(browser, testUser);
await loginBasic(page, defaultUserAdmin);
await page.goto(routes.base + routes.admin.users, {
waitUntil: 'networkidle',
});
await page.getByTestId('user-2').locator('.user-edit-cell').click();
await page.getByTestId('disable-mfa-button').click();
await page.waitForTimeout(800);
await page.getByRole('button', { name: 'Disable MFA' }).click();
await page.waitForTimeout(800);
await page.goto(routes.base + routes.admin.users + `/${testUser.username}`, {
waitUntil: 'networkidle',
});
await expect(page.locator('.mfa .status .message')).toHaveText('Disabled');
await logout(page);
await loginBasic(page, testUser);
await page.waitForTimeout(800);
await expect(page.locator('.mfa .status .message')).toHaveText('Disabled');
});
});

test.describe('Test password change', () => {
Expand Down Expand Up @@ -145,3 +168,103 @@ test.describe('Test password change', () => {
expect(page.url()).toBe(routes.base + routes.me);
});
});

test.describe('Test security keys', () => {
let testUser: User;

test.beforeEach(() => {
dockerRestart();
testUser = { ...testUserTemplate, username: 'test' };
});

test('Create user and log in with security key', async ({ page, browser, context }) => {
await waitForBase(page);
await createUser(browser, testUser);
const { credentialId, rpId, privateKey, userHandle } = await enableSecurityKey(
browser,
testUser,
'key_name',
);
await page.goto(routes.base);
await waitForRoute(page, routes.auth.login);
await page.getByTestId('login-form-username').fill(testUser.username);
await page.getByTestId('login-form-password').fill(testUser.password);
await page.getByTestId('login-form-submit').click();
await page.waitForTimeout(1000);

const authenticator = await context.newCDPSession(page);
await authenticator.send('WebAuthn.enable');
const { authenticatorId: loginAuthenticatorId } = await authenticator.send(
'WebAuthn.addVirtualAuthenticator',
{
options: {
protocol: 'ctap2',
transport: 'usb',
hasResidentKey: true,
hasUserVerification: true,
isUserVerified: true,
},
},
);

await authenticator.send('WebAuthn.addCredential', {
authenticatorId: loginAuthenticatorId,
credential: {
credentialId,
isResidentCredential: true,
rpId,
privateKey,
userHandle,
signCount: 1,
},
});
await page.getByTestId('use-security-key').click();
await page.waitForTimeout(2000);
await expect(page.url()).toBe(routes.base + routes.me);
});

test('Add security key to admin and log in', async ({ page, browser, context }) => {
await waitForBase(page);
const { credentialId, rpId, privateKey, userHandle } = await enableSecurityKey(
browser,
defaultUserAdmin,
'key_name',
);
await page.goto(routes.base);
await waitForRoute(page, routes.auth.login);
await page.getByTestId('login-form-username').fill(defaultUserAdmin.username);
await page.getByTestId('login-form-password').fill(defaultUserAdmin.password);
await page.getByTestId('login-form-submit').click();
await page.waitForTimeout(1000);

const authenticator = await context.newCDPSession(page);
await authenticator.send('WebAuthn.enable');
const { authenticatorId: loginAuthenticatorId } = await authenticator.send(
'WebAuthn.addVirtualAuthenticator',
{
options: {
protocol: 'ctap2',
transport: 'usb',
hasResidentKey: true,
hasUserVerification: true,
isUserVerified: true,
},
},
);

await authenticator.send('WebAuthn.addCredential', {
authenticatorId: loginAuthenticatorId,
credential: {
credentialId,
isResidentCredential: true,
rpId,
privateKey,
userHandle,
signCount: 1,
},
});
await page.getByTestId('use-security-key').click();
await page.waitForTimeout(2000);
await expect(page.url()).toBe(routes.base + routes.admin.wizard);
});
});
44 changes: 43 additions & 1 deletion e2e/tests/groups.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { expect, test } from '@playwright/test';

import { routes, testUserTemplate } from '../config';
import { defaultUserAdmin, routes, testUserTemplate } from '../config';
import { createUser } from '../utils/controllers/createUser';
import { createGroup } from '../utils/controllers/groups';
import { loginBasic } from '../utils/controllers/login';
import { dockerRestart } from '../utils/docker';
import { waitForBase } from '../utils/waitForBase';
import { waitForPromise } from '../utils/waitForPromise';
import { waitForRoute } from '../utils/waitForRoute';

test.describe('Test groups', () => {
Expand All @@ -18,4 +20,44 @@ test.describe('Test groups', () => {
await waitForRoute(page, routes.admin.wizard);
expect(page.url()).toBe(routes.base + routes.admin.wizard);
});

test('Bulk assign groups', async ({ page, browser }) => {
const additionalUsers = [
{ ...testUserTemplate, mail: '[email protected]', username: 'test2' },
{ ...testUserTemplate, mail: '[email protected]', username: 'test3' },
];
const test_group_name = 'test_group';
await waitForBase(page);
const testUser1 = { ...testUserTemplate, mail: '[email protected]', username: 'test1' };
await createUser(browser, testUser1, ['Admin']);

for (const newuser of additionalUsers) {
await createUser(browser, newuser);
}
await createGroup(browser, true, test_group_name);
await loginBasic(page, defaultUserAdmin);
await page.goto(routes.base + routes.admin.users, {
waitUntil: 'networkidle',
});

const userCheckboxes = page.locator('[data-testid^="user-"]').locator('.select-cell');
const checkboxCount = await page.locator('[data-testid^="user-"]').count();
for (let i = 0; i < checkboxCount; i++) {
await userCheckboxes.nth(i).click();
}
await page.getByTestId('group-bulk-assign').click();
await page.locator('.groups-container').waitFor({ state: 'visible', timeout: 10000 });
await waitForPromise(2000);
await page.locator('.select-row').nth(2).click();
await page.getByTestId('confirm-bulk-assign').click();
await page.locator('.groups-container').waitFor({ state: 'hidden', timeout: 10000 });
const testGroupElements = page.locator(
`.groups-cell .group .text-container:has-text("${test_group_name}")`,
);

// 2(default admin + user with admin group) + additional users
await expect(testGroupElements).toHaveCount(2 + additionalUsers.length, {
timeout: 5000,
});
});
});
Loading