chore: enforce orgid in audit logs where required (#12283)

* chore: enforce orgid in audit logs where required
This commit is contained in:
Steven Masley 2024-02-26 08:27:33 -06:00 committed by GitHub
parent 74b749b890
commit f44c89d200
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 154 additions and 80 deletions

View File

@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"flag"
"fmt"
"net"
"net/http"
@ -25,6 +26,9 @@ type RequestParams struct {
Audit Auditor
Log slog.Logger
// OrganizationID is only provided when possible. If an audit resource extends
// beyond the org scope, leave this as the nil uuid.
OrganizationID uuid.UUID
Request *http.Request
Action database.AuditAction
AdditionalFields json.RawMessage
@ -96,7 +100,7 @@ func ResourceTarget[T Auditable](tgt T) string {
case database.HealthSettings:
return "" // no target?
default:
panic(fmt.Sprintf("unknown resource %T", tgt))
panic(fmt.Sprintf("unknown resource %T for ResourceTarget", tgt))
}
}
@ -129,7 +133,7 @@ func ResourceID[T Auditable](tgt T) uuid.UUID {
// Artificial ID for auditing purposes
return typed.ID
default:
panic(fmt.Sprintf("unknown resource %T", tgt))
panic(fmt.Sprintf("unknown resource %T for ResourceID", tgt))
}
}
@ -160,10 +164,59 @@ func ResourceType[T Auditable](tgt T) database.ResourceType {
case database.HealthSettings:
return database.ResourceTypeHealthSettings
default:
panic(fmt.Sprintf("unknown resource %T", typed))
panic(fmt.Sprintf("unknown resource %T for ResourceType", typed))
}
}
// ResourceRequiresOrgID will ensure given resources are always audited with an
// organization ID.
func ResourceRequiresOrgID[T Auditable]() bool {
var tgt T
switch any(tgt).(type) {
case database.Template, database.TemplateVersion:
return true
case database.Workspace, database.WorkspaceBuild:
return true
case database.AuditableGroup:
return true
case database.User:
return false
case database.GitSSHKey:
return false
case database.APIKey:
return false
case database.License:
return false
case database.WorkspaceProxy:
return false
case database.AuditOAuthConvertState:
// The merge state is for the given user
return false
case database.HealthSettings:
// Artificial ID for auditing purposes
return false
default:
panic(fmt.Sprintf("unknown resource %T for ResourceRequiresOrgID", tgt))
}
}
// requireOrgID will either panic (in unit tests) or log an error (in production)
// if the given resource requires an organization ID and the provided ID is nil.
func requireOrgID[T Auditable](ctx context.Context, id uuid.UUID, log slog.Logger) uuid.UUID {
if ResourceRequiresOrgID[T]() && id == uuid.Nil {
var tgt T
resourceName := fmt.Sprintf("%T", tgt)
if flag.Lookup("test.v") != nil {
// In unit tests we panic to fail the tests
panic(fmt.Sprintf("missing required organization ID for resource %q", resourceName))
}
log.Error(ctx, "missing required organization ID for resource in audit log",
slog.F("resource", resourceName),
)
}
return id
}
// InitRequest initializes an audit log for a request. It returns a function
// that should be deferred, causing the audit log to be committed when the
// handler returns.
@ -243,6 +296,7 @@ func InitRequest[T Auditable](w http.ResponseWriter, p *RequestParams) (*Request
StatusCode: int32(sw.Status),
RequestID: httpmw.RequestID(p.Request),
AdditionalFields: p.AdditionalFields,
OrganizationID: requireOrgID[T](logCtx, p.OrganizationID, p.Log),
}
err := p.Audit.Export(ctx, auditLog)
if err != nil {
@ -276,7 +330,7 @@ func BackgroundAudit[T Auditable](ctx context.Context, p *BackgroundAuditParams[
ID: uuid.New(),
Time: dbtime.Now(),
UserID: p.UserID,
OrganizationID: p.OrganizationID,
OrganizationID: requireOrgID[T](ctx, p.OrganizationID, p.Log),
Ip: ip,
UserAgent: sql.NullString{},
ResourceType: either(p.Old, p.New, ResourceType[T], p.Action),

View File

@ -61,6 +61,7 @@ func (api *API) deleteTemplate(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionDelete,
OrganizationID: template.OrganizationID,
})
)
defer commitAudit()
@ -127,12 +128,14 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
Log: api.Logger,
Request: r,
Action: database.AuditActionCreate,
OrganizationID: organization.ID,
})
templateVersionAudit, commitTemplateVersionAudit = audit.InitRequest[database.TemplateVersion](rw, &audit.RequestParams{
Audit: auditor,
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: organization.ID,
})
)
defer commitTemplateAudit()
@ -546,6 +549,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: template.OrganizationID,
})
)
defer commitAudit()

View File

@ -1044,6 +1044,7 @@ func (api *API) postArchiveTemplateVersions(rw http.ResponseWriter, r *http.Requ
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: template.OrganizationID,
})
)
defer commitAudit()
@ -1126,6 +1127,7 @@ func (api *API) setArchiveTemplateVersion(archive bool) func(rw http.ResponseWri
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: templateVersion.OrganizationID,
})
)
defer commitAudit()
@ -1211,6 +1213,7 @@ func (api *API) patchActiveTemplateVersion(rw http.ResponseWriter, r *http.Reque
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: template.OrganizationID,
})
)
defer commitAudit()
@ -1314,6 +1317,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht
Log: api.Logger,
Request: r,
Action: database.AuditActionCreate,
OrganizationID: organization.ID,
})
req codersdk.CreateTemplateVersionRequest

View File

@ -345,6 +345,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
Request: r,
Action: database.AuditActionCreate,
AdditionalFields: wriBytes,
OrganizationID: organization.ID,
})
defer commitAudit()
@ -648,6 +649,7 @@ func (api *API) patchWorkspace(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
)
defer commitAudit()
@ -738,6 +740,7 @@ func (api *API) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
)
defer commitAudit()
@ -812,6 +815,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
)
defer commitAudit()
@ -900,6 +904,7 @@ func (api *API) putWorkspaceDormant(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
)
aReq.Old = oldWorkspace
@ -1098,6 +1103,7 @@ func (api *API) putFavoriteWorkspace(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
defer commitAudit()
aReq.Old = workspace
@ -1144,6 +1150,7 @@ func (api *API) deleteFavoriteWorkspace(rw http.ResponseWriter, r *http.Request)
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
defer commitAudit()
@ -1182,6 +1189,7 @@ func (api *API) putWorkspaceAutoupdates(rw http.ResponseWriter, r *http.Request)
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: workspace.OrganizationID,
})
)
defer commitAudit()

View File

@ -38,6 +38,7 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request)
Log: api.Logger,
Request: r,
Action: database.AuditActionCreate,
OrganizationID: org.ID,
})
)
defer commitAudit()
@ -101,6 +102,7 @@ func (api *API) patchGroup(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: group.OrganizationID,
})
)
defer commitAudit()
@ -303,6 +305,7 @@ func (api *API) deleteGroup(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionDelete,
OrganizationID: group.OrganizationID,
})
)
defer commitAudit()

View File

@ -165,6 +165,7 @@ func (api *API) patchTemplateACL(rw http.ResponseWriter, r *http.Request) {
Log: api.Logger,
Request: r,
Action: database.AuditActionWrite,
OrganizationID: template.OrganizationID,
})
)
defer commitAudit()