Files
abrown38andClaude Opus 5.5 500bfc7bdd Ban lists: where Guard and fail2ban ban addresses
A program bans an address (every port, or a protocol's ports) for a
time; the ban list says what it is kept from: the firewall, some hosts
behind it, or everything. Bans drop ahead of every rule, close the
address's connections, group IPv6 by /64, are kept in the database
across restarts and audited. Never banned: the inside, the firewall's
own addresses, the caller, the list's never. bans grant (bans:write:LIST),
hc banlist/bans/ban/unban, Traffic -> Bans, a fail2ban action in contrib/,
and failed logins and refused tokens logged for Guard and fail2ban.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:29:37 +01:00

135 lines
4.4 KiB
Go

package model
import (
"encoding/json"
"time"
)
// Grant gives access to one resource (see internal/auth).
type Grant struct {
Resource string `json:"resource"` // config, sets, bans, log, audit, *
Access string `json:"access"` // read or write (write implies read)
// Sets limits a "sets" grant to these dynamic sets, and a "bans"
// grant to these ban lists (none: all).
Sets []string `json:"sets,omitempty"`
}
// User is a person or a service account.
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
PasswordHash string `json:"-"`
HasPassword bool `json:"has_password"`
Admin bool `json:"admin"`
Grants []Grant `json:"grants"`
Disabled bool `json:"disabled"`
Comment string `json:"comment"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}
// Token is an API token. The secret is shown once, when it is made; only
// its hash is stored.
type Token struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Username string `json:"username,omitempty"`
Name string `json:"name"`
PublicID string `json:"public_id"`
SecretHash string `json:"-"`
// Grants restrict the token to part of its user's permissions; nil
// means all of them.
Grants []Grant `json:"grants"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
LastUsedIP string `json:"last_used_ip,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
// RotatedAt is when the token last got a new secret (nil: never).
RotatedAt *time.Time `json:"rotated_at,omitempty"`
// After a rotation with a grace period, the previous secret keeps
// working until PrevExpiresAt.
PrevSecretHash string `json:"-"`
PrevExpiresAt *time.Time `json:"previous_secret_valid_until,omitempty"`
}
// IssuedAt is when the token's current secret was made: its last
// rotation, or its creation.
func (t *Token) IssuedAt() time.Time {
if t.RotatedAt != nil {
return *t.RotatedAt
}
return t.CreatedAt
}
// Session is a web UI login.
type Session struct {
IDHash string `json:"-"`
UserID int64 `json:"user_id"`
CSRFToken string `json:"-"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
LastSeenAt time.Time `json:"last_seen_at"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
}
// AuditEvent records an action: logins, users and tokens, changes,
// dynamic set entries.
type AuditEvent struct {
ID int64 `json:"id"`
Time time.Time `json:"time"`
UserID int64 `json:"user_id,omitempty"`
Username string `json:"username,omitempty"`
Auth string `json:"auth,omitempty"` // session, token, socket
TokenID int64 `json:"token_id,omitempty"`
IP string `json:"ip,omitempty"`
Action string `json:"action"`
Object string `json:"object,omitempty"`
Result string `json:"result"` // success, failure, denied
Detail string `json:"detail,omitempty"`
}
// Change is one object's change: Before is nil for a create, After is nil
// for a delete. Settings have ID 0.
type Change struct {
Kind string `json:"kind"`
ID int64 `json:"id"`
Label string `json:"label"`
Before json.RawMessage `json:"before,omitempty"`
After json.RawMessage `json:"after,omitempty"`
}
// Action describes a change in a word.
func (c Change) Action() string {
switch {
case c.Before == nil:
return "create"
case c.After == nil:
return "delete"
}
return "update"
}
// Inverse is the change that undoes c.
func (c Change) Inverse() Change {
c.Before, c.After = c.After, c.Before
return c
}
// HistoryEntry is a saved configuration change.
type HistoryEntry struct {
ID int64 `json:"id"`
Time time.Time `json:"time"`
UserID int64 `json:"user_id,omitempty"`
Username string `json:"username"`
Via string `json:"via"` // web, cli, api
Description string `json:"description"`
Changes []Change `json:"changes"`
// RevertOf is the entry this one undid; RevertedBy the entry that
// undid this one.
RevertOf int64 `json:"revert_of,omitempty"`
RevertedBy int64 `json:"reverted_by,omitempty"`
}