mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: audit user secret create, update, and delete (#24756)
Emit user secret audit log entries for create/update/delete operations. Reads stay un-audited, matching every other resource. Audit log entries record changes in user secret name, environment variable name, file path, and value. The secret value column is marked `ActionSecret` so the diff records the change without showing the ciphertext or plaintext. Closes a TOCTOU window on delete to ensure no phantom audit logs for a delete of a non-existent secret. Secret update accepts a small TOCTOU window matching the other audited resources (templates, workspaces, chats). The two-query pattern is wrapped in a transaction so audit state can't leak from a failed mutation.
This commit is contained in:
@@ -34,7 +34,8 @@ type Auditable interface {
|
||||
idpsync.RoleSyncSettings |
|
||||
database.TaskTable |
|
||||
database.AiSeatState |
|
||||
database.Chat
|
||||
database.Chat |
|
||||
database.UserSecret
|
||||
}
|
||||
|
||||
// Map is a map of changed fields in an audited resource. It maps field names to
|
||||
|
||||
@@ -142,6 +142,8 @@ func ResourceTarget[T Auditable](tgt T) string {
|
||||
// for display; collisions affect the display label and search
|
||||
// filter but not the primary resource identifier.
|
||||
return typed.ID.String()[:8]
|
||||
case database.UserSecret:
|
||||
return typed.Name
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
|
||||
}
|
||||
@@ -210,6 +212,8 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
|
||||
return typed.UserID
|
||||
case database.Chat:
|
||||
return typed.ID
|
||||
case database.UserSecret:
|
||||
return typed.ID
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
|
||||
}
|
||||
@@ -269,6 +273,8 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
|
||||
return database.ResourceTypeAiSeat
|
||||
case database.Chat:
|
||||
return database.ResourceTypeChat
|
||||
case database.UserSecret:
|
||||
return database.ResourceTypeUserSecret
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
|
||||
}
|
||||
@@ -333,6 +339,9 @@ func ResourceRequiresOrgID[T Auditable]() bool {
|
||||
// Chats always have a non-null organization_id (since
|
||||
// migration 000467).
|
||||
return true
|
||||
case database.UserSecret:
|
||||
// User secrets are global to the user across organizations.
|
||||
return false
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user