Fix audit-identity gaps: updated_by, org membership, UTC timestamps#2651
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe changes add audit-field persistence and retrieval, normalize timestamps to UTC, restrict organization listings by authorization and membership, batch identity resolution in service responses, and update workspace organization retrieval and audit metadata handling. ChangesPlatform schema, audit persistence, and UTC handling
Organization access control
Service and workspace integration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant OrganizationHandler
participant Authorization
participant OrganizationService
participant OrganizationRepository
Client->>OrganizationHandler: GET /organizations
OrganizationHandler->>Authorization: HasEffectiveScope(request, authzMode, manage scope)
alt manage scope present
OrganizationHandler->>OrganizationService: ListOrganizations(pagination)
OrganizationService->>OrganizationRepository: List all organizations
else no manage scope
OrganizationHandler->>OrganizationService: ListOrganizationsForUser(actor, resolved organization, pagination)
OrganizationService->>OrganizationRepository: Count and list memberships
OrganizationService->>OrganizationRepository: AddMembership when resolved organization is missing
end
OrganizationRepository-->>OrganizationService: Organizations and count
OrganizationService-->>OrganizationHandler: Organization response
OrganizationHandler-->>Client: Paginated JSON response
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
platform-api/internal/repository/llm.go (1)
479-491: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftRecord
updated_byfor template rename and enable operations.
RenameFamilyandSetEnabledupdate the entity andupdated_at, but neither receives an actor nor writesupdated_by. After these mutations, audit reads can report the previous user as the last editor. Thread the actor through the service/repository APIs and includeupdated_byin bothUPDATEstatements.Based on the PR objective to initialize and update audit identity for LLM providers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/repository/llm.go` around lines 479 - 491, Update RenameFamily and SetEnabled to accept an actor identity, thread it through their service and repository callers, and write it to updated_by alongside the existing updated_at mutation. Preserve the current update filters and enabled/display_name behavior while ensuring both operations record the acting user.
🧹 Nitpick comments (3)
platform-api/internal/handler/organization.go (1)
166-183: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winActor resolution unnecessarily gates the privileged list-all path.
performedByis only consumed in theelsebranch (ListOrganizationsForUser); theap:organization:managebranch doesn't use it at all. Resolving it unconditionally before the scope check means a transient identity-resolution failure blocks organization managers from listing all orgs, even though that path has no dependency on it.♻️ Proposed refactor
func (h *OrganizationHandler) ListOrganizations(w http.ResponseWriter, r *http.Request) error { limit, offset := parsePagination(r) - // resolveActorErr also creates the caller's user_idp_references row on first - // use, which is what makes the membership heal below FK-safe. - performedBy, err := resolveActorErr(r, h.identity, "list organizations") - if err != nil { - return err - } - var orgs []api.Organization var total int + var err error if middleware.HasEffectiveScope(r, h.authzMode, "ap:organization:manage") { orgs, total, err = h.orgService.ListOrganizations(limit, offset) } else { + // resolveActorErr also creates the caller's user_idp_references row on + // first use, which is what makes the membership heal below FK-safe. + performedBy, actorErr := resolveActorErr(r, h.identity, "list organizations") + if actorErr != nil { + return actorErr + } resolvedOrgUUID, _ := middleware.GetOrganizationFromRequest(r) orgs, total, err = h.orgService.ListOrganizationsForUser(performedBy, resolvedOrgUUID, limit, offset) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/handler/organization.go` around lines 166 - 183, Move resolveActorErr in ListOrganizations inside the non-privileged else branch, immediately before ListOrganizationsForUser, and declare performedBy there. Keep the privileged ap:organization:manage path independent of actor resolution while preserving the existing error handling for user-scoped listing.platform-api/internal/repository/llm_audit_test.go (1)
31-163: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding update-path tests for LLM provider and proxy
updated_by.The Create+Read path is well covered, but there's no test verifying that
UpdatechangesUpdatedByto the new actor while preservingCreatedBy— the API key repo hasTestAPIKeyRepo_UpdateAndRevoke_SetUpdatedByWithoutTouchingCreatedByfor this pattern. Adding equivalent tests for LLM providers and proxies would close the coverage gap.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/repository/llm_audit_test.go` around lines 31 - 163, Add update-path tests alongside TestLLMProviderRepo_CreateAndRead_SetsUpdatedBy and TestLLMProxyRepo_CreateAndRead_SetsUpdatedBy that update each record with a different actor, then verify UpdatedBy changes to that actor while CreatedBy remains unchanged. Follow the existing API-key test pattern and validate the persisted values through the repository read path.platform-api/internal/service/api.go (1)
304-320: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider batching project-handle resolution too.
Identity resolution is now batched for the list response, but
modelToRESTAPIUnresolvedstill does a per-items.projectRepo.GetProjectByUUID(...)call for every API in the page (N+1). Not a regression from this diff — the same per-item lookup existed via the oldmodelToRESTAPI— but since the batching pattern for identity was just introduced here, extending it to project handles would close the remaining N+1 gap on this list endpoint.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/service/api.go` around lines 304 - 320, Extend the list-response flow around modelToRESTAPIUnresolved to batch-resolve project handles instead of calling projectRepo.GetProjectByUUID per API. Collect the required project UUIDs while processing apiModels, resolve them in one repository operation, and apply the results to the corresponding responses while preserving existing missing-project and error behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@platform-api/internal/handler/organization_integration_test.go`:
- Around line 53-59: Configure SQLite foreign-key enforcement for the entire
sqlDB pool in the test setup, rather than relying on the single-connection
PRAGMA Exec call. Update the sql.Open configuration around sqlDB to use the
supported DSN foreign-keys parameter, or set the pool to one connection with
SetMaxOpenConns(1); remove the per-connection PRAGMA call if the chosen
configuration makes it unnecessary.
In `@platform-api/internal/repository/api.go`:
- Around line 62-63: Update the timestamp initialization in the API creation
flow to capture one UTC timestamp and assign it to both CreatedAt and UpdatedAt,
preserving the equal-on-creation invariant used by CreateAPIAssociation and
APIKeyRepo.Create.
In `@platform-api/internal/service/organization_test.go`:
- Around line 42-48: Update the SQLite setup around sql.Open so foreign-key
enforcement applies consistently to every connection: add _foreign_keys=on to
the DSN, or constrain the sqlDB pool to a single connection. Remove the
connection-specific PRAGMA setup if the DSN option is used, while preserving the
existing failure handling and FK assertions.
In `@portals/ai-workspace/src/contexts/ChoreoUserContext.tsx`:
- Around line 90-94: Update the organization-fetching flow around the single
res.json() call to follow the pagination metadata and request subsequent pages
until pagination.total organizations are collected, merging each response’s list
rather than retaining only the first page. Preserve the existing organization
mapping behavior, and add a regression test covering multiple paginated
responses.
---
Outside diff comments:
In `@platform-api/internal/repository/llm.go`:
- Around line 479-491: Update RenameFamily and SetEnabled to accept an actor
identity, thread it through their service and repository callers, and write it
to updated_by alongside the existing updated_at mutation. Preserve the current
update filters and enabled/display_name behavior while ensuring both operations
record the acting user.
---
Nitpick comments:
In `@platform-api/internal/handler/organization.go`:
- Around line 166-183: Move resolveActorErr in ListOrganizations inside the
non-privileged else branch, immediately before ListOrganizationsForUser, and
declare performedBy there. Keep the privileged ap:organization:manage path
independent of actor resolution while preserving the existing error handling for
user-scoped listing.
In `@platform-api/internal/repository/llm_audit_test.go`:
- Around line 31-163: Add update-path tests alongside
TestLLMProviderRepo_CreateAndRead_SetsUpdatedBy and
TestLLMProxyRepo_CreateAndRead_SetsUpdatedBy that update each record with a
different actor, then verify UpdatedBy changes to that actor while CreatedBy
remains unchanged. Follow the existing API-key test pattern and validate the
persisted values through the repository read path.
In `@platform-api/internal/service/api.go`:
- Around line 304-320: Extend the list-response flow around
modelToRESTAPIUnresolved to batch-resolve project handles instead of calling
projectRepo.GetProjectByUUID per API. Collect the required project UUIDs while
processing apiModels, resolve them in one repository operation, and apply the
results to the corresponding responses while preserving existing missing-project
and error behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fd1547a0-7aeb-4ce5-93d9-4d566d50f33e
📒 Files selected for processing (76)
platform-api/internal/database/schema.postgres.sqlplatform-api/internal/database/schema.sqlplatform-api/internal/database/schema.sqlite.sqlplatform-api/internal/handler/gateway_internal.goplatform-api/internal/handler/organization.goplatform-api/internal/handler/organization_integration_test.goplatform-api/internal/handler/secret.goplatform-api/internal/integration/audit_identity_it_test.goplatform-api/internal/integration/crud_test.goplatform-api/internal/middleware/auth.goplatform-api/internal/middleware/authorization.goplatform-api/internal/model/apikey.goplatform-api/internal/model/user_organization_mapping.goplatform-api/internal/repository/api.goplatform-api/internal/repository/api_test.goplatform-api/internal/repository/apikey.goplatform-api/internal/repository/apikey_test.goplatform-api/internal/repository/application.goplatform-api/internal/repository/audit.goplatform-api/internal/repository/custom_policy.goplatform-api/internal/repository/deployment.goplatform-api/internal/repository/gateway.goplatform-api/internal/repository/gateway_test.goplatform-api/internal/repository/interfaces.goplatform-api/internal/repository/llm.goplatform-api/internal/repository/llm_audit_test.goplatform-api/internal/repository/organization.goplatform-api/internal/repository/project.goplatform-api/internal/repository/secret.goplatform-api/internal/repository/secret_test.goplatform-api/internal/repository/subscription_plan_repository.goplatform-api/internal/repository/subscription_repository.goplatform-api/internal/repository/user_identity_mapping.goplatform-api/internal/repository/user_organization_mapping.goplatform-api/internal/repository/user_organization_mapping_test.goplatform-api/internal/server/server.goplatform-api/internal/service/api.goplatform-api/internal/service/apikey.goplatform-api/internal/service/apikey_user.goplatform-api/internal/service/application.goplatform-api/internal/service/artifact_import.goplatform-api/internal/service/deployment.goplatform-api/internal/service/deployment_test.goplatform-api/internal/service/gateway.goplatform-api/internal/service/identity_deleted_user_test.goplatform-api/internal/service/llm.goplatform-api/internal/service/llm_apikey.goplatform-api/internal/service/llm_deployment.goplatform-api/internal/service/llm_proxy_apikey.goplatform-api/internal/service/mcp.goplatform-api/internal/service/mcp_deployment.goplatform-api/internal/service/organization.goplatform-api/internal/service/organization_test.goplatform-api/internal/service/project.goplatform-api/plugins/eventgateway/repository/webbroker_api.goplatform-api/plugins/eventgateway/repository/websub_api.goplatform-api/plugins/eventgateway/service/webbroker_api.goplatform-api/plugins/eventgateway/service/webbroker_api_deployment.goplatform-api/plugins/eventgateway/service/websub_api.goplatform-api/plugins/eventgateway/service/websub_api_deployment.goportals/ai-workspace/src/apis/platformApis.tsportals/ai-workspace/src/contexts/ChoreoUserContext.tsxportals/ai-workspace/src/pages/appShell/appShellPages/externalServers/ExternalServersOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/providerTemplate/EditProviderTemplate.tsxportals/ai-workspace/src/pages/appShell/appShellPages/providerTemplate/ProviderTemplateOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/proxies/EditLLMProxy.tsxportals/ai-workspace/src/pages/appShell/appShellPages/proxies/LLMProxyOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/EditServiceProvider.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderConnectionTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderGuardrailsTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderModelsTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderRateLimitingTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderResourcesTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderSecurityTab.tsxportals/ai-workspace/src/utils/types.ts
💤 Files with no reviewable changes (2)
- platform-api/internal/service/artifact_import.go
- portals/ai-workspace/src/apis/platformApis.ts
Dependency Validation ResultsDependency name: github.com/gorilla/websocket Dependency name: golang.org/x/crypto |
|
The SQLite connection opens without ?_loc=UTC: Fix would be: sql.Open(DriverSQLite, cfg.Path+"?_loc=UTC") Let's do that in a separate PR. |
Fixed via c667b7b. cc: @renuka-fernando |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
platform-api/internal/service/project.go (1)
189-207: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid taking addresses of slice elements during an
appendloop.Although this is currently memory-safe because
projectsis pre-allocated with a capacity oflen(projectModels)and will not reallocate, taking the address of a slice element (&projects[len(projects)-1].CreatedBy) during anappendloop is a fragile pattern. If future code changes cause the slice to grow beyond its capacity and reallocate,createdByFieldswill silently hold pointers to the old, abandoned array. The identity resolver would then mutate the old array, and the resolved values would be lost in the returned slice.Consider populating the pointers in a separate loop after all elements have been appended to guarantee they point to the final array.
♻️ Proposed refactor
projects := make([]api.Project, 0, len(projectModels)) - createdByFields := make([]**string, 0, len(projectModels)) for _, projectModel := range projectModels { apiProj, err := s.modelToAPIUnresolved(projectModel, org.Handle) if err != nil { return nil, 0, err } if apiProj == nil { s.slogger.Warn("Failed to convert project model to API", "organizationId", organizationID) continue } // updatedBy is detail-only; omit it from list responses. apiProj.UpdatedBy = nil projects = append(projects, *apiProj) - createdByFields = append(createdByFields, &projects[len(projects)-1].CreatedBy) } + + createdByFields := make([]**string, 0, len(projects)) + for i := range projects { + createdByFields = append(createdByFields, &projects[i].CreatedBy) + } + if err := s.identity.ResolveIdentityFields(createdByFields); err != nil { return nil, 0, err }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@platform-api/internal/service/project.go` around lines 189 - 207, Refactor the project conversion loop in the relevant service method so it only appends converted projects; after all appends complete, populate createdByFields in a separate loop using addresses of elements in the finalized projects slice. Keep the existing model conversion, UpdatedBy omission, warning, and identity resolution behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@platform-api/internal/repository/deployment.go`:
- Around line 67-71: The deployment creation flow must normalize non-zero
deployment.CreatedAt values to UTC before persistence. In
platform-api/internal/repository/deployment.go lines 67-71, update the CreatedAt
handling to preserve caller-provided timestamps while converting them with
UTC(); in lines 343-350, ensure the corresponding deployment insert path
receives the normalized value, with no direct change needed if it is corrected
by the root-cause normalization.
---
Nitpick comments:
In `@platform-api/internal/service/project.go`:
- Around line 189-207: Refactor the project conversion loop in the relevant
service method so it only appends converted projects; after all appends
complete, populate createdByFields in a separate loop using addresses of
elements in the finalized projects slice. Keep the existing model conversion,
UpdatedBy omission, warning, and identity resolution behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f63684aa-ecf3-4183-bf7f-96fc15aad24d
📒 Files selected for processing (81)
platform-api/internal/database/connection.goplatform-api/internal/database/schema.postgres.sqlplatform-api/internal/database/schema.sqlplatform-api/internal/database/schema.sqlite.sqlplatform-api/internal/handler/api.goplatform-api/internal/handler/gateway_internal.goplatform-api/internal/handler/llm_deployment.goplatform-api/internal/handler/organization.goplatform-api/internal/handler/organization_integration_test.goplatform-api/internal/handler/secret.goplatform-api/internal/integration/audit_identity_it_test.goplatform-api/internal/integration/crud_test.goplatform-api/internal/middleware/auth.goplatform-api/internal/middleware/authorization.goplatform-api/internal/model/api.goplatform-api/internal/model/apikey.goplatform-api/internal/model/user_organization_mapping.goplatform-api/internal/repository/api.goplatform-api/internal/repository/api_test.goplatform-api/internal/repository/apikey.goplatform-api/internal/repository/apikey_test.goplatform-api/internal/repository/application.goplatform-api/internal/repository/audit.goplatform-api/internal/repository/custom_policy.goplatform-api/internal/repository/deployment.goplatform-api/internal/repository/gateway.goplatform-api/internal/repository/gateway_test.goplatform-api/internal/repository/interfaces.goplatform-api/internal/repository/llm.goplatform-api/internal/repository/llm_audit_test.goplatform-api/internal/repository/mcp.goplatform-api/internal/repository/organization.goplatform-api/internal/repository/project.goplatform-api/internal/repository/secret.goplatform-api/internal/repository/secret_test.goplatform-api/internal/repository/subscription_plan_repository.goplatform-api/internal/repository/subscription_repository.goplatform-api/internal/repository/user_identity_mapping.goplatform-api/internal/repository/user_organization_mapping.goplatform-api/internal/repository/user_organization_mapping_test.goplatform-api/internal/server/server.goplatform-api/internal/service/api.goplatform-api/internal/service/apikey.goplatform-api/internal/service/apikey_user.goplatform-api/internal/service/application.goplatform-api/internal/service/artifact_import.goplatform-api/internal/service/deployment.goplatform-api/internal/service/deployment_test.goplatform-api/internal/service/gateway.goplatform-api/internal/service/identity_deleted_user_test.goplatform-api/internal/service/llm.goplatform-api/internal/service/llm_apikey.goplatform-api/internal/service/llm_deployment.goplatform-api/internal/service/llm_proxy_apikey.goplatform-api/internal/service/mcp.goplatform-api/internal/service/mcp_deployment.goplatform-api/internal/service/organization.goplatform-api/internal/service/organization_test.goplatform-api/internal/service/project.goplatform-api/plugins/eventgateway/repository/webbroker_api.goplatform-api/plugins/eventgateway/repository/websub_api.goplatform-api/plugins/eventgateway/service/webbroker_api.goplatform-api/plugins/eventgateway/service/webbroker_api_deployment.goplatform-api/plugins/eventgateway/service/websub_api.goplatform-api/plugins/eventgateway/service/websub_api_deployment.goportals/ai-workspace/src/apis/platformApis.tsportals/ai-workspace/src/contexts/ChoreoUserContext.tsxportals/ai-workspace/src/pages/appShell/appShellPages/externalServers/ExternalServersOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/providerTemplate/EditProviderTemplate.tsxportals/ai-workspace/src/pages/appShell/appShellPages/providerTemplate/ProviderTemplateOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/proxies/EditLLMProxy.tsxportals/ai-workspace/src/pages/appShell/appShellPages/proxies/LLMProxyOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/EditServiceProvider.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderConnectionTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderGuardrailsTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderModelsTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverview.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderRateLimitingTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderResourcesTab.tsxportals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderSecurityTab.tsxportals/ai-workspace/src/utils/types.ts
💤 Files with no reviewable changes (1)
- portals/ai-workspace/src/apis/platformApis.ts
🚧 Files skipped from review as they are similar to previous changes (71)
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderSecurityTab.tsx
- platform-api/internal/handler/api.go
- platform-api/internal/repository/gateway_test.go
- portals/ai-workspace/src/pages/appShell/appShellPages/providerTemplate/ProviderTemplateOverview.tsx
- platform-api/internal/service/llm_proxy_apikey.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/EditServiceProvider.tsx
- platform-api/internal/model/api.go
- portals/ai-workspace/src/pages/appShell/appShellPages/proxies/EditLLMProxy.tsx
- platform-api/internal/repository/custom_policy.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderModelsTab.tsx
- portals/ai-workspace/src/pages/appShell/appShellPages/proxies/LLMProxyOverview.tsx
- platform-api/internal/service/llm_apikey.go
- portals/ai-workspace/src/pages/appShell/appShellPages/externalServers/ExternalServersOverview.tsx
- platform-api/internal/middleware/authorization.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderRateLimitingTab.tsx
- platform-api/internal/service/apikey.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderOverview.tsx
- platform-api/internal/handler/secret.go
- platform-api/internal/handler/gateway_internal.go
- platform-api/internal/repository/apikey_test.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderGuardrailsTab.tsx
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderResourcesTab.tsx
- platform-api/internal/repository/user_organization_mapping.go
- platform-api/internal/repository/secret_test.go
- platform-api/plugins/eventgateway/repository/websub_api.go
- platform-api/internal/model/user_organization_mapping.go
- platform-api/internal/handler/organization_integration_test.go
- platform-api/internal/middleware/auth.go
- platform-api/internal/model/apikey.go
- platform-api/internal/repository/subscription_plan_repository.go
- platform-api/internal/repository/subscription_repository.go
- platform-api/internal/handler/llm_deployment.go
- platform-api/internal/handler/organization.go
- platform-api/internal/service/mcp_deployment.go
- platform-api/internal/database/schema.sql
- platform-api/internal/service/apikey_user.go
- platform-api/internal/repository/organization.go
- platform-api/internal/repository/user_identity_mapping.go
- platform-api/internal/repository/mcp.go
- platform-api/internal/service/organization_test.go
- platform-api/plugins/eventgateway/service/webbroker_api_deployment.go
- platform-api/internal/repository/project.go
- platform-api/internal/service/mcp.go
- platform-api/internal/repository/llm_audit_test.go
- platform-api/internal/server/server.go
- platform-api/internal/service/deployment_test.go
- platform-api/plugins/eventgateway/service/websub_api_deployment.go
- platform-api/plugins/eventgateway/service/websub_api.go
- platform-api/internal/service/application.go
- platform-api/plugins/eventgateway/service/webbroker_api.go
- platform-api/internal/repository/secret.go
- platform-api/internal/repository/application.go
- portals/ai-workspace/src/pages/appShell/appShellPages/serviceProvider/ServiceProviderConnectionTab.tsx
- platform-api/internal/repository/api.go
- platform-api/internal/database/schema.postgres.sql
- platform-api/internal/service/artifact_import.go
- platform-api/internal/database/schema.sqlite.sql
- platform-api/internal/service/identity_deleted_user_test.go
- platform-api/internal/service/deployment.go
- platform-api/internal/service/gateway.go
- platform-api/internal/repository/gateway.go
- platform-api/internal/repository/user_organization_mapping_test.go
- platform-api/internal/service/llm_deployment.go
- platform-api/internal/service/organization.go
- platform-api/internal/repository/interfaces.go
- portals/ai-workspace/src/utils/types.ts
- platform-api/internal/integration/audit_identity_it_test.go
- platform-api/internal/service/llm.go
- platform-api/internal/repository/apikey.go
- platform-api/internal/service/api.go
- platform-api/internal/repository/llm.go
…loyment repo - In CreateWithLimitEnforcement, add else branch to call .UTC() on caller-provided CreatedAt values; the existing fix only covered the zero-value (default to now) path, leaving import-flow timestamps potentially in local time and breaking the eviction ORDER BY created_at ASC ordering on non-UTC hosts. - In SetCurrentWithDetails, call .UTC() on the dereferenced performedAt pointer so the concurrency token is always UTC-normalized, matching the updatedAt column already written as time.Now().UTC().
|
@CodeRabbit approve |
✅ Action performedComments resolved and changes approved. |
Purpose
Follow-up to #2370 / #2371, which moved all audit-identity columns (
created_by,updated_by,revoked_by,performed_by) to internal UUIDs resolved viauser_idp_references. Retesting those flows end-to-end (UI → REST API → DB) surfaced several gaps:updated_bywas never set on create forrest_apis,llm_providers,llm_proxies, andapi_keys— it stayedNULLuntil the first edit.api_keysupdate/revoke also touched onlyupdated_at, neverupdated_by.GET /organizationsignoreduser_organization_mappingsentirely and returned every organization to every caller, unfiltered.user_organization_mappingshas noON DELETE CASCADE, contradicting the schema (which always had it).ChoreoUserContextparsed the paginated{count, list, pagination}org envelope as a single object, soid/namecame outundefined; a second, uncalled copy of the same bug lived inplatformApis.getOrganization().deleted_userfallback resolution (for a removed/repointed user) had no test coverage.updated_at, deploymentperformed_at) were computed with local-server time in the service layer instead of UTC, diverging from the sibling UTC columns in the same rows on any non-UTC host.Goals
updated_by == created_byimmediately after creating a REST API, LLM provider, LLM proxy, or API key. Ensureupdated_byaccurately reflects the acting user on every subsequent update/revoke.GET /organizationsreturns only the organizations the caller is a member of; callers withap:organization:managekeep list-all behavior. A caller's membership in a pre-existing/seeded org is healed lazily on first list.ON DELETE CASCADEkept, SQLite FK enforcement hardened via?_foreign_keys=on).ai-workspacecorrectly parses the organizations list, drops dead code, and surfacescreatedBy/updatedByon relevant detail views without ever round-tripping them back in update requests.Approach
UpdatedByto the create paths for the four affected models;APIKeyRepository.Revokenow takes anupdatedByactor (signature change, all callers/mocks updated).OrganizationRepositorygainedListOrganizationsForUser/CountOrganizationsForUser. The handler branches onmiddleware.HasEffectiveScope(r, mode, "ap:organization:manage")to pick list-all vs. membership-filtered, with a best-effortAddMembershipheal (failures logged at Warn, not fatal) before listing.APIRepo.CreateAPIAssociation,GatewayRepo.UpdateGatewaynow computetime.Now().UTC()themselves, matching every other repoCreate), which fixes every caller (REST API, WebBroker, WebSub) in one place;performedAtin the REST/LLM/MCP/WebBroker/WebSub deployment flows switched totime.Now().UTC().ResolveIdentityFieldswired into every list endpoint (not just orgs) to avoid one identity lookup per row.getOrganization(), added "Created by" to the LLM Provider/Proxy, MCP External Server, and Provider Template overview pages, and strippedupdatedByfrom every update-payload builder now that the field exists on those types.User stories
GET /organizationsshows only orgs I belong to, not every org on the platform."deleted_user"audit trail instead of erroring or leaking an internal UUID.