diff --git a/backend/src/lib/access.ts b/backend/src/lib/access.ts index d506115..d8aa50a 100644 --- a/backend/src/lib/access.ts +++ b/backend/src/lib/access.ts @@ -7,11 +7,15 @@ import { } from "better-auth/plugins/organization/access"; // RBAC for clinics (organizations). We extend Better Auth's default -// organization statements (organization / member / invitation / team) with a -// `patient` resource so roles can be granted fine-grained access to records. +// organization statements (organization / member / invitation / team) with +// clinical resources (`patient`, `appointment`, `prescription`, `task`) so roles +// can be granted fine-grained access to records. export const statements = { ...defaultStatements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], } as const; export const ac = createAccessControl(statements); @@ -21,26 +25,38 @@ export const ac = createAccessControl(statements); // and add a read-only `viewer`. In the UI these read as Owner / Admin / // Clinician (member) / Viewer. // -// owner / admin: run the clinic AND have full access to patient records. +// owner / admin: run the clinic AND have full access to clinical records. export const owner = ac.newRole({ ...ownerAc.statements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); export const admin = ac.newRole({ ...adminAc.statements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); -// member (clinician): a regular member who can read and edit patient records. +// member (clinician): a regular member who can read and edit clinical records. export const member = ac.newRole({ ...memberAc.statements, patient: ["read", "write"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); -// viewer: read-only access to patient records. +// viewer: read-only access to clinical records. export const viewer = ac.newRole({ patient: ["read"], + appointment: ["read"], + prescription: ["read"], + task: ["read"], }); export const roles = { owner, admin, member, viewer }; diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 6844f62..49b1a7d 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -5,7 +5,7 @@ import type { NextFunction, Request, Response } from "express"; import { auth } from "../auth.js"; import { db } from "../db/index.js"; import { member } from "../db/schema/auth.js"; -import { roles } from "../lib/access.js"; +import { roles, type statements } from "../lib/access.js"; import { HttpError } from "../lib/http-error.js"; // Validates the Better Auth session cookie and attaches the user + session. @@ -61,8 +61,12 @@ export async function requireOrg( } } -type PatientAction = "read" | "write" | "delete"; -type PermissionRequest = { patient?: PatientAction[] }; +// A permission request maps clinic resources (patient / appointment / … defined +// in src/lib/access.ts) to the actions required on them. Mirrors the shape Better +// Auth's `role.authorize` accepts. +type PermissionRequest = Partial<{ + [R in keyof typeof statements]: ((typeof statements)[R][number])[]; +}>; // Gates a route on a clinic permission, evaluated against the caller's role(s) // using the shared access-control definitions. Must run after requireOrg. diff --git a/frontend/lib/access.ts b/frontend/lib/access.ts index cbd3e8d..b03c489 100644 --- a/frontend/lib/access.ts +++ b/frontend/lib/access.ts @@ -11,6 +11,9 @@ import { export const statements = { ...defaultStatements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], } as const; export const ac = createAccessControl(statements); @@ -18,20 +21,32 @@ export const ac = createAccessControl(statements); export const owner = ac.newRole({ ...ownerAc.statements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); export const admin = ac.newRole({ ...adminAc.statements, patient: ["read", "write", "delete"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); export const member = ac.newRole({ ...memberAc.statements, patient: ["read", "write"], + appointment: ["read", "write", "delete"], + prescription: ["read", "write", "delete"], + task: ["read", "write", "delete"], }); export const viewer = ac.newRole({ patient: ["read"], + appointment: ["read"], + prescription: ["read"], + task: ["read"], }); export const roles = { owner, admin, member, viewer };