mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 23:00:55 +03:00
EventManager: add datetime placeholder
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
@@ -92,8 +92,9 @@ func ExecutePreAction(conn *BaseConnection, operation, filePath, virtualPath str
|
||||
if !hasHook && !hasNotifiersPlugin && !hasRules {
|
||||
return 0, nil
|
||||
}
|
||||
dateTime := time.Now()
|
||||
event = newActionNotification(&conn.User, operation, filePath, virtualPath, "", "", "",
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, openFlags, conn.getNotificationStatus(nil), 0, nil)
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, openFlags, conn.getNotificationStatus(nil), 0, dateTime, nil)
|
||||
if hasNotifiersPlugin {
|
||||
plugin.Handler.NotifyFsEvent(event)
|
||||
}
|
||||
@@ -113,7 +114,7 @@ func ExecutePreAction(conn *BaseConnection, operation, filePath, virtualPath str
|
||||
Protocol: event.Protocol,
|
||||
IP: event.IP,
|
||||
Role: event.Role,
|
||||
Timestamp: event.Timestamp,
|
||||
Timestamp: dateTime,
|
||||
Email: conn.User.Email,
|
||||
Object: nil,
|
||||
}
|
||||
@@ -138,8 +139,9 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
|
||||
if !hasHook && !hasNotifiersPlugin && !hasRules {
|
||||
return nil
|
||||
}
|
||||
dateTime := time.Now()
|
||||
notification := newActionNotification(&conn.User, operation, filePath, virtualPath, target, virtualTarget, sshCmd,
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, 0, conn.getNotificationStatus(err), elapsed, metadata)
|
||||
conn.protocol, conn.GetRemoteIP(), conn.ID, fileSize, 0, conn.getNotificationStatus(err), elapsed, dateTime, metadata)
|
||||
if hasNotifiersPlugin {
|
||||
plugin.Handler.NotifyFsEvent(notification)
|
||||
}
|
||||
@@ -160,7 +162,7 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
|
||||
Protocol: notification.Protocol,
|
||||
IP: notification.IP,
|
||||
Role: notification.Role,
|
||||
Timestamp: notification.Timestamp,
|
||||
Timestamp: dateTime,
|
||||
Email: conn.User.Email,
|
||||
Object: nil,
|
||||
Metadata: metadata,
|
||||
@@ -198,6 +200,7 @@ func newActionNotification(
|
||||
operation, filePath, virtualPath, target, virtualTarget, sshCmd, protocol, ip, sessionID string,
|
||||
fileSize int64,
|
||||
openFlags, status int, elapsed int64,
|
||||
datetime time.Time,
|
||||
metadata map[string]string,
|
||||
) *notifier.FsEvent {
|
||||
var bucket, endpoint string
|
||||
@@ -239,7 +242,7 @@ func newActionNotification(
|
||||
SessionID: sessionID,
|
||||
OpenFlags: openFlags,
|
||||
Role: user.Role,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: datetime.UnixNano(),
|
||||
Elapsed: elapsed,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lithammer/shortuuid/v4"
|
||||
"github.com/rs/xid"
|
||||
@@ -71,7 +72,7 @@ func TestNewActionNotification(t *testing.T) {
|
||||
c := NewBaseConnection("id", ProtocolSSH, "", "", user)
|
||||
sessionID := xid.New().String()
|
||||
a := newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(errors.New("fake error")), 0, nil)
|
||||
123, 0, c.getNotificationStatus(errors.New("fake error")), 0, time.Now(), nil)
|
||||
assert.Equal(t, user.Username, a.Username)
|
||||
assert.Equal(t, 0, len(a.Bucket))
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
@@ -79,38 +80,38 @@ func TestNewActionNotification(t *testing.T) {
|
||||
|
||||
user.FsConfig.Provider = sdk.S3FilesystemProvider
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0, nil)
|
||||
123, 0, c.getNotificationStatus(nil), 0, time.Now(), nil)
|
||||
assert.Equal(t, "s3bucket", a.Bucket)
|
||||
assert.Equal(t, "endpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.GCSFilesystemProvider
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(ErrQuotaExceeded), 0, nil)
|
||||
123, 0, c.getNotificationStatus(ErrQuotaExceeded), 0, time.Now(), nil)
|
||||
assert.Equal(t, "gcsbucket", a.Bucket)
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
assert.Equal(t, 3, a.Status)
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(fmt.Errorf("wrapper quota error: %w", ErrQuotaExceeded)), 0, nil)
|
||||
123, 0, c.getNotificationStatus(fmt.Errorf("wrapper quota error: %w", ErrQuotaExceeded)), 0, time.Now(), nil)
|
||||
assert.Equal(t, "gcsbucket", a.Bucket)
|
||||
assert.Equal(t, 0, len(a.Endpoint))
|
||||
assert.Equal(t, 3, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.HTTPFilesystemProvider
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSSH, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0, nil)
|
||||
123, 0, c.getNotificationStatus(nil), 0, time.Now(), nil)
|
||||
assert.Equal(t, "httpendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
user.FsConfig.Provider = sdk.AzureBlobFilesystemProvider
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0, nil)
|
||||
123, 0, c.getNotificationStatus(nil), 0, time.Now(), nil)
|
||||
assert.Equal(t, "azcontainer", a.Bucket)
|
||||
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSCP, "", sessionID,
|
||||
123, os.O_APPEND, c.getNotificationStatus(nil), 0, nil)
|
||||
123, os.O_APPEND, c.getNotificationStatus(nil), 0, time.Now(), nil)
|
||||
assert.Equal(t, "azcontainer", a.Bucket)
|
||||
assert.Equal(t, "azendpoint", a.Endpoint)
|
||||
assert.Equal(t, 1, a.Status)
|
||||
@@ -118,7 +119,7 @@ func TestNewActionNotification(t *testing.T) {
|
||||
|
||||
user.FsConfig.Provider = sdk.SFTPFilesystemProvider
|
||||
a = newActionNotification(&user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, c.getNotificationStatus(nil), 0, nil)
|
||||
123, 0, c.getNotificationStatus(nil), 0, time.Now(), nil)
|
||||
assert.Equal(t, "sftpendpoint", a.Endpoint)
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ func TestActionHTTP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "",
|
||||
xid.New().String(), 123, 0, 1, 0, nil)
|
||||
xid.New().String(), 123, 0, 1, 0, time.Now(), nil)
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, status)
|
||||
@@ -175,7 +176,7 @@ func TestActionCMD(t *testing.T) {
|
||||
}
|
||||
sessionID := shortuuid.New()
|
||||
a := newActionNotification(user, operationDownload, "path", "vpath", "target", "", "", ProtocolSFTP, "", sessionID,
|
||||
123, 0, 1, 0, map[string]string{"key": "value"})
|
||||
123, 0, 1, 0, time.Now(), map[string]string{"key": "value"})
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, status)
|
||||
@@ -208,7 +209,7 @@ func TestWrongActions(t *testing.T) {
|
||||
}
|
||||
|
||||
a := newActionNotification(user, operationUpload, "", "", "", "", "", ProtocolSFTP, "", xid.New().String(),
|
||||
123, 0, 1, 0, nil)
|
||||
123, 0, 1, 0, time.Now(), nil)
|
||||
status, err := actionHandler.Handle(a)
|
||||
assert.Error(t, err, "action with bad command must fail")
|
||||
assert.Equal(t, 1, status)
|
||||
|
||||
@@ -110,7 +110,7 @@ func (d *dbDefender) AddEvent(ip, protocol string, event HostEvent) bool {
|
||||
eventManager.handleIPBlockedEvent(EventParams{
|
||||
Event: ipBlockedEventName,
|
||||
IP: ip,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Status: 1,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) bool {
|
||||
eventManager.handleIPBlockedEvent(EventParams{
|
||||
Event: ipBlockedEventName,
|
||||
IP: ip,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Status: 1,
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -58,6 +58,7 @@ const (
|
||||
maxAttachmentsSize = int64(10 * 1024 * 1024)
|
||||
objDataPlaceholder = "{{ObjectData}}"
|
||||
objDataPlaceholderString = "{{ObjectDataString}}"
|
||||
dateTimeMillisFormat = "2006-01-02T15:04:05.000"
|
||||
)
|
||||
|
||||
// Supported IDP login events
|
||||
@@ -89,7 +90,7 @@ func init() {
|
||||
ObjectType: objectType,
|
||||
IP: ip,
|
||||
Role: role,
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Object: object,
|
||||
}
|
||||
if u, ok := object.(*dataprovider.User); ok {
|
||||
@@ -557,7 +558,7 @@ type EventParams struct {
|
||||
IP string
|
||||
Role string
|
||||
Email string
|
||||
Timestamp int64
|
||||
Timestamp time.Time
|
||||
UID string
|
||||
IDPCustomFields *map[string]string
|
||||
Object plugin.Renderer
|
||||
@@ -641,7 +642,7 @@ func (p *EventParams) setBackupParams(backupPath string) {
|
||||
p.FsPath = backupPath
|
||||
p.ObjectName = filepath.Base(backupPath)
|
||||
p.VirtualPath = "/" + p.ObjectName
|
||||
p.Timestamp = time.Now().UnixNano()
|
||||
p.Timestamp = time.Now()
|
||||
info, err := os.Stat(backupPath)
|
||||
if err == nil {
|
||||
p.FileSize = info.Size()
|
||||
@@ -775,6 +776,12 @@ func (*EventParams) getStringReplacement(val string, jsonEscaped bool) string {
|
||||
}
|
||||
|
||||
func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []string {
|
||||
var dateTimeString string
|
||||
if Config.TZ == "local" {
|
||||
dateTimeString = p.Timestamp.Local().Format(dateTimeMillisFormat)
|
||||
} else {
|
||||
dateTimeString = p.Timestamp.UTC().Format(dateTimeMillisFormat)
|
||||
}
|
||||
replacements := []string{
|
||||
"{{Name}}", p.getStringReplacement(p.Name, jsonEscaped),
|
||||
"{{Event}}", p.Event,
|
||||
@@ -791,7 +798,8 @@ func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []s
|
||||
"{{IP}}", p.IP,
|
||||
"{{Role}}", p.getStringReplacement(p.Role, jsonEscaped),
|
||||
"{{Email}}", p.getStringReplacement(p.Email, jsonEscaped),
|
||||
"{{Timestamp}}", strconv.FormatInt(p.Timestamp, 10),
|
||||
"{{Timestamp}}", strconv.FormatInt(p.Timestamp.UnixNano(), 10),
|
||||
"{{DateTime}}", dateTimeString,
|
||||
"{{StatusString}}", p.getStatusString(),
|
||||
"{{UID}}", p.getStringReplacement(p.UID, jsonEscaped),
|
||||
"{{Ext}}", p.getStringReplacement(p.Extension, jsonEscaped),
|
||||
|
||||
@@ -800,6 +800,28 @@ func TestEventManagerErrors(t *testing.T) {
|
||||
stopEventScheduler()
|
||||
}
|
||||
|
||||
func TestDateTimePlaceholder(t *testing.T) {
|
||||
oldTZ := Config.TZ
|
||||
|
||||
Config.TZ = ""
|
||||
dateTime := time.Now()
|
||||
params := EventParams{
|
||||
Timestamp: dateTime,
|
||||
}
|
||||
replacements := params.getStringReplacements(false, false)
|
||||
r := strings.NewReplacer(replacements...)
|
||||
res := r.Replace("{{DateTime}}")
|
||||
assert.Equal(t, dateTime.UTC().Format(dateTimeMillisFormat), res)
|
||||
|
||||
Config.TZ = "local"
|
||||
replacements = params.getStringReplacements(false, false)
|
||||
r = strings.NewReplacer(replacements...)
|
||||
res = r.Replace("{{DateTime}}")
|
||||
assert.Equal(t, dateTime.Local().Format(dateTimeMillisFormat), res)
|
||||
|
||||
Config.TZ = oldTZ
|
||||
}
|
||||
|
||||
func TestEventRuleActions(t *testing.T) {
|
||||
actionName := "test rule action"
|
||||
action := dataprovider.BaseEventAction{
|
||||
|
||||
@@ -5431,7 +5431,7 @@ func TestBackupAsAttachment(t *testing.T) {
|
||||
|
||||
common.HandleCertificateEvent(common.EventParams{
|
||||
Name: "example.com",
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Status: 1,
|
||||
Event: renewalEvent,
|
||||
})
|
||||
@@ -7108,7 +7108,7 @@ func TestEventRuleCertificate(t *testing.T) {
|
||||
Recipients: []string{"test@example.com"},
|
||||
Subject: `"{{Event}} {{StatusString}}"`,
|
||||
ContentType: 0,
|
||||
Body: "Domain: {{Name}} Timestamp: {{Timestamp}} {{ErrorString}}",
|
||||
Body: "Domain: {{Name}} Timestamp: {{Timestamp}} {{ErrorString}} Date time: {{DateTime}}",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -7163,7 +7163,7 @@ func TestEventRuleCertificate(t *testing.T) {
|
||||
|
||||
common.HandleCertificateEvent(common.EventParams{
|
||||
Name: "example.com",
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Status: 1,
|
||||
Event: renewalEvent,
|
||||
})
|
||||
@@ -7178,9 +7178,10 @@ func TestEventRuleCertificate(t *testing.T) {
|
||||
assert.Contains(t, email.Data, `Domain: example.com Timestamp`)
|
||||
|
||||
lastReceivedEmail.reset()
|
||||
dateTime := time.Now()
|
||||
params := common.EventParams{
|
||||
Name: "example.com",
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: dateTime,
|
||||
Status: 2,
|
||||
Event: renewalEvent,
|
||||
}
|
||||
@@ -7195,6 +7196,7 @@ func TestEventRuleCertificate(t *testing.T) {
|
||||
assert.True(t, slices.Contains(email.To, "test@example.com"))
|
||||
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "%s KO"`, renewalEvent))
|
||||
assert.Contains(t, email.Data, `Domain: example.com Timestamp`)
|
||||
assert.Contains(t, email.Data, dateTime.UTC().Format("2006-01-02T15:04:05.000"))
|
||||
assert.Contains(t, email.Data, errRenew.Error())
|
||||
|
||||
_, err = httpdtest.RemoveEventRule(rule1, http.StatusOK)
|
||||
@@ -7208,7 +7210,7 @@ func TestEventRuleCertificate(t *testing.T) {
|
||||
// ignored no more certificate rules
|
||||
common.HandleCertificateEvent(common.EventParams{
|
||||
Name: "example.com",
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Timestamp: time.Now(),
|
||||
Status: 1,
|
||||
Event: renewalEvent,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user