144 lines
4.7 KiB
Markdown
144 lines
4.7 KiB
Markdown
# Private Production Presentations
|
|
|
|
Production presentations are public by default at `/p/[slug]`. Each project can
|
|
switch its production presentation to private with the project-level
|
|
`production_presentation_visibility` field.
|
|
|
|
## Project Visibility
|
|
|
|
**Storage:** `projects.production_presentation_visibility`
|
|
|
|
Values:
|
|
|
|
- `public` - default; anonymous users can open `/p/[slug]`
|
|
- `private` - anonymous users must log in; access is then checked by staff
|
|
permissions or customer grants
|
|
|
|
The project create/edit forms expose this as **Production Presentation
|
|
Visibility**. Existing projects stay public after migration for backward
|
|
compatibility.
|
|
|
|
## Customer Access Grants
|
|
|
|
**Storage:** `production_presentation_access`
|
|
|
|
This table grants a customer user access to a private production presentation:
|
|
|
|
- `projectId`
|
|
- `userId`
|
|
- unique active pair: `(projectId, userId)`
|
|
|
|
User lifecycle remains in the existing users system. Presentation access is
|
|
stored in DB rows, not config files.
|
|
|
|
## Runtime Access Flow
|
|
|
|
**Services:**
|
|
|
|
- `backend/src/services/runtime-presentation-access.ts` handles slug lookup and
|
|
private-presentation listing.
|
|
- `backend/src/services/access-policy.ts` owns the final access decision through
|
|
`canViewProductionPresentation(user, projectSlug)`.
|
|
|
|
The service determines:
|
|
|
|
- whether the project slug points to a private production presentation
|
|
- whether an authenticated user can use admin APIs (`Public` users are never
|
|
treated as staff, even if stale permissions exist)
|
|
- whether a customer user has a `production_presentation_access` row
|
|
- which private production slugs are allowed for the current customer user
|
|
|
|
For public production slugs, read-only runtime requests stay public.
|
|
|
|
For private production slugs:
|
|
|
|
- anonymous `GET` requests return `401`
|
|
- authenticated users with any RBAC permission can access all private
|
|
production presentations as platform staff
|
|
- authenticated users with `Public` role and no permissions must have an access
|
|
row for the project
|
|
- authenticated but unauthorized users receive `403`
|
|
- authorized users are marked as `req.isRuntimePublicRequest = true` so runtime
|
|
routes return only runtime-safe entity fields
|
|
|
|
Protected runtime data includes:
|
|
|
|
- `/api/projects`
|
|
- `/api/tour_pages`
|
|
- `/api/project_audio_tracks`
|
|
- `/api/project-transition-settings/project/:projectId/env/production`
|
|
|
|
## User Creation And Edit Flow
|
|
|
|
`Administrator`, `Platform Owner`, and `Account Manager` can create users and
|
|
edit existing users.
|
|
|
|
When the selected role is `Public`, the user form shows **Allowed
|
|
Private Production Presentations**. The selector lists only projects whose
|
|
production presentation visibility is `private`.
|
|
|
|
On create submit:
|
|
|
|
- the user is created normally
|
|
- if the selected role is `Public`, selected private project IDs create
|
|
`production_presentation_access` rows
|
|
- if the selected role is not `Public`, selected project IDs are ignored
|
|
|
|
On edit submit:
|
|
|
|
- existing `production_presentation_access` rows for that user are replaced by
|
|
the selected private project IDs
|
|
- switching a user away from `Public` clears their private presentation grants
|
|
- loading a Public user edit form preselects the user's current private
|
|
presentation grants
|
|
|
|
Do not grant customer viewer users broad app permissions such as
|
|
`READ_PROJECTS` or `READ_TOUR_PAGES`; any RBAC permission makes the user
|
|
platform staff for private presentation access.
|
|
|
|
Backend hardening also rejects custom permissions for users whose role is
|
|
`Public`, rejects permissions assigned to the `Public` role through the Roles
|
|
service, and ignores `Public` user or role-fallback permissions in
|
|
`AccessPolicy` for admin APIs.
|
|
|
|
## Audit And Cleanup
|
|
|
|
Use the backend audit command to detect stale access records from older data:
|
|
|
|
```bash
|
|
cd backend
|
|
npm run check:public-access
|
|
```
|
|
|
|
It reports:
|
|
|
|
- RBAC permissions assigned to roles named `Public`
|
|
- custom permissions assigned to Public users
|
|
- private production presentation grants assigned to non-Public users
|
|
|
|
To clean those stale records after reviewing the output:
|
|
|
|
```bash
|
|
npm run fix:public-access
|
|
```
|
|
|
|
The fix removes only the stale Public-role permissions, Public-user custom
|
|
permissions, and non-Public private presentation grants.
|
|
|
|
## Frontend Behavior
|
|
|
|
If runtime data loading receives `401`, `/p/[slug]` redirects to:
|
|
|
|
```text
|
|
/login?next=/p/[slug]
|
|
```
|
|
|
|
After login, `login.tsx` redirects to `next` when it is a safe local path. If a
|
|
Public customer user reaches authenticated application pages,
|
|
`LayoutAuthenticated` redirects to the first private production slug returned by
|
|
`/api/auth/me`; if the Public user has no private presentation grants, it
|
|
redirects to `/`.
|
|
|
|
Public production presentations remain accessible to anonymous users, platform
|
|
staff, and logged-in customer users.
|