Skip to content

Commit 088f219

Browse files
authored
feat: add audit logs for dormancy events (#15298)
1 parent 1456561 commit 088f219

File tree

26 files changed

+340
-105
lines changed

26 files changed

+340
-105
lines changed

cli/server_createadminuser.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
197197
UpdatedAt: dbtime.Now(),
198198
RBACRoles: []string{rbac.RoleOwner().String()},
199199
LoginType: database.LoginTypePassword,
200+
Status: "",
200201
})
201202
if err != nil {
202203
return xerrors.Errorf("insert user: %w", err)

coderd/apidoc/docs.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/audit/fields.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package audit
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"cdr.dev/slog"
8+
)
9+
10+
type BackgroundSubsystem string
11+
12+
const (
13+
BackgroundSubsystemDormancy BackgroundSubsystem = "dormancy"
14+
)
15+
16+
func BackgroundTaskFields(subsystem BackgroundSubsystem) map[string]string {
17+
return map[string]string{
18+
"automatic_actor": "coder",
19+
"automatic_subsystem": string(subsystem),
20+
}
21+
}
22+
23+
func BackgroundTaskFieldsBytes(ctx context.Context, logger slog.Logger, subsystem BackgroundSubsystem) []byte {
24+
af := BackgroundTaskFields(subsystem)
25+
26+
wriBytes, err := json.Marshal(af)
27+
if err != nil {
28+
logger.Error(ctx, "marshal additional fields for dormancy audit", slog.Error(err))
29+
return []byte("{}")
30+
}
31+
32+
return wriBytes
33+
}

coderd/audit/request.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ type BackgroundAuditParams[T Auditable] struct {
6262
Audit Auditor
6363
Log slog.Logger
6464

65-
UserID uuid.UUID
66-
RequestID uuid.UUID
67-
Status int
68-
Action database.AuditAction
69-
OrganizationID uuid.UUID
70-
IP string
65+
UserID uuid.UUID
66+
RequestID uuid.UUID
67+
Status int
68+
Action database.AuditAction
69+
OrganizationID uuid.UUID
70+
IP string
71+
// todo: this should automatically marshal an interface{} instead of accepting a raw message.
7172
AdditionalFields json.RawMessage
7273

7374
New T

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,7 @@ func New(options *Options) *API {
702702

703703
apiKeyMiddleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
704704
DB: options.Database,
705+
ActivateDormantUser: ActivateDormantUser(options.Logger, &api.Auditor, options.Database),
705706
OAuth2Configs: oauthConfigs,
706707
RedirectToLogin: false,
707708
DisableSessionExpiryRefresh: options.DeploymentValues.Sessions.DisableExpiryRefresh.Value(),

coderd/coderdtest/coderdtest.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,9 @@ func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationI
719719
Name: RandomName(t),
720720
Password: "SomeSecurePassword!",
721721
OrganizationIDs: organizationIDs,
722+
// Always create users as active in tests to ignore an extra audit log
723+
// when logging in.
724+
UserStatus: ptr.Ref(codersdk.UserStatusActive),
722725
}
723726
for _, m := range mutators {
724727
m(&req)

coderd/database/dbgen/dbgen.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ func User(t testing.TB, db database.Store, orig database.User) database.User {
342342
UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()),
343343
RBACRoles: takeFirstSlice(orig.RBACRoles, []string{}),
344344
LoginType: takeFirst(orig.LoginType, database.LoginTypePassword),
345+
Status: string(takeFirst(orig.Status, database.UserStatusDormant)),
345346
})
346347
require.NoError(t, err, "insert user")
347348

coderd/database/dbmem/dbmem.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7709,6 +7709,11 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
77097709
}
77107710
}
77117711

7712+
status := database.UserStatusDormant
7713+
if arg.Status != "" {
7714+
status = database.UserStatus(arg.Status)
7715+
}
7716+
77127717
user := database.User{
77137718
ID: arg.ID,
77147719
Email: arg.Email,
@@ -7717,7 +7722,7 @@ func (q *FakeQuerier) InsertUser(_ context.Context, arg database.InsertUserParam
77177722
UpdatedAt: arg.UpdatedAt,
77187723
Username: arg.Username,
77197724
Name: arg.Name,
7720-
Status: database.UserStatusDormant,
7725+
Status: status,
77217726
RBACRoles: arg.RBACRoles,
77227727
LoginType: arg.LoginType,
77237728
}
@@ -8640,6 +8645,7 @@ func (q *FakeQuerier) UpdateInactiveUsersToDormant(_ context.Context, params dat
86408645
updated = append(updated, database.UpdateInactiveUsersToDormantRow{
86418646
ID: user.ID,
86428647
Email: user.Email,
8648+
Username: user.Username,
86438649
LastSeenAt: user.LastSeenAt,
86448650
})
86458651
}

coderd/database/queries.sql.go

Lines changed: 17 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/users.sql

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,15 @@ INSERT INTO
6767
created_at,
6868
updated_at,
6969
rbac_roles,
70-
login_type
70+
login_type,
71+
status
7172
)
7273
VALUES
73-
($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *;
74+
($1, $2, $3, $4, $5, $6, $7, $8, $9,
75+
-- if the status passed in is empty, fallback to dormant, which is what
76+
-- we were doing before.
77+
COALESCE(NULLIF(@status::text, '')::user_status, 'dormant'::user_status)
78+
) RETURNING *;
7479

7580
-- name: UpdateUserProfile :one
7681
UPDATE
@@ -286,7 +291,7 @@ SET
286291
WHERE
287292
last_seen_at < @last_seen_after :: timestamp
288293
AND status = 'active'::user_status
289-
RETURNING id, email, last_seen_at;
294+
RETURNING id, email, username, last_seen_at;
290295

291296
-- AllUserIDs returns all UserIDs regardless of user status or deletion.
292297
-- name: AllUserIDs :many

coderd/httpmw/apikey.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const (
8282

8383
type ExtractAPIKeyConfig struct {
8484
DB database.Store
85+
ActivateDormantUser func(ctx context.Context, u database.User) (database.User, error)
8586
OAuth2Configs *OAuth2Configs
8687
RedirectToLogin bool
8788
DisableSessionExpiryRefresh bool
@@ -414,21 +415,20 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
414415
})
415416
}
416417

417-
if userStatus == database.UserStatusDormant {
418-
// If coder confirms that the dormant user is valid, it can switch their account to active.
419-
// nolint:gocritic
420-
u, err := cfg.DB.UpdateUserStatus(dbauthz.AsSystemRestricted(ctx), database.UpdateUserStatusParams{
421-
ID: key.UserID,
422-
Status: database.UserStatusActive,
423-
UpdatedAt: dbtime.Now(),
418+
if userStatus == database.UserStatusDormant && cfg.ActivateDormantUser != nil {
419+
id, _ := uuid.Parse(actor.ID)
420+
user, err := cfg.ActivateDormantUser(ctx, database.User{
421+
ID: id,
422+
Username: actor.FriendlyName,
423+
Status: userStatus,
424424
})
425425
if err != nil {
426426
return write(http.StatusInternalServerError, codersdk.Response{
427427
Message: internalErrorMessage,
428-
Detail: fmt.Sprintf("can't activate a dormant user: %s", err.Error()),
428+
Detail: fmt.Sprintf("update user status: %s", err.Error()),
429429
})
430430
}
431-
userStatus = u.Status
431+
userStatus = user.Status
432432
}
433433

434434
if userStatus != database.UserStatusActive {

coderd/provisionerdserver/provisionerdserver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,7 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
10631063
wriBytes, err := json.Marshal(buildResourceInfo)
10641064
if err != nil {
10651065
s.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
1066+
wriBytes = []byte("{}")
10661067
}
10671068

10681069
bag := audit.BaggageFromContext(ctx)

0 commit comments

Comments
 (0)