Add OIDC sign-in, group-derived roles and declarative instance configuration
Workspaces, group mappings and sign-in methods now come from a configuration file synchronized at startup; workspaces dropped from it are disabled, never deleted. Instance administrators (a new role) see and help every workspace and may delete disabled ones; workspace administrators and employees are derived from the user's groups at every SSO sign-in or administrator edit, and the employee permission set belongs to the workspace. Self-registration is gone: users come from SSO (only when their groups map) or from the administrator. Break-glass bootstrap accounts get an HMAC-derived hourly password that all replicas agree on. First SSO sign-in links a matching email contact to the user. The demo runs on the same model with a generated per-start password. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
e37d583179
commit
4764f3545f
68 files changed
+7220
-964
No files matched your search
@@ -1,4 +1,4 @@
|
||||
const {openSetup}=require('./support/browser.cjs');
|
||||
const {openSetup,isolatedUser,signIn}=require('./support/browser.cjs');
|
||||
// End-to-end coverage for scheduling contacts, country imports, working hours, and display timezones.
|
||||
|
||||
const assert=require('node:assert/strict');
|
||||
@@ -14,26 +14,19 @@ fs.mkdirSync('test-results',{recursive:true});
|
||||
const save=async()=>{await dialog().getByRole('button',{name:'Save changes',exact:true}).click();await dialog().waitFor({state:'hidden'});};
|
||||
const state=()=>page.evaluate(()=>{const app=Alpine.$data(document.body);return JSON.parse(JSON.stringify({workspace:app.workspace,user:app.user,endpoint:app.endpoint(),displayZone:app.displayZone}));});
|
||||
try {
|
||||
const label='Calendars '+Date.now();
|
||||
await page.goto(base);
|
||||
await page.getByRole('button',{name:'New here? Create an account'}).click();
|
||||
await page.getByLabel('Username',{exact:true}).fill('calendars-'+Date.now());
|
||||
await page.getByLabel('Display name',{exact:true}).fill('Calendar tester');
|
||||
await page.getByLabel('Password',{exact:true}).fill('local-browser-test-password');
|
||||
await page.getByRole('button',{name:'Create account',exact:true}).click();
|
||||
await page.getByRole('button',{name:'New workspace',exact:true}).waitFor();
|
||||
await page.getByRole('button',{name:'New workspace',exact:true}).click();
|
||||
await dialog().getByLabel('Name',{exact:true}).fill(label);await save();
|
||||
await page.getByRole('button',{name:'New workspace',exact:true}).click();
|
||||
await dialog().getByLabel('Name',{exact:true}).fill(label);await save();
|
||||
// Sandbox 2 belongs to this suite: an administrator plus Bob Chen as an employee.
|
||||
const tester=await isolatedUser({sandbox:2,prefix:'calendars',display:'Calendar tester',employees:['Bob Chen']});
|
||||
await signIn(page,tester.username,tester.password);
|
||||
await page.getByLabel('Workspace',{exact:true}).waitFor();
|
||||
await openSetup(page,'Workspace settings');
|
||||
await page.getByRole('button',{name:'+ Add teammate'}).click();await dialog().getByLabel('Registered username').fill('bob');await save();
|
||||
await page.locator('#workspace-members').getByText('Bob Chen',{exact:true}).waitFor();
|
||||
await openSetup(page,'Schedules');
|
||||
await page.locator('.schedule-create > summary').click();
|
||||
await page.getByRole('button',{name:'+ Rotation',exact:true}).click();
|
||||
await dialog().getByLabel('Schedule name').fill('Distributed rotation');
|
||||
await dialog().getByLabel('Handover time').fill('09:00');
|
||||
await dialog().getByLabel('Remove participant').first().click();
|
||||
await dialog().getByLabel('Remove participant').first().click();
|
||||
await dialog().getByRole('button',{name:'+ Add participant · name or email'}).click();
|
||||
const picker=()=>page.getByRole('region',{name:'Choose a person'});
|
||||
await picker().getByLabel('Search workspace users or enter email').fill('Bob');
|
||||
|
||||
Reference in new issue
Block a user