Skip to content

feat: add audit logs for dormancy events #15298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Oct 31, 2024
Merged

feat: add audit logs for dormancy events #15298

merged 14 commits into from
Oct 31, 2024

Conversation

coadler
Copy link
Contributor

@coadler coadler commented Oct 30, 2024

An audit log will now be generated when Coder automatically modifies user statuses to dormant, or when a user logs in and are automatically converted to active.

Closes #15277

An audit log will now be generated when Coder automatically modifies
user statuses to dormant, or when a user logs in and are automatically
converted to active.
@coadler coadler self-assigned this Oct 30, 2024
@coadler coadler requested a review from sreya October 30, 2024 23:40
Copy link
Collaborator

@sreya sreya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're dropping a login audit log when a user logs in via OIDC which I suppose captures the diff of a dormant user going to active. But we're not dropping an audit log event specifically for when their user status is being updated from dormant to active that I can tell. Seems like we should be dropping an extra one with a write action to be consistent with the builtin logs you added. It's also consistent with us dropping extra audit logs for special cases like when we convert the user type.

@@ -197,6 +197,7 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
UpdatedAt: dbtime.Now(),
RBACRoles: []string{rbac.RoleOwner().String()},
LoginType: database.LoginTypePassword,
Status: "",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we pass "" here? Should we just pass active?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I would omit it but the linter doesn't like leaving it out. It doesn't really matter what this is created as, so "" just does the default behavior which is dormant.

Comment on lines +74 to +78
($1, $2, $3, $4, $5, $6, $7, $8, $9,
-- if the status passed in is empty, fallback to dormant, which is what
-- we were doing before.
COALESCE(NULLIF(@status::text, '')::user_status, 'dormant'::user_status)
) RETURNING *;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we handle this as a special case? Shouldn't we expect the status to be provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really want to leak the previous default behavior out into the API code. I think it makes more sense to do the default behavior here, which is what was happening before when it wasn't being inserted.

wriBytes = []byte("{}")
}

audit.BackgroundAudit(ctx, &audit.BackgroundAuditParams[database.User]{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't audit if we fail to update the user status. Also why is this a background audit if it's part of an API request?

Copy link
Contributor Author

@coadler coadler Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I don't really think we get anything valuable from generating an audit log if this fails. The main reason for failed audit logs is for logging users attempting to take an action for which they don't have permission, or similar. The status failing to update here doesn't really matter, it can only ever be a 500 error. Plus, it'll be captured by the main login audit anyways.

I think BackgroundAudit is poorly named, it's really just for anything not specifically tied to a request. I don't think it make sense to use a request audit here because the status could successfully be changed while the api request as a whole fails. Also, since dormancy is really something Coder just does in the background, it maintains consistency. The user isn't really intentionally modifying their status.

Comment on lines +1350 to +1353
status := ""
if req.UserStatus != nil {
status = string(*req.UserStatus)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly when would we expect the status to be empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty just means use the default behavior, which is dormant. It would be a breaking change if this was required

@coadler coadler requested a review from sreya October 31, 2024 20:08
@@ -82,6 +82,7 @@ const (

type ExtractAPIKeyConfig struct {
DB database.Store
HandleDormancy func(ctx context.Context, u database.User) database.User
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally dislike these passthrough functions and given that we pass the same implementation in the enterprise path, is it reasonable to move this function off this config (and the API struct) and just have it as a package level function somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reasonable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the passthrough is necessary because we can't import audit in httpmw since audit already imports httpmw itself, so I have to result to this jank

@@ -25,71 +25,49 @@ const (

// CheckInactiveUsers function updates status of inactive users from active to dormant
// using default parameters.
func CheckInactiveUsers(ctx context.Context, logger slog.Logger, db database.Store, auditor audit.Auditor) func() {
return CheckInactiveUsersWithOptions(ctx, logger, db, auditor, jobInterval, accountDormancyPeriod)
func CheckInactiveUsers(ctx context.Context, logger slog.Logger, clk quartz.Clock, db database.Store, auditor audit.Auditor) func() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't even understand the point of this function if the other is also exported

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like it's just to hardcode the dormancy period

@coadler coadler requested a review from sreya October 31, 2024 21:13
@coadler coadler merged commit 088f219 into main Oct 31, 2024
28 of 30 checks passed
@coadler coadler deleted the colin/audit-dormancy branch October 31, 2024 22:55
@github-actions github-actions bot locked and limited conversation to collaborators Oct 31, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Log an audit event when users are transferred in and out of dormancy.
2 participants