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:
Aleksandr BogomiakovandClaude committed 2026-10-07 13:28:43 +01:00
1 parent e37d583179
commit 4764f3545f
68 files changed
+7220 -964

No files matched your search

+14 -4
View File
@@ -10,9 +10,13 @@ Scheduling errors are product failures. A change to ownership, time boundaries,
| Configuration graph | `src/configuration.rs` tests | None | Dependency removal/redirect, last-step and fallback protection, pending swap cancellation, approved-history retention, exact route permutation |
| CSV import | `src/holidays.rs` tests | None | Country/date keys, quoting, BOM, duplicates, conflicts, invalid rows |
| Provider compilation | `src/providers/` tests | None | Unsupported capabilities fail; normalization detects changed ownership |
| Instance access, pure | `src/access.rs`, `src/identity.rs` tests | None | Configuration validation (unknown fields, key/name/group rules, sign-in method required), exact group matching and administrator-over-employee, claim paths, HMAC bootstrap passwords per hour/user/secret, contact-merge rules, username derivation |
| Application/persistence | `tests/integration.rs`, `tests/support/` | Disposable PostgreSQL | Real Cot migrations, scoped authorization, concurrent edits, atomic audit/history/outbox, approval revalidation |
| Instance access, database | `tests/access.rs` | PostgreSQL with CREATEDB | Configuration sync (create, adopt, rename, disable, re-enable, safety refusals, ambiguity), group-derived roles and revocation, instance administrators acting without membership, employee permission set, bootstrap accounts across hours and concurrent replicas, administrator operations and workspace deletion, SSO account creation/revocation and email contact linking |
| SSO over HTTP | `tests/oidc.rs`, `tests/support/fake_oidc.rs` | PostgreSQL with CREATEDB + loopback | The real application behind a fake OpenID provider: state cookie and one-time flow, PKCE, RS256 verification, rejected issuer/audience/expiry/nonce/signature, groups from ID token and userinfo, no-groups refusal without an account, session cookies, no registration endpoint |
| Demo lifecycle | `tests/demo_lifecycle.rs` | PostgreSQL with CREATEDB | Database protection and reset, demo configuration seeding with the six accounts, retention and reseeding (own binary because the demonstration notice is process-wide) |
| HTTP provider contract | Integration suite + stateful local fake | PostgreSQL + loopback | Wire methods/payloads, partial 429 failure, checkpoint recovery, read-back, drift, external overrides, owned-only deletion |
| Browser/API | `tests/browser.cjs`, `tests/browser-calendars.cjs`, `tests/browser-configuration.cjs` | Seeded local app + Chromium | Forms, calendar, approval, workspace isolation, CSRF/origin, people/imports/timezones, removal dependencies, native routing drag/drop and position editing, desktop/mobile |
| Browser/API | `tests/browser*.cjs` (all listed in `npm run test:browser`) | Freshly started demo + Chromium | Forms, calendar, approval, workspace isolation, CSRF/origin, people/imports/timezones, removal dependencies, native routing drag/drop and position editing, desktop/mobile; `browser-access.cjs`: demo login guide, no registration, SSO error banner, instance page and read-only rules, employee permission editing, role-based views |
| Live acceptance | Dedicated JSM test team | Test credentials | Actual provider timeline, subscription-specific escalations and routing; never ordinary CI |
## Invariants
@@ -31,7 +35,7 @@ Scheduling errors are product failures. A change to ownership, time boundaries,
## Running and extending
`cargo test` runs pure tests and explicitly reports database tests as ignored. CI also runs `cargo test --test integration -- --ignored --test-threads=1` with PostgreSQL 17. Fixtures use UUID names and never truncate shared tables. Use a disposable database. Run this suite serially because worker tests inspect the global queue. Worker tests explicitly prioritize only their own job so a bounded batch cannot skip it because of jobs left by a prior run.
`cargo test` runs pure tests and explicitly reports database tests as ignored. With `MEGURI_TEST_DATABASE_URL` pointing at a disposable PostgreSQL 17 database whose user has CREATEDB, CI and developers run everything with `cargo test --locked --tests -- --ignored --test-threads=1`. Scheduling fixtures create uniquely keyed workspaces and users through the real sync and group path and never truncate shared tables; tests whose subject is whole-instance state (sync, bootstrap, administration, SSO) use `IsolatedDatabase`, a uniquely named database dropped at the end, because sync disables every unconfigured workspace. Run serially because worker tests inspect the global queue. Worker tests explicitly prioritize only their own job so a bounded batch cannot skip it because of jobs left by a prior run. `tests/oidc.rs` installs process-wide configuration and therefore holds one test function.
The fake models JSM responses and is not a substitute for live acceptance. New endpoints need verification against official documentation, a wire assertion, and a failure/recovery test. Do not make the fake accept an invented contract to get tests green.
@@ -103,13 +107,13 @@ Binding selection uses one anchored, viewport-clamped popover beside the origina
`browser-repeat-defaults.cjs` verifies absent escalation defaults render as editable numbers, edited values survive reopening the draft, and explicit dictionary lookups remain intact.
The main Acme Engineering demo contains six services (Payments, Search, Billing, Identity, Data platform, Developer portal), each with two or three ordered owners drawn from five login members: alice, bob, carol, dave, erin. All five share the weekly Monday 11:00 Time off rotation. Dave and Erin only access Acme; Carol also retains Support/Commerce access. Other demo workspaces remain separate. Login password remains `meguri-demo-only`.
The main Acme Engineering demo contains six services (Payments, Search, Billing, Identity, Data platform, Developer portal), each with two or three ordered owners drawn from five login members: alice, bob, carol, dave, erin. All five share the weekly Monday 11:00 Time off rotation. Dave and Erin only access Acme; Carol also has Support/Commerce access through her groups. Other demo workspaces remain separate. The login password is the one generated for the run.
Generation rule results use the actual authorized server expansion, including schedules, escalation policies and routes, with resolved names and compact settings. The panel is always present with explicit checking, invalid, disabled and empty states; it respects iteration filters and does not simulate generation in the browser. Preview data contains only scheduling resources and scoped people, never integration configuration.
Permanent assignees are tested as single-participant rotations, including absence, replacement approval and publication blocking. The removed fixed-assignee schedule and generation types are rejected rather than normalized.
Workspace isolation regressions exercise independent memberships and variables, a single revision for configuration and working-time changes, read-only generation preview, stale review rejection, atomic activation/history/outbox, and owner settings permissions.
Workspace isolation regressions exercise independent memberships and variables, a single revision for configuration and working-time changes, read-only generation preview, stale review rejection, atomic activation/history/outbox, and workspace-administrator settings permissions.
The default Engineering example stores projects as one list of dictionaries: each entry has `name` and an ordered `responsible` person list. The Project iteration reads both fields directly from its current entry; no parallel responsible lookup variable is needed. The independent `on_call` list still defines the shared weekly rotation.
@@ -124,3 +128,9 @@ Map input details display evaluated server values for each block and pass, never
The map’s all-passes input overview lists deduplicated upstream sources, not expanded output settings: variables with used binding addresses and resource references grouped by producing block. References internal to the selected whole rule are omitted; selecting an individual output block shows its dependencies. Selecting a concrete pass still exposes its evaluated field values.
Expanded map resources use type-specific labeled fields and the same compact reference cards as inputs, never JSON dumps. Priority schedules show ordered people, fallback and zoned handover; rotations add anchor/cycle; policies show recipient/timing/condition and repeat behavior; routes show conditions, working window and destination. Reference names resolve from the preview snapshot. Browser regression checks visible names, participant order and absence of UUID metadata.
## Access and identity regressions
Authorization boundaries are product failures like scheduling ones. A change to configuration sync, group mapping, roles, SSO, bootstrap or administration needs a regression in `tests/access.rs`, `tests/oidc.rs` or the pure tests. Existing cases pin: exact (not prefix/case-insensitive) group matching; membership removal when a group or workspace disappears, including for open sessions; instance administrators acting on workspaces without membership and being audited under their own ID; employees unable to administer integrations or members; the employee permission set applying to existing and future employees at once, with `view` mandatory and administrator-only permissions refused; refusal of a configuration that would disable everything and of ambiguous adoption before any write; bootstrap passwords equal for replicas in one UTC hour, rotated (ending sessions) in the next, and locked out when the block is removed; no SSO account takeover by a bootstrap name; SSO accounts created only when groups map, revoked at the next sign-in, never given a password; administrators unable to edit their own groups or SSO groups; deletion only of disabled, unconfigured workspaces with the typed key; contact linking rewriting the current document while history keeps the old ID and audit still resolves it, skipping ambiguous or conflicting data and never granting access.
The browser access suite also runs against the real demo. No test may weaken these checks to make a failing sign-in pass; fix the cause or record the limitation.