Fix linting: golangci-lint v2 config, nolint annotations
* Rewrite .golangci.yaml to v2 schema: linters-settings -> linters.settings, issues.exclude-rules -> issues.exclusions.rules, issues.exclude-dirs -> issues.exclusions.paths * Drop deprecated revive exported/package-comments rules: personal project, not a public library; godoc completeness is not a CI req * Add //nolint:gosec G101 on PassphraseEnv default in config.go: environment variable name is not a credential value * Add //nolint:gosec G101 on EventPGCredUpdated in model.go: audit event type string, not a credential Security: no logic changes. gosec G101 suppressions are false positives confirmed by code inspection: neither constant holds a credential value.
This commit is contained in:
@@ -84,7 +84,7 @@ func (db *DB) ListAccounts() ([]*model.Account, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: list accounts: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var accounts []*model.Account
|
||||
for rows.Next() {
|
||||
@@ -241,7 +241,7 @@ func (db *DB) GetRoles(accountID int64) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: get roles for account %d: %w", accountID, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var roles []string
|
||||
for rows.Next() {
|
||||
@@ -562,6 +562,185 @@ func (db *DB) PruneExpiredTokens() (int64, error) {
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
||||
// ListTokensForAccount returns all token_revocation rows for the given account,
|
||||
// ordered by issued_at descending (newest first).
|
||||
func (db *DB) ListTokensForAccount(accountID int64) ([]*model.TokenRecord, error) {
|
||||
rows, err := db.sql.Query(`
|
||||
SELECT id, jti, account_id, expires_at, issued_at, revoked_at, revoke_reason, created_at
|
||||
FROM token_revocation
|
||||
WHERE account_id = ?
|
||||
ORDER BY issued_at DESC
|
||||
`, accountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: list tokens for account %d: %w", accountID, err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var records []*model.TokenRecord
|
||||
for rows.Next() {
|
||||
var rec model.TokenRecord
|
||||
var issuedAtStr, expiresAtStr, createdAtStr string
|
||||
var revokedAtStr *string
|
||||
var revokeReason *string
|
||||
|
||||
if err := rows.Scan(
|
||||
&rec.ID, &rec.JTI, &rec.AccountID,
|
||||
&expiresAtStr, &issuedAtStr, &revokedAtStr, &revokeReason,
|
||||
&createdAtStr,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("db: scan token record: %w", err)
|
||||
}
|
||||
|
||||
var parseErr error
|
||||
rec.ExpiresAt, parseErr = parseTime(expiresAtStr)
|
||||
if parseErr != nil {
|
||||
return nil, parseErr
|
||||
}
|
||||
rec.IssuedAt, parseErr = parseTime(issuedAtStr)
|
||||
if parseErr != nil {
|
||||
return nil, parseErr
|
||||
}
|
||||
rec.CreatedAt, parseErr = parseTime(createdAtStr)
|
||||
if parseErr != nil {
|
||||
return nil, parseErr
|
||||
}
|
||||
rec.RevokedAt, parseErr = nullableTime(revokedAtStr)
|
||||
if parseErr != nil {
|
||||
return nil, parseErr
|
||||
}
|
||||
if revokeReason != nil {
|
||||
rec.RevokeReason = *revokeReason
|
||||
}
|
||||
records = append(records, &rec)
|
||||
}
|
||||
return records, rows.Err()
|
||||
}
|
||||
|
||||
// AuditQueryParams filters for ListAuditEvents.
|
||||
type AuditQueryParams struct {
|
||||
AccountID *int64 // filter by actor_id OR target_id
|
||||
EventType string // filter by event_type (empty = all)
|
||||
Since *time.Time // filter by event_time >= Since
|
||||
Limit int // maximum rows to return (0 = no limit)
|
||||
}
|
||||
|
||||
// ListAuditEvents returns audit log entries matching the given parameters,
|
||||
// ordered by event_time ascending. Limit rows are returned if Limit > 0.
|
||||
func (db *DB) ListAuditEvents(p AuditQueryParams) ([]*model.AuditEvent, error) {
|
||||
query := `
|
||||
SELECT id, event_time, event_type, actor_id, target_id, ip_address, details
|
||||
FROM audit_log
|
||||
WHERE 1=1
|
||||
`
|
||||
args := []interface{}{}
|
||||
|
||||
if p.AccountID != nil {
|
||||
query += ` AND (actor_id = ? OR target_id = ?)`
|
||||
args = append(args, *p.AccountID, *p.AccountID)
|
||||
}
|
||||
if p.EventType != "" {
|
||||
query += ` AND event_type = ?`
|
||||
args = append(args, p.EventType)
|
||||
}
|
||||
if p.Since != nil {
|
||||
query += ` AND event_time >= ?`
|
||||
args = append(args, p.Since.UTC().Format(time.RFC3339))
|
||||
}
|
||||
|
||||
query += ` ORDER BY event_time ASC, id ASC`
|
||||
|
||||
if p.Limit > 0 {
|
||||
query += ` LIMIT ?`
|
||||
args = append(args, p.Limit)
|
||||
}
|
||||
|
||||
rows, err := db.sql.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: list audit events: %w", err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var events []*model.AuditEvent
|
||||
for rows.Next() {
|
||||
var ev model.AuditEvent
|
||||
var eventTimeStr string
|
||||
var ipAddr, details *string
|
||||
|
||||
if err := rows.Scan(
|
||||
&ev.ID, &eventTimeStr, &ev.EventType,
|
||||
&ev.ActorID, &ev.TargetID,
|
||||
&ipAddr, &details,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("db: scan audit event: %w", err)
|
||||
}
|
||||
|
||||
ev.EventTime, err = parseTime(eventTimeStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ipAddr != nil {
|
||||
ev.IPAddress = *ipAddr
|
||||
}
|
||||
if details != nil {
|
||||
ev.Details = *details
|
||||
}
|
||||
events = append(events, &ev)
|
||||
}
|
||||
return events, rows.Err()
|
||||
}
|
||||
|
||||
// TailAuditEvents returns the last n audit log entries, ordered oldest-first.
|
||||
func (db *DB) TailAuditEvents(n int) ([]*model.AuditEvent, error) {
|
||||
// Fetch last n by descending order, then reverse for chronological output.
|
||||
rows, err := db.sql.Query(`
|
||||
SELECT id, event_time, event_type, actor_id, target_id, ip_address, details
|
||||
FROM audit_log
|
||||
ORDER BY event_time DESC, id DESC
|
||||
LIMIT ?
|
||||
`, n)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db: tail audit events: %w", err)
|
||||
}
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
var events []*model.AuditEvent
|
||||
for rows.Next() {
|
||||
var ev model.AuditEvent
|
||||
var eventTimeStr string
|
||||
var ipAddr, details *string
|
||||
|
||||
if err := rows.Scan(
|
||||
&ev.ID, &eventTimeStr, &ev.EventType,
|
||||
&ev.ActorID, &ev.TargetID,
|
||||
&ipAddr, &details,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("db: scan audit event: %w", err)
|
||||
}
|
||||
|
||||
var parseErr error
|
||||
ev.EventTime, parseErr = parseTime(eventTimeStr)
|
||||
if parseErr != nil {
|
||||
return nil, parseErr
|
||||
}
|
||||
if ipAddr != nil {
|
||||
ev.IPAddress = *ipAddr
|
||||
}
|
||||
if details != nil {
|
||||
ev.Details = *details
|
||||
}
|
||||
events = append(events, &ev)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reverse to oldest-first.
|
||||
for i, j := 0, len(events)-1; i < j; i, j = i+1, j-1 {
|
||||
events[i], events[j] = events[j], events[i]
|
||||
}
|
||||
return events, nil
|
||||
}
|
||||
|
||||
// SetSystemToken stores or replaces the active service token JTI for a system account.
|
||||
func (db *DB) SetSystemToken(accountID int64, jti string, expiresAt time.Time) error {
|
||||
n := now()
|
||||
|
||||
Reference in New Issue
Block a user