Background
The UpdateProjectResource RPC updates a resource. Today core/resource Update only writes title/metadata to Postgres. It does not touch SpiceDB. So if a resource's project (#project) or owner (#owner) changes, the SpiceDB relations drift away from Postgres. This is gap (7) of #1661.
What the (now closed) PR did
PR #1686 reworked Update to reconcile those relations:
- load the existing resource first
- resolve a PAT principal to its underlying user
- keep the current owner when the request sends no principal (a title/metadata-only update should not blank the owner)
- rebuild the URN from the target project
- on project change: delete the old
#project tuple, add the new one
- on owner change: delete the old
#owner tuple, add the new one
- repository
Update now persists urn/project_id/principal_id/principal_type (and stores NULL, not "", when there is no principal, matching Create)
- e2e test moves a resource from project A to project B and asserts the
#project subject flips to B only
The reconcile logic itself is sound. The deletes are scoped to the one resource's object_id plus the single relation name, so there is no over-delete.
Why we paused it
The update path also runs two checks (in pkg/server/connect_interceptors/authorization.go, the UpdateProjectResource entry) that were never designed for a project move:
-
AuthZ — IsAuthorized(Object{namespace, resource_id}, update). This only checks the update permission on the resource itself. On a move it does not check any permission on the target project. So a caller who can update a resource could move it into a project (and org) where they have no rights.
-
Billing entitlement — CheckPlanEntitlement(Object{project, request.project_id}). This checks the plan of the project named in the request (the target). For a cross-org move, how usage and entitlements are counted between the source org and the target org is undefined.
Open questions (decide before re-landing the reconcile)
- Should
UpdateProjectResource be allowed to change the project at all, or should "move" be a separate, explicit operation?
- Same-org move vs different-org move — do we allow both? A different-org move is the bigger one: it changes billing, ownership, and audit ownership.
- AuthZ on a move: which permission(s) should we require on the target project (for example create/update on the destination) on top of
update on the resource?
- Billing on a cross-org move: which org's plan applies, and how is usage/entitlement re-attributed?
- Owner change: when the principal changes, do we need any check on the new owner?
- URN: the URN is built from the project name, so moving projects changes the URN. Confirm downstream consumers handle a URN change.
Scope
This stays part of gap (7) of #1661. The SpiceDB reconcile should land only after the move / AuthZ / billing behaviour above is decided. PR #1686 is closed in favor of this issue; its branch and code can be reused once the behaviour is settled.
Background
The
UpdateProjectResourceRPC updates a resource. Todaycore/resourceUpdateonly writestitle/metadatato Postgres. It does not touch SpiceDB. So if a resource's project (#project) or owner (#owner) changes, the SpiceDB relations drift away from Postgres. This is gap (7) of #1661.What the (now closed) PR did
PR #1686 reworked
Updateto reconcile those relations:#projecttuple, add the new one#ownertuple, add the new oneUpdatenow persistsurn/project_id/principal_id/principal_type(and stores NULL, not"", when there is no principal, matchingCreate)#projectsubject flips to B onlyThe reconcile logic itself is sound. The deletes are scoped to the one resource's
object_idplus the single relation name, so there is no over-delete.Why we paused it
The update path also runs two checks (in
pkg/server/connect_interceptors/authorization.go, theUpdateProjectResourceentry) that were never designed for a project move:AuthZ —
IsAuthorized(Object{namespace, resource_id}, update). This only checks theupdatepermission on the resource itself. On a move it does not check any permission on the target project. So a caller who can update a resource could move it into a project (and org) where they have no rights.Billing entitlement —
CheckPlanEntitlement(Object{project, request.project_id}). This checks the plan of the project named in the request (the target). For a cross-org move, how usage and entitlements are counted between the source org and the target org is undefined.Open questions (decide before re-landing the reconcile)
UpdateProjectResourcebe allowed to change the project at all, or should "move" be a separate, explicit operation?updateon the resource?Scope
This stays part of gap (7) of #1661. The SpiceDB reconcile should land only after the move / AuthZ / billing behaviour above is decided. PR #1686 is closed in favor of this issue; its branch and code can be reused once the behaviour is settled.