mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
notifier plugin: add support for login succeeded events
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
e8140d7310
commit
456517af87
15 changed files with 58 additions and 24 deletions
|
@ -482,7 +482,7 @@ The configuration file contains the following sections:
|
||||||
- `fs_events`, list of strings. Defines the filesystem events that will be notified to this plugin.
|
- `fs_events`, list of strings. Defines the filesystem events that will be notified to this plugin.
|
||||||
- `provider_events`, list of strings. Defines the provider events that will be notified to this plugin.
|
- `provider_events`, list of strings. Defines the provider events that will be notified to this plugin.
|
||||||
- `provider_objects`, list if strings. Defines the provider objects that will be notified to this plugin.
|
- `provider_objects`, list if strings. Defines the provider objects that will be notified to this plugin.
|
||||||
- `log_events`, list of integers. Defines the log events that will be notified to this plugin. `1` means "Login failed", `2` means "Login with non-existent user", `3` means "No login tried", `4` means "Algorithm negotiation failed".
|
- `log_events`, list of integers. Defines the log events that will be notified to this plugin. `1` means "Login failed", `2` means "Login with non-existent user", `3` means "No login tried", `4` means "Algorithm negotiation failed", `5` means "Login succeeded".
|
||||||
- `retry_max_time`, integer. Defines the maximum number of seconds an event can be late. SFTPGo adds a timestamp to each event and add to an internal queue any events that a the plugin fails to handle (the plugin returns an error or it is not running). If a plugin fails to handle an event that is too late, based on this configuration, it will be discarded. SFTPGo will try to resend queued events every 30 seconds. 0 means no retry.
|
- `retry_max_time`, integer. Defines the maximum number of seconds an event can be late. SFTPGo adds a timestamp to each event and add to an internal queue any events that a the plugin fails to handle (the plugin returns an error or it is not running). If a plugin fails to handle an event that is too late, based on this configuration, it will be discarded. SFTPGo will try to resend queued events every 30 seconds. 0 means no retry.
|
||||||
- `retry_queue_max_size`, integer. Defines the maximum number of events that the internal queue can hold. Once the queue is full, the events that cannot be sent to the plugin will be discarded. 0 means no limit.
|
- `retry_queue_max_size`, integer. Defines the maximum number of events that the internal queue can hold. Once the queue is full, the events that cannot be sent to the plugin will be discarded. 0 means no limit.
|
||||||
- `kms_options`, struct. Defines the options for kms plugins.
|
- `kms_options`, struct. Defines the options for kms plugins.
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -46,14 +46,14 @@ require (
|
||||||
github.com/minio/sio v0.3.1
|
github.com/minio/sio v0.3.1
|
||||||
github.com/otiai10/copy v1.14.0
|
github.com/otiai10/copy v1.14.0
|
||||||
github.com/pires/go-proxyproto v0.7.0
|
github.com/pires/go-proxyproto v0.7.0
|
||||||
github.com/pkg/sftp v1.13.6
|
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/prometheus/client_golang v1.19.0
|
github.com/prometheus/client_golang v1.19.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rs/cors v1.10.1
|
github.com/rs/cors v1.10.1
|
||||||
github.com/rs/xid v1.5.0
|
github.com/rs/xid v1.5.0
|
||||||
github.com/rs/zerolog v1.32.0
|
github.com/rs/zerolog v1.32.0
|
||||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3
|
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896
|
||||||
github.com/shirou/gopsutil/v3 v3.24.3
|
github.com/shirou/gopsutil/v3 v3.24.3
|
||||||
github.com/spf13/afero v1.11.0
|
github.com/spf13/afero v1.11.0
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
|
@ -185,7 +185,6 @@ require (
|
||||||
replace (
|
replace (
|
||||||
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20240313174824-cf52df3aa8f7
|
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20240313174824-cf52df3aa8f7
|
||||||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2
|
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20240210102745-f1ffc43f78d2
|
||||||
github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20240214104840-fbb0b8bdb30c
|
|
||||||
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
|
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
|
||||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20240405104909-a6b14455cac6
|
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20240405104909-a6b14455cac6
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -314,6 +314,8 @@ github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317 h1:kupFhKi4R3XqKmUmqGSHWn/WZbC9CnwSoW421tL1gGw=
|
||||||
|
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
@ -353,6 +355,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3 h1:svxTNm3r2kRlpuVSUKi0WKQlsAq8VI0EzDWPNqeNn/o=
|
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3 h1:svxTNm3r2kRlpuVSUKi0WKQlsAq8VI0EzDWPNqeNn/o=
|
||||||
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
|
github.com/sftpgo/sdk v0.1.6-0.20240317102632-f6eb95ea55c3/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
|
||||||
|
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896 h1:ykxybS9WKurHTatKJ9WjqYD+WH9YH/2QMxCkxUPTVLY=
|
||||||
|
github.com/sftpgo/sdk v0.1.6-0.20240409173349-421b3dff3896/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
|
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
|
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
|
|
@ -415,7 +415,9 @@ func setStartDirectory(startDirectory string, cc ftpserver.ClientContext) {
|
||||||
|
|
||||||
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
||||||
metric.AddLoginAttempt(loginMethod)
|
metric.AddLoginAttempt(loginMethod)
|
||||||
if err != nil && err != common.ErrInternalFailure {
|
if err == nil {
|
||||||
|
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolFTP, user.Username, ip, "", nil)
|
||||||
|
} else if err != common.ErrInternalFailure {
|
||||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod,
|
logger.ConnectionFailedLog(user.Username, ip, loginMethod,
|
||||||
common.ProtocolFTP, err.Error())
|
common.ProtocolFTP, err.Error())
|
||||||
event := common.HostEventLoginFailed
|
event := common.HostEventLoginFailed
|
||||||
|
|
|
@ -472,6 +472,8 @@ func getLogEventString(event notifier.LogEventType) string {
|
||||||
return "No login tried"
|
return "No login tried"
|
||||||
case notifier.LogEventTypeNotNegotiated:
|
case notifier.LogEventTypeNotNegotiated:
|
||||||
return "Algorithm negotiation failed"
|
return "Algorithm negotiation failed"
|
||||||
|
case notifier.LogEventTypeLoginOK:
|
||||||
|
return "Login succeeded"
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -697,7 +697,9 @@ func updateLoginMetrics(user *dataprovider.User, loginMethod, ip string, err err
|
||||||
default:
|
default:
|
||||||
protocol = common.ProtocolHTTP
|
protocol = common.ProtocolHTTP
|
||||||
}
|
}
|
||||||
if err != nil && err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
if err == nil {
|
||||||
|
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, protocol, user.Username, ip, "", nil)
|
||||||
|
} else if err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod, protocol, err.Error())
|
logger.ConnectionFailedLog(user.Username, ip, loginMethod, protocol, err.Error())
|
||||||
err = handleDefenderEventLoginFailed(ip, err)
|
err = handleDefenderEventLoginFailed(ip, err)
|
||||||
logEv := notifier.LogEventTypeLoginFailed
|
logEv := notifier.LogEventTypeLoginFailed
|
||||||
|
|
|
@ -3398,6 +3398,7 @@ func TestGetLogEventString(t *testing.T) {
|
||||||
assert.Equal(t, "Login with non-existent user", getLogEventString(notifier.LogEventTypeLoginNoUser))
|
assert.Equal(t, "Login with non-existent user", getLogEventString(notifier.LogEventTypeLoginNoUser))
|
||||||
assert.Equal(t, "No login tried", getLogEventString(notifier.LogEventTypeNoLoginTried))
|
assert.Equal(t, "No login tried", getLogEventString(notifier.LogEventTypeNoLoginTried))
|
||||||
assert.Equal(t, "Algorithm negotiation failed", getLogEventString(notifier.LogEventTypeNotNegotiated))
|
assert.Equal(t, "Algorithm negotiation failed", getLogEventString(notifier.LogEventTypeNotNegotiated))
|
||||||
|
assert.Equal(t, "Login succeeded", getLogEventString(notifier.LogEventTypeLoginOK))
|
||||||
assert.Empty(t, getLogEventString(0))
|
assert.Empty(t, getLogEventString(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,9 @@ func (c *NotifierConfig) hasActions() bool {
|
||||||
if len(c.ProviderEvents) > 0 && len(c.ProviderObjects) > 0 {
|
if len(c.ProviderEvents) > 0 && len(c.ProviderObjects) > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if len(c.LogEvents) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,10 +253,6 @@ func (p *notifierPlugin) notifyProviderAction(event *notifier.ProviderEvent, obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *notifierPlugin) notifyLogEvent(event *notifier.LogEvent) {
|
func (p *notifierPlugin) notifyLogEvent(event *notifier.LogEvent) {
|
||||||
if !util.Contains(p.config.NotifierOptions.LogEvents, int(event.Event)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
Handler.addTask()
|
Handler.addTask()
|
||||||
defer Handler.removeTask()
|
defer Handler.removeTask()
|
||||||
|
|
|
@ -331,18 +331,28 @@ func (m *Manager) NotifyLogEvent(event notifier.LogEventType, protocol, username
|
||||||
m.notifLock.RLock()
|
m.notifLock.RLock()
|
||||||
defer m.notifLock.RUnlock()
|
defer m.notifLock.RUnlock()
|
||||||
|
|
||||||
e := ¬ifier.LogEvent{
|
var e *notifier.LogEvent
|
||||||
Timestamp: time.Now().UnixNano(),
|
|
||||||
Event: event,
|
|
||||||
Protocol: protocol,
|
|
||||||
Username: username,
|
|
||||||
IP: ip,
|
|
||||||
Message: err.Error(),
|
|
||||||
Role: role,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, n := range m.notifiers {
|
for _, n := range m.notifiers {
|
||||||
n.notifyLogEvent(e)
|
if util.Contains(n.config.NotifierOptions.LogEvents, int(event)) {
|
||||||
|
if e == nil {
|
||||||
|
message := ""
|
||||||
|
if err != nil {
|
||||||
|
message = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
e = ¬ifier.LogEvent{
|
||||||
|
Timestamp: time.Now().UnixNano(),
|
||||||
|
Event: event,
|
||||||
|
Protocol: protocol,
|
||||||
|
Username: username,
|
||||||
|
IP: ip,
|
||||||
|
Message: message,
|
||||||
|
Role: role,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.notifyLogEvent(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1248,7 +1248,9 @@ func (c *Configuration) validateKeyboardInteractiveCredentials(conn ssh.ConnMeta
|
||||||
|
|
||||||
func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) {
|
func updateLoginMetrics(user *dataprovider.User, ip, method string, err error) {
|
||||||
metric.AddLoginAttempt(method)
|
metric.AddLoginAttempt(method)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
|
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolSSH, user.Username, ip, "", err)
|
||||||
|
} else {
|
||||||
logger.ConnectionFailedLog(user.Username, ip, method, common.ProtocolSSH, err.Error())
|
logger.ConnectionFailedLog(user.Username, ip, method, common.ProtocolSSH, err.Error())
|
||||||
if method != dataprovider.SSHLoginMethodPublicKey {
|
if method != dataprovider.SSHLoginMethodPublicKey {
|
||||||
// some clients try all available public keys for a user, we
|
// some clients try all available public keys for a user, we
|
||||||
|
|
|
@ -422,7 +422,9 @@ func writeLog(r *http.Request, status int, err error) {
|
||||||
|
|
||||||
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
func updateLoginMetrics(user *dataprovider.User, ip, loginMethod string, err error) {
|
||||||
metric.AddLoginAttempt(loginMethod)
|
metric.AddLoginAttempt(loginMethod)
|
||||||
if err != nil && err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
if err == nil {
|
||||||
|
plugin.Handler.NotifyLogEvent(notifier.LogEventTypeLoginOK, common.ProtocolWebDAV, user.Username, ip, "", nil)
|
||||||
|
} else if err != common.ErrInternalFailure && err != common.ErrNoCredentials {
|
||||||
logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolWebDAV, err.Error())
|
logger.ConnectionFailedLog(user.Username, ip, loginMethod, common.ProtocolWebDAV, err.Error())
|
||||||
event := common.HostEventLoginFailed
|
event := common.HostEventLoginFailed
|
||||||
logEv := notifier.LogEventTypeLoginFailed
|
logEv := notifier.LogEventTypeLoginFailed
|
||||||
|
|
|
@ -5214,12 +5214,14 @@ components:
|
||||||
- 2
|
- 2
|
||||||
- 3
|
- 3
|
||||||
- 4
|
- 4
|
||||||
|
- 5
|
||||||
description: >
|
description: >
|
||||||
Event status:
|
Event status:
|
||||||
* `1` - Login failed
|
* `1` - Login failed
|
||||||
* `2` - Login failed non-existent user
|
* `2` - Login failed non-existent user
|
||||||
* `3` - No login tried
|
* `3` - No login tried
|
||||||
* `4` - Algorithm negotiation failed
|
* `4` - Algorithm negotiation failed
|
||||||
|
* `5` - Login succeeded
|
||||||
FsEventStatus:
|
FsEventStatus:
|
||||||
type: integer
|
type: integer
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -900,6 +900,7 @@
|
||||||
"add": "Addition",
|
"add": "Addition",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"login_failed": "Login failed",
|
"login_failed": "Login failed",
|
||||||
|
"login_ok": "Login succeeded",
|
||||||
"login_missing_user": "Login with non-existent user",
|
"login_missing_user": "Login with non-existent user",
|
||||||
"no_login_tried": "No login tried",
|
"no_login_tried": "No login tried",
|
||||||
"algo_negotiation_failed": "Algorithm negotiation failed",
|
"algo_negotiation_failed": "Algorithm negotiation failed",
|
||||||
|
|
|
@ -900,6 +900,7 @@
|
||||||
"add": "Aggiunta",
|
"add": "Aggiunta",
|
||||||
"update": "Aggiornamento",
|
"update": "Aggiornamento",
|
||||||
"login_failed": "Accesso fallito",
|
"login_failed": "Accesso fallito",
|
||||||
|
"login_ok": "Accesso riuscito",
|
||||||
"login_missing_user": "Accesso con utente inesistente",
|
"login_missing_user": "Accesso con utente inesistente",
|
||||||
"no_login_tried": "Nessun accesso tentato",
|
"no_login_tried": "Nessun accesso tentato",
|
||||||
"algo_negotiation_failed": "Negoziazione algoritmo fallita",
|
"algo_negotiation_failed": "Negoziazione algoritmo fallita",
|
||||||
|
|
|
@ -385,6 +385,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
function selectLogEvents(){
|
function selectLogEvents(){
|
||||||
let idActions = $('#idActions');
|
let idActions = $('#idActions');
|
||||||
idActions.empty();
|
idActions.empty();
|
||||||
|
idActions.append(new Option($.t('events.login_ok'),"5",false,false));
|
||||||
idActions.append(new Option($.t('events.login_failed'),"1",false,false));
|
idActions.append(new Option($.t('events.login_failed'),"1",false,false));
|
||||||
idActions.append(new Option($.t('events.login_missing_user'),"2",false,false));
|
idActions.append(new Option($.t('events.login_missing_user'),"2",false,false));
|
||||||
idActions.append(new Option($.t('events.no_login_tried'),"3",false,false));
|
idActions.append(new Option($.t('events.no_login_tried'),"3",false,false));
|
||||||
|
@ -875,6 +876,8 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
return $.t('events.no_login_tried');
|
return $.t('events.no_login_tried');
|
||||||
case 4:
|
case 4:
|
||||||
return $.t('events.algo_negotiation_failed');
|
return $.t('events.algo_negotiation_failed');
|
||||||
|
case 5:
|
||||||
|
return $.t('events.login_ok');
|
||||||
default:
|
default:
|
||||||
console.log(`unknown log action "${data}"`);
|
console.log(`unknown log action "${data}"`);
|
||||||
return "";
|
return "";
|
||||||
|
@ -914,7 +917,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
defaultContent: "",
|
defaultContent: "",
|
||||||
render: function(data, type, row) {
|
render: function(data, type, row) {
|
||||||
if (type === 'display') {
|
if (type === 'display') {
|
||||||
return escapeHTML(data);
|
if (data){
|
||||||
|
return escapeHTML(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -924,7 +929,9 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
defaultContent: "",
|
defaultContent: "",
|
||||||
render: function(data, type, row) {
|
render: function(data, type, row) {
|
||||||
if (type === 'display') {
|
if (type === 'display') {
|
||||||
return escapeHTML(data);
|
if (data){
|
||||||
|
return escapeHTML(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue