Skip to content

Commit 8282e46

Browse files
authored
chore: add audit log tests (coder#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)