feat: add filtering and pagination to audit logs endpoint
The logsHandler now accepts query parameters to filter logs by action and IP address, with support for pagination via limit and offset parameters. Query parameters are safely bound to prepared statements to prevent SQL injection, and pagination values are validated to ensure they fall within acceptable ranges.
This commit is contained in:
51
auth.go
51
auth.go
@@ -3,9 +3,12 @@ package main
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -130,14 +133,56 @@ func logsHandler(cfg Config, db *sql.DB) http.HandlerFunc {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
rows, err := db.Query(`SELECT timestamp, action, ip, cidr, ttl_seconds, reason, api_client_ip
|
||||
FROM audit_log ORDER BY timestamp DESC LIMIT 200`)
|
||||
|
||||
query := `SELECT timestamp, action, ip, cidr, ttl_seconds, reason, api_client_ip FROM audit_log`
|
||||
var args []any
|
||||
argIndex := 1
|
||||
|
||||
filterAction := r.URL.Query().Get("action")
|
||||
filterIP := r.URL.Query().Get("ip")
|
||||
limitStr := r.URL.Query().Get("limit")
|
||||
offsetStr := r.URL.Query().Get("offset")
|
||||
|
||||
var conditions []string
|
||||
if filterAction != "" {
|
||||
conditions = append(conditions, fmt.Sprintf(`action = $%d`, argIndex))
|
||||
args = append(args, filterAction)
|
||||
argIndex++
|
||||
}
|
||||
if filterIP != "" {
|
||||
conditions = append(conditions, fmt.Sprintf(`ip = $%d`, argIndex))
|
||||
args = append(args, filterIP)
|
||||
argIndex++
|
||||
}
|
||||
|
||||
if len(conditions) > 0 {
|
||||
query += " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
query += ` ORDER BY timestamp DESC`
|
||||
|
||||
limit := 200
|
||||
offset := 0
|
||||
if limitStr != "" {
|
||||
if parsed, err := strconv.Atoi(limitStr); err == nil && parsed > 0 && parsed <= 1000 {
|
||||
limit = parsed
|
||||
}
|
||||
}
|
||||
if offsetStr != "" {
|
||||
if parsed, err := strconv.Atoi(offsetStr); err == nil && parsed >= 0 {
|
||||
offset = parsed
|
||||
}
|
||||
}
|
||||
query += fmt.Sprintf(` LIMIT %d OFFSET %d`, limit, offset)
|
||||
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
slog.Error("failed to query logs", "error", err)
|
||||
http.Error(w, "database error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
var logs []map[string]interface{}
|
||||
|
||||
logs := make([]map[string]interface{}, 0)
|
||||
for rows.Next() {
|
||||
var ts time.Time
|
||||
var action, apiClientIP, reason string
|
||||
|
||||
Reference in New Issue
Block a user