fix: show audit logs for forgot password flow (#15181)

Fixes https://github.com/coder/coder/issues/15150

Audit logs for requesting a password reset, and a user updating their
password, now show up in the audit log.
This commit is contained in:
Danielle Maywood
2024-10-22 13:47:30 +01:00
committed by GitHub
parent 297089e944
commit 5076161078
17 changed files with 130 additions and 40 deletions
+4 -2
View File
@@ -9116,7 +9116,8 @@ const docTemplate = `{
"stop",
"login",
"logout",
"register"
"register",
"request_password_reset"
],
"x-enum-varnames": [
"AuditActionCreate",
@@ -9126,7 +9127,8 @@ const docTemplate = `{
"AuditActionStop",
"AuditActionLogin",
"AuditActionLogout",
"AuditActionRegister"
"AuditActionRegister",
"AuditActionRequestPasswordReset"
]
},
"codersdk.AuditDiff": {
+4 -2
View File
@@ -8090,7 +8090,8 @@
"stop",
"login",
"logout",
"register"
"register",
"request_password_reset"
],
"x-enum-varnames": [
"AuditActionCreate",
@@ -8100,7 +8101,8 @@
"AuditActionStop",
"AuditActionLogin",
"AuditActionLogout",
"AuditActionRegister"
"AuditActionRegister",
"AuditActionRequestPasswordReset"
]
},
"codersdk.AuditDiff": {
+14 -3
View File
@@ -274,8 +274,15 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
b := strings.Builder{}
// NOTE: WriteString always returns a nil error, so we never check it
_, _ = b.WriteString("{user} ")
// Requesting a password reset can be performed by anyone that knows the email
// of a user so saying the user performed this action might be slightly misleading.
if alog.AuditLog.Action != database.AuditActionRequestPasswordReset {
_, _ = b.WriteString("{user} ")
}
if alog.AuditLog.StatusCode >= 400 {
_, _ = b.WriteString("unsuccessfully attempted to ")
_, _ = b.WriteString(string(alog.AuditLog.Action))
@@ -298,8 +305,12 @@ func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
return b.String()
}
_, _ = b.WriteString(" ")
_, _ = b.WriteString(codersdk.ResourceType(alog.AuditLog.ResourceType).FriendlyString())
if alog.AuditLog.Action == database.AuditActionRequestPasswordReset {
_, _ = b.WriteString(" for")
} else {
_, _ = b.WriteString(" ")
_, _ = b.WriteString(codersdk.ResourceType(alog.AuditLog.ResourceType).FriendlyString())
}
if alog.AuditLog.ResourceType == database.ResourceTypeConvertLogin {
_, _ = b.WriteString(" to")
+2 -1
View File
@@ -19,7 +19,8 @@ CREATE TYPE audit_action AS ENUM (
'stop',
'login',
'logout',
'register'
'register',
'request_password_reset'
);
CREATE TYPE automatic_updates AS ENUM (
@@ -0,0 +1,2 @@
-- It's not possible to drop enum values from enum types, so the UP has "IF NOT
-- EXISTS".
@@ -0,0 +1,2 @@
ALTER TYPE audit_action
ADD VALUE IF NOT EXISTS 'request_password_reset';
+12 -9
View File
@@ -138,14 +138,15 @@ func AllAppSharingLevelValues() []AppSharingLevel {
type AuditAction string
const (
AuditActionCreate AuditAction = "create"
AuditActionWrite AuditAction = "write"
AuditActionDelete AuditAction = "delete"
AuditActionStart AuditAction = "start"
AuditActionStop AuditAction = "stop"
AuditActionLogin AuditAction = "login"
AuditActionLogout AuditAction = "logout"
AuditActionRegister AuditAction = "register"
AuditActionCreate AuditAction = "create"
AuditActionWrite AuditAction = "write"
AuditActionDelete AuditAction = "delete"
AuditActionStart AuditAction = "start"
AuditActionStop AuditAction = "stop"
AuditActionLogin AuditAction = "login"
AuditActionLogout AuditAction = "logout"
AuditActionRegister AuditAction = "register"
AuditActionRequestPasswordReset AuditAction = "request_password_reset"
)
func (e *AuditAction) Scan(src interface{}) error {
@@ -192,7 +193,8 @@ func (e AuditAction) Valid() bool {
AuditActionStop,
AuditActionLogin,
AuditActionLogout,
AuditActionRegister:
AuditActionRegister,
AuditActionRequestPasswordReset:
return true
}
return false
@@ -208,6 +210,7 @@ func AllAuditActionValues() []AuditAction {
AuditActionLogin,
AuditActionLogout,
AuditActionRegister,
AuditActionRequestPasswordReset,
}
}
+3 -1
View File
@@ -220,7 +220,7 @@ func (api *API) postRequestOneTimePasscode(rw http.ResponseWriter, r *http.Reque
Audit: *auditor,
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
Action: database.AuditActionRequestPasswordReset,
})
)
defer commitAudit()
@@ -253,6 +253,7 @@ func (api *API) postRequestOneTimePasscode(rw http.ResponseWriter, r *http.Reque
}
// We continue if err == sql.ErrNoRows to help prevent a timing-based attack.
aReq.Old = user
aReq.UserID = user.ID
passcode := uuid.New()
passcodeExpiresAt := dbtime.Now().Add(api.OneTimePasscodeValidityPeriod)
@@ -365,6 +366,7 @@ func (api *API) postChangePasswordWithOneTimePasscode(rw http.ResponseWriter, r
}
// We continue if err == sql.ErrNoRows to help prevent a timing-based attack.
aReq.Old = user
aReq.UserID = user.ID
equal, err := userpassword.Compare(string(user.HashedOneTimePasscode), req.OneTimePasscode)
if err != nil {