Skip to content

Commit 8282e46

Browse files
authored
chore: add audit log tests (#4764)
* added test for stopping a workspace build * formatted sfriendly string; added tests * logging unmarshal error in auditLogDescription * prettier * got rid of extra workspace word * PR feedback * fixed mistake; wrote tests in penance * fix be
1 parent 01ec483 commit 8282e46

File tree

10 files changed

+104
-20
lines changed

10 files changed

+104
-20
lines changed

coderd/audit.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -219,24 +219,18 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
219219
}
220220
}
221221

222-
type WorkspaceResourceInfo struct {
223-
WorkspaceName string
224-
}
225-
226222
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
227223
str := fmt.Sprintf("{user} %s %s",
228224
codersdk.AuditAction(alog.Action).FriendlyString(),
229225
codersdk.ResourceType(alog.ResourceType).FriendlyString(),
230226
)
231227

232-
// Strings for build updates follow the below format:
233-
// "{user} started workspace build for workspace {target}"
234-
// where target is a workspace instead of the workspace build
228+
// Strings for workspace_builds follow the below format:
229+
// "{user} started workspace build for {target}"
230+
// where target is a workspace instead of the workspace build,
231+
// passed in on the FE via AuditLog.AdditionalFields rather than derived in request.go:35
235232
if alog.ResourceType == database.ResourceTypeWorkspaceBuild {
236-
workspaceBytes := []byte(alog.AdditionalFields)
237-
var workspaceResourceInfo WorkspaceResourceInfo
238-
_ = json.Unmarshal(workspaceBytes, &workspaceResourceInfo)
239-
str += " for workspace " + workspaceResourceInfo.WorkspaceName
233+
str += " for"
240234
}
241235

242236
// We don't display the name for git ssh keys. It's fairly long and doesn't

coderd/audit/request.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func ResourceTarget[T Auditable](tgt T) string {
4646
return typed.Name
4747
case database.WorkspaceBuild:
4848
// this isn't used
49-
return string(typed.BuildNumber)
49+
return ""
5050
case database.GitSSHKey:
5151
return typed.PublicKey
5252
case database.Group:

coderd/workspacebuilds_test.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -536,13 +536,20 @@ func TestWorkspaceBuildStatus(t *testing.T) {
536536
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
537537
defer cancel()
538538
auditor := audit.NewMock()
539+
numLogs := len(auditor.AuditLogs)
539540
client, closeDaemon, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor})
540541
user := coderdtest.CreateFirstUser(t, client)
542+
numLogs++ // add an audit log for user
541543
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
544+
numLogs++ // add an audit log for template version
545+
542546
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
543547
closeDaemon.Close()
544548
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
549+
numLogs++ // add an audit log for template creation
550+
545551
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
552+
numLogs++ // add an audit log for workspace creation
546553

547554
// initial returned state is "pending"
548555
require.EqualValues(t, codersdk.WorkspaceStatusPending, workspace.LatestBuild.Status)
@@ -561,11 +568,22 @@ func TestWorkspaceBuildStatus(t *testing.T) {
561568
require.NoError(t, err)
562569
require.EqualValues(t, codersdk.WorkspaceStatusStopped, workspace.LatestBuild.Status)
563570

571+
// assert an audit log has been created for workspace stopping
572+
numLogs++ // add an audit log for workspace_build stop
573+
require.Len(t, auditor.AuditLogs, numLogs)
574+
require.Equal(t, database.AuditActionStop, auditor.AuditLogs[numLogs-1].Action)
575+
564576
_ = closeDaemon.Close()
565577
// after successful cancel is "canceled"
566578
build = coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStart)
567579
err = client.CancelWorkspaceBuild(ctx, build.ID)
568580
require.NoError(t, err)
581+
582+
numLogs++ // add an audit log for workspace build start
583+
// assert an audit log has been created workspace starting
584+
require.Len(t, auditor.AuditLogs, numLogs)
585+
require.Equal(t, database.AuditActionStart, auditor.AuditLogs[numLogs-1].Action)
586+
569587
workspace, err = client.Workspace(ctx, workspace.ID)
570588
require.NoError(t, err)
571589
require.EqualValues(t, codersdk.WorkspaceStatusCanceled, workspace.LatestBuild.Status)
@@ -577,8 +595,9 @@ func TestWorkspaceBuildStatus(t *testing.T) {
577595
workspace, err = client.DeletedWorkspace(ctx, workspace.ID)
578596
require.NoError(t, err)
579597
require.EqualValues(t, codersdk.WorkspaceStatusDeleted, workspace.LatestBuild.Status)
598+
numLogs++ // add an audit log for workspace build deletion
580599

581600
// assert an audit log has been created for deletion
582-
require.Len(t, auditor.AuditLogs, 7)
583-
assert.Equal(t, database.AuditActionDelete, auditor.AuditLogs[6].Action)
601+
require.Len(t, auditor.AuditLogs, numLogs)
602+
require.Equal(t, database.AuditActionDelete, auditor.AuditLogs[numLogs-1].Action)
584603
}

docs/admin/audit-logs.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ We track **create, update and delete** events for the following resources:
1111
- Template
1212
- TemplateVersion
1313
- Workspace
14+
- Workspace start/stop
1415
- User
1516
- Group
1617

scripts/apitypings/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,8 @@ func (g *Generator) typescriptType(ty types.Type) (TypescriptType, error) {
688688
return TypescriptType{ValueType: "string", Optional: true}, nil
689689
case "github.com/google/uuid.UUID":
690690
return TypescriptType{ValueType: "string"}, nil
691+
case "encoding/json.RawMessage":
692+
return TypescriptType{ValueType: "Record<string, string>"}, nil
691693
}
692694

693695
// Then see if the type is defined elsewhere. If it is, we can just

site/src/api/typesGenerated.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ export interface AuditLog {
6565
readonly action: AuditAction
6666
readonly diff: AuditDiff
6767
readonly status_code: number
68-
// This is likely an enum in an external package ("encoding/json.RawMessage")
69-
readonly additional_fields: string
68+
readonly additional_fields: Record<string, string>
7069
readonly description: string
7170
readonly user?: User
7271
}

site/src/components/AuditLogRow/AuditLogRow.stories.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import TableContainer from "@material-ui/core/TableContainer"
55
import TableHead from "@material-ui/core/TableHead"
66
import TableRow from "@material-ui/core/TableRow"
77
import { ComponentMeta, Story } from "@storybook/react"
8-
import { MockAuditLog, MockAuditLog2 } from "testHelpers/entities"
8+
import {
9+
MockAuditLog,
10+
MockAuditLog2,
11+
MockAuditLogWithWorkspaceBuild,
12+
} from "testHelpers/entities"
913
import { AuditLogRow, AuditLogRowProps } from "./AuditLogRow"
1014

1115
export default {
@@ -38,3 +42,8 @@ WithDiff.args = {
3842
auditLog: MockAuditLog2,
3943
defaultIsDiffOpen: true,
4044
}
45+
46+
export const WithWorkspaceBuild = Template.bind({})
47+
WithWorkspaceBuild.args = {
48+
auditLog: MockAuditLogWithWorkspaceBuild,
49+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { readableActionMessage } from "./AuditLogRow"
2+
import {
3+
MockAuditLog,
4+
MockAuditLogWithWorkspaceBuild,
5+
} from "testHelpers/entities"
6+
7+
describe("readableActionMessage()", () => {
8+
it("renders the correct string for a workspaceBuild audit log", async () => {
9+
// When
10+
const friendlyString = readableActionMessage(MockAuditLogWithWorkspaceBuild)
11+
12+
// Then
13+
expect(friendlyString).toBe(
14+
"<strong>TestUser</strong> stopped workspace build for <strong>test2</strong>",
15+
)
16+
})
17+
it("renders the correct string for a workspaceBuild audit log with a duplicate word", async () => {
18+
// When
19+
const AuditLogWithRepeat = {
20+
...MockAuditLogWithWorkspaceBuild,
21+
additional_fields: {
22+
workspaceName: "workspace",
23+
},
24+
}
25+
const friendlyString = readableActionMessage(AuditLogWithRepeat)
26+
27+
// Then
28+
expect(friendlyString).toBe(
29+
"<strong>TestUser</strong> stopped workspace build for <strong>workspace</strong>",
30+
)
31+
})
32+
it("renders the correct string for a workspace audit log", async () => {
33+
// When
34+
const friendlyString = readableActionMessage(MockAuditLog)
35+
36+
// Then
37+
expect(friendlyString).toBe(
38+
"<strong>TestUser</strong> updated workspace <strong>bruno-dev</strong>",
39+
)
40+
})
41+
})

site/src/components/AuditLogRow/AuditLogRow.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,17 @@ import userAgentParser from "ua-parser-js"
1616
import { combineClasses } from "util/combineClasses"
1717
import { AuditLogDiff } from "./AuditLogDiff"
1818

19-
const readableActionMessage = (auditLog: AuditLog) => {
19+
export const readableActionMessage = (auditLog: AuditLog): string => {
20+
let target = auditLog.resource_target.trim()
21+
22+
// audit logs with a resource_type of workspace build use workspace name as a target
23+
if (auditLog.resource_type === "workspace_build") {
24+
target = auditLog.additional_fields.workspaceName.trim()
25+
}
26+
2027
return auditLog.description
2128
.replace("{user}", `<strong>${auditLog.user?.username.trim()}</strong>`)
22-
.replace("{target}", `<strong>${auditLog.resource_target.trim()}</strong>`)
29+
.replace("{target}", `<strong>${target}</strong>`)
2330
}
2431

2532
const httpStatusColor = (httpStatus: number): PaletteIndex => {

site/src/testHelpers/entities.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ export const MockAuditLog: TypesGen.AuditLog = {
916916
},
917917
},
918918
status_code: 200,
919-
additional_fields: "",
919+
additional_fields: {},
920920
description: "{user} updated workspace {target}",
921921
user: MockUser,
922922
}
@@ -949,6 +949,18 @@ export const MockAuditLog2: TypesGen.AuditLog = {
949949
},
950950
}
951951

952+
export const MockAuditLogWithWorkspaceBuild: TypesGen.AuditLog = {
953+
...MockAuditLog,
954+
id: "f90995bf-4a2b-4089-b597-e66e025e523e",
955+
request_id: "61555889-2875-475c-8494-f7693dd5d75b",
956+
action: "stop",
957+
resource_type: "workspace_build",
958+
description: "{user} stopped workspace build for {target}",
959+
additional_fields: {
960+
workspaceName: "test2",
961+
},
962+
}
963+
952964
export const MockWorkspaceQuota: TypesGen.WorkspaceQuota = {
953965
user_workspace_count: 0,
954966
user_workspace_limit: 100,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy