REST API: add events search
This commit is contained in:
parent
97d0a48557
commit
74fc3aaf37
25 changed files with 1708 additions and 55 deletions
19
.github/workflows/development.yml
vendored
19
.github/workflows/development.yml
vendored
|
@ -31,7 +31,11 @@ jobs:
|
||||||
|
|
||||||
- name: Build for Linux/macOS x86_64
|
- name: Build for Linux/macOS x86_64
|
||||||
if: startsWith(matrix.os, 'windows-') != true
|
if: startsWith(matrix.os, 'windows-') != true
|
||||||
run: go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
run: |
|
||||||
|
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||||
|
cd tests/eventsearcher
|
||||||
|
go build -trimpath -ldflags "-s -w" -o eventsearcher
|
||||||
|
cd -
|
||||||
|
|
||||||
- name: Build for macOS arm64
|
- name: Build for macOS arm64
|
||||||
if: startsWith(matrix.os, 'macos-') == true
|
if: startsWith(matrix.os, 'macos-') == true
|
||||||
|
@ -43,6 +47,9 @@ jobs:
|
||||||
$GIT_COMMIT = (git describe --always --dirty) | Out-String
|
$GIT_COMMIT = (git describe --always --dirty) | Out-String
|
||||||
$DATE_TIME = ([datetime]::Now.ToUniversalTime().toString("yyyy-MM-ddTHH:mm:ssZ")) | Out-String
|
$DATE_TIME = ([datetime]::Now.ToUniversalTime().toString("yyyy-MM-ddTHH:mm:ssZ")) | Out-String
|
||||||
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/version.date=$DATE_TIME" -o sftpgo.exe
|
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/version.date=$DATE_TIME" -o sftpgo.exe
|
||||||
|
cd tests/eventsearcher
|
||||||
|
go build -trimpath -ldflags "-s -w" -o eventsearcher.exe
|
||||||
|
cd ../..
|
||||||
|
|
||||||
- name: Run test cases using SQLite provider
|
- name: Run test cases using SQLite provider
|
||||||
run: go test -v -p 1 -timeout 15m ./... -coverprofile=coverage.txt -covermode=atomic
|
run: go test -v -p 1 -timeout 15m ./... -coverprofile=coverage.txt -covermode=atomic
|
||||||
|
@ -120,7 +127,10 @@ jobs:
|
||||||
go-version: 1.17
|
go-version: 1.17
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
run: |
|
||||||
|
cd tests/eventsearcher
|
||||||
|
go build -trimpath -ldflags "-s -w" -o eventsearcher
|
||||||
|
cd -
|
||||||
env:
|
env:
|
||||||
GOARCH: 386
|
GOARCH: 386
|
||||||
|
|
||||||
|
@ -173,7 +183,10 @@ jobs:
|
||||||
go-version: 1.17
|
go-version: 1.17
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
run: |
|
||||||
|
cd tests/eventsearcher
|
||||||
|
go build -trimpath -ldflags "-s -w" -o eventsearcher
|
||||||
|
cd -
|
||||||
|
|
||||||
- name: Run tests using PostgreSQL provider
|
- name: Run tests using PostgreSQL provider
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -41,3 +41,4 @@ linters:
|
||||||
- dupl
|
- dupl
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- dogsled
|
- dogsled
|
||||||
|
- govet
|
||||||
|
|
|
@ -422,6 +422,11 @@ func GetPluginsConfig() []plugin.Config {
|
||||||
return globalConf.PluginsConfig
|
return globalConf.PluginsConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPluginsConfig sets the plugin configuration
|
||||||
|
func SetPluginsConfig(config []plugin.Config) {
|
||||||
|
globalConf.PluginsConfig = config
|
||||||
|
}
|
||||||
|
|
||||||
// GetMFAConfig returns multi-factor authentication config
|
// GetMFAConfig returns multi-factor authentication config
|
||||||
func GetMFAConfig() mfa.Config {
|
func GetMFAConfig() mfa.Config {
|
||||||
return globalConf.MFAConfig
|
return globalConf.MFAConfig
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/httpd"
|
"github.com/drakkan/sftpgo/v2/httpd"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
"github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/smtp"
|
"github.com/drakkan/sftpgo/v2/smtp"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
|
@ -292,6 +293,15 @@ func TestSetGetConfig(t *testing.T) {
|
||||||
config.SetTelemetryConfig(telemetryConf)
|
config.SetTelemetryConfig(telemetryConf)
|
||||||
assert.Equal(t, telemetryConf.BindPort, config.GetTelemetryConfig().BindPort)
|
assert.Equal(t, telemetryConf.BindPort, config.GetTelemetryConfig().BindPort)
|
||||||
assert.Equal(t, telemetryConf.BindAddress, config.GetTelemetryConfig().BindAddress)
|
assert.Equal(t, telemetryConf.BindAddress, config.GetTelemetryConfig().BindAddress)
|
||||||
|
pluginConf := []plugin.Config{
|
||||||
|
{
|
||||||
|
Type: "eventsearcher",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.SetPluginsConfig(pluginConf)
|
||||||
|
if assert.Len(t, config.GetPluginsConfig(), 1) {
|
||||||
|
assert.Equal(t, pluginConf[0].Type, config.GetPluginsConfig()[0].Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceToStart(t *testing.T) {
|
func TestServiceToStart(t *testing.T) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ const (
|
||||||
PermAdminManageDefender = "manage_defender"
|
PermAdminManageDefender = "manage_defender"
|
||||||
PermAdminViewDefender = "view_defender"
|
PermAdminViewDefender = "view_defender"
|
||||||
PermAdminRetentionChecks = "retention_checks"
|
PermAdminRetentionChecks = "retention_checks"
|
||||||
|
PermAdminViewEvents = "view_events"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -46,7 +47,7 @@ var (
|
||||||
validAdminPerms = []string{PermAdminAny, PermAdminAddUsers, PermAdminChangeUsers, PermAdminDeleteUsers,
|
validAdminPerms = []string{PermAdminAny, PermAdminAddUsers, PermAdminChangeUsers, PermAdminDeleteUsers,
|
||||||
PermAdminViewUsers, PermAdminViewConnections, PermAdminCloseConnections, PermAdminViewServerStatus,
|
PermAdminViewUsers, PermAdminViewConnections, PermAdminCloseConnections, PermAdminViewServerStatus,
|
||||||
PermAdminManageAdmins, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem,
|
PermAdminManageAdmins, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem,
|
||||||
PermAdminManageDefender, PermAdminViewDefender, PermAdminRetentionChecks}
|
PermAdminManageDefender, PermAdminViewDefender, PermAdminRetentionChecks, PermAdminViewEvents}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TOTPConfig defines the time-based one time password configuration
|
// TOTPConfig defines the time-based one time password configuration
|
||||||
|
|
|
@ -111,4 +111,4 @@ You can forward SFTPGo events to several publish/subscribe systems using the [sf
|
||||||
|
|
||||||
## Database services
|
## Database services
|
||||||
|
|
||||||
You can store SFTPGo events in database systems using the [sftpgo-plugin-eventstore](https://github.com/sftpgo/sftpgo-plugin-eventstore).
|
You can store SFTPGo events in database systems using the [sftpgo-plugin-eventstore](https://github.com/sftpgo/sftpgo-plugin-eventstore) and you can search the stored events using the [sftpgo-plugin-eventsearch](https://github.com/sftpgo/sftpgo-plugin-eventsearch).
|
||||||
|
|
|
@ -36,6 +36,7 @@ You can create other administrator and assign them the following permissions:
|
||||||
- manage system
|
- manage system
|
||||||
- manage admins
|
- manage admins
|
||||||
- manage data retention
|
- manage data retention
|
||||||
|
- view events
|
||||||
|
|
||||||
You can also restrict administrator access based on the source IP address. If you are running SFTPGo behind a reverse proxy you need to allow both the proxy IP address and the real client IP.
|
You can also restrict administrator access based on the source IP address. If you are running SFTPGo behind a reverse proxy you need to allow both the proxy IP address and the real client IP.
|
||||||
|
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -7,7 +7,7 @@ require (
|
||||||
github.com/Azure/azure-storage-blob-go v0.14.0
|
github.com/Azure/azure-storage-blob-go v0.14.0
|
||||||
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
|
||||||
github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8
|
github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8
|
||||||
github.com/aws/aws-sdk-go v1.41.6
|
github.com/aws/aws-sdk-go v1.41.9
|
||||||
github.com/cockroachdb/cockroach-go/v2 v2.2.1
|
github.com/cockroachdb/cockroach-go/v2 v2.2.1
|
||||||
github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b
|
github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
|
@ -29,7 +29,7 @@ require (
|
||||||
github.com/klauspost/compress v1.13.6
|
github.com/klauspost/compress v1.13.6
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||||
github.com/lestrrat-go/jwx v1.2.7
|
github.com/lestrrat-go/jwx v1.2.8
|
||||||
github.com/lib/pq v1.10.3
|
github.com/lib/pq v1.10.3
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
@ -44,7 +44,7 @@ require (
|
||||||
github.com/pkg/sftp v1.13.4
|
github.com/pkg/sftp v1.13.4
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.3.0
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/prometheus/common v0.32.0 // indirect
|
github.com/prometheus/common v0.32.1 // indirect
|
||||||
github.com/rs/cors v1.8.0
|
github.com/rs/cors v1.8.0
|
||||||
github.com/rs/xid v1.3.0
|
github.com/rs/xid v1.3.0
|
||||||
github.com/rs/zerolog v1.25.0
|
github.com/rs/zerolog v1.25.0
|
||||||
|
@ -63,10 +63,10 @@ require (
|
||||||
gocloud.dev v0.24.0
|
gocloud.dev v0.24.0
|
||||||
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
|
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f
|
||||||
golang.org/x/sys v0.0.0-20211020154033-fcb26fe61c20
|
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
||||||
google.golang.org/api v0.59.0
|
google.golang.org/api v0.59.0
|
||||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a // indirect
|
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect
|
||||||
google.golang.org/grpc v1.41.0
|
google.golang.org/grpc v1.41.0
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -137,8 +137,8 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo
|
||||||
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||||
github.com/aws/aws-sdk-go v1.41.6 h1:ojO1jWhE3lkJlTFQOq0rlWZ11q18LIdsZNtGJ07FFEA=
|
github.com/aws/aws-sdk-go v1.41.9 h1:Xb4gWjA90ju0u6Fr2lMAsMOGuhw1g4sTFOqh9SUHgN0=
|
||||||
github.com/aws/aws-sdk-go v1.41.6/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
github.com/aws/aws-sdk-go v1.41.9/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4=
|
github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
|
github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY=
|
||||||
|
@ -290,7 +290,6 @@ github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
github.com/goccy/go-json v0.7.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.7.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
||||||
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
|
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
|
||||||
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
@ -549,14 +548,13 @@ github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBB
|
||||||
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
|
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
|
||||||
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
|
||||||
github.com/lestrrat-go/codegen v1.0.1/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
github.com/lestrrat-go/codegen v1.0.1/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
||||||
github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
|
|
||||||
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
|
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
|
||||||
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
|
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
|
||||||
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
|
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
|
||||||
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||||
github.com/lestrrat-go/jwx v1.2.6/go.mod h1:tJuGuAI3LC71IicTx82Mz1n3w9woAs2bYJZpkjJQ5aU=
|
github.com/lestrrat-go/jwx v1.2.6/go.mod h1:tJuGuAI3LC71IicTx82Mz1n3w9woAs2bYJZpkjJQ5aU=
|
||||||
github.com/lestrrat-go/jwx v1.2.7 h1:wO7fEc3PW56wpQBMU5CyRkrk4DVsXxCoJg7oIm5HHE4=
|
github.com/lestrrat-go/jwx v1.2.8 h1:qvSpsYZrI5gyFUnb6cmaEqef470/glBiz2ADEDFlyT0=
|
||||||
github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA=
|
github.com/lestrrat-go/jwx v1.2.8/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
|
||||||
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
|
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
|
||||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -688,8 +686,8 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
github.com/prometheus/common v0.32.0 h1:HRmM4uANZDAjdvbsdfOoqI5UDbjz0faKeMs/cGPKKI0=
|
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||||
github.com/prometheus/common v0.32.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
|
@ -965,8 +963,8 @@ golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211020154033-fcb26fe61c20 h1:NPtIbR2t8Mhg6UCbkTQMNMejkPpH6IvV4PdZnh1Mqpk=
|
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA=
|
||||||
golang.org/x/sys v0.0.0-20211020154033-fcb26fe61c20/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -1169,8 +1167,8 @@ google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEc
|
||||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a h1:8maMHMQp9NroHXhc3HelFX9Ay2lWlXLcdH5mw5Biz0s=
|
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c h1:FqrtZMB5Wr+/RecOM3uPJNPfWR8Upb5hAPnt7PU6i4k=
|
||||||
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
|
154
httpd/api_events.go
Normal file
154
httpd/api_events.go
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
package httpd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commonEventSearchParams struct {
|
||||||
|
StartTimestamp int64
|
||||||
|
EndTimestamp int64
|
||||||
|
Actions []string
|
||||||
|
Username string
|
||||||
|
IP string
|
||||||
|
InstanceIDs []string
|
||||||
|
ExcludeIDs []string
|
||||||
|
Limit int
|
||||||
|
Order int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commonEventSearchParams) fromRequest(r *http.Request) error {
|
||||||
|
c.Limit = 100
|
||||||
|
|
||||||
|
if _, ok := r.URL.Query()["limit"]; ok {
|
||||||
|
limit, err := strconv.Atoi(r.URL.Query().Get("limit"))
|
||||||
|
if err != nil {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("invalid limit: %v", err))
|
||||||
|
}
|
||||||
|
if limit < 1 || limit > 1000 {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("limit is out of the 1-1000 range: %v", limit))
|
||||||
|
}
|
||||||
|
c.Limit = limit
|
||||||
|
}
|
||||||
|
if _, ok := r.URL.Query()["order"]; ok {
|
||||||
|
order := r.URL.Query().Get("order")
|
||||||
|
if order != dataprovider.OrderASC && order != dataprovider.OrderDESC {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("invalid order %#v", order))
|
||||||
|
}
|
||||||
|
if order == dataprovider.OrderASC {
|
||||||
|
c.Order = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := r.URL.Query()["start_timestamp"]; ok {
|
||||||
|
ts, err := strconv.ParseInt(r.URL.Query().Get("start_timestamp"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("invalid start_timestamp: %v", err))
|
||||||
|
}
|
||||||
|
c.StartTimestamp = ts
|
||||||
|
}
|
||||||
|
if _, ok := r.URL.Query()["end_timestamp"]; ok {
|
||||||
|
ts, err := strconv.ParseInt(r.URL.Query().Get("end_timestamp"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("invalid end_timestamp: %v", err))
|
||||||
|
}
|
||||||
|
c.EndTimestamp = ts
|
||||||
|
}
|
||||||
|
c.Actions = getCommaSeparatedQueryParam(r, "actions")
|
||||||
|
c.Username = r.URL.Query().Get("username")
|
||||||
|
c.IP = r.URL.Query().Get("ip")
|
||||||
|
c.InstanceIDs = getCommaSeparatedQueryParam(r, "instance_ids")
|
||||||
|
c.ExcludeIDs = getCommaSeparatedQueryParam(r, "exclude_ids")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type fsEventSearchParams struct {
|
||||||
|
commonEventSearchParams
|
||||||
|
SSHCmd string
|
||||||
|
Protocols []string
|
||||||
|
Statuses []int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fsEventSearchParams) fromRequest(r *http.Request) error {
|
||||||
|
if err := s.commonEventSearchParams.fromRequest(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.IP = r.URL.Query().Get("ip")
|
||||||
|
s.SSHCmd = r.URL.Query().Get("ssh_cmd")
|
||||||
|
s.Protocols = getCommaSeparatedQueryParam(r, "protocols")
|
||||||
|
statuses := getCommaSeparatedQueryParam(r, "statuses")
|
||||||
|
for _, status := range statuses {
|
||||||
|
val, err := strconv.Atoi(status)
|
||||||
|
if err != nil {
|
||||||
|
return util.NewValidationError(fmt.Sprintf("invalid status: %v", status))
|
||||||
|
}
|
||||||
|
s.Statuses = append(s.Statuses, int32(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type providerEventSearchParams struct {
|
||||||
|
commonEventSearchParams
|
||||||
|
ObjectName string
|
||||||
|
ObjectTypes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *providerEventSearchParams) fromRequest(r *http.Request) error {
|
||||||
|
if err := s.commonEventSearchParams.fromRequest(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.ObjectName = r.URL.Query().Get("object_name")
|
||||||
|
s.ObjectTypes = getCommaSeparatedQueryParam(r, "object_types")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchFsEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||||
|
|
||||||
|
params := fsEventSearchParams{}
|
||||||
|
err := params.fromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, _, err := plugin.Handler.SearchFsEvents(params.StartTimestamp, params.EndTimestamp, params.Username,
|
||||||
|
params.IP, params.SSHCmd, params.Actions, params.Protocols, params.InstanceIDs, params.ExcludeIDs,
|
||||||
|
params.Statuses, params.Limit, params.Order)
|
||||||
|
if err != nil {
|
||||||
|
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Write(data) //nolint:errcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchProviderEvents(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||||
|
|
||||||
|
params := providerEventSearchParams{}
|
||||||
|
err := params.fromRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, _, err := plugin.Handler.SearchProviderEvents(params.StartTimestamp, params.EndTimestamp, params.Username,
|
||||||
|
params.IP, params.ObjectName, params.Limit, params.Order, params.Actions, params.ObjectTypes, params.InstanceIDs,
|
||||||
|
params.ExcludeIDs)
|
||||||
|
if err != nil {
|
||||||
|
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Write(data) //nolint:errcheck
|
||||||
|
}
|
|
@ -3,13 +3,11 @@ package httpd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/common"
|
"github.com/drakkan/sftpgo/v2/common"
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getRetentionChecks(w http.ResponseWriter, r *http.Request) {
|
func getRetentionChecks(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -26,18 +24,14 @@ func startRetentionCheck(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var check common.RetentionCheck
|
var check common.RetentionCheck
|
||||||
|
|
||||||
err = render.DecodeJSON(r.Body, &check.Folders)
|
err = render.DecodeJSON(r.Body, &check.Folders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
|
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, val := range strings.Split(r.URL.Query().Get("notifications"), ",") {
|
|
||||||
val = strings.TrimSpace(val)
|
check.Notifications = getCommaSeparatedQueryParam(r, "notifications")
|
||||||
if val != "" {
|
|
||||||
check.Notifications = append(check.Notifications, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check.Notifications = util.RemoveDuplicates(check.Notifications)
|
|
||||||
for _, notification := range check.Notifications {
|
for _, notification := range check.Notifications {
|
||||||
if notification == common.RetentionCheckNotificationEmail {
|
if notification == common.RetentionCheckNotificationEmail {
|
||||||
claims, err := getTokenClaims(r)
|
claims, err := getTokenClaims(r)
|
||||||
|
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/klauspost/compress/zip"
|
"github.com/klauspost/compress/zip"
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/metric"
|
"github.com/drakkan/sftpgo/v2/metric"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,6 +77,9 @@ func getRespStatus(err error) int {
|
||||||
if os.IsPermission(err) {
|
if os.IsPermission(err) {
|
||||||
return http.StatusForbidden
|
return http.StatusForbidden
|
||||||
}
|
}
|
||||||
|
if errors.Is(err, plugin.ErrNoSearcher) {
|
||||||
|
return http.StatusNotImplemented
|
||||||
|
}
|
||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +96,28 @@ func getMappedStatusCode(err error) int {
|
||||||
return statusCode
|
return statusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getURLParam(r *http.Request, key string) string {
|
||||||
|
v := chi.URLParam(r, key)
|
||||||
|
unescaped, err := url.PathUnescape(v)
|
||||||
|
if err != nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return unescaped
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCommaSeparatedQueryParam(r *http.Request, key string) []string {
|
||||||
|
var result []string
|
||||||
|
|
||||||
|
for _, val := range strings.Split(r.URL.Query().Get(key), ",") {
|
||||||
|
val = strings.TrimSpace(val)
|
||||||
|
if val != "" {
|
||||||
|
result = append(result, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.RemoveDuplicates(result)
|
||||||
|
}
|
||||||
|
|
||||||
func handleCloseConnection(w http.ResponseWriter, r *http.Request) {
|
func handleCloseConnection(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||||
connectionID := getURLParam(r, "connectionID")
|
connectionID := getURLParam(r, "connectionID")
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -77,6 +76,8 @@ const (
|
||||||
userProfilePath = "/api/v2/user/profile"
|
userProfilePath = "/api/v2/user/profile"
|
||||||
retentionBasePath = "/api/v2/retention/users"
|
retentionBasePath = "/api/v2/retention/users"
|
||||||
retentionChecksPath = "/api/v2/retention/users/checks"
|
retentionChecksPath = "/api/v2/retention/users/checks"
|
||||||
|
fsEventsPath = "/api/v2/events/fs"
|
||||||
|
providerEventsPath = "/api/v2/events/provider"
|
||||||
healthzPath = "/healthz"
|
healthzPath = "/healthz"
|
||||||
webRootPathDefault = "/"
|
webRootPathDefault = "/"
|
||||||
webBasePathDefault = "/web"
|
webBasePathDefault = "/web"
|
||||||
|
@ -490,15 +491,6 @@ func getServicesStatus() ServicesStatus {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
func getURLParam(r *http.Request, key string) string {
|
|
||||||
v := chi.URLParam(r, key)
|
|
||||||
unescaped, err := url.PathUnescape(v)
|
|
||||||
if err != nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return unescaped
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileServer(r chi.Router, path string, root http.FileSystem) {
|
func fileServer(r chi.Router, path string, root http.FileSystem) {
|
||||||
if path != "/" && path[len(path)-1] != '/' {
|
if path != "/" && path[len(path)-1] != '/' {
|
||||||
r.Get(path, http.RedirectHandler(path+"/", http.StatusMovedPermanently).ServeHTTP)
|
r.Get(path, http.RedirectHandler(path+"/", http.StatusMovedPermanently).ServeHTTP)
|
||||||
|
|
|
@ -46,6 +46,7 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/logger"
|
"github.com/drakkan/sftpgo/v2/logger"
|
||||||
"github.com/drakkan/sftpgo/v2/mfa"
|
"github.com/drakkan/sftpgo/v2/mfa"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/sftpd"
|
"github.com/drakkan/sftpgo/v2/sftpd"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
|
@ -100,6 +101,8 @@ const (
|
||||||
user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes"
|
user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes"
|
||||||
userProfilePath = "/api/v2/user/profile"
|
userProfilePath = "/api/v2/user/profile"
|
||||||
retentionBasePath = "/api/v2/retention/users"
|
retentionBasePath = "/api/v2/retention/users"
|
||||||
|
fsEventsPath = "/api/v2/events/fs"
|
||||||
|
providerEventsPath = "/api/v2/events/provider"
|
||||||
healthzPath = "/healthz"
|
healthzPath = "/healthz"
|
||||||
webBasePath = "/web"
|
webBasePath = "/web"
|
||||||
webBasePathAdmin = "/web/admin"
|
webBasePathAdmin = "/web/admin"
|
||||||
|
@ -248,6 +251,21 @@ func TestMain(m *testing.M) {
|
||||||
logger.WarnToConsole("error loading configuration: %v", err)
|
logger.WarnToConsole("error loading configuration: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
wdPath, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
logger.WarnToConsole("error getting exe path: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
pluginsConfig := []plugin.Config{
|
||||||
|
{
|
||||||
|
Type: "eventsearcher",
|
||||||
|
Cmd: filepath.Join(wdPath, "..", "tests", "eventsearcher", "eventsearcher"),
|
||||||
|
AutoMTLS: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if runtime.GOOS == osWindows {
|
||||||
|
pluginsConfig[0].Cmd += ".exe"
|
||||||
|
}
|
||||||
providerConf := config.GetProviderConf()
|
providerConf := config.GetProviderConf()
|
||||||
credentialsPath = filepath.Join(os.TempDir(), "test_credentials")
|
credentialsPath = filepath.Join(os.TempDir(), "test_credentials")
|
||||||
providerConf.CredentialsPath = credentialsPath
|
providerConf.CredentialsPath = credentialsPath
|
||||||
|
@ -284,6 +302,11 @@ func TestMain(m *testing.M) {
|
||||||
logger.ErrorToConsole("error initializing MFA: %v", err)
|
logger.ErrorToConsole("error initializing MFA: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
err = plugin.Initialize(pluginsConfig, true)
|
||||||
|
if err != nil {
|
||||||
|
logger.ErrorToConsole("error initializing plugin: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
httpdConf := config.GetHTTPDConfig()
|
httpdConf := config.GetHTTPDConfig()
|
||||||
|
|
||||||
|
@ -5512,6 +5535,73 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
|
||||||
checkResponseCode(t, http.StatusInternalServerError, rr)
|
checkResponseCode(t, http.StatusInternalServerError, rr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchEvents(t *testing.T) {
|
||||||
|
token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr := executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
|
// the test eventsearcher plugin returns error if start_timestamp < 0
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&end_timestamp=123456&statuses=1,2", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusInternalServerError, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=a", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, providerEventsPath, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
|
// the test eventsearcher plugin returns error if start_timestamp < 0
|
||||||
|
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusInternalServerError, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?limit=2000", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=a", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?end_timestamp=a", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?order=ASSC", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
|
||||||
|
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?statuses=a,b", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
setBearerForReq(req, token)
|
||||||
|
rr = executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMFAErrors(t *testing.T) {
|
func TestMFAErrors(t *testing.T) {
|
||||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/drakkan/sftpgo/v2/dataprovider"
|
"github.com/drakkan/sftpgo/v2/dataprovider"
|
||||||
"github.com/drakkan/sftpgo/v2/kms"
|
"github.com/drakkan/sftpgo/v2/kms"
|
||||||
"github.com/drakkan/sftpgo/v2/sdk"
|
"github.com/drakkan/sftpgo/v2/sdk"
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin"
|
||||||
"github.com/drakkan/sftpgo/v2/util"
|
"github.com/drakkan/sftpgo/v2/util"
|
||||||
"github.com/drakkan/sftpgo/v2/vfs"
|
"github.com/drakkan/sftpgo/v2/vfs"
|
||||||
)
|
)
|
||||||
|
@ -305,6 +306,8 @@ func TestGetRespStatus(t *testing.T) {
|
||||||
err = fmt.Errorf("generic error")
|
err = fmt.Errorf("generic error")
|
||||||
respStatus = getRespStatus(err)
|
respStatus = getRespStatus(err)
|
||||||
assert.Equal(t, http.StatusInternalServerError, respStatus)
|
assert.Equal(t, http.StatusInternalServerError, respStatus)
|
||||||
|
respStatus = getRespStatus(plugin.ErrNoSearcher)
|
||||||
|
assert.Equal(t, http.StatusNotImplemented, respStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMappedStatusCode(t *testing.T) {
|
func TestMappedStatusCode(t *testing.T) {
|
||||||
|
|
|
@ -3400,6 +3400,7 @@ components:
|
||||||
- manage_defender
|
- manage_defender
|
||||||
- view_defender
|
- view_defender
|
||||||
- retention_checks
|
- retention_checks
|
||||||
|
- view_events
|
||||||
description: |
|
description: |
|
||||||
Admin permissions:
|
Admin permissions:
|
||||||
* `*` - all permissions are granted
|
* `*` - all permissions are granted
|
||||||
|
@ -3417,6 +3418,7 @@ components:
|
||||||
* `manage_defender` - remove ip from the dynamic blocklist is allowed
|
* `manage_defender` - remove ip from the dynamic blocklist is allowed
|
||||||
* `view_defender` - list the dynamic blocklist is allowed
|
* `view_defender` - list the dynamic blocklist is allowed
|
||||||
* `retention_checks` - view and start retention checks is allowed
|
* `retention_checks` - view and start retention checks is allowed
|
||||||
|
* `view_events` - view and search filesystem and provider events is allowed
|
||||||
LoginMethods:
|
LoginMethods:
|
||||||
type: string
|
type: string
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -977,6 +977,10 @@ func (s *httpdServer) initializeRouter() {
|
||||||
router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
|
router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
|
||||||
router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
|
router.With(checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
|
||||||
startRetentionCheck)
|
startRetentionCheck)
|
||||||
|
router.With(checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||||
|
Get(fsEventsPath, searchFsEvents)
|
||||||
|
router.With(checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||||
|
Get(providerEventsPath, searchProviderEvents)
|
||||||
router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
||||||
Get(apiKeysPath, getAPIKeys)
|
Get(apiKeysPath, getAPIKeys)
|
||||||
router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
router.With(forbidAPIKeyAuthentication, checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-plugin"
|
"github.com/hashicorp/go-plugin"
|
||||||
|
@ -117,7 +116,7 @@ func (p *authPlugin) initialize() error {
|
||||||
Managed: false,
|
Managed: false,
|
||||||
Logger: &logger.HCLogAdapter{
|
Logger: &logger.HCLogAdapter{
|
||||||
Logger: hclog.New(&hclog.LoggerOptions{
|
Logger: hclog.New(&hclog.LoggerOptions{
|
||||||
Name: fmt.Sprintf("%v.%v.%v", logSender, auth.PluginName, filepath.Base(p.config.Cmd)),
|
Name: fmt.Sprintf("%v.%v", logSender, auth.PluginName),
|
||||||
Level: pluginsLogLevel,
|
Level: pluginsLogLevel,
|
||||||
DisableTime: true,
|
DisableTime: true,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -19,7 +19,7 @@ type GRPCClient struct {
|
||||||
// SearchFsEvents implements the Searcher interface
|
// SearchFsEvents implements the Searcher interface
|
||||||
func (c *GRPCClient) SearchFsEvents(startTimestamp, endTimestamp int64, username, ip, sshCmd string, actions,
|
func (c *GRPCClient) SearchFsEvents(startTimestamp, endTimestamp int64, username, ip, sshCmd string, actions,
|
||||||
protocols, instanceIDs, excludeIDs []string, statuses []int32, limit, order int,
|
protocols, instanceIDs, excludeIDs []string, statuses []int32, limit, order int,
|
||||||
) ([]byte, error) {
|
) ([]byte, []string, []string, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -39,15 +39,15 @@ func (c *GRPCClient) SearchFsEvents(startTimestamp, endTimestamp int64, username
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return resp.Data, nil
|
return resp.Data, resp.SameTsAtStart, resp.SameTsAtEnd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchProviderEvents implements the Searcher interface
|
// SearchProviderEvents implements the Searcher interface
|
||||||
func (c *GRPCClient) SearchProviderEvents(startTimestamp, endTimestamp int64, username, ip, objectName string,
|
func (c *GRPCClient) SearchProviderEvents(startTimestamp, endTimestamp int64, username, ip, objectName string,
|
||||||
limit, order int, actions, objectTypes, instanceIDs, excludeIDs []string,
|
limit, order int, actions, objectTypes, instanceIDs, excludeIDs []string,
|
||||||
) ([]byte, error) {
|
) ([]byte, []string, []string, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rpcTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -66,9 +66,9 @@ func (c *GRPCClient) SearchProviderEvents(startTimestamp, endTimestamp int64, us
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
return resp.Data, nil
|
return resp.Data, resp.SameTsAtStart, resp.SameTsAtEnd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GRPCServer defines the gRPC server that GRPCClient talks to.
|
// GRPCServer defines the gRPC server that GRPCClient talks to.
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (p *kmsPlugin) initialize() error {
|
||||||
Managed: false,
|
Managed: false,
|
||||||
Logger: &logger.HCLogAdapter{
|
Logger: &logger.HCLogAdapter{
|
||||||
Logger: hclog.New(&hclog.LoggerOptions{
|
Logger: hclog.New(&hclog.LoggerOptions{
|
||||||
Name: fmt.Sprintf("%v.%v.%v", logSender, kmsplugin.PluginName, filepath.Base(p.config.Cmd)),
|
Name: fmt.Sprintf("%v.%v", logSender, kmsplugin.PluginName),
|
||||||
Level: pluginsLogLevel,
|
Level: pluginsLogLevel,
|
||||||
DisableTime: true,
|
DisableTime: true,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -166,7 +165,7 @@ func (p *notifierPlugin) initialize() error {
|
||||||
Managed: false,
|
Managed: false,
|
||||||
Logger: &logger.HCLogAdapter{
|
Logger: &logger.HCLogAdapter{
|
||||||
Logger: hclog.New(&hclog.LoggerOptions{
|
Logger: hclog.New(&hclog.LoggerOptions{
|
||||||
Name: fmt.Sprintf("%v.%v.%v", logSender, notifier.PluginName, filepath.Base(p.config.Cmd)),
|
Name: fmt.Sprintf("%v.%v", logSender, notifier.PluginName),
|
||||||
Level: pluginsLogLevel,
|
Level: pluginsLogLevel,
|
||||||
DisableTime: true,
|
DisableTime: true,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/go-plugin"
|
"github.com/hashicorp/go-plugin"
|
||||||
|
@ -58,7 +57,7 @@ func (p *searcherPlugin) initialize() error {
|
||||||
Managed: false,
|
Managed: false,
|
||||||
Logger: &logger.HCLogAdapter{
|
Logger: &logger.HCLogAdapter{
|
||||||
Logger: hclog.New(&hclog.LoggerOptions{
|
Logger: hclog.New(&hclog.LoggerOptions{
|
||||||
Name: fmt.Sprintf("%v.%v.%v", logSender, eventsearcher.PluginName, filepath.Base(p.config.Cmd)),
|
Name: fmt.Sprintf("%v.%v", logSender, eventsearcher.PluginName),
|
||||||
Level: pluginsLogLevel,
|
Level: pluginsLogLevel,
|
||||||
DisableTime: true,
|
DisableTime: true,
|
||||||
}),
|
}),
|
||||||
|
|
27
tests/eventsearcher/go.mod
Normal file
27
tests/eventsearcher/go.mod
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module github.com/drakkan/sftpgo/tests/eventsearcher
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/drakkan/sftpgo/v2 v2.1.1-0.20211020173949-97d0a4855756
|
||||||
|
github.com/hashicorp/go-plugin v1.4.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fatih/color v1.13.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
github.com/hashicorp/go-hclog v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
|
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||||
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect
|
||||||
|
google.golang.org/grpc v1.41.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
replace github.com/drakkan/sftpgo/v2 => ../..
|
1216
tests/eventsearcher/go.sum
Normal file
1216
tests/eventsearcher/go.sum
Normal file
File diff suppressed because it is too large
Load diff
117
tests/eventsearcher/main.go
Normal file
117
tests/eventsearcher/main.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/v2/sdk/plugin/eventsearcher"
|
||||||
|
"github.com/hashicorp/go-plugin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotSupported = errors.New("unsupported parameter")
|
||||||
|
)
|
||||||
|
|
||||||
|
type fsEvent struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
FsPath string `json:"fs_path"`
|
||||||
|
FsTargetPath string `json:"fs_target_path,omitempty"`
|
||||||
|
VirtualPath string `json:"virtual_path"`
|
||||||
|
VirtualTargetPath string `json:"virtual_target_path,omitempty"`
|
||||||
|
SSHCmd string `json:"ssh_cmd,omitempty"`
|
||||||
|
FileSize int64 `json:"file_size,omitempty"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
|
InstanceID string `json:"instance_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type providerEvent struct {
|
||||||
|
ID string `json:"id" gorm:"primaryKey"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
|
ObjectType string `json:"object_type"`
|
||||||
|
ObjectName string `json:"object_name"`
|
||||||
|
ObjectData []byte `json:"object_data"`
|
||||||
|
InstanceID string `json:"instance_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Searcher struct{}
|
||||||
|
|
||||||
|
func (s *Searcher) SearchFsEvents(startTimestamp, endTimestamp int64, username, ip, sshCmd string, actions,
|
||||||
|
protocols, instanceIDs, excludeIDs []string, statuses []int32, limit, order int,
|
||||||
|
) ([]byte, []string, []string, error) {
|
||||||
|
if startTimestamp < 0 {
|
||||||
|
return nil, nil, nil, errNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []fsEvent{
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Timestamp: 100,
|
||||||
|
Action: "upload",
|
||||||
|
Username: "username1",
|
||||||
|
FsPath: "/tmp/file.txt",
|
||||||
|
FsTargetPath: "/tmp/target.txt",
|
||||||
|
VirtualPath: "file.txt",
|
||||||
|
VirtualTargetPath: "target.txt",
|
||||||
|
SSHCmd: "scp",
|
||||||
|
FileSize: 123,
|
||||||
|
Status: 1,
|
||||||
|
Protocol: "SFTP",
|
||||||
|
IP: "::1",
|
||||||
|
InstanceID: "instance1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(results)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Searcher) SearchProviderEvents(startTimestamp, endTimestamp int64, username, ip, objectName string,
|
||||||
|
limit, order int, actions, objectTypes, instanceIDs, excludeIDs []string,
|
||||||
|
) ([]byte, []string, []string, error) {
|
||||||
|
if startTimestamp < 0 {
|
||||||
|
return nil, nil, nil, errNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []providerEvent{
|
||||||
|
{
|
||||||
|
ID: "1",
|
||||||
|
Timestamp: 100,
|
||||||
|
Action: "add",
|
||||||
|
Username: "username1",
|
||||||
|
IP: "127.0.0.1",
|
||||||
|
ObjectType: "api_key",
|
||||||
|
ObjectName: "123",
|
||||||
|
ObjectData: []byte("data"),
|
||||||
|
InstanceID: "instance1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(results)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.Serve(&plugin.ServeConfig{
|
||||||
|
HandshakeConfig: eventsearcher.Handshake,
|
||||||
|
Plugins: map[string]plugin.Plugin{
|
||||||
|
eventsearcher.PluginName: &eventsearcher.Plugin{Impl: &Searcher{}},
|
||||||
|
},
|
||||||
|
GRPCServer: plugin.DefaultGRPCServer,
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in a new issue