Restrict workspace settings to administrators, add demo countries, Helm chart and instance event log
Employees keep schedule editing but can no longer change workspace preferences or existing scheduling-directory entries (enforced by the server, hidden in the UI). Demo accounts live in Cyprus, the United Kingdom and Armenia with matching bank holidays. A Helm chart deploys the service with a pre-upgrade migrate hook. Refused SSO sign-ins are stored with their support reference as instance events and searchable on the instance administration page. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
7afdc4be97
commit
548494f12e
11 files changed
+413
-18
No files matched your search
@@ -100,6 +100,37 @@ fs.mkdirSync('test-results', { recursive: true });
|
||||
await row.getByRole('button', { name: 'Reset password' }).click();
|
||||
await dialog(root).getByLabel('New password', { exact: true }).fill('second-test-password-2');
|
||||
await save(root, 'Reset password');
|
||||
// The event log shows these administrator actions, can be searched, and refuses non-administrators.
|
||||
const log = root.locator('#instance-events');
|
||||
await log.locator('[data-event-action="user.created"]').filter({ hasText: username }).first().waitFor();
|
||||
await log.locator('[data-event-action="user.password_reset"]').filter({ hasText: username }).first().waitFor();
|
||||
assert.match(await log.locator('[data-event-action="user.created"]').first().innerText(), /admin/);
|
||||
await log.getByPlaceholder(/sign-in reference/).fill('no-such-event-zzzz-' + Date.now());
|
||||
await log.getByRole('button', { name: 'Search', exact: true }).click();
|
||||
await log.locator('[data-events-empty]').waitFor();
|
||||
assert.equal(await log.locator('.event-row').count(), 0);
|
||||
await log.getByPlaceholder(/sign-in reference/).fill(username);
|
||||
await log.getByRole('button', { name: 'Search', exact: true }).click();
|
||||
await log.locator('.event-row').first().waitFor();
|
||||
const matching = await log.locator('.event-row').evaluateAll(rows => rows.map(r => r.textContent));
|
||||
assert(matching.length >= 3 && matching.every(text => text.includes(username)), 'Search returns only events about the user');
|
||||
assert.equal(await log.locator('[data-events-empty]').isVisible(), false);
|
||||
assert.equal(await log.locator('[data-events-more]').isVisible(), false);
|
||||
await root.screenshot({ path: 'test-results/access-event-log.png', fullPage: true });
|
||||
// A support reference in the URL prefills the search.
|
||||
const byReference = await open();
|
||||
await signIn(byReference, 'admin');
|
||||
await byReference.goto(`${base}/?view=instance&ref=${username}`);
|
||||
await byReference.locator('#instance-events [data-event-action="user.created"]').first().waitFor();
|
||||
assert.equal(await byReference.locator('#instance-events input[type=search]').inputValue(), username);
|
||||
const mobile = await open({ width: 390, height: 844 });
|
||||
await signIn(mobile, 'admin');
|
||||
await mobile.goto(`${base}/?view=instance`);
|
||||
await mobile.locator('#instance-events .event-row').first().waitFor();
|
||||
assert(await mobile.evaluate(() => document.documentElement.scrollWidth <= innerWidth), 'Event log must not overflow on mobile');
|
||||
const forbidden = await (await apiLogin('bob')).call('GET', '/api/admin/events');
|
||||
assert.equal(forbidden.status, 403);
|
||||
assert.equal((await (await apiLogin('alice')).call('GET', '/api/admin/events?search=x')).status, 403);
|
||||
const outsider = await open();
|
||||
await signIn(outsider, username, 'second-test-password-2');
|
||||
await outsider.getByRole('heading', { name: 'No workspace access yet', exact: true }).waitFor();
|
||||
|
||||
Reference in new issue
Block a user