Stop 500ing when a service account acts on an incident
Every incident-mutation handler read userFromContext(ctx) and wrote the result's .ID into acknowledged_by/incident_events.user_id without checking the ok bool. For a team-scoped service-account caller this returned a zero-value user id, which violated the users(id) FK and 500'd on acknowledge, unacknowledge, resolve, snooze, unsnooze and create-note. handleDeleteNote didn't crash but silently matched zero rows instead (WHERE user_id = 0), so a service account could never delete its own note. Add acknowledged_by_service_account_id (incidents) and service_account_id (incident_events) as nullable FKs to service_accounts(id), parallel to and mutually exclusive with the existing human columns (migration 015, with a CHECK enforcing the exclusion). Route every one of the six handlers plus delete-note through a new callerActorIDs() helper that branches on Caller.AsHuman()/ServiceAccountID() instead of assuming a human, and thread a serviceAccountID parameter through logEvent and the new acknowledgeIncidentAs (acknowledgeIncident itself is untouched: its only other caller, the push-notification Acknowledge button, is always human). Render the new actor distinctly from both a human and "the server acted" in the web UI's incident timeline and facts card. handleIncidentAssign, handleIncidentArchive and handleIncidentUnarchive are deliberately not touched here — they track no actor at all today, for anyone, which is a separate pre-existing gap (follow-up issue to come). Fixes #25
This commit is contained in:
+16
-5
@@ -99,13 +99,24 @@ func (c Caller) ServiceAccountID() (int64, bool) {
|
||||
return c.sa.id, true
|
||||
}
|
||||
|
||||
// ServiceAccountName reports this caller's own service-account name, for a
|
||||
// handler's synchronous response — the same credential it authenticated
|
||||
// with, already resolved onto the Caller by serveAsServiceAccount, so no
|
||||
// extra query is needed.
|
||||
func (c Caller) ServiceAccountName() (string, bool) {
|
||||
if c.sa == nil {
|
||||
return "", false
|
||||
}
|
||||
return c.sa.name, true
|
||||
}
|
||||
|
||||
// Identity is a stable, log/audit-facing string distinguishing a human
|
||||
// caller from a service account — "user:42" or "service-account:7". Not
|
||||
// wired into any database column today (incidents.go's acknowledged_by/
|
||||
// assigned_to/user_id are explicitly out of scope for this change — that
|
||||
// needs its own schema migration, tracked separately), but this is the one
|
||||
// place in the request path that already knows which kind of caller this
|
||||
// is, and that follow-up will want exactly this accessor.
|
||||
// wired into any database column — incidents.go's acknowledged_by/
|
||||
// incident_events.user_id use AsHuman()/ServiceAccountID() directly against
|
||||
// the parallel *_service_account_id columns (migration 015) instead, since a
|
||||
// column needs the id, not this rendered string. assigned_to stays
|
||||
// human-only and out of scope (terdut-server#25's follow-up).
|
||||
func (c Caller) Identity() string {
|
||||
switch {
|
||||
case c.user != nil:
|
||||
|
||||
Reference in New Issue
Block a user