39948-vm/documentation/private-production-presentations.md
2026-07-03 16:11:24 +02:00

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.