Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -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
},
}
}
+35
View File
@@ -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), " ")
}
+45
View File
@@ -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{},
}
}
+244
View File
@@ -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)
}
+286
View File
@@ -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
}