Optimized audit, event logging system

 Audit logs
 Admin edit user permissions
This commit is contained in:
2024-07-03 22:57:17 +08:00
parent 5200e00c9b
commit 8c89d89382
7 changed files with 107 additions and 12 deletions

View File

@ -21,6 +21,7 @@ var AutoMaintainRange = []any{
&models.ActionEvent{},
&models.Notification{},
&models.NotificationSubscriber{},
&models.AuditRecord{},
}
func RunMigration(source *gorm.DB) error {

View File

@ -0,0 +1,13 @@
package models
import "gorm.io/datatypes"
type AuditRecord struct {
BaseModel
Action string `json:"action"`
Metadata datatypes.JSONMap `json:"metadata"`
UserAgent string `json:"user_agent"`
IpAddress string `json:"ip_address"`
AccountID uint `json:"account_id"`
}

View File

@ -11,5 +11,7 @@ func MapAdminAPIs(app *fiber.App) {
admin.Delete("/badges/:badgeId", revokeBadge)
admin.Post("/notify/all", notifyAllUser)
admin.Put("/users/:user/permissions", editUserPermission)
}
}

View File

@ -0,0 +1,46 @@
package admin
import (
"fmt"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
"github.com/gofiber/fiber/v2"
)
func editUserPermission(c *fiber.Ctx) error {
userId, _ := c.ParamsInt("user")
if err := exts.EnsureGrantedPerm(c, "AdminUserPermission", true); err != nil {
return err
}
operator := c.Locals("user").(models.Account)
var data struct {
PermNodes map[string]any `json:"perm_nodes" validate:"required"`
}
if err := exts.BindAndValidate(c, &data); err != nil {
return err
}
var user models.Account
if err := database.C.Where("id = ?", userId).First(&user).Error; err != nil {
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("account was not found: %v", err))
}
prev := user.PermNodes
user.PermNodes = data.PermNodes
if err := database.C.Save(&user).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} else {
services.AddAuditRecord(operator, "user.permissions.edit", c.IP(), c.Get(fiber.HeaderUserAgent), map[string]any{
"previous_permissions": prev,
"new_permissions": data.PermNodes,
})
}
return c.SendStatus(fiber.StatusOK)
}

View File

@ -3,18 +3,46 @@ package services
import (
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"github.com/rs/zerolog/log"
)
func AddEvent(user models.Account, event, target, ip, ua string) models.ActionEvent {
evt := models.ActionEvent{
var writeEventQueue []models.ActionEvent
var writeAuditQueue []models.AuditRecord
// AddEvent to keep operation logs by user themselves clear to query
func AddEvent(user models.Account, event, target, ip, ua string) {
writeEventQueue = append(writeEventQueue, models.ActionEvent{
Type: event,
Target: target,
IpAddress: ip,
UserAgent: ua,
AccountID: user.ID,
}
database.C.Save(&evt)
return evt
})
}
// AddAuditRecord to keep logs to make administrators' operations clear to query
func AddAuditRecord(operator models.Account, act, ip, ua string, metadata map[string]any) {
writeAuditQueue = append(writeAuditQueue, models.AuditRecord{
Action: act,
Metadata: metadata,
IpAddress: ip,
UserAgent: ua,
AccountID: operator.ID,
})
}
// SaveEventChanges runs every 60 seconds to save events / audits changes into database
func SaveEventChanges() {
if len(writeEventQueue) > 0 {
count := len(writeEventQueue)
database.C.CreateInBatches(writeEventQueue, min(count, 1000))
log.Info().Int("count", count).Msg("Saved action events changes into database...")
clear(writeEventQueue)
}
if len(writeAuditQueue) > 0 {
count := len(writeAuditQueue)
database.C.CreateInBatches(writeAuditQueue, min(count, 1000))
log.Info().Int("count", count).Msg("Saved audit records changes into database...")
clear(writeAuditQueue)
}
}