feat(server): add project sharing access control
This commit is contained in:
@@ -204,6 +204,59 @@ Renames the infinite canvas project and updates `updatedAt`.
|
||||
|
||||
Deletes the project record. PostgreSQL cascades canvas nodes, chat messages, history, and connections; after the database delete succeeds, image assets referenced by the canvas and generated-file history are queued for asynchronous OSS deletion.
|
||||
|
||||
## Sharing and access control
|
||||
|
||||
Sharing uses a separate authorization policy from project ownership. Effective access is resolved in this order: owner, explicit email/phone member, then link permission.
|
||||
|
||||
- `canvas`: canvas document only; messages and agent endpoints are unavailable.
|
||||
- `viewer`: canvas and chat history are readable, but every mutation is denied.
|
||||
- `editor`: viewer access plus canvas/agent mutations; authentication is mandatory.
|
||||
- `owner`: editor access plus share administration and project deletion.
|
||||
|
||||
Share IDs are 27-character, case-sensitive base62 values generated from cryptographic randomness. The database stores a SHA-256 lookup hash and AES-GCM ciphertext rather than a plaintext link credential. Supply `SHARING_ENCRYPTION_SECRET` through the deployment secret manager; when omitted, local development falls back to `Auth.TokenSecret`.
|
||||
|
||||
All management endpoints require `Authorization: Bearer <accessToken>` and verify project ownership:
|
||||
|
||||
- `GET /api/projects/:id/sharing` returns link permission, authorized people, owner information, and visitor count.
|
||||
- `PUT /api/projects/:id/sharing/link` accepts `private`, `canvas`, `viewer`, or `editor`.
|
||||
- `POST /api/projects/:id/sharing/members` accepts an email/phone `identifier` and a `canvas`, `viewer`, or `editor` permission. Identifiers are normalized and unique per project; inviting the owner returns `share.self_invite`, while inviting an existing member returns `share.member_exists` (both HTTP `409`).
|
||||
- `PATCH /api/projects/:id/sharing/members/:memberId` changes a member permission.
|
||||
- `DELETE /api/projects/:id/sharing/members/:memberId` removes one member.
|
||||
- `GET /api/projects/:id/sharing/visitors` lists deduplicated visitors and visit counts.
|
||||
- `DELETE /api/projects/:id/sharing/access` removes the policy, members, and visitors; the old link immediately fails on the next request.
|
||||
|
||||
Example invite:
|
||||
|
||||
```json
|
||||
{
|
||||
"identifier": "editor@example.com",
|
||||
"permission": "editor"
|
||||
}
|
||||
```
|
||||
|
||||
`GET /api/shares/:shareId` is the public bootstrap. `Authorization` is optional for `canvas`/`viewer`, mandatory for `editor`, and used to resolve explicit member access. Clients send a random installation-scoped `X-Share-Visitor` value for visitor deduplication.
|
||||
|
||||
```json
|
||||
{
|
||||
"shareId": "XhRewKnS4it7X9kEMFLcdVJentg",
|
||||
"project": { "id": "project-id", "nodes": [], "messages": [] },
|
||||
"access": {
|
||||
"permission": "canvas",
|
||||
"source": "link",
|
||||
"authenticated": false,
|
||||
"isOwner": false,
|
||||
"capabilities": {
|
||||
"canViewCanvas": true,
|
||||
"canViewChat": false,
|
||||
"canEdit": false,
|
||||
"canManage": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Subsequent shared project requests send `X-Share-Id: <shareId>`. Middleware re-resolves access, binds it to the exact project ID, and only then creates an internal owner-scoped repository context. Client-supplied `X-User-Id` is ignored.
|
||||
|
||||
## Agent Chat
|
||||
|
||||
`POST /api/projects/:id/agent/chat`
|
||||
|
||||
Reference in New Issue
Block a user