This commit is contained in:
2026-07-25 15:14:23 -05:00
parent e19b23de81
commit 6594aece09
8 changed files with 248 additions and 18 deletions
+105 -7
View File
@@ -31,7 +31,13 @@ func (s *Server) handlePortal(w http.ResponseWriter, r *http.Request) {
http.Error(w, "unable to list installer actions", http.StatusInternalServerError)
return
}
auditEvents, err := s.listAuditEvents(r)
auditFilter := auditFiltersFromRequest(r)
auditEventTypes, err := s.listAuditEventTypes(r)
if err != nil {
http.Error(w, "unable to list audit event types", http.StatusInternalServerError)
return
}
auditEvents, err := s.listAuditEvents(r, auditFilter)
if err != nil {
http.Error(w, "unable to list audit events", http.StatusInternalServerError)
return
@@ -42,6 +48,8 @@ func (s *Server) handlePortal(w http.ResponseWriter, r *http.Request) {
CSRFToken: tech.CSRFToken,
Authorizations: authorizations,
AuditEvents: auditEvents,
AuditFilters: auditFilter,
AuditEventTypes: auditEventTypes,
Packages: packages,
Actions: actions,
NewCode: r.URL.Query().Get("code"),
@@ -74,13 +82,102 @@ func (s *Server) listActions(r *http.Request) ([]actionRecord, error) {
return records, rows.Err()
}
func (s *Server) listAuditEvents(r *http.Request) ([]auditRecord, error) {
func auditFiltersFromRequest(r *http.Request) auditFilters {
query := r.URL.Query()
timeRange := strings.TrimSpace(query.Get("audit_range"))
switch timeRange {
case "24h", "7d", "30d", "90d", "all":
default:
timeRange = "30d"
}
return auditFilters{
TimeRange: timeRange,
EventType: limitedFilter(query.Get("audit_event"), 100),
Actor: limitedFilter(query.Get("audit_actor"), 255),
Hostname: limitedFilter(query.Get("audit_hostname"), 255),
PackageSlug: limitedFilter(query.Get("audit_package"), 100),
SourceIP: limitedFilter(query.Get("audit_ip"), 64),
Details: limitedFilter(query.Get("audit_details"), 255),
}
}
func limitedFilter(value string, maxLength int) string {
value = strings.TrimSpace(value)
characters := []rune(value)
if len(characters) > maxLength {
return string(characters[:maxLength])
}
return value
}
func (s *Server) listAuditEventTypes(r *http.Request) ([]string, error) {
rows, err := s.db.QueryContext(
r.Context(),
`SELECT event_type, actor, hostname, package_slug, source_ip, details, created_at
`SELECT DISTINCT event_type
FROM audit_events
ORDER BY created_at DESC
LIMIT 100`,
ORDER BY event_type`,
)
if err != nil {
return nil, err
}
defer rows.Close()
var eventTypes []string
for rows.Next() {
var eventType string
if err := rows.Scan(&eventType); err != nil {
return nil, err
}
eventTypes = append(eventTypes, eventType)
}
return eventTypes, rows.Err()
}
func auditQuery(filters auditFilters) (string, []any) {
query := `SELECT event_type, actor, hostname, package_slug, source_ip, details, created_at
FROM audit_events
WHERE 1 = 1`
var arguments []any
timeClauses := map[string]string{
"24h": " AND created_at >= UTC_TIMESTAMP(6) - INTERVAL 1 DAY",
"7d": " AND created_at >= UTC_TIMESTAMP(6) - INTERVAL 7 DAY",
"30d": " AND created_at >= UTC_TIMESTAMP(6) - INTERVAL 30 DAY",
"90d": " AND created_at >= UTC_TIMESTAMP(6) - INTERVAL 90 DAY",
}
query += timeClauses[filters.TimeRange]
if filters.EventType != "" {
query += " AND event_type = ?"
arguments = append(arguments, filters.EventType)
}
containsFilters := []struct {
column string
value string
}{
{"actor", filters.Actor},
{"hostname", filters.Hostname},
{"package_slug", filters.PackageSlug},
{"source_ip", filters.SourceIP},
{"details", filters.Details},
}
for _, filter := range containsFilters {
if filter.value == "" {
continue
}
query += " AND LOCATE(?, " + filter.column + ") > 0"
arguments = append(arguments, filter.value)
}
query += " ORDER BY created_at DESC LIMIT 250"
return query, arguments
}
func (s *Server) listAuditEvents(r *http.Request, filters auditFilters) ([]auditRecord, error) {
query, arguments := auditQuery(filters)
rows, err := s.db.QueryContext(
r.Context(),
query,
arguments...,
)
if err != nil {
return nil, err
@@ -147,9 +244,10 @@ func (s *Server) listAuthorizations(r *http.Request) ([]authorizationRecord, err
WHERE h.authorization_id = a.id),
a.created_at, a.expires_at, a.revoked_at,
COALESCE((
SELECT GROUP_CONCAT(ap.display_name
ORDER BY ap.display_name SEPARATOR ', ')
SELECT GROUP_CONCAT(p.display_name
ORDER BY p.display_name SEPARATOR ', ')
FROM authorization_packages ap
JOIN packages p ON p.id = ap.package_id
WHERE ap.authorization_id = a.id
), ''),
COALESCE((