Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
376776620e | ||
|
|
3725d0603c | ||
|
|
97bffdc201 | ||
|
|
6501275715 |
@ -76,7 +76,17 @@ export default {
|
||||
},
|
||||
];
|
||||
|
||||
await queryInterface.bulkInsert('users', rows);
|
||||
// Check which users already exist to make seeder idempotent
|
||||
const existing = await queryInterface.sequelize.query<{ id: string }>(
|
||||
`SELECT id FROM users WHERE id IN (:ids)`,
|
||||
{ replacements: { ids }, type: 'SELECT' },
|
||||
);
|
||||
const existingIds = new Set(existing.map((row) => row.id));
|
||||
const toInsert = rows.filter((row) => !existingIds.has(row.id as string));
|
||||
|
||||
if (toInsert.length > 0) {
|
||||
await queryInterface.bulkInsert('users', toInsert);
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
|
||||
@ -2,7 +2,8 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const __dirname = import.meta.dirname;
|
||||
const ENV_FILE = path.resolve(__dirname, '..', '..', '.env');
|
||||
// Path: shared/config -> shared -> src -> backend/.env (3 levels up)
|
||||
const ENV_FILE = path.resolve(__dirname, '..', '..', '..', '.env');
|
||||
|
||||
interface EnvEntry {
|
||||
key: string;
|
||||
|
||||
@ -57,7 +57,11 @@ Backend current baseline:
|
||||
|
||||
## Active Workstreams
|
||||
|
||||
### 1. Backend Migration And Runtime Verification
|
||||
The workstreams are numbered in dependency/execution order and grouped into five phases. Earlier phases unblock later ones; within a phase, items may run in parallel unless a dependency is stated. Renumbered from the previous flat backlog: the former Role Model Decision, Product Onboarding Contract, and Permission-Based Frontend Authorization workstreams are now merged into Workstream 3 (RBAC); the former standalone seed-verification step is merged into Workstream 4.
|
||||
|
||||
**Phase 1 — Foundation (everything else depends on a backend that boots, migrates, and serves routes).**
|
||||
|
||||
### 1. Backend Migration And Runtime Boot
|
||||
|
||||
Status: open.
|
||||
|
||||
@ -68,7 +72,7 @@ Backend product modules exist, but the configured local database migration/seed
|
||||
Required work:
|
||||
|
||||
1. Run `npm run db:migrate` against the configured local database.
|
||||
2. Run `npm run db:seed`.
|
||||
2. Run `npm run db:seed` (current seed set; the final RBAC seed content is delivered in Workstream 4, which re-verifies this step).
|
||||
3. Start backend with the documented env file.
|
||||
4. Verify public content catalog routes, auth routes, and product module routes respond.
|
||||
5. Record exact failing migration/seed/runtime errors if any.
|
||||
@ -81,9 +85,11 @@ Acceptance criteria:
|
||||
- `GET /api/public/content-catalog/:contentType` works for the required seeded content catalog types.
|
||||
- Any remaining backend runtime blocker is captured as a specific follow-up with file/error references.
|
||||
|
||||
**Phase 2 — Identity, Tenancy & Access Core (the security spine: tenant isolation, the role/permission model, the seeded fixtures, and the finalized protected API surface). Workstreams 5–7 finalize what is public, what exists, and what is permission-gated, so they belong with the access model rather than after it.**
|
||||
|
||||
### 2. Tenant Boundary Audit And Tests
|
||||
|
||||
Status: open and high priority.
|
||||
Status: open and high priority. Co-designed with Workstream 3: the campus/organization scoping here and the `campusId`-on-`users` model in §3.1 are the same scoping layer.
|
||||
|
||||
Problem:
|
||||
|
||||
@ -103,223 +109,135 @@ Acceptance criteria:
|
||||
- A non-global user cannot read or mutate another tenant's data.
|
||||
- Campus-scoped users cannot mutate another campus unless their backend permission explicitly allows it.
|
||||
|
||||
### 3. Role Model Decision
|
||||
### 3. RBAC: Role Hierarchy, Scoped Provisioning, And Guards
|
||||
|
||||
Status: open.
|
||||
Status: open and high priority. Umbrella workstream for the platform's user/role model. **Merged here:** the former Role Model Decision (role-model choice), the former Product Onboarding Contract (provisioning, §3.4), and the former Permission-Based Frontend Authorization (frontend guards, §3.6). Consumes the existing permission engine in `middlewares/check-permissions.ts`.
|
||||
|
||||
Problem:
|
||||
Decisions recorded (owner-approved):
|
||||
|
||||
Backend currently maps generated roles to product roles. It is not yet a final product decision whether this mapping is the supported model or product roles should become first-class persisted backend roles/permissions.
|
||||
- Role model: **first-class persisted roles** in the `roles` table, each carrying a `scope`, with preset permission sets and code-enforced relational constraints. The `GENERATED_ROLE_TO_PRODUCT_ROLE` / `STAFF_TYPE_TO_PRODUCT_ROLE` mappings and the 5-value `productRole` are retired.
|
||||
- Frontend: **permission-based guards plus an authentication guard**; forbidden direct-URL access redirects to the 404 page; menu items the user cannot access are hidden.
|
||||
|
||||
Required decision:
|
||||
#### 3.0 Current state (analysis, verified)
|
||||
|
||||
- Option A: keep generated-role to product-role mapping as the supported model.
|
||||
- Option B: create first-class persisted product roles: `Teacher`, `Para`, `Office Manager`, `Director`, `Superintendent`.
|
||||
- Roles today (`backend/src/db/seeders/20200430130760-user-roles.ts`, `backend/src/shared/constants/roles.ts`): 7 generated roles (`Super Administrator`, `Administrator`, `Platform Owner`, `Tenant Director`, `Campus Manager`, `Academic Coordinator`, `Finance Officer`) + `Public` + a `User` default-role name referenced in code. `globalAccess: true` is set only on `Super Administrator` and `Administrator`. These map down to 5 `productRole` values (`teacher | para | office | director | superintendent`).
|
||||
- Authorization engine: `middlewares/check-permissions.ts` grants a request when (1) `currentUser.id === req.params.id || req.body?.id` (self-access), (2) a `custom_permissions` row matches, or (3) the effective role (assigned `app_role`, else cached `Public`) has the `${METHOD}_${ENTITY}` permission. CRUD routers wire `checkCrudPermissions(entity)`. This engine is sound and is kept.
|
||||
- Permission catalog: CRUD × 27 entities + `READ_API_DOCS` + `CREATE_SEARCH`. **Product modules are not represented** (no permission exists for the FRAME, walkthrough, quizzes, director-dashboard, attendance-fill, ESA/vocational/community pages, classrooms, etc.).
|
||||
- Multi-tenancy: `organizationId` on most models (unified, per Workstream 2). Campus link for a user is only indirect, via the `staff` row's `campusId`; `staff_type` is `teacher | admin | support`. There is **no `campusId` on `users`** and no first-class concept of director/owner/student/guardian.
|
||||
- Seeders: **no organization row is seeded at all**, and the seeded campuses (`shared/constants/campuses.ts`, 6 rows) have **no `organizationId`**. `20200430130760` assigns roles by raw SQL: `super_admin@flatlogic.com`→SuperAdmin, `admin@flatlogic.com`→Administrator, `client@hello.com`→PlatformOwner, `john@doe.com`→TenantDirector. **This contradicts the product spec**, which states `admin@flatlogic.com` (`SEED_ADMIN_EMAIL`) is the super admin. No staff profiles are seeded.
|
||||
- Frontend: `ModuleRouteGuard` + sidebar filter gate purely on the 5-value `productRole` (`shared/constants/appData.ts` `MODULES[].roles`, `shared/constants/moduleRoutes.ts`). `CurrentUser.permissions` exists but is unused. There is **no authentication guard** (an unauthenticated user reaching the shell is not redirected to `/login`), and `canUserRoleAccessModuleRoute` returns `true` for unknown paths (forbidden routes fall through to the catch-all rather than failing at the guard). Only `/login` and `*` (404) are public; there is no signup route.
|
||||
|
||||
Required work after decision:
|
||||
#### 3.1 Target role hierarchy and scope
|
||||
|
||||
1. Document the chosen role model.
|
||||
2. Align `/api/auth/me` permissions and product role payload with the chosen model.
|
||||
3. Add backend role/permission tests for staff, director, and superintendent paths.
|
||||
4. Keep frontend navigation driven by backend role/permissions.
|
||||
Eleven roles across five scopes (guest is the unauthenticated case, not a stored user):
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Role model is documented in backend docs.
|
||||
- Backend tests prove role access for product modules.
|
||||
- No frontend role grants capability that backend does not enforce.
|
||||
|
||||
### 4. Product Onboarding Contract
|
||||
|
||||
Status: blocked by customer decision.
|
||||
|
||||
Problem:
|
||||
|
||||
Generated auth signup/profile endpoints exist, but they do not define the product workflow for company creation, campus creation, user creation, staff profile creation, role assignment, campus assignment, or profile updates.
|
||||
|
||||
No-go rules:
|
||||
|
||||
- Do not implement frontend registration.
|
||||
- Do not implement profile creation/editing UI.
|
||||
- Do not treat generated signup/profile endpoints as the product onboarding contract.
|
||||
- Do not add temporary compatibility paths.
|
||||
|
||||
Required customer decisions:
|
||||
|
||||
1. Who creates a company.
|
||||
2. Who creates campuses.
|
||||
3. Who invites or creates users.
|
||||
4. How staff profiles are created and linked to users.
|
||||
5. How roles and campuses are assigned.
|
||||
6. Which profile fields users may update themselves.
|
||||
7. Which profile fields require director/superintendent/admin permissions.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Add backend onboarding contract and docs.
|
||||
2. Add tenant-scoped backend workflows.
|
||||
3. Add frontend typed API modules.
|
||||
4. Add frontend business hooks.
|
||||
5. Add UI only after backend contract exists.
|
||||
6. Add authenticated Playwright workflows using backend-seeded fixtures.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Onboarding workflow is customer-approved and documented.
|
||||
- Backend owns all creation, assignment, and permission checks.
|
||||
- Frontend only exposes flows backed by typed backend contracts.
|
||||
|
||||
### 5. Refresh Token Maintenance
|
||||
|
||||
Status: open.
|
||||
|
||||
Problem:
|
||||
|
||||
Cookie auth and refresh rotation exist, but scheduled cleanup for expired/revoked refresh-token rows remains unresolved.
|
||||
1. `super_admin` — scope `system`. Unlimited across the whole platform.
|
||||
2. `system_admin` — scope `system`. Unlimited platform-wide **except** creating/deleting `super_admin` or other `system_admin`.
|
||||
3. `owner` — scope `organization`. Unlimited inside its own company.
|
||||
4. `superintendent` — scope `organization`. Unlimited inside the company **except** creating/deleting `owner` or other `superintendent`, and **except** deleting the company profile.
|
||||
5. `director` — scope `campus`. Unlimited inside its assigned campus; creates campus users and classrooms; grants extra per-user permissions.
|
||||
6. `office_manager` — scope `campus`. Reads all campus pages **except** the director dashboard; takes quizzes; leaves read receipts; may fill the attendance page.
|
||||
7. `teacher` — scope `campus`. Reads all campus pages except the director dashboard.
|
||||
8. `support_staff` — scope `campus`. Reads all campus pages except the director dashboard. (Replaces the current `para` value.)
|
||||
9. `student` — scope `external`. Reads only `/community-partnerships`, `/vocational-opportunities`, `/esa-funding`.
|
||||
10. `guardian` — scope `external`. Same external pages as `student`.
|
||||
11. `guest` — unauthenticated. Public routes only (`/login`, `/signup`, error page); any other URL redirects to 404.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Define refresh-token retention period.
|
||||
2. Add cleanup job or operational command.
|
||||
3. Ensure cleanup is observable.
|
||||
4. Document operational usage.
|
||||
1. Add a `scope` column to the `roles` model/migration: enum `system | organization | campus | external | guest`. Keep `globalAccess` for the two system roles (it already short-circuits tenant filtering in the CRUD layer).
|
||||
2. Replace the seeded role set with these 11 (plus keep `Public` as the unauthenticated fallback mapped to `guest`). Write a data migration mapping legacy roles → new roles (`Super Administrator`→`super_admin`, `Administrator`→`system_admin`, `Platform Owner`→`owner`, `Tenant Director`→`superintendent`, `Campus Manager`→`director`, `Academic Coordinator`→`teacher`, `Finance Officer`→`office_manager`; `para`/`support` HR type → `support_staff`).
|
||||
3. Retire `GENERATED_ROLE_TO_PRODUCT_ROLE`, `STAFF_TYPE_TO_PRODUCT_ROLE`, and `PRODUCT_ROLE_VALUES`; the role name + scope is now the source of truth. Replace `productRole` in the `/api/auth/me` DTO with `role: { name, scope }` (keep a stable string the frontend can switch on).
|
||||
4. Add a campus link for campus-scoped users. Recommended: a nullable `campusId` on `users` (authorization scope), independent of the HR `staff` row. `organizationId` remains the tenant key; `campusId` is required for `director`/`office_manager`/`teacher`/`support_staff` and optional for `student`/`guardian`; null for `system`/`organization` scopes. (Same scoping layer as Workstream 2.)
|
||||
|
||||
#### 3.2 Permission catalog expansion
|
||||
|
||||
The frontend can only gate on permissions the backend actually checks, so every guarded page/action needs a permission name.
|
||||
|
||||
1. Keep the CRUD × entity permissions; add `classrooms` (alias of `classes` if the same table, otherwise a new entity) to CRUD.
|
||||
2. Add product-feature permissions for the module routes and special actions, named in the existing `${VERB}_${THING}` style — e.g. `READ_DIRECTOR_DASHBOARD`, `FILL_ATTENDANCE`, `TAKE_QUIZ`, `ACK_READ_RECEIPT`, `READ_COMMUNITY_PARTNERSHIPS`, `READ_VOCATIONAL_OPPORTUNITIES`, `READ_ESA_FUNDING`, plus one per remaining campus module page.
|
||||
3. Define the **role → permission preset matrix** (the "predefined permissions per role" from the spec) and seed it into `rolesPermissionsPermissions`. Directors additionally hand out per-user `custom_permissions`.
|
||||
4. Document the matrix in `backend/docs/auth-profile.md` (or a new `backend/docs/rbac.md`).
|
||||
|
||||
#### 3.3 Relational role constraints (the "except …" rules)
|
||||
|
||||
The constraints are about *who the target is*, not just *what verb on what entity*, so a flat `${METHOD}_${ENTITY}` permission cannot express them. Implement a policy layer enforced in the **service layer** (authoritative), with a middleware in front for early rejection:
|
||||
|
||||
1. On user create/update/delete, compare actor scope+role against the target's role: `system_admin` cannot create/delete `super_admin` or `system_admin`; `superintendent` cannot create/delete `owner` or `superintendent`; campus roles cannot escalate above their scope; a director may only create/manage users within its own `campusId`.
|
||||
2. On organization delete, require `super_admin`/`system_admin`/`owner` (a `superintendent` is blocked).
|
||||
3. Cross-scope tenancy: every actor may only act within its `organizationId` (and `campusId` for campus scope) unless `globalAccess`.
|
||||
4. Fix the self-access bypass security hole: `currentUser.id === req.body?.id` (`middlewares/check-permissions.ts`, the self-access branch) lets any user pass a guard by putting their own id in the request body; restrict self-access to read/update of the caller's own profile only.
|
||||
|
||||
#### 3.4 Scoped provisioning / onboarding contract (former Product Onboarding Contract)
|
||||
|
||||
Auto-provisioning chain, all backend-owned, each step sending an invitation email with a login link (`services/email/list/invitation.ts` already exists). No-go rules carried over from the former onboarding workstream: do not implement frontend self-registration; do not build profile creation/editing UI ahead of the backend contract; do not treat the generated `signup`/`profile` endpoints as the product contract; no temporary compatibility paths.
|
||||
|
||||
1. `super_admin`/`system_admin` create an `owner` user → **auto-create the company (organization)** and link the owner; both start minimal (owner has only email+password; company has only the owner link).
|
||||
2. `owner`/`superintendent` create superintendents, other org users, and campuses; assign a `director` to a campus.
|
||||
3. `director` creates campus users (`teacher`, `support_staff`, `office_manager`, `student`, `guardian`) and classrooms, and grants extra `custom_permissions`.
|
||||
4. Every created user receives a login-link email; on first login they land on their own editable profile.
|
||||
5. Define which profile fields each role may self-edit vs. which require a higher role.
|
||||
|
||||
#### 3.5 Backend guard coverage audit
|
||||
|
||||
1. Confirm **every** route mounts authentication + `checkCrudPermissions`/`checkPermissions` (cross-reference Workstream 5 public-route audit so only intended routes are public).
|
||||
2. Add the §3.3 relational policy to the user, organization, campus, staff, and classroom write paths.
|
||||
3. Add the product-feature permission checks (§3.2) to the feature routes (FRAME, walkthrough, quizzes, attendance, communications, content-catalog, etc.).
|
||||
4. Ensure list/count/autocomplete are tenant- and campus-scoped (ties to Workstream 2).
|
||||
|
||||
#### 3.6 Frontend guards (former Permission-Based Frontend Authorization)
|
||||
|
||||
Context: the backend already authorizes every request by **permission**, not role; the user DTO already carries `permissions: string[]`, but the frontend ignores it and gates on the 5-value `productRole`. The goal is to gate UI affordances (routes/menu items/buttons/request triggers) by **permission** — using the same `${METHOD}_${ENTITY}` names the backend checks — while the backend stays the sole source of truth (frontend gating is UX-only and must never be the only enforcement).
|
||||
|
||||
1. **AuthGuard.** Wrap the shell: unauthenticated → redirect to `/login`; only `/login`, `/signup`, and the error page are reachable without a session.
|
||||
2. **Expose permissions in the auth contract.** Confirm `GET /api/auth/me` and the sign-in response include the resolved effective `permissions: string[]` (role permissions ∪ `custom_permissions`); merge `custom_permissions` if not already; document the field in `backend/docs/auth-profile.md`.
|
||||
3. **Shared permission vocabulary.** Add a typed catalog on the frontend (`frontend/src/shared/auth/permissions.ts`) mirroring backend `${METHOD}_${ENTITY}` names; keep it as `shared/constants`-style UI config; do not import backend code.
|
||||
4. **Permission selector/hook.** Implement `hasPermission/hasAnyPermission/hasAllPermissions` in `frontend/src/business/auth/` (pure functions over `CurrentUser.permissions`) plus a `usePermissions()` hook.
|
||||
5. **Gate routes by permission.** Replace `productRole`-based `canUserRoleAccessModuleRoute`/`getAccessibleModules` with permission checks; each `MODULES[]` entry declares the permission(s) it requires. Keep a role concept only where genuinely role-specific (e.g. dashboards), not for resource access.
|
||||
6. **Forbidden direct-URL access redirects to the 404 page** (change `canUserRoleAccessModuleRoute`'s unknown-path `return true` and the guard's redirect target); inaccessible sidebar items are hidden.
|
||||
7. **Encode the role-specific page sets.** `office_manager` sees all campus pages except the director dashboard (plus quiz/read-receipt/attendance-fill affordances); `teacher`/`support_staff` see all campus pages except the director dashboard; `student`/`guardian` see only `/community-partnerships`, `/vocational-opportunities`, `/esa-funding`; `director`/`superintendent`/`owner` see their full scope; `guest` sees only public routes.
|
||||
8. **Gate affordances + handle 403.** Hide/disable create/edit/delete triggers by the matching permission (e.g. hide "Add campus" without `CREATE_CAMPUSES`); surface backend `forbidden` responses through one handler (toast + no crash) so UI/permission drift degrades gracefully.
|
||||
9. **Roles/permissions admin UI.** Let an admin/director create a role, attach/detach permissions, and assign `custom_permissions` to a user — backed by the existing `roles`/`permissions`/`users` endpoints.
|
||||
10. **Docs.** Update `frontend/docs/frontend-architecture.md` and `backend/docs/auth-profile.md` to describe the permission-based frontend model and the `${METHOD}_${ENTITY}` contract.
|
||||
|
||||
#### 3.7 Tests
|
||||
|
||||
1. Backend: relational-constraint tests (`system_admin` cannot delete `super_admin`; `superintendent` cannot delete `owner`/`superintendent` or the company; director scoped to its campus), tenant/campus isolation, and the provisioning chain (owner-create auto-creates company).
|
||||
2. Frontend: per-role route-guard tests (each role can/cannot open the right pages), sidebar-visibility tests, `hasPermission`/selector unit tests, unauthenticated→`/login`, and forbidden-URL→404.
|
||||
3. Backend-seeded authenticated e2e using the Workstream 4 fixtures (feeds Workstream 8) proving UI affordances match backend enforcement for at least one CRUD entity.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Expired and revoked refresh-token rows are cleaned after the approved retention window.
|
||||
- Cleanup failures are visible and not silent.
|
||||
- Auth behavior remains unchanged for valid sessions.
|
||||
- The 11 roles exist as first-class scoped roles; the legacy mapping and 5-value `productRole` are gone.
|
||||
- Backend is the sole authority: every route enforces authentication + permission + (where relational) the §3.3 policy; removing a frontend check never grants real access (backend still returns 403).
|
||||
- The "except …" constraints hold in tests for `system_admin`, `superintendent`, and `director`.
|
||||
- Frontend route/menu/affordance visibility is permission-driven; unauthenticated users are redirected to `/login`; forbidden direct URLs land on 404; guest sees only public routes.
|
||||
- An admin can create a role, assign permissions, and the change is reflected in both backend enforcement and frontend UI for affected users.
|
||||
- Owner-creation auto-creates the company; each provisioning step emails a login link.
|
||||
- Backend and frontend `typecheck`/`lint`/`test` pass; the Workstream 4 seed runs clean and backs an authenticated e2e.
|
||||
|
||||
### 6. API Documentation Hardening
|
||||
### 4. RBAC Seed Data And Fixtures
|
||||
|
||||
Status: open.
|
||||
|
||||
Problem:
|
||||
|
||||
Markdown docs exist for migrated modules, but Swagger/OpenAPI coverage for product-specific and cookie-session endpoints is incomplete.
|
||||
Status: open — depends on Workstream 3 (run after the role/permission/model changes land). Implements the spec's seed requirement: a preset company with one campus and staff covering every role, plus one test user per role. Also re-verifies the `npm run db:seed` gate from Workstream 1 against the final seed content.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Document `/api/auth/signin/local`.
|
||||
2. Document `/api/auth/refresh`.
|
||||
3. Document `/api/auth/signout`.
|
||||
4. Document `/api/auth/me`.
|
||||
5. Document product module endpoints that are not covered by generated Swagger output.
|
||||
6. Document response and error shapes per endpoint.
|
||||
1. **Reconcile the admin identity with the spec.** Make `admin@flatlogic.com` (`SEED_ADMIN_EMAIL`) the **`super_admin`** (the spec's superadmin), not `Administrator`. Add a distinct `system_admin` seed user. Repurpose or delete the leftover template users (`john@doe.com`, `client@hello.com`, `super_admin@flatlogic.com`) so the seed set is exactly the role fixtures below.
|
||||
2. **Seed one organization (company)** and **one campus** linked to it (give the existing `campuses` seed rows an `organizationId`, or seed a dedicated test campus).
|
||||
3. **Seed 10 users, one per stored role** — `super_admin`, `system_admin`, `owner`, `superintendent`, `director`, `office_manager`, `teacher`, `support_staff`, `student`, `guardian` — each with `app_roleId` set, `organizationId` set (except the two `system` users), and `campusId` set for the campus-scoped and external users as applicable. (`guest` = no user.) Passwords come from `SEED_ADMIN_PASSWORD`/`SEED_USER_PASSWORD` (no plaintext in the repo); test credentials live only in ignored local env or a dedicated test-seed config (per Workstream 8).
|
||||
4. **Seed staff profiles** for the campus staff roles (`director`, `office_manager`, `teacher`, `support_staff`) so the campus has staff covering every staff role, each linked to the org, the campus, and its user.
|
||||
5. Make the seed **idempotent** and reversible (clean `down`), and ordered after the role/permission seeder.
|
||||
6. Re-verify the seeded data: one company, one campus under it, staff with all campus roles, and exactly one loginable user per role. Add a seed/integration check.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- API docs match actual route payloads and response shapes.
|
||||
- Cookie auth behavior is explicit.
|
||||
- Frontend API contract tests remain aligned with docs.
|
||||
- `npm run db:seed` produces exactly one company, one campus under that company, staff covering every campus role, and one user per role with correct role/org/campus links.
|
||||
- `admin@flatlogic.com` is the `super_admin` and can log in; each seeded role can log in and lands on the access surface defined in §3.6.
|
||||
- No template leftover users remain; no plaintext credentials are committed.
|
||||
|
||||
### 7. Policy And Safety Acknowledgment Persistence
|
||||
### 5. Public Backend Route Audit
|
||||
|
||||
Status: open pending product contract.
|
||||
|
||||
Problem:
|
||||
|
||||
Policy content is document-backed, but policy/protocol acknowledgments are not yet persisted.
|
||||
|
||||
Required decisions:
|
||||
|
||||
1. Which policies/protocols require acknowledgment.
|
||||
2. Which roles must acknowledge.
|
||||
3. Whether acknowledgments are per document version.
|
||||
4. Who can report acknowledgment status.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Add acknowledgment backend model/migration/service/route.
|
||||
2. Enforce tenant and role scope.
|
||||
3. Add frontend typed API and business workflow.
|
||||
4. Add report views only if required.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Acknowledgments survive reload.
|
||||
- Acknowledgment status is tenant-scoped.
|
||||
- Unauthorized roles cannot view individual acknowledgment records.
|
||||
|
||||
### 8. Attendance Source Contracts
|
||||
|
||||
Status: partially open.
|
||||
|
||||
Current state:
|
||||
|
||||
- Campus attendance daily aggregate summaries are implemented for the current UI.
|
||||
- Staff attendance snapshot/reporting is read-only.
|
||||
- Student/class attendance source-of-truth workflow is not defined.
|
||||
|
||||
Open decisions:
|
||||
|
||||
1. Whether campus attendance aggregates are manually entered, imported, or derived from student attendance records.
|
||||
2. Which external or internal source owns staff attendance writes/imports.
|
||||
3. Whether student-level attendance UI is required.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Add write/import endpoints only after source contract exists.
|
||||
2. Keep derived summaries server-side if summaries are derived.
|
||||
3. Add backend tests for source-of-truth calculations.
|
||||
4. Add frontend workflows only after backend contracts exist.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Attendance source of truth is documented.
|
||||
- UI values can be traced to backend records or server-side derivation.
|
||||
- No frontend-only attendance source remains in persisted workflows.
|
||||
|
||||
### 9. Generated Audio Provider Contract
|
||||
|
||||
Status: open pending provider decision.
|
||||
|
||||
Problem:
|
||||
|
||||
Classroom timer uses built-in Web Audio sounds. AI-generated audio UI is intentionally not exposed because no backend audio provider contract exists.
|
||||
|
||||
No-go rules:
|
||||
|
||||
- Do not add generated audio UI.
|
||||
- Do not call external audio providers from frontend runtime.
|
||||
- Do not add frontend API keys or provider secrets.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Define backend provider contract.
|
||||
2. Keep provider secrets in backend env.
|
||||
3. Add backend service with native provider errors where required.
|
||||
4. Add typed frontend API and explicit loading/error states.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Generated audio is backend-mediated.
|
||||
- No provider secret reaches the browser.
|
||||
- Provider failures remain visible.
|
||||
|
||||
### 10. File Upload And Download Permissions
|
||||
|
||||
Status: open.
|
||||
|
||||
Problem:
|
||||
|
||||
Backend file and document routes exist. Product file workflows need explicit permission verification before new upload/download UI is added.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Audit upload permissions.
|
||||
2. Audit download permissions.
|
||||
3. Verify tenant and document ownership checks.
|
||||
4. Add typed frontend upload client only for approved workflows.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Unauthorized users cannot access another tenant's files.
|
||||
- Upload/download behavior is documented and tested before new UI is added.
|
||||
|
||||
### 11. Public Backend Route Audit
|
||||
|
||||
Status: open.
|
||||
Status: open. Feeds Workstream 3 §3.5 (guard coverage): the guard audit can only assert "every non-public route is authenticated" once the public set is fixed.
|
||||
|
||||
Problem:
|
||||
|
||||
@ -337,128 +255,9 @@ Acceptance criteria:
|
||||
- Every unauthenticated backend route is documented.
|
||||
- No tenant-owned data is exposed through accidental public routes.
|
||||
|
||||
### 12. Backend-Seeded Authenticated E2E
|
||||
### 6. API Surface Coverage And Dead-Endpoint Decision
|
||||
|
||||
Status: blocked by onboarding/profile fixtures.
|
||||
|
||||
Problem:
|
||||
|
||||
Current Playwright coverage includes backend-free smoke tests and backend-seeded content catalog tests. Authenticated persisted workflows need backend-seeded users, roles, campuses, staff profiles, and known credentials.
|
||||
|
||||
Required prerequisites:
|
||||
|
||||
1. Product onboarding/profile contract.
|
||||
2. Backend-seeded auth fixtures.
|
||||
3. Tenant-scoped campus/staff fixtures.
|
||||
4. Stable test credentials stored only in ignored local env or dedicated test seed config.
|
||||
|
||||
Required workflows after prerequisites:
|
||||
|
||||
1. Login to dashboard.
|
||||
2. Director creates or edits a FRAME entry and sees it after reload.
|
||||
3. Staff completes QBS quiz and director/superintendent sees compliance.
|
||||
4. Office enters campus attendance and superintendent sees aggregate.
|
||||
5. Director submits walkthrough and sees summary update.
|
||||
6. Staff marks a sign learned and progress persists after reload.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Authenticated e2e tests use backend seeds, not frontend mock data.
|
||||
- Tests do not require production secrets.
|
||||
- Tests are documented and repeatable.
|
||||
|
||||
### 13. Accessibility Test Coverage
|
||||
|
||||
Status: open.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Add axe/Playwright accessibility checks for login.
|
||||
2. Add axe/Playwright checks for dashboard.
|
||||
3. Add checks for sidebar navigation.
|
||||
4. Add checks for modal dialogs.
|
||||
5. Add checks for forms with validation.
|
||||
6. Add checks for tables/reports.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Accessibility tests run in a documented command.
|
||||
- Critical violations block completion of the refactor.
|
||||
|
||||
### 14. `ref-frontend/` Removal
|
||||
|
||||
Status: open.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Confirm no active docs require `ref-frontend/` for endpoint contract reference.
|
||||
2. Confirm no scripts import or run `ref-frontend/`.
|
||||
3. Delete `ref-frontend/`.
|
||||
4. Update root docs and any setup docs.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Only `frontend/` and `backend/` remain as active application code.
|
||||
- No docs describe `ref-frontend/` as needed for normal development.
|
||||
|
||||
### 15. OAuth Provider Strategy Modernization
|
||||
|
||||
Status: open. Deferred until after the backend TypeScript/ESM migration (now complete), to keep auth-flow changes separate from the language/module migration.
|
||||
|
||||
Problem:
|
||||
|
||||
Social login uses `passport-google-oauth2` (0.2.0, last published 2022) and `passport-microsoft` (2.1.0). The Google strategy is low-maintenance and should be modernized, ideally consolidating both providers on a single maintained OAuth/OIDC library.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Choose target: minimal swap to `passport-google-oauth20`, or consolidate both providers on `openid-client`.
|
||||
2. Replace the Google (and optionally Microsoft) passport strategy in `backend/src/auth/auth.ts`.
|
||||
3. Keep the existing cookie-based session and JWT issuance unchanged.
|
||||
4. Verify callback URLs, scopes, and the social-signup user flow end to end.
|
||||
5. Update auth docs (`backend/docs/auth-profile.md`, `cookie-auth.md`).
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Google and Microsoft sign-in work end to end with the chosen library.
|
||||
- No deprecated/unmaintained OAuth strategy remains in dependencies.
|
||||
- Cookie/JWT auth behavior is unchanged for existing sessions.
|
||||
- This change is isolated from the language/module migration (separate PR/task).
|
||||
|
||||
### 16. Permission-Based Frontend Authorization
|
||||
|
||||
Status: open.
|
||||
|
||||
Problem:
|
||||
|
||||
The backend already authorizes every request by **permission**, not by role. `backend/src/middlewares/check-permissions.ts` exposes `checkCrudPermissions(entity)`, which derives a permission name `${METHOD}_${ENTITY}` (e.g. `READ_CAMPUSES`, `CREATE_USERS`, `DELETE_ROLES` via `METHOD_MAP`) and grants access when any of the following holds, in order: (1) self-access — `currentUser.id === req.params.id`/`req.body.id`; (2) the user has a direct `custom_permissions` entry with that name; (3) the user's `app_role` (or the `Public` role for unauthenticated/no-role requests) has that permission via `role.getPermissions()`. An admin can therefore create roles and attach permissions (`Roles ↔ Permissions` many-to-many through `RolesDBApi.setPermissions`), plus assign per-user `custom_permissions`.
|
||||
|
||||
The frontend, however, does **not** gate on permissions. Module/page access is currently decided by `productRole` (`teacher | para | office | director | superintendent`) in `frontend/src/business/app-shell/selectors.ts` (`canAccessModule(modules, moduleId, userRole)`), and per the architecture contract frontend route hiding is **UX-only**. The user DTO already carries a `permissions: string[]` array (built by `getPermissionNames` in `services/auth.ts`), but the frontend ignores it. This makes the frontend authorization model inconsistent with the backend: a role with customized permissions is correctly enforced by the backend but not reflected in what the UI shows or allows.
|
||||
|
||||
Goal: make the frontend gate UI affordances (routes/menu items/buttons/request triggers) by **permission** — using the same `${METHOD}_${ENTITY}` permission names the backend checks — while keeping the backend as the sole source of truth (frontend gating stays UX-only; it must never be the only enforcement).
|
||||
|
||||
Required work:
|
||||
|
||||
1. **Expose permissions reliably in the auth contract.** Confirm `GET /api/auth/me` and the sign-in response include the resolved `permissions: string[]` (effective permissions = role permissions ∪ `custom_permissions`). Add `custom_permissions` to the resolution if not already merged. Document the exact field in `backend/docs/auth-profile.md`.
|
||||
2. **Define a shared permission vocabulary.** Add a typed catalog of permission names on the frontend (`frontend/src/shared/auth/permissions.ts`) mirroring backend naming `${METHOD}_${ENTITY}` (READ/CREATE/UPDATE/DELETE × entity). Keep it in `shared/constants`-style UI config; do not import backend code.
|
||||
3. **Add a permission selector/hook.** Implement `hasPermission(user, permissionName)` and `hasAnyPermission/hasAllPermissions` in `frontend/src/business/auth/` (pure functions over `CurrentUser.permissions`), plus a `usePermissions()` hook for components. Include the self-access nuance only where relevant (the backend allows self-access regardless of permission).
|
||||
4. **Gate routes by permission.** Replace/augment role-based `canAccessModule` with permission-based checks. Each route/module declares the permission(s) it requires; the router redirects/hides when the user lacks them. Keep `productRole` only where a true role concept is still needed (e.g. dashboards), not for resource access.
|
||||
5. **Gate UI affordances.** Hide or disable create/edit/delete buttons and other action triggers based on the matching permission (e.g. hide "Add campus" without `CREATE_CAMPUSES`). Avoid firing requests the backend will reject with 403.
|
||||
6. **Handle 403 consistently.** Ensure the API layer surfaces backend `forbidden` responses to a single handler (toast + no crash), so permission drift between UI and backend degrades gracefully.
|
||||
7. **Admin UI for roles/permissions.** Verify the roles management screens let an admin create a role, attach/detach permissions, and assign `custom_permissions` to a user — backed by the existing `roles`/`permissions`/`users` endpoints.
|
||||
8. **Tests.** Unit-test `hasPermission`/selectors with permission fixtures; add route-guard tests proving a user without `READ_X` cannot open module X; add a backend-seeded e2e proving UI affordances match backend enforcement for at least one CRUD entity.
|
||||
9. **Docs.** Update `frontend/docs/frontend-architecture.md` and `backend/docs/auth-profile.md` to describe the permission-based frontend model and the `${METHOD}_${ENTITY}` contract.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Frontend route/menu/affordance visibility is driven by the user's effective permissions, using the same names the backend enforces.
|
||||
- The backend remains the sole authority: removing a frontend check never grants real access (backend still returns 403).
|
||||
- An admin can create a role, assign permissions, and the change is reflected in both backend enforcement and frontend UI for affected users.
|
||||
- `productRole`-based gating is removed for resource access (kept only for genuine role-specific UI), with no remaining role↔permission inconsistency.
|
||||
- Frontend `typecheck`, `lint`, `test` pass; a seeded e2e proves UI/permission alignment for at least one entity.
|
||||
|
||||
### 17. API Surface Coverage And Dead-Endpoint Decision
|
||||
|
||||
Status: open (analysis complete; decision pending).
|
||||
Status: open (analysis complete; decision pending). Belongs with the access core: the permission catalog (§3.2) and guard audit (§3.5) must cover exactly the endpoints that survive this classification.
|
||||
|
||||
Problem:
|
||||
|
||||
@ -526,19 +325,18 @@ Required work:
|
||||
them (e.g. real `students`/`staff`/`guardians`/`invoices` management) and bring
|
||||
each group up to the target backend architecture. Do **not** prune them.
|
||||
2. `auth` extras: keep `signup`/password-reset/verify-email only if onboarding/recovery is
|
||||
in scope (see Workstream 4); otherwise prune. OAuth callbacks tie to Workstream 15.
|
||||
3. `file`: keep only if document/avatar upload is on the roadmap (ties to Workstream 10);
|
||||
in scope (see Workstream 3 §3.4 provisioning); otherwise prune. OAuth callbacks tie to Workstream 15.
|
||||
3. `file`: keep only if document/avatar upload is on the roadmap (ties to Workstream 7);
|
||||
otherwise prune.
|
||||
4. `search`: prune unless a search UI is planned.
|
||||
5. After any prune, re-run backend `typecheck`/`lint`, and regenerate
|
||||
`database-schema.md` only if models change.
|
||||
|
||||
Cross-references: Workstream 10 (file upload), 11 (public route audit), 15 (OAuth), 16
|
||||
(permission-based authorization).
|
||||
Cross-references: Workstream 7 (file upload), 5 (public route audit), 15 (OAuth), 3 (RBAC / permission-based authorization).
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Every backend endpoint is classified as **used**, **prune**, or **wire (planned)**, with no “unclassified” remainder.
|
||||
- Every backend endpoint is classified as **used**, **prune**, or **wire (planned)**, with no "unclassified" remainder.
|
||||
- Pruned routes are removed cleanly (route + service + db/api + permission wiring) with `typecheck`/`lint` green and no dangling imports.
|
||||
- The frontend remains fully wired (0 orphan calls) after changes.
|
||||
- This document reflects the final surface.
|
||||
@ -552,23 +350,256 @@ Performance hardening applied (owner chose **wire**, not prune, for the generic
|
||||
- The per-request `UsersDBApi.findBy` (passport JWT strategy → `req.currentUser`, read by every guarded route) was collapsed from `findOne` + 4 parallel association getters + a `getPermissions()` into a **single** eager-loaded query (`findOne` + `include` of `app_role`+`permissions`, `staff_user`, `custom_permissions`, `organizations`). Its returned `app_role` now carries `permissions`, and `middlewares/check-permissions.ts` was reordered to read that eager-loaded `permissions` array before falling back to `getPermissions()` — removing the extra per-request permissions query. Same returned shape/fields as before (`AuthenticatedUser`); ~6–7 queries per request → 1. The cached `Public` role still works (its record carries `permissions`).
|
||||
- `AuthService.currentUserProfile` (the `GET /me`, signin and refresh responses) now uses a dedicated `UsersDBApi.findProfileById` — a single eager-loaded query (`findByPk` + scoped `include`/`attributes`) returning only the columns and relations the profile DTO reads — instead of the heavy generic `findBy` (1 `findOne` + parallel association getters + `getPermissions`) plus a separate `getCampus`. Required idiomatic `NonAttribute` association declarations on the `Users`/`Roles`/`Staff` models so the include is type-safe without casts. The per-request passport `findBy` that populates `req.currentUser` is unchanged (it is the auth gate read by every guard); `/me` still performs that auth fetch plus this one lean profile query.
|
||||
|
||||
## Strict Implementation Sequence
|
||||
### 7. File Upload And Download Permissions
|
||||
|
||||
Use this order unless the user explicitly reprioritizes:
|
||||
Status: open. Permission-gated surface; aligns with Workstream 3 §3.2/§3.5 and Workstream 6 (`file` extras).
|
||||
|
||||
1. Backend migration and seed verification.
|
||||
2. Tenant boundary audit and tests.
|
||||
3. Role model decision and enforcement tests.
|
||||
4. Public route audit.
|
||||
5. API documentation hardening.
|
||||
6. Product onboarding after customer decision.
|
||||
7. Authenticated backend-seeded e2e after onboarding/profile fixtures exist.
|
||||
8. Remaining optional product contracts: acknowledgments, attendance imports, generated audio, file upload/download UI.
|
||||
9. Accessibility test coverage.
|
||||
10. Remove `ref-frontend/`.
|
||||
11. OAuth provider strategy modernization.
|
||||
12. Permission-based frontend authorization (align frontend gating with backend permission checks).
|
||||
13. API surface coverage decision: classify every endpoint and prune or wire the unused backend layer (Workstream 17).
|
||||
Problem:
|
||||
|
||||
Backend file and document routes exist. Product file workflows need explicit permission verification before new upload/download UI is added.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Audit upload permissions.
|
||||
2. Audit download permissions.
|
||||
3. Verify tenant and document ownership checks.
|
||||
4. Add typed frontend upload client only for approved workflows.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Unauthorized users cannot access another tenant's files.
|
||||
- Upload/download behavior is documented and tested before new UI is added.
|
||||
|
||||
**Phase 3 — Verification & Quality (locks in correctness once the access model and fixtures exist).**
|
||||
|
||||
### 8. Backend-Seeded Authenticated E2E
|
||||
|
||||
Status: depends on Workstream 4 fixtures and Workstream 3 guards.
|
||||
|
||||
Problem:
|
||||
|
||||
Current Playwright coverage includes backend-free smoke tests and backend-seeded content catalog tests. Authenticated persisted workflows need backend-seeded users, roles, campuses, staff profiles, and known credentials.
|
||||
|
||||
Required prerequisites:
|
||||
|
||||
1. Product onboarding/profile contract (Workstream 3 §3.4).
|
||||
2. Backend-seeded auth fixtures (Workstream 4).
|
||||
3. Tenant-scoped campus/staff fixtures (Workstream 4).
|
||||
4. Stable test credentials stored only in ignored local env or dedicated test seed config.
|
||||
|
||||
Required workflows after prerequisites:
|
||||
|
||||
1. Login to dashboard.
|
||||
2. Director creates or edits a FRAME entry and sees it after reload.
|
||||
3. Staff completes QBS quiz and director/superintendent sees compliance.
|
||||
4. Office enters campus attendance and superintendent sees aggregate.
|
||||
5. Director submits walkthrough and sees summary update.
|
||||
6. Staff marks a sign learned and progress persists after reload.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Authenticated e2e tests use backend seeds, not frontend mock data.
|
||||
- Tests do not require production secrets.
|
||||
- Tests are documented and repeatable.
|
||||
|
||||
### 9. API Documentation Hardening
|
||||
|
||||
Status: open. Run after the route surface is finalized (Workstreams 3, 5, 6) so the docs describe the real endpoints.
|
||||
|
||||
Problem:
|
||||
|
||||
Markdown docs exist for migrated modules, but Swagger/OpenAPI coverage for product-specific and cookie-session endpoints is incomplete.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Document `/api/auth/signin/local`.
|
||||
2. Document `/api/auth/refresh`.
|
||||
3. Document `/api/auth/signout`.
|
||||
4. Document `/api/auth/me`.
|
||||
5. Document product module endpoints that are not covered by generated Swagger output.
|
||||
6. Document response and error shapes per endpoint.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- API docs match actual route payloads and response shapes.
|
||||
- Cookie auth behavior is explicit.
|
||||
- Frontend API contract tests remain aligned with docs.
|
||||
|
||||
### 10. Accessibility Test Coverage
|
||||
|
||||
Status: open.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Add axe/Playwright accessibility checks for login.
|
||||
2. Add axe/Playwright checks for dashboard.
|
||||
3. Add checks for sidebar navigation.
|
||||
4. Add checks for modal dialogs.
|
||||
5. Add checks for forms with validation.
|
||||
6. Add checks for tables/reports.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Accessibility tests run in a documented command.
|
||||
- Critical violations block completion of the refactor.
|
||||
|
||||
**Phase 4 — Product Contracts (each gated by a customer/provider decision; independent of one another and parallelizable once the access core is in place).**
|
||||
|
||||
### 11. Policy And Safety Acknowledgment Persistence
|
||||
|
||||
Status: open pending product contract.
|
||||
|
||||
Problem:
|
||||
|
||||
Policy content is document-backed, but policy/protocol acknowledgments are not yet persisted.
|
||||
|
||||
Required decisions:
|
||||
|
||||
1. Which policies/protocols require acknowledgment.
|
||||
2. Which roles must acknowledge.
|
||||
3. Whether acknowledgments are per document version.
|
||||
4. Who can report acknowledgment status.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Add acknowledgment backend model/migration/service/route.
|
||||
2. Enforce tenant and role scope.
|
||||
3. Add frontend typed API and business workflow.
|
||||
4. Add report views only if required.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Acknowledgments survive reload.
|
||||
- Acknowledgment status is tenant-scoped.
|
||||
- Unauthorized roles cannot view individual acknowledgment records.
|
||||
|
||||
### 12. Attendance Source Contracts
|
||||
|
||||
Status: partially open.
|
||||
|
||||
Current state:
|
||||
|
||||
- Campus attendance daily aggregate summaries are implemented for the current UI.
|
||||
- Staff attendance snapshot/reporting is read-only.
|
||||
- Student/class attendance source-of-truth workflow is not defined.
|
||||
|
||||
Open decisions:
|
||||
|
||||
1. Whether campus attendance aggregates are manually entered, imported, or derived from student attendance records.
|
||||
2. Which external or internal source owns staff attendance writes/imports.
|
||||
3. Whether student-level attendance UI is required.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Add write/import endpoints only after source contract exists.
|
||||
2. Keep derived summaries server-side if summaries are derived.
|
||||
3. Add backend tests for source-of-truth calculations.
|
||||
4. Add frontend workflows only after backend contracts exist.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Attendance source of truth is documented.
|
||||
- UI values can be traced to backend records or server-side derivation.
|
||||
- No frontend-only attendance source remains in persisted workflows.
|
||||
|
||||
### 13. Generated Audio Provider Contract
|
||||
|
||||
Status: open pending provider decision.
|
||||
|
||||
Problem:
|
||||
|
||||
Classroom timer uses built-in Web Audio sounds. AI-generated audio UI is intentionally not exposed because no backend audio provider contract exists.
|
||||
|
||||
No-go rules:
|
||||
|
||||
- Do not add generated audio UI.
|
||||
- Do not call external audio providers from frontend runtime.
|
||||
- Do not add frontend API keys or provider secrets.
|
||||
|
||||
Required work after decision:
|
||||
|
||||
1. Define backend provider contract.
|
||||
2. Keep provider secrets in backend env.
|
||||
3. Add backend service with native provider errors where required.
|
||||
4. Add typed frontend API and explicit loading/error states.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Generated audio is backend-mediated.
|
||||
- No provider secret reaches the browser.
|
||||
- Provider failures remain visible.
|
||||
|
||||
**Phase 5 — Operations, Modernization & Cleanup (low coupling; can run any time after Phase 2, scheduled last to avoid mixing with the access-model changes).**
|
||||
|
||||
### 14. Refresh Token Maintenance
|
||||
|
||||
Status: open.
|
||||
|
||||
Problem:
|
||||
|
||||
Cookie auth and refresh rotation exist, but scheduled cleanup for expired/revoked refresh-token rows remains unresolved.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Define refresh-token retention period.
|
||||
2. Add cleanup job or operational command.
|
||||
3. Ensure cleanup is observable.
|
||||
4. Document operational usage.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Expired and revoked refresh-token rows are cleaned after the approved retention window.
|
||||
- Cleanup failures are visible and not silent.
|
||||
- Auth behavior remains unchanged for valid sessions.
|
||||
|
||||
### 15. OAuth Provider Strategy Modernization
|
||||
|
||||
Status: open. Deferred until after the backend TypeScript/ESM migration (now complete), to keep auth-flow changes separate from the language/module migration.
|
||||
|
||||
Problem:
|
||||
|
||||
Social login uses `passport-google-oauth2` (0.2.0, last published 2022) and `passport-microsoft` (2.1.0). The Google strategy is low-maintenance and should be modernized, ideally consolidating both providers on a single maintained OAuth/OIDC library.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Choose target: minimal swap to `passport-google-oauth20`, or consolidate both providers on `openid-client`.
|
||||
2. Replace the Google (and optionally Microsoft) passport strategy in `backend/src/auth/auth.ts`.
|
||||
3. Keep the existing cookie-based session and JWT issuance unchanged.
|
||||
4. Verify callback URLs, scopes, and the social-signup user flow end to end.
|
||||
5. Update auth docs (`backend/docs/auth-profile.md`, `cookie-auth.md`).
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Google and Microsoft sign-in work end to end with the chosen library.
|
||||
- No deprecated/unmaintained OAuth strategy remains in dependencies.
|
||||
- Cookie/JWT auth behavior is unchanged for existing sessions.
|
||||
- This change is isolated from the language/module migration (separate PR/task).
|
||||
|
||||
### 16. `ref-frontend/` Removal
|
||||
|
||||
Status: open.
|
||||
|
||||
Required work:
|
||||
|
||||
1. Confirm no active docs require `ref-frontend/` for endpoint contract reference.
|
||||
2. Confirm no scripts import or run `ref-frontend/`.
|
||||
3. Delete `ref-frontend/`.
|
||||
4. Update root docs and any setup docs.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Only `frontend/` and `backend/` remain as active application code.
|
||||
- No docs describe `ref-frontend/` as needed for normal development.
|
||||
|
||||
## Phased Execution Order
|
||||
|
||||
Run in workstream-number order unless the user explicitly reprioritizes. The phases are the dependency gates; within a phase, items may proceed in parallel.
|
||||
|
||||
1. **Phase 1 — Foundation:** Workstream 1 (backend migrates, boots, serves routes).
|
||||
2. **Phase 2 — Identity, Tenancy & Access Core:** Workstream 2 (tenant isolation) → Workstream 3 (RBAC: roles, permissions, constraints, provisioning, guards) → Workstream 4 (RBAC seed/fixtures), with Workstream 5 (public route audit), Workstream 6 (API surface classify/wire), and Workstream 7 (file permissions) finalizing the protected surface alongside Workstream 3.
|
||||
3. **Phase 3 — Verification & Quality:** Workstream 8 (authenticated seeded e2e) → Workstream 9 (API docs) → Workstream 10 (accessibility).
|
||||
4. **Phase 4 — Product Contracts (customer-gated, parallel):** Workstreams 11 (acknowledgments), 12 (attendance source), 13 (generated audio).
|
||||
5. **Phase 5 — Operations, Modernization & Cleanup:** Workstreams 14 (refresh-token cleanup), 15 (OAuth modernization), 16 (`ref-frontend/` removal).
|
||||
|
||||
## Definition Of Done
|
||||
|
||||
@ -577,9 +608,12 @@ The integration refactor is complete only when all of the following are true:
|
||||
- Backend migrations and seeders pass on the configured local database.
|
||||
- Backend lint passes without broad ignores.
|
||||
- Backend tenant isolation tests prove cross-tenant data is not visible or mutable.
|
||||
- The 11-role scoped hierarchy (Workstream 3) is enforced backend-side, including the relational "except …" constraints for `system_admin`, `superintendent`, and `director`; the legacy generated-role mapping and 5-value `productRole` are removed.
|
||||
- Backend role/permission tests cover product staff, director, and superintendent paths.
|
||||
- Frontend gating is permission-driven with an authentication guard; unauthenticated users redirect to `/login`, forbidden direct URLs land on 404, and guest sees only public routes.
|
||||
- The RBAC seed (Workstream 4) provisions one company, one campus with staff covering every campus role, and one loginable user per role, with `admin@flatlogic.com` as the super admin.
|
||||
- Every unauthenticated backend route is documented as public-by-design.
|
||||
- Product onboarding contract is customer-approved or explicitly excluded from release scope.
|
||||
- Product onboarding contract is implemented per Workstream 3 §3.4 or explicitly excluded from release scope.
|
||||
- Every visible frontend workflow is backed by a typed backend API contract.
|
||||
- No frontend runtime workflow depends on mock, sample, seed, or fallback records for persisted behavior.
|
||||
- Frontend `typecheck`, `lint`, `test`, `build`, `test:e2e`, and documented seeded e2e suites pass in their required environments.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user