UN-3853 [FIX] Attribute platform-key-created resources to the API key's creator - #2274
UN-3853 [FIX] Attribute platform-key-created resources to the API key's creator#2274kirtimanmishrazipstack wants to merge 2 commits into
Conversation
|
| Filename | Overview |
|---|---|
| backend/platform_api/services.py | Adds centralized resolution from a platform-key service account to its human creator, with an explicit service-account fallback when the creator has been deleted. |
| backend/prompt_studio/prompt_studio_core_v2/prompt_studio_helper.py | Applies resolved ownership when imported Prompt Studio resources receive their initial OWNER membership. |
| backend/api_v2/api_deployment_views.py | Assigns API deployment ownership to the platform key creator while retaining the acting service account for audit behavior. |
| backend/pipeline_v2/views.py | Assigns pipeline OWNER membership through the new platform-key owner resolver. |
| frontend/src/components/widgets/owner-display.js | Centralizes actual-owner selection, “Me” labeling, co-owner counts, and the residual “Platform key” fallback. |
| frontend/src/components/widgets/resource-table/ResourceTable.jsx | Reuses the centralized owner-display logic for resource table rows. |
Sequence Diagram
sequenceDiagram
participant Client as Platform API client
participant API as Resource create endpoint
participant Resolver as owner_user_for
participant Key as PlatformApiKey
participant Resource as Created resource
Client->>API: Create using platform API key
API->>Resolver: Resolve request.user
Resolver->>Key: Find key by service account
alt Creator still exists
Key-->>Resolver: created_by user
Resolver-->>API: Human key creator
API->>Resource: Add OWNER membership for creator
else Creator was deleted
Key-->>Resolver: created_by is null
Resolver-->>API: Service account
API->>Resource: Add service-account membership
Note over Resource: UI displays "Platform key"
end
Reviews (3): Last reviewed commit: "UN-3853 [FIX] Name the real owner on the..." | Re-trigger Greptile
Merge this before the cloud PRZipstack/unstract-cloud#1753 imports |
…reator A platform API key authenticates as a service account, and every resource create path granted the OWNER membership row to that machine identity. Service accounts are filtered out of every owner surface (HasMembersMixin), so such a resource ended up with no human owner: invisible to its creator in list views, manageable only through the org-admin fallback, and rendered in "Owned By" as a synthetic @platform.internal address dressed up as a colleague. Record the key's creator as owner instead — the same successor delete_api_user_for_key already hands ownership to when a key is deleted, now applied at creation rather than only at deletion. The service account loses nothing: permission classes and for_user() short-circuit on is_service_account. Where no human can be named (the key's creator has since been deleted), the resource stays deliberately ownerless and the table labels it "Platform key" rather than naming a machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JHnDZZWGhsevUdwgMyR2ai
…s too The ticket asks for Owned By to be correct on every resource type. The API deployment and ETL pipeline card views were still wrong: OwnerFieldRow read created_by_email only, and their serializers never exposed owner_emails — so those cards named the audit creator, which on a platform-key create is the service account. The backend fix alone could not reach them. Expose owner_emails on both serializers (their querysets already prefetch memberships__user, so it costs no extra query), and move the owner-label rule into one resolveOwnerDisplay helper shared by the table and the cards. The two had already drifted on both the source field and the "Me" rule — the card said "Me" to any owner, which is the co-owner bug the table's comment warns about. Cards now match the table: "Me" tracks the displayed owner. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JHnDZZWGhsevUdwgMyR2ai
90013a8 to
b818d85
Compare
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|



What
Why
Unknown, and since UN-3770 [MISC] Make list pagination consistent across shared resource endpoints #2208 prints the robot's synthetic@platform.internaladdress with an avatar, which reads as a colleague who does not exist.UN / Unknownin Owned By.How
owner_user_for(user)inbackend/platform_api/services.py: if the acting user is a platform key's service account, return the key'screated_by; otherwise return the user unchanged. It short-circuits before any query for a normal session, so the UI path is unaffected.delete_api_user_for_keyin the same module already hands everything a key created over toplatform_api_key.created_bywhen the key is deleted. The change applies that same successor at creation time instead of only at deletion time.for_user()manager already short-circuits onis_service_account.created_byis untouched everywhere. It is an audit field, and "the robot did it" is the truthful answer.renderOwnerinResourceTable.jsxrecognises the reservedplatform.internaldomain and rendersPlatform keyinstead of dressing a machine identity up as a person. This is what makes a data migration unnecessary — rows created before this change still read correctly.Can this PR break any existing features. If yes, please list possible items. If no, please explain why.
owner_user_for()returns the user unchanged before touching the database, so every UI-driven create behaves exactly as before — bit for bit.IsOwner,IsOwnerOrSharedUser,IsParentWorkflowOwnerand friends all returnTruefor a service account before ownership is consulted, andfor_user()returns the unfiltered queryset for one.delete_api_user_for_keystill works. Its membership transfer simply finds nothing left to move, and itscreated_by/modified_byre-pointing is unchanged and still required.Database Migrations
Platform keylabel, and they are exactly as reachable as they are today (org admins). If we later decide to repair them, the shipped_transfer_membership_rows(key.api_user, key.created_by)does it from a shell — no new code, and no irreversible migration running against production for a handful of rows.Env Config
Relevant Docs
Related Issues or PRs
created_by_emailfallback added in UN-3770 [MISC] Make list pagination consistent across shared resource endpoints #2208 is what changed the symptom fromUnknownto a synthetic address; this PR removes the need for it on the platform-key path.Dependencies Versions
Notes on Testing
Unknownin Owned ByPOST /api/v1/unstract/{org}/prompt-studio/using a Bearer platform key, then open the Prompt Studio listcreated_by, then reload the listPlatform keyPlatform key, same access as beforeNo automated tests added, per the repo convention of not adding tests unless asked. Happy to add coverage for
owner_user_forif reviewers want it.Screenshots
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code
https://claude.ai/code/session_01JHnDZZWGhsevUdwgMyR2ai