Admin portal - Operator access and audit¶
- Status: Draft
- Prefix:
ACC - Module: admin
- Feature: operator-access
- Related: Admin portal
Purpose¶
Only named members of the BizOS platform team can use the admin portal, each with the least access they need, and everything they change can be traced back to them.
Users and permissions¶
| Role | Role Key | Intended for | Can |
|---|---|---|---|
| Super Admin | super_admin |
Platform lead (at least 1, ideally 2) | Everything, including managing operators and roles, deleting tenants, restoring and resetting tenant data |
| Admin | admin |
Day-to-day platform operations | Read everything; suspend and reactivate tenants; retry or cancel provisioning; take on-demand backups; retry failed jobs and migrations; resend emails |
| Support | support |
Technical and customer support | Read tenant list and detail, provisioning status, backup list, and activity log; resend transactional emails; no destructive or sensitive write actions |
Permission matrix for operations (✓ = allowed):
| Action | Super Admin (super_admin) |
Admin (admin) |
Support (support) |
|---|---|---|---|
| View dashboard, tenants, provisioning, health, activity log | ✓ | ✓ | ✓ |
| Edit tenant metadata (display name, owner contact, notes) | ✓ | ✓ | |
| Suspend / reactivate tenant | ✓ | ✓ | |
| Retry / cancel provisioning run | ✓ | ✓ | |
| Take on-demand backup | ✓ | ✓ | |
| Retry failed background job or tenant migration | ✓ | ✓ | |
| Resend transactional email | ✓ | ✓ | ✓ |
| Configure system SMTP | ✓ | ✓ | |
| Restore tenant from backup | ✓ | ||
| Reset tenant to defaults | ✓ | ||
| Schedule / cancel tenant deletion | ✓ | ||
| Manage operators and roles | ✓ | ||
| Export activity log | ✓ | ✓ | |
| Delete activity log entries | ✓ |
Requirements¶
| ID | Priority | Requirement |
|---|---|---|
| ACC-01 | Must | Operator accounts live in the control plane and are separate from tenant user accounts. |
| ACC-02 | Must | There is no self sign-up. A Super Admin invites an operator by email; the invite link expires after 72 hours and can be used once. |
| ACC-03 | Must | Sign-in requires email, password, and a time-based one-time code (TOTP). MFA enrolment is forced on first sign-in. |
| ACC-04 | Must | Each operator has exactly one role: Super Admin (super_admin), Admin (admin), or Support (support). The server enforces the permission matrix on every request; hiding buttons in the UI is not enough. |
| ACC-05 | Must | Sessions expire after 30 minutes of inactivity and after 12 hours in total. Sign-out and an unauthenticated visit to a protected admin route return to the shared customer-portal sign-in in the corresponding environment (with redirect back to the admin URL after success). |
| ACC-06 | Must | After 5 failed sign-in attempts in 15 minutes the account is locked for 15 minutes, and the attempt is audited. |
| ACC-07 | Must | A Super Admin can change an operator's role, deactivate an operator (ending their sessions immediately), and reset their MFA. The last active Super Admin cannot be deactivated or demoted. |
| ACC-08 | Must | Every operator write action creates an audit entry: time, operator, role, action, target (tenant or object), reason (when required), before and after values for edits, source IP, and result (success or failure with error). |
| ACC-09 | Must | Sign-in, sign-out, failed sign-in, lockout, MFA changes, permission denials, and unauthorized portal access (a customer tenant session hitting the admin portal) are audited. |
| ACC-10 | Must | Activity entries cannot be edited through the portal or the application API. |
| ACC-11 | Must | The activity log screen filters by time range, operator, action, tenant, and result. |
| ACC-12 | Should | Activity log export to CSV for the current filter, itself audited. |
| ACC-16 | Must | A Super Admin can delete one activity entry. Admin and Support cannot. Deleting any entry other than Activity entries deleted writes a new entry that stays in the log. Deleting an Activity entries deleted entry removes it and writes nothing. |
| ACC-17 | Must | A Super Admin can delete every activity entry in an inclusive UTC date range. Both dates are required. Operator, action, tenant, and result filters do not apply. The deletion writes a new entry that stays in the log when the range includes any entry other than Activity entries deleted. A range that contains only those rows, or no rows, writes nothing. |
| ACC-18 | Must | Adding a user to a tenant writes a tenant_member_created activity entry. |
| ACC-19 | Must | A tenant user changing their name or email writes a profile_updated activity entry with the before and after values. |
| ACC-20 | Must | The activity table and its action filter omit Activity entries deleted. Above the table, the latest deletion is one line. Opening it lists deletion records and does not offer delete on those records. |
| ACC-21 | Must | Opening an activity entry shows a Field, Before, and After table of only the business fields whose stored before and after values differ. Labels and value formats follow the portal (for example Tenant name, Active, and storage in GB). Unchanged fields and system timestamps stay out of that table. When no business field changed, the dialog says "No business fields changed." When before or after was not stored, the dialog says so and shows only the recorded side. A Details control, closed by default, shows the original before and after JSON, including unchanged fields and timestamps. The stored audit payload is not changed. |
| ACC-13 | Should | Operators can see and end their own active sessions. |
| ACC-14 | Could | Optional IP allowlist for the admin portal, set by deployment configuration. |
| ACC-15 | Could | Local development only: when NODE_ENV is not production and ADMIN_DEV_SKIP_MFA=true, the TOTP step offers "Skip 2FA (development only)", which finishes sign-in without a code. The skip is never available in production, does not mark MFA as enrolled, and the sign-in audit entry records that the code was skipped. |
Screens¶
Sign-in. Email and password, then the TOTP step. First sign-in shows the QR code and asks for a code before continuing. In local development the TOTP step can also show a skip button (ACC-15).
Operators (Super Admin only). Table: name, email, role, status (Invited, Active, Deactivated), MFA enrolled, last sign-in. Actions: invite, change role, deactivate, reactivate, reset MFA, resend invite.
Activity log (/audit-log). Table, newest first: time, operator, action, target, result. Opening a row shows the operator, target, result, reason, source IP, and time, then a table of only the business fields that changed (ACC-21). Details, closed until opened, shows the full before and after JSON. Filters as in ACC-11. A Super Admin sees a delete control on each row and a delete control for the From–To dates (ACC-16, ACC-17). The action list includes Tenant deleted, Tenant member created, Profile updated, Permission denied, and Unauthorized portal access. Activity entries deleted stays out of that list and out of the table. Above the table, the latest deletion is one line; opening it lists deletion records without a delete control (ACC-20). On the tenant detail page, the Activity tab shows the same log filtered to that tenant.
My account. Change password, re-enrol MFA, active sessions (ACC-13).
Business rules¶
- Operator status:
Invited→Active→Deactivated→Active(a Super Admin can reactivate). - Actions that require a reason (free text, at least 10 characters): suspend, reactivate, restore, reset, schedule deletion, cancel deletion, role change, deactivate operator.
- Audit retention: at least 1 year (see open questions).
- The database rejects updates to an activity entry. Deletes are allowed through the Super Admin API. Deleting an Activity entries deleted entry, or a date range that contains only those entries, does not write another entry.
- The activity list omits Activity entries deleted unless that action is requested. The screen shows the latest of those records above the table (ACC-20).
Acceptance criteria¶
- ACC-01: A tenant user's credentials are rejected by the admin portal sign-in, and an operator's credentials are rejected by tenant sign-in.
- ACC-02: An expired or already-used invite link shows an error and creates no account.
- ACC-03: A correct password with a wrong or missing TOTP code does not create a session.
- ACC-04: A Support operator calling a write endpoint directly (outside the UI) receives a forbidden response, and the attempt is audited as a failure.
- ACC-05: After 30 minutes without activity, or after Sign out, the browser opens the shared sign-in at the customer portal (e.g.
http://localhost:4200/sign-in?redirect=…) and a successful sign-in returns to the admin screen. - ACC-06: The sixth failed attempt within 15 minutes locks the account even with the correct password, until 15 minutes pass.
- ACC-07: Deactivating an operator ends their open session at the next request; the last active Super Admin cannot be deactivated or demoted.
- ACC-08: Suspending a tenant creates exactly one audit entry with the operator, tenant, reason, and result.
- ACC-09: Failed sign-in is audited. A customer tenant session hitting an admin route or exchange writes
unauthorized_portal_access. A Support operator missing a write permission writespermission_denied. - ACC-10: No portal screen or API endpoint can change an activity entry.
- ACC-11: Filtering by one tenant and one action returns only matching entries.
- ACC-16: Deleting one ordinary entry removes it and leaves an
activity_entries_deletedrow. Deleting that row removes it and leaves no new row. Admin and Support do not haveaudit:delete. - ACC-17: Deleting from 2026-09-02 to 2026-09-03 removes entries on those days, leaves a later entry and the deletion row, and a missing date is rejected. Deleting a range that holds only
activity_entries_deletedrows, or an empty range, leaves no new row. - ACC-18: Creating a tenant member writes one
tenant_member_createdentry for that tenant. - ACC-19: Changing a tenant user's name and email writes one
profile_updatedentry with before and after. An unchanged profile writes nothing. - ACC-20: The default activity list omits
activity_entries_deleted. Selecting that action returns those rows. The screen shows the latest deletion above the table and lists the history without a delete control. - ACC-21: Opening a tenant update that changed only the display name shows Tenant name, the old name, and the new name, and hides the unchanged state, plan, and storage. Details starts closed and then shows the original JSON, including
storageMaxBytesandupdatedAt. A timestamp-only difference says "No business fields changed." A row with no before value says "Before was not recorded." and does not add a Before column. - ACC-15: With
NODE_ENV=production, the skip button is hidden and calling the skip endpoint directly returns forbidden without creating a session.
Out of scope¶
- Single sign-on with an external identity provider.
- Signing in to a tenant workspace as a tenant user (impersonation).
- Customer-facing audit log for tenant users; that belongs to the tenant workspace (activity log).
Open questions¶
- Are three roles enough, or do we need a separate billing role once the Billing part starts?
- Audit retention period: 1 year, or longer for compliance?
- Is TOTP enough, or do we also require passkeys or hardware keys for Super Admins?
- Should the IP allowlist (ACC-14) be required for production?