Initial QSfera import
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
SHELL := bash
|
||||
NAME := audit
|
||||
|
||||
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
|
||||
include ../../.bingo/Variables.mk
|
||||
endif
|
||||
|
||||
include ../../.make/default.mk
|
||||
include ../../.make/go.mk
|
||||
include ../../.make/release.mk
|
||||
include ../../.make/docs.mk
|
||||
@@ -0,0 +1,30 @@
|
||||
# Audit
|
||||
|
||||
The audit service logs all events of the system as an audit log. Per default, it will be logged to standard out, but can also be configured to a file output. Supported log formats are json or a minimal human-readable format.
|
||||
|
||||
With audit logs, you are able to prove compliance with corporate guidelines as well as to enable reporting and auditing of operations. The audit service takes note of actions conducted by users and administrators.
|
||||
|
||||
Example minimal format:
|
||||
```
|
||||
file_delete)
|
||||
user 'user_id' trashed file 'item_id'
|
||||
file_trash_delete)
|
||||
user 'user_id' removed file 'item_id' from trashbin
|
||||
```
|
||||
|
||||
Example json:
|
||||
```
|
||||
{"RemoteAddr":"","User":"user_id","URL":"","Method":"","UserAgent":"","Time":"","App":"admin_audit","Message":"user 'user_id' trashed file 'item_id'","Action":"file_delete","CLI":false,"Level":1,"Path":"path","Owner":"user_id","FileID":"item_id"}
|
||||
{"RemoteAddr":"","User":"user_id","URL":"","Method":"","UserAgent":"","Time":"","App":"admin_audit","Message":"user 'user_id' removed file 'item_id' from trashbin","Action":"file_trash_delete","CLI":false,"Level":1,"Path":"path","Owner":"user_id","FileID":"item_id"}
|
||||
```
|
||||
|
||||
The audit service is not started automatically when running as single binary started via `qsfera server` or when running as docker container and must be started and stopped manually on demand.
|
||||
|
||||
The audit service logs:
|
||||
|
||||
- File system operations
|
||||
(create/delete/move; including actions on the trash bin and versioning)
|
||||
- User management operations
|
||||
(creation/deletion of users)
|
||||
- Sharing operations
|
||||
(user/group sharing, sharing via link, changing permissions, calls to sharing API from clients)
|
||||
@@ -0,0 +1,18 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
func Health(cfg *config.Config) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "health",
|
||||
Short: "Check health status",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Not implemented
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/qsfera/server/pkg/clihelper"
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// GetCommands provides all commands for this service
|
||||
func GetCommands(cfg *config.Config) []*cobra.Command {
|
||||
return []*cobra.Command{
|
||||
// start this service
|
||||
Server(cfg),
|
||||
|
||||
// interaction with this service
|
||||
|
||||
// infos about this service
|
||||
Health(cfg),
|
||||
Version(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
// Execute is the entry point for the audit command.
|
||||
func Execute(cfg *config.Config) error {
|
||||
app := clihelper.DefaultApp(&cobra.Command{
|
||||
Use: "audit",
|
||||
Short: "starts audit service",
|
||||
})
|
||||
app.AddCommand(GetCommands(cfg)...)
|
||||
app.SetArgs(os.Args[1:])
|
||||
|
||||
return app.ExecuteContext(cfg.Context)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/signal"
|
||||
|
||||
"github.com/qsfera/server/pkg/config/configlog"
|
||||
"github.com/qsfera/server/pkg/generators"
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/pkg/runner"
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/qsfera/server/services/audit/pkg/config/parser"
|
||||
"github.com/qsfera/server/services/audit/pkg/server/debug"
|
||||
svc "github.com/qsfera/server/services/audit/pkg/service"
|
||||
"github.com/qsfera/server/services/audit/pkg/types"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events/stream"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Server is the entrypoint for the server command.
|
||||
func Server(cfg *config.Config) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "server",
|
||||
Short: fmt.Sprintf("start the %s service without runtime (unsupervised mode)", cfg.Service.Name),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return configlog.ReturnFatal(parser.ParseConfig(cfg))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var cancel context.CancelFunc
|
||||
if cfg.Context == nil {
|
||||
cfg.Context, cancel = signal.NotifyContext(context.Background(), runner.StopSignals...)
|
||||
defer cancel()
|
||||
}
|
||||
ctx := cfg.Context
|
||||
logger := log.Configure(cfg.Service.Name, cfg.Commons, cfg.LogLevel)
|
||||
gr := runner.NewGroup()
|
||||
|
||||
connName := generators.GenerateConnectionName(cfg.Service.Name, generators.NTypeBus)
|
||||
client, err := stream.NatsFromConfig(connName, false, stream.NatsConfig(cfg.Events))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
evts, err := events.Consume(client, "audit", types.RegisteredEvents()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we need an additional context for the audit server in order to
|
||||
// cancel it anytime
|
||||
svcCtx, svcCancel := context.WithCancel(ctx)
|
||||
defer svcCancel()
|
||||
|
||||
gr.Add(runner.New(cfg.Service.Name+".svc", func() error {
|
||||
svc.AuditLoggerFromConfig(svcCtx, cfg.Auditlog, evts, logger)
|
||||
return nil
|
||||
}, func() {
|
||||
svcCancel()
|
||||
}))
|
||||
|
||||
{
|
||||
debugServer, err := debug.Server(
|
||||
debug.Logger(logger),
|
||||
debug.Context(ctx),
|
||||
debug.Config(cfg),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Info().Err(err).Str("server", "debug").Msg("Failed to initialize server")
|
||||
return err
|
||||
}
|
||||
|
||||
gr.Add(runner.NewGolangHttpServerRunner(cfg.Service.Name+".debug", debugServer))
|
||||
}
|
||||
|
||||
grResults := gr.Run(ctx)
|
||||
|
||||
// return the first non-nil error found in the results
|
||||
for _, grResult := range grResults {
|
||||
if grResult.RunnerError != nil {
|
||||
return grResult.RunnerError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Version prints the service versions of all running instances.
|
||||
func Version(cfg *config.Config) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "print the version of this binary and the running service instances",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// not implemented
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qsfera/server/pkg/shared"
|
||||
)
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service
|
||||
Service Service `yaml:"-"`
|
||||
LogLevel string `yaml:"loglevel" env:"OC_LOG_LEVEL;AUDIT_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'." introductionVersion:"1.0.0"`
|
||||
Debug Debug `yaml:"debug"`
|
||||
|
||||
Events Events `yaml:"events"`
|
||||
Auditlog Auditlog `yaml:"auditlog"`
|
||||
|
||||
Context context.Context `yaml:"-"`
|
||||
}
|
||||
|
||||
// Events combines the configuration options for the event bus.
|
||||
type Events struct {
|
||||
Endpoint string `yaml:"endpoint" env:"OC_EVENTS_ENDPOINT;AUDIT_EVENTS_ENDPOINT" desc:"The address of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture." introductionVersion:"1.0.0"`
|
||||
Cluster string `yaml:"cluster" env:"OC_EVENTS_CLUSTER;AUDIT_EVENTS_CLUSTER" desc:"The clusterID of the event system. The event system is the message queuing service. It is used as message broker for the microservice architecture. Mandatory when using NATS as event system." introductionVersion:"1.0.0"`
|
||||
TLSInsecure bool `yaml:"tls_insecure" env:"OC_INSECURE;OC_EVENTS_TLS_INSECURE;AUDIT_EVENTS_TLS_INSECURE" desc:"Whether to verify the server TLS certificates." introductionVersion:"1.0.0"`
|
||||
TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OC_EVENTS_TLS_ROOT_CA_CERTIFICATE;AUDIT_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided AUDIT_EVENTS_TLS_INSECURE will be seen as false." introductionVersion:"1.0.0"`
|
||||
EnableTLS bool `yaml:"enable_tls" env:"OC_EVENTS_ENABLE_TLS;AUDIT_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the КуСфера service which receives and delivers events between the services." introductionVersion:"1.0.0"`
|
||||
AuthUsername string `yaml:"username" env:"OC_EVENTS_AUTH_USERNAME;AUDIT_EVENTS_AUTH_USERNAME" desc:"The username to authenticate with the events broker. The events broker is the КуСфера service which receives and delivers events between the services." introductionVersion:"1.0.0"`
|
||||
AuthPassword string `yaml:"password" env:"OC_EVENTS_AUTH_PASSWORD;AUDIT_EVENTS_AUTH_PASSWORD" desc:"The password to authenticate with the events broker. The events broker is the КуСфера service which receives and delivers events between the services." introductionVersion:"1.0.0"`
|
||||
}
|
||||
|
||||
// Auditlog holds audit log information
|
||||
type Auditlog struct {
|
||||
LogToConsole bool `yaml:"log_to_console" env:"AUDIT_LOG_TO_CONSOLE" desc:"Logs to stdout if set to 'true'. Independent of the LOG_TO_FILE option." introductionVersion:"1.0.0"`
|
||||
LogToFile bool `yaml:"log_to_file" env:"AUDIT_LOG_TO_FILE" desc:"Logs to file if set to 'true'. Independent of the LOG_TO_CONSOLE option." introductionVersion:"1.0.0"`
|
||||
FilePath string `yaml:"filepath" env:"AUDIT_FILEPATH" desc:"Filepath of the logfile. Mandatory if LOG_TO_FILE is set to 'true'." introductionVersion:"1.0.0"`
|
||||
Format string `yaml:"format" env:"AUDIT_FORMAT" desc:"Log format. Supported values are '' (empty) and 'json'. Using 'json' is advised, '' (empty) renders the 'minimal' format. See the text description for more details." introductionVersion:"1.0.0"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package config
|
||||
|
||||
// Debug defines the available debug configuration.
|
||||
type Debug struct {
|
||||
Addr string `yaml:"addr" env:"AUDIT_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed." introductionVersion:"1.0.0"`
|
||||
Token string `yaml:"token" env:"AUDIT_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint." introductionVersion:"1.0.0"`
|
||||
Pprof bool `yaml:"pprof" env:"AUDIT_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling." introductionVersion:"1.0.0"`
|
||||
Zpages bool `yaml:"zpages" env:"AUDIT_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing in-memory traces." introductionVersion:"1.0.0"`
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
)
|
||||
|
||||
// FullDefaultConfig returns a fully initialized default configuration
|
||||
func FullDefaultConfig() *config.Config {
|
||||
cfg := DefaultConfig()
|
||||
EnsureDefaults(cfg)
|
||||
Sanitize(cfg)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// DefaultConfig returns a basic default configuration
|
||||
func DefaultConfig() *config.Config {
|
||||
return &config.Config{
|
||||
Debug: config.Debug{
|
||||
Addr: "127.0.0.1:9229",
|
||||
Zpages: false,
|
||||
Pprof: false,
|
||||
},
|
||||
Service: config.Service{
|
||||
Name: "audit",
|
||||
},
|
||||
Events: config.Events{
|
||||
Endpoint: "127.0.0.1:9233",
|
||||
Cluster: "qsfera-cluster",
|
||||
EnableTLS: false,
|
||||
},
|
||||
Auditlog: config.Auditlog{
|
||||
LogToConsole: true,
|
||||
Format: "json",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureDefaults adds default values to the configuration if they are not set yet
|
||||
func EnsureDefaults(cfg *config.Config) {
|
||||
if cfg.LogLevel == "" {
|
||||
cfg.LogLevel = "error"
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize sanitized the configuration
|
||||
func Sanitize(cfg *config.Config) {
|
||||
// sanitize config
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
occfg "github.com/qsfera/server/pkg/config"
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/qsfera/server/services/audit/pkg/config/defaults"
|
||||
|
||||
"github.com/qsfera/server/pkg/config/envdecode"
|
||||
)
|
||||
|
||||
// ParseConfig loads configuration from known paths.
|
||||
func ParseConfig(cfg *config.Config) error {
|
||||
err := occfg.BindSourcesToStructs(cfg.Service.Name, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaults.EnsureDefaults(cfg)
|
||||
|
||||
// load all env variables relevant to the config in the current context.
|
||||
if err := envdecode.Decode(cfg); err != nil {
|
||||
// no environment variable set for this config is an expected "error"
|
||||
if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defaults.Sanitize(cfg)
|
||||
|
||||
return Validate(cfg)
|
||||
}
|
||||
|
||||
// Validate validates the configuration
|
||||
func Validate(cfg *config.Config) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package config
|
||||
|
||||
// Service defines the available service configuration.
|
||||
type Service struct {
|
||||
Name string `yaml:"-"`
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Context context.Context
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
func newOptions(opts ...Option) Options {
|
||||
opt := Options{}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&opt)
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Context provides a function to set the context option.
|
||||
func Context(val context.Context) Option {
|
||||
return func(o *Options) {
|
||||
o.Context = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/qsfera/server/pkg/checks"
|
||||
"github.com/qsfera/server/pkg/handlers"
|
||||
"github.com/qsfera/server/pkg/service/debug"
|
||||
"github.com/qsfera/server/pkg/version"
|
||||
)
|
||||
|
||||
// Server initializes the debug service and server.
|
||||
func Server(opts ...Option) (*http.Server, error) {
|
||||
options := newOptions(opts...)
|
||||
|
||||
readyHandlerConfiguration := handlers.NewCheckHandlerConfiguration().
|
||||
WithLogger(options.Logger).
|
||||
WithCheck("nats reachability", checks.NewNatsCheck(options.Config.Events.Endpoint))
|
||||
|
||||
return debug.NewService(
|
||||
debug.Logger(options.Logger),
|
||||
debug.Name(options.Config.Service.Name),
|
||||
debug.Version(version.GetString()),
|
||||
debug.Address(options.Config.Debug.Addr),
|
||||
debug.Token(options.Config.Debug.Token),
|
||||
debug.Pprof(options.Config.Debug.Pprof),
|
||||
debug.Zpages(options.Config.Debug.Zpages),
|
||||
debug.Ready(handlers.NewCheckHandler(readyHandlerConfiguration)),
|
||||
), nil
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/services/audit/pkg/config"
|
||||
"github.com/qsfera/server/services/audit/pkg/types"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
)
|
||||
|
||||
// Log is used to log to different outputs
|
||||
type Log func([]byte)
|
||||
|
||||
// Marshaller is used to marshal events
|
||||
type Marshaller func(any) ([]byte, error)
|
||||
|
||||
// AuditLoggerFromConfig will start a new AuditLogger generated from the config
|
||||
func AuditLoggerFromConfig(ctx context.Context, cfg config.Auditlog, ch <-chan events.Event, log log.Logger) {
|
||||
var logs []Log
|
||||
|
||||
if cfg.LogToConsole {
|
||||
logs = append(logs, WriteToStdout())
|
||||
}
|
||||
|
||||
if cfg.LogToFile {
|
||||
logs = append(logs, WriteToFile(cfg.FilePath, log))
|
||||
}
|
||||
|
||||
StartAuditLogger(ctx, ch, log, Marshal(cfg.Format, log), logs...)
|
||||
|
||||
}
|
||||
|
||||
// StartAuditLogger will block. run in separate go routine
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func StartAuditLogger(ctx context.Context, ch <-chan events.Event, log log.Logger, marshaller Marshaller, logto ...Log) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case i, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var auditEvent any
|
||||
switch ev := i.Event.(type) {
|
||||
case events.ShareCreated:
|
||||
auditEvent = types.ShareCreated(ev)
|
||||
case events.LinkCreated:
|
||||
auditEvent = types.LinkCreated(ev)
|
||||
case events.ShareUpdated:
|
||||
auditEvent = types.ShareUpdated(ev)
|
||||
case events.LinkUpdated:
|
||||
auditEvent = types.LinkUpdated(ev)
|
||||
case events.ShareRemoved:
|
||||
auditEvent = types.ShareRemoved(ev)
|
||||
case events.LinkRemoved:
|
||||
auditEvent = types.LinkRemoved(ev)
|
||||
case events.ReceivedShareUpdated:
|
||||
auditEvent = types.ReceivedShareUpdated(ev)
|
||||
case events.LinkAccessed:
|
||||
auditEvent = types.LinkAccessed(ev)
|
||||
case events.LinkAccessFailed:
|
||||
auditEvent = types.LinkAccessFailed(ev)
|
||||
case events.ContainerCreated:
|
||||
auditEvent = types.ContainerCreated(ev)
|
||||
case events.FileUploaded:
|
||||
auditEvent = types.FileUploaded(ev)
|
||||
case events.FileDownloaded:
|
||||
auditEvent = types.FileDownloaded(ev)
|
||||
case events.ItemMoved:
|
||||
auditEvent = types.ItemMoved(ev)
|
||||
case events.ItemTrashed:
|
||||
auditEvent = types.ItemTrashed(ev)
|
||||
case events.ItemPurged:
|
||||
auditEvent = types.ItemPurged(ev)
|
||||
case events.ItemRestored:
|
||||
auditEvent = types.ItemRestored(ev)
|
||||
case events.FileVersionRestored:
|
||||
auditEvent = types.FileVersionRestored(ev)
|
||||
case events.SpaceCreated:
|
||||
auditEvent = types.SpaceCreated(ev)
|
||||
case events.SpaceRenamed:
|
||||
auditEvent = types.SpaceRenamed(ev)
|
||||
case events.SpaceDisabled:
|
||||
auditEvent = types.SpaceDisabled(ev)
|
||||
case events.SpaceEnabled:
|
||||
auditEvent = types.SpaceEnabled(ev)
|
||||
case events.SpaceDeleted:
|
||||
auditEvent = types.SpaceDeleted(ev)
|
||||
case events.SpaceShared:
|
||||
auditEvent = types.SpaceShared(ev)
|
||||
case events.SpaceUnshared:
|
||||
auditEvent = types.SpaceUnshared(ev)
|
||||
case events.SpaceUpdated:
|
||||
auditEvent = types.SpaceUpdated(ev)
|
||||
case events.UserCreated:
|
||||
auditEvent = types.UserCreated(ev)
|
||||
case events.UserDeleted:
|
||||
auditEvent = types.UserDeleted(ev)
|
||||
case events.UserFeatureChanged:
|
||||
auditEvent = types.UserFeatureChanged(ev)
|
||||
case events.GroupCreated:
|
||||
auditEvent = types.GroupCreated(ev)
|
||||
case events.GroupDeleted:
|
||||
auditEvent = types.GroupDeleted(ev)
|
||||
case events.GroupMemberAdded:
|
||||
auditEvent = types.GroupMemberAdded(ev)
|
||||
case events.GroupMemberRemoved:
|
||||
auditEvent = types.GroupMemberRemoved(ev)
|
||||
case events.ScienceMeshInviteTokenGenerated:
|
||||
auditEvent = types.ScienceMeshInviteTokenGenerated(ev)
|
||||
default:
|
||||
log.Error().Interface("event", ev).Msg(fmt.Sprintf("can't handle event of type '%T'", ev))
|
||||
if ctx.Err() != nil {
|
||||
// if context is done, do not process more events
|
||||
return
|
||||
}
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
b, err := marshaller(auditEvent)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error marshaling the event")
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for _, l := range logto {
|
||||
l(b)
|
||||
}
|
||||
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// WriteToFile returns a Log function writing to a file
|
||||
func WriteToFile(path string, log log.Logger) Log {
|
||||
return func(content []byte) {
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("error opening file '%s'", path)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err := fmt.Fprintln(file, string(content)); err != nil {
|
||||
log.Error().Err(err).Msgf("error writing to file '%s'", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteToStdout return a Log function writing to Stdout
|
||||
func WriteToStdout() Log {
|
||||
return func(content []byte) {
|
||||
fmt.Println(string(content))
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal returns a Marshaller from the `format` string
|
||||
func Marshal(format string, log log.Logger) Marshaller {
|
||||
switch format {
|
||||
default:
|
||||
log.Error().Msgf("unknown format '%s'", format)
|
||||
return nil
|
||||
case "json":
|
||||
return json.Marshal
|
||||
case "minimal":
|
||||
return func(ev any) ([]byte, error) {
|
||||
b, err := json.Marshal(ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := make(map[string]any)
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format := fmt.Sprintf("%s)\n %s", m["Action"], m["Message"])
|
||||
return []byte(format), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,724 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/services/audit/pkg/types"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
|
||||
group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
rtypes "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
)
|
||||
|
||||
var testCases = []struct {
|
||||
Alias string
|
||||
SystemEvent events.Event
|
||||
CheckAuditEvent func(*testing.T, []byte)
|
||||
}{
|
||||
{
|
||||
Alias: "ShareCreated - user",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ShareCreated{
|
||||
Sharer: userID("sharing-userid"),
|
||||
GranteeUserID: userID("beshared-userid"),
|
||||
GranteeGroupID: nil,
|
||||
ItemID: resourceID("provider-1", "storage-1", "itemid-1"),
|
||||
CTime: timestamp(0),
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareCreated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "1970-01-01T00:00:00Z", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-userid'", "file_shared")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "")
|
||||
// AuditEventShareCreated fields
|
||||
require.Equal(t, "", ev.ItemType)
|
||||
require.Equal(t, "", ev.ExpirationDate)
|
||||
require.Equal(t, false, ev.SharePass)
|
||||
//require.Equal(t, "stat:true ", ev.Permissions) // TODO: BUG! Should work
|
||||
require.Equal(t, "user", ev.ShareType)
|
||||
require.Equal(t, "beshared-userid", ev.ShareWith)
|
||||
require.Equal(t, "sharing-userid", ev.ShareOwner)
|
||||
require.Equal(t, "", ev.ShareToken)
|
||||
},
|
||||
}, {
|
||||
Alias: "ShareCreated - group",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ShareCreated{
|
||||
Sharer: userID("sharing-userid"),
|
||||
GranteeUserID: nil,
|
||||
GranteeGroupID: groupID("beshared-groupid"),
|
||||
ItemID: resourceID("provider-1", "storage-1", "itemid-1"),
|
||||
CTime: timestamp(10e8),
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareCreated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' shared file 'itemid-1' with 'beshared-groupid'", "file_shared")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "")
|
||||
// AuditEventShareCreated fields
|
||||
require.Equal(t, "", ev.ItemType)
|
||||
require.Equal(t, "", ev.ExpirationDate)
|
||||
require.Equal(t, false, ev.SharePass)
|
||||
//require.Equal(t, "stat:true ", ev.Permissions) // TODO: BUG! Should work
|
||||
require.Equal(t, "group", ev.ShareType)
|
||||
require.Equal(t, "beshared-groupid", ev.ShareWith)
|
||||
require.Equal(t, "sharing-userid", ev.ShareOwner)
|
||||
require.Equal(t, "", ev.ShareToken)
|
||||
|
||||
},
|
||||
}, {
|
||||
Alias: "ShareUpdated",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ShareUpdated{
|
||||
ShareID: shareID("shareid"),
|
||||
Sharer: userID("sharing-userid"),
|
||||
GranteeUserID: nil,
|
||||
GranteeGroupID: groupID("beshared-groupid"),
|
||||
ItemID: resourceID("provider-1", "storage-1", "itemid-1"),
|
||||
Permissions: sharePermissions("stat", "get_quota"),
|
||||
MTime: timestamp(10e8),
|
||||
Updated: "permissions",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareUpdated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' updated field 'permissions' of share 'shareid'", "share_permission_updated")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "", ev.ExpirationDate) // no expiration for shares
|
||||
require.Equal(t, false, ev.SharePass)
|
||||
require.Equal(t, "get_quota:true stat:true", ev.Permissions)
|
||||
require.Equal(t, "group", ev.ShareType)
|
||||
require.Equal(t, "beshared-groupid", ev.ShareWith)
|
||||
require.Equal(t, "sharing-userid", ev.ShareOwner)
|
||||
require.Equal(t, "", ev.ShareToken) // token not filled for shares
|
||||
},
|
||||
}, {
|
||||
Alias: "LinkUpdated - permissions",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.LinkUpdated{
|
||||
ShareID: linkID("shareid"),
|
||||
Sharer: userID("sharing-userid"),
|
||||
ItemID: resourceID("provider-1", "storage-1", "itemid-1"),
|
||||
Permissions: linkPermissions("stat"),
|
||||
MTime: timestamp(10e8),
|
||||
DisplayName: "link",
|
||||
Expiration: timestamp(10e8 + 10e5),
|
||||
PasswordProtected: true,
|
||||
Token: "token-123",
|
||||
FieldUpdated: "permissions",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareUpdated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "user 'sharing-userid' updated field 'permissions' of public link 'shareid'", "share_permission_updated")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "2001-09-20T15:33:20Z", ev.ExpirationDate)
|
||||
require.Equal(t, true, ev.SharePass)
|
||||
require.Equal(t, "stat:true", ev.Permissions)
|
||||
require.Equal(t, "link", ev.ShareType)
|
||||
require.Equal(t, "", ev.ShareWith) // not filled on links
|
||||
require.Equal(t, "sharing-userid", ev.ShareOwner)
|
||||
require.Equal(t, "token-123", ev.ShareToken)
|
||||
},
|
||||
}, {
|
||||
Alias: "ShareRemoved",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ShareRemoved{
|
||||
ShareID: shareID("shareid"),
|
||||
ShareKey: nil,
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareRemoved{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "0001-01-01T00:00:00Z", "share id:'shareid' uid:'' item-id:'' was removed", "file_unshared")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "", ev.ShareType)
|
||||
require.Equal(t, "", ev.ShareWith) // not filled on links
|
||||
},
|
||||
}, {
|
||||
Alias: "LinkRemoved - id",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.LinkRemoved{
|
||||
Executant: userID("sharing-userid"),
|
||||
ShareID: linkID("shareid"),
|
||||
ShareToken: "",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareRemoved{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "", "user 'sharing-userid' removed public link with id:'shareid'", "file_unshared")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "link", ev.ShareType)
|
||||
require.Equal(t, "", ev.ShareWith) // not filled on links
|
||||
},
|
||||
}, {
|
||||
Alias: "LinkRemoved - token",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.LinkRemoved{
|
||||
Executant: userID("sharing-userid"),
|
||||
ShareID: nil,
|
||||
ShareToken: "token-123",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventShareRemoved{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "", "user 'sharing-userid' removed public link with id:'token-123'", "file_unshared")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "", "sharing-userid", "token-123")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "link", ev.ShareType)
|
||||
require.Equal(t, "", ev.ShareWith) // not filled on links
|
||||
},
|
||||
}, {
|
||||
Alias: "Share accepted",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ReceivedShareUpdated{
|
||||
ShareID: shareID("shareid"),
|
||||
ItemID: resourceID("provider-1", "storageid-1", "itemid-1"),
|
||||
Permissions: sharePermissions("get_quota"),
|
||||
GranteeUserID: userID("beshared-userid"),
|
||||
GranteeGroupID: nil,
|
||||
Sharer: userID("sharing-userid"),
|
||||
MTime: timestamp(10e8),
|
||||
State: "SHARE_STATE_ACCEPTED",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventReceivedShareUpdated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T01:46:40Z", "user 'beshared-userid' accepted share 'shareid' from user 'sharing-userid'", "share_accepted")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType)
|
||||
require.Equal(t, "user", ev.ShareType)
|
||||
require.Equal(t, "beshared-userid", ev.ShareWith)
|
||||
},
|
||||
}, {
|
||||
Alias: "Share declined",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ReceivedShareUpdated{
|
||||
ShareID: shareID("shareid"),
|
||||
ItemID: resourceID("provider-1", "storageid-1", "itemid-1"),
|
||||
Permissions: sharePermissions("get_quota"),
|
||||
GranteeUserID: userID("beshared-userid"),
|
||||
GranteeGroupID: nil,
|
||||
Sharer: userID("sharing-userid"),
|
||||
MTime: timestamp(10e8),
|
||||
State: "SHARE_STATE_DECLINED",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventReceivedShareUpdated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "beshared-userid", "2001-09-09T01:46:40Z", "user 'beshared-userid' declined share 'shareid' from user 'sharing-userid'", "share_declined")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType)
|
||||
require.Equal(t, "user", ev.ShareType)
|
||||
require.Equal(t, "beshared-userid", ev.ShareWith)
|
||||
},
|
||||
}, {
|
||||
Alias: "Link accessed - success",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.LinkAccessed{
|
||||
ShareID: linkID("shareid"),
|
||||
Sharer: userID("sharing-userid"),
|
||||
ItemID: resourceID("provider-1", "storage-1", "itemid-1"),
|
||||
Permissions: linkPermissions("stat"),
|
||||
DisplayName: "link",
|
||||
Expiration: timestamp(10e8 + 10e5),
|
||||
PasswordProtected: true,
|
||||
CTime: timestamp(10e8),
|
||||
Token: "token-123",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventLinkAccessed{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharing-userid", "2001-09-09T01:46:40Z", "link with token 'token-123' was accessed. Success: true", "public_link_accessed")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "itemid-1", "sharing-userid", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "token-123", ev.ShareToken)
|
||||
require.Equal(t, true, ev.Success)
|
||||
},
|
||||
}, {
|
||||
Alias: "Link accessed - failure",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.LinkAccessFailed{
|
||||
ShareID: linkID("shareid"),
|
||||
Token: "token-123",
|
||||
Status: 8,
|
||||
Message: "access denied",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventLinkAccessed{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "", "link with token 'token-123' was accessed. Success: false", "public_link_accessed")
|
||||
// AuditEventSharing fields
|
||||
checkSharingAuditEvent(t, ev.AuditEventSharing, "", "", "shareid")
|
||||
// AuditEventShareUpdated fields
|
||||
require.Equal(t, "", ev.ItemType) // not implemented atm
|
||||
require.Equal(t, "token-123", ev.ShareToken)
|
||||
require.Equal(t, false, ev.Success)
|
||||
},
|
||||
}, {
|
||||
Alias: "File created",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.FileUploaded{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileCreated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' created file 'pro-1$sto-123!iid-123/item'", "file_create")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
},
|
||||
}, {
|
||||
Alias: "File read",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.FileDownloaded{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileRead{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' read file 'pro-1$sto-123!iid-123/item'", "file_read")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
},
|
||||
}, {
|
||||
Alias: "File trashed",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ItemTrashed{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileDeleted{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' trashed file 'pro-1$sto-123!iid-123/item'", "file_delete")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
},
|
||||
}, {
|
||||
Alias: "File renamed",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ItemMoved{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
OldReference: reference("pro-1", "sto-123", "iid-123", "./anotheritem"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileRenamed{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' moved file 'pro-1$sto-123!iid-123/item' from './anotheritem' to './item'", "file_rename")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
// AuditEventFileRenamed fields
|
||||
require.Equal(t, "./anotheritem", ev.OldPath)
|
||||
|
||||
},
|
||||
}, {
|
||||
Alias: "File purged",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ItemPurged{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFilePurged{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' removed file 'pro-1$sto-123!iid-123/item' from trashbin", "file_trash_delete")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
},
|
||||
}, {
|
||||
Alias: "File restored",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ItemRestored{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
OldReference: reference("pro-1", "sto-123", "sto-123!iid-123/item", "./oldpath"),
|
||||
Key: "",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileRestored{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' restored file 'pro-1$sto-123!iid-123/item' from trashbin to './item'", "file_trash_restore")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
// AuditEventFileRestored fields
|
||||
require.Equal(t, "./oldpath", ev.OldPath)
|
||||
|
||||
},
|
||||
}, {
|
||||
Alias: "File version restored",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.FileVersionRestored{
|
||||
Executant: userID("uid-123"),
|
||||
Ref: reference("pro-1", "sto-123", "iid-123", "./item"),
|
||||
Owner: userID("uid-123"), // NOTE: owner not yet implemented in reva
|
||||
Key: "v1",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventFileVersionRestored{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "uid-123", "", "user 'uid-123' restored file 'pro-1$sto-123!iid-123/item' in version 'v1'", "file_version_restore")
|
||||
// AuditEventSharing fields
|
||||
checkFilesAuditEvent(t, ev.AuditEventFiles, "pro-1$sto-123!iid-123/item", "uid-123", "./item")
|
||||
// AuditEventFileRestored fields
|
||||
require.Equal(t, "v1", ev.Key)
|
||||
|
||||
},
|
||||
}, {
|
||||
Alias: "Space created",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.SpaceCreated{
|
||||
Executant: userID("uid-123"),
|
||||
ID: &provider.StorageSpaceId{OpaqueId: "storage-1$space-123"},
|
||||
Owner: userID("uid-123"),
|
||||
Root: resourceID("pro-1", "sto-123", "iid-123"),
|
||||
Name: "test-space",
|
||||
Type: "project",
|
||||
Quota: nil, // Quota not interesting atm
|
||||
MTime: timestamp(10e9),
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventSpaceCreated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "2286-11-20T17:46:40Z", "user 'uid-123' created a space 'space-123' with name 'test-space' (storage: 'storage-1')", "space_created")
|
||||
// AuditEventSpaces fields
|
||||
checkSpacesAuditEvent(t, ev.AuditEventSpaces, "storage-1$space-123")
|
||||
// AuditEventFileRestored fields
|
||||
require.Equal(t, "uid-123", ev.Owner)
|
||||
require.Equal(t, "pro-1$sto-123!iid-123", ev.RootItem)
|
||||
require.Equal(t, "test-space", ev.Name)
|
||||
require.Equal(t, "project", ev.Type)
|
||||
},
|
||||
}, {
|
||||
Alias: "Space renamed",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.SpaceRenamed{
|
||||
Executant: userID("uid-123"),
|
||||
ID: &provider.StorageSpaceId{OpaqueId: "storage-1$space-123"},
|
||||
Owner: userID("uid-123"),
|
||||
Name: "new-name",
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventSpaceRenamed{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "", "user 'uid-123' renamed space 'space-123' to 'new-name' (storage: 'storage-1')", "space_renamed")
|
||||
// AuditEventSpaces fields
|
||||
checkSpacesAuditEvent(t, ev.AuditEventSpaces, "storage-1$space-123")
|
||||
// AuditEventSpaceRenamed fields
|
||||
require.Equal(t, "new-name", ev.NewName)
|
||||
},
|
||||
}, {
|
||||
Alias: "Space disabled",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.SpaceDisabled{
|
||||
Executant: userID("uid-123"),
|
||||
ID: &provider.StorageSpaceId{OpaqueId: "storage-1$space-123"},
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventSpaceDisabled{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "0001-01-01T00:00:00Z", "user 'uid-123' disabled the space 'space-123' (storage: 'storage-1')", "space_disabled")
|
||||
// AuditEventSpaces fields
|
||||
checkSpacesAuditEvent(t, ev.AuditEventSpaces, "storage-1$space-123")
|
||||
},
|
||||
}, {
|
||||
Alias: "Space enabled",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.SpaceEnabled{
|
||||
Executant: userID("uid-123"),
|
||||
ID: &provider.StorageSpaceId{OpaqueId: "storage-1$space-123"},
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventSpaceEnabled{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "", "user 'uid-123' (re-) enabled the space 'space-123' (storage: 'storage-1')", "space_enabled")
|
||||
// AuditEventSpaces fields
|
||||
checkSpacesAuditEvent(t, ev.AuditEventSpaces, "storage-1$space-123")
|
||||
},
|
||||
}, {
|
||||
Alias: "Space deleted",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.SpaceDeleted{
|
||||
Executant: userID("uid-123"),
|
||||
ID: &provider.StorageSpaceId{OpaqueId: "storage-1$space-123"},
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventSpaceDeleted{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "", "0001-01-01T00:00:00Z", "user 'uid-123' deleted the space 'space-123' (storage: 'storage-1')", "space_deleted")
|
||||
// AuditEventSpaces fields
|
||||
checkSpacesAuditEvent(t, ev.AuditEventSpaces, "storage-1$space-123")
|
||||
},
|
||||
},
|
||||
{
|
||||
Alias: "ScienceMesh - InviteTokenGenerated",
|
||||
SystemEvent: events.Event{
|
||||
Event: events.ScienceMeshInviteTokenGenerated{
|
||||
Sharer: userID("sharer-user-id"),
|
||||
RecipientMail: "mail@qsfera.test",
|
||||
Token: "token-123",
|
||||
Description: "some-description",
|
||||
Expiration: uint64(10e8),
|
||||
InviteLink: "http://qsfera.test/invite",
|
||||
Timestamp: timestamp(10e8),
|
||||
},
|
||||
},
|
||||
CheckAuditEvent: func(t *testing.T, b []byte) {
|
||||
ev := types.AuditEventScienceMeshInviteTokenGenerated{}
|
||||
require.NoError(t, json.Unmarshal(b, &ev))
|
||||
|
||||
// AuditEvent fields
|
||||
checkBaseAuditEvent(t, ev.AuditEvent, "sharer-user-id", "2001-09-09T01:46:40Z", "user 'sharer-user-id' generated a ScienceMesh invite with token 'token-123'", "science_mesh_invite_token_generated")
|
||||
// AuditEventScienceMeshInviteTokenGenerated fields
|
||||
require.Equal(t, "mail@qsfera.test", ev.RecipientMail)
|
||||
require.Equal(t, "token-123", ev.Token)
|
||||
require.Equal(t, "some-description", ev.Description)
|
||||
require.Equal(t, uint64(10e8), ev.Expiration)
|
||||
require.Equal(t, "http://qsfera.test/invite", ev.InviteLink)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAuditLogging(t *testing.T) {
|
||||
l := log.NewLogger()
|
||||
|
||||
inch := make(chan events.Event)
|
||||
defer close(inch)
|
||||
|
||||
outch := make(chan []byte)
|
||||
defer close(outch)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
|
||||
go StartAuditLogger(ctx, inch, l, Marshal("json", l), func(b []byte) {
|
||||
outch <- b
|
||||
})
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.Alias, func(t *testing.T) {
|
||||
inch <- tc.SystemEvent
|
||||
tc.CheckAuditEvent(t, <-outch)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func checkBaseAuditEvent(t *testing.T, ev types.AuditEvent, user string, time string, message string, action string) {
|
||||
require.Equal(t, "", ev.RemoteAddr) // not implemented atm
|
||||
require.Equal(t, user, ev.User)
|
||||
require.Equal(t, "", ev.URL) // not implemented atm
|
||||
require.Equal(t, "", ev.Method) // not implemented atm
|
||||
require.Equal(t, "", ev.UserAgent) // not implemented atm
|
||||
require.Equal(t, time, ev.Time)
|
||||
require.Equal(t, "admin_audit", ev.App)
|
||||
require.Equal(t, message, ev.Message)
|
||||
require.Equal(t, action, ev.Action)
|
||||
require.Equal(t, false, ev.CLI) // not implemented atm
|
||||
require.Equal(t, 1, ev.Level)
|
||||
}
|
||||
|
||||
func checkSharingAuditEvent(t *testing.T, ev types.AuditEventSharing, itemID string, owner string, shareID string) {
|
||||
require.Equal(t, itemID, ev.FileID)
|
||||
require.Equal(t, owner, ev.Owner)
|
||||
require.Equal(t, "", ev.Path) // not implemented atm
|
||||
require.Equal(t, shareID, ev.ShareID)
|
||||
}
|
||||
|
||||
func checkFilesAuditEvent(t *testing.T, ev types.AuditEventFiles, itemID string, owner string, path string) {
|
||||
require.Equal(t, itemID, ev.FileID)
|
||||
require.Equal(t, owner, ev.Owner)
|
||||
require.Equal(t, path, ev.Path)
|
||||
}
|
||||
|
||||
func checkSpacesAuditEvent(t *testing.T, ev types.AuditEventSpaces, spaceID string) {
|
||||
require.Equal(t, spaceID, ev.SpaceID)
|
||||
}
|
||||
func shareID(id string) *collaboration.ShareId {
|
||||
return &collaboration.ShareId{
|
||||
OpaqueId: id,
|
||||
}
|
||||
}
|
||||
|
||||
func linkID(id string) *link.PublicShareId {
|
||||
return &link.PublicShareId{
|
||||
OpaqueId: id,
|
||||
}
|
||||
}
|
||||
|
||||
func userID(id string) *user.UserId {
|
||||
return &user.UserId{
|
||||
OpaqueId: id,
|
||||
Idp: "idp",
|
||||
}
|
||||
}
|
||||
|
||||
func groupID(id string) *group.GroupId {
|
||||
return &group.GroupId{
|
||||
OpaqueId: id,
|
||||
Idp: "idp",
|
||||
}
|
||||
}
|
||||
|
||||
func resourceID(sid, spid, oid string) *provider.ResourceId {
|
||||
return &provider.ResourceId{
|
||||
StorageId: sid,
|
||||
SpaceId: spid,
|
||||
OpaqueId: oid,
|
||||
}
|
||||
}
|
||||
|
||||
func reference(sid, spid, oid, path string) *provider.Reference {
|
||||
return &provider.Reference{
|
||||
ResourceId: resourceID(sid, spid, oid),
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
func timestamp(seconds uint64) *rtypes.Timestamp {
|
||||
return &rtypes.Timestamp{
|
||||
Seconds: seconds,
|
||||
Nanos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func sharePermissions(perms ...string) *collaboration.SharePermissions {
|
||||
return &collaboration.SharePermissions{
|
||||
Permissions: permissions(perms...),
|
||||
}
|
||||
}
|
||||
|
||||
func linkPermissions(perms ...string) *link.PublicSharePermissions {
|
||||
return &link.PublicSharePermissions{
|
||||
Permissions: permissions(perms...),
|
||||
}
|
||||
}
|
||||
|
||||
func permissions(permissions ...string) *provider.ResourcePermissions {
|
||||
perms := &provider.ResourcePermissions{}
|
||||
|
||||
for _, p := range permissions {
|
||||
switch p {
|
||||
case "stat":
|
||||
perms.Stat = true
|
||||
case "get_path":
|
||||
perms.GetPath = true
|
||||
case "list_container":
|
||||
perms.ListContainer = true
|
||||
case "get_quota":
|
||||
perms.GetQuota = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return perms
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
|
||||
group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
|
||||
sdk "github.com/opencloud-eu/reva/v2/pkg/sdk/common"
|
||||
)
|
||||
|
||||
const _linktype = "link"
|
||||
|
||||
// BasicAuditEvent creates an AuditEvent from given values
|
||||
func BasicAuditEvent(uid string, ctime string, msg string, action string) AuditEvent {
|
||||
return AuditEvent{
|
||||
User: uid,
|
||||
Time: ctime,
|
||||
App: "admin_audit",
|
||||
Message: msg,
|
||||
Action: action,
|
||||
Level: 1,
|
||||
|
||||
// NOTE: those values are not in the events and can therefore not be filled at the moment
|
||||
RemoteAddr: "",
|
||||
URL: "",
|
||||
Method: "",
|
||||
UserAgent: "",
|
||||
CLI: false,
|
||||
}
|
||||
}
|
||||
|
||||
// SharingAuditEvent creates an AuditEventSharing from given values
|
||||
func SharingAuditEvent(shareid string, fileid string, uid string, base AuditEvent) AuditEventSharing {
|
||||
return AuditEventSharing{
|
||||
AuditEvent: base,
|
||||
FileID: fileid,
|
||||
Owner: uid,
|
||||
ShareID: shareid,
|
||||
|
||||
// NOTE: those values are not in the events and can therefore not be filled at the moment
|
||||
Path: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ShareCreated converts a ShareCreated Event to an AuditEventShareCreated
|
||||
func ShareCreated(ev events.ShareCreated) AuditEventShareCreated {
|
||||
uid := ev.Sharer.OpaqueId
|
||||
grantee, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageShareCreated(uid, ev.ItemID.OpaqueId, grantee), ActionShareCreated)
|
||||
return AuditEventShareCreated{
|
||||
AuditEventSharing: SharingAuditEvent("", ev.ItemID.OpaqueId, uid, base),
|
||||
ShareOwner: uid,
|
||||
ShareWith: grantee,
|
||||
ShareType: typ,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
ExpirationDate: "",
|
||||
SharePass: false,
|
||||
Permissions: "",
|
||||
ShareToken: "",
|
||||
}
|
||||
}
|
||||
|
||||
// LinkCreated converts a ShareCreated Event to an AuditEventShareCreated
|
||||
func LinkCreated(ev events.LinkCreated) AuditEventShareCreated {
|
||||
uid := ev.Sharer.OpaqueId
|
||||
with, typ := "", _linktype
|
||||
base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageLinkCreated(uid, ev.ItemID.OpaqueId, ev.ShareID.OpaqueId), ActionShareCreated)
|
||||
return AuditEventShareCreated{
|
||||
AuditEventSharing: SharingAuditEvent("", ev.ItemID.OpaqueId, uid, base),
|
||||
ShareOwner: uid,
|
||||
ShareWith: with,
|
||||
ShareType: typ,
|
||||
ExpirationDate: formatTime(ev.Expiration),
|
||||
SharePass: ev.PasswordProtected,
|
||||
Permissions: normalizeString(ev.Permissions.GetPermissions().String()),
|
||||
ShareToken: ev.Token,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ShareUpdated converts a ShareUpdated event to an AuditEventShareUpdated
|
||||
func ShareUpdated(ev events.ShareUpdated) AuditEventShareUpdated {
|
||||
uid := ev.Sharer.OpaqueId
|
||||
with, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.MTime), MessageShareUpdated(uid, ev.ShareID.OpaqueId, ev.Updated), updateType(ev.Updated))
|
||||
return AuditEventShareUpdated{
|
||||
AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base),
|
||||
ShareOwner: uid,
|
||||
ShareWith: with,
|
||||
ShareType: typ,
|
||||
Permissions: normalizeString(ev.Permissions.GetPermissions().String()),
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
ExpirationDate: "",
|
||||
SharePass: false,
|
||||
ShareToken: "",
|
||||
}
|
||||
}
|
||||
|
||||
// LinkUpdated converts a LinkUpdated event to an AuditEventShareUpdated
|
||||
func LinkUpdated(ev events.LinkUpdated) AuditEventShareUpdated {
|
||||
uid := ev.Sharer.OpaqueId
|
||||
with, typ := "", _linktype
|
||||
base := BasicAuditEvent(uid, formatTime(ev.MTime), MessageLinkUpdated(uid, ev.ShareID.GetOpaqueId(), ev.FieldUpdated), updateType(ev.FieldUpdated))
|
||||
return AuditEventShareUpdated{
|
||||
AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base),
|
||||
ShareOwner: uid,
|
||||
ShareWith: with,
|
||||
ShareType: typ,
|
||||
Permissions: normalizeString(ev.Permissions.GetPermissions().String()),
|
||||
ExpirationDate: formatTime(ev.Expiration),
|
||||
SharePass: ev.PasswordProtected,
|
||||
ShareToken: ev.Token,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ShareRemoved converts a ShareRemoved event to an AuditEventShareRemoved
|
||||
func ShareRemoved(ev events.ShareRemoved) AuditEventShareRemoved {
|
||||
sid, uid, iid, with, typ := "", "", "", "", ""
|
||||
if ev.ShareID != nil {
|
||||
sid = ev.ShareID.GetOpaqueId()
|
||||
}
|
||||
|
||||
if ev.ShareKey != nil {
|
||||
uid = ev.ShareKey.GetOwner().GetOpaqueId()
|
||||
iid = ev.ShareKey.GetResourceId().GetOpaqueId()
|
||||
with, typ = extractGrantee(ev.ShareKey.GetGrantee().GetUserId(), ev.ShareKey.GetGrantee().GetGroupId())
|
||||
}
|
||||
base := BasicAuditEvent(uid, formatTime(utils.TimeToTS(ev.Timestamp)), MessageShareRemoved(uid, sid, iid), ActionShareRemoved)
|
||||
return AuditEventShareRemoved{
|
||||
AuditEventSharing: SharingAuditEvent(sid, iid, uid, base),
|
||||
ShareWith: with,
|
||||
ShareType: typ,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// LinkRemoved converts a LinkRemoved event to an AuditEventShareRemoved
|
||||
func LinkRemoved(ev events.LinkRemoved) AuditEventShareRemoved {
|
||||
uid, sid, typ := ev.Executant.GetOpaqueId(), "", _linktype
|
||||
if ev.ShareID != nil {
|
||||
sid = ev.ShareID.GetOpaqueId()
|
||||
} else {
|
||||
sid = ev.ShareToken
|
||||
}
|
||||
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageLinkRemoved(uid, sid), ActionShareRemoved)
|
||||
return AuditEventShareRemoved{
|
||||
AuditEventSharing: SharingAuditEvent(sid, "", uid, base),
|
||||
ShareWith: "",
|
||||
ShareType: typ,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// ReceivedShareUpdated converts a ReceivedShareUpdated event to an AuditEventReceivedShareUpdated
|
||||
func ReceivedShareUpdated(ev events.ReceivedShareUpdated) AuditEventReceivedShareUpdated {
|
||||
uid := ev.Sharer.GetOpaqueId()
|
||||
sid := ev.ShareID.GetOpaqueId()
|
||||
with, typ := extractGrantee(ev.GranteeUserID, ev.GranteeGroupID)
|
||||
itemID := ev.ItemID.GetOpaqueId()
|
||||
|
||||
msg, utype := "", ""
|
||||
switch ev.State {
|
||||
case "SHARE_STATE_ACCEPTED":
|
||||
msg = MessageShareAccepted(with, sid, uid)
|
||||
utype = ActionShareAccepted
|
||||
case "SHARE_STATE_DECLINED":
|
||||
msg = MessageShareDeclined(with, sid, uid)
|
||||
utype = ActionShareDeclined
|
||||
}
|
||||
base := BasicAuditEvent(with, formatTime(ev.MTime), msg, utype)
|
||||
return AuditEventReceivedShareUpdated{
|
||||
AuditEventSharing: SharingAuditEvent(sid, itemID, uid, base),
|
||||
ShareType: typ,
|
||||
ShareWith: with,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// LinkAccessed converts a LinkAccessed event to an AuditEventLinkAccessed
|
||||
func LinkAccessed(ev events.LinkAccessed) AuditEventLinkAccessed {
|
||||
uid := ev.Sharer.OpaqueId
|
||||
base := BasicAuditEvent(uid, formatTime(ev.CTime), MessageLinkAccessed(ev.Token, true), ActionLinkAccessed)
|
||||
return AuditEventLinkAccessed{
|
||||
AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), ev.ItemID.OpaqueId, uid, base),
|
||||
ShareToken: ev.Token,
|
||||
Success: true,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// LinkAccessFailed converts a LinkAccessFailed event to an AuditEventLinkAccessed
|
||||
func LinkAccessFailed(ev events.LinkAccessFailed) AuditEventLinkAccessed {
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageLinkAccessed(ev.Token, false), ActionLinkAccessed)
|
||||
return AuditEventLinkAccessed{
|
||||
AuditEventSharing: SharingAuditEvent(ev.ShareID.GetOpaqueId(), "", "", base),
|
||||
ShareToken: ev.Token,
|
||||
Success: false,
|
||||
|
||||
// NOTE: those values are not in the event and can therefore not be filled at the moment
|
||||
ItemType: "",
|
||||
}
|
||||
}
|
||||
|
||||
// FilesAuditEvent creates an AuditEventFiles from the given values
|
||||
func FilesAuditEvent(base AuditEvent, itemid, owner, path string) AuditEventFiles {
|
||||
return AuditEventFiles{
|
||||
AuditEvent: base,
|
||||
FileID: itemid,
|
||||
Owner: owner,
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerCreated converts a ContainerCreated event to an AuditEventContainerCreated
|
||||
func ContainerCreated(ev events.ContainerCreated) AuditEventContainerCreated {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageContainerCreated(ev.Executant.GetOpaqueId(), iid), ActionContainerCreated)
|
||||
return AuditEventContainerCreated{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
}
|
||||
}
|
||||
|
||||
// FileUploaded converts a FileUploaded event to an AuditEventFileCreated
|
||||
func FileUploaded(ev events.FileUploaded) AuditEventFileCreated {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileCreated(ev.Executant.GetOpaqueId(), iid), ActionFileCreated)
|
||||
return AuditEventFileCreated{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
}
|
||||
}
|
||||
|
||||
// FileDownloaded converts a FileDownloaded event to an AuditEventFileRead
|
||||
func FileDownloaded(ev events.FileDownloaded) AuditEventFileRead {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileRead(ev.Executant.GetOpaqueId(), iid), ActionFileRead)
|
||||
return AuditEventFileRead{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
}
|
||||
}
|
||||
|
||||
// ItemMoved converts a ItemMoved event to an AuditEventFileRenamed
|
||||
func ItemMoved(ev events.ItemMoved) AuditEventFileRenamed {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
|
||||
oldpath := ""
|
||||
if ev.OldReference != nil {
|
||||
oldpath = ev.OldReference.GetPath()
|
||||
}
|
||||
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileRenamed(ev.Executant.GetOpaqueId(), iid, oldpath, path), ActionFileRenamed)
|
||||
return AuditEventFileRenamed{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
OldPath: oldpath,
|
||||
}
|
||||
}
|
||||
|
||||
// ItemTrashed converts a ItemTrashed event to an AuditEventFileDeleted
|
||||
func ItemTrashed(ev events.ItemTrashed) AuditEventFileDeleted {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileTrashed(ev.Executant.GetOpaqueId(), iid), ActionFileTrashed)
|
||||
return AuditEventFileDeleted{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
}
|
||||
}
|
||||
|
||||
// ItemPurged converts a ItemPurged event to an AuditEventFilePurged
|
||||
func ItemPurged(ev events.ItemPurged) AuditEventFilePurged {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFilePurged(ev.Executant.GetOpaqueId(), iid), ActionFilePurged)
|
||||
return AuditEventFilePurged{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
}
|
||||
}
|
||||
|
||||
// ItemRestored converts a ItemRestored event to an AuditEventFileRestored
|
||||
func ItemRestored(ev events.ItemRestored) AuditEventFileRestored {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
|
||||
oldpath := ""
|
||||
if ev.OldReference != nil {
|
||||
oldpath = ev.OldReference.GetPath()
|
||||
}
|
||||
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileRestored(ev.Executant.GetOpaqueId(), iid, path), ActionFileRestored)
|
||||
return AuditEventFileRestored{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
OldPath: oldpath,
|
||||
}
|
||||
}
|
||||
|
||||
// FileVersionRestored converts a FileVersionRestored event to an AuditEventFileVersionRestored
|
||||
func FileVersionRestored(ev events.FileVersionRestored) AuditEventFileVersionRestored {
|
||||
iid, path, uid := extractFileDetails(ev.Ref, ev.Owner)
|
||||
base := BasicAuditEvent(uid, formatTime(ev.Timestamp), MessageFileVersionRestored(ev.Executant.GetOpaqueId(), iid, ev.Key), ActionFileVersionRestored)
|
||||
return AuditEventFileVersionRestored{
|
||||
AuditEventFiles: FilesAuditEvent(base, iid, uid, path),
|
||||
Key: ev.Key,
|
||||
}
|
||||
}
|
||||
|
||||
// SpacesAuditEvent creates an AuditEventSpaces from the given values
|
||||
func SpacesAuditEvent(base AuditEvent, spaceID string) AuditEventSpaces {
|
||||
return AuditEventSpaces{
|
||||
AuditEvent: base,
|
||||
SpaceID: spaceID,
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceCreated converts a SpaceCreated event to an AuditEventSpaceCreated
|
||||
func SpaceCreated(ev events.SpaceCreated) AuditEventSpaceCreated {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
iid, _, owner := extractFileDetails(&provider.Reference{ResourceId: ev.Root}, ev.Owner)
|
||||
base := BasicAuditEvent("", formatTime(ev.MTime), MessageSpaceCreated(ev.Executant.GetOpaqueId(), sid, ev.Name), ActionSpaceCreated)
|
||||
return AuditEventSpaceCreated{
|
||||
AuditEventSpaces: SpacesAuditEvent(base, sid),
|
||||
Owner: owner,
|
||||
RootItem: iid,
|
||||
Name: ev.Name,
|
||||
Type: ev.Type,
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceRenamed converts a SpaceRenamed event to an AuditEventSpaceRenamed
|
||||
func SpaceRenamed(ev events.SpaceRenamed) AuditEventSpaceRenamed {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageSpaceRenamed(ev.Executant.GetOpaqueId(), sid, ev.Name), ActionSpaceRenamed)
|
||||
return AuditEventSpaceRenamed{
|
||||
AuditEventSpaces: SpacesAuditEvent(base, sid),
|
||||
NewName: ev.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceDisabled converts a SpaceDisabled event to an AuditEventSpaceDisabled
|
||||
func SpaceDisabled(ev events.SpaceDisabled) AuditEventSpaceDisabled {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
base := BasicAuditEvent("", formatTime(utils.TimeToTS(ev.Timestamp)), MessageSpaceDisabled(ev.Executant.GetOpaqueId(), sid), ActionSpaceDisabled)
|
||||
return AuditEventSpaceDisabled{
|
||||
AuditEventSpaces: SpacesAuditEvent(base, sid),
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceEnabled converts a SpaceEnabled event to an AuditEventSpaceEnabled
|
||||
func SpaceEnabled(ev events.SpaceEnabled) AuditEventSpaceEnabled {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageSpaceEnabled(ev.Executant.GetOpaqueId(), sid), ActionSpaceEnabled)
|
||||
return AuditEventSpaceEnabled{
|
||||
AuditEventSpaces: SpacesAuditEvent(base, sid),
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceDeleted converts a SpaceDeleted event to an AuditEventSpaceDeleted
|
||||
func SpaceDeleted(ev events.SpaceDeleted) AuditEventSpaceDeleted {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
base := BasicAuditEvent("", formatTime(utils.TimeToTS(ev.Timestamp)), MessageSpaceDeleted(ev.Executant.GetOpaqueId(), sid), ActionSpaceDeleted)
|
||||
return AuditEventSpaceDeleted{
|
||||
AuditEventSpaces: SpacesAuditEvent(base, sid),
|
||||
}
|
||||
}
|
||||
|
||||
// SpaceShared converts a SpaceShared event to an AuditEventSpaceShared
|
||||
func SpaceShared(ev events.SpaceShared) AuditEventSpaceShared {
|
||||
sse := AuditEventSpaceShared{}
|
||||
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
grantee := "N/A"
|
||||
if ev.GranteeUserID != nil {
|
||||
sse.GranteeUserID = ev.GranteeUserID.OpaqueId
|
||||
grantee = "user:" + ev.GranteeUserID.OpaqueId
|
||||
} else if ev.GranteeGroupID != nil {
|
||||
sse.GranteeGroupID = ev.GranteeGroupID.OpaqueId
|
||||
grantee = "group:" + ev.GranteeGroupID.OpaqueId
|
||||
}
|
||||
base := BasicAuditEvent("", "", MessageSpaceShared(ev.Executant.GetOpaqueId(), sid, grantee), ActionSpaceShared)
|
||||
sse.AuditEventSpaces = SpacesAuditEvent(base, sid)
|
||||
|
||||
return sse
|
||||
}
|
||||
|
||||
// SpaceUnshared converts a SpaceUnshared event to an AuditEventSpaceUnshared
|
||||
func SpaceUnshared(ev events.SpaceUnshared) AuditEventSpaceUnshared {
|
||||
sue := AuditEventSpaceUnshared{}
|
||||
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
grantee := "N/A"
|
||||
if ev.GranteeUserID != nil {
|
||||
sue.GranteeUserID = ev.GranteeUserID.OpaqueId
|
||||
grantee = "user:" + ev.GranteeUserID.OpaqueId
|
||||
} else if ev.GranteeGroupID != nil {
|
||||
sue.GranteeGroupID = ev.GranteeGroupID.OpaqueId
|
||||
grantee = "group:" + ev.GranteeGroupID.OpaqueId
|
||||
}
|
||||
base := BasicAuditEvent("", formatTime(utils.TimeToTS(ev.Timestamp)), MessageSpaceUnshared(ev.Executant.GetOpaqueId(), sid, grantee), ActionSpaceUnshared)
|
||||
sue.AuditEventSpaces = SpacesAuditEvent(base, sid)
|
||||
|
||||
return sue
|
||||
}
|
||||
|
||||
// SpaceUpdated converts a SpaceUpdated event to an AuditEventSpaceUpdated
|
||||
func SpaceUpdated(ev events.SpaceUpdated) AuditEventSpaceUpdated {
|
||||
sid := ev.ID.GetOpaqueId()
|
||||
opaqueMap := sdk.DecodeOpaqueMap(ev.Space.Opaque)
|
||||
sue := AuditEventSpaceUpdated{
|
||||
Name: ev.Space.Name,
|
||||
Opaque: opaqueMap,
|
||||
}
|
||||
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageSpaceUpdated(ev.Executant.GetOpaqueId(), sid, ev.Space.Name, ev.Space.Quota.QuotaMaxBytes, opaqueMap), ActionSpaceUpdated)
|
||||
sue.AuditEventSpaces = SpacesAuditEvent(base, sid)
|
||||
|
||||
return sue
|
||||
}
|
||||
|
||||
// UserCreated converts a UserCreated event to an AuditEventUserCreated
|
||||
func UserCreated(ev events.UserCreated) AuditEventUserCreated {
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageUserCreated(ev.Executant.GetOpaqueId(), ev.UserID), ActionUserCreated)
|
||||
return AuditEventUserCreated{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// UserDeleted converts a UserDeleted event to an AuditEventUserDeleted
|
||||
func UserDeleted(ev events.UserDeleted) AuditEventUserDeleted {
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageUserDeleted(ev.Executant.GetOpaqueId(), ev.UserID), ActionUserDeleted)
|
||||
return AuditEventUserDeleted{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// UserFeatureChanged converts a UserFeatureChanged event to an AuditEventUserFeatureChanged
|
||||
func UserFeatureChanged(ev events.UserFeatureChanged) AuditEventUserFeatureChanged {
|
||||
msg := MessageUserFeatureChanged(ev.Executant.GetOpaqueId(), ev.UserID, ev.Features)
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), msg, ActionUserFeatureChanged)
|
||||
return AuditEventUserFeatureChanged{
|
||||
AuditEvent: base,
|
||||
UserID: ev.UserID,
|
||||
Features: ev.Features,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupCreated converts a GroupCreated event to an AuditEventGroupCreated
|
||||
func GroupCreated(ev events.GroupCreated) AuditEventGroupCreated {
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageGroupCreated(ev.Executant.GetOpaqueId(), ev.GroupID), ActionGroupCreated)
|
||||
return AuditEventGroupCreated{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupDeleted converts a GroupDeleted event to an AuditEventGroupDeleted
|
||||
func GroupDeleted(ev events.GroupDeleted) AuditEventGroupDeleted {
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), MessageGroupDeleted(ev.Executant.GetOpaqueId(), ev.GroupID), ActionGroupDeleted)
|
||||
return AuditEventGroupDeleted{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupMemberAdded converts a GroupMemberAdded event to an AuditEventGroupMemberAdded
|
||||
func GroupMemberAdded(ev events.GroupMemberAdded) AuditEventGroupMemberAdded {
|
||||
msg := MessageGroupMemberAdded(ev.Executant.GetOpaqueId(), ev.GroupID, ev.UserID)
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), msg, ActionGroupMemberAdded)
|
||||
return AuditEventGroupMemberAdded{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupMemberRemoved converts a GroupMemberRemoved event to an AuditEventGroupMemberRemove
|
||||
func GroupMemberRemoved(ev events.GroupMemberRemoved) AuditEventGroupMemberRemoved {
|
||||
msg := MessageGroupMemberRemoved(ev.Executant.GetOpaqueId(), ev.GroupID, ev.UserID)
|
||||
base := BasicAuditEvent("", formatTime(ev.Timestamp), msg, ActionGroupMemberRemoved)
|
||||
return AuditEventGroupMemberRemoved{
|
||||
AuditEvent: base,
|
||||
GroupID: ev.GroupID,
|
||||
UserID: ev.UserID,
|
||||
}
|
||||
}
|
||||
|
||||
// ScienceMeshInviteTokenGenerated converts a ScienceMeshInviteTokenGenerated event to an AuditEventScienceMeshInviteTokenGenerated
|
||||
func ScienceMeshInviteTokenGenerated(ev events.ScienceMeshInviteTokenGenerated) AuditEventScienceMeshInviteTokenGenerated {
|
||||
msg := MessageScienceMeshInviteTokenGenerated(ev.Sharer.GetOpaqueId(), ev.Token)
|
||||
base := BasicAuditEvent(ev.Sharer.GetOpaqueId(), formatTime(ev.Timestamp), msg, ActionScienceMeshInviteTokenGenerated)
|
||||
return AuditEventScienceMeshInviteTokenGenerated{
|
||||
AuditEvent: base,
|
||||
RecipientMail: ev.RecipientMail,
|
||||
Token: ev.Token,
|
||||
Description: ev.Description,
|
||||
Expiration: ev.Expiration,
|
||||
InviteLink: ev.InviteLink,
|
||||
}
|
||||
}
|
||||
|
||||
func extractGrantee(uid *user.UserId, gid *group.GroupId) (string, string) {
|
||||
switch {
|
||||
case uid != nil && uid.OpaqueId != "":
|
||||
return uid.OpaqueId, "user"
|
||||
case gid != nil && gid.OpaqueId != "":
|
||||
return gid.OpaqueId, "group"
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func extractFileDetails(ref *provider.Reference, owner *user.UserId) (string, string, string) {
|
||||
id, path := "", ""
|
||||
if ref != nil {
|
||||
path = ref.GetPath()
|
||||
id, _ = storagespace.FormatReference(ref)
|
||||
}
|
||||
|
||||
uid := ""
|
||||
if owner != nil {
|
||||
uid = owner.GetOpaqueId()
|
||||
}
|
||||
return id, path, uid
|
||||
}
|
||||
|
||||
func formatTime(t *types.Timestamp) string {
|
||||
if t == nil {
|
||||
return ""
|
||||
}
|
||||
return time.Unix(int64(t.Seconds), int64(t.Nanos)).UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func updateType(u string) string {
|
||||
switch u {
|
||||
case "permissions":
|
||||
return ActionSharePermissionUpdated
|
||||
case "displayname":
|
||||
return ActionShareDisplayNameUpdated
|
||||
case "TYPE_PERMISSIONS":
|
||||
return ActionSharePermissionUpdated
|
||||
case "TYPE_DISPLAYNAME":
|
||||
return ActionShareDisplayNameUpdated
|
||||
case "TYPE_PASSWORD":
|
||||
return ActionSharePasswordUpdated
|
||||
case "TYPE_EXPIRATION":
|
||||
return ActionShareExpirationUpdated
|
||||
default:
|
||||
fmt.Println("Unknown update type", u)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeString tries to create a somewhat stable string
|
||||
// from a prototext string. The prototext strings are unstable
|
||||
// on purpose an insert additional spaces randomly.
|
||||
// See: https://protobuf.dev/reference/go/faq/#unstable-text
|
||||
func normalizeString(str string) string {
|
||||
return strings.Join(strings.Fields(str), " ")
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
)
|
||||
|
||||
// RegisteredEvents returns the events the service is registered for
|
||||
func RegisteredEvents() []events.Unmarshaller {
|
||||
return []events.Unmarshaller{
|
||||
events.ShareCreated{},
|
||||
events.ShareUpdated{},
|
||||
events.LinkCreated{},
|
||||
events.LinkUpdated{},
|
||||
events.ShareRemoved{},
|
||||
events.LinkRemoved{},
|
||||
events.ReceivedShareUpdated{},
|
||||
events.LinkAccessed{},
|
||||
events.LinkAccessFailed{},
|
||||
events.ContainerCreated{},
|
||||
events.FileUploaded{},
|
||||
events.FileDownloaded{},
|
||||
events.ItemTrashed{},
|
||||
events.ItemMoved{},
|
||||
events.ItemPurged{},
|
||||
events.ItemRestored{},
|
||||
events.FileVersionRestored{},
|
||||
events.SpaceCreated{},
|
||||
events.SpaceRenamed{},
|
||||
events.SpaceEnabled{},
|
||||
events.SpaceDisabled{},
|
||||
events.SpaceDeleted{},
|
||||
events.SpaceShared{},
|
||||
events.SpaceUnshared{},
|
||||
events.SpaceUpdated{},
|
||||
events.UserCreated{},
|
||||
events.UserDeleted{},
|
||||
events.UserFeatureChanged{},
|
||||
events.GroupCreated{},
|
||||
events.GroupDeleted{},
|
||||
events.GroupMemberAdded{},
|
||||
events.GroupMemberRemoved{},
|
||||
events.BackchannelLogout{},
|
||||
events.ScienceMeshInviteTokenGenerated{},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
)
|
||||
|
||||
// short identifiers for audit actions
|
||||
const (
|
||||
// Sharing
|
||||
ActionShareCreated = "file_shared"
|
||||
ActionSharePermissionUpdated = "share_permission_updated"
|
||||
ActionShareDisplayNameUpdated = "share_name_updated"
|
||||
ActionSharePasswordUpdated = "share_password_updated"
|
||||
ActionShareExpirationUpdated = "share_expiration_updated"
|
||||
ActionShareRemoved = "file_unshared"
|
||||
ActionShareAccepted = "share_accepted"
|
||||
ActionShareDeclined = "share_declined"
|
||||
ActionLinkAccessed = "public_link_accessed"
|
||||
|
||||
// Files
|
||||
ActionContainerCreated = "container_create"
|
||||
ActionFileCreated = "file_create"
|
||||
ActionFileRead = "file_read"
|
||||
ActionFileTrashed = "file_delete"
|
||||
ActionFileRenamed = "file_rename"
|
||||
ActionFilePurged = "file_trash_delete"
|
||||
ActionFileRestored = "file_trash_restore"
|
||||
ActionFileVersionRestored = "file_version_restore"
|
||||
|
||||
// Spaces
|
||||
ActionSpaceCreated = "space_created"
|
||||
ActionSpaceRenamed = "space_renamed"
|
||||
ActionSpaceDisabled = "space_disabled"
|
||||
ActionSpaceEnabled = "space_enabled"
|
||||
ActionSpaceDeleted = "space_deleted"
|
||||
ActionSpaceShared = "space_shared"
|
||||
ActionSpaceUnshared = "space_unshared"
|
||||
ActionSpaceUpdated = "space_updated"
|
||||
|
||||
// Users
|
||||
ActionUserCreated = "user_created"
|
||||
ActionUserDeleted = "user_deleted"
|
||||
ActionUserFeatureChanged = "user_feature_changed"
|
||||
|
||||
// Groups
|
||||
ActionGroupCreated = "group_created"
|
||||
ActionGroupDeleted = "group_deleted"
|
||||
ActionGroupMemberAdded = "group_member_added"
|
||||
ActionGroupMemberRemoved = "group_member_removed"
|
||||
|
||||
// ScienceMesh
|
||||
ActionScienceMeshInviteTokenGenerated = "science_mesh_invite_token_generated"
|
||||
)
|
||||
|
||||
// MessageShareCreated returns the human-readable string that describes the action
|
||||
func MessageShareCreated(sharer, item, grantee string) string {
|
||||
return fmt.Sprintf("user '%s' shared file '%s' with '%s'", sharer, item, grantee)
|
||||
}
|
||||
|
||||
// MessageLinkCreated returns the human-readable string that describes the action
|
||||
func MessageLinkCreated(sharer, item, shareID string) string {
|
||||
return fmt.Sprintf("user '%s' created a public link to file '%s' with id '%s'", sharer, item, shareID)
|
||||
}
|
||||
|
||||
// MessageShareUpdated returns the human-readable string that describes the action
|
||||
func MessageShareUpdated(sharer, shareID, fieldUpdated string) string {
|
||||
return fmt.Sprintf("user '%s' updated field '%s' of share '%s'", sharer, fieldUpdated, shareID)
|
||||
}
|
||||
|
||||
// MessageLinkUpdated returns the human-readable string that describes the action
|
||||
func MessageLinkUpdated(sharer, shareID, fieldUpdated string) string {
|
||||
return fmt.Sprintf("user '%s' updated field '%s' of public link '%s'", sharer, fieldUpdated, shareID)
|
||||
}
|
||||
|
||||
// MessageShareRemoved returns the human-readable string that describes the action
|
||||
func MessageShareRemoved(sharer, shareID, itemid string) string {
|
||||
return fmt.Sprintf("share id:'%s' uid:'%s' item-id:'%s' was removed", shareID, sharer, itemid)
|
||||
}
|
||||
|
||||
// MessageLinkRemoved returns the human-readable string that describes the action
|
||||
func MessageLinkRemoved(executant, shareID string) string {
|
||||
return fmt.Sprintf("user '%s' removed public link with id:'%s'", executant, shareID)
|
||||
}
|
||||
|
||||
// MessageShareAccepted returns the human-readable string that describes the action
|
||||
func MessageShareAccepted(userid, shareID, sharerID string) string {
|
||||
return fmt.Sprintf("user '%s' accepted share '%s' from user '%s'", userid, shareID, sharerID)
|
||||
}
|
||||
|
||||
// MessageShareDeclined returns the human-readable string that describes the action
|
||||
func MessageShareDeclined(userid, shareID, sharerID string) string {
|
||||
return fmt.Sprintf("user '%s' declined share '%s' from user '%s'", userid, shareID, sharerID)
|
||||
}
|
||||
|
||||
// MessageLinkAccessed returns the human-readable string that describes the action
|
||||
func MessageLinkAccessed(token string, success bool) string {
|
||||
return fmt.Sprintf("link with token '%s' was accessed. Success: %v", token, success)
|
||||
}
|
||||
|
||||
// MessageContainerCreated returns the human-readable string that describes the action
|
||||
func MessageContainerCreated(executant, item string) string {
|
||||
return fmt.Sprintf("user '%s' created folder '%s'", executant, item)
|
||||
}
|
||||
|
||||
// MessageFileCreated returns the human-readable string that describes the action
|
||||
func MessageFileCreated(executant, item string) string {
|
||||
return fmt.Sprintf("user '%s' created file '%s'", executant, item)
|
||||
}
|
||||
|
||||
// MessageFileRead returns the human-readable string that describes the action
|
||||
func MessageFileRead(executant, item string) string {
|
||||
return fmt.Sprintf("user '%s' read file '%s'", executant, item)
|
||||
}
|
||||
|
||||
// MessageFileTrashed returns the human-readable string that describes the action
|
||||
func MessageFileTrashed(executant, item string) string {
|
||||
return fmt.Sprintf("user '%s' trashed file '%s'", executant, item)
|
||||
}
|
||||
|
||||
// MessageFileRenamed returns the human-readable string that describes the action
|
||||
func MessageFileRenamed(executant, item, oldpath, newpath string) string {
|
||||
return fmt.Sprintf("user '%s' moved file '%s' from '%s' to '%s'", executant, item, oldpath, newpath)
|
||||
}
|
||||
|
||||
// MessageFilePurged returns the human-readable string that describes the action
|
||||
func MessageFilePurged(executant, item string) string {
|
||||
return fmt.Sprintf("user '%s' removed file '%s' from trashbin", executant, item)
|
||||
}
|
||||
|
||||
// MessageFileRestored returns the human-readable string that describes the action
|
||||
func MessageFileRestored(executant, item, path string) string {
|
||||
return fmt.Sprintf("user '%s' restored file '%s' from trashbin to '%s'", executant, item, path)
|
||||
}
|
||||
|
||||
// MessageFileVersionRestored returns the human-readable string that describes the action
|
||||
func MessageFileVersionRestored(executant, item, version string) string {
|
||||
return fmt.Sprintf("user '%s' restored file '%s' in version '%s'", executant, item, version)
|
||||
}
|
||||
|
||||
// MessageSpaceCreated returns the human-readable string that describes the action
|
||||
func MessageSpaceCreated(executant, spaceID, name string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' created a space '%s' with name '%s' (storage: '%s')", executant, spaceID, name, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceRenamed returns the human-readable string that describes the action
|
||||
func MessageSpaceRenamed(executant, spaceID, name string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' renamed space '%s' to '%s' (storage: '%s')", executant, spaceID, name, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceDisabled returns the human-readable string that describes the action
|
||||
func MessageSpaceDisabled(executant, spaceID string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' disabled the space '%s' (storage: '%s')", executant, spaceID, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceEnabled returns the human-readable string that describes the action
|
||||
func MessageSpaceEnabled(executant, spaceID string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' (re-) enabled the space '%s' (storage: '%s')", executant, spaceID, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceDeleted returns the human-readable string that describes the action
|
||||
func MessageSpaceDeleted(executant, spaceID string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' deleted the space '%s' (storage: '%s')", executant, spaceID, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceShared returns the human-readable string that describes the action
|
||||
func MessageSpaceShared(executant, spaceID, grantee string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' shared the space '%s' with '%s' (storage: '%s')", executant, spaceID, grantee, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceUnshared returns the human-readable string that describes the action
|
||||
func MessageSpaceUnshared(executant, spaceID, grantee string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' unshared the space '%s' with '%s' (storage: '%s')", executant, spaceID, grantee, storagID)
|
||||
}
|
||||
|
||||
// MessageSpaceUpdated returns the human-readable string that describes the action
|
||||
func MessageSpaceUpdated(executant, spaceID, name string, quota uint64, opaque map[string]string) string {
|
||||
storagID, spaceID := storagespace.SplitStorageID(spaceID)
|
||||
return fmt.Sprintf("user '%s' updated space '%s'. name: '%s', quota: '%d', opaque: '%s' (storage: '%s')",
|
||||
executant, spaceID, name, quota, opaque, storagID)
|
||||
}
|
||||
|
||||
// MessageUserCreated returns the human-readable string that describes the action
|
||||
func MessageUserCreated(executant, userID string) string {
|
||||
return fmt.Sprintf("user '%s' created the user '%s'", executant, userID)
|
||||
}
|
||||
|
||||
// MessageUserDeleted returns the human-readable string that describes the action
|
||||
func MessageUserDeleted(executant, userID string) string {
|
||||
return fmt.Sprintf("user '%s' deleted the user '%s'", executant, userID)
|
||||
}
|
||||
|
||||
// MessageUserFeatureChanged returns the human-readable string that describes the action
|
||||
func MessageUserFeatureChanged(executant, userID string, features []events.UserFeature) string {
|
||||
// Result is: "user '%executant%' changed user %username%'s features: %featurename%=%featurevalue% %featurename%=%featurevalue%"
|
||||
var sb strings.Builder
|
||||
sb.WriteString("user '")
|
||||
sb.WriteString(executant)
|
||||
sb.WriteString("' changed user ")
|
||||
sb.WriteString(userID)
|
||||
sb.WriteString("'s features:")
|
||||
for _, f := range features {
|
||||
sb.WriteString(f.Name)
|
||||
sb.WriteRune('=')
|
||||
sb.WriteString(f.Value)
|
||||
sb.WriteRune(' ')
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// MessageGroupCreated returns the human-readable string that describes the action
|
||||
func MessageGroupCreated(executant, groupID string) string {
|
||||
return fmt.Sprintf("user '%s' created group '%s'", executant, groupID)
|
||||
}
|
||||
|
||||
// MessageGroupDeleted returns the human-readable string that describes the action
|
||||
func MessageGroupDeleted(executant, groupID string) string {
|
||||
return fmt.Sprintf("user '%s' deleted group '%s'", executant, groupID)
|
||||
}
|
||||
|
||||
// MessageGroupMemberAdded returns the human-readable string that describes the action
|
||||
func MessageGroupMemberAdded(executant, userID, groupID string) string {
|
||||
return fmt.Sprintf("user '%s' added user '%s' was added to group '%s'", executant, userID, groupID)
|
||||
}
|
||||
|
||||
// MessageGroupMemberRemoved returns the human-readable string that describes the action
|
||||
func MessageGroupMemberRemoved(executant, userID, groupID string) string {
|
||||
return fmt.Sprintf("user '%s' added user '%s' was removed from group '%s'", executant, userID, groupID)
|
||||
}
|
||||
|
||||
// MessageScienceMeshInviteTokenGenerated returns the human-readable string that describes the action
|
||||
func MessageScienceMeshInviteTokenGenerated(user, token string) string {
|
||||
return fmt.Sprintf("user '%s' generated a ScienceMesh invite with token '%s'", user, token)
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
package types
|
||||
|
||||
import "github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
|
||||
// AuditEvent is the basic audit event
|
||||
type AuditEvent struct {
|
||||
RemoteAddr string // the remote client IP
|
||||
User string // the UID of the user performing the action. Or "IP x.x.x.x.", "cron", "CLI", "unknown"
|
||||
URL string // the process request URI
|
||||
Method string // the HTTP request method
|
||||
UserAgent string // the HTTP request user agent
|
||||
Time string // the time of the event eg: 2018-05-08T08:26:00+00:00
|
||||
App string // always 'admin_audit'
|
||||
Message string // sentence explaining the action
|
||||
Action string // unique action identifier eg: file_delete or public_link_created
|
||||
CLI bool // if the action was performed from the CLI
|
||||
Level int // the log level of the entry (usually 1 for audit events)
|
||||
}
|
||||
|
||||
/*
|
||||
Sharing
|
||||
*/
|
||||
|
||||
// AuditEventSharing is the basic audit event for shares
|
||||
type AuditEventSharing struct {
|
||||
AuditEvent
|
||||
|
||||
FileID string // The file identifier for the item shared.
|
||||
Owner string // The UID of the owner of the shared item.
|
||||
Path string // The path to the shared item.
|
||||
ShareID string // The sharing identifier. (not available for public_link_accessed or when recipient un-shares)
|
||||
}
|
||||
|
||||
// AuditEventShareCreated is the event logged when a share is created
|
||||
type AuditEventShareCreated struct {
|
||||
AuditEventSharing
|
||||
|
||||
ItemType string // file or folder
|
||||
ExpirationDate string // The text expiration date in format 'yyyy-mm-dd'
|
||||
SharePass bool // If the share is password protected.
|
||||
Permissions string // The permissions string eg: "READ"
|
||||
ShareType string // group user or link
|
||||
ShareWith string // The UID or GID of the share recipient. (not available for public link)
|
||||
ShareOwner string // The UID of the share owner.
|
||||
ShareToken string // For link shares the unique token, else null
|
||||
}
|
||||
|
||||
// AuditEventShareUpdated is the event logged when a share is updated
|
||||
type AuditEventShareUpdated struct {
|
||||
AuditEventSharing
|
||||
|
||||
ItemType string // file or folder
|
||||
ExpirationDate string // The text expiration date in format 'yyyy-mm-dd'
|
||||
SharePass bool // If the share is password protected.
|
||||
Permissions string // The permissions string eg: "READ"
|
||||
ShareType string // group user or link
|
||||
ShareWith string // The UID or GID of the share recipient. (not available for public link)
|
||||
ShareOwner string // The UID of the share owner.
|
||||
ShareToken string // For link shares the unique token, else null
|
||||
}
|
||||
|
||||
// AuditEventShareRemoved is the event logged when a share is removed
|
||||
type AuditEventShareRemoved struct {
|
||||
AuditEventSharing
|
||||
ItemType string // file or folder
|
||||
ShareType string // group user or link
|
||||
ShareWith string // The UID or GID of the share recipient.
|
||||
}
|
||||
|
||||
// AuditEventReceivedShareUpdated is the event logged when a share is accepted or declined
|
||||
type AuditEventReceivedShareUpdated struct {
|
||||
AuditEventSharing
|
||||
ItemType string // file or folder
|
||||
ShareType string // group user or link
|
||||
ShareWith string // The UID or GID of the share recipient.
|
||||
}
|
||||
|
||||
// AuditEventLinkAccessed is the event logged when a link is accessed
|
||||
type AuditEventLinkAccessed struct {
|
||||
AuditEventSharing
|
||||
ShareToken string // The share token.
|
||||
Success bool // If the request was successful.
|
||||
ItemType string // file or folder
|
||||
}
|
||||
|
||||
/*
|
||||
Files
|
||||
*/
|
||||
|
||||
// AuditEventFiles is the basic audit event for files
|
||||
type AuditEventFiles struct {
|
||||
AuditEvent
|
||||
|
||||
Path string // The full path to the created file.
|
||||
Owner string // The UID of the owner of the file.
|
||||
FileID string // The newly created files identifier.
|
||||
}
|
||||
|
||||
// AuditEventContainerCreated is the event logged when a container is created
|
||||
type AuditEventContainerCreated struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileCreated is the event logged when a file is created
|
||||
type AuditEventFileCreated struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileRead is the event logged when a file is read (aka downloaded)
|
||||
type AuditEventFileRead struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileUpdated is the event logged when a file is updated
|
||||
// TODO: How to differentiate between new uploads and new version uploads?
|
||||
// FIXME: implement
|
||||
type AuditEventFileUpdated struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileDeleted is the event logged when a file is deleted (aka trashed)
|
||||
type AuditEventFileDeleted struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileCopied is the event logged when a file is copied
|
||||
// TODO: copy is a download&upload for now. How to know it was a copy?
|
||||
// FIXME: implement
|
||||
type AuditEventFileCopied struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileRenamed is the event logged when a file is renamed (moved)
|
||||
type AuditEventFileRenamed struct {
|
||||
AuditEventFiles
|
||||
|
||||
OldPath string
|
||||
}
|
||||
|
||||
// AuditEventFilePurged is the event logged when a file is purged (deleted from trash-bin)
|
||||
type AuditEventFilePurged struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
// AuditEventFileRestored is the event logged when a file is restored (from trash-bin)
|
||||
type AuditEventFileRestored struct {
|
||||
AuditEventFiles
|
||||
|
||||
OldPath string
|
||||
}
|
||||
|
||||
// AuditEventFileVersionRestored is the event logged when a file version is restored
|
||||
type AuditEventFileVersionRestored struct {
|
||||
AuditEventFiles
|
||||
|
||||
Key string
|
||||
}
|
||||
|
||||
// AuditEventFileVersionDeleted is the event logged when a file version is deleted
|
||||
// TODO: is this even possible?
|
||||
type AuditEventFileVersionDeleted struct {
|
||||
AuditEventFiles
|
||||
}
|
||||
|
||||
/*
|
||||
Spaces
|
||||
*/
|
||||
|
||||
// AuditEventSpaces is the basic audit event for spaces
|
||||
type AuditEventSpaces struct {
|
||||
AuditEvent
|
||||
|
||||
SpaceID string
|
||||
}
|
||||
|
||||
// AuditEventSpaceCreated is the event logged when a space is created
|
||||
type AuditEventSpaceCreated struct {
|
||||
AuditEventSpaces
|
||||
|
||||
Owner string
|
||||
RootItem string
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// AuditEventSpaceRenamed is the event logged when a space is renamed
|
||||
type AuditEventSpaceRenamed struct {
|
||||
AuditEventSpaces
|
||||
|
||||
NewName string
|
||||
}
|
||||
|
||||
// AuditEventSpaceDisabled is the event logged when a space is disabled
|
||||
type AuditEventSpaceDisabled struct {
|
||||
AuditEventSpaces
|
||||
}
|
||||
|
||||
// AuditEventSpaceEnabled is the event logged when a space is (re-)enabled
|
||||
type AuditEventSpaceEnabled struct {
|
||||
AuditEventSpaces
|
||||
}
|
||||
|
||||
// AuditEventSpaceDeleted is the event logged when a space is deleted
|
||||
type AuditEventSpaceDeleted struct {
|
||||
AuditEventSpaces
|
||||
}
|
||||
|
||||
// AuditEventSpaceShared is the event logged when a space is shared
|
||||
type AuditEventSpaceShared struct {
|
||||
AuditEventSpaces
|
||||
|
||||
GranteeUserID string
|
||||
GranteeGroupID string
|
||||
}
|
||||
|
||||
// AuditEventSpaceUnshared is the event logged when a space is unshared
|
||||
type AuditEventSpaceUnshared struct {
|
||||
AuditEventSpaces
|
||||
|
||||
GranteeUserID string
|
||||
GranteeGroupID string
|
||||
}
|
||||
|
||||
// AuditEventSpaceUpdated is the event logged when a space is updated
|
||||
type AuditEventSpaceUpdated struct {
|
||||
AuditEventSpaces
|
||||
|
||||
Name string
|
||||
Opaque map[string]string
|
||||
QuotaMaxBytes uint64
|
||||
}
|
||||
|
||||
// AuditEventUserCreated is the event logged when a user is created
|
||||
type AuditEventUserCreated struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventUserDeleted is the event logged when a user is deleted
|
||||
type AuditEventUserDeleted struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventUserFeatureChanged is the event logged when a user feature is changed
|
||||
type AuditEventUserFeatureChanged struct {
|
||||
AuditEvent
|
||||
UserID string
|
||||
Features []events.UserFeature
|
||||
}
|
||||
|
||||
// AuditEventGroupCreated is the event logged when a group is created
|
||||
type AuditEventGroupCreated struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
}
|
||||
|
||||
// AuditEventGroupDeleted is the event logged when a group is deleted
|
||||
type AuditEventGroupDeleted struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
}
|
||||
|
||||
// AuditEventGroupMemberAdded is the event logged when a group member is added
|
||||
type AuditEventGroupMemberAdded struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventGroupMemberRemoved is the event logged when a group member is removed
|
||||
type AuditEventGroupMemberRemoved struct {
|
||||
AuditEvent
|
||||
GroupID string
|
||||
UserID string
|
||||
}
|
||||
|
||||
// AuditEventScienceMeshInviteTokenGenerated is the event logged when a ScienceMesh invite token is generated
|
||||
type AuditEventScienceMeshInviteTokenGenerated struct {
|
||||
AuditEvent
|
||||
RecipientMail string
|
||||
Token string
|
||||
Description string
|
||||
Expiration uint64
|
||||
InviteLink string
|
||||
}
|
||||
Reference in New Issue
Block a user