Smail/server/controllers/config_audit.go

123 lines
3.5 KiB
Go

package controllers
import (
"net/http"
"strings"
"time"
"git.pcux.cn/xuans/Smail/config"
"git.pcux.cn/xuans/Smail/dto/response"
"git.pcux.cn/xuans/Smail/services/access"
appcontext "git.pcux.cn/xuans/Smail/utils/context"
)
// GetConfigAuditDetail 返回一条已脱敏的配置审计事件。
func GetConfigAuditDetail(ctx *appcontext.Context, w http.ResponseWriter, req *http.Request) {
manager, ok := configManagerOrError(w)
if !ok {
return
}
if err := authorizeCapability(ctx, access.CapabilityAuditRead, "", false); err != nil {
writeConfigAccessError(w, err)
return
}
parts := strings.Split(strings.Trim(req.URL.Path, "/"), "/")
if len(parts) != 5 || parts[0] != "api" || parts[1] != "v1" || parts[2] != "audits" || parts[3] != "config" || strings.TrimSpace(parts[4]) == "" {
writeConfigError(w, config.ErrInvalidAuditQuery)
return
}
event, err := manager.AuditDetail(req.Context(), parts[4])
if err != nil {
writeConfigError(w, err)
return
}
response.NewSuccessResponse(event).FPrint(w)
}
type configAuditExportRequest struct {
Format string `json:"format"`
ID string `json:"id"`
ActorID string `json:"actorId"`
Operation string `json:"operation"`
Section string `json:"section"`
Limit int `json:"limit"`
From string `json:"from"`
To string `json:"to"`
}
// ExportConfigAuditFormal 实现正式配置审计导出接口。
func ExportConfigAuditFormal(ctx *appcontext.Context, w http.ResponseWriter, req *http.Request) {
manager, ok := configManagerOrError(w)
if !ok {
return
}
if err := authorizeCapability(ctx, access.CapabilityAuditExport, "", false); err != nil {
writeConfigAccessError(w, err)
return
}
var input configAuditExportRequest
if err := decodeJSONRequest(w, req, &input); err != nil {
writeConfigError(w, err)
return
}
query, err := auditExportQuery(input)
if err != nil {
writeConfigError(w, err)
return
}
format := strings.ToLower(strings.TrimSpace(input.Format))
if format == "" {
format = "json"
}
if format != "json" && format != "csv" {
writeConfigError(w, config.ErrInvalidAuditFormat)
return
}
var data []byte
if format == "csv" {
data, err = manager.ExportAuditCSV(req.Context(), query)
} else {
data, err = manager.ExportAuditJSON(req.Context(), query)
}
if err != nil {
writeConfigError(w, err)
return
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Content-Disposition", `attachment; filename="config-audit.`+format+`"`)
if format == "csv" {
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
} else {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
}
_, _ = w.Write(data)
}
func auditExportQuery(input configAuditExportRequest) (config.ConfigAuditQuery, error) {
query := config.ConfigAuditQuery{
ID: strings.TrimSpace(input.ID),
ActorID: strings.TrimSpace(input.ActorID),
Operation: config.ConfigOperation(strings.TrimSpace(input.Operation)),
Section: config.Section(strings.TrimSpace(input.Section)),
Limit: input.Limit,
}
if raw := strings.TrimSpace(input.From); raw != "" {
value, err := time.Parse(time.RFC3339, raw)
if err != nil {
return config.ConfigAuditQuery{}, config.ErrInvalidAuditQuery
}
query.From = value
}
if raw := strings.TrimSpace(input.To); raw != "" {
value, err := time.Parse(time.RFC3339, raw)
if err != nil {
return config.ConfigAuditQuery{}, config.ErrInvalidAuditQuery
}
query.To = value
}
if err := config.ValidateConfigAuditQuery(query); err != nil {
return config.ConfigAuditQuery{}, err
}
return query, nil
}