API: Improve audit log messages in the cluster endpoint handlers

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-10-20 14:29:40 +02:00
parent ced4be2e40
commit 252aff2a6b
6 changed files with 125 additions and 49 deletions

View File

@@ -29,6 +29,34 @@ The API package exposes PhotoPrisms HTTP endpoints via Gin handlers. Each fil
- Derive client IPs through `api.ClientIP` and extract bearer tokens with `header.BearerToken` or the helper setters. Use constant-time comparison for tokens and secrets.
- For downloads or proxy endpoints, validate URLs against allowed schemes (`http`, `https`) and reject private or loopback addresses unless explicitly required.
## Audit Logging
- Emit security events via `event.Audit*` (`AuditInfo`, `AuditWarn`, `AuditErr`, `AuditDebug`) and always build the slice as **Who → What → Outcome**.
- **Who:** `ClientIP(c)` followed by the most specific actor context (`"session %s"`, `"client %s"`, `"user %s"`).
- **What:** Resource constant plus action segments (for example, `string(acl.ResourceCluster)`, `"node %s"`). Place extra context such as counts or error placeholders in separate segments before the outcome.
- **Outcome:** End with a single token like `event.Succeeded`, `event.Failed`, or `authn.Denied`; nothing comes after it.
- Prefer existing helpers (`ClientIP`, `clean.Log`, `clean.LogQuote`, `clean.Error`) instead of formatting values manually, and avoid inline `=` expressions.
- Example patterns:
```go
event.AuditInfo([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"node %s",
event.Deleted,
}, s.RefID, uuid)
event.AuditErr([]string{
clientIp,
"session %s",
string(acl.ResourceCluster),
"download theme",
"%s",
event.Failed,
}, refID, clean.Error(err))
```
- See `specs/common/audit-logs.md` for the full conventions and additional examples that agents should follow.
## Swagger Documentation
- Annotate handlers with Swagger comments that include full `/api/v1/...` paths, request/response schemas, and security definitions. Only annotate routes that are externally accessible.

View File

@@ -50,11 +50,13 @@ func ClusterMetrics(router *gin.RouterGroup) {
counts[role]++
}
c.JSON(http.StatusOK, cluster.MetricsResponse{
resp := cluster.MetricsResponse{
UUID: conf.ClusterUUID(),
ClusterCIDR: conf.ClusterCIDR(),
Nodes: counts,
Time: time.Now().UTC().Format(time.RFC3339),
})
}
c.JSON(http.StatusOK, resp)
})
}

View File

@@ -108,7 +108,14 @@ func ClusterListNodes(router *gin.RouterGroup) {
resp := reg.BuildClusterNodes(page, opts)
// Audit list access.
event.AuditInfo([]string{ClientIP(c), "session %s", string(acl.ResourceCluster), "nodes", "list", event.Succeeded, "count=%d", "offset=%d", "returned=%d"}, s.RefID, count, offset, len(resp))
event.AuditDebug([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"list nodes",
"count %d offset %d returned %d",
event.Succeeded,
}, s.RefID, count, offset, len(resp))
c.JSON(http.StatusOK, resp)
})
@@ -166,7 +173,13 @@ func ClusterGetNode(router *gin.RouterGroup) {
resp := reg.BuildClusterNode(*n, opts)
// Audit get access.
event.AuditInfo([]string{ClientIP(c), "session %s", string(acl.ResourceCluster), "nodes", "get", uuid, event.Succeeded}, s.RefID)
event.AuditInfo([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"get node %s",
event.Succeeded,
}, s.RefID, uuid)
c.JSON(http.StatusOK, resp)
})
@@ -238,8 +251,9 @@ func ClusterUpdateNode(router *gin.RouterGroup) {
if req.AdvertiseUrl != "" {
n.AdvertiseUrl = req.AdvertiseUrl
}
if s := normalizeSiteURL(req.SiteUrl); s != "" {
n.SiteUrl = s
if u := normalizeSiteURL(req.SiteUrl); u != "" {
n.SiteUrl = u
}
n.UpdatedAt = time.Now().UTC().Format(time.RFC3339)
@@ -249,7 +263,14 @@ func ClusterUpdateNode(router *gin.RouterGroup) {
return
}
event.AuditInfo([]string{ClientIP(c), string(acl.ResourceCluster), "nodes", "update", uuid, event.Succeeded})
event.AuditInfo([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"node %s",
event.Updated,
}, s.RefID, uuid)
c.JSON(http.StatusOK, cluster.StatusResponse{Status: "ok"})
})
}
@@ -303,7 +324,14 @@ func ClusterDeleteNode(router *gin.RouterGroup) {
return
}
event.AuditInfo([]string{ClientIP(c), string(acl.ResourceCluster), "nodes", "delete", uuid, event.Succeeded})
event.AuditWarn([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"node %s",
event.Deleted,
}, s.RefID, uuid)
c.JSON(http.StatusOK, cluster.StatusResponse{Status: "ok"})
})
}

View File

@@ -53,7 +53,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
r := limiter.Auth.Request(clientIp)
if r.Reject() || limiter.Auth.Reject(clientIp) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "rate limit", event.Denied})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "rate limit exceeded", event.Denied})
limiter.AbortJSON(c)
return
}
@@ -63,7 +63,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
token := header.BearerToken(c)
if expected == "" || token == "" || subtle.ConstantTimeCompare([]byte(expected), []byte(token)) != 1 {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "auth", event.Denied})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid join token", event.Denied})
r.Success() // return reserved tokens; still unauthorized
AbortUnauthorized(c)
return
@@ -73,7 +73,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
var req cluster.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "form invalid", "%s"}, clean.Error(err))
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid form", "%s", event.Failed}, clean.Error(err))
AbortBadRequest(c)
return
}
@@ -85,13 +85,13 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// If an existing ClientID is provided, require the corresponding client secret for verification.
if RegisterRequireClientSecret && req.ClientID != "" {
if !rnd.IsUID(req.ClientID, entity.ClientUID) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid client id"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid client id", event.Failed})
AbortBadRequest(c)
return
}
pw := entity.FindPassword(req.ClientID)
if pw == nil || req.ClientSecret == "" || !pw.Valid(req.ClientSecret) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid client secret"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid client secret", event.Denied})
AbortUnauthorized(c)
return
}
@@ -101,14 +101,14 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// Enforce DNS label semantics for node names: lowercase [a-z0-9-], 132, start/end alnum.
if name == "" || len(name) > 32 || name[0] == '-' || name[len(name)-1] == '-' {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid name"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name", event.Failed})
AbortBadRequest(c)
return
}
for i := 0; i < len(name); i++ {
b := name[i]
if !(b == '-' || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid name chars"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid name chars", event.Failed})
AbortBadRequest(c)
return
}
@@ -117,7 +117,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// Validate advertise URL if provided (https required for non-local domains).
if u := strings.TrimSpace(req.AdvertiseUrl); u != "" {
if !validateAdvertiseURL(u) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid advertise url"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid advertise url", event.Failed})
AbortBadRequest(c)
return
}
@@ -126,7 +126,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// Validate site URL if provided (https required for non-local domains).
if su := strings.TrimSpace(req.SiteUrl); su != "" {
if !validateSiteURL(su) {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "invalid site url"})
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "invalid site url", event.Failed})
AbortBadRequest(c)
return
}
@@ -139,7 +139,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
regy, err := reg.NewClientRegistryWithConfig(conf)
if err != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register node", "registry", event.Failed, "%s"}, clean.Error(err))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register", "%s", event.Failed}, clean.Error(err))
AbortUnexpectedError(c)
return
}
@@ -154,7 +154,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// If caller attempts to change UUID by name without proving client secret, block with 409.
if RegisterRequireClientSecret {
if requestedUUID != "" && n.UUID != "" && requestedUUID != n.UUID && req.ClientID == "" {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "uuid change requires client secret", event.Denied}, clean.LogQuote(name))
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s uuid change requires client secret", event.Denied}, clean.Log(name))
c.JSON(http.StatusConflict, gin.H{"error": "client secret required to change node uuid"})
return
}
@@ -184,16 +184,16 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
if oldUUID != requestedUUID {
n.UUID = requestedUUID
// Emit audit event for UUID change.
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "change uuid", "old %s", "new %s", event.Succeeded}, clean.LogQuote(name), clean.Log(oldUUID), clean.Log(requestedUUID))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "change uuid old %s new %s", event.Updated}, clean.Log(name), clean.Log(oldUUID), clean.Log(requestedUUID))
}
} else if n.UUID == "" {
// Assign a fresh UUID if missing and none requested.
n.UUID = rnd.UUIDv7()
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "new uuid", "%s", event.Succeeded}, clean.LogQuote(name), clean.Log(n.UUID))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "assign uuid %s", event.Created}, clean.Log(name), clean.Log(n.UUID))
}
// Persist metadata changes so UpdatedAt advances.
if putErr := regy.Put(n); putErr != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.Log(name), clean.Error(putErr))
AbortUnexpectedError(c)
return
}
@@ -201,16 +201,16 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
var respSecret *cluster.RegisterSecrets
if req.RotateSecret {
if n, err = regy.RotateSecret(n.UUID); err != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", "%s", event.Failed}, clean.LogQuote(name), clean.Error(err))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", "%s", event.Failed}, clean.Log(name), clean.Error(err))
AbortUnexpectedError(c)
return
}
respSecret = &cluster.RegisterSecrets{ClientSecret: n.ClientSecret, RotatedAt: n.RotatedAt}
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate secret", event.Succeeded}, clean.LogQuote(name))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s rotate secret", event.Succeeded}, clean.Log(name))
// Extra safety: ensure the updated secret is persisted even if subsequent steps fail.
if putErr := regy.Put(n); putErr != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist rotated secret", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist rotated secret", "%s", event.Failed}, clean.Log(name), clean.Error(putErr))
AbortUnexpectedError(c)
return
}
@@ -225,7 +225,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
creds, _, credsErr = provisioner.EnsureCredentials(c, conf, n.UUID, name, req.RotateDatabase)
if credsErr != nil {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "ensure database", "%s", event.Failed}, clean.LogQuote(name), clean.Error(credsErr))
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "node %s", "ensure database", "%s", event.Failed}, clean.Log(name), clean.Error(credsErr))
c.JSON(http.StatusConflict, gin.H{"error": credsErr.Error()})
return
}
@@ -235,11 +235,11 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
n.Database.RotatedAt = creds.RotatedAt
n.Database.Driver = provisioner.DatabaseDriver
if putErr := regy.Put(n); putErr != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.LogQuote(name), clean.Error(putErr))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "node %s", "persist node", "%s", event.Failed}, clean.Log(name), clean.Error(putErr))
AbortUnexpectedError(c)
return
}
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "rotate db", event.Succeeded}, clean.LogQuote(name))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s rotate database", event.Succeeded}, clean.Log(name))
}
}
@@ -273,7 +273,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
resp.Database.RotatedAt = creds.RotatedAt
}
c.Header(header.CacheControl, header.CacheControlNoStore)
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "registration", event.Updated}, clean.LogQuote(name))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", event.Updated}, clean.Log(name))
c.JSON(http.StatusOK, resp)
return
}
@@ -315,7 +315,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
if shouldProvisionDB {
if creds, _, err = provisioner.EnsureCredentials(c, conf, n.UUID, name, true); err != nil {
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register node", "ensure database", "%s", event.Failed}, clean.Error(err))
event.AuditWarn([]string{clientIp, string(acl.ResourceCluster), "register", "ensure database", "%s", event.Failed}, clean.Error(err))
c.JSON(http.StatusConflict, gin.H{"error": err.Error()})
return
}
@@ -329,7 +329,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
}
if err = regy.Put(n); err != nil {
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register node", "persist node", "%s", event.Failed}, clean.Error(err))
event.AuditErr([]string{clientIp, string(acl.ResourceCluster), "register", "persist node", "%s", event.Failed}, clean.Error(err))
AbortUnexpectedError(c)
return
}
@@ -355,7 +355,7 @@ func ClusterNodesRegister(router *gin.RouterGroup) {
// When DB provisioning is skipped, leave Database fields zero-value.
c.Header(header.CacheControl, header.CacheControlNoStore)
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", "registration", event.Created}, clean.LogQuote(name))
event.AuditInfo([]string{clientIp, string(acl.ResourceCluster), "node %s", event.Created}, clean.Log(name))
c.JSON(http.StatusCreated, resp)
})
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/auth/acl"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/photoprism/get"
"github.com/photoprism/photoprism/internal/service/cluster"
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
@@ -51,14 +52,24 @@ func ClusterSummary(router *gin.RouterGroup) {
themeVersion = v
}
c.JSON(http.StatusOK, cluster.SummaryResponse{
resp := cluster.SummaryResponse{
UUID: conf.ClusterUUID(),
ClusterCIDR: conf.ClusterCIDR(),
Nodes: len(nodes),
Database: cluster.DatabaseInfo{Driver: conf.DatabaseDriverName(), Host: conf.DatabaseHost(), Port: conf.DatabasePort()},
Theme: themeVersion,
Time: time.Now().UTC().Format(time.RFC3339),
})
}
event.AuditDebug([]string{
ClientIP(c),
"session %s",
string(acl.ResourceCluster),
"get summary for cluster uuid %s",
event.Succeeded,
}, s.RefID, conf.ClusterUUID())
c.JSON(http.StatusOK, resp)
})
}
@@ -85,6 +96,13 @@ func ClusterHealth(router *gin.RouterGroup) {
return
}
event.AuditDebug([]string{
ClientIP(c),
string(acl.ResourceCluster),
"health check",
event.Succeeded,
})
c.JSON(http.StatusOK, NewHealthResponse("ok"))
})
}

View File

@@ -75,7 +75,7 @@ func ClusterGetTheme(router *gin.RouterGroup) {
// Resolve symbolic links.
if resolved, err := filepath.EvalSymlinks(themePath); err != nil {
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "%s"}, refID, clean.Error(err))
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme resolve", "%s", event.Failed}, refID, clean.Error(err))
AbortNotFound(c)
return
} else {
@@ -84,7 +84,7 @@ func ClusterGetTheme(router *gin.RouterGroup) {
// Check if theme path exists.
if !fs.PathExists(themePath) {
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "theme path not found"}, refID)
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme path", "not found"}, refID)
AbortNotFound(c)
return
}
@@ -93,17 +93,17 @@ func ClusterGetTheme(router *gin.RouterGroup) {
// This aligns with bootstrap behavior, which only installs a theme when
// app.js exists locally or can be fetched from the Portal.
if !fs.FileExistsNotEmpty(filepath.Join(themePath, "app.js")) {
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "app.js missing or empty"}, refID)
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme app.js", "not found"}, refID)
AbortNotFound(c)
return
}
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "creating theme archive from %s"}, refID, clean.Log(themePath))
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme create archive", "%s", "started"}, refID, clean.Log(themePath))
if version, err := theme.DetectVersion(themePath); err == nil {
updateNodeThemeVersion(conf, session, version, clientIp, refID)
} else {
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "version", "%s"}, refID, clean.Error(err))
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "detect theme version", "%s", event.Failed}, refID, clean.Error(err))
}
// Add response headers.
@@ -114,14 +114,14 @@ func ClusterGetTheme(router *gin.RouterGroup) {
zipWriter := zip.NewWriter(c.Writer)
defer func(w *zip.Writer) {
if closeErr := w.Close(); closeErr != nil {
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to close", "%s"}, refID, clean.Error(closeErr))
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme close", "%s", event.Failed}, refID, clean.Error(closeErr))
}
}(zipWriter)
err := filepath.WalkDir(themePath, func(filePath string, info gofs.DirEntry, walkErr error) error {
// Handle errors.
if walkErr != nil {
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to traverse theme path", "%s"}, refID, clean.Error(walkErr))
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme traverse", "%s", event.Failed}, refID, clean.Error(walkErr))
// If the error occurs on a directory, skip descending to avoid cascading errors.
if info != nil && info.IsDir() {
@@ -157,11 +157,11 @@ func ClusterGetTheme(router *gin.RouterGroup) {
// Get the relative file name to use as alias in the zip.
alias := filepath.ToSlash(fs.RelName(filePath, themePath))
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "adding %s to archive"}, refID, clean.Log(alias))
event.AuditDebug([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme add", "%s", event.Added}, refID, clean.Log(alias))
// Stream zipped file contents.
if zipErr := fs.ZipFile(zipWriter, filePath, alias, false); zipErr != nil {
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", "failed to add %s", "%s"}, refID, clean.Log(alias), clean.Error(zipErr))
event.AuditWarn([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme add %s", "%s", event.Failed}, refID, clean.Log(alias), clean.Error(zipErr))
}
return nil
@@ -169,9 +169,9 @@ func ClusterGetTheme(router *gin.RouterGroup) {
// Log result.
if err != nil {
event.AuditErr([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", event.Failed, "%s"}, refID, clean.Error(err))
event.AuditErr([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme", "%s", event.Failed}, refID, clean.Error(err))
} else {
event.AuditInfo([]string{clientIp, "session %s", string(acl.ResourceCluster), "theme", "download", event.Succeeded}, refID)
event.AuditInfo([]string{clientIp, "session %s", string(acl.ResourceCluster), "download theme", event.Succeeded}, refID)
}
})
}
@@ -198,7 +198,7 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio
regy, err := reg.NewClientRegistryWithConfig(conf)
if err != nil {
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "registry", "%s"}, refID, clean.Error(err))
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata registry", "%s", event.Failed}, refID, clean.Error(err))
return
}
@@ -217,7 +217,7 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio
}
if node == nil {
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "node not found"}, refID)
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata node", "skipped"}, refID)
return
}
@@ -228,9 +228,9 @@ func updateNodeThemeVersion(conf *config.Config, session *entity.Session, versio
node.Theme = normalized
if err = regy.Put(node); err != nil {
event.AuditWarn([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "%s"}, refID, clean.Error(err))
event.AuditWarn([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata", "%s", event.Failed}, refID, clean.Error(err))
return
}
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme", "metadata", "updated"}, refID)
event.AuditDebug([]string{clientIP, "session %s", string(acl.ResourceCluster), "theme metadata", event.Updated}, refID)
}