diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index b1dfad2..c393935 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,10 +4,14 @@
-
+
+
+
-
-
+
+
+
+
@@ -153,7 +157,6 @@
-
@@ -178,7 +181,8 @@
-
+
+
true
diff --git a/pkg/internal/database/migrator.go b/pkg/internal/database/migrator.go
index 175938a..68a7133 100644
--- a/pkg/internal/database/migrator.go
+++ b/pkg/internal/database/migrator.go
@@ -21,6 +21,7 @@ var AutoMaintainRange = []any{
&models.ActionEvent{},
&models.Notification{},
&models.NotificationSubscriber{},
+ &models.AuditRecord{},
}
func RunMigration(source *gorm.DB) error {
diff --git a/pkg/internal/models/audit.go b/pkg/internal/models/audit.go
new file mode 100644
index 0000000..fac39eb
--- /dev/null
+++ b/pkg/internal/models/audit.go
@@ -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"`
+}
diff --git a/pkg/internal/server/admin/index.go b/pkg/internal/server/admin/index.go
index 46d95dc..b26b3ea 100644
--- a/pkg/internal/server/admin/index.go
+++ b/pkg/internal/server/admin/index.go
@@ -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)
}
}
diff --git a/pkg/internal/server/admin/permissions_api.go b/pkg/internal/server/admin/permissions_api.go
new file mode 100644
index 0000000..4db3148
--- /dev/null
+++ b/pkg/internal/server/admin/permissions_api.go
@@ -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)
+}
diff --git a/pkg/internal/services/events.go b/pkg/internal/services/events.go
index fa626b9..e2dba6e 100644
--- a/pkg/internal/services/events.go
+++ b/pkg/internal/services/events.go
@@ -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)
+ }
}
diff --git a/pkg/main.go b/pkg/main.go
index dae1fd4..d44e178 100644
--- a/pkg/main.go
+++ b/pkg/main.go
@@ -70,6 +70,7 @@ func main() {
quartz.AddFunc("@every 60s", services.RecycleAuthContext)
quartz.AddFunc("@every 60m", services.RecycleUnConfirmAccount)
quartz.AddFunc("@every 5m", services.KexCleanup)
+ quartz.AddFunc("@every 60s", services.SaveEventChanges)
quartz.Start()
// Messages