mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
CLI: Add audit logs to cluster management commands
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -8,7 +8,11 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/http/header"
|
||||
)
|
||||
|
||||
@@ -53,3 +57,23 @@ func obtainClientCredentialsViaRegister(portalURL, joinToken, nodeName string) (
|
||||
}
|
||||
return id, secret, nil
|
||||
}
|
||||
|
||||
// clusterAuditWho builds the leading audit log segments for CLI commands.
|
||||
func clusterAuditWho(ctx *cli.Context, conf *config.Config) []string {
|
||||
actor := clean.Log(conf.NodeName())
|
||||
if actor == "" {
|
||||
actor = clean.Log(conf.SiteUrl())
|
||||
}
|
||||
if actor == "" {
|
||||
actor = "cli"
|
||||
}
|
||||
|
||||
context := "cli"
|
||||
if ctx != nil && ctx.Command != nil {
|
||||
if full := strings.TrimSpace(ctx.Command.FullName()); full != "" {
|
||||
context = "cli " + full
|
||||
}
|
||||
}
|
||||
|
||||
return []string{actor, context}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/pkg/txt/report"
|
||||
)
|
||||
@@ -74,6 +76,13 @@ func clusterNodesListAction(ctx *cli.Context) error {
|
||||
opts := reg.NodeOpts{IncludeAdvertiseUrl: true, IncludeDatabase: true}
|
||||
out := reg.BuildClusterNodes(page, opts)
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
event.AuditInfo(append(who,
|
||||
string(acl.ResourceCluster),
|
||||
"list nodes count %d",
|
||||
event.Succeeded,
|
||||
), len(out))
|
||||
|
||||
if ctx.Bool("json") {
|
||||
b, _ := json.Marshal(out)
|
||||
fmt.Println(string(b))
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
)
|
||||
@@ -112,6 +114,29 @@ func clusterNodesModAction(ctx *cli.Context) error {
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
|
||||
nodeID := n.UUID
|
||||
if nodeID == "" {
|
||||
nodeID = n.Name
|
||||
}
|
||||
|
||||
changeSummary := strings.Join(changes, ", ")
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
segments := []string{
|
||||
string(acl.ResourceCluster),
|
||||
"update node %s",
|
||||
}
|
||||
args := []interface{}{clean.Log(nodeID)}
|
||||
|
||||
if changeSummary != "" {
|
||||
segments = append(segments, "%s")
|
||||
args = append(args, clean.Log(changeSummary))
|
||||
}
|
||||
|
||||
segments = append(segments, event.Updated)
|
||||
|
||||
event.AuditInfo(append(who, segments...), args...)
|
||||
|
||||
log.Infof("node %s has been updated", clean.LogQuote(n.Name))
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster/provisioner"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
@@ -109,6 +111,13 @@ func clusterNodesRemoveAction(ctx *cli.Context) error {
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
event.AuditInfo(append(who,
|
||||
string(acl.ResourceCluster),
|
||||
"node %s",
|
||||
event.Deleted,
|
||||
), clean.Log(uuid))
|
||||
|
||||
loggedDeletion := false
|
||||
|
||||
if dropDB {
|
||||
@@ -122,6 +131,11 @@ func clusterNodesRemoveAction(ctx *cli.Context) error {
|
||||
return cli.Exit(fmt.Errorf("failed to drop database credentials for node %s: %w", clean.Log(uuid), err), 1)
|
||||
}
|
||||
log.Infof("node %s database %s and user %s have been dropped", clean.Log(uuid), clean.Log(dbName), clean.Log(dbUser))
|
||||
event.AuditInfo(append(who,
|
||||
string(acl.ResourceCluster),
|
||||
"drop database %s user %s",
|
||||
event.Succeeded,
|
||||
), clean.Log(dbName), clean.Log(dbUser))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster/theme"
|
||||
@@ -170,6 +173,35 @@ func clusterNodesRotateAction(ctx *cli.Context) error {
|
||||
return cli.Exit(err, 1)
|
||||
}
|
||||
|
||||
nodeID := resp.Node.UUID
|
||||
if nodeID == "" {
|
||||
nodeID = resp.Node.Name
|
||||
}
|
||||
|
||||
rotatedParts := make([]string, 0, 2)
|
||||
if rotateDatabase {
|
||||
rotatedParts = append(rotatedParts, "database")
|
||||
}
|
||||
if rotateSecret {
|
||||
rotatedParts = append(rotatedParts, "secret")
|
||||
}
|
||||
|
||||
detail := strings.Join(rotatedParts, ", ")
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
segments := []string{
|
||||
string(acl.ResourceCluster),
|
||||
"rotate node %s",
|
||||
}
|
||||
args := []interface{}{clean.Log(nodeID)}
|
||||
if detail != "" {
|
||||
segments = append(segments, "%s")
|
||||
args = append(args, clean.Log(detail))
|
||||
}
|
||||
segments = append(segments, event.Succeeded)
|
||||
|
||||
event.AuditInfo(append(who, segments...), args...)
|
||||
|
||||
if ctx.Bool("json") {
|
||||
jb, _ := json.Marshal(resp)
|
||||
fmt.Println(string(jb))
|
||||
|
||||
@@ -6,7 +6,9 @@ import (
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
reg "github.com/photoprism/photoprism/internal/service/cluster/registry"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt/report"
|
||||
@@ -57,6 +59,13 @@ func clusterNodesShowAction(ctx *cli.Context) error {
|
||||
opts := reg.NodeOpts{IncludeAdvertiseUrl: true, IncludeDatabase: true}
|
||||
dto := reg.BuildClusterNode(*n, opts)
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
event.AuditInfo(append(who,
|
||||
string(acl.ResourceCluster),
|
||||
"show node %s",
|
||||
event.Succeeded,
|
||||
), clean.Log(dto.UUID))
|
||||
|
||||
if ctx.Bool("json") {
|
||||
b, _ := json.Marshal(dto)
|
||||
fmt.Println(string(b))
|
||||
|
||||
@@ -16,7 +16,9 @@ import (
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster"
|
||||
clusternode "github.com/photoprism/photoprism/internal/service/cluster/node"
|
||||
"github.com/photoprism/photoprism/internal/service/cluster/theme"
|
||||
@@ -293,6 +295,18 @@ func clusterRegisterAction(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
nodeID := resp.Node.UUID
|
||||
if nodeID == "" {
|
||||
nodeID = resp.Node.Name
|
||||
}
|
||||
|
||||
who := clusterAuditWho(ctx, conf)
|
||||
event.AuditInfo(append(who,
|
||||
string(acl.ResourceCluster),
|
||||
"register node %s",
|
||||
event.Succeeded,
|
||||
), clean.Log(nodeID))
|
||||
|
||||
// Optional persistence
|
||||
if ctx.Bool("write-config") {
|
||||
if err := persistRegisterResponse(conf, &resp); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user