mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
This PR adds fields to templates that constrain values for workspaces derived from that template. - Autostop: Adds a field max_ttl on the template which limits the maximum value of ttl on all workspaces derived from that template. Defaulting to 168 hours, enforced on edits to workspace metadata. New workspaces will default to the templates's `max_ttl` if not specified. - Autostart: Adds a field min_autostart_duration which limits the minimum duration between successive autostarts of a template, measured from a single reference time. Defaulting to 1 hour, enforced on edits to workspace metadata.
126 lines
4.6 KiB
Go
126 lines
4.6 KiB
Go
package audit
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/coder/coder/coderd/database"
|
|
)
|
|
|
|
// Auditable is mostly a marker interface. It contains a definitive list of all
|
|
// auditable types. If you want to audit a new type, first define it in
|
|
// AuditableResources, then add it to this interface.
|
|
type Auditable interface {
|
|
database.GitSSHKey |
|
|
database.OrganizationMember |
|
|
database.Organization |
|
|
database.Template |
|
|
database.TemplateVersion |
|
|
database.User |
|
|
database.Workspace
|
|
}
|
|
|
|
type Action string
|
|
|
|
const (
|
|
// ActionIgnore ignores diffing for the field.
|
|
ActionIgnore = "ignore"
|
|
// ActionTrack includes the value in the diff if the value changed.
|
|
ActionTrack = "track"
|
|
// ActionSecret includes a zero value of the same type if the value changed.
|
|
// It lets you indicate that a value changed, but without leaking its
|
|
// contents.
|
|
ActionSecret = "secret"
|
|
)
|
|
|
|
// Table is a map of struct names to a map of field names that indicate that
|
|
// field's AuditType.
|
|
type Table map[string]map[string]Action
|
|
|
|
// AuditableResources contains a definitive list of all auditable resources and
|
|
// which fields are auditable.
|
|
var AuditableResources = auditMap(map[any]map[string]Action{
|
|
&database.GitSSHKey{}: {
|
|
"user_id": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"private_key": ActionSecret, // We don't want to expose private keys in diffs.
|
|
"public_key": ActionTrack, // Public keys are ok to expose in a diff.
|
|
},
|
|
&database.OrganizationMember{}: {
|
|
"user_id": ActionTrack,
|
|
"organization_id": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"roles": ActionTrack,
|
|
},
|
|
&database.Organization{}: {
|
|
"id": ActionTrack,
|
|
"name": ActionTrack,
|
|
"description": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
},
|
|
&database.Template{}: {
|
|
"id": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"organization_id": ActionTrack,
|
|
"deleted": ActionIgnore, // Changes, but is implicit when a delete event is fired.
|
|
"name": ActionTrack,
|
|
"provisioner": ActionTrack,
|
|
"active_version_id": ActionTrack,
|
|
"description": ActionTrack,
|
|
"max_ttl": ActionTrack,
|
|
"min_autostart_interval": ActionTrack,
|
|
},
|
|
&database.TemplateVersion{}: {
|
|
"id": ActionTrack,
|
|
"template_id": ActionTrack,
|
|
"organization_id": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes, but is implicit and not helpful in a diff.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"name": ActionTrack,
|
|
"readme": ActionTrack,
|
|
"job_id": ActionIgnore, // Not helpful in a diff because jobs aren't tracked in audit logs.
|
|
},
|
|
&database.User{}: {
|
|
"id": ActionTrack,
|
|
"email": ActionTrack,
|
|
"username": ActionTrack,
|
|
"hashed_password": ActionSecret, // Do not expose a users hashed password.
|
|
"created_at": ActionIgnore, // Never changes.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"status": ActionTrack,
|
|
"rbac_roles": ActionTrack,
|
|
},
|
|
&database.Workspace{}: {
|
|
"id": ActionTrack,
|
|
"created_at": ActionIgnore, // Never changes.
|
|
"updated_at": ActionIgnore, // Changes, but is implicit and not helpful in a diff.
|
|
"owner_id": ActionTrack,
|
|
"organization_id": ActionTrack,
|
|
"template_id": ActionTrack,
|
|
"deleted": ActionIgnore, // Changes, but is implicit when a delete event is fired.
|
|
"name": ActionTrack,
|
|
"autostart_schedule": ActionTrack,
|
|
"ttl": ActionTrack,
|
|
},
|
|
})
|
|
|
|
// auditMap converts a map of struct pointers to a map of struct names as
|
|
// strings. It's a convenience wrapper so that structs can be passed in by value
|
|
// instead of manually typing struct names as strings.
|
|
func auditMap(m map[any]map[string]Action) Table {
|
|
out := make(Table, len(m))
|
|
|
|
for k, v := range m {
|
|
out[structName(reflect.TypeOf(k).Elem())] = v
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func (t Action) String() string {
|
|
return string(t)
|
|
}
|