Files
coder/enterprise/audit/audit.go
T
Cian Johnston 847a88c6ca chore: clean up stale and dangerous //nolint comments (#23643)
## Changes

- **Commit 1**: Remove 17 unnecessary `//nolint` directives:
  - `//nolint:varnamelen` — linter not active
  - `//nolint:unused` on exported `SlimUnsupported`
  - `//nolint:govet` in `coderd/httpmw/csrf` — no longer fires
  - `//nolint:revive` on functions refactored since the nolint was added
- `//nolint:paralleltest` citing Go 1.22 loop variable capture
(obsolete)
- Bare `//nolint` narrowed to specific `//nolint:gocritic` with
justification

- **Commit 2**: Fix root causes behind 5 dangerous nolint suppressions:
- Add `MinVersion: tls.VersionTLS12` to TLS client config (removes
`gosec` G402)
- Delete trivial unexported wrappers `apiKey()`/`normalizeProvider()` in
chatprovider (removes `revive` confusing-naming)
- Add doc comments to `StartWithAssert` and `Router` (removes `revive`
exported)
  - Rename unused parameters to `_` in integration test helpers

> 🤖 This PR was created using Coder Agents and reviewed by me.
2026-03-26 14:13:53 +00:00

85 lines
2.1 KiB
Go

package audit
import (
"context"
"database/sql"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/audit"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
)
type BackendDetails struct {
Actor *Actor
}
type Actor struct {
ID uuid.UUID `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
}
// Backends can store or send audit logs to arbitrary locations.
type Backend interface {
// Decision determines the FilterDecisions that the backend tolerates.
Decision() FilterDecision
// Export sends an audit log to the backend.
Export(ctx context.Context, alog database.AuditLog, details BackendDetails) error
}
func NewAuditor(db database.Store, filter Filter, backends ...Backend) audit.Auditor {
return &auditor{
db: db,
filter: filter,
backends: backends,
Differ: audit.Differ{DiffFn: func(old, newVal any) audit.Map {
return diffValues(old, newVal, AuditableResources)
}},
}
}
// auditor is the enterprise implementation of the Auditor interface.
type auditor struct {
db database.Store
filter Filter
backends []Backend
audit.Differ
}
func (a *auditor) Export(ctx context.Context, alog database.AuditLog) error {
decision, err := a.filter.Check(ctx, alog)
if err != nil {
return xerrors.Errorf("filter check: %w", err)
}
// AsSystemRestricted is used to look up the actor name even
// when the caller lacks read access to the user.
actor, err := a.db.GetUserByID(dbauthz.AsSystemRestricted(ctx), alog.UserID) //nolint:gocritic // see above
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
return err
}
for _, backend := range a.backends {
if decision&backend.Decision() != backend.Decision() {
continue
}
err = backend.Export(ctx, alog, BackendDetails{Actor: &Actor{
ID: actor.ID,
Email: actor.Email,
Username: actor.Username,
}})
if err != nil {
// naively return the first error. should probably make this smarter
// by returning multiple errors.
return xerrors.Errorf("export audit log to backend: %w", err)
}
}
return nil
}