96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"hyapp-admin-server/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const contextAuditLogged = "auditLogged"
|
|
|
|
type AuditStore interface {
|
|
CreateOperationLog(log model.OperationLog) error
|
|
}
|
|
|
|
func Audit(store AuditStore) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
start := time.Now()
|
|
c.Next()
|
|
|
|
if store == nil || c.GetBool(contextAuditLogged) || !shouldAudit(c.Request.Method) {
|
|
return
|
|
}
|
|
|
|
status := "success"
|
|
if c.Writer.Status() >= http.StatusBadRequest {
|
|
status = "failed"
|
|
}
|
|
path := c.FullPath()
|
|
if path == "" {
|
|
path = c.Request.URL.Path
|
|
}
|
|
_ = store.CreateOperationLog(model.OperationLog{
|
|
UserID: CurrentUserID(c),
|
|
RequestID: CurrentRequestID(c),
|
|
Username: CurrentUsername(c),
|
|
Action: strings.ToLower(c.Request.Method) + " " + path,
|
|
Resource: auditResource(path),
|
|
ResourceID: auditResourceID(c),
|
|
Method: c.Request.Method,
|
|
Path: c.Request.URL.Path,
|
|
IP: c.ClientIP(),
|
|
UserAgent: c.Request.UserAgent(),
|
|
Status: status,
|
|
HTTPStatus: c.Writer.Status(),
|
|
RiskLevel: auditRisk(c.Request.Method, path),
|
|
LatencyMS: time.Since(start).Milliseconds(),
|
|
Detail: fmt.Sprintf("http_status=%d", c.Writer.Status()),
|
|
})
|
|
}
|
|
}
|
|
|
|
func auditResourceID(c *gin.Context) string {
|
|
if id := c.Param("id"); id != "" {
|
|
return id
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func auditRisk(method string, path string) string {
|
|
if method == http.MethodDelete || strings.Contains(path, "reset-password") || strings.Contains(path, "permissions") || strings.Contains(path, "data-scopes") {
|
|
return "high"
|
|
}
|
|
if shouldAudit(method) {
|
|
return "normal"
|
|
}
|
|
return "low"
|
|
}
|
|
|
|
func MarkAuditLogged(c *gin.Context) {
|
|
c.Set(contextAuditLogged, true)
|
|
}
|
|
|
|
func shouldAudit(method string) bool {
|
|
switch method {
|
|
case http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func auditResource(path string) string {
|
|
parts := strings.SplitSeq(strings.Trim(path, "/"), "/")
|
|
for part := range parts {
|
|
if part != "" && !strings.HasPrefix(part, ":") && part != "api" && part != "v1" {
|
|
return part
|
|
}
|
|
}
|
|
return "unknown"
|
|
}
|