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
…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
|
| Filename | Overview |
|---|---|
| backend/platform_api/services.py | Adds the central service-account-to-key-creator ownership resolver and shared reserved email-domain constant. |
| backend/api_v2/api_deployment_views.py | Grants API deployment ownership to the resolved platform-key creator. |
| backend/pipeline_v2/views.py | Grants pipeline ownership to the resolved platform-key creator during creation. |
| backend/prompt_studio/prompt_studio_core_v2/views.py | Attributes newly created Prompt Studio resources to the platform-key creator. |
| backend/workflow_manager/workflow_v2/views.py | Attributes new workflow OWNER memberships to the resolved human owner. |
| frontend/src/components/widgets/owner-display.js | Centralizes owner selection, “Me” labeling, co-owner counts, and platform-key fallback rendering. |
| frontend/src/components/widgets/resource-table/ResourceTable.jsx | Reuses the shared owner-display resolver for resource-table rows. |
| frontend/src/components/widgets/card-grid-view/CardFieldComponents.jsx | Aligns card owner rendering with the shared table behavior. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
K[Platform API key request] --> S[Service-account identity]
S --> R[owner_user_for]
R -->|Creator exists| H[Key creator]
R -->|Creator deleted| S
H --> M[OWNER membership]
S --> A[Audit created_by]
M --> D[Human owner display and access]
A --> P[Platform key label when no human owner exists]
Reviews (2): Last reviewed commit: "UN-3853 [FIX] Name the real owner on the..." | Re-trigger Greptile
…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
Frontend Lint Report (Biome)✅ All checks passed! No linting or formatting issues found. |
|
Unstract test resultsPer-group results
Critical paths
|
Merge this before the cloud PRZipstack/unstract-cloud#1753 imports |



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