mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-24 08:30:27 +00:00
WebUI: add a token validation mode that allows checking the signature
Some checks failed
Code scanning - action / CodeQL-Build (push) Has been cancelled
CI / Test and deploy (1.22, macos-latest, true) (push) Has been cancelled
CI / Test and deploy (1.22, ubuntu-latest, true) (push) Has been cancelled
CI / Test and deploy (1.22, windows-latest, false) (push) Has been cancelled
CI / Test build flags (push) Has been cancelled
CI / Test with PgSQL/MySQL/Cockroach (push) Has been cancelled
CI / Build Linux packages (aarch64, ubuntu18.04, go1.22.7, arm64) (push) Has been cancelled
CI / Build Linux packages (amd64, ubuntu:18.04, go1.22.7, amd64) (push) Has been cancelled
CI / Build Linux packages (armv7, ubuntu18.04, go1.22.7, arm7) (push) Has been cancelled
CI / Build Linux packages (ppc64le, ubuntu18.04, go1.22.7, ppc64le) (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Docker / Build (alpine, false, ubuntu-latest) (push) Has been cancelled
Docker / Build (alpine, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian, false, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian-plugins, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (distroless, false, ubuntu-latest) (push) Has been cancelled
Some checks failed
Code scanning - action / CodeQL-Build (push) Has been cancelled
CI / Test and deploy (1.22, macos-latest, true) (push) Has been cancelled
CI / Test and deploy (1.22, ubuntu-latest, true) (push) Has been cancelled
CI / Test and deploy (1.22, windows-latest, false) (push) Has been cancelled
CI / Test build flags (push) Has been cancelled
CI / Test with PgSQL/MySQL/Cockroach (push) Has been cancelled
CI / Build Linux packages (aarch64, ubuntu18.04, go1.22.7, arm64) (push) Has been cancelled
CI / Build Linux packages (amd64, ubuntu:18.04, go1.22.7, amd64) (push) Has been cancelled
CI / Build Linux packages (armv7, ubuntu18.04, go1.22.7, arm7) (push) Has been cancelled
CI / Build Linux packages (ppc64le, ubuntu18.04, go1.22.7, ppc64le) (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Docker / Build (alpine, false, ubuntu-latest) (push) Has been cancelled
Docker / Build (alpine, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian, false, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (debian-plugins, true, ubuntu-latest) (push) Has been cancelled
Docker / Build (distroless, false, ubuntu-latest) (push) Has been cancelled
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
5f67fcdce5
commit
433d45ed87
15 changed files with 485 additions and 85 deletions
50
go.mod
50
go.mod
|
@ -5,19 +5,19 @@ go 1.22.2
|
|||
require (
|
||||
cloud.google.com/go/storage v1.43.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.33
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.32
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.18
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.23.6
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.8
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7
|
||||
github.com/aws/aws-sdk-go-v2 v1.31.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.36
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.34
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.22
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.24.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.63.0
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.0
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.31.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.8
|
||||
github.com/coreos/go-oidc/v3 v3.11.0
|
||||
|
@ -47,7 +47,7 @@ require (
|
|||
github.com/pires/go-proxyproto v0.7.0
|
||||
github.com/pkg/sftp v1.13.7-0.20240410063531-637088883317
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.20.3
|
||||
github.com/prometheus/client_golang v1.20.4
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/rs/xid v1.6.0
|
||||
|
@ -73,7 +73,7 @@ require (
|
|||
golang.org/x/sys v0.25.0
|
||||
golang.org/x/term v0.24.0
|
||||
golang.org/x/time v0.6.0
|
||||
google.golang.org/api v0.197.0
|
||||
google.golang.org/api v0.198.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
|
@ -86,24 +86,24 @@ require (
|
|||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.20.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.23.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.0 // indirect
|
||||
github.com/aws/smithy-go v1.21.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
|
@ -176,7 +176,7 @@ require (
|
|||
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.66.2 // indirect
|
||||
google.golang.org/grpc v1.67.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
99
go.sum
99
go.sum
|
@ -26,8 +26,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xP
|
|||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0 h1:Be6KInmFEKV81c0pOAEbRYehLMwmmGI1exuFj248AMk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.0/go.mod h1:WCPBHsOXfBVnivScjs2ypRfimjEW0qPVLGgJkZlrIOA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
|
@ -39,48 +39,48 @@ github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHc
|
|||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.33 h1:Nof9o/MsmH4oa0s2q9a0k7tMz5x/Yj5k06lDODWz3BU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.33/go.mod h1:kEqdYzRb8dd8Sy2pOdEbExTTF5v7ozEXX0McgPE7xks=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 h1:7Cxhp/BnT2RcGy4VisJ9miUPecY+lyE9I8JvcZofn9I=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.32/go.mod h1:P5/QMF3/DCHbXGEGkdbilXHsyTBX5D3HSwcrSc9p20I=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 h1:pfQ2sqNpMVK6xz2RbqLEL0GH87JOwSxPV2rzm8Zsb74=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13/go.mod h1:NG7RXPUlqfsCLLFfi0+IpKN4sCB9D9fw/qTaSB+xRoU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.18 h1:9DIp7vhmOPmueCDwpXa45bEbLHHTt1kcxChdTJWWxvI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.18/go.mod h1:aJv/Fwz8r56ozwYFRC4bzoeL1L17GYQYemfblOBux1M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
|
||||
github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.5 h1:xDAuZTn4IMm8o1LnBZvmrL8JA1io4o3YWNXgohbf20g=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.5/go.mod h1:wYSv6iDS621sEFLfKvpPE2ugjTuGlAG7iROg0hLOkfc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.36 h1:4IlvHh6Olc7+61O1ktesh0jOcqmq/4WG6C2Aj5SKXy0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.36/go.mod h1:IiBpC0HPAGq9Le0Xxb1wpAKzEfAQ3XlYgJLYKEVYcfw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.34 h1:gmkk1l/cDGSowPRzkdxYi8edw+gN4HmVK151D/pqGNc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.34/go.mod h1:4R9OEV3tgFMsok4ZeFpExn7zQaZRa9MRGFYnI/xC/vs=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.22 h1:MUD/42Etbj6sVZ0HpOe4G/4+wDF7ZJhqZXSqNKZokPM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.22/go.mod h1:wp0iN4VH1riPNX68N8MU+mz/7ggSeWc+zBhsdALp+zM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17 h1:Roo69qTpfu8OlJ2Tb7pAYVuF0CpuUMB0IYWwYP/4DZM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.17/go.mod h1:NcWPxQzGM1USQggaTVwz6VpqMZPX1CvDJLDh6jnOCa4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19 h1:FLMkfEiRjhgeDTCjjLoc3URo/TBkgeQbocA78lfkzSI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.19/go.mod h1:Vx+GucNSsdhaxs3aZIKfSUjKVGsxN25nX2SRcdhuw08=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsdzgl7ZL2KlXiUAoJnI/VxfHCvDFr2QDFj6u4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17 h1:u+EfGmksnJc/x5tq3A+OD7LrMbSSR/5TrKLvkdy/fhY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.17/go.mod h1:VaMx6302JHax2vHJWgRo+5n9zvbacs3bLU/23DNQrTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.23.6 h1:4ZL1yFmgCUTksVdHa71xao4X8ii5k6KtD93Fr08p1NU=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.23.6/go.mod h1:ck+HLSlQVYL8LIth8IrZ5qPQ4KTletB/O+WWqW8gtjQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2 h1:Kp6PWAlXwP1UvIflkIP6MFZYBNDCa4mFCGtxrpICVOg=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.2/go.mod h1:5FmD/Dqq57gP+XwaUnd5WFPipAuzrf0HmupX27Gvjvc=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.8 h1:HNXhQReFG2fbucvPRxDabbIGQf/6dieOfTnzoGPEqXI=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.8/go.mod h1:BYr9P/rrcLNJ8A36nT15p8tpoVDZ5lroHuMn/njecBw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 h1:pIaGg+08llrP7Q5aiz9ICWbY8cqhTkyy+0SHvfzQpTc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7/go.mod h1:eEygMHnTKH/3kNp9Jr1n3PdejuSNcgwLe1dWgQtO0VQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1KvQzkzPyiKUdlWJqy+J4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7/go.mod h1:bCbAxKDqNvkHxRaIMnyVPXPo+OaPRwvmgzMxbz1VKSA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8KNk8sDkNPFaOKDE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o=
|
||||
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
|
||||
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.18 h1:OWYvKL53l1rbsUmW7bQyJVsYU/Ii3bbAAQIIFNbM0Tk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.18/go.mod h1:CUx0G1v3wG6l01tUB+j7Y8kclA8NSqK4ef0YG79a4cg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.20 h1:rTWjG6AvWekO2B1LHeM3ktU7MqyX9rzWQ7hgzneZW7E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.20/go.mod h1:RGW2DDpVc8hu6Y6yG8G5CHVmVOAn1oV8rNKOHRJyswg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.18 h1:eb+tFOIl9ZsUe2259/BKPeniKuz4/02zZFH/i4Nf8Rg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.18/go.mod h1:GVCC2IJNJTmdlyEsSmofEy7EfJncP7DNnXDzRjJ5Keg=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.24.0 h1:l6NntGJyBZfgSMxj7Djlo2t9R0bYk2iMpqhDtuD/RSQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.24.0/go.mod h1:zyg91TcJAFCMk+J1eiKjtAEUD9CvFqz+8PYytGk00GM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.63.0 h1:F6KG9CT7PPqAjnRxjKmYJopVnXPwjlzPI2FEgXHajNY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.63.0/go.mod h1:NLTqRLe3pUNu3nTEHI6XlHLKYmc8fbHUdMxAB6+s41Q=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.0 h1:r+37fBAonXAmRx2MX0naWDKZpAaP2AOQ22cf9Cg71GA=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.0/go.mod h1:WyLS5qwXHtjKAONYZq/4ewdd+hcVsa3LBu77Ow5uj3k=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.23.0 h1:fHySkG0IGj2nepgGJPmmhZYL9ndnsq1Tvc6MeuVQCaQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.23.0/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.0 h1:cU/OeQPNReyMj1JEBgjE29aclYZYtXcsPMXbTkVGMFk=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.0/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.31.0 h1:GNVxIHBTi2EgwCxpNiozhNasMOK+ROUA2Z3X+cSBX58=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.31.0/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
|
||||
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
|
||||
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
|
@ -103,8 +103,9 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/
|
|||
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
|
@ -316,8 +317,8 @@ github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
|||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
|
||||
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
|
||||
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
|
@ -517,8 +518,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.197.0 h1:x6CwqQLsFiA5JKAiGyGBjc2bNtHtLddhJCE2IKuhhcQ=
|
||||
google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60nRNw=
|
||||
google.golang.org/api v0.198.0 h1:OOH5fZatk57iN0A7tjJQzt6aPfYQ1JiWkt1yGseazks=
|
||||
google.golang.org/api v0.198.0/go.mod h1:/Lblzl3/Xqqk9hw/yS97TImKTUwnf1bv89v7+OagJzc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
@ -535,8 +536,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||
google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bolt "go.etcd.io/bbolt"
|
||||
|
@ -182,6 +183,50 @@ func (p *BoltProvider) updateAPIKeyLastUse(keyID string) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getAdminSignature(username string) (string, error) {
|
||||
var updatedAt int64
|
||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||
bucket, err := p.getAdminsBucket(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := bucket.Get([]byte(username))
|
||||
var admin Admin
|
||||
err = json.Unmarshal(u, &admin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updatedAt = admin.UpdatedAt
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(updatedAt, 10), nil
|
||||
}
|
||||
|
||||
func (p *BoltProvider) getUserSignature(username string) (string, error) {
|
||||
var updatedAt int64
|
||||
err := p.dbHandle.View(func(tx *bolt.Tx) error {
|
||||
bucket, err := p.getUsersBucket(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u := bucket.Get([]byte(username))
|
||||
var user User
|
||||
err = json.Unmarshal(u, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updatedAt = user.UpdatedAt
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(updatedAt, 10), nil
|
||||
}
|
||||
|
||||
func (p *BoltProvider) setUpdatedAt(username string) {
|
||||
p.dbHandle.Update(func(tx *bolt.Tx) error { //nolint:errcheck
|
||||
bucket, err := p.getUsersBucket(tx)
|
||||
|
|
|
@ -783,6 +783,8 @@ type Provider interface {
|
|||
updateLastLogin(username string) error
|
||||
updateAdminLastLogin(username string) error
|
||||
setUpdatedAt(username string)
|
||||
getAdminSignature(username string) (string, error)
|
||||
getUserSignature(username string) (string, error)
|
||||
getFolders(limit, offset int, order string, minimal bool) ([]vfs.BaseVirtualFolder, error)
|
||||
getFolderByName(name string) (vfs.BaseVirtualFolder, error)
|
||||
addFolder(folder *vfs.BaseVirtualFolder) error
|
||||
|
@ -2098,6 +2100,20 @@ func UserExists(username, role string) (User, error) {
|
|||
return provider.userExists(username, role)
|
||||
}
|
||||
|
||||
// GetAdminSignature returns the signature for the admin with the specified
|
||||
// username.
|
||||
func GetAdminSignature(username string) (string, error) {
|
||||
username = config.convertName(username)
|
||||
return provider.getAdminSignature(username)
|
||||
}
|
||||
|
||||
// GetUserSignature returns the signature for the user with the specified
|
||||
// username.
|
||||
func GetUserSignature(username string) (string, error) {
|
||||
username = config.convertName(username)
|
||||
return provider.getUserSignature(username)
|
||||
}
|
||||
|
||||
// GetUserWithGroupSettings tries to return the user with the specified username
|
||||
// loading also the group settings
|
||||
func GetUserWithGroupSettings(username, role string) (User, error) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -207,6 +208,32 @@ func (p *MemoryProvider) updateAPIKeyLastUse(keyID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getAdminSignature(username string) (string, error) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
return "", errMemoryProviderClosed
|
||||
}
|
||||
admin, err := p.adminExistsInternal(username)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(admin.UpdatedAt, 10), nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) getUserSignature(username string) (string, error) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
if p.dbHandle.isClosed {
|
||||
return "", errMemoryProviderClosed
|
||||
}
|
||||
user, err := p.userExistsInternal(username)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(user.UpdatedAt, 10), nil
|
||||
}
|
||||
|
||||
func (p *MemoryProvider) setUpdatedAt(username string) {
|
||||
p.dbHandle.Lock()
|
||||
defer p.dbHandle.Unlock()
|
||||
|
|
|
@ -325,6 +325,14 @@ func (p *MySQLProvider) getUsedQuota(username string) (int, int64, int64, int64,
|
|||
return sqlCommonGetUsedQuota(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *MySQLProvider) getAdminSignature(username string) (string, error) {
|
||||
return sqlCommonGetAdminSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *MySQLProvider) getUserSignature(username string) (string, error) {
|
||||
return sqlCommonGetUserSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *MySQLProvider) setUpdatedAt(username string) {
|
||||
sqlCommonSetUpdatedAt(username, p.dbHandle)
|
||||
}
|
||||
|
|
|
@ -343,6 +343,14 @@ func (p *PGSQLProvider) getUsedQuota(username string) (int, int64, int64, int64,
|
|||
return sqlCommonGetUsedQuota(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *PGSQLProvider) getAdminSignature(username string) (string, error) {
|
||||
return sqlCommonGetAdminSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *PGSQLProvider) getUserSignature(username string) (string, error) {
|
||||
return sqlCommonGetUserSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *PGSQLProvider) setUpdatedAt(username string) {
|
||||
sqlCommonSetUpdatedAt(username, p.dbHandle)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"fmt"
|
||||
"net/netip"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -1248,6 +1249,32 @@ func sqlCommonUpdateQuota(username string, filesAdd int, sizeAdd int64, reset bo
|
|||
return err
|
||||
}
|
||||
|
||||
func sqlCommonGetAdminSignature(username string, dbHandle *sql.DB) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
|
||||
defer cancel()
|
||||
|
||||
q := getAdminSignatureQuery()
|
||||
var updatedAt int64
|
||||
err := dbHandle.QueryRowContext(ctx, q, username).Scan(&updatedAt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(updatedAt, 10), nil
|
||||
}
|
||||
|
||||
func sqlCommonGetUserSignature(username string, dbHandle *sql.DB) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
|
||||
defer cancel()
|
||||
|
||||
q := getUserSignatureQuery()
|
||||
var updatedAt int64
|
||||
err := dbHandle.QueryRowContext(ctx, q, username).Scan(&updatedAt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(updatedAt, 10), nil
|
||||
}
|
||||
|
||||
func sqlCommonGetUsedQuota(username string, dbHandle *sql.DB) (int, int64, int64, int64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
|
||||
defer cancel()
|
||||
|
|
|
@ -246,6 +246,14 @@ func (p *SQLiteProvider) getUsedQuota(username string) (int, int64, int64, int64
|
|||
return sqlCommonGetUsedQuota(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *SQLiteProvider) getAdminSignature(username string) (string, error) {
|
||||
return sqlCommonGetAdminSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *SQLiteProvider) getUserSignature(username string) (string, error) {
|
||||
return sqlCommonGetUserSignature(username, p.dbHandle)
|
||||
}
|
||||
|
||||
func (p *SQLiteProvider) setUpdatedAt(username string) {
|
||||
sqlCommonSetUpdatedAt(username, p.dbHandle)
|
||||
}
|
||||
|
|
|
@ -650,6 +650,14 @@ func getUpdateQuotaQuery(reset bool) string {
|
|||
WHERE username = %s`, sqlTableUsers, sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2], sqlPlaceholders[3])
|
||||
}
|
||||
|
||||
func getAdminSignatureQuery() string {
|
||||
return fmt.Sprintf(`SELECT updated_at FROM %s WHERE username = %s`, sqlTableAdmins, sqlPlaceholders[0])
|
||||
}
|
||||
|
||||
func getUserSignatureQuery() string {
|
||||
return fmt.Sprintf(`SELECT updated_at FROM %s WHERE username = %s`, sqlTableUsers, sqlPlaceholders[0])
|
||||
}
|
||||
|
||||
func getSetUpdateAtQuery() string {
|
||||
return fmt.Sprintf(`UPDATE %s SET updated_at = %s WHERE username = %s`, sqlTableUsers, sqlPlaceholders[0], sqlPlaceholders[1])
|
||||
}
|
||||
|
|
|
@ -45,11 +45,10 @@ const (
|
|||
tokenAudienceWebLogin tokenAudience = "WebLogin"
|
||||
)
|
||||
|
||||
type tokenValidation = int
|
||||
|
||||
const (
|
||||
tokenValidationFull = iota
|
||||
tokenValidationNoIPMatch tokenValidation = iota
|
||||
tokenValidationModeDefault = 0
|
||||
tokenValidationModeNoIPMatch = 1
|
||||
tokenValidationModeUserSignature = 2
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -74,7 +73,7 @@ var (
|
|||
// with the login form
|
||||
csrfTokenDuration = 4 * time.Hour
|
||||
tokenRefreshThreshold = 10 * time.Minute
|
||||
tokenValidationMode = tokenValidationFull
|
||||
tokenValidationMode = tokenValidationModeDefault
|
||||
)
|
||||
|
||||
type jwtTokenClaims struct {
|
||||
|
@ -567,10 +566,51 @@ func verifyOAuth2Token(csrfTokenAuth *jwtauth.JWTAuth, tokenString, ip string) (
|
|||
}
|
||||
|
||||
func validateIPForToken(token jwt.Token, ip string) error {
|
||||
if tokenValidationMode != tokenValidationNoIPMatch {
|
||||
if tokenValidationMode&tokenValidationModeNoIPMatch == 0 {
|
||||
if !slices.Contains(token.Audience(), ip) {
|
||||
return errInvalidToken
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTokenSignature(r *http.Request, token jwt.Token) error {
|
||||
if _, ok := r.Context().Value(oidcTokenKey).(string); ok {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
if tokenValidationMode&tokenValidationModeUserSignature != 0 {
|
||||
for _, audience := range token.Audience() {
|
||||
switch audience {
|
||||
case tokenAudienceAPI, tokenAudienceWebAdmin:
|
||||
err = validateSignatureForToken(token, dataprovider.GetAdminSignature)
|
||||
case tokenAudienceAPIUser, tokenAudienceWebClient:
|
||||
err = validateSignatureForToken(token, dataprovider.GetUserSignature)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
invalidateToken(r, false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func validateSignatureForToken(token jwt.Token, getter func(string) (string, error)) error {
|
||||
username := ""
|
||||
if u, ok := token.Get(claimUsernameKey); ok {
|
||||
c := jwtTokenClaims{}
|
||||
username = c.decodeString(u)
|
||||
}
|
||||
|
||||
signature, err := getter(username)
|
||||
if err != nil {
|
||||
logger.Debug(logSender, "", "unable to get signature for username %q: %v", username, err)
|
||||
return errInvalidToken
|
||||
}
|
||||
if signature != "" && signature == token.Subject() {
|
||||
return nil
|
||||
}
|
||||
logger.Debug(logSender, "", "signature mismatch for username %q, signature %q, token signature %q",
|
||||
username, signature, token.Subject())
|
||||
return errInvalidToken
|
||||
}
|
||||
|
|
|
@ -967,11 +967,7 @@ func (c *Conf) getKeyPairs(configDir string) []common.TLSKeyPair {
|
|||
}
|
||||
|
||||
func (c *Conf) setTokenValidationMode() {
|
||||
if c.TokenValidation == 1 {
|
||||
tokenValidationMode = tokenValidationNoIPMatch
|
||||
} else {
|
||||
tokenValidationMode = tokenValidationFull
|
||||
}
|
||||
tokenValidationMode = c.TokenValidation
|
||||
}
|
||||
|
||||
func (c *Conf) loadFromProvider() error {
|
||||
|
|
|
@ -48,6 +48,7 @@ import (
|
|||
"github.com/sftpgo/sdk/plugin/notifier"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/html"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/acme"
|
||||
"github.com/drakkan/sftpgo/v2/internal/common"
|
||||
|
@ -285,6 +286,7 @@ RKjnkiEZeG4+G91Xu7+HmcBLwV86k5I+tXK9O1Okomr6Zry8oqVcxU5TB6VRS+rA
|
|||
ubwF00Drdvk2+kDZfxIM137nBiy7wgCJi2Ksm5ihN3dUF6Q0oNPl
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
defaultAdminUsername = "admin"
|
||||
defaultAdminPass = "password"
|
||||
defeaultUsername = "test_user"
|
||||
)
|
||||
|
||||
|
@ -945,6 +947,172 @@ func TestInvalidToken(t *testing.T) {
|
|||
assert.Contains(t, rr.Body.String(), util.I18nErrorInvalidToken)
|
||||
}
|
||||
|
||||
func TestTokenSignatureValidation(t *testing.T) {
|
||||
tokenValidationMode = 0
|
||||
server := httpdServer{
|
||||
binding: Binding{
|
||||
Address: "",
|
||||
Port: 8080,
|
||||
EnableWebAdmin: true,
|
||||
EnableWebClient: true,
|
||||
EnableRESTAPI: true,
|
||||
},
|
||||
enableWebAdmin: true,
|
||||
enableWebClient: true,
|
||||
enableRESTAPI: true,
|
||||
}
|
||||
server.initializeRouter()
|
||||
testServer := httptest.NewServer(server.router)
|
||||
defer testServer.Close()
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
req, err := http.NewRequest(http.MethodGet, tokenPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.SetBasicAuth(defaultAdminUsername, defaultAdminPass)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
var resp map[string]any
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
accessToken := resp["access_token"]
|
||||
require.NotEmpty(t, accessToken)
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, versionPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
// change the token validation mode
|
||||
tokenValidationMode = 2
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, versionPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
// Now update the admin
|
||||
admin, err := dataprovider.AdminExists(defaultAdminUsername)
|
||||
assert.NoError(t, err)
|
||||
err = dataprovider.UpdateAdmin(&admin, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
// token validation mode is 0, the old token is still valid
|
||||
tokenValidationMode = 0
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, versionPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
// change the token validation mode
|
||||
tokenValidationMode = 2
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, versionPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, rr.Code)
|
||||
// the token is invalidated, changing the validation mode has no effect
|
||||
tokenValidationMode = 0
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, versionPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, rr.Code)
|
||||
|
||||
userPwd := "pwd"
|
||||
user := dataprovider.User{
|
||||
BaseUser: sdk.BaseUser{
|
||||
Username: defeaultUsername,
|
||||
Password: userPwd,
|
||||
HomeDir: filepath.Join(os.TempDir(), defeaultUsername),
|
||||
Status: 1,
|
||||
},
|
||||
}
|
||||
user.Permissions = make(map[string][]string)
|
||||
user.Permissions["/"] = []string{dataprovider.PermAny}
|
||||
err = dataprovider.AddUser(&user, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
dataprovider.DeleteUser(defeaultUsername, "", "", "") //nolint:errcheck
|
||||
}()
|
||||
|
||||
tokenValidationMode = 2
|
||||
req, err = http.NewRequest(http.MethodGet, webClientLoginPath, nil)
|
||||
require.NoError(t, err)
|
||||
rr = httptest.NewRecorder()
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
loginCookie := strings.Split(rr.Header().Get("Set-Cookie"), ";")[0]
|
||||
assert.NotEmpty(t, loginCookie)
|
||||
csrfToken, err := getCSRFTokenFromBody(rr.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, csrfToken)
|
||||
// Now login
|
||||
form := make(url.Values)
|
||||
form.Set(csrfFormToken, csrfToken)
|
||||
form.Set("username", defeaultUsername)
|
||||
form.Set("password", userPwd)
|
||||
req, err = http.NewRequest(http.MethodPost, webClientLoginPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Cookie", loginCookie)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
rr = httptest.NewRecorder()
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
userCookie := strings.Split(rr.Header().Get("Set-Cookie"), ";")[0]
|
||||
assert.NotEmpty(t, userCookie)
|
||||
// Test a WebClient page and a JSON API
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, webClientFilesPath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Cookie", userCookie)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, webClientProfilePath, nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Cookie", userCookie)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
csrfToken, err = getCSRFTokenFromBody(rr.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, csrfToken)
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, webClientFilePath+"?path=missing.txt", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Cookie", userCookie)
|
||||
req.Header.Set(csrfHeaderToken, csrfToken)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusNotFound, rr.Code)
|
||||
|
||||
tokenValidationMode = 0
|
||||
err = dataprovider.DeleteUser(defeaultUsername, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, webClientFilePath+"?path=missing.txt", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Cookie", userCookie)
|
||||
req.Header.Set(csrfHeaderToken, csrfToken)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusNotFound, rr.Code)
|
||||
|
||||
tokenValidationMode = 2
|
||||
rr = httptest.NewRecorder()
|
||||
req, err = http.NewRequest(http.MethodGet, webClientFilePath+"?path=missing.txt", nil)
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Cookie", userCookie)
|
||||
req.Header.Set(csrfHeaderToken, csrfToken)
|
||||
testServer.Config.Handler.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusFound, rr.Code)
|
||||
|
||||
tokenValidationMode = 0
|
||||
}
|
||||
|
||||
func TestUpdateWebAdminInvalidClaims(t *testing.T) {
|
||||
server := httpdServer{}
|
||||
server.initializeRouter()
|
||||
|
@ -3848,6 +4016,46 @@ func TestI18NErrors(t *testing.T) {
|
|||
assert.Equal(t, `{"a":"b"}`, errI18n.Args())
|
||||
}
|
||||
|
||||
func getCSRFTokenFromBody(body io.Reader) (string, error) {
|
||||
doc, err := html.Parse(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var csrfToken string
|
||||
var f func(*html.Node)
|
||||
|
||||
f = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "input" {
|
||||
var name, value string
|
||||
for _, attr := range n.Attr {
|
||||
if attr.Key == "value" {
|
||||
value = attr.Val
|
||||
}
|
||||
if attr.Key == "name" {
|
||||
name = attr.Val
|
||||
}
|
||||
}
|
||||
if name == csrfFormToken {
|
||||
csrfToken = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
|
||||
f(doc)
|
||||
|
||||
if csrfToken == "" {
|
||||
return "", errors.New("CSRF token not found")
|
||||
}
|
||||
|
||||
return csrfToken, nil
|
||||
}
|
||||
|
||||
func isSharedProviderSupported() bool {
|
||||
// SQLite shares the implementation with other SQL-based provider but it makes no sense
|
||||
// to use it outside test cases
|
||||
|
|
|
@ -95,6 +95,10 @@ func validateJWTToken(w http.ResponseWriter, r *http.Request, audience tokenAudi
|
|||
doRedirect("Your token is not valid", nil)
|
||||
return err
|
||||
}
|
||||
if err := checkTokenSignature(r, token); err != nil {
|
||||
doRedirect("Your token is no longer valid", nil)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -135,6 +135,8 @@ func TestOIDCInitialization(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOIDCLoginLogout(t *testing.T) {
|
||||
tokenValidationMode = 2
|
||||
|
||||
oidcMgr, ok := oidcMgr.(*memoryOIDCManager)
|
||||
require.True(t, ok)
|
||||
server := getTestOIDCServer()
|
||||
|
@ -552,6 +554,8 @@ func TestOIDCLoginLogout(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
err = dataprovider.DeleteUser(username, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
tokenValidationMode = 0
|
||||
}
|
||||
|
||||
func TestOIDCRefreshToken(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue