From a577bfdb6908338b981a6b6f361d5c804204324d Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 9 Dec 2022 16:30:39 +0000 Subject: [PATCH 1/3] resolves #5269 --- coderd/audit.go | 10 ++++++---- coderd/workspaces.go | 17 +++++++++++++---- .../AuditLogRow/AuditLogDescription.test.tsx | 13 +++++++++++++ .../AuditLogRow/AuditLogDescription.tsx | 12 +++++++++++- site/src/i18n/en/auditLog.json | 5 +++-- site/src/testHelpers/entities.ts | 7 +++++++ 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/coderd/audit.go b/coderd/audit.go index c645e11e7b4e9..9ac95102ab684 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -161,8 +161,9 @@ func (api *API) convertAuditLogs(ctx context.Context, dblogs []database.GetAudit } type AdditionalFields struct { - WorkspaceName string - BuildNumber string + WorkspaceName string + BuildNumber string + WorkspaceOwner string } func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog { @@ -198,8 +199,9 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs if err != nil { api.Logger.Error(ctx, "unmarshal additional fields", slog.Error(err)) resourceInfo := map[string]string{ - "workspaceName": "unknown", - "buildNumber": "unknown", + "workspaceName": "unknown", + "buildNumber": "unknown", + "workspaceOwner": "unknown", } dblog.AdditionalFields, err = json.Marshal(resourceInfo) api.Logger.Error(ctx, "marshal additional fields", slog.Error(err)) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index f69a98b8f0741..e5562bef14b77 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -236,6 +236,14 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) // Create a new workspace for the currently authenticated user. func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) { + workspaceResourceInfo := map[string]string{ + "workspaceOwner": httpmw.UserParam(r).Username, + } + wriBytes, err := json.Marshal(workspaceResourceInfo) + if err != nil { + // log something + } + var ( ctx = r.Context() organization = httpmw.OrganizationParam(r) @@ -243,10 +251,11 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req auditor = api.Auditor.Load() user = httpmw.UserParam(r) aReq, commitAudit = audit.InitRequest[database.Workspace](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionCreate, + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionCreate, + AdditionalFields: wriBytes, }) ) defer commitAudit() diff --git a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx b/site/src/components/AuditLogRow/AuditLogDescription.test.tsx index cf8801678cca6..4f3f8162b8c69 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.test.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.test.tsx @@ -1,6 +1,7 @@ import { MockAuditLog, MockAuditLogWithWorkspaceBuild, + MockWorkspaceCreateAuditLogForDifferentOwner, } from "testHelpers/entities" import { AuditLogDescription } from "./AuditLogDescription" import { render } from "../../testHelpers/renderHelpers" @@ -46,4 +47,16 @@ describe("AuditLogDescription", () => { getByTextContent("TestUser stopped build for workspace workspace"), ).toBeDefined() }) + it("renders the correct string for a workspace created for a different owner", async () => { + render( + , + ) + expect( + getByTextContent( + `TestUser created workspace bruno-dev on behalf of ${MockWorkspaceCreateAuditLogForDifferentOwner.additional_fields.workspaceOwner}`, + ), + ).toBeDefined() + }) }) diff --git a/site/src/components/AuditLogRow/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription.tsx index c559d3114c024..e19088e9e752e 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.tsx @@ -45,7 +45,17 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ )} {auditLog.is_deleted && ( - <> {t("auditLog:table.logRow.deletedLabel")} + <>{t("auditLog:table.logRow.deletedLabel")} + + )} + {/* logs for workspaces created on behalf of other users indicate ownership in the description */} + {auditLog.additional_fields.workspaceOwner && ( + + <> + {t("auditLog:table.logRow.onBehalfOf", { + owner: auditLog.additional_fields.workspaceOwner, + })} + )} diff --git a/site/src/i18n/en/auditLog.json b/site/src/i18n/en/auditLog.json index b68d33ab5fcb1..b9b8068d20aaa 100644 --- a/site/src/i18n/en/auditLog.json +++ b/site/src/i18n/en/auditLog.json @@ -8,11 +8,12 @@ "emptyPage": "No audit logs available on this page", "noLogs": "No audit logs available", "logRow": { - "deletedLabel": "(deleted)", + "deletedLabel": " (deleted)", "ip": "IP: ", "os": "OS: ", "browser": "Browser: ", - "notAvailable": "Not available" + "notAvailable": "Not available", + "onBehalfOf": " on behalf of {{owner}}" } } } diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 725383b3f3c98..7d42881754ba7 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1020,6 +1020,13 @@ export const MockAuditLog2: TypesGen.AuditLog = { }, } +export const MockWorkspaceCreateAuditLogForDifferentOwner = { + ...MockAuditLog, + additional_fields: { + workspaceOwner: "Member" + } +} + export const MockAuditLogWithWorkspaceBuild: TypesGen.AuditLog = { ...MockAuditLog, id: "f90995bf-4a2b-4089-b597-e66e025e523e", From d51c8772529e82c3922ca8ede7b21fa3d3928442 Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 9 Dec 2022 16:36:13 +0000 Subject: [PATCH 2/3] clean up --- coderd/workspaces.go | 36 +++++++++---------- .../AuditLogRow/AuditLogDescription.tsx | 19 +++++----- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/coderd/workspaces.go b/coderd/workspaces.go index e5562bef14b77..146d3bf2374a3 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -236,28 +236,28 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request) // Create a new workspace for the currently authenticated user. func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Request) { - workspaceResourceInfo := map[string]string{ - "workspaceOwner": httpmw.UserParam(r).Username, - } + var ( + ctx = r.Context() + organization = httpmw.OrganizationParam(r) + apiKey = httpmw.APIKey(r) + auditor = api.Auditor.Load() + user = httpmw.UserParam(r) + workspaceResourceInfo = map[string]string{ + "workspaceOwner": user.Username, + } + ) wriBytes, err := json.Marshal(workspaceResourceInfo) if err != nil { - // log something + api.Logger.Warn(ctx, "marshal workspace owner name") } - var ( - ctx = r.Context() - organization = httpmw.OrganizationParam(r) - apiKey = httpmw.APIKey(r) - auditor = api.Auditor.Load() - user = httpmw.UserParam(r) - aReq, commitAudit = audit.InitRequest[database.Workspace](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionCreate, - AdditionalFields: wriBytes, - }) - ) + aReq, commitAudit := audit.InitRequest[database.Workspace](rw, &audit.RequestParams{ + Audit: *auditor, + Log: api.Logger, + Request: r, + Action: database.AuditActionCreate, + AdditionalFields: wriBytes, + }) defer commitAudit() if !api.Authorize(r, rbac.ActionCreate, diff --git a/site/src/components/AuditLogRow/AuditLogDescription.tsx b/site/src/components/AuditLogRow/AuditLogDescription.tsx index e19088e9e752e..38382f96ff9a2 100644 --- a/site/src/components/AuditLogRow/AuditLogDescription.tsx +++ b/site/src/components/AuditLogRow/AuditLogDescription.tsx @@ -49,15 +49,16 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({ )} {/* logs for workspaces created on behalf of other users indicate ownership in the description */} - {auditLog.additional_fields.workspaceOwner && ( - - <> - {t("auditLog:table.logRow.onBehalfOf", { - owner: auditLog.additional_fields.workspaceOwner, - })} - - - )} + {auditLog.additional_fields.workspaceOwner && + auditLog.additional_fields.workspaceOwner !== "unknown" && ( + + <> + {t("auditLog:table.logRow.onBehalfOf", { + owner: auditLog.additional_fields.workspaceOwner, + })} + + + )} ) } From 3018fb9abeac7e88b8e2786e6bb2d92cc732d7bd Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 9 Dec 2022 16:43:36 +0000 Subject: [PATCH 3/3] fixed audit link --- coderd/audit.go | 6 +++++- site/src/testHelpers/entities.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/coderd/audit.go b/coderd/audit.go index 9ac95102ab684..b30dd37c28683 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -333,8 +333,12 @@ func auditLogResourceLink(alog database.GetAuditLogsOffsetRow, additionalFields return fmt.Sprintf("/users?filter=%s", alog.ResourceTarget) case database.ResourceTypeWorkspace: + workspaceOwner := alog.UserUsername.String + if len(additionalFields.WorkspaceOwner) != 0 && additionalFields.WorkspaceOwner != "unknown" { + workspaceOwner = additionalFields.WorkspaceOwner + } return fmt.Sprintf("/@%s/%s", - alog.UserUsername.String, alog.ResourceTarget) + workspaceOwner, alog.ResourceTarget) case database.ResourceTypeWorkspaceBuild: if len(additionalFields.WorkspaceName) == 0 || len(additionalFields.BuildNumber) == 0 { return "" diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 7d42881754ba7..c7425ebeca8c6 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -1023,8 +1023,8 @@ export const MockAuditLog2: TypesGen.AuditLog = { export const MockWorkspaceCreateAuditLogForDifferentOwner = { ...MockAuditLog, additional_fields: { - workspaceOwner: "Member" - } + workspaceOwner: "Member", + }, } export const MockAuditLogWithWorkspaceBuild: TypesGen.AuditLog = {