diff --git a/coderd/audit.go b/coderd/audit.go index 54be5e3045b52..e177494d6d934 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -1,6 +1,7 @@ package coderd import ( + "database/sql" "encoding/json" "fmt" "net" @@ -129,7 +130,7 @@ func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) { Time: params.Time, UserID: user.ID, Ip: ipNet, - UserAgent: r.UserAgent(), + UserAgent: sql.NullString{String: r.UserAgent(), Valid: true}, ResourceType: database.ResourceType(params.ResourceType), ResourceID: params.ResourceID, ResourceTarget: user.Username, @@ -163,6 +164,7 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog { _ = json.Unmarshal(dblog.Diff, &diff) var user *codersdk.User + if dblog.UserUsername.Valid { user = &codersdk.User{ ID: dblog.UserID, @@ -186,7 +188,7 @@ func convertAuditLog(dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog { Time: dblog.Time, OrganizationID: dblog.OrganizationID, IP: ip, - UserAgent: dblog.UserAgent, + UserAgent: dblog.UserAgent.String, ResourceType: codersdk.ResourceType(dblog.ResourceType), ResourceID: dblog.ResourceID, ResourceTarget: dblog.ResourceTarget, diff --git a/coderd/audit/request.go b/coderd/audit/request.go index ef15611fa8de2..55c4599cdc61a 100644 --- a/coderd/audit/request.go +++ b/coderd/audit/request.go @@ -2,6 +2,7 @@ package audit import ( "context" + "database/sql" "encoding/json" "fmt" "net" @@ -32,6 +33,20 @@ type Request[T Auditable] struct { New T } +type BuildAuditParams[T Auditable] struct { + Audit Auditor + Log slog.Logger + + UserID uuid.UUID + JobID uuid.UUID + Status int + Action database.AuditAction + AdditionalFields json.RawMessage + + New T + Old T +} + func ResourceTarget[T Auditable](tgt T) string { switch typed := any(tgt).(type) { case database.Organization: @@ -147,7 +162,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request Time: database.Now(), UserID: httpmw.APIKey(p.Request).UserID, Ip: ip, - UserAgent: p.Request.UserAgent(), + UserAgent: sql.NullString{String: p.Request.UserAgent(), Valid: true}, ResourceType: either(req.Old, req.New, ResourceType[T]), ResourceID: either(req.Old, req.New, ResourceID[T]), ResourceTarget: either(req.Old, req.New, ResourceTarget[T]), @@ -164,6 +179,40 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request } } +// BuildAudit creates an audit log for a workspace build. +// The audit log is committed upon invocation. +func BuildAudit[T Auditable](ctx context.Context, p *BuildAuditParams[T]) { + // As the audit request has not been initiated directly by a user, we omit + // certain user details. + ip := parseIP("") + // We do not show diffs for build audit logs + var diffRaw = []byte("{}") + + if p.AdditionalFields == nil { + p.AdditionalFields = json.RawMessage("{}") + } + + err := p.Audit.Export(ctx, database.AuditLog{ + ID: uuid.New(), + Time: database.Now(), + UserID: p.UserID, + Ip: ip, + UserAgent: sql.NullString{}, + ResourceType: either(p.Old, p.New, ResourceType[T]), + ResourceID: either(p.Old, p.New, ResourceID[T]), + ResourceTarget: either(p.Old, p.New, ResourceTarget[T]), + Action: p.Action, + Diff: diffRaw, + StatusCode: int32(p.Status), + RequestID: p.JobID, + AdditionalFields: p.AdditionalFields, + }) + if err != nil { + p.Log.Error(ctx, "export audit log", slog.Error(err)) + return + } +} + func either[T Auditable, R any](old, new T, fn func(T) R) R { if ResourceID(new) != uuid.Nil { return fn(new) diff --git a/coderd/coderd.go b/coderd/coderd.go index 6cc154757364f..c9954a9f3412c 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -681,6 +681,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti Telemetry: api.Telemetry, Tags: tags, QuotaCommitter: &api.QuotaCommitter, + Auditor: &api.Auditor, AcquireJobDebounce: debounce, Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)), }) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index d351cd1b2637b..be9c0c7b9c19f 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -143,8 +143,8 @@ CREATE TABLE audit_logs ( "time" timestamp with time zone NOT NULL, user_id uuid NOT NULL, organization_id uuid NOT NULL, - ip inet NOT NULL, - user_agent character varying(256) NOT NULL, + ip inet, + user_agent character varying(256), resource_type resource_type NOT NULL, resource_id uuid NOT NULL, resource_target text NOT NULL, diff --git a/coderd/database/migrations/000082_add_audit_log_nullable_fields.down.sql b/coderd/database/migrations/000082_add_audit_log_nullable_fields.down.sql new file mode 100644 index 0000000000000..ed62e38ab8bad --- /dev/null +++ b/coderd/database/migrations/000082_add_audit_log_nullable_fields.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE audit_logs ALTER COLUMN ip SET NOT NULL; +ALTER TABLE audit_logs ALTER COLUMN user_agent SET NOT NULL; diff --git a/coderd/database/migrations/000082_add_audit_log_nullable_fields.up.sql b/coderd/database/migrations/000082_add_audit_log_nullable_fields.up.sql new file mode 100644 index 0000000000000..21dee2a224ee4 --- /dev/null +++ b/coderd/database/migrations/000082_add_audit_log_nullable_fields.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE audit_logs ALTER COLUMN ip DROP NOT NULL; +ALTER TABLE audit_logs ALTER COLUMN user_agent DROP NOT NULL; diff --git a/coderd/database/models.go b/coderd/database/models.go index dc5b802668129..46e96a83e71b5 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -411,7 +411,7 @@ type AuditLog struct { UserID uuid.UUID `db:"user_id" json:"user_id"` OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` Ip pqtype.Inet `db:"ip" json:"ip"` - UserAgent string `db:"user_agent" json:"user_agent"` + UserAgent sql.NullString `db:"user_agent" json:"user_agent"` ResourceType ResourceType `db:"resource_type" json:"resource_type"` ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` ResourceTarget string `db:"resource_target" json:"resource_target"` diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 77eb2d519b649..d5bdd266b3bec 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -455,7 +455,7 @@ type GetAuditLogsOffsetRow struct { UserID uuid.UUID `db:"user_id" json:"user_id"` OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` Ip pqtype.Inet `db:"ip" json:"ip"` - UserAgent string `db:"user_agent" json:"user_agent"` + UserAgent sql.NullString `db:"user_agent" json:"user_agent"` ResourceType ResourceType `db:"resource_type" json:"resource_type"` ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` ResourceTarget string `db:"resource_target" json:"resource_target"` @@ -562,7 +562,7 @@ type InsertAuditLogParams struct { UserID uuid.UUID `db:"user_id" json:"user_id"` OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"` Ip pqtype.Inet `db:"ip" json:"ip"` - UserAgent string `db:"user_agent" json:"user_agent"` + UserAgent sql.NullString `db:"user_agent" json:"user_agent"` ResourceType ResourceType `db:"resource_type" json:"resource_type"` ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` ResourceTarget string `db:"resource_target" json:"resource_target"` diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 240d042ee5ee0..dbb6a2c131ef1 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "net/url" "reflect" "sync" @@ -19,6 +20,7 @@ import ( "cdr.dev/slog" + "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/parameter" "github.com/coder/coder/coderd/telemetry" @@ -44,6 +46,7 @@ type Server struct { Pubsub database.Pubsub Telemetry telemetry.Reporter QuotaCommitter *atomic.Pointer[proto.QuotaCommitter] + Auditor *atomic.Pointer[audit.Auditor] AcquireJobDebounce time.Duration } @@ -520,6 +523,43 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p case *proto.FailedJob_TemplateImport_: } + // if failed job is a workspace build, audit the outcome + if job.Type == database.ProvisionerJobTypeWorkspaceBuild { + auditor := server.Auditor.Load() + build, getBuildErr := server.Database.GetWorkspaceBuildByJobID(ctx, job.ID) + if getBuildErr != nil { + server.Logger.Error(ctx, "failed to create audit log - get build err", slog.Error(err)) + } else { + auditAction := auditActionFromTransition(build.Transition) + workspace, getWorkspaceErr := server.Database.GetWorkspaceByID(ctx, build.WorkspaceID) + if getWorkspaceErr != nil { + server.Logger.Error(ctx, "failed to create audit log - get workspace err", slog.Error(err)) + } else { + // We pass the workspace name to the Auditor so that it + // can form a friendly string for the user. + workspaceResourceInfo := map[string]string{ + "workspaceName": workspace.Name, + } + + wriBytes, err := json.Marshal(workspaceResourceInfo) + if err != nil { + server.Logger.Error(ctx, "could not marshal workspace name", slog.Error(err)) + } + + audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{ + Audit: *auditor, + Log: server.Logger, + UserID: job.InitiatorID, + JobID: job.ID, + Action: auditAction, + New: build, + Status: http.StatusInternalServerError, + AdditionalFields: wriBytes, + }) + } + } + } + data, err := json.Marshal(ProvisionerJobLogsNotifyMessage{EndOfLogs: true}) if err != nil { return nil, xerrors.Errorf("marshal job log: %w", err) @@ -598,11 +638,14 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete return nil, xerrors.Errorf("get workspace build: %w", err) } + var workspace database.Workspace + var getWorkspaceError error + err = server.Database.InTx(func(db database.Store) error { now := database.Now() var workspaceDeadline time.Time - workspace, err := db.GetWorkspaceByID(ctx, workspaceBuild.WorkspaceID) - if err == nil { + workspace, getWorkspaceError = db.GetWorkspaceByID(ctx, workspaceBuild.WorkspaceID) + if getWorkspaceError == nil { if workspace.Ttl.Valid { workspaceDeadline = now.Add(time.Duration(workspace.Ttl.Int64)) } @@ -658,6 +701,34 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete return nil, xerrors.Errorf("complete job: %w", err) } + // audit the outcome of the workspace build + if getWorkspaceError == nil { + auditor := server.Auditor.Load() + auditAction := auditActionFromTransition(workspaceBuild.Transition) + + // We pass the workspace name to the Auditor so that it + // can form a friendly string for the user. + workspaceResourceInfo := map[string]string{ + "workspaceName": workspace.Name, + } + + wriBytes, err := json.Marshal(workspaceResourceInfo) + if err != nil { + server.Logger.Error(ctx, "marshal resource info", slog.Error(err)) + } + + audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{ + Audit: *auditor, + Log: server.Logger, + UserID: job.InitiatorID, + JobID: job.ID, + Action: auditAction, + New: workspaceBuild, + Status: http.StatusOK, + AdditionalFields: wriBytes, + }) + } + err = server.Pubsub.Publish(codersdk.WorkspaceNotifyChannel(workspaceBuild.WorkspaceID), []byte{}) if err != nil { return nil, xerrors.Errorf("update workspace: %w", err) @@ -969,6 +1040,19 @@ func convertWorkspaceTransition(transition database.WorkspaceTransition) (sdkpro } } +func auditActionFromTransition(transition database.WorkspaceTransition) database.AuditAction { + switch transition { + case database.WorkspaceTransitionStart: + return database.AuditActionStart + case database.WorkspaceTransitionStop: + return database.AuditActionStop + case database.WorkspaceTransitionDelete: + return database.AuditActionDelete + default: + return database.AuditActionWrite + } +} + // WorkspaceProvisionJob is the payload for the "workspace_provision" job type. type WorkspaceProvisionJob struct { WorkspaceBuildID uuid.UUID `json:"workspace_build_id"` diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index c63775a4e1839..cd1c65de16ace 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "net/url" + "sync/atomic" "testing" "time" @@ -12,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/provisionerdserver" @@ -21,6 +23,13 @@ import ( sdkproto "github.com/coder/coder/provisionersdk/proto" ) +func mockAuditor() *atomic.Pointer[audit.Auditor] { + ptr := &atomic.Pointer[audit.Auditor]{} + mock := audit.Auditor(audit.NewMock()) + ptr.Store(&mock) + return ptr +} + func TestAcquireJob(t *testing.T) { t.Parallel() t.Run("Debounce", func(t *testing.T) { @@ -36,6 +45,7 @@ func TestAcquireJob(t *testing.T) { Pubsub: pubsub, Telemetry: telemetry.NewNoop(), AcquireJobDebounce: time.Hour, + Auditor: mockAuditor(), } job, err := srv.AcquireJob(context.Background(), nil) require.NoError(t, err) @@ -799,5 +809,6 @@ func setup(t *testing.T) *provisionerdserver.Server { Database: db, Pubsub: pubsub, Telemetry: telemetry.NewNoop(), + Auditor: mockAuditor(), } } diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 48466dade9017..c00432f8ce7b3 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -15,8 +15,6 @@ import ( "golang.org/x/exp/slices" "golang.org/x/xerrors" - "cdr.dev/slog" - "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" @@ -280,58 +278,8 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { return } - auditor := api.Auditor.Load() - - // if user deletes a workspace, audit the workspace - if action == rbac.ActionDelete { - aReq, commitAudit := audit.InitRequest[database.Workspace](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: database.AuditActionDelete, - }) - - defer commitAudit() - aReq.Old = workspace - } - - latestBuild, latestBuildErr := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID) - - // if a user starts/stops a workspace, audit the workspace build - if action == rbac.ActionUpdate { - var auditAction database.AuditAction - if createBuild.Transition == codersdk.WorkspaceTransitionStart { - auditAction = database.AuditActionStart - } else if createBuild.Transition == codersdk.WorkspaceTransitionStop { - auditAction = database.AuditActionStop - } else { - auditAction = database.AuditActionWrite - } - - // We pass the workspace name to the Auditor so that it - // can form a friendly string for the user. - workspaceResourceInfo := map[string]string{ - "workspaceName": workspace.Name, - } - - wriBytes, err := json.Marshal(workspaceResourceInfo) - if err != nil { - api.Logger.Error(ctx, "could not marshal workspace name", slog.Error(err)) - } - - aReq, commitAudit := audit.InitRequest[database.WorkspaceBuild](rw, &audit.RequestParams{ - Audit: *auditor, - Log: api.Logger, - Request: r, - Action: auditAction, - AdditionalFields: wriBytes, - }) - - defer commitAudit() - aReq.Old = latestBuild - } - if createBuild.TemplateVersionID == uuid.Nil { + latestBuild, latestBuildErr := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID) if latestBuildErr != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ Message: "Internal error fetching the latest workspace build.", diff --git a/coderd/workspacebuilds_test.go b/coderd/workspacebuilds_test.go index 43fbe51f0f5a9..77183886ef2c2 100644 --- a/coderd/workspacebuilds_test.go +++ b/coderd/workspacebuilds_test.go @@ -576,9 +576,9 @@ func TestWorkspaceBuildStatus(t *testing.T) { numLogs := len(auditor.AuditLogs) client, closeDaemon, api := coderdtest.NewWithAPI(t, &coderdtest.Options{IncludeProvisionerDaemon: true, Auditor: auditor}) user := coderdtest.CreateFirstUser(t, client) - numLogs++ // add an audit log for user version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) - numLogs++ // add an audit log for template version + numLogs++ // add an audit log for template version creation + numLogs++ // add an audit log for template version update coderdtest.AwaitTemplateVersionJob(t, client, version.ID) closeDaemon.Close() @@ -598,6 +598,8 @@ func TestWorkspaceBuildStatus(t *testing.T) { require.NoError(t, err) require.EqualValues(t, codersdk.WorkspaceStatusRunning, workspace.LatestBuild.Status) + numLogs++ // add an audit log for workspace_build starting + // after successful stop is "stopped" build := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop) _ = coderdtest.AwaitWorkspaceBuildJob(t, client, build.ID) @@ -616,11 +618,6 @@ func TestWorkspaceBuildStatus(t *testing.T) { err = client.CancelWorkspaceBuild(ctx, build.ID) require.NoError(t, err) - numLogs++ // add an audit log for workspace build start - // assert an audit log has been created workspace starting - require.Len(t, auditor.AuditLogs, numLogs) - require.Equal(t, database.AuditActionStart, auditor.AuditLogs[numLogs-1].Action) - workspace, err = client.Workspace(ctx, workspace.ID) require.NoError(t, err) require.EqualValues(t, codersdk.WorkspaceStatusCanceled, workspace.LatestBuild.Status) @@ -632,9 +629,4 @@ func TestWorkspaceBuildStatus(t *testing.T) { workspace, err = client.DeletedWorkspace(ctx, workspace.ID) require.NoError(t, err) require.EqualValues(t, codersdk.WorkspaceStatusDeleted, workspace.LatestBuild.Status) - numLogs++ // add an audit log for workspace build deletion - - // assert an audit log has been created for deletion - require.Len(t, auditor.AuditLogs, numLogs) - require.Equal(t, database.AuditActionDelete, auditor.AuditLogs[numLogs-1].Action) } diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 1711de1ba3413..89a126d0233ec 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -1101,6 +1101,9 @@ func TestWorkspaceUpdateAutostart(t *testing.T) { }) ) + // await job to ensure audit logs for workspace_build start are created + _ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) + // ensure test invariant: new workspaces have no autostart schedule. require.Empty(t, workspace.AutostartSchedule, "expected newly-minted workspace to have no autostart schedule") @@ -1136,8 +1139,8 @@ func TestWorkspaceUpdateAutostart(t *testing.T) { interval := next.Sub(testCase.at) require.Equal(t, testCase.expectedInterval, interval, "unexpected interval") - require.Len(t, auditor.AuditLogs, 5) - assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[4].Action) + require.Len(t, auditor.AuditLogs, 6) + assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[5].Action) }) } @@ -1245,8 +1248,8 @@ func TestWorkspaceUpdateTTL(t *testing.T) { require.Equal(t, testCase.ttlMillis, updated.TTLMillis, "expected autostop ttl to equal requested") - require.Len(t, auditor.AuditLogs, 5) - assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[4].Action) + require.Len(t, auditor.AuditLogs, 6) + assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs[5].Action) }) } diff --git a/enterprise/audit/audittest/rand.go b/enterprise/audit/audittest/rand.go index ea44c8f8e64dd..5bf443142ae19 100644 --- a/enterprise/audit/audittest/rand.go +++ b/enterprise/audit/audittest/rand.go @@ -1,6 +1,7 @@ package audittest import ( + "database/sql" "net" "net/http" "time" @@ -22,7 +23,7 @@ func RandomLog() database.AuditLog { IPNet: *inet, Valid: true, }, - UserAgent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36", + UserAgent: sql.NullString{String: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36", Valid: true}, ResourceType: database.ResourceTypeOrganization, ResourceID: uuid.New(), ResourceTarget: "colin's organization", diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index e8399b38433d2..342a80f2c7932 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -112,14 +112,13 @@ var AuditableResources = auditMap(map[any]map[string]Action{ "avatar_url": ActionTrack, "quota_allowance": ActionTrack, }, - // We don't show any diff for the WorkspaceBuild resource, - // save for the template_version_id + // We don't show any diff for the WorkspaceBuild resource &database.WorkspaceBuild{}: { "id": ActionIgnore, "created_at": ActionIgnore, "updated_at": ActionIgnore, "workspace_id": ActionIgnore, - "template_version_id": ActionTrack, + "template_version_id": ActionIgnore, "build_number": ActionIgnore, "transition": ActionIgnore, "initiator_id": ActionIgnore, diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index e9f357f970e30..95aab294d5524 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -206,6 +206,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request) Pubsub: api.Pubsub, Provisioners: daemon.Provisioners, Telemetry: api.Telemetry, + Auditor: &api.AGPL.Auditor, Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)), Tags: rawTags, }) diff --git a/enterprise/trialer/trialer.go b/enterprise/trialer/trialer.go index 1ee49343d823b..48b67a9b9576c 100644 --- a/enterprise/trialer/trialer.go +++ b/enterprise/trialer/trialer.go @@ -11,9 +11,10 @@ import ( "golang.org/x/xerrors" + "github.com/google/uuid" + "github.com/coder/coder/coderd/database" "github.com/coder/coder/enterprise/coderd/license" - "github.com/google/uuid" ) type request struct { diff --git a/site/src/components/AuditLogRow/AuditLogRow.tsx b/site/src/components/AuditLogRow/AuditLogRow.tsx index c00d1564d5c95..aa51fd6f01734 100644 --- a/site/src/components/AuditLogRow/AuditLogRow.tsx +++ b/site/src/components/AuditLogRow/AuditLogRow.tsx @@ -20,7 +20,10 @@ export const readableActionMessage = (auditLog: AuditLog): string => { let target = auditLog.resource_target.trim() // audit logs with a resource_type of workspace build use workspace name as a target - if (auditLog.resource_type === "workspace_build") { + if ( + auditLog.resource_type === "workspace_build" && + auditLog.additional_fields.workspaceName + ) { target = auditLog.additional_fields.workspaceName.trim() }