Merge branch 'main' into bugfix/CARECON-212-Kopiermonster-fix

This commit is contained in:
Jens Hausherr 2025-04-29 17:48:26 +02:00 committed by GitHub
commit 5d4634b12a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
162 changed files with 5118 additions and 1592 deletions

View file

@ -2,17 +2,17 @@ freebsd_task:
name: FreeBSD
matrix:
- name: FreeBSD 14.1
- name: FreeBSD 14.2
freebsd_instance:
image_family: freebsd-14-1
image_family: freebsd-14-2
pkginstall_script:
- pkg update -f
- pkg install -y go123
- pkg install -y go124
- pkg install -y git
setup_script:
- ln -s /usr/local/bin/go123 /usr/local/bin/go
- ln -s /usr/local/bin/go124 /usr/local/bin/go
- pw groupadd sftpgo
- pw useradd sftpgo -g sftpgo -w none -m
- mkdir /home/sftpgo/sftpgo

View file

@ -15,7 +15,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
go: ['1.23']
go: ['1.24']
os: [ubuntu-latest, macos-latest]
steps:
@ -119,7 +119,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build
run: |
@ -130,7 +130,7 @@ jobs:
$COMMITS_FROM_TAG= ((git rev-list $REV_LIST --count) | Out-String).Trim()
$FILE_VERSION = $LATEST_TAG.substring(1) + "." + $COMMITS_FROM_TAG
go install github.com/tc-hib/go-winres@latest
go-winres simply --arch amd64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch amd64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o sftpgo.exe
cd tests/eventsearcher
go build -trimpath -ldflags "-s -w" -o eventsearcher.exe
@ -142,11 +142,11 @@ jobs:
$Env:CGO_ENABLED='0'
$Env:GOOS='windows'
$Env:GOARCH='arm64'
go-winres simply --arch arm64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch arm64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
mkdir x86
$Env:GOARCH='386'
go-winres simply --arch 386 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch 386 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
Remove-Item Env:\CGO_ENABLED
Remove-Item Env:\GOOS
@ -154,7 +154,7 @@ jobs:
- name: Sign binaries
if: ${{ github.event_name != 'pull_request' }}
uses: azure/trusted-signing-action@v0.5.0
uses: azure/trusted-signing-action@v0.5.1
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: nicola
@ -248,7 +248,7 @@ jobs:
- name: Sign installers
if: ${{ github.event_name != 'pull_request' }}
uses: azure/trusted-signing-action@v0.5.0
uses: azure/trusted-signing-action@v0.5.1
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: nicola
@ -326,7 +326,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build
run: |
@ -390,7 +390,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- name: Build
run: |
@ -529,7 +529,7 @@ jobs:
gzip output/man/man1/*
cp sftpgo output/
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build
@ -611,10 +611,9 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
- uses: actions/checkout@v4
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
args: --timeout=10m
version: latest

View file

@ -9,7 +9,7 @@ permissions:
contents: write
env:
GO_VERSION: 1.23.3
GO_VERSION: 1.24.0
jobs:
prepare-sources-with-deps:
@ -72,17 +72,17 @@ jobs:
$DATE_TIME = ([datetime]::Now.ToUniversalTime().toString("yyyy-MM-ddTHH:mm:ssZ")) | Out-String
$FILE_VERSION = $Env:SFTPGO_VERSION.substring(1) + ".0"
go install github.com/tc-hib/go-winres@latest
go-winres simply --arch amd64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch amd64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o sftpgo.exe
mkdir arm64
$Env:CGO_ENABLED='0'
$Env:GOOS='windows'
$Env:GOARCH='arm64'
go-winres simply --arch arm64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch arm64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
mkdir x86
$Env:GOARCH='386'
go-winres simply --arch 386 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version $FILE_VERSION --file-description "SFTPGo server" --product-name SFTPGo --copyright "AGPL-3.0 with additional terms" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go-winres simply --arch 386 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
Remove-Item Env:\CGO_ENABLED
Remove-Item Env:\GOOS
@ -91,7 +91,7 @@ jobs:
SFTPGO_VERSION: ${{ steps.get_version.outputs.VERSION }}
- name: Sign binaries
uses: azure/trusted-signing-action@v0.5.0
uses: azure/trusted-signing-action@v0.5.1
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: nicola
@ -153,7 +153,7 @@ jobs:
SFTPGO_ISS_VERSION: ${{ steps.get_version.outputs.VERSION }}
- name: Sign installers
uses: azure/trusted-signing-action@v0.5.0
uses: azure/trusted-signing-action@v0.5.1
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: nicola
@ -227,7 +227,7 @@ jobs:
prepare-mac:
name: Prepare macOS binaries
runs-on: macos-12
runs-on: macos-13
steps:
- uses: actions/checkout@v4
@ -379,7 +379,7 @@ jobs:
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build

View file

@ -1,52 +1,54 @@
version: "2"
run:
timeout: 10m
issues-exit-code: 1
tests: true
linters-settings:
dupl:
threshold: 150
errcheck:
check-type-assertions: false
check-blank: false
goconst:
min-len: 3
min-occurrences: 3
gocyclo:
min-complexity: 15
gofmt:
simplify: true
goimports:
local-prefixes: github.com/drakkan/sftpgo
#govet:
# report about shadowed variables
#check-shadowing: true
#enable:
# - fieldalignment
issues:
include:
- EXC0002
- EXC0012
- EXC0013
- EXC0014
- EXC0015
linters:
enable:
- goconst
- errcheck
- gofmt
- goimports
- revive
- unconvert
- unparam
- bodyclose
- dogsled
- dupl
- goconst
- gocyclo
- misspell
- whitespace
- dupl
- revive
- rowserrcheck
- dogsled
- govet
- unconvert
- unparam
- whitespace
settings:
dupl:
threshold: 150
errcheck:
check-type-assertions: false
check-blank: false
goconst:
min-len: 3
min-occurrences: 3
gocyclo:
min-complexity: 15
exclusions:
generated: lax
presets:
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
simplify: true
goimports:
local-prefixes:
- github.com/drakkan/sftpgo
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View file

@ -1,4 +1,4 @@
FROM golang:1.23-bookworm AS builder
FROM golang:1.24-bookworm AS builder
ENV GOFLAGS="-mod=readonly"

View file

@ -1,4 +1,4 @@
FROM golang:1.23-alpine3.21 AS builder
FROM golang:1.24-alpine3.21 AS builder
ENV GOFLAGS="-mod=readonly"

View file

@ -1,4 +1,4 @@
FROM golang:1.23-bookworm AS builder
FROM golang:1.24-bookworm AS builder
ENV CGO_ENABLED=0 GOFLAGS="-mod=readonly"

View file

@ -1,7 +1,6 @@
# SFTPGo
[![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg?branch=main&event=push)
[![Code Coverage](https://codecov.io/gh/drakkan/sftpgo/branch/main/graph/badge.svg)](https://codecov.io/gh/drakkan/sftpgo/branch/main)
[![CI Status](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg)](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg)
[![License: AGPL-3.0-only](https://img.shields.io/badge/License-AGPLv3-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
@ -41,7 +40,9 @@ It is important to understand that you should support SFTPGo and any other Open
[<img src="./img/7digital.png" alt="7digital logo" width="178" height="56">](https://www.7digital.com/)
</br></br>
[<img src="./img/vps2day.png" alt="VPS2day logo" width="234" height="56">](https://www.vps2day.com/)
[<img src="./img/servinga.png" alt="servinga logo" width="258" height="56">](https://servinga.com/)
</br></br>
[<img src="./img/reui.png" alt="ReUI logo" width="151" height="56">](https://www.reui.io/)
## Support
@ -76,7 +77,7 @@ We are very grateful to all the people who contributed with ideas and/or pull re
Thank you to [ysura](https://www.ysura.com/) for granting us stable access to a test AWS S3 account.
Thank you to [KeenThemes](https://keenthemes.com/) for granting us a custom license to use their amazing [Mega Bundle](https://keenthemes.com/products/templates-mega-bundle) for SFTPGo UI.
Thank you to [KeenThemes](https://keenthemes.com/) for granting us a custom license to use their amazing [themes](https://keenthemes.com/bootstrap-templates) for the SFTPGo WebAdmin and WebClient user interfaces, across both the Open Source and Open Core versions.
Thank you to [Crowdin](https://crowdin.com/) for granting us an Open Source License.
@ -86,7 +87,7 @@ Thank you to [Incode](https://www.incode.it/) for helping us to improve the UI/U
SFTPGo source code is licensed under the GNU AGPL-3.0-only with [additional terms](./NOTICE).
The [theme](https://keenthemes.com/products/templates-mega-bundle) used in WebAdmin and WebClient user interfaces is proprietary, this means:
The [theme](https://keenthemes.com/bootstrap-templates) used in WebAdmin and WebClient user interfaces is proprietary, this means:
- KeenThemes HTML/CSS/JS components are allowed for use only within the SFTPGo product and restricted to be used in a resealable HTML template that can compete with KeenThemes products anyhow.
- The SFTPGo WebAdmin and WebClient user interfaces (HTML, CSS and JS components) based on this theme are allowed for use only within the SFTPGo product and therefore cannot be used in derivative works/products without an explicit grant from the [SFTPGo Team](mailto:support@sftpgo.com).
@ -95,4 +96,4 @@ More information about [compliance](https://sftpgo.com/compliance.html).
## Copyright
Copyright (C) 2019 Nicola Murino
Copyright (C) 2019 - 2025 Nicola Murino

View file

@ -1,15 +1,15 @@
module github.com/drakkan/ldapauth
go 1.22.2
go 1.23.0
require (
github.com/go-ldap/ldap/v3 v3.4.8
golang.org/x/crypto v0.31.0
github.com/go-ldap/ldap/v3 v3.4.10
golang.org/x/crypto v0.37.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/google/uuid v1.6.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.32.0 // indirect
)

View file

@ -5,11 +5,11 @@ github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1L
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU=
github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
@ -43,12 +43,17 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@ -56,12 +61,18 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -69,28 +80,38 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View file

@ -1,43 +1,38 @@
module github.com/drakkan/sftpgo/ldapauthserver
go 1.22.2
go 1.23.0
require (
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/render v1.0.3
github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-ldap/ldap/v3 v3.4.10
github.com/nathanaelle/password/v2 v2.0.1
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
golang.org/x/crypto v0.31.0
github.com/rs/zerolog v1.34.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.20.1
golang.org/x/crypto v0.37.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/afero v1.14.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View file

@ -5,24 +5,24 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU=
github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@ -33,8 +33,6 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
@ -53,46 +51,40 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nathanaelle/password/v2 v2.0.1 h1:ItoCTdsuIWzilYmllQPa3DR3YoCXcpfxScWLqr8Ii2s=
github.com/nathanaelle/password/v2 v2.0.1/go.mod h1:eaoT+ICQEPNtikBRIAatN8ThWwMhVG+r1jTw60BvPJk=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -100,8 +92,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -111,14 +103,17 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -127,13 +122,18 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -146,35 +146,42 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

224
go.mod
View file

@ -1,203 +1,205 @@
module github.com/drakkan/sftpgo/v2
go 1.22.7
go 1.24.0
require (
cloud.google.com/go/storage v1.49.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0
cloud.google.com/go/storage v1.52.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.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.32.7
github.com/aws/aws-sdk-go-v2/config v1.28.7
github.com/aws/aws-sdk-go-v2/credentials v1.17.48
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.45
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.25.8
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.8
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3
github.com/bmatcuk/doublestar/v4 v4.7.1
github.com/cockroachdb/cockroach-go/v2 v2.3.8
github.com/coreos/go-oidc/v3 v3.12.0
github.com/aws/aws-sdk-go-v2 v1.36.3
github.com/aws/aws-sdk-go-v2/config v1.29.14
github.com/aws/aws-sdk-go-v2/credentials v1.17.67
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.73
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.29.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.4
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/cockroachdb/cockroach-go/v2 v2.4.0
github.com/coreos/go-oidc/v3 v3.14.1
github.com/drakkan/webdav v0.0.0-20241026165615-b8b8f74ae71b
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
github.com/fclairamb/ftpserverlib v0.25.0
github.com/fclairamb/go-log v0.5.0
github.com/go-acme/lego/v4 v4.21.0
github.com/go-chi/chi/v5 v5.2.0
github.com/go-chi/jwtauth/v5 v5.3.2
github.com/go-acme/lego/v4 v4.23.1
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/jwtauth/v5 v5.3.3
github.com/go-chi/render v1.0.3
github.com/go-sql-driver/mysql v1.8.1
github.com/go-sql-driver/mysql v1.9.2
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-plugin v1.6.2
github.com/hashicorp/go-plugin v1.6.3
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/jackc/pgx/v5 v5.7.2
github.com/jackc/pgx/v5 v5.7.4
github.com/jlaffaye/ftp v0.2.0
github.com/klauspost/compress v1.17.11
github.com/lestrrat-go/jwx/v2 v2.1.3
github.com/klauspost/compress v1.18.0
github.com/lestrrat-go/jwx/v2 v2.1.6-0.20250420095048-a18531964875
github.com/lithammer/shortuuid/v4 v4.2.0
github.com/mattn/go-sqlite3 v1.14.24
github.com/mattn/go-sqlite3 v1.14.28
github.com/mhale/smtpd v0.8.3
github.com/minio/sio v0.4.1
github.com/otiai10/copy v1.14.0
github.com/otiai10/copy v1.14.1
github.com/pires/go-proxyproto v0.8.0
github.com/pkg/sftp v1.13.7
github.com/pkg/sftp v1.13.9
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.22.0
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.11.1
github.com/rs/xid v1.6.0
github.com/rs/zerolog v1.33.0
github.com/rs/zerolog v1.34.0
github.com/sftpgo/sdk v0.1.9-0.20241011171103-64fc18a344f9
github.com/shirou/gopsutil/v3 v3.24.5
github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/spf13/afero v1.14.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
github.com/studio-b12/gowebdav v0.10.0
github.com/subosito/gotenv v1.6.0
github.com/unrolled/secure v1.17.0
github.com/wagslane/go-password-validator v0.3.0
github.com/wneessen/go-mail v0.5.2
github.com/wneessen/go-mail v0.6.2
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a
go.etcd.io/bbolt v1.3.11
go.etcd.io/bbolt v1.4.0
go.uber.org/automaxprocs v1.6.0
gocloud.dev v0.40.0
golang.org/x/crypto v0.31.0
golang.org/x/net v0.33.0
golang.org/x/oauth2 v0.25.0
golang.org/x/sys v0.29.0
golang.org/x/term v0.27.0
golang.org/x/time v0.9.0
google.golang.org/api v0.214.0
gocloud.dev v0.41.0
golang.org/x/crypto v0.37.0
golang.org/x/net v0.39.0
golang.org/x/oauth2 v0.29.0
golang.org/x/sys v0.32.0
golang.org/x/term v0.31.0
golang.org/x/time v0.11.0
google.golang.org/api v0.230.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require (
cel.dev/expr v0.19.1 // indirect
cloud.google.com/go v0.118.0 // indirect
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cel.dev/expr v0.23.1 // indirect
cloud.google.com/go v0.120.1 // indirect
cloud.google.com/go/auth v0.16.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.3.1 // indirect
cloud.google.com/go/monitoring v1.22.1 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // 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.26 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
github.com/aws/smithy-go v1.22.3 // 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/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // 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/envoyproxy/go-control-plane v0.13.3 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.2 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.1-0.20250421195336-4ff65aefe8a5 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.3 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.6 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/miekg/dns v1.1.65 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.61.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.63.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sagikazarmark/locafero v0.9.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/tools v0.32.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/genproto v0.0.0-20250102185135-69823020774d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
google.golang.org/grpc v1.69.2 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
google.golang.org/genproto v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace (
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20240603150004-6a8f643fbf2e
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20250204143431-e069fad14727
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20240430173938-7ba8270c8e7f
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20241215104834-a9cd4736223d
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20250420150858-04abdc60032d
)

492
go.sum
View file

@ -1,106 +1,110 @@
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ=
cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM=
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go v0.120.1 h1:Z+5V7yd383+9617XDCyszmK5E4wJRJL+tquMfDj9hLM=
cloud.google.com/go v0.120.1/go.mod h1:56Vs7sf/i2jYM6ZL9NYlC82r04PThNcPS5YgFmb0rp8=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E=
cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34=
cloud.google.com/go/kms v1.20.3 h1:a61yIN5LN8ozWxOC6tjUx5V5SEzfkS+b69kYMQfzGzE=
cloud.google.com/go/kms v1.20.3/go.mod h1:YvX+xhp2E2Sc3vol5IcRlBhH14Ecl3kegUY/DtH7EWQ=
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng=
cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
cloud.google.com/go/monitoring v1.22.1 h1:KQbnAC4IAH+5x3iWuPZT5iN9VXqKMzzOgqcYB6fqPDE=
cloud.google.com/go/monitoring v1.22.1/go.mod h1:AuZZXAoN0WWWfsSvET1Cpc4/1D8LXq8KRDU87fMS6XY=
cloud.google.com/go/storage v1.49.0 h1:zenOPBOWHCnojRd9aJZAyQXBYqkJkdQS42dxL55CIMw=
cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5PiwehZI9qGU=
cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI=
cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE=
cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/storage v1.52.0 h1:ROpzMW/IwipKtatA69ikxibdzQSiXJrY9f6IgBa9AlA=
cloud.google.com/go/storage v1.52.0/go.mod h1:4wrBAbAYUvYkbrf19ahGm4I5kDQhESSqN3CGEkMGvOY=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
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.5.0 h1:mlmW46Q0B79I+Aj4azKC6xDMFN9a9SyZWESlGWYXbFs=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0/go.mod h1:PXe2h+LKcWTX9afWdZoHyODqR4fBa5boUM/8uJfZ0Jo=
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/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI=
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
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.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE=
github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.45 h1:ZxB8WFVYwolhDZxuZXoesHkl+L9cXLWy0K/G0QkNATc=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.45/go.mod h1:1krrbyoFFDqaNldmltPTP+mK3sAXLHPoaFtISOw2Hkk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
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.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.25.8 h1:A9pJ60b8AKwlXiSbznKBcDaBTA7jIaI6gHSDqQeAZOg=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.25.8/go.mod h1:/aDbp2jKTGdpJwFHuwQeypaIPlCjkxMqDVUB+7GizdU=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.8 h1:WT3EPriVEpHE2jeNqHqj7l43JCIWPoZjNNRluZ7agII=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.8/go.mod h1:By/yiMzR0yfhPaqRWE3GrT9B/Z6871z1GfWGc+vf4Y8=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.73 h1:I91eIdOJMVK9oNiH2jvhp/AxMW+Gff8Rb5VjVHMhcJU=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.73/go.mod h1:vq7/m7dahFXcdzWVOvvjasDI9RcsD3RsTfHmDundJYg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.29.0 h1:ReXrjtwv4LSfi8bLmbMparantI0YnRb1NlPMf8gAVCQ=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.29.0/go.mod h1:ctydsY6pVUtI6JnPssiu5YZabqUt4ZNONqJHehtiKBo=
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 h1:tWUG+4wZqdMl/znThEk9tcCy8tTMxq8dW0JTgamohrY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.4 h1:EKXYJ8kgz4fiqef8xApu7eH0eae2SrVG+oHCLFybMRI=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.4/go.mod h1:yGhDiLKguA3iFJYxbrQkQiNzuy+ddxesSZYWVeeEH5Q=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
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.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
@ -113,31 +117,32 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 h1:boJj011Hh+874zpIySeApCX4GeOjPl9qhRF3QuIZq+Q=
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cockroachdb/cockroach-go/v2 v2.3.8 h1:53yoUo4+EtrC1NrAEgnnad4AS3ntNvGup1PAXZ7UmpE=
github.com/cockroachdb/cockroach-go/v2 v2.3.8/go.mod h1:9uH5jK4yQ3ZQUT9IXe4I2fHzMIF5+JC/oOdzTRgJYJk=
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/cockroachdb/cockroach-go/v2 v2.4.0 h1:7K5vpE3m7LylIbmpbr4eEhApDTPMgFgR+eDPy1sdJjM=
github.com/cockroachdb/cockroach-go/v2 v2.4.0/go.mod h1:9U179XbCx4qFWtNhc7BiWLPfuyMVQ7qdAhfrwLz1vH0=
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
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/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0 h1:EW9gIJRmt9lzk66Fhh4S8VEtURA6QHZqGeSRE9Nb2/U=
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/drakkan/crypto v0.0.0-20241215104834-a9cd4736223d h1:xpQVtm9fMX+Moy260dDuYlKyER0L8jj7WJF6bcLXtL4=
github.com/drakkan/crypto v0.0.0-20241215104834-a9cd4736223d/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
github.com/drakkan/crypto v0.0.0-20250420150858-04abdc60032d h1:uPtHXlWXdRX3GU7o72UFr+N4gkWXJ9UuBsNAqjqnB2k=
github.com/drakkan/crypto v0.0.0-20250420150858-04abdc60032d/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
github.com/drakkan/ftp v0.0.0-20240430173938-7ba8270c8e7f h1:S9JUlrOzjK58UKoLqqb40YLyVlt0bcIFtYrvnanV3zc=
github.com/drakkan/ftp v0.0.0-20240430173938-7ba8270c8e7f/go.mod h1:4p8lUl4vQ80L598CygL+3IFtm+3nggvvW/palOlViwE=
github.com/drakkan/ftpserverlib v0.0.0-20240603150004-6a8f643fbf2e h1:VBpqQeChkGXSV1FXCtvd3BJTyB+DcMgiu7SfkpsGuKw=
github.com/drakkan/ftpserverlib v0.0.0-20240603150004-6a8f643fbf2e/go.mod h1:aAwyOAC6IIe+IZeeGD1QjuE3GGDzqW/c5Xtn+Dp0JUM=
github.com/drakkan/ftpserverlib v0.0.0-20250204143431-e069fad14727 h1:OwxAvQejxuEYFtuXcOxuepEjt6VPLEQ3zK+5k9p4M60=
github.com/drakkan/ftpserverlib v0.0.0-20250204143431-e069fad14727/go.mod h1:TRdVBbJEt+KihZKGl6jgSp6H/yPc0NxMUAlMZuNHcmY=
github.com/drakkan/webdav v0.0.0-20241026165615-b8b8f74ae71b h1:Y1tLiQ8fnxM5f3wiBjAXsHzHNwiY9BR+mXZA75nZwrs=
github.com/drakkan/webdav v0.0.0-20241026165615-b8b8f74ae71b/go.mod h1:zOVb1QDhwwqWn2L2qZ0U3swMSO4GTSNyIwXCGO/UGWE=
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
@ -145,15 +150,15 @@ github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001/go.mod h1:kltMsfRMTH
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.13.3 h1:F2vYcSF8iRNhfvhZQRZ5Dvuyu0TpXazE9+h53TzkvA4=
github.com/envoyproxy/go-control-plane v0.13.3/go.mod h1:uhvHSBAMSvy2Y+CuAYfByIRH19zcdir1rgmMzKUo3eA=
github.com/envoyproxy/go-control-plane/envoy v1.32.2 h1:zidqwmijfcbyKqVxjQDFx042PgX+p9U+/fu/f9VtSk8=
github.com/envoyproxy/go-control-plane/envoy v1.32.2/go.mod h1:eR2SOX2IedqlPvmiKjUH7Wu//S602JKI7HPC/L3SRq8=
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
@ -163,18 +168,18 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o=
github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/jwtauth/v5 v5.3.2 h1:s+ON3ATyyMs3Me0kqyuua6Rwu+2zqIIkL0GCaMarwvs=
github.com/go-chi/jwtauth/v5 v5.3.2/go.mod h1:O4QvPRuZLZghl9WvfVaON+ARfGzpD2PBX/QY5vUz7aQ=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4=
github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/jwtauth/v5 v5.3.3 h1:50Uzmacu35/ZP9ER2Ht6SazwPsnLQ9LRJy6zTZJpHEo=
github.com/go-chi/jwtauth/v5 v5.3.3/go.mod h1:O4QvPRuZLZghl9WvfVaON+ARfGzpD2PBX/QY5vUz7aQ=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-jose/go-jose/v4 v4.1.1-0.20250421195336-4ff65aefe8a5 h1:bOa3sZOH8fLlvgO9T1avU8Rwe79aNDyrOSRb1XHeqRM=
github.com/go-jose/go-jose/v4 v4.1.1-0.20250421195336-4ff65aefe8a5/go.mod h1:6zIvRV16dgvcCxXgz0tOZ9XjntGo7SWTeTEd2hR5JlE=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
@ -187,15 +192,17 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
@ -218,12 +225,13 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -231,8 +239,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -244,12 +252,10 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -258,14 +264,16 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -274,63 +282,60 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.1.3 h1:Ud4lb2QuxRClYAmRleF50KrbKIoM1TddXgBrneT5/Jo=
github.com/lestrrat-go/jwx/v2 v2.1.3/go.mod h1:q6uFgbgZfEmQrfJfrCo90QcQOcXFMfbI/fO0NqRtvZo=
github.com/lestrrat-go/jwx/v2 v2.1.6-0.20250420095048-a18531964875 h1:96mya9rDuzBBnNlzlIjiQBBuUkNxCaysfTb8mU4xFRo=
github.com/lestrrat-go/jwx/v2 v2.1.6-0.20250420095048-a18531964875/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mhale/smtpd v0.8.3 h1:8j8YNXajksoSLZja3HdwvYVZPuJSqAxFsib3adzRRt8=
github.com/mhale/smtpd v0.8.3/go.mod h1:MQl+y2hwIEQCXtNhe5+55n0GZOjSmeqORDIXbqUL3x4=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
github.com/minio/sio v0.4.1 h1:EMe3YBC1nf+sRQia65Rutxi+Z554XPV0dt8BIBA+a/0=
github.com/minio/sio v0.4.1/go.mod h1:oBSjJeGbBdRMZZwna07sX9EFzZy+ywu5aofRiV1g79I=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -342,30 +347,29 @@ 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.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
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=
github.com/prometheus/common v0.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4/go.mod h1:MnkX001NG75g3p8bhFycnyIjeQoOjGL6CEIsdE/nKSY=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
@ -380,16 +384,18 @@ github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -406,54 +412,54 @@ github.com/studio-b12/gowebdav v0.10.0 h1:Yewz8FFiadcGEu4hxS/AAJQlHelndqln1bns3h
github.com/studio-b12/gowebdav v0.10.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I=
github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ=
github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8=
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a h1:XfF01GyP+0eWCaVp0y6rNN+kFp7pt9Da4UUYrJ5XPWA=
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a/go.mod h1:aXb8yZQEWo1XHGMf1qQfnb83GR/EJ2EBlwtUgAaNBoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/detectors/gcp v1.33.0 h1:FVPoXEoILwgbZUu4X7YSgsESsAmGRgoYcnXkzgQPhP4=
go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c9s2SOlgoSRyMpNVzUj2fZqI=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCtNbsP3JkNqU=
go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0 h1:PB3Zrjs1sG1GBX51SXyTSoOTqcDglmsk7nT6tkKPb/k=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.35.0/go.mod h1:U2R3XyVPzn0WX7wOIypPuptulsMcPDPs/oiSVOMVnHY=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
gocloud.dev v0.40.0 h1:f8LgP+4WDqOG/RXoUcyLpeIAGOcAbZrZbDQCUee10ng=
gocloud.dev v0.40.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ=
gocloud.dev v0.41.0 h1:qBKd9jZkBKEghYbP/uThpomhedK5s2Gy6Lz7h/zYYrM=
gocloud.dev v0.41.0/go.mod h1:IetpBcWLUwroOOxKr90lhsZ8vWxeSkuszBnW62sbcf0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@ -462,8 +468,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -475,11 +481,11 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -488,9 +494,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -513,16 +519,16 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@ -530,11 +536,11 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -545,32 +551,32 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA=
google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE=
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
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=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20250102185135-69823020774d h1:3NH+6ZtWWhXDpEJEAtzF1Gp/zA87pKkIB4gO1Ag8VSI=
google.golang.org/genproto v0.0.0-20250102185135-69823020774d/go.mod h1:zhXVSAeuPiprFfMSrt7Jo1Uighv2Nfu3HAZrw83tcYE=
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d h1:H8tOf8XM88HvKqLTxe755haY6r1fqqzLbEnfrmLXlSA=
google.golang.org/genproto/googleapis/api v0.0.0-20250102185135-69823020774d/go.mod h1:2v7Z7gP2ZUOGsaFyxATQSRoBnKygqVq2Cwnvom7QiqY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
google.golang.org/genproto v0.0.0-20250422160041-2d3770c4ea7f h1:iZiXS7qm4saaCcdK7S/i1Qx9ZHO2oa16HQqwYc1tPKY=
google.golang.org/genproto v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cej/8iHf9mPl71o/a+R1rrvSFrAAVCUFX9s/sbNttBc=
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
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.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
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=
@ -580,13 +586,11 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

BIN
img/reui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
img/servinga.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8 KiB

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build bundle
// +build bundle
package bundle

View file

@ -24,6 +24,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/util"
)
@ -56,6 +57,15 @@ renewed by the SFTPGo service
logger.ErrorToConsole("unable to initialize KMS: %v", err)
os.Exit(1)
}
if config.HasKMSPlugin() {
if err := plugin.Initialize(config.GetPluginsConfig(), "debug"); err != nil {
logger.ErrorToConsole("unable to initialize plugin system: %v", err)
os.Exit(1)
}
registerSignals()
defer plugin.Handler.Cleanup()
}
mfaConfig := config.GetMFAConfig()
err = mfaConfig.Initialize()
if err != nil {

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build awscontainer
// +build awscontainer
package cmd

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !awscontainer
// +build !awscontainer
package cmd

View file

@ -24,6 +24,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/service"
"github.com/drakkan/sftpgo/v2/internal/util"
)
@ -65,6 +66,15 @@ Please take a look at the usage below to customize the options.`,
logger.ErrorToConsole("Unable to initialize KMS: %v", err)
os.Exit(1)
}
if config.HasKMSPlugin() {
if err := plugin.Initialize(config.GetPluginsConfig(), "debug"); err != nil {
logger.ErrorToConsole("unable to initialize plugin system: %v", err)
os.Exit(1)
}
registerSignals()
defer plugin.Handler.Cleanup()
}
mfaConfig := config.GetMFAConfig()
err = mfaConfig.Initialize()
if err != nil {
@ -78,11 +88,12 @@ Please take a look at the usage below to customize the options.`,
providerConf.Actions.ExecuteOn = nil
logger.InfoToConsole("Initializing provider: %q config file: %q", providerConf.Driver, viper.ConfigFileUsed())
err = dataprovider.InitializeDatabase(providerConf, configDir)
if err == nil {
switch err {
case nil:
logger.InfoToConsole("Data provider successfully initialized/updated")
} else if err == dataprovider.ErrNoInitRequired {
case dataprovider.ErrNoInitRequired:
logger.InfoToConsole("%v", err.Error())
} else {
default:
logger.ErrorToConsole("Unable to initialize/update the data provider: %v", err)
os.Exit(1)
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !noportable
// +build !noportable
package cmd

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build noportable
// +build noportable
package cmd

View file

@ -27,6 +27,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/util"
)
@ -58,6 +59,15 @@ Please take a look at the usage below to customize the options.`,
logger.ErrorToConsole("unable to initialize KMS: %v", err)
os.Exit(1)
}
if config.HasKMSPlugin() {
if err := plugin.Initialize(config.GetPluginsConfig(), "debug"); err != nil {
logger.ErrorToConsole("unable to initialize plugin system: %v", err)
os.Exit(1)
}
registerSignals()
defer plugin.Handler.Cleanup()
}
mfaConfig := config.GetMFAConfig()
err = mfaConfig.Initialize()
if err != nil {

View file

@ -24,6 +24,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/util"
)
@ -56,6 +57,21 @@ Please take a look at the usage below to customize the options.`,
logger.ErrorToConsole("unable to initialize KMS: %v", err)
os.Exit(1)
}
if config.HasKMSPlugin() {
if err := plugin.Initialize(config.GetPluginsConfig(), "debug"); err != nil {
logger.ErrorToConsole("unable to initialize plugin system: %v", err)
os.Exit(1)
}
registerSignals()
defer plugin.Handler.Cleanup()
}
mfaConfig := config.GetMFAConfig()
err = mfaConfig.Initialize()
if err != nil {
logger.ErrorToConsole("Unable to initialize MFA: %v", err)
os.Exit(1)
}
providerConf := config.GetProviderConf()
logger.InfoToConsole("Reverting provider: %q config file: %q target version %d", providerConf.Driver,
viper.ConfigFileUsed(), revertProviderTargetVersion)

View file

@ -0,0 +1,41 @@
// Copyright (C) 2025 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !windows
package cmd
import (
"os"
"os/signal"
"syscall"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
)
func registerSignals() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
go func() {
for sig := range c {
switch sig {
case syscall.SIGINT, syscall.SIGTERM:
logger.DebugToConsole("Received interrupt request")
plugin.Handler.Cleanup()
os.Exit(0)
}
}
}()
}

View file

@ -0,0 +1,36 @@
// Copyright (C) 2025 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"os"
"os/signal"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
)
func registerSignals() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
logger.DebugToConsole("Received interrupt request")
plugin.Handler.Cleanup()
os.Exit(0)
}
}()
}

View file

@ -1,227 +0,0 @@
// Copyright (C) 2019 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"io"
"os"
"os/user"
"path/filepath"
"github.com/rs/xid"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/drakkan/sftpgo/v2/internal/common"
"github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/sftpd"
"github.com/drakkan/sftpgo/v2/internal/version"
)
var (
logJournalD = false
preserveHomeDir = false
baseHomeDir = ""
subsystemCmd = &cobra.Command{
Use: "startsubsys",
Short: "Use sftpgo as SFTP file transfer subsystem",
Long: `In this mode SFTPGo speaks the server side of SFTP protocol to stdout and
expects client requests from stdin.
This mode is not intended to be called directly, but from sshd using the
Subsystem option.
For example adding a line like this one in "/etc/ssh/sshd_config":
Subsystem sftp sftpgo startsubsys
Command-line flags should be specified in the Subsystem declaration.
`,
Run: func(_ *cobra.Command, _ []string) {
logSender := "startsubsys"
connectionID := xid.New().String()
var zeroLogLevel zerolog.Level
switch logLevel {
case "info":
zeroLogLevel = zerolog.InfoLevel
case "warn":
zeroLogLevel = zerolog.WarnLevel
case "error":
zeroLogLevel = zerolog.ErrorLevel
default:
zeroLogLevel = zerolog.DebugLevel
}
logger.SetLogTime(logUTCTime)
if logJournalD {
logger.InitJournalDLogger(zeroLogLevel)
} else {
logger.InitStdErrLogger(zeroLogLevel)
}
osUser, err := user.Current()
if err != nil {
logger.Error(logSender, connectionID, "unable to get the current user: %v", err)
os.Exit(1)
}
username := osUser.Username
homedir := osUser.HomeDir
logger.Info(logSender, connectionID, "starting SFTPGo %v as subsystem, user %q home dir %q config dir %q base home dir %q",
version.Get(), username, homedir, configDir, baseHomeDir)
err = config.LoadConfig(configDir, configFile)
if err != nil {
logger.Error(logSender, connectionID, "unable to load configuration: %v", err)
os.Exit(1)
}
kmsConfig := config.GetKMSConfig()
if err := kmsConfig.Initialize(); err != nil {
logger.Error(logSender, connectionID, "unable to initialize KMS: %v", err)
os.Exit(1)
}
if err := plugin.Initialize(config.GetPluginsConfig(), logLevel); err != nil {
logger.Error(logSender, connectionID, "unable to initialize plugin system: %v", err)
os.Exit(1)
}
mfaConfig := config.GetMFAConfig()
err = mfaConfig.Initialize()
if err != nil {
logger.Error(logSender, "", "unable to initialize MFA: %v", err)
os.Exit(1)
}
dataProviderConf := config.GetProviderConf()
if dataProviderConf.Driver == dataprovider.SQLiteDataProviderName || dataProviderConf.Driver == dataprovider.BoltDataProviderName {
logger.Debug(logSender, connectionID, "data provider %q not supported in subsystem mode, using %q provider",
dataProviderConf.Driver, dataprovider.MemoryDataProviderName)
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
dataProviderConf.Name = ""
}
config.SetProviderConf(dataProviderConf)
err = dataprovider.Initialize(dataProviderConf, configDir, false)
if err != nil {
logger.Error(logSender, connectionID, "unable to initialize the data provider: %v", err)
os.Exit(1)
}
smtpConfig := config.GetSMTPConfig()
err = smtpConfig.Initialize(configDir, false)
if err != nil {
logger.Error(logSender, connectionID, "unable to initialize SMTP configuration: %v", err)
os.Exit(1)
}
commonConfig := config.GetCommonConfig()
// idle connection are managed externally
commonConfig.IdleTimeout = 0
config.SetCommonConfig(commonConfig)
if err := common.Initialize(config.GetCommonConfig(), dataProviderConf.GetShared()); err != nil {
logger.Error(logSender, connectionID, "%v", err)
os.Exit(1)
}
httpConfig := config.GetHTTPConfig()
if err := httpConfig.Initialize(configDir); err != nil {
logger.Error(logSender, connectionID, "unable to initialize http client: %v", err)
os.Exit(1)
}
commandConfig := config.GetCommandConfig()
if err := commandConfig.Initialize(); err != nil {
logger.Error(logSender, connectionID, "unable to initialize commands configuration: %v", err)
os.Exit(1)
}
user, err := dataprovider.UserExists(username, "")
if err == nil {
if user.HomeDir != filepath.Clean(homedir) && !preserveHomeDir {
// update the user
user.HomeDir = filepath.Clean(homedir)
err = dataprovider.UpdateUser(&user, dataprovider.ActionExecutorSystem, "", "")
if err != nil {
logger.Error(logSender, connectionID, "unable to update user %q: %v", username, err)
os.Exit(1)
}
}
} else {
user.Username = username
if baseHomeDir != "" && filepath.IsAbs(baseHomeDir) {
user.HomeDir = filepath.Join(baseHomeDir, username)
} else {
user.HomeDir = filepath.Clean(homedir)
}
logger.Debug(logSender, connectionID, "home dir for new user %q", user.HomeDir)
user.Password = connectionID
user.Permissions = make(map[string][]string)
user.Permissions["/"] = []string{dataprovider.PermAny}
err = dataprovider.AddUser(&user, dataprovider.ActionExecutorSystem, "", "")
if err != nil {
logger.Error(logSender, connectionID, "unable to add user %q: %v", username, err)
os.Exit(1)
}
}
err = user.LoadAndApplyGroupSettings()
if err != nil {
logger.Error(logSender, connectionID, "unable to apply group settings for user %q: %v", username, err)
os.Exit(1)
}
err = sftpd.ServeSubSystemConnection(&user, connectionID, os.Stdin, os.Stdout)
if err != nil && err != io.EOF {
logger.Warn(logSender, connectionID, "serving subsystem finished with error: %v", err)
os.Exit(1)
}
logger.Info(logSender, connectionID, "serving subsystem finished")
plugin.Handler.Cleanup()
os.Exit(0)
},
}
)
func init() {
subsystemCmd.Flags().BoolVarP(&preserveHomeDir, "preserve-home", "p", false, `If the user already exists, the existing home
directory will not be changed`)
subsystemCmd.Flags().StringVarP(&baseHomeDir, "base-home-dir", "d", "", `If the user does not exist specify an alternate
starting directory. The home directory for a new
user will be:
[base-home-dir]/[username]
base-home-dir must be an absolute path.`)
subsystemCmd.Flags().BoolVarP(&logJournalD, "log-to-journald", "j", false, `Send logs to journald. Only available on Linux.
Use:
$ journalctl -o verbose -f
To see full logs.
If not set, the logs will be sent to the standard
error`)
addConfigFlags(subsystemCmd)
viper.SetDefault(logLevelKey, defaultLogLevel)
viper.BindEnv(logLevelKey, "SFTPGO_LOG_LEVEL") //nolint:errcheck
subsystemCmd.Flags().StringVar(&logLevel, logLevelFlag, viper.GetString(logLevelKey),
`Set the log level. Supported values:
debug, info, warn, error.
This flag can be set
using SFTPGO_LOG_LEVEL env var too.
`)
viper.BindPFlag(logLevelKey, subsystemCmd.Flags().Lookup(logLevelFlag)) //nolint:errcheck
viper.SetDefault(logUTCTimeKey, defaultLogUTCTime)
viper.BindEnv(logUTCTimeKey, "SFTPGO_LOG_UTC_TIME") //nolint:errcheck
subsystemCmd.Flags().BoolVar(&logUTCTime, logUTCTimeFlag, viper.GetBool(logUTCTimeKey),
`Use UTC time for logging. This flag can be set
using SFTPGO_LOG_UTC_TIME env var too.
`)
viper.BindPFlag(logUTCTimeKey, subsystemCmd.Flags().Lookup(logUTCTimeFlag)) //nolint:errcheck
rootCmd.AddCommand(subsystemCmd)
}

View file

@ -19,6 +19,7 @@ import (
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
@ -899,12 +900,12 @@ func getProxyPolicy(allowed, skipped []func(net.IP) bool, def proxyproto.Policy)
// Each SSH connection can open several channels for SFTP or SSH commands
type SSHConnection struct {
id string
conn net.Conn
conn io.Closer
lastActivity atomic.Int64
}
// NewSSHConnection returns a new SSHConnection
func NewSSHConnection(id string, conn net.Conn) *SSHConnection {
func NewSSHConnection(id string, conn io.Closer) *SSHConnection {
c := &SSHConnection{
id: id,
conn: conn,

View file

@ -64,7 +64,7 @@ func (c *fakeConnection) AddUser(user dataprovider.User) error {
if err != nil {
return err
}
c.BaseConnection.User = user
c.User = user
return nil
}
@ -999,9 +999,10 @@ func TestConnectionStatus(t *testing.T) {
assert.Len(t, stats, 3)
for _, stat := range stats {
assert.Equal(t, stat.Username, username)
if stat.ConnectionID == "SFTP_id1" {
switch stat.ConnectionID {
case "SFTP_id1":
assert.Len(t, stat.Transfers, 2)
} else if stat.ConnectionID == "DAV_id3" {
case "DAV_id3":
assert.Len(t, stat.Transfers, 1)
}
}

View file

@ -771,10 +771,7 @@ func (c *BaseConnection) Copy(virtualSourcePath, virtualTargetPath string) error
return err
}
}
createTargetDir := true
if dstInfo != nil && dstInfo.IsDir() {
createTargetDir = false
}
createTargetDir := dstInfo == nil || !dstInfo.IsDir()
if err := c.checkCopy(srcInfo, dstInfo, virtualSourcePath, destPath); err != nil {
return err
}

View file

@ -62,7 +62,7 @@ func (d *dbDefender) GetHost(ip string) (dataprovider.DefenderEntry, error) {
// and increase ban time if the IP is found.
// This method must be called as soon as the client connects
func (d *dbDefender) IsBanned(ip, protocol string) bool {
if d.baseDefender.isBanned(ip, protocol) {
if d.isBanned(ip, protocol) {
return true
}
@ -95,15 +95,15 @@ func (d *dbDefender) AddEvent(ip, protocol string, event HostEvent) bool {
return true
}
score := d.baseDefender.getScore(event)
score := d.getScore(event)
host, err := dataprovider.AddDefenderEvent(ip, score, d.getStartObservationTime())
if err != nil {
return false
}
d.baseDefender.logEvent(ip, protocol, event, host.Score)
d.logEvent(ip, protocol, event, host.Score)
if host.Score > d.config.Threshold {
d.baseDefender.logBan(ip, protocol)
d.logBan(ip, protocol)
banTime := time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
err = dataprovider.SetDefenderBanTime(ip, util.GetTimeAsMsSinceEpoch(banTime))
if err == nil {

View file

@ -148,7 +148,7 @@ func (d *memoryDefender) IsBanned(ip, protocol string) bool {
defer d.RUnlock()
return d.baseDefender.isBanned(ip, protocol)
return d.isBanned(ip, protocol)
}
// DeleteHost removes the specified IP from the defender lists
@ -188,7 +188,7 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) bool {
delete(d.banned, ip)
}
score := d.baseDefender.getScore(event)
score := d.getScore(event)
ev := hostEvent{
dateTime: time.Now(),
@ -207,11 +207,11 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) bool {
idx++
}
}
d.baseDefender.logEvent(ip, protocol, event, hs.TotalScore)
d.logEvent(ip, protocol, event, hs.TotalScore)
hs.Events = hs.Events[:idx]
if hs.TotalScore >= d.config.Threshold {
d.baseDefender.logBan(ip, protocol)
d.logBan(ip, protocol)
d.banned[ip] = time.Now().Add(time.Duration(d.config.BanTime) * time.Minute)
delete(d.hosts, ip)
d.cleanupBanned()
@ -225,7 +225,7 @@ func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent) bool {
d.hosts[ip] = hs
}
} else {
d.baseDefender.logEvent(ip, protocol, event, ev.score)
d.logEvent(ip, protocol, event, ev.score)
d.hosts[ip] = hostScore{
TotalScore: ev.score,
Events: []hostEvent{ev},

View file

@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
"html"
"io"
"mime"
"mime/multipart"
@ -56,8 +57,8 @@ import (
const (
ipBlockedEventName = "IP Blocked"
maxAttachmentsSize = int64(10 * 1024 * 1024)
objDataPlaceholder = "{{ObjectData}}"
objDataPlaceholderString = "{{ObjectDataString}}"
objDataPlaceholder = "{{.ObjectData}}"
objDataPlaceholderString = "{{.ObjectDataString}}"
dateTimeMillisFormat = "2006-01-02T15:04:05.000"
)
@ -775,55 +776,70 @@ func (p *EventParams) getRetentionReportsAsMailAttachment() (*mail.File, error)
}, nil
}
func (*EventParams) getStringReplacement(val string, jsonEscaped bool) string {
if jsonEscaped {
func (*EventParams) getStringReplacement(val string, escapeMode int) string {
switch escapeMode {
case 1:
return util.JSONEscape(val)
case 2:
return html.EscapeString(val)
default:
return val
}
return val
}
func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []string {
func (p *EventParams) getStringReplacements(addObjectData bool, escapeMode int) []string {
var dateTimeString string
if Config.TZ == "local" {
dateTimeString = p.Timestamp.Local().Format(dateTimeMillisFormat)
} else {
dateTimeString = p.Timestamp.UTC().Format(dateTimeMillisFormat)
}
year := dateTimeString[0:4]
month := dateTimeString[5:7]
day := dateTimeString[8:10]
hour := dateTimeString[11:13]
minute := dateTimeString[14:16]
replacements := []string{
"{{Name}}", p.getStringReplacement(p.Name, jsonEscaped),
"{{Event}}", p.Event,
"{{Status}}", fmt.Sprintf("%d", p.Status),
"{{VirtualPath}}", p.getStringReplacement(p.VirtualPath, jsonEscaped),
"{{EscapedVirtualPath}}", p.getStringReplacement(url.QueryEscape(p.VirtualPath), jsonEscaped),
"{{FsPath}}", p.getStringReplacement(p.FsPath, jsonEscaped),
"{{VirtualTargetPath}}", p.getStringReplacement(p.VirtualTargetPath, jsonEscaped),
"{{FsTargetPath}}", p.getStringReplacement(p.FsTargetPath, jsonEscaped),
"{{ObjectName}}", p.getStringReplacement(p.ObjectName, jsonEscaped),
"{{ObjectBaseName}}", p.getStringReplacement(strings.TrimSuffix(p.ObjectName, p.Extension), jsonEscaped),
"{{ObjectType}}", p.ObjectType,
"{{FileSize}}", strconv.FormatInt(p.FileSize, 10),
"{{Elapsed}}", strconv.FormatInt(p.Elapsed, 10),
"{{Protocol}}", p.Protocol,
"{{IP}}", p.IP,
"{{Role}}", p.getStringReplacement(p.Role, jsonEscaped),
"{{Email}}", p.getStringReplacement(p.Email, jsonEscaped),
"{{Timestamp}}", strconv.FormatInt(p.Timestamp.UnixNano(), 10),
"{{DateTime}}", dateTimeString,
"{{StatusString}}", p.getStatusString(),
"{{UID}}", p.getStringReplacement(p.UID, jsonEscaped),
"{{Ext}}", p.getStringReplacement(p.Extension, jsonEscaped),
"{{.Name}}", p.getStringReplacement(p.Name, escapeMode),
"{{.Event}}", p.Event,
"{{.Status}}", fmt.Sprintf("%d", p.Status),
"{{.VirtualPath}}", p.getStringReplacement(p.VirtualPath, escapeMode),
"{{.EscapedVirtualPath}}", p.getStringReplacement(url.QueryEscape(p.VirtualPath), escapeMode),
"{{.FsPath}}", p.getStringReplacement(p.FsPath, escapeMode),
"{{.VirtualTargetPath}}", p.getStringReplacement(p.VirtualTargetPath, escapeMode),
"{{.FsTargetPath}}", p.getStringReplacement(p.FsTargetPath, escapeMode),
"{{.ObjectName}}", p.getStringReplacement(p.ObjectName, escapeMode),
"{{.ObjectBaseName}}", p.getStringReplacement(strings.TrimSuffix(p.ObjectName, p.Extension), escapeMode),
"{{.ObjectType}}", p.ObjectType,
"{{.FileSize}}", strconv.FormatInt(p.FileSize, 10),
"{{.Elapsed}}", strconv.FormatInt(p.Elapsed, 10),
"{{.Protocol}}", p.Protocol,
"{{.IP}}", p.IP,
"{{.Role}}", p.getStringReplacement(p.Role, escapeMode),
"{{.Email}}", p.getStringReplacement(p.Email, escapeMode),
"{{.Timestamp}}", strconv.FormatInt(p.Timestamp.UnixNano(), 10),
"{{.DateTime}}", dateTimeString,
"{{.Year}}", year,
"{{.Month}}", month,
"{{.Day}}", day,
"{{.Hour}}", hour,
"{{.Minute}}", minute,
"{{.StatusString}}", p.getStatusString(),
"{{.UID}}", p.getStringReplacement(p.UID, escapeMode),
"{{.Ext}}", p.getStringReplacement(p.Extension, escapeMode),
}
if p.VirtualPath != "" {
replacements = append(replacements, "{{VirtualDirPath}}", p.getStringReplacement(path.Dir(p.VirtualPath), jsonEscaped))
replacements = append(replacements, "{{.VirtualDirPath}}", p.getStringReplacement(path.Dir(p.VirtualPath), escapeMode))
}
if p.VirtualTargetPath != "" {
replacements = append(replacements, "{{VirtualTargetDirPath}}", p.getStringReplacement(path.Dir(p.VirtualTargetPath), jsonEscaped))
replacements = append(replacements, "{{TargetName}}", p.getStringReplacement(path.Base(p.VirtualTargetPath), jsonEscaped))
replacements = append(replacements, "{{.VirtualTargetDirPath}}", p.getStringReplacement(path.Dir(p.VirtualTargetPath), escapeMode))
replacements = append(replacements, "{{.TargetName}}", p.getStringReplacement(path.Base(p.VirtualTargetPath), escapeMode))
}
if len(p.errors) > 0 {
replacements = append(replacements, "{{ErrorString}}", p.getStringReplacement(strings.Join(p.errors, ", "), jsonEscaped))
replacements = append(replacements, "{{.ErrorString}}", p.getStringReplacement(strings.Join(p.errors, ", "), escapeMode))
} else {
replacements = append(replacements, "{{ErrorString}}", "")
replacements = append(replacements, "{{.ErrorString}}", "")
}
replacements = append(replacements, objDataPlaceholder, "{}")
replacements = append(replacements, objDataPlaceholderString, "")
@ -831,23 +847,23 @@ func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []s
data, err := p.Object.RenderAsJSON(p.Event != operationDelete)
if err == nil {
dataString := util.BytesToString(data)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, false)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, true)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, 0)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, 1)
}
}
if p.IDPCustomFields != nil {
for k, v := range *p.IDPCustomFields {
replacements = append(replacements, fmt.Sprintf("{{IDPField%s}}", k), p.getStringReplacement(v, jsonEscaped))
replacements = append(replacements, fmt.Sprintf("{{.IDPField%s}}", k), p.getStringReplacement(v, escapeMode))
}
}
replacements = append(replacements, "{{Metadata}}", "{}")
replacements = append(replacements, "{{MetadataString}}", "")
replacements = append(replacements, "{{.Metadata}}", "{}")
replacements = append(replacements, "{{.MetadataString}}", "")
if len(p.Metadata) > 0 {
data, err := json.Marshal(p.Metadata)
if err == nil {
dataString := util.BytesToString(data)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, false)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, true)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, 0)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, 1)
}
}
return replacements
@ -1182,7 +1198,7 @@ func getMailAttachments(conn *BaseConnection, attachments []string, replacer *st
}
func replaceWithReplacer(input string, replacer *strings.Replacer) string {
if !strings.Contains(input, "{{") {
if !strings.Contains(input, "{{.") {
return input
}
return replacer.Replace(input)
@ -1269,7 +1285,7 @@ func getHTTPRuleActionEndpoint(c *dataprovider.EventActionHTTPConfig, replacer *
if err != nil {
return "", fmt.Errorf("invalid endpoint: %w", err)
}
if strings.Contains(u.Path, "{{") {
if strings.Contains(u.Path, "{{.") {
pathComponents := strings.Split(u.Path, "/")
for idx := range pathComponents {
part := replaceWithReplacer(pathComponents[idx], replacer)
@ -1303,7 +1319,7 @@ func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.
if part.Body != "" {
cType := h.Get("Content-Type")
if strings.Contains(strings.ToLower(cType), "application/json") {
replacements := params.getStringReplacements(addObjectData, true)
replacements := params.getStringReplacements(addObjectData, 1)
jsonReplacer := strings.NewReplacer(replacements...)
_, err = partWriter.Write(util.StringToBytes(replaceWithReplacer(part.Body, jsonReplacer)))
} else {
@ -1351,7 +1367,7 @@ func getHTTPRuleActionBody(c *dataprovider.EventActionHTTPConfig, replacer *stri
return bytes.NewBuffer(data), "", nil
}
if c.HasJSONBody() {
replacements := params.getStringReplacements(addObjectData, true)
replacements := params.getStringReplacements(addObjectData, 1)
jsonReplacer := strings.NewReplacer(replacements...)
return bytes.NewBufferString(replaceWithReplacer(c.Body, jsonReplacer)), "", nil
}
@ -1439,7 +1455,7 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
addObjectData = c.HasObjectData()
}
replacements := params.getStringReplacements(addObjectData, false)
replacements := params.getStringReplacements(addObjectData, 0)
replacer := strings.NewReplacer(replacements...)
endpoint, err := getHTTPRuleActionEndpoint(&c, replacer)
if err != nil {
@ -1510,7 +1526,7 @@ func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, params *E
}
}
}
replacements := params.getStringReplacements(addObjectData, false)
replacements := params.getStringReplacements(addObjectData, 0)
replacer := strings.NewReplacer(replacements...)
args := make([]string, 0, len(c.Args))
@ -1524,7 +1540,7 @@ func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, params *E
cmd := exec.CommandContext(ctx, c.Cmd, args...)
cmd.Env = []string{}
for _, keyVal := range c.EnvVars {
if keyVal.Value == "$" {
if keyVal.Value == "$" && !strings.HasPrefix(strings.ToUpper(keyVal.Key), "SFTPGO_") {
val := os.Getenv(keyVal.Key)
if val == "" {
eventManagerLog(logger.LevelDebug, "empty value for environment variable %q", keyVal.Key)
@ -1565,9 +1581,16 @@ func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, params *Event
addObjectData = true
}
}
replacements := params.getStringReplacements(addObjectData, false)
replacements := params.getStringReplacements(addObjectData, 0)
replacer := strings.NewReplacer(replacements...)
body := replaceWithReplacer(c.Body, replacer)
var body string
if c.ContentType == 1 {
replacements := params.getStringReplacements(addObjectData, 2)
bodyReplacer := strings.NewReplacer(replacements...)
body = replaceWithReplacer(c.Body, bodyReplacer)
} else {
body = replaceWithReplacer(c.Body, replacer)
}
subject := replaceWithReplacer(c.Subject, replacer)
recipients := getEmailAddressesWithReplacer(c.Recipients, replacer)
bcc := getEmailAddressesWithReplacer(c.Bcc, replacer)
@ -2139,7 +2162,7 @@ func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, conditions
params *EventParams,
) error {
addObjectData := false
replacements := params.getStringReplacements(addObjectData, false)
replacements := params.getStringReplacements(addObjectData, 0)
replacer := strings.NewReplacer(replacements...)
switch c.Type {
case dataprovider.FilesystemActionRename:
@ -2539,7 +2562,7 @@ func executeAdminCheckAction(c *dataprovider.EventActionIDPAccountCheck, params
return nil, err
}
replacements := params.getStringReplacements(false, true)
replacements := params.getStringReplacements(false, 1)
replacer := strings.NewReplacer(replacements...)
data := replaceWithReplacer(c.TemplateAdmin, replacer)
@ -2609,7 +2632,7 @@ func executeUserCheckAction(c *dataprovider.EventActionIDPAccountCheck, params *
if err != nil && !errors.Is(err, util.ErrNotFound) {
return nil, err
}
replacements := params.getStringReplacements(false, true)
replacements := params.getStringReplacements(false, 1)
replacer := strings.NewReplacer(replacements...)
data := replaceWithReplacer(c.TemplateUser, replacer)
@ -2831,6 +2854,16 @@ func (j *eventCronJob) getTask(rule *dataprovider.EventRule) (dataprovider.Task,
return dataprovider.Task{}, nil
}
func (j *eventCronJob) getEventParams() EventParams {
return EventParams{
Event: "Schedule",
Name: j.ruleName,
Status: 1,
Timestamp: time.Now(),
updateStatusFromError: true,
}
}
func (j *eventCronJob) Run() {
eventManagerLog(logger.LevelDebug, "executing scheduled rule %q", j.ruleName)
rule, err := dataprovider.EventRuleExists(j.ruleName)
@ -2881,9 +2914,9 @@ func (j *eventCronJob) Run() {
}
}(task.Name)
executeAsyncRulesActions([]dataprovider.EventRule{rule}, EventParams{Status: 1, updateStatusFromError: true})
executeAsyncRulesActions([]dataprovider.EventRule{rule}, j.getEventParams())
} else {
executeAsyncRulesActions([]dataprovider.EventRule{rule}, EventParams{Status: 1, updateStatusFromError: true})
executeAsyncRulesActions([]dataprovider.EventRule{rule}, j.getEventParams())
}
eventManagerLog(logger.LevelDebug, "execution for scheduled rule %q finished", j.ruleName)
}

View file

@ -808,16 +808,20 @@ func TestDateTimePlaceholder(t *testing.T) {
params := EventParams{
Timestamp: dateTime,
}
replacements := params.getStringReplacements(false, false)
replacements := params.getStringReplacements(false, 0)
r := strings.NewReplacer(replacements...)
res := r.Replace("{{DateTime}}")
res := r.Replace("{{.DateTime}}")
assert.Equal(t, dateTime.UTC().Format(dateTimeMillisFormat), res)
res = r.Replace("{{.Year}}-{{.Month}}-{{.Day}}T{{.Hour}}:{{.Minute}}")
assert.Equal(t, dateTime.UTC().Format(dateTimeMillisFormat)[:16], res)
Config.TZ = "local"
replacements = params.getStringReplacements(false, false)
replacements = params.getStringReplacements(false, 0)
r = strings.NewReplacer(replacements...)
res = r.Replace("{{DateTime}}")
res = r.Replace("{{.DateTime}}")
assert.Equal(t, dateTime.Local().Format(dateTimeMillisFormat), res)
res = r.Replace("{{.Year}}-{{.Month}}-{{.Day}}T{{.Hour}}:{{.Minute}}")
assert.Equal(t, dateTime.Local().Format(dateTimeMillisFormat)[:16], res)
Config.TZ = oldTZ
}
@ -841,7 +845,7 @@ func TestEventRuleActions(t *testing.T) {
HTTPConfig: dataprovider.EventActionHTTPConfig{
Endpoint: "http://foo\x7f.com/", // invalid URL
SkipTLSVerify: true,
Body: `"data": "{{ObjectDataString}}"`,
Body: `"data": "{{.ObjectDataString}}"`,
Method: http.MethodPost,
QueryParameters: []dataprovider.KeyValue{
{
@ -909,7 +913,7 @@ func TestEventRuleActions(t *testing.T) {
assert.Contains(t, getErrorString(err), "error getting user")
action.Options.HTTPConfig.Parts = nil
action.Options.HTTPConfig.Body = "{{ObjectData}}"
action.Options.HTTPConfig.Body = "{{.ObjectData}}"
// test disk and transfer quota reset
username1 := "user1"
username2 := "user2"
@ -1245,7 +1249,7 @@ func TestEventRuleActions(t *testing.T) {
Type: dataprovider.FilesystemActionCompress,
Compress: dataprovider.EventActionFsCompress{
Name: "test.zip",
Paths: []string{"/{{VirtualPath}}"},
Paths: []string{"/{{.VirtualPath}}"},
},
},
}
@ -1950,12 +1954,27 @@ func TestScheduledActions(t *testing.T) {
backupsPath := filepath.Join(os.TempDir(), "backups")
err := os.RemoveAll(backupsPath)
assert.NoError(t, err)
now := time.Now().UTC().Format(dateTimeMillisFormat)
// The backup action sets the home directory to the backup path.
expectedDirPath := filepath.Join(backupsPath, fmt.Sprintf("%s_%s_%s", now[0:4], now[5:7], now[8:10]))
action := &dataprovider.BaseEventAction{
Name: "action",
action1 := &dataprovider.BaseEventAction{
Name: "action1",
Type: dataprovider.ActionTypeBackup,
}
err = dataprovider.AddEventAction(action, "", "", "")
err = dataprovider.AddEventAction(action1, "", "", "")
assert.NoError(t, err)
action2 := &dataprovider.BaseEventAction{
Name: "action2",
Type: dataprovider.ActionTypeFilesystem,
Options: dataprovider.BaseEventActionOptions{
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionMkdirs,
MkDirs: []string{"{{.Year}}_{{.Month}}_{{.Day}}"},
},
},
}
err = dataprovider.AddEventAction(action2, "", "", "")
assert.NoError(t, err)
rule := &dataprovider.EventRule{
Name: "rule",
@ -1974,10 +1993,16 @@ func TestScheduledActions(t *testing.T) {
Actions: []dataprovider.EventAction{
{
BaseEventAction: dataprovider.BaseEventAction{
Name: action.Name,
Name: action1.Name,
},
Order: 1,
},
{
BaseEventAction: dataprovider.BaseEventAction{
Name: action2.Name,
},
Order: 2,
},
},
}
@ -1992,9 +2017,10 @@ func TestScheduledActions(t *testing.T) {
job.Run()
assert.DirExists(t, backupsPath)
assert.DirExists(t, expectedDirPath)
action.Type = dataprovider.ActionTypeEmail
action.Options = dataprovider.BaseEventActionOptions{
action1.Type = dataprovider.ActionTypeEmail
action1.Options = dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"example@example.com"},
Subject: "test with attachments",
@ -2002,16 +2028,19 @@ func TestScheduledActions(t *testing.T) {
Attachments: []string{"/file1.txt"},
},
}
err = dataprovider.UpdateEventAction(action, "", "", "")
err = dataprovider.UpdateEventAction(action1, "", "", "")
assert.NoError(t, err)
job.Run() // action is not compatible with a scheduled rule
err = dataprovider.DeleteEventRule(rule.Name, "", "", "")
assert.NoError(t, err)
err = dataprovider.DeleteEventAction(action.Name, "", "", "")
err = dataprovider.DeleteEventAction(action1.Name, "", "", "")
assert.NoError(t, err)
err = dataprovider.DeleteEventAction(action2.Name, "", "", "")
assert.NoError(t, err)
err = os.RemoveAll(backupsPath)
assert.NoError(t, err)
stopEventScheduler()
}
@ -2130,11 +2159,11 @@ func TestWriteHTTPPartsError(t *testing.T) {
}
func TestReplacePathsPlaceholders(t *testing.T) {
replacer := strings.NewReplacer("{{VirtualPath}}", "/path1")
paths := []string{"{{VirtualPath}}", "/path1"}
replacer := strings.NewReplacer("{{.VirtualPath}}", "/path1")
paths := []string{"{{.VirtualPath}}", "/path1"}
paths = replacePathsPlaceholders(paths, replacer)
assert.Equal(t, []string{"/path1"}, paths)
paths = []string{"{{VirtualPath}}", "/path2"}
paths = []string{"{{.VirtualPath}}", "/path2"}
paths = replacePathsPlaceholders(paths, replacer)
assert.Equal(t, []string{"/path1", "/path2"}, paths)
}
@ -2227,7 +2256,7 @@ func TestOnDemandRule(t *testing.T) {
Recipients: []string{"example@example.org"},
Subject: "subject",
Body: "body",
Attachments: []string{"/{{VirtualPath}}"},
Attachments: []string{"/{{.VirtualPath}}"},
},
},
}
@ -2268,21 +2297,21 @@ func getErrorString(err error) string {
func TestHTTPEndpointWithPlaceholders(t *testing.T) {
c := dataprovider.EventActionHTTPConfig{
Endpoint: "http://127.0.0.1:8080/base/url/{{Name}}/{{VirtualPath}}/upload",
Endpoint: "http://127.0.0.1:8080/base/url/{{.Name}}/{{.VirtualPath}}/upload",
QueryParameters: []dataprovider.KeyValue{
{
Key: "u",
Value: "{{Name}}",
Value: "{{.Name}}",
},
{
Key: "p",
Value: "{{VirtualPath}}",
Value: "{{.VirtualPath}}",
},
},
}
name := "uname"
vPath := "/a dir/@ file.txt"
replacer := strings.NewReplacer("{{Name}}", name, "{{VirtualPath}}", vPath)
replacer := strings.NewReplacer("{{.Name}}", name, "{{.VirtualPath}}", vPath)
u, err := getHTTPRuleActionEndpoint(&c, replacer)
assert.NoError(t, err)
expected := "http://127.0.0.1:8080/base/url/" + url.PathEscape(name) + "/" + url.PathEscape(vPath) +
@ -2302,9 +2331,9 @@ func TestMetadataReplacement(t *testing.T) {
"key": "value",
},
}
replacements := params.getStringReplacements(false, false)
replacements := params.getStringReplacements(false, 0)
replacer := strings.NewReplacer(replacements...)
reader, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{Body: "{{Metadata}} {{MetadataString}}"}, replacer, nil, dataprovider.User{}, params, false)
reader, _, err := getHTTPRuleActionBody(&dataprovider.EventActionHTTPConfig{Body: "{{.Metadata}} {{.MetadataString}}"}, replacer, nil, dataprovider.User{}, params, false)
require.NoError(t, err)
data, err := io.ReadAll(reader)
require.NoError(t, err)

View file

@ -3784,8 +3784,8 @@ func TestEventRule(t *testing.T) {
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test1@example.com", "test2@example.com"},
Bcc: []string{"test3@example.com"},
Subject: `New "{{Event}}" from "{{Name}}" status {{StatusString}}`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}} Data: {{ObjectData}} {{ErrorString}}",
Subject: `New "{{.Event}}" from "{{.Name}}" status {{.StatusString}}`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}} Data: {{.ObjectData}} {{.ErrorString}}",
},
},
}
@ -3795,8 +3795,8 @@ func TestEventRule(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `Failed "{{Event}}" from "{{Name}}"`,
Body: "Fs path {{FsPath}}, protocol: {{Protocol}}, IP: {{IP}} {{ErrorString}}",
Subject: `Failed "{{.Event}}" from "{{.Name}}"`,
Body: "Fs path {{.FsPath}}, protocol: {{.Protocol}}, IP: {{.IP}} {{.ErrorString}}",
},
},
}
@ -3941,7 +3941,7 @@ func TestEventRule(t *testing.T) {
EnvVars: []dataprovider.KeyValue{
{
Key: "SFTPGO_ACTION_PATH",
Value: "{{FsPath}}",
Value: "{{.FsPath}}",
},
{
Key: "CUSTOM_ENV_VAR",
@ -4050,7 +4050,7 @@ func TestEventRule(t *testing.T) {
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: New "download" from "%s"`, user.Username))
}
// test upload action command with arguments
action1.Options.CmdConfig.Args = []string{"{{Event}}", "{{VirtualPath}}", "custom_arg"}
action1.Options.CmdConfig.Args = []string{"{{.Event}}", "{{.VirtualPath}}", "custom_arg"}
action1, _, err = httpdtest.UpdateEventAction(action1, http.StatusOK)
assert.NoError(t, err)
uploadLogFilePath := filepath.Join(os.TempDir(), "upload.log")
@ -4128,8 +4128,8 @@ func TestEventRuleStatues(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test6@example.com"},
Subject: `New "{{Event}}" error`,
Body: "{{ErrorString}}",
Subject: `New "{{.Event}}" error`,
Body: "{{.ErrorString}}",
},
},
}
@ -4241,7 +4241,7 @@ func TestEventRuleDisabledCommand(t *testing.T) {
EnvVars: []dataprovider.KeyValue{
{
Key: "SFTPGO_OBJECT_DATA",
Value: "{{ObjectData}}",
Value: "{{.ObjectData}}",
},
},
},
@ -4253,8 +4253,8 @@ func TestEventRuleDisabledCommand(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test3@example.com"},
Subject: `New "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}} Data: {{ObjectData}}",
Subject: `New "{{.Event}}" from "{{.Name}}"`,
Body: "Object name: {{.ObjectName}} object type: {{.ObjectType}} Data: {{.ObjectData}}",
},
},
}
@ -4265,8 +4265,8 @@ func TestEventRuleDisabledCommand(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `Failed "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}",
Subject: `Failed "{{.Event}}" from "{{.Name}}"`,
Body: "Object name: {{.ObjectName}} object type: {{.ObjectType}}, IP: {{.IP}}",
},
},
}
@ -4390,7 +4390,7 @@ func TestEventRuleProviderEvents(t *testing.T) {
EnvVars: []dataprovider.KeyValue{
{
Key: "SFTPGO_OBJECT_DATA",
Value: "{{ObjectData}}",
Value: "{{.ObjectData}}",
},
},
},
@ -4402,8 +4402,8 @@ func TestEventRuleProviderEvents(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test3@example.com"},
Subject: `New "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}} Data: {{ObjectData}}",
Subject: `New "{{.Event}}" from "{{.Name}}"`,
Body: "Object name: {{.ObjectName}} object type: {{.ObjectType}} Data: {{.ObjectData}}",
},
},
}
@ -4414,8 +4414,8 @@ func TestEventRuleProviderEvents(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `Failed "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}",
Subject: `Failed "{{.Event}}" from "{{.Name}}"`,
Body: "Object name: {{.ObjectName}} object type: {{.ObjectType}}, IP: {{.IP}}",
},
},
}
@ -4561,8 +4561,8 @@ func TestEventRuleFsActions(t *testing.T) {
Renames: []dataprovider.RenameConfig{
{
KeyValue: dataprovider.KeyValue{
Key: "/{{VirtualDirPath}}/{{ObjectName}}",
Value: "/{{ObjectName}}_renamed",
Key: "/{{.VirtualDirPath}}/{{.ObjectName}}",
Value: "/{{.ObjectName}}_renamed",
},
},
},
@ -4575,7 +4575,7 @@ func TestEventRuleFsActions(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionDelete,
Deletes: []string{"/{{ObjectName}}_renamed"},
Deletes: []string{"/{{.ObjectName}}_renamed"},
},
},
}
@ -4593,7 +4593,7 @@ func TestEventRuleFsActions(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionExist,
Exist: []string{"/{{VirtualPath}}"},
Exist: []string{"/{{.VirtualPath}}"},
},
},
}
@ -4831,8 +4831,8 @@ func TestEventActionObjectBaseName(t *testing.T) {
Renames: []dataprovider.RenameConfig{
{
KeyValue: dataprovider.KeyValue{
Key: "/{{VirtualDirPath}}/{{ObjectName}}",
Value: "/{{ObjectBaseName}}",
Key: "/{{.VirtualDirPath}}/{{.ObjectName}}",
Value: "/{{.ObjectBaseName}}",
},
},
},
@ -4911,8 +4911,8 @@ func TestUploadEventRule(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test1@example.com"},
Subject: `New "{{Event}}" from "{{Name}}" status {{StatusString}}`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}} Data: {{ObjectData}} {{ErrorString}}",
Subject: `New "{{.Event}}" from "{{.Name}}" status {{.StatusString}}`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}} Data: {{.ObjectData}} {{.ErrorString}}",
},
},
}
@ -5047,8 +5047,8 @@ func TestEventRulePreDelete(t *testing.T) {
Renames: []dataprovider.RenameConfig{
{
KeyValue: dataprovider.KeyValue{
Key: "/{{VirtualPath}}",
Value: fmt.Sprintf("/%s/{{VirtualPath}}", movePath),
Key: "/{{.VirtualPath}}",
Value: fmt.Sprintf("/%s/{{.VirtualPath}}", movePath),
},
UpdateModTime: true,
},
@ -5410,7 +5410,7 @@ func TestFsActionCopy(t *testing.T) {
Type: dataprovider.FilesystemActionCopy,
Copy: []dataprovider.KeyValue{
{
Key: "/{{VirtualPath}}/",
Key: "/{{.VirtualPath}}/",
Value: "/dircopy/",
},
},
@ -5492,8 +5492,8 @@ func TestEventFsActionsGroupFilters(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"example@example.net"},
Subject: `New "{{Event}}" from "{{Name}}" status {{StatusString}}`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}} {{ErrorString}}",
Subject: `New "{{.Event}}" from "{{.Name}}" status {{.StatusString}}`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}} {{.ErrorString}}",
},
},
}
@ -5623,8 +5623,8 @@ func TestEventProviderActionGroupFilters(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"example@example.net"},
Subject: `New "{{Event}}" from "{{Name}}"`,
Body: "IP: {{IP}}",
Subject: `New "{{.Event}}" from "{{.Name}}"`,
Body: "IP: {{.IP}}",
},
},
}
@ -5759,9 +5759,9 @@ func TestBackupAsAttachment(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}} {{StatusString}}"`,
Body: "Domain: {{Name}}",
Attachments: []string{"/{{VirtualPath}}"},
Subject: `"{{.Event}} {{.StatusString}}"`,
Body: "Domain: {{.Name}}",
Attachments: []string{"/{{.VirtualPath}}"},
},
},
}
@ -5840,11 +5840,11 @@ func TestEventActionHTTPMultipart(t *testing.T) {
Value: "application/json",
},
},
Body: `{"FilePath": "{{VirtualPath}}"}`,
Body: `{"FilePath": "{{.VirtualPath}}"}`,
},
{
Name: "file",
Filepath: "/{{VirtualPath}}",
Filepath: "/{{.VirtualPath}}",
},
},
},
@ -5920,8 +5920,8 @@ func TestEventActionCompress(t *testing.T) {
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionCompress,
Compress: dataprovider.EventActionFsCompress{
Name: "/{{VirtualPath}}.zip",
Paths: []string{"/{{VirtualPath}}"},
Name: "/{{.VirtualPath}}.zip",
Paths: []string{"/{{.VirtualPath}}"},
},
},
},
@ -6091,7 +6091,7 @@ func TestEventActionCompressQuotaErrors(t *testing.T) {
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"Compress failed"`,
Body: "Error: {{ErrorString}}",
Body: "Error: {{.ErrorString}}",
},
},
}
@ -6235,8 +6235,8 @@ func TestEventActionCompressQuotaFolder(t *testing.T) {
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionCompress,
Compress: dataprovider.EventActionFsCompress{
Name: "/{{VirtualPath}}.zip",
Paths: []string{"/{{VirtualPath}}", testDir},
Name: "/{{.VirtualPath}}.zip",
Paths: []string{"/{{.VirtualPath}}", testDir},
},
},
},
@ -6361,8 +6361,8 @@ func TestEventActionCompressErrors(t *testing.T) {
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionCompress,
Compress: dataprovider.EventActionFsCompress{
Name: "/{{VirtualPath}}.zip",
Paths: []string{"/{{VirtualPath}}.zip"}, // cannot compress itself
Name: "/{{.VirtualPath}}.zip",
Paths: []string{"/{{.VirtualPath}}.zip"}, // cannot compress itself
},
},
},
@ -6424,7 +6424,7 @@ func TestEventActionCompressErrors(t *testing.T) {
// try to overwrite a directory
testDir := "/adir"
action1.Options.FsConfig.Compress.Name = testDir
action1.Options.FsConfig.Compress.Paths = []string{"/{{VirtualPath}}"}
action1.Options.FsConfig.Compress.Paths = []string{"/{{.VirtualPath}}"}
_, _, err = httpdtest.UpdateEventAction(action1, http.StatusOK)
assert.NoError(t, err)
conn, client, err = getSftpClient(user)
@ -6469,8 +6469,8 @@ func TestEventActionEmailAttachments(t *testing.T) {
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionCompress,
Compress: dataprovider.EventActionFsCompress{
Name: "/archive/{{VirtualPath}}.zip",
Paths: []string{"/{{VirtualPath}}"},
Name: "/archive/{{.VirtualPath}}.zip",
Paths: []string{"/{{.VirtualPath}}"},
},
},
},
@ -6483,9 +6483,9 @@ func TestEventActionEmailAttachments(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}}" from "{{Name}}"`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}} {{EscapedVirtualPath}}",
Attachments: []string{"/archive/{{VirtualPath}}.zip"},
Subject: `"{{.Event}}" from "{{.Name}}"`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}} {{.EscapedVirtualPath}}",
Attachments: []string{"/archive/{{.VirtualPath}}.zip"},
},
},
}
@ -6604,8 +6604,8 @@ func TestEventActionsRetentionReports(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}}" from "{{Name}}"`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}}",
Subject: `"{{.Event}}" from "{{.Name}}"`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}}",
Attachments: []string{dataprovider.RetentionReportPlaceHolder},
},
},
@ -6832,8 +6832,8 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}}" from "{{Name}}"`,
Body: "Fs path {{FsPath}}, size: {{FileSize}}, protocol: {{Protocol}}, IP: {{IP}}",
Subject: `"{{.Event}}" from "{{.Name}}"`,
Body: "Fs path {{.FsPath}}, size: {{.FileSize}}, protocol: {{.Protocol}}, IP: {{.IP}}",
},
},
}
@ -6964,9 +6964,9 @@ func TestEventRuleRenameEvent(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}}" from "{{Name}}"`,
Subject: `"{{.Event}}" from "{{.Name}}"`,
ContentType: 1,
Body: `<p>Fs path {{FsPath}}, Target path "{{VirtualTargetDirPath}}/{{TargetName}}", size: {{FileSize}}</p>`,
Body: `<p>Fs path {{.FsPath}}, Name: {{.Name}}, Target path "{{.VirtualTargetDirPath}}/{{.TargetName}}", size: {{.FileSize}}</p>`,
},
},
}
@ -6991,7 +6991,9 @@ func TestEventRuleRenameEvent(t *testing.T) {
rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated)
assert.NoError(t, err)
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
u := getTestUser()
u.Username = "test <html > chars"
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
conn, client, err := getSftpClient(user)
if assert.NoError(t, err) {
@ -7015,6 +7017,7 @@ func TestEventRuleRenameEvent(t *testing.T) {
assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "rename" from "%s"`, user.Username))
assert.Contains(t, email.Data, "Content-Type: text/html")
assert.Contains(t, email.Data, fmt.Sprintf("Target path %q", path.Join("/subdir", testFileName)))
assert.Contains(t, email.Data, "Name: test &lt;html &gt; chars,")
}
_, err = httpdtest.RemoveEventRule(rule1, http.StatusOK)
@ -7045,9 +7048,9 @@ func TestEventRuleIDPLogin(t *testing.T) {
username := `test_"idp_"login`
custom1 := `cust"oa"1`
u := map[string]any{
"username": "{{Name}}",
"username": "{{.Name}}",
"status": 1,
"home_dir": filepath.Join(os.TempDir(), "{{IDPFieldcustom1}}"),
"home_dir": filepath.Join(os.TempDir(), "{{.IDPFieldcustom1}}"),
"permissions": map[string][]string{
"/": {dataprovider.PermAny},
},
@ -7055,7 +7058,7 @@ func TestEventRuleIDPLogin(t *testing.T) {
userTmpl, err := json.Marshal(u)
require.NoError(t, err)
a := map[string]any{
"username": "{{Name}}",
"username": "{{.Name}}",
"status": 1,
"permissions": []string{dataprovider.PermAdminAny},
}
@ -7081,8 +7084,8 @@ func TestEventRuleIDPLogin(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}} {{StatusString}}"`,
Body: "{{Name}} Custom field: {{IDPFieldcustom1}}",
Subject: `"{{.Event}} {{.StatusString}}"`,
Body: "{{.Name}} Custom field: {{.IDPFieldcustom1}}",
},
},
}
@ -7327,8 +7330,8 @@ func TestEventRuleEmailField(t *testing.T) {
Type: dataprovider.ActionTypeEmail,
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"{{Email}}"},
Subject: `"{{Event}}" from "{{Name}}"`,
Recipients: []string{"{{.Email}}"},
Subject: `"{{.Event}}" from "{{.Name}}"`,
Body: "Sample email body",
},
},
@ -7342,7 +7345,7 @@ func TestEventRuleEmailField(t *testing.T) {
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `"Failure`,
Body: "{{ErrorString}}",
Body: "{{.ErrorString}}",
},
},
}
@ -7473,9 +7476,9 @@ func TestEventRuleCertificate(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test@example.com"},
Subject: `"{{Event}} {{StatusString}}"`,
Subject: `"{{.Event}} {{.StatusString}}"`,
ContentType: 0,
Body: "Domain: {{Name}} Timestamp: {{Timestamp}} {{ErrorString}} Date time: {{DateTime}}",
Body: "Domain: {{.Name}} Timestamp: {{.Timestamp}} {{.ErrorString}} Date time: {{.DateTime}}",
},
},
}
@ -7613,8 +7616,8 @@ func TestEventRuleIPBlocked(t *testing.T) {
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"test3@example.com", "test4@example.com"},
Subject: `New "{{Event}}"`,
Body: "IP: {{IP}} Timestamp: {{Timestamp}}",
Subject: `New "{{.Event}}"`,
Body: "IP: {{.IP}} Timestamp: {{.Timestamp}}",
},
},
}

View file

@ -35,7 +35,7 @@ var (
)
// BaseTransfer contains protocols common transfer details for an upload or a download.
type BaseTransfer struct { //nolint:maligned
type BaseTransfer struct {
ID int64
BytesSent atomic.Int64
BytesReceived atomic.Int64

View file

@ -24,6 +24,7 @@ import (
"strconv"
"strings"
kmsplugin "github.com/sftpgo/sdk/plugin/kms"
"github.com/spf13/viper"
"github.com/subosito/gotenv"
@ -99,26 +100,27 @@ var (
DisableWWWAuthHeader: false,
}
defaultHTTPDBinding = httpd.Binding{
Address: "",
Port: 8080,
EnableWebAdmin: true,
EnableWebClient: true,
EnableRESTAPI: true,
EnabledLoginMethods: 0,
EnableHTTPS: false,
CertificateFile: "",
CertificateKeyFile: "",
MinTLSVersion: 12,
ClientAuthType: 0,
TLSCipherSuites: nil,
Protocols: nil,
ProxyMode: 0,
ProxyAllowed: nil,
ClientIPProxyHeader: "",
ClientIPHeaderDepth: 0,
HideLoginURL: 0,
RenderOpenAPI: true,
Languages: []string{"en"},
Address: "",
Port: 8080,
EnableWebAdmin: true,
EnableWebClient: true,
EnableRESTAPI: true,
EnabledLoginMethods: 0,
DisabledLoginMethods: 0,
EnableHTTPS: false,
CertificateFile: "",
CertificateKeyFile: "",
MinTLSVersion: 12,
ClientAuthType: 0,
TLSCipherSuites: nil,
Protocols: nil,
ProxyMode: 0,
ProxyAllowed: nil,
ClientIPProxyHeader: "",
ClientIPHeaderDepth: 0,
HideLoginURL: 0,
RenderOpenAPI: true,
Languages: []string{"en"},
OIDC: httpd.OIDC{
ClientID: "",
ClientSecret: "",
@ -134,21 +136,23 @@ var (
Debug: false,
},
Security: httpd.SecurityConf{
Enabled: false,
AllowedHosts: nil,
AllowedHostsAreRegex: false,
HostsProxyHeaders: nil,
HTTPSRedirect: false,
HTTPSHost: "",
HTTPSProxyHeaders: nil,
STSSeconds: 0,
STSIncludeSubdomains: false,
STSPreload: false,
ContentTypeNosniff: false,
ContentSecurityPolicy: "",
PermissionsPolicy: "",
CrossOriginOpenerPolicy: "",
CacheControl: "",
Enabled: false,
AllowedHosts: nil,
AllowedHostsAreRegex: false,
HostsProxyHeaders: nil,
HTTPSRedirect: false,
HTTPSHost: "",
HTTPSProxyHeaders: nil,
STSSeconds: 0,
STSIncludeSubdomains: false,
STSPreload: false,
ContentTypeNosniff: false,
ContentSecurityPolicy: "",
PermissionsPolicy: "",
CrossOriginOpenerPolicy: "",
CrossOriginResourcePolicy: "",
CrossOriginEmbedderPolicy: "",
CacheControl: "",
},
Branding: httpd.Branding{},
}
@ -585,6 +589,16 @@ func SetPluginsConfig(config []plugin.Config) {
globalConf.PluginsConfig = config
}
// HasKMSPlugin returns true if at least one KMS plugin is configured.
func HasKMSPlugin() bool {
for _, c := range globalConf.PluginsConfig {
if c.Type == kmsplugin.PluginName {
return true
}
}
return false
}
// GetMFAConfig returns multi-factor authentication config
func GetMFAConfig() mfa.Config {
return globalConf.MFAConfig
@ -1565,9 +1579,27 @@ func getHTTPDSecurityConfFromEnv(idx int) (httpd.SecurityConf, bool) { //nolint:
isSet = true
}
crossOriginOpenedPolicy, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__CROSS_ORIGIN_OPENER_POLICY", idx))
crossOriginOpenerPolicy, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__CROSS_ORIGIN_OPENER_POLICY", idx))
if ok {
result.CrossOriginOpenerPolicy = crossOriginOpenedPolicy
result.CrossOriginOpenerPolicy = crossOriginOpenerPolicy
isSet = true
}
crossOriginResourcePolicy, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__CROSS_ORIGIN_RESOURCE_POLICY", idx))
if ok {
result.CrossOriginResourcePolicy = crossOriginResourcePolicy
isSet = true
}
crossOriginEmbedderPolicy, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__CROSS_ORIGIN_EMBEDDER_POLICY", idx))
if ok {
result.CrossOriginEmbedderPolicy = crossOriginEmbedderPolicy
isSet = true
}
referredPolicy, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__REFERRER_POLICY", idx))
if ok {
result.ReferrerPolicy = referredPolicy
isSet = true
}
@ -1854,6 +1886,12 @@ func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo
isSet = true
}
disabledLoginMethods, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__DISABLED_LOGIN_METHODS", idx), 32)
if ok {
binding.DisabledLoginMethods = int(disabledLoginMethods)
isSet = true
}
renderOpenAPI, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__RENDER_OPENAPI", idx))
if ok {
binding.RenderOpenAPI = renderOpenAPI

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build darwin
// +build darwin
package config

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !linux && !darwin
// +build !linux,!darwin
package config

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build linux
// +build linux
package config

View file

@ -343,6 +343,18 @@ func TestSetGetConfig(t *testing.T) {
if assert.Len(t, config.GetPluginsConfig(), 1) {
assert.Equal(t, pluginConf[0].Type, config.GetPluginsConfig()[0].Type)
}
assert.False(t, config.HasKMSPlugin())
pluginConf = []plugin.Config{
{
Type: "notifier",
},
{
Type: "kms",
},
}
config.SetPluginsConfig(pluginConf)
assert.Len(t, config.GetPluginsConfig(), 2)
assert.True(t, config.HasKMSPlugin())
}
func TestServiceToStart(t *testing.T) {
@ -528,7 +540,7 @@ func TestOverrideSliceValues(t *testing.T) {
c = make(map[string]any)
c["httpd"] = httpd.Conf{
Bindings: []httpd.Binding{},
Bindings: nil,
}
jsonConf, err = json.Marshal(c)
assert.NoError(t, err)
@ -1192,6 +1204,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT", "0")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_REST_API", "0")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS", "3")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__DISABLED_LOGIN_METHODS", "12")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI", "0")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__LANGUAGES", "en,es")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS", "1 ")
@ -1230,7 +1243,10 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CONTENT_SECURITY_POLICY", "script-src $NONCE")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__PERMISSIONS_POLICY", "fullscreen=(), geolocation=()")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_OPENER_POLICY", "same-origin")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_RESOURCE_POLICY", "same-site")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_EMBEDDER_POLICY", "require-corp")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CACHE_CONTROL", "private")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__REFERRER_POLICY", "no-referrer")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__EXTRA_CSS__0__PATH", "path1")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__EXTRA_CSS__1__PATH", "path2")
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__BRANDING__WEB_ADMIN__FAVICON_PATH", "favicon.ico")
@ -1261,6 +1277,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_REST_API")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__DISABLED_LOGIN_METHODS")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__LANGUAGES")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE")
@ -1297,7 +1314,10 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CONTENT_SECURITY_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__PERMISSIONS_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_OPENER_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_RESOURCE_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CROSS_ORIGIN_EMBEDDER_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__CACHE_CONTROL")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__REFERRER_POLICY")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__EXTRA_CSS__0__PATH")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__EXTRA_CSS__1__PATH")
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__BRANDING__WEB_ADMIN__FAVICON_PATH")
@ -1323,6 +1343,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.True(t, bindings[0].EnableWebClient)
require.True(t, bindings[0].EnableRESTAPI)
require.Equal(t, 0, bindings[0].EnabledLoginMethods)
require.Equal(t, 0, bindings[0].DisabledLoginMethods)
require.True(t, bindings[0].RenderOpenAPI)
require.Len(t, bindings[0].Languages, 1)
assert.Contains(t, bindings[0].Languages, "en")
@ -1336,6 +1357,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.Len(t, bindings[0].OIDC.Scopes, 3)
require.False(t, bindings[0].OIDC.InsecureSkipSignatureCheck)
require.False(t, bindings[0].OIDC.Debug)
require.Empty(t, bindings[0].Security.ReferrerPolicy)
require.Equal(t, 8000, bindings[1].Port)
require.Equal(t, "127.0.0.1", bindings[1].Address)
require.False(t, bindings[1].EnableHTTPS)
@ -1344,6 +1366,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.True(t, bindings[1].EnableWebClient)
require.True(t, bindings[1].EnableRESTAPI)
require.Equal(t, 0, bindings[1].EnabledLoginMethods)
require.Equal(t, 0, bindings[1].DisabledLoginMethods)
require.True(t, bindings[1].RenderOpenAPI)
require.Len(t, bindings[1].Languages, 1)
assert.Contains(t, bindings[1].Languages, "en")
@ -1366,6 +1389,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.False(t, bindings[2].EnableWebClient)
require.False(t, bindings[2].EnableRESTAPI)
require.Equal(t, 3, bindings[2].EnabledLoginMethods)
require.Equal(t, 12, bindings[2].DisabledLoginMethods)
require.False(t, bindings[2].RenderOpenAPI)
require.Len(t, bindings[2].Languages, 2)
assert.Contains(t, bindings[2].Languages, "en")
@ -1417,7 +1441,10 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
require.Equal(t, "script-src $NONCE", bindings[2].Security.ContentSecurityPolicy)
require.Equal(t, "fullscreen=(), geolocation=()", bindings[2].Security.PermissionsPolicy)
require.Equal(t, "same-origin", bindings[2].Security.CrossOriginOpenerPolicy)
require.Equal(t, "same-site", bindings[2].Security.CrossOriginResourcePolicy)
require.Equal(t, "require-corp", bindings[2].Security.CrossOriginEmbedderPolicy)
require.Equal(t, "private", bindings[2].Security.CacheControl)
require.Equal(t, "no-referrer", bindings[2].Security.ReferrerPolicy)
require.Equal(t, "favicon.ico", bindings[2].Branding.WebAdmin.FaviconPath)
require.Equal(t, "logo.png", bindings[2].Branding.WebClient.LogoPath)
require.Equal(t, "disclaimer", bindings[2].Branding.WebClient.DisclaimerName)

View file

@ -366,6 +366,7 @@ func (a *Admin) validateGroups() error {
func (a *Admin) validate() error {
a.SetEmptySecretsIfNil()
a.Password = strings.TrimSpace(a.Password)
if a.Username == "" {
return util.NewI18nError(util.NewValidationError("username is mandatory"), util.I18nErrorUsernameRequired)
}
@ -481,7 +482,7 @@ func (a *Admin) checkUserAndPass(password, ip string) error {
if err := a.CanLogin(ip); err != nil {
return err
}
if a.Password == "" || password == "" {
if a.Password == "" || strings.TrimSpace(password) == "" {
return errors.New("credentials cannot be null or empty")
}
match, err := a.CheckPassword(password)

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !nobolt
// +build !nobolt
package dataprovider
@ -31,6 +30,7 @@ import (
"time"
bolt "go.etcd.io/bbolt"
bolterrors "go.etcd.io/bbolt/errors"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
@ -39,7 +39,7 @@ import (
)
const (
boltDatabaseVersion = 29
boltDatabaseVersion = 32
)
var (
@ -2134,11 +2134,11 @@ func (p *BoltProvider) addSharedSession(_ Session) error {
return ErrNotImplemented
}
func (p *BoltProvider) deleteSharedSession(_ string) error {
func (p *BoltProvider) deleteSharedSession(_ string, _ SessionType) error {
return ErrNotImplemented
}
func (p *BoltProvider) getSharedSession(_ string) (Session, error) {
func (p *BoltProvider) getSharedSession(_ string, _ SessionType) (Session, error) {
return Session{}, ErrNotImplemented
}
@ -3185,6 +3185,13 @@ func (p *BoltProvider) migrateDatabase() error {
providerLog(logger.LevelError, "%v", err)
logger.ErrorToConsole("%v", err)
return err
case version == 29, version == 30, version == 31:
logger.InfoToConsole("updating database schema version: %d -> 32", version)
providerLog(logger.LevelInfo, "updating database schema version: %d -> 32", version)
if err := updateEventActions(); err != nil {
return err
}
return updateBoltDatabaseVersion(p.dbHandle, 32)
default:
if version > boltDatabaseVersion {
providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version,
@ -3206,6 +3213,15 @@ func (p *BoltProvider) revertDatabase(targetVersion int) error { //nolint:gocycl
return errors.New("current version match target version, nothing to do")
}
switch dbVersion.Version {
case 30, 31, 32:
logger.InfoToConsole("downgrading database schema version: %d -> 29", dbVersion.Version)
providerLog(logger.LevelInfo, "downgrading database schema version: %d -> 29", dbVersion.Version)
if dbVersion.Version == 32 {
if err := restoreEventActions(); err != nil {
return err
}
}
return updateBoltDatabaseVersion(p.dbHandle, 29)
default:
return fmt.Errorf("database schema version not handled: %v", dbVersion.Version)
}
@ -3215,7 +3231,7 @@ func (p *BoltProvider) resetDatabase() error {
return p.dbHandle.Update(func(tx *bolt.Tx) error {
for _, bucketName := range boltBuckets {
err := tx.DeleteBucket(bucketName)
if err != nil && !errors.Is(err, bolt.ErrBucketNotFound) {
if err != nil && !errors.Is(err, bolterrors.ErrBucketNotFound) {
return fmt.Errorf("unable to remove bucket %v: %w", bucketName, err)
}
}
@ -3946,7 +3962,7 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
return dbVersion, err
}
/*func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
err := dbHandle.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(dbVersionBucket)
if bucket == nil {
@ -3962,4 +3978,4 @@ func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
return bucket.Put(dbVersionKey, buf)
})
return err
}*/
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build nobolt
// +build nobolt
package dataprovider

View file

@ -27,6 +27,7 @@ import (
"crypto/sha512"
"crypto/subtle"
"crypto/x509"
"database/sql"
"encoding/base64"
"encoding/hex"
"encoding/json"
@ -90,7 +91,7 @@ const (
CockroachDataProviderName = "cockroachdb"
// DumpVersion defines the version for the dump.
// For restore/load we support the current version and the previous one
DumpVersion = 16
DumpVersion = 17
argonPwdPrefix = "$argon2id$"
bcryptPwdPrefix = "$2a$"
@ -824,8 +825,8 @@ type Provider interface {
cleanupActiveTransfers(before time.Time) error
getActiveTransfers(from time.Time) ([]ActiveTransfer, error)
addSharedSession(session Session) error
deleteSharedSession(key string) error
getSharedSession(key string) (Session, error)
deleteSharedSession(key string, sessionType SessionType) error
getSharedSession(key string, sessionType SessionType) (Session, error)
cleanupSharedSessions(sessionType SessionType, before int64) error
getEventActions(limit, offset int, order string, minimal bool) ([]BaseEventAction, error)
dumpEventActions() ([]BaseEventAction, error)
@ -2244,8 +2245,8 @@ func AddSharedSession(session Session) error {
}
// DeleteSharedSession deletes the session with the specified key
func DeleteSharedSession(key string) error {
err := provider.deleteSharedSession(key)
func DeleteSharedSession(key string, sessionType SessionType) error {
err := provider.deleteSharedSession(key, sessionType)
if err != nil {
providerLog(logger.LevelError, "unable to add shared session, key %q, err: %v", key, err)
}
@ -2253,8 +2254,8 @@ func DeleteSharedSession(key string) error {
}
// GetSharedSession retrieves the session with the specified key
func GetSharedSession(key string) (Session, error) {
return provider.getSharedSession(key)
func GetSharedSession(key string, sessionType SessionType) (Session, error) {
return provider.getSharedSession(key, sessionType)
}
// CleanupSharedSessions removes the shared session with the specified type and
@ -2557,6 +2558,17 @@ func DumpData(scopes []string) (BackupData, error) {
func ParseDumpData(data []byte) (BackupData, error) {
var dump BackupData
err := json.Unmarshal(data, &dump)
if err != nil {
return dump, err
}
if dump.Version < 17 {
providerLog(logger.LevelInfo, "updating placeholders for actions restored from dump version %d", dump.Version)
eventActions, err := updateEventActionPlaceholders(dump.EventActions)
if err != nil {
return dump, fmt.Errorf("unable to update event action placeholders for dump version %d: %w", dump.Version, err)
}
dump.EventActions = eventActions
}
return dump, err
}
@ -3525,7 +3537,7 @@ func checkUserAndPass(user *User, password, ip, protocol string) (User, error) {
if err != nil {
return *user, ErrInvalidCredentials
}
if user.Password == "" || password == "" {
if user.Password == "" || strings.TrimSpace(password) == "" {
return *user, errors.New("credentials cannot be null or empty")
}
if !user.Filters.Hooks.CheckPasswordDisabled {
@ -3700,7 +3712,8 @@ func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool, error)
}
func getSSLMode() string {
if config.Driver == PGSQLDataProviderName || config.Driver == CockroachDataProviderName {
switch config.Driver {
case PGSQLDataProviderName, CockroachDataProviderName:
switch config.SSLMode {
case 0:
return "disable"
@ -3715,7 +3728,7 @@ func getSSLMode() string {
case 5:
return "allow"
}
} else if config.Driver == MySQLDataProviderName {
case MySQLDataProviderName:
if config.requireCustomTLSForMySQL() {
return "custom"
}
@ -4242,18 +4255,19 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
u.Filters.TOTPConfig = totpConfig
u.Filters.RecoveryCodes = recoveryCodes
err = provider.updateUser(&u)
if err == nil {
webDAVUsersCache.swap(&u, "")
}
}
if err != nil {
return u, err
}
providerLog(logger.LevelDebug, "user %q added/updated from pre-login hook response, id: %d", username, userID)
if userID == 0 {
return provider.userExists(username, "")
user, err := provider.userExists(username, "")
if err != nil {
return u, err
}
return u, nil
providerLog(logger.LevelDebug, "user %q added/updated from pre-login hook response, id: %d", username, userID)
if userID > 0 {
webDAVUsersCache.swap(&user, "")
}
return user, nil
}
// ExecutePostLoginHook executes the post login hook if defined
@ -4671,6 +4685,176 @@ func isExternalAuthConfigured(loginMethod string) bool {
}
}
func replaceTemplateVars(input string) string {
var result strings.Builder
i := 0
for i < len(input) {
if i+2 <= len(input) && input[i:i+2] == "{{" {
if i+2 < len(input) {
nextChar := input[i+2]
if nextChar == ' ' || nextChar == '.' || nextChar == '-' {
// Don't replace if followed by space, dot or minus.
result.WriteString("{{")
i += 2
continue
}
}
// Find the closing "}}"
closing := strings.Index(input[i:], "}}")
if closing != -1 {
// Replace with {{. only if it's a proper template variable.
result.WriteString("{{.")
result.WriteString(input[i+2 : i+closing])
result.WriteString("}}")
i += closing + 2
continue
}
}
result.WriteByte(input[i])
i++
}
return result.String()
}
func restoreTemplateVars(input string) string {
var result strings.Builder
i := 0
for i < len(input) {
if i+3 <= len(input) && input[i:i+3] == "{{." {
if i+3 < len(input) {
nextChar := input[i+3]
if nextChar == ' ' || nextChar == '.' || nextChar == '-' {
// Don't change if it's a space, dot, or minus
result.WriteString("{{.")
i += 3
continue
}
}
// Find the closing "}}"
closing := strings.Index(input[i:], "}}")
if closing != -1 {
// Strip the dot and write the rest
result.WriteString("{{")
result.WriteString(input[i+3 : i+closing])
result.WriteString("}}")
i += closing + 2
continue
}
}
result.WriteByte(input[i])
i++
}
return result.String()
}
func updateEventActionPlaceholders(actions []BaseEventAction) ([]BaseEventAction, error) {
var result []BaseEventAction
for _, action := range actions {
options, err := json.Marshal(action.Options)
if err != nil {
return nil, err
}
convertedOptions := replaceTemplateVars(string(options))
var opts BaseEventActionOptions
err = json.Unmarshal([]byte(convertedOptions), &opts)
if err != nil {
return nil, err
}
action.Options = opts
result = append(result, action)
}
return result, nil
}
func restoreEventActionsPlaceholders(actions []BaseEventAction) ([]BaseEventAction, error) {
var result []BaseEventAction
for _, action := range actions {
options, err := json.Marshal(action.Options)
if err != nil {
return nil, err
}
convertedOptions := restoreTemplateVars(string(options))
var opts BaseEventActionOptions
err = json.Unmarshal([]byte(convertedOptions), &opts)
if err != nil {
return nil, err
}
action.Options = opts
result = append(result, action)
}
return result, nil
}
func updateEventActions() error {
actions, err := provider.dumpEventActions()
if err != nil {
return err
}
convertedActions, err := updateEventActionPlaceholders(actions)
if err != nil {
return err
}
for _, action := range convertedActions {
providerLog(logger.LevelInfo, "updating placeholders for event action %q", action.Name)
if err := provider.updateEventAction(&action); err != nil {
return fmt.Errorf("unable to save updated event action %q: %w", action.Name, err)
}
}
return nil
}
func restoreEventActions() error {
actions, err := provider.dumpEventActions()
if err != nil {
return err
}
convertedActions, err := restoreEventActionsPlaceholders(actions)
if err != nil {
return err
}
for _, action := range convertedActions {
providerLog(logger.LevelInfo, "restoring placeholders for event action %q", action.Name)
if err := provider.updateEventAction(&action); err != nil {
return fmt.Errorf("unable to save updated event action %q: %w", action.Name, err)
}
}
return nil
}
func updateSQLDatabaseFrom31To32(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database data version: 31 -> 32")
providerLog(logger.LevelInfo, "updating database data version: 31 -> 32")
if err := updateEventActions(); err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
defer cancel()
return sqlCommonUpdateDatabaseVersion(ctx, dbHandle, 32)
}
func downgradeSQLDatabaseFrom32To31(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database data version: 32 -> 31")
providerLog(logger.LevelInfo, "downgrading database data version: 32 -> 31")
if err := restoreEventActions(); err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
defer cancel()
return sqlCommonUpdateDatabaseVersion(ctx, dbHandle, 31)
}
func getConfigPath(name, configDir string) string {
if !util.IsFileInputValid(name) {
return ""

View file

@ -418,6 +418,10 @@ func (l *IPList) IsListed(ip, protocol string) (bool, int, error) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.Ranges.Len() == 0 {
return false, 0, nil
}
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
return false, 0, fmt.Errorf("invalid IP %s", ip)

View file

@ -2117,11 +2117,11 @@ func (p *MemoryProvider) addSharedSession(_ Session) error {
return ErrNotImplemented
}
func (p *MemoryProvider) deleteSharedSession(_ string) error {
func (p *MemoryProvider) deleteSharedSession(_ string, _ SessionType) error {
return ErrNotImplemented
}
func (p *MemoryProvider) getSharedSession(_ string) (Session, error) {
func (p *MemoryProvider) getSharedSession(_ string, _ SessionType) (Session, error) {
return Session{}, ErrNotImplemented
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !nomysql
// +build !nomysql
package dataprovider
@ -194,6 +193,18 @@ const (
"CREATE INDEX `{{prefix}}ip_lists_deleted_at_idx` ON `{{ip_lists}}` (`deleted_at`);" +
"CREATE INDEX `{{prefix}}ip_lists_first_last_idx` ON `{{ip_lists}}` (`first`, `last`);" +
"INSERT INTO {{schema_version}} (version) VALUES (29);"
mysqlV30SQL = "ALTER TABLE `{{shares}}` ADD COLUMN `options` longtext NULL;"
mysqlV30DownSQL = "ALTER TABLE `{{shares}}` DROP COLUMN `options`;"
mysqlV31SQL = "DROP TABLE IF EXISTS `{{shared_sessions}}` CASCADE;" +
"CREATE TABLE `{{shared_sessions}}` (`key` varchar(128) NOT NULL, `type` integer NOT NULL, `data` longtext NOT NULL, " +
"`timestamp` bigint NOT NULL, PRIMARY KEY (`key`, `type`));" +
"CREATE INDEX `{{prefix}}shared_sessions_type_idx` ON `{{shared_sessions}}` (`type`);" +
"CREATE INDEX `{{prefix}}shared_sessions_timestamp_idx` ON `{{shared_sessions}}` (`timestamp`);"
mysqlV31DownSQL = "DROP TABLE IF EXISTS `{{shared_sessions}}` CASCADE;" +
"CREATE TABLE `{{shared_sessions}}` (`key` varchar(128) NOT NULL PRIMARY KEY, " +
"`data` longtext NOT NULL, `type` integer NOT NULL, `timestamp` bigint NOT NULL);" +
"CREATE INDEX `{{prefix}}shared_sessions_type_idx` ON `{{shared_sessions}}` (`type`);" +
"CREATE INDEX `{{prefix}}shared_sessions_timestamp_idx` ON `{{shared_sessions}}` (`timestamp`);"
)
// MySQLProvider defines the auth provider for MySQL/MariaDB database
@ -587,12 +598,12 @@ func (p *MySQLProvider) addSharedSession(session Session) error {
return sqlCommonAddSession(session, p.dbHandle)
}
func (p *MySQLProvider) deleteSharedSession(key string) error {
return sqlCommonDeleteSession(key, p.dbHandle)
func (p *MySQLProvider) deleteSharedSession(key string, sessionType SessionType) error {
return sqlCommonDeleteSession(key, sessionType, p.dbHandle)
}
func (p *MySQLProvider) getSharedSession(key string) (Session, error) {
return sqlCommonGetSession(key, p.dbHandle)
func (p *MySQLProvider) getSharedSession(key string, sessionType SessionType) (Session, error) {
return sqlCommonGetSession(key, sessionType, p.dbHandle)
}
func (p *MySQLProvider) cleanupSharedSessions(sessionType SessionType, before int64) error {
@ -802,6 +813,12 @@ func (p *MySQLProvider) migrateDatabase() error {
providerLog(logger.LevelError, "%v", err)
logger.ErrorToConsole("%v", err)
return err
case version == 29:
return updateMySQLDatabaseFromV29(p.dbHandle)
case version == 30:
return updateMySQLDatabaseFromV30(p.dbHandle)
case version == 31:
return updateMySQLDatabaseFromV31(p.dbHandle)
default:
if version > sqlDatabaseVersion {
providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version,
@ -824,6 +841,12 @@ func (p *MySQLProvider) revertDatabase(targetVersion int) error {
}
switch dbVersion.Version {
case 30:
return downgradeMySQLDatabaseFromV30(p.dbHandle)
case 31:
return downgradeMySQLDatabaseFromV31(p.dbHandle)
case 32:
return downgradeMySQLDatabaseFromV32(p.dbHandle)
default:
return fmt.Errorf("database schema version not handled: %d", dbVersion.Version)
}
@ -861,3 +884,73 @@ func (p *MySQLProvider) normalizeError(err error, fieldType int) error {
}
return err
}
func updateMySQLDatabaseFromV29(dbHandle *sql.DB) error {
if err := updateMySQLDatabaseFrom29To30(dbHandle); err != nil {
return err
}
return updateMySQLDatabaseFromV30(dbHandle)
}
func updateMySQLDatabaseFromV30(dbHandle *sql.DB) error {
if err := updateMySQLDatabaseFrom30To31(dbHandle); err != nil {
return err
}
return updateMySQLDatabaseFromV31(dbHandle)
}
func updateMySQLDatabaseFromV31(dbHandle *sql.DB) error {
return updateSQLDatabaseFrom31To32(dbHandle)
}
func downgradeMySQLDatabaseFromV30(dbHandle *sql.DB) error {
return downgradeMySQLDatabaseFrom30To29(dbHandle)
}
func downgradeMySQLDatabaseFromV31(dbHandle *sql.DB) error {
if err := downgradeMySQLDatabaseFrom31To30(dbHandle); err != nil {
return err
}
return downgradeMySQLDatabaseFromV30(dbHandle)
}
func downgradeMySQLDatabaseFromV32(dbHandle *sql.DB) error {
if err := downgradeSQLDatabaseFrom32To31(dbHandle); err != nil {
return err
}
return downgradeMySQLDatabaseFromV31(dbHandle)
}
func updateMySQLDatabaseFrom29To30(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 29 -> 30")
providerLog(logger.LevelInfo, "updating database schema version: 29 -> 30")
sql := strings.ReplaceAll(mysqlV30SQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 30, true)
}
func downgradeMySQLDatabaseFrom30To29(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 30 -> 29")
providerLog(logger.LevelInfo, "downgrading database schema version: 30 -> 29")
sql := strings.ReplaceAll(mysqlV30DownSQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 29, false)
}
func updateMySQLDatabaseFrom30To31(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 30 -> 31")
providerLog(logger.LevelInfo, "updating database schema version: 30 -> 31")
sql := strings.ReplaceAll(mysqlV31SQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 31, true)
}
func downgradeMySQLDatabaseFrom31To30(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 31 -> 30")
providerLog(logger.LevelInfo, "downgrading database schema version: 31 -> 30")
sql := strings.ReplaceAll(mysqlV31DownSQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, strings.Split(sql, ";"), 30, false)
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build nomysql
// +build nomysql
package dataprovider

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !nopgsql
// +build !nopgsql
package dataprovider
@ -210,6 +209,19 @@ INSERT INTO {{schema_version}} (version) VALUES (29);
`
// not supported in CockroachDB
ipListsLikeIndex = `CREATE INDEX "{{prefix}}ip_lists_ipornet_like_idx" ON "{{ip_lists}}" ("ipornet" varchar_pattern_ops);`
pgsqlV30SQL = `ALTER TABLE "{{shares}}" ADD COLUMN "options" text NULL;`
pgsqlV30DownSQL = `ALTER TABLE "{{shares}}" DROP COLUMN "options" CASCADE;`
pgsqlV31SQL = `DROP TABLE "{{shared_sessions}}";
CREATE TABLE "{{shared_sessions}}" ("key" varchar(128) NOT NULL, "type" integer NOT NULL,
"data" text NOT NULL, "timestamp" bigint NOT NULL, PRIMARY KEY ("key", "type"));
CREATE INDEX "{{prefix}}shared_sessions_type_idx" ON "{{shared_sessions}}" ("type");
CREATE INDEX "{{prefix}}shared_sessions_timestamp_idx" ON "{{shared_sessions}}" ("timestamp");
`
pgsqlV31DownSQL = `DROP TABLE "{{shared_sessions}}" CASCADE;
CREATE TABLE "{{shared_sessions}}" ("key" varchar(128) NOT NULL PRIMARY KEY,
"data" text NOT NULL, "type" integer NOT NULL, "timestamp" bigint NOT NULL);
CREATE INDEX "{{prefix}}shared_sessions_type_idx" ON "{{shared_sessions}}" ("type");
CREATE INDEX "{{prefix}}shared_sessions_timestamp_idx" ON "{{shared_sessions}}" ("timestamp");`
)
var (
@ -605,12 +617,12 @@ func (p *PGSQLProvider) addSharedSession(session Session) error {
return sqlCommonAddSession(session, p.dbHandle)
}
func (p *PGSQLProvider) deleteSharedSession(key string) error {
return sqlCommonDeleteSession(key, p.dbHandle)
func (p *PGSQLProvider) deleteSharedSession(key string, sessionType SessionType) error {
return sqlCommonDeleteSession(key, sessionType, p.dbHandle)
}
func (p *PGSQLProvider) getSharedSession(key string) (Session, error) {
return sqlCommonGetSession(key, p.dbHandle)
func (p *PGSQLProvider) getSharedSession(key string, sessionType SessionType) (Session, error) {
return sqlCommonGetSession(key, sessionType, p.dbHandle)
}
func (p *PGSQLProvider) cleanupSharedSessions(sessionType SessionType, before int64) error {
@ -826,6 +838,12 @@ func (p *PGSQLProvider) migrateDatabase() error { //nolint:dupl
providerLog(logger.LevelError, "%v", err)
logger.ErrorToConsole("%v", err)
return err
case version == 29:
return updatePGSQLDatabaseFromV29(p.dbHandle)
case version == 30:
return updatePGSQLDatabaseFromV30(p.dbHandle)
case version == 31:
return updatePGSQLDatabaseFromV31(p.dbHandle)
default:
if version > sqlDatabaseVersion {
providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version,
@ -848,6 +866,12 @@ func (p *PGSQLProvider) revertDatabase(targetVersion int) error {
}
switch dbVersion.Version {
case 30:
return downgradePGSQLDatabaseFromV30(p.dbHandle)
case 31:
return downgradePGSQLDatabaseFromV31(p.dbHandle)
case 32:
return downgradePGSQLDatabaseFromV32(p.dbHandle)
default:
return fmt.Errorf("database schema version not handled: %d", dbVersion.Version)
}
@ -885,3 +909,73 @@ func (p *PGSQLProvider) normalizeError(err error, fieldType int) error {
}
return err
}
func updatePGSQLDatabaseFromV29(dbHandle *sql.DB) error {
if err := updatePGSQLDatabaseFrom29To30(dbHandle); err != nil {
return err
}
return updatePGSQLDatabaseFromV30(dbHandle)
}
func updatePGSQLDatabaseFromV30(dbHandle *sql.DB) error {
if err := updatePGSQLDatabaseFrom30To31(dbHandle); err != nil {
return err
}
return updatePGSQLDatabaseFromV31(dbHandle)
}
func updatePGSQLDatabaseFromV31(dbHandle *sql.DB) error {
return updateSQLDatabaseFrom31To32(dbHandle)
}
func downgradePGSQLDatabaseFromV30(dbHandle *sql.DB) error {
return downgradePGSQLDatabaseFrom30To29(dbHandle)
}
func downgradePGSQLDatabaseFromV31(dbHandle *sql.DB) error {
if err := downgradePGSQLDatabaseFrom31To30(dbHandle); err != nil {
return err
}
return downgradePGSQLDatabaseFromV30(dbHandle)
}
func downgradePGSQLDatabaseFromV32(dbHandle *sql.DB) error {
if err := downgradeSQLDatabaseFrom32To31(dbHandle); err != nil {
return err
}
return downgradePGSQLDatabaseFromV31(dbHandle)
}
func updatePGSQLDatabaseFrom29To30(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 29 -> 30")
providerLog(logger.LevelInfo, "updating database schema version: 29 -> 30")
sql := strings.ReplaceAll(pgsqlV30SQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 30, true)
}
func downgradePGSQLDatabaseFrom30To29(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 30 -> 29")
providerLog(logger.LevelInfo, "downgrading database schema version: 30 -> 29")
sql := strings.ReplaceAll(pgsqlV30DownSQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 29, false)
}
func updatePGSQLDatabaseFrom30To31(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 30 -> 31")
providerLog(logger.LevelInfo, "updating database schema version: 30 -> 31")
sql := strings.ReplaceAll(pgsqlV31SQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 31, true)
}
func downgradePGSQLDatabaseFrom31To30(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 31 -> 30")
providerLog(logger.LevelInfo, "downgrading database schema version: 31 -> 30")
sql := strings.ReplaceAll(pgsqlV31DownSQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 30, false)
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build nopgsql
// +build nopgsql
package dataprovider

View file

@ -36,7 +36,7 @@ import (
)
const (
sqlDatabaseVersion = 29
sqlDatabaseVersion = 32
defaultSQLQueryTimeout = 10 * time.Second
longSQLQueryTimeout = 60 * time.Second
)
@ -3265,14 +3265,14 @@ func sqlCommonAddSession(session Session, dbHandle *sql.DB) error {
return err
}
func sqlCommonGetSession(key string, dbHandle sqlQuerier) (Session, error) {
func sqlCommonGetSession(key string, sessionType SessionType, dbHandle sqlQuerier) (Session, error) {
var session Session
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
defer cancel()
q := getSessionQuery()
var data []byte // type hint, some driver will use string instead of []byte if the type is any
err := dbHandle.QueryRowContext(ctx, q, key).Scan(&session.Key, &data, &session.Type, &session.Timestamp)
err := dbHandle.QueryRowContext(ctx, q, key, sessionType).Scan(&session.Key, &data, &session.Type, &session.Timestamp)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return session, util.NewRecordNotFoundError(err.Error())
@ -3283,12 +3283,12 @@ func sqlCommonGetSession(key string, dbHandle sqlQuerier) (Session, error) {
return session, nil
}
func sqlCommonDeleteSession(key string, dbHandle *sql.DB) error {
func sqlCommonDeleteSession(key string, sessionType SessionType, dbHandle *sql.DB) error {
ctx, cancel := context.WithTimeout(context.Background(), defaultSQLQueryTimeout)
defer cancel()
q := getDeleteSessionQuery()
res, err := dbHandle.ExecContext(ctx, q, key)
res, err := dbHandle.ExecContext(ctx, q, key, sessionType)
if err != nil {
return err
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !nosqlite && cgo
// +build !nosqlite,cgo
package dataprovider
@ -24,6 +23,7 @@ import (
"errors"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/mattn/go-sqlite3"
@ -179,6 +179,20 @@ CREATE INDEX "{{prefix}}ip_lists_ip_updated_at_idx" ON "{{ip_lists}}" ("updated_
CREATE INDEX "{{prefix}}ip_lists_ip_deleted_at_idx" ON "{{ip_lists}}" ("deleted_at");
CREATE INDEX "{{prefix}}ip_lists_first_last_idx" ON "{{ip_lists}}" ("first", "last");
INSERT INTO {{schema_version}} (version) VALUES (29);
`
sqliteV30SQL = `ALTER TABLE "{{shares}}" ADD COLUMN "options" text NULL;`
sqliteV30DownSQL = `ALTER TABLE "{{shares}}" DROP COLUMN "options";`
sqliteV31SQL = `DROP TABLE "{{shared_sessions}}";
CREATE TABLE "{{shared_sessions}}" ("key" varchar(128) NOT NULL, "type" integer NOT NULL,
"data" text NOT NULL, "timestamp" bigint NOT NULL, PRIMARY KEY ("key", "type"));
CREATE INDEX "{{prefix}}shared_sessions_type_idx" ON "{{shared_sessions}}" ("type");
CREATE INDEX "{{prefix}}shared_sessions_timestamp_idx" ON "{{shared_sessions}}" ("timestamp");
`
sqliteV31DownSQL = `DROP TABLE "{{shared_sessions}}";
CREATE TABLE "{{shared_sessions}}" ("key" varchar(128) NOT NULL PRIMARY KEY, "data" text NOT NULL,
"type" integer NOT NULL, "timestamp" bigint NOT NULL);
CREATE INDEX "{{prefix}}shared_sessions_type_idx" ON "{{shared_sessions}}" ("type");
CREATE INDEX "{{prefix}}shared_sessions_timestamp_idx" ON "{{shared_sessions}}" ("timestamp");
`
)
@ -508,12 +522,12 @@ func (p *SQLiteProvider) addSharedSession(session Session) error {
return sqlCommonAddSession(session, p.dbHandle)
}
func (p *SQLiteProvider) deleteSharedSession(key string) error {
return sqlCommonDeleteSession(key, p.dbHandle)
func (p *SQLiteProvider) deleteSharedSession(key string, sessionType SessionType) error {
return sqlCommonDeleteSession(key, sessionType, p.dbHandle)
}
func (p *SQLiteProvider) getSharedSession(key string) (Session, error) {
return sqlCommonGetSession(key, p.dbHandle)
func (p *SQLiteProvider) getSharedSession(key string, sessionType SessionType) (Session, error) {
return sqlCommonGetSession(key, sessionType, p.dbHandle)
}
func (p *SQLiteProvider) cleanupSharedSessions(sessionType SessionType, before int64) error {
@ -722,6 +736,12 @@ func (p *SQLiteProvider) migrateDatabase() error { //nolint:dupl
providerLog(logger.LevelError, "%v", err)
logger.ErrorToConsole("%v", err)
return err
case version == 29:
return updateSQLiteDatabaseFromV29(p.dbHandle)
case version == 30:
return updateSQLiteDatabaseFromV30(p.dbHandle)
case version == 31:
return updateSQLiteDatabaseFromV31(p.dbHandle)
default:
if version > sqlDatabaseVersion {
providerLog(logger.LevelError, "database schema version %d is newer than the supported one: %d", version,
@ -744,6 +764,12 @@ func (p *SQLiteProvider) revertDatabase(targetVersion int) error {
}
switch dbVersion.Version {
case 30:
return downgradeSQLiteDatabaseFromV30(p.dbHandle)
case 31:
return downgradeSQLiteDatabaseFromV31(p.dbHandle)
case 32:
return downgradeSQLiteDatabaseFromV32(p.dbHandle)
default:
return fmt.Errorf("database schema version not handled: %d", dbVersion.Version)
}
@ -789,6 +815,76 @@ func executePragmaOptimize(dbHandle *sql.DB) error {
return err
}
func updateSQLiteDatabaseFromV29(dbHandle *sql.DB) error {
if err := updateSQLiteDatabaseFrom29To30(dbHandle); err != nil {
return err
}
return updateSQLiteDatabaseFromV30(dbHandle)
}
func updateSQLiteDatabaseFromV30(dbHandle *sql.DB) error {
if err := updateSQLiteDatabaseFrom30To31(dbHandle); err != nil {
return err
}
return updateSQLiteDatabaseFromV31(dbHandle)
}
func updateSQLiteDatabaseFromV31(dbHandle *sql.DB) error {
return updateSQLDatabaseFrom31To32(dbHandle)
}
func downgradeSQLiteDatabaseFromV30(dbHandle *sql.DB) error {
return downgradeSQLiteDatabaseFrom30To29(dbHandle)
}
func downgradeSQLiteDatabaseFromV31(dbHandle *sql.DB) error {
if err := downgradeSQLiteDatabaseFrom31To30(dbHandle); err != nil {
return err
}
return downgradeSQLiteDatabaseFromV30(dbHandle)
}
func downgradeSQLiteDatabaseFromV32(dbHandle *sql.DB) error {
if err := downgradeSQLDatabaseFrom32To31(dbHandle); err != nil {
return err
}
return downgradeSQLiteDatabaseFromV31(dbHandle)
}
func updateSQLiteDatabaseFrom29To30(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 29 -> 30")
providerLog(logger.LevelInfo, "updating database schema version: 29 -> 30")
sql := strings.ReplaceAll(sqliteV30SQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 30, true)
}
func downgradeSQLiteDatabaseFrom30To29(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 30 -> 29")
providerLog(logger.LevelInfo, "downgrading database schema version: 30 -> 29")
sql := strings.ReplaceAll(sqliteV30DownSQL, "{{shares}}", sqlTableShares)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 29, false)
}
func updateSQLiteDatabaseFrom30To31(dbHandle *sql.DB) error {
logger.InfoToConsole("updating database schema version: 30 -> 31")
providerLog(logger.LevelInfo, "updating database schema version: 30 -> 31")
sql := strings.ReplaceAll(sqliteV31SQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 31, true)
}
func downgradeSQLiteDatabaseFrom31To30(dbHandle *sql.DB) error {
logger.InfoToConsole("downgrading database schema version: 31 -> 30")
providerLog(logger.LevelInfo, "downgrading database schema version: 31 -> 30")
sql := strings.ReplaceAll(sqliteV31DownSQL, "{{shared_sessions}}", sqlTableSharedSessions)
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
return sqlCommonExecSQLAndUpdateDBVersion(dbHandle, []string{sql}, 30, false)
}
/*func setPragmaFK(dbHandle *sql.DB, value string) error {
ctx, cancel := context.WithTimeout(context.Background(), longSQLQueryTimeout)
defer cancel()

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build nosqlite || !cgo
// +build nosqlite !cgo
package dataprovider

View file

@ -81,25 +81,27 @@ func getAddSessionQuery() string {
"ON DUPLICATE KEY UPDATE `data`=VALUES(`data`), `timestamp`=VALUES(`timestamp`)",
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2], sqlPlaceholders[3])
}
return fmt.Sprintf(`INSERT INTO %s (key,data,type,timestamp) VALUES (%s,%s,%s,%s) ON CONFLICT(key) DO UPDATE SET data=
return fmt.Sprintf(`INSERT INTO %s (key,data,type,timestamp) VALUES (%s,%s,%s,%s) ON CONFLICT(key,type) DO UPDATE SET data=
EXCLUDED.data, timestamp=EXCLUDED.timestamp`,
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2], sqlPlaceholders[3])
}
func getDeleteSessionQuery() string {
if config.Driver == MySQLDataProviderName {
return fmt.Sprintf("DELETE FROM %s WHERE `key` = %s", sqlTableSharedSessions, sqlPlaceholders[0])
return fmt.Sprintf("DELETE FROM %s WHERE `key` = %s AND `type` = %s",
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1])
}
return fmt.Sprintf(`DELETE FROM %s WHERE key = %s`, sqlTableSharedSessions, sqlPlaceholders[0])
return fmt.Sprintf(`DELETE FROM %s WHERE key = %s AND type = %s`,
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1])
}
func getSessionQuery() string {
if config.Driver == MySQLDataProviderName {
return fmt.Sprintf("SELECT `key`,`data`,`type`,`timestamp` FROM %s WHERE `key` = %s", sqlTableSharedSessions,
sqlPlaceholders[0])
return fmt.Sprintf("SELECT `key`,`data`,`type`,`timestamp` FROM %s WHERE `key` = %s AND `type` = %s",
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1])
}
return fmt.Sprintf(`SELECT key,data,type,timestamp FROM %s WHERE key = %s`, sqlTableSharedSessions,
sqlPlaceholders[0])
return fmt.Sprintf(`SELECT key,data,type,timestamp FROM %s WHERE key = %s AND type = %s`,
sqlTableSharedSessions, sqlPlaceholders[0], sqlPlaceholders[1])
}
func getCleanupSessionsQuery() string {

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build unixcrypt && cgo
// +build unixcrypt,cgo
package dataprovider

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !unixcrypt || !cgo
// +build !unixcrypt !cgo
package dataprovider

View file

@ -640,6 +640,8 @@ func TestBasicFTPHandling(t *testing.T) {
}
err = ftpUploadFile(testFilePath, testFileName, testFileSize, client, 0)
assert.NoError(t, err)
_, err = client.FileSize(path.Join("/", testDir))
assert.Error(t, err)
size, err := client.FileSize(path.Join("/", testDir, testFileName))
assert.NoError(t, err)
assert.Equal(t, testFileSize, size)

View file

@ -287,7 +287,7 @@ func (c *Connection) RemoveDir(name string) error {
func (c *Connection) Symlink(oldname, newname string) error {
c.UpdateLastActivity()
return c.BaseConnection.CreateSymlink(oldname, newname)
return c.CreateSymlink(oldname, newname)
}
// ReadDir implements ClientDriverExtensionFilelist

View file

@ -136,7 +136,7 @@ func (t *transfer) closeIO() error {
} else if t.reader != nil {
err = t.reader.Close()
if metadater, ok := t.reader.(vfs.Metadater); ok {
t.BaseTransfer.SetMetadata(metadater.Metadata())
t.SetMetadata(metadater.Metadata())
}
}
return err

View file

@ -66,7 +66,7 @@ func testSMTPConfig(w http.ResponseWriter, r *http.Request) {
}
}
if req.AuthType == 3 {
if err := req.Config.OAuth2.Validate(); err != nil {
if err := req.OAuth2.Validate(); err != nil {
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
return
}
@ -106,10 +106,10 @@ func (s *httpdServer) handleSMTPOAuth2TokenRequestPost(w http.ResponseWriter, r
}
configs.SetNilsToEmpty()
if err := configs.SMTP.TryDecrypt(); err == nil {
req.OAuth2Config.ClientSecret = configs.SMTP.OAuth2.ClientSecret.GetPayload()
req.ClientSecret = configs.SMTP.OAuth2.ClientSecret.GetPayload()
}
}
cfg := req.OAuth2Config.GetOAuth2()
cfg := req.GetOAuth2()
cfg.RedirectURL = req.BaseRedirectURL + webOAuth2RedirectPath
clientSecret := kms.NewPlainSecret(cfg.ClientSecret)
clientSecret.SetAdditionalData(xid.New().String())

View file

@ -68,8 +68,8 @@ func getCommonSearchParamsFromRequest(r *http.Request) (eventsearcher.CommonSear
}
c.EndTimestamp = ts
}
c.Username = r.URL.Query().Get("username")
c.IP = r.URL.Query().Get("ip")
c.Username = strings.TrimSpace(r.URL.Query().Get("username"))
c.IP = strings.TrimSpace(r.URL.Query().Get("ip"))
c.InstanceIDs = getCommaSeparatedQueryParam(r, "instance_ids")
c.FromID = r.URL.Query().Get("from_id")
@ -93,9 +93,9 @@ func getFsSearchParamsFromRequest(r *http.Request) (eventsearcher.FsEventSearch,
s.FsProvider = val
}
s.Actions = getCommaSeparatedQueryParam(r, "actions")
s.SSHCmd = r.URL.Query().Get("ssh_cmd")
s.Bucket = r.URL.Query().Get("bucket")
s.Endpoint = r.URL.Query().Get("endpoint")
s.SSHCmd = strings.TrimSpace(r.URL.Query().Get("ssh_cmd"))
s.Bucket = strings.TrimSpace(r.URL.Query().Get("bucket"))
s.Endpoint = strings.TrimSpace(r.URL.Query().Get("endpoint"))
s.Protocols = getCommaSeparatedQueryParam(r, "protocols")
statuses := getCommaSeparatedQueryParam(r, "statuses")
for _, status := range statuses {
@ -117,7 +117,7 @@ func getProviderSearchParamsFromRequest(r *http.Request) (eventsearcher.Provider
return s, err
}
s.Actions = getCommaSeparatedQueryParam(r, "actions")
s.ObjectName = r.URL.Query().Get("object_name")
s.ObjectName = strings.TrimSpace(r.URL.Query().Get("object_name"))
s.ObjectTypes = getCommaSeparatedQueryParam(r, "object_types")
return s, nil
}

View file

@ -49,7 +49,7 @@ func getUserConnection(w http.ResponseWriter, r *http.Request) (*Connection, err
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%v_%v", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return nil, err
}

View file

@ -507,7 +507,6 @@ func (s *httpdServer) checkPublicShare(w http.ResponseWriter, r *http.Request, v
if share.Password != "" {
if isWebClient {
if err := s.checkWebClientShareCredentials(w, r, &share); err != nil {
handleDefenderEventLoginFailed(ipAddr, err) //nolint:errcheck
return share, nil, dataprovider.ErrInvalidCredentials
}
} else {

View file

@ -732,7 +732,7 @@ func updateLoginMetrics(user *dataprovider.User, loginMethod, ip string, err err
dataprovider.ExecutePostLoginHook(user, loginMethod, ip, protocol, err)
}
func checkHTTPClientUser(user *dataprovider.User, r *http.Request, connectionID string, checkSessions bool) error {
func checkHTTPClientUser(user *dataprovider.User, r *http.Request, connectionID string, checkSessions, isOIDCLogin bool) error {
if slices.Contains(user.Filters.DeniedProtocols, common.ProtocolHTTP) {
logger.Info(logSender, connectionID, "cannot login user %q, protocol HTTP is not allowed", user.Username)
return util.NewI18nError(
@ -740,7 +740,7 @@ func checkHTTPClientUser(user *dataprovider.User, r *http.Request, connectionID
util.I18nErrorProtocolForbidden,
)
}
if !isLoggedInWithOIDC(r) && !user.IsLoginMethodAllowed(dataprovider.LoginMethodPassword, common.ProtocolHTTP) {
if !isLoggedInWithOIDC(r) && !isOIDCLogin && !user.IsLoginMethodAllowed(dataprovider.LoginMethodPassword, common.ProtocolHTTP) {
logger.Info(logSender, connectionID, "cannot login user %q, password login method is not allowed", user.Username)
return util.NewI18nError(
fmt.Errorf("login method password is not allowed for user %q", user.Username),
@ -784,7 +784,7 @@ func getActiveUser(username string, r *http.Request) (dataprovider.User, error)
if err := user.CheckLoginConditions(); err != nil {
return user, util.NewRecordNotFoundError(fmt.Sprintf("user %q cannot login: %v", username, err))
}
if err := checkHTTPClientUser(&user, r, xid.New().String(), false); err != nil {
if err := checkHTTPClientUser(&user, r, xid.New().String(), false, false); err != nil {
return user, util.NewRecordNotFoundError(fmt.Sprintf("user %q cannot login: %v", username, err))
}
return user, nil

View file

@ -96,8 +96,8 @@ func updateTokensDuration(api, cookie, share int) {
if isTokenDurationValid(share) {
shareTokenDuration = time.Duration(share) * time.Minute
}
logger.Debug(logSender, "", "API token duration %s, cookie token duration %s, cookie refresh threshold %s, share token duration %s",
apiTokenDuration, cookieTokenDuration, cookieRefreshThreshold, shareTokenDuration)
logger.Debug(logSender, "", "API token duration %s, cookie token duration %s, cookie refresh threshold %s, share token duration %s, csrf token duration %s",
apiTokenDuration, cookieTokenDuration, cookieRefreshThreshold, shareTokenDuration, csrfTokenDuration)
}
func getTokenDuration(audience tokenAudience) time.Duration {

View file

@ -126,7 +126,7 @@ func (f *httpdFile) closeIO() error {
} else if f.reader != nil {
err = f.reader.Close()
if metadater, ok := f.reader.(vfs.Metadater); ok {
f.BaseTransfer.SetMetadata(metadater.Metadata())
f.SetMetadata(metadater.Metadata())
}
}
return err

View file

@ -414,11 +414,17 @@ type SecurityConf struct {
ContentSecurityPolicy string `json:"content_security_policy" mapstructure:"content_security_policy"`
// PermissionsPolicy allows to set the Permissions-Policy header value. Default is "".
PermissionsPolicy string `json:"permissions_policy" mapstructure:"permissions_policy"`
// CrossOriginOpenerPolicy allows to set the `Cross-Origin-Opener-Policy` header value. Default is "".
// CrossOriginOpenerPolicy allows to set the Cross-Origin-Opener-Policy header value. Default is "".
CrossOriginOpenerPolicy string `json:"cross_origin_opener_policy" mapstructure:"cross_origin_opener_policy"`
// CacheControl allow to set the Cache-Control header value.
// CrossOriginResourcePolicy allows to set the Cross-Origin-Resource-Policy header value. Default is "".
CrossOriginResourcePolicy string `json:"cross_origin_resource_policy" mapstructure:"cross_origin_resource_policy"`
// CrossOriginEmbedderPolicy allows to set the Cross-Origin-Embedder-Policy header value. Default is "".
CrossOriginEmbedderPolicy string `json:"cross_origin_embedder_policy" mapstructure:"cross_origin_embedder_policy"`
// CacheControl allows to set the Cache-Control header value.
CacheControl string `json:"cache_control" mapstructure:"cache_control"`
proxyHeaders []string
// ReferrerPolicy allows to set the Referrer-Policy header values.
ReferrerPolicy string `json:"referrer_policy" mapstructure:"referrer_policy"`
proxyHeaders []string
}
func (s *SecurityConf) updateProxyHeaders() {
@ -561,7 +567,21 @@ type Binding struct {
//
// You can combine the values. For example 3 means that you can only login using OIDC on
// both WebClient and WebAdmin UI.
// Deprecated because it is not extensible, use DisabledLoginMethods
EnabledLoginMethods int `json:"enabled_login_methods" mapstructure:"enabled_login_methods"`
// Defines the login methods disabled for the WebAdmin and WebClient UIs:
//
// - 1 means OIDC for the WebAdmin UI
// - 2 means OIDC for the WebClient UI
// - 4 means login form for the WebAdmin UI
// - 8 means login form for the WebClient UI
// - 16 means basic auth for admin REST API
// - 32 means basic auth for user REST API
// - 64 means API key auth for admins
// - 128 means API key auth for users
// You can combine the values. For example 12 means that you can only login using OIDC on
// both WebClient and WebAdmin UI.
DisabledLoginMethods int `json:"disabled_login_methods" mapstructure:"disabled_login_methods"`
// you also need to provide a certificate for enabling HTTPS
EnableHTTPS bool `json:"enable_https" mapstructure:"enable_https"`
// Certificate and matching private key for this specific binding, if empty the global
@ -683,45 +703,76 @@ func (b *Binding) IsValid() bool {
func (b *Binding) isWebAdminOIDCLoginDisabled() bool {
if b.EnableWebAdmin {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&1 == 0
return b.DisabledLoginMethods&1 != 0
}
return false
}
func (b *Binding) isWebClientOIDCLoginDisabled() bool {
if b.EnableWebClient {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&2 == 0
return b.DisabledLoginMethods&2 != 0
}
return false
}
func (b *Binding) isWebAdminLoginFormDisabled() bool {
if b.EnableWebAdmin {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&4 == 0
return b.DisabledLoginMethods&4 != 0
}
return false
}
func (b *Binding) isWebClientLoginFormDisabled() bool {
if b.EnableWebClient {
if b.EnabledLoginMethods == 0 {
return false
}
return b.EnabledLoginMethods&8 == 0
return b.DisabledLoginMethods&8 != 0
}
return false
}
func (b *Binding) isAdminTokenEndpointDisabled() bool {
return b.DisabledLoginMethods&16 != 0
}
func (b *Binding) isUserTokenEndpointDisabled() bool {
return b.DisabledLoginMethods&32 != 0
}
func (b *Binding) isAdminAPIKeyAuthDisabled() bool {
return b.DisabledLoginMethods&64 != 0
}
func (b *Binding) isUserAPIKeyAuthDisabled() bool {
return b.DisabledLoginMethods&128 != 0
}
func (b *Binding) hasLoginForAPI() bool {
return !b.isAdminTokenEndpointDisabled() || !b.isUserTokenEndpointDisabled() ||
!b.isAdminAPIKeyAuthDisabled() || !b.isUserAPIKeyAuthDisabled()
}
// convertLoginMethods checks if the deprecated EnabledLoginMethods is set and
// convert the value to DisabledLoginMethods.
func (b *Binding) convertLoginMethods() {
if b.DisabledLoginMethods > 0 || b.EnabledLoginMethods == 0 {
// DisabledLoginMethods already in use or EnabledLoginMethods not set.
return
}
if b.EnabledLoginMethods&1 == 0 {
b.DisabledLoginMethods++
}
if b.EnabledLoginMethods&2 == 0 {
b.DisabledLoginMethods += 2
}
if b.EnabledLoginMethods&4 == 0 {
b.DisabledLoginMethods += 4
}
if b.EnabledLoginMethods&8 == 0 {
b.DisabledLoginMethods += 8
}
}
func (b *Binding) checkLoginMethods() error {
b.convertLoginMethods()
if b.isWebAdminLoginFormDisabled() && b.isWebAdminOIDCLoginDisabled() {
return errors.New("no login method available for WebAdmin UI")
}
@ -738,6 +789,9 @@ func (b *Binding) checkLoginMethods() error {
return errors.New("no login method available for WebClient UI")
}
}
if b.EnableRESTAPI && !b.hasLoginForAPI() {
return errors.New("no login method available for REST API")
}
return nil
}

View file

@ -578,27 +578,28 @@ func TestInitialization(t *testing.T) {
httpdConf.Bindings[0].OIDC = httpd.OIDC{}
httpdConf.Bindings[0].EnableWebClient = true
httpdConf.Bindings[0].EnableWebAdmin = true
httpdConf.Bindings[0].EnabledLoginMethods = 1
httpdConf.Bindings[0].EnableRESTAPI = true
httpdConf.Bindings[0].DisabledLoginMethods = 14
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebAdmin UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 2
httpdConf.Bindings[0].DisabledLoginMethods = 13
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebAdmin UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 6
httpdConf.Bindings[0].DisabledLoginMethods = 9
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 4
httpdConf.Bindings[0].DisabledLoginMethods = 11
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
}
httpdConf.Bindings[0].EnabledLoginMethods = 3
httpdConf.Bindings[0].DisabledLoginMethods = 12
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebAdmin UI")
@ -608,6 +609,12 @@ func TestInitialization(t *testing.T) {
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for WebClient UI")
}
httpdConf.Bindings[0].EnableWebClient = false
httpdConf.Bindings[0].DisabledLoginMethods = 240
err = httpdConf.Initialize(configDir, isShared)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "no login method available for REST API")
}
err = dataprovider.Close()
assert.NoError(t, err)
err = httpdConf.Initialize(configDir, isShared)
@ -621,6 +628,72 @@ func TestInitialization(t *testing.T) {
assert.NoError(t, err)
}
func TestMigrateEventActionPlaceholders(t *testing.T) {
if config.GetProviderConf().Driver == dataprovider.MemoryDataProviderName {
t.Skip("this test is not supported with the memory provider")
}
// Add some event actions using the old placeholders syntax
a1 := dataprovider.BaseEventAction{
Name: xid.New().String(),
Type: dataprovider.ActionTypeEmail,
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `Failed "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}",
},
},
}
a2 := dataprovider.BaseEventAction{
Name: xid.New().String(),
Type: dataprovider.ActionTypeFilesystem,
Options: dataprovider.BaseEventActionOptions{
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionRename,
Renames: []dataprovider.RenameConfig{
{
KeyValue: dataprovider.KeyValue{
Key: "/{{VirtualDirPath}}/{{ObjectName}}",
Value: "/{{ObjectName}}_renamed",
},
},
},
},
},
}
action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
assert.NoError(t, err)
action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated)
assert.NoError(t, err)
// Revert the database to the previous version.
err = dataprovider.Close()
assert.NoError(t, err)
err = config.LoadConfig(configDir, "")
assert.NoError(t, err)
providerConf := config.GetProviderConf()
err = dataprovider.RevertDatabase(providerConf, configDir, 29)
assert.NoError(t, err)
// Close and initialize.
err = dataprovider.Close()
assert.NoError(t, err)
err = dataprovider.Initialize(providerConf, configDir, true)
assert.NoError(t, err)
// Check that actions are migrated.
action1Get, _, err := httpdtest.GetEventActionByName(action1.Name, http.StatusOK)
assert.NoError(t, err)
action2Get, _, err := httpdtest.GetEventActionByName(action2.Name, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, `Failed "{{.Event}}" from "{{.Name}}"`, action1Get.Options.EmailConfig.Subject)
assert.Equal(t, `Object name: {{.ObjectName}} object type: {{.ObjectType}}, IP: {{.IP}}`, action1Get.Options.EmailConfig.Body)
assert.Equal(t, `/{{.VirtualDirPath}}/{{.ObjectName}}`, action2Get.Options.FsConfig.Renames[0].Key)
assert.Equal(t, `/{{.ObjectName}}_renamed`, action2Get.Options.FsConfig.Renames[0].Value)
// Clenup.
_, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
assert.NoError(t, err)
_, err = httpdtest.RemoveEventAction(action2, http.StatusOK)
assert.NoError(t, err)
}
func TestBasicUserHandling(t *testing.T) {
u := getTestUser()
u.Email = "user@user.com"
@ -1357,19 +1430,19 @@ func TestGroupSettingsOverride(t *testing.T) {
switch f.Name {
case folderName1:
assert.Equal(t, mappedPath1, f.MappedPath)
assert.Equal(t, 3, f.BaseVirtualFolder.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 5, f.BaseVirtualFolder.FsConfig.OSConfig.WriteBufferSize)
assert.Equal(t, 3, f.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 5, f.FsConfig.OSConfig.WriteBufferSize)
assert.True(t, slices.Contains([]string{"/vdir1", "/vdir2"}, f.VirtualPath))
case folderName2:
assert.Equal(t, mappedPath2, f.MappedPath)
assert.Equal(t, "/vdir3", f.VirtualPath)
assert.Equal(t, 0, f.BaseVirtualFolder.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 0, f.BaseVirtualFolder.FsConfig.OSConfig.WriteBufferSize)
assert.Equal(t, 0, f.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 0, f.FsConfig.OSConfig.WriteBufferSize)
case folderName3:
assert.Equal(t, mappedPath3, f.MappedPath)
assert.Equal(t, "/vdir4", f.VirtualPath)
assert.Equal(t, 1, f.BaseVirtualFolder.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 2, f.BaseVirtualFolder.FsConfig.OSConfig.WriteBufferSize)
assert.Equal(t, 1, f.FsConfig.OSConfig.ReadBufferSize)
assert.Equal(t, 2, f.FsConfig.OSConfig.WriteBufferSize)
}
}
}
@ -1856,9 +1929,9 @@ func TestBasicActionRulesHandling(t *testing.T) {
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"email@example.com"},
Bcc: []string{"bcc@example.com"},
Subject: "Event: {{Event}}",
Subject: "Event: {{.Event}}",
Body: "test mail body",
Attachments: []string{"/{{VirtualPath}}"},
Attachments: []string{"/{{.VirtualPath}}"},
},
}
@ -1896,7 +1969,7 @@ func TestBasicActionRulesHandling(t *testing.T) {
Value: "b",
},
},
Body: `{"event":"{{Event}}","name":"{{Name}}"}`,
Body: `{"event":"{{.Event}}","name":"{{.Name}}"}`,
},
}
action, _, err = httpdtest.UpdateEventAction(a, http.StatusOK)
@ -4879,9 +4952,6 @@ func TestAddUserInvalidFsConfig(t *testing.T) {
u.FsConfig.AzBlobConfig.AccountName = "name"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.Container = "container"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.FsConfig.AzBlobConfig.AccountKey = kms.NewSecret(sdkkms.SecretStatusRedacted, "key", "", "")
u.FsConfig.AzBlobConfig.KeyPrefix = "/amedir/subdir/"
_, _, err = httpdtest.AddUser(u, http.StatusBadRequest)
@ -6838,6 +6908,57 @@ func TestAdminGenerateRecoveryCodesSaveError(t *testing.T) {
assert.NoError(t, err)
}
func TestAdminCredentialsWithSpaces(t *testing.T) {
a := getTestAdmin()
a.Username = xid.New().String()
a.Password = " " + xid.New().String() + " "
admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
assert.NoError(t, err)
// For admins the password is always trimmed.
_, err = getJWTAPITokenFromTestServer(a.Username, a.Password)
assert.Error(t, err)
_, err = getJWTAPITokenFromTestServer(a.Username, strings.TrimSpace(a.Password))
assert.NoError(t, err)
// The password sent from the WebAdmin UI is automatically trimmed
_, err = getJWTWebToken(a.Username, a.Password)
assert.NoError(t, err)
_, err = getJWTWebToken(a.Username, strings.TrimSpace(a.Password))
assert.NoError(t, err)
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
assert.NoError(t, err)
}
func TestUserCredentialsWithSpaces(t *testing.T) {
u := getTestUser()
u.Password = " " + xid.New().String() + " "
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
// For users the password is not trimmed
_, err = getJWTAPIUserTokenFromTestServer(u.Username, u.Password)
assert.NoError(t, err)
_, err = getJWTAPIUserTokenFromTestServer(u.Username, strings.TrimSpace(u.Password))
assert.Error(t, err)
_, err = getJWTWebClientTokenFromTestServer(u.Username, u.Password)
assert.NoError(t, err)
_, err = getJWTWebClientTokenFromTestServer(u.Username, strings.TrimSpace(u.Password))
assert.Error(t, err)
user.Password = u.Password
conn, sftpClient, err := getSftpClient(user)
if assert.NoError(t, err) {
conn.Close()
sftpClient.Close()
}
user.Password = strings.TrimSpace(u.Password)
_, _, err = getSftpClient(user)
assert.Error(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
}
func TestNamingRules(t *testing.T) {
smtpCfg := smtp.Config{
Host: "127.0.0.1",
@ -8358,7 +8479,7 @@ func TestLoaddata(t *testing.T) {
Timeout: 10,
SkipTLSVerify: true,
Method: http.MethodPost,
Body: `{"event":"{{Event}}","name":"{{Name}}"}`,
Body: `{"event":"{{.Event}}","name":"{{.Name}}"}`,
},
},
}
@ -8604,6 +8725,85 @@ func TestLoaddata(t *testing.T) {
assert.NoError(t, err)
}
func TestLoaddataConvertActions(t *testing.T) {
a1 := dataprovider.BaseEventAction{
Name: xid.New().String(),
Type: dataprovider.ActionTypeEmail,
Options: dataprovider.BaseEventActionOptions{
EmailConfig: dataprovider.EventActionEmailConfig{
Recipients: []string{"failure@example.com"},
Subject: `Failed "{{Event}}" from "{{Name}}"`,
Body: "Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}",
},
},
}
a2 := dataprovider.BaseEventAction{
Name: xid.New().String(),
Type: dataprovider.ActionTypeFilesystem,
Options: dataprovider.BaseEventActionOptions{
FsConfig: dataprovider.EventActionFilesystemConfig{
Type: dataprovider.FilesystemActionRename,
Renames: []dataprovider.RenameConfig{
{
KeyValue: dataprovider.KeyValue{
Key: "/{{VirtualDirPath}}/{{ObjectName}}",
Value: "/{{ObjectName}}_renamed",
},
},
},
},
},
}
backupData := dataprovider.BackupData{
EventActions: []dataprovider.BaseEventAction{a1, a2},
Version: 16,
}
backupContent, err := json.Marshal(backupData)
assert.NoError(t, err)
backupFilePath := filepath.Join(backupsPath, "backup.json")
err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
assert.NoError(t, err)
_, resp, err := httpdtest.Loaddata(backupFilePath, "1", "2", http.StatusOK)
assert.NoError(t, err, string(resp))
// Check that actions are migrated.
action1, _, err := httpdtest.GetEventActionByName(a1.Name, http.StatusOK)
assert.NoError(t, err)
action2, _, err := httpdtest.GetEventActionByName(a2.Name, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, `Failed "{{.Event}}" from "{{.Name}}"`, action1.Options.EmailConfig.Subject)
assert.Equal(t, `Object name: {{.ObjectName}} object type: {{.ObjectType}}, IP: {{.IP}}`, action1.Options.EmailConfig.Body)
assert.Equal(t, `/{{.VirtualDirPath}}/{{.ObjectName}}`, action2.Options.FsConfig.Renames[0].Key)
assert.Equal(t, `/{{.ObjectName}}_renamed`, action2.Options.FsConfig.Renames[0].Value)
// If we restore a backup from the current version actions are not migrated.
backupData = dataprovider.BackupData{
EventActions: []dataprovider.BaseEventAction{a1, a2},
Version: dataprovider.DumpVersion,
}
backupContent, err = json.Marshal(backupData)
assert.NoError(t, err)
backupFilePath = filepath.Join(backupsPath, "backup.json")
err = os.WriteFile(backupFilePath, backupContent, os.ModePerm)
assert.NoError(t, err)
_, resp, err = httpdtest.Loaddata(backupFilePath, "1", "2", http.StatusOK)
assert.NoError(t, err, string(resp))
action1, _, err = httpdtest.GetEventActionByName(a1.Name, http.StatusOK)
assert.NoError(t, err)
action2, _, err = httpdtest.GetEventActionByName(a2.Name, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, `Failed "{{Event}}" from "{{Name}}"`, action1.Options.EmailConfig.Subject)
assert.Equal(t, `Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}`, action1.Options.EmailConfig.Body)
assert.Equal(t, `/{{VirtualDirPath}}/{{ObjectName}}`, action2.Options.FsConfig.Renames[0].Key)
assert.Equal(t, `/{{ObjectName}}_renamed`, action2.Options.FsConfig.Renames[0].Value)
// Cleanup.
_, err = httpdtest.RemoveEventAction(action1, http.StatusOK)
assert.NoError(t, err)
_, err = httpdtest.RemoveEventAction(action2, http.StatusOK)
assert.NoError(t, err)
actions, _, err := httpdtest.GetEventActions(0, 0, http.StatusOK)
assert.NoError(t, err)
assert.Len(t, actions, 0)
}
func TestLoaddataMode(t *testing.T) {
err := dataprovider.UpdateConfigs(nil, "", "", "")
assert.NoError(t, err)
@ -8661,7 +8861,7 @@ func TestLoaddataMode(t *testing.T) {
Timeout: 10,
SkipTLSVerify: true,
Method: http.MethodPost,
Body: `{"event":"{{Event}}","name":"{{Name}}"}`,
Body: `{"event":"{{.Event}}","name":"{{.Name}}"}`,
},
},
}
@ -20874,13 +21074,6 @@ func TestWebAdminBasicMock(t *testing.T) {
assert.Contains(t, rr.Body.String(), util.I18nError500Message)
form.Set("default_users_expiration", "10")
req, _ = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
req.RemoteAddr = defaultRemoteAddr
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
setJWTCookieForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
form.Set("password", admin.Password)
req, _ = http.NewRequest(http.MethodPost, webAdminPath, bytes.NewBuffer([]byte(form.Encode())))
req.RemoteAddr = defaultRemoteAddr
@ -23834,7 +24027,7 @@ func TestWebEventAction(t *testing.T) {
Value: "value1",
},
},
Body: `{"event":"{{Event}}","name":"{{Name}}"}`,
Body: `{"event":"{{.Event}}","name":"{{.Name}}"}`,
},
},
}
@ -23953,12 +24146,12 @@ func TestWebEventAction(t *testing.T) {
form.Del("http_headers[0][http_header_key]")
form.Del("http_headers[0][http_header_val]")
form.Set("multipart_body[0][http_part_name]", "part1")
form.Set("multipart_body[0][http_part_file]", "{{VirtualPath}}")
form.Set("multipart_body[0][http_part_file]", "{{.VirtualPath}}")
form.Set("multipart_body[0][http_part_body]", "")
form.Set("multipart_body[0][http_part_headers]", "X-MyHeader: a:b,c")
form.Set("multipart_body[12][http_part_name]", "part2")
form.Set("multipart_body[12][http_part_headers]", "Content-Type:application/json \r\n")
form.Set("multipart_body[12][http_part_body]", "{{ObjectData}}")
form.Set("multipart_body[12][http_part_body]", "{{.ObjectData}}")
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name),
bytes.NewBuffer([]byte(form.Encode())))
assert.NoError(t, err)
@ -23975,12 +24168,12 @@ func TestWebEventAction(t *testing.T) {
assert.Equal(t, 0, dbAction.Options.HTTPConfig.Timeout)
if assert.Len(t, dbAction.Options.HTTPConfig.Parts, 2) {
assert.Equal(t, "part1", dbAction.Options.HTTPConfig.Parts[0].Name)
assert.Equal(t, "/{{VirtualPath}}", dbAction.Options.HTTPConfig.Parts[0].Filepath)
assert.Equal(t, "/{{.VirtualPath}}", dbAction.Options.HTTPConfig.Parts[0].Filepath)
assert.Empty(t, dbAction.Options.HTTPConfig.Parts[0].Body)
assert.Equal(t, "X-MyHeader", dbAction.Options.HTTPConfig.Parts[0].Headers[0].Key)
assert.Equal(t, "a:b,c", dbAction.Options.HTTPConfig.Parts[0].Headers[0].Value)
assert.Equal(t, "part2", dbAction.Options.HTTPConfig.Parts[1].Name)
assert.Equal(t, "{{ObjectData}}", dbAction.Options.HTTPConfig.Parts[1].Body)
assert.Equal(t, "{{.ObjectData}}", dbAction.Options.HTTPConfig.Parts[1].Body)
assert.Empty(t, dbAction.Options.HTTPConfig.Parts[1].Filepath)
assert.Equal(t, "Content-Type", dbAction.Options.HTTPConfig.Parts[1].Headers[0].Key)
assert.Equal(t, "application/json", dbAction.Options.HTTPConfig.Parts[1].Headers[0].Value)

View file

@ -2395,10 +2395,59 @@ func TestDbTokenManager(t *testing.T) {
dbTokenManager.Cleanup()
isInvalidated = dbTokenManager.Get(testToken)
assert.True(t, isInvalidated)
err := dataprovider.DeleteSharedSession(key)
err := dataprovider.DeleteSharedSession(key, dataprovider.SessionTypeInvalidToken)
assert.NoError(t, err)
}
func TestDatabaseSharedSessions(t *testing.T) {
if !isSharedProviderSupported() {
t.Skip("this test it is not available with this provider")
}
session1 := dataprovider.Session{
Key: "1",
Data: map[string]string{"a": "b"},
Type: dataprovider.SessionTypeOIDCAuth,
Timestamp: 10,
}
err := dataprovider.AddSharedSession(session1)
assert.NoError(t, err)
// Adding another session with the same key but a different type should work
session2 := session1
session2.Type = dataprovider.SessionTypeOIDCToken
err = dataprovider.AddSharedSession(session2)
assert.NoError(t, err)
err = dataprovider.DeleteSharedSession(session1.Key, dataprovider.SessionTypeInvalidToken)
assert.ErrorIs(t, err, util.ErrNotFound)
_, err = dataprovider.GetSharedSession(session1.Key, dataprovider.SessionTypeResetCode)
assert.ErrorIs(t, err, util.ErrNotFound)
session1Get, err := dataprovider.GetSharedSession(session1.Key, dataprovider.SessionTypeOIDCAuth)
assert.NoError(t, err)
assert.Equal(t, session1.Timestamp, session1Get.Timestamp)
var stored map[string]string
err = json.Unmarshal(session1Get.Data.([]byte), &stored)
assert.NoError(t, err)
assert.Equal(t, session1.Data, stored)
session1.Timestamp = 20
session1.Data = map[string]string{"c": "d"}
err = dataprovider.AddSharedSession(session1)
assert.NoError(t, err)
session1Get, err = dataprovider.GetSharedSession(session1.Key, dataprovider.SessionTypeOIDCAuth)
assert.NoError(t, err)
assert.Equal(t, session1.Timestamp, session1Get.Timestamp)
stored = make(map[string]string)
err = json.Unmarshal(session1Get.Data.([]byte), &stored)
assert.NoError(t, err)
assert.Equal(t, session1.Data, stored)
err = dataprovider.DeleteSharedSession(session1.Key, dataprovider.SessionTypeOIDCAuth)
assert.NoError(t, err)
err = dataprovider.DeleteSharedSession(session2.Key, dataprovider.SessionTypeOIDCToken)
assert.NoError(t, err)
_, err = dataprovider.GetSharedSession(session1.Key, dataprovider.SessionTypeOIDCAuth)
assert.ErrorIs(t, err, util.ErrNotFound)
_, err = dataprovider.GetSharedSession(session2.Key, dataprovider.SessionTypeOIDCToken)
assert.ErrorIs(t, err, util.ErrNotFound)
}
func TestAllowedProxyUnixDomainSocket(t *testing.T) {
b := Binding{
Address: filepath.Join(os.TempDir(), "sock"),
@ -3389,11 +3438,15 @@ func TestSecureMiddlewareIntegration(t *testing.T) {
Value: "https",
},
},
STSSeconds: 31536000,
STSIncludeSubdomains: true,
STSPreload: true,
ContentTypeNosniff: true,
CacheControl: "private",
STSSeconds: 31536000,
STSIncludeSubdomains: true,
STSPreload: true,
ContentTypeNosniff: true,
CacheControl: "private",
CrossOriginOpenerPolicy: "same-origin",
CrossOriginResourcePolicy: "same-site",
CrossOriginEmbedderPolicy: "require-corp",
ReferrerPolicy: "no-referrer",
},
},
enableWebAdmin: true,
@ -3448,6 +3501,10 @@ func TestSecureMiddlewareIntegration(t *testing.T) {
assert.NotEmpty(t, r.Header.Get(forwardedHostHeader))
assert.Equal(t, "max-age=31536000; includeSubDomains; preload", rr.Header().Get("Strict-Transport-Security"))
assert.Equal(t, "nosniff", rr.Header().Get("X-Content-Type-Options"))
assert.Equal(t, "require-corp", rr.Header().Get("Cross-Origin-Embedder-Policy"))
assert.Equal(t, "same-origin", rr.Header().Get("Cross-Origin-Opener-Policy"))
assert.Equal(t, "same-site", rr.Header().Get("Cross-Origin-Resource-Policy"))
assert.Equal(t, "no-referrer", rr.Header().Get("Referrer-Policy"))
server.binding.Security.Enabled = false
server.binding.Security.updateProxyHeaders()
@ -3893,6 +3950,116 @@ func TestHTTPSRedirect(t *testing.T) {
assert.NoError(t, err)
}
func TestDisabledAdminLoginMethods(t *testing.T) {
server := httpdServer{
binding: Binding{
Address: "",
Port: 8080,
EnableWebAdmin: true,
EnableWebClient: true,
EnableRESTAPI: true,
DisabledLoginMethods: 20,
},
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)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, defaultAdminUsername, "forgot-password"), nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, path.Join(adminPath, defaultAdminUsername, "reset-password"), nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webAdminLoginPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webAdminResetPwdPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webAdminForgotPwdPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
}
func TestDisabledUserLoginMethods(t *testing.T) {
server := httpdServer{
binding: Binding{
Address: "",
Port: 8080,
EnableWebAdmin: true,
EnableWebClient: true,
EnableRESTAPI: true,
DisabledLoginMethods: 40,
},
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, userTokenPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, userPath+"/user/forgot-password", nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, userPath+"/user/reset-password", nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webClientLoginPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webClientResetPwdPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
rr = httptest.NewRecorder()
req, err = http.NewRequest(http.MethodPost, webClientForgotPwdPath, nil)
require.NoError(t, err)
testServer.Config.Handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)
}
func TestGetLogEventString(t *testing.T) {
assert.Equal(t, "Login failed", getLogEventString(notifier.LogEventTypeLoginFailed))
assert.Equal(t, "Login with non-existent user", getLogEventString(notifier.LogEventTypeLoginNoUser))
@ -4060,6 +4227,39 @@ func TestI18NErrors(t *testing.T) {
assert.Equal(t, `{"a":"b"}`, errI18n.Args())
}
func TestConvertEnabledLoginMethods(t *testing.T) {
b := Binding{
EnabledLoginMethods: 0,
DisabledLoginMethods: 1,
}
b.convertLoginMethods()
assert.Equal(t, 1, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 1
b.convertLoginMethods()
assert.Equal(t, 14, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 2
b.convertLoginMethods()
assert.Equal(t, 13, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 3
b.convertLoginMethods()
assert.Equal(t, 12, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 4
b.convertLoginMethods()
assert.Equal(t, 11, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 7
b.convertLoginMethods()
assert.Equal(t, 8, b.DisabledLoginMethods)
b.DisabledLoginMethods = 0
b.EnabledLoginMethods = 15
b.convertLoginMethods()
assert.Equal(t, 0, b.DisabledLoginMethods)
}
func getCSRFTokenFromBody(body io.Reader) (string, error) {
doc, err := html.Parse(body)
if err != nil {

View file

@ -417,7 +417,7 @@ func checkAPIKeyAuth(tokenAuth *jwtauth.JWTAuth, scope dataprovider.APIKeyScope)
k, err := dataprovider.APIKeyExists(keyID)
if err != nil {
handleDefenderEventLoginFailed(util.GetIPFromRemoteAddress(r.RemoteAddr), util.NewRecordNotFoundError("invalid api key")) //nolint:errcheck
logger.Debug(logSender, "invalid api key %q: %v", apiKey, err)
logger.Debug(logSender, "", "invalid api key %q: %v", apiKey, err)
sendAPIResponse(w, r, errors.New("the provided api key is not valid"), "", http.StatusBadRequest)
return
}
@ -551,7 +551,7 @@ func authenticateUserWithAPIKey(username, keyID string, tokenAuth *jwtauth.JWTAu
return err
}
connectionID := fmt.Sprintf("%v_%v", protocol, xid.New().String())
if err := checkHTTPClientUser(&user, r, connectionID, true); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, true, false); err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, err, r)
return err
}

View file

@ -132,11 +132,11 @@ func (o *dbOAuth2Manager) addPendingAuth(pendingAuth oauth2PendingAuth) {
}
func (o *dbOAuth2Manager) removePendingAuth(state string) {
dataprovider.DeleteSharedSession(state) //nolint:errcheck
dataprovider.DeleteSharedSession(state, dataprovider.SessionTypeOAuth2Auth) //nolint:errcheck
}
func (o *dbOAuth2Manager) getPendingAuth(state string) (oauth2PendingAuth, error) {
session, err := dataprovider.GetSharedSession(state)
session, err := dataprovider.GetSharedSession(state, dataprovider.SessionTypeOAuth2Auth)
if err != nil {
return oauth2PendingAuth{}, errors.New("oauth2: unable to get the auth request for the specified state")
}

View file

@ -86,7 +86,7 @@ func TestDbOAuth2Manager(t *testing.T) {
a, err := m.getPendingAuth(auth.State)
assert.NoError(t, err)
assert.Equal(t, sdkkms.SecretStatusPlain, a.ClientSecret.GetStatus())
session, err := dataprovider.GetSharedSession(auth.State)
session, err := dataprovider.GetSharedSession(auth.State, dataprovider.SessionTypeOAuth2Auth)
assert.NoError(t, err)
authReq := oauth2PendingAuth{}
err = json.Unmarshal(session.Data.([]byte), &authReq)
@ -107,10 +107,10 @@ func TestDbOAuth2Manager(t *testing.T) {
m.addPendingAuth(auth)
_, err = m.getPendingAuth(auth.State)
assert.Error(t, err)
_, err = dataprovider.GetSharedSession(auth.State)
_, err = dataprovider.GetSharedSession(auth.State, dataprovider.SessionTypeOAuth2Auth)
assert.NoError(t, err)
m.cleanup()
_, err = dataprovider.GetSharedSession(auth.State)
_, err = dataprovider.GetSharedSession(auth.State, dataprovider.SessionTypeOAuth2Auth)
assert.Error(t, err)
_, err = m.decodePendingAuthData("not a byte array")
require.Error(t, err)
@ -126,7 +126,7 @@ func TestDbOAuth2Manager(t *testing.T) {
}
auth.ClientSecret.SetStatus(sdkkms.SecretStatusSecretBox)
m.addPendingAuth(auth)
_, err = dataprovider.GetSharedSession(auth.State)
_, err = dataprovider.GetSharedSession(auth.State, dataprovider.SessionTypeOAuth2Auth)
assert.Error(t, err)
asJSON, err := json.Marshal(auth)
assert.NoError(t, err)

View file

@ -396,7 +396,7 @@ func (t *oidcToken) refreshUser(r *http.Request) error {
if err := user.CheckLoginConditions(); err != nil {
return err
}
if err := checkHTTPClientUser(&user, r, xid.New().String(), true); err != nil {
if err := checkHTTPClientUser(&user, r, xid.New().String(), true, false); err != nil {
return err
}
t.Permissions = user.Filters.WebClient
@ -460,7 +460,7 @@ func (t *oidcToken) getUser(r *http.Request) error {
return err
}
connectionID := fmt.Sprintf("%s_%s", common.ProtocolOIDC, xid.New().String())
if err := checkHTTPClientUser(user, r, connectionID, true); err != nil {
if err := checkHTTPClientUser(user, r, connectionID, true, true); err != nil {
updateLoginMetrics(user, dataprovider.LoginMethodIDP, ipAddr, err, r)
return err
}
@ -553,7 +553,7 @@ func (s *httpdServer) oidcTokenAuthenticator(audience tokenAudience) func(next h
}
jwtTokenClaims := jwtTokenClaims{
JwtID: token.Cookie,
Username: token.Username,
Username: dataprovider.ConvertName(token.Username),
Permissions: token.Permissions,
Role: token.TokenRole,
HideUserPageSections: token.HideUserPageSections,

View file

@ -906,7 +906,8 @@ func TestOIDCToken(t *testing.T) {
},
Filters: dataprovider.UserFilters{
BaseUserFilters: sdk.BaseUserFilters{
DeniedProtocols: []string{common.ProtocolHTTP},
DeniedProtocols: []string{common.ProtocolHTTP},
DeniedLoginMethods: []string{dataprovider.LoginMethodPassword},
},
},
}
@ -1181,18 +1182,18 @@ func TestOIDCEvMgrIntegration(t *testing.T) {
// add a special chars to check json replacer
username := `test_"oidc_eventmanager`
u := map[string]any{
"username": "{{Name}}",
"username": "{{.Name}}",
"status": 1,
"home_dir": filepath.Join(os.TempDir(), "{{IDPFieldcustom1.sub}}"),
"home_dir": filepath.Join(os.TempDir(), "{{.IDPFieldcustom1.sub}}"),
"permissions": map[string][]string{
"/": {dataprovider.PermAny},
},
"description": "{{IDPFieldcustom2}}",
"description": "{{.IDPFieldcustom2}}",
}
userTmpl, err := json.Marshal(u)
require.NoError(t, err)
a := map[string]any{
"username": "{{Name}}",
"username": "{{.Name}}",
"status": 1,
"permissions": []string{dataprovider.PermAdminAny},
}
@ -1548,7 +1549,7 @@ func TestOIDCWithLoginFormsDisabled(t *testing.T) {
server := getTestOIDCServer()
server.binding.OIDC.ImplicitRoles = true
server.binding.EnabledLoginMethods = 3
server.binding.DisabledLoginMethods = 12
server.binding.EnableWebAdmin = true
server.binding.EnableWebClient = true
err := server.binding.OIDC.initialize()

View file

@ -167,11 +167,11 @@ func (o *dbOIDCManager) addPendingAuth(pendingAuth oidcPendingAuth) {
}
func (o *dbOIDCManager) removePendingAuth(state string) {
dataprovider.DeleteSharedSession(state) //nolint:errcheck
dataprovider.DeleteSharedSession(state, dataprovider.SessionTypeOIDCAuth) //nolint:errcheck
}
func (o *dbOIDCManager) getPendingAuth(state string) (oidcPendingAuth, error) {
session, err := dataprovider.GetSharedSession(state)
session, err := dataprovider.GetSharedSession(state, dataprovider.SessionTypeOIDCAuth)
if err != nil {
return oidcPendingAuth{}, errors.New("oidc: unable to get the auth request for the specified state")
}
@ -204,7 +204,7 @@ func (o *dbOIDCManager) addToken(token oidcToken) {
}
func (o *dbOIDCManager) removeToken(cookie string) {
dataprovider.DeleteSharedSession(cookie) //nolint:errcheck
dataprovider.DeleteSharedSession(cookie, dataprovider.SessionTypeOIDCToken) //nolint:errcheck
}
func (o *dbOIDCManager) updateTokenUsage(token oidcToken) {
@ -215,7 +215,7 @@ func (o *dbOIDCManager) updateTokenUsage(token oidcToken) {
}
func (o *dbOIDCManager) getToken(cookie string) (oidcToken, error) {
session, err := dataprovider.GetSharedSession(cookie)
session, err := dataprovider.GetSharedSession(cookie, dataprovider.SessionTypeOIDCToken)
if err != nil {
return oidcToken{}, errors.New("oidc: unable to get the token for the specified session")
}

View file

@ -110,7 +110,7 @@ func (m *dbResetCodeManager) Add(code *resetCode) error {
}
func (m *dbResetCodeManager) Get(code string) (*resetCode, error) {
session, err := dataprovider.GetSharedSession(code)
session, err := dataprovider.GetSharedSession(code, dataprovider.SessionTypeResetCode)
if err != nil {
return nil, err
}
@ -132,7 +132,7 @@ func (m *dbResetCodeManager) decodeData(data any) (*resetCode, error) {
}
func (m *dbResetCodeManager) Delete(code string) error {
return dataprovider.DeleteSharedSession(code)
return dataprovider.DeleteSharedSession(code, dataprovider.SessionTypeResetCode)
}
func (m *dbResetCodeManager) Cleanup() {

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !bundle
// +build !bundle
package httpd

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build bundle
// +build bundle
package httpd

View file

@ -244,7 +244,7 @@ func (s *httpdServer) handleWebClientLoginPost(w http.ResponseWriter, r *http.Re
}
protocol := common.ProtocolHTTP
username := strings.TrimSpace(r.Form.Get("username"))
password := strings.TrimSpace(r.Form.Get("password"))
password := r.Form.Get("password")
if username == "" || password == "" {
updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}},
dataprovider.LoginMethodPassword, ipAddr, common.ErrNoCredentials, r)
@ -273,7 +273,7 @@ func (s *httpdServer) handleWebClientLoginPost(w http.ResponseWriter, r *http.Re
return
}
connectionID := fmt.Sprintf("%v_%v", protocol, xid.New().String())
if err := checkHTTPClientUser(&user, r, connectionID, true); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, true, false); err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, err, r)
s.renderClientLoginPage(w, r, util.NewI18nError(err, util.I18nError403Message))
return
@ -312,7 +312,7 @@ func (s *httpdServer) handleWebClientPasswordResetPost(w http.ResponseWriter, r
return
}
connectionID := fmt.Sprintf("%v_%v", getProtocolFromRequest(r), xid.New().String())
if err := checkHTTPClientUser(user, r, connectionID, true); err != nil {
if err := checkHTTPClientUser(user, r, connectionID, true, false); err != nil {
s.renderClientResetPwdPage(w, r, util.NewI18nError(err, util.I18nErrorLoginAfterReset))
return
}
@ -840,7 +840,7 @@ func (s *httpdServer) getUserToken(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, nil, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
if username == "" || password == "" {
if username == "" || strings.TrimSpace(password) == "" {
updateLoginMetrics(&dataprovider.User{BaseUser: sdk.BaseUser{Username: username}},
dataprovider.LoginMethodPassword, ipAddr, common.ErrNoCredentials, r)
w.Header().Set(common.HTTPAuthenticationHeader, basicRealm)
@ -862,7 +862,7 @@ func (s *httpdServer) getUserToken(w http.ResponseWriter, r *http.Request) {
return
}
connectionID := fmt.Sprintf("%v_%v", protocol, xid.New().String())
if err := checkHTTPClientUser(&user, r, connectionID, true); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, true, false); err != nil {
updateLoginMetrics(&user, dataprovider.LoginMethodPassword, ipAddr, err, r)
sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
@ -1039,7 +1039,7 @@ func (s *httpdServer) refreshClientToken(w http.ResponseWriter, r *http.Request,
logger.Debug(logSender, "", "unable to refresh cookie for user %q: %v", user.Username, err)
return
}
if err := checkHTTPClientUser(&user, r, xid.New().String(), true); err != nil {
if err := checkHTTPClientUser(&user, r, xid.New().String(), true, false); err != nil {
logger.Debug(logSender, "", "unable to refresh cookie for user %q: %v", user.Username, err)
return
}
@ -1188,8 +1188,9 @@ func (s *httpdServer) badHostHandler(w http.ResponseWriter, r *http.Request) {
break
}
}
logger.Debug(logSender, "", "the host %q is not allowed", host)
s.sendForbiddenResponse(w, r, util.NewI18nError(
util.NewGenericError(fmt.Sprintf("The host %q is not allowed", host)),
util.NewGenericError(http.StatusText(http.StatusForbidden)),
util.I18nErrorConnectionForbidden,
))
}
@ -1243,17 +1244,20 @@ func (s *httpdServer) initializeRouter() {
s.router.Use(middleware.Recoverer)
if s.binding.Security.Enabled {
secureMiddleware := secure.New(secure.Options{
AllowedHosts: s.binding.Security.AllowedHosts,
AllowedHostsAreRegex: s.binding.Security.AllowedHostsAreRegex,
HostsProxyHeaders: s.binding.Security.HostsProxyHeaders,
SSLProxyHeaders: s.binding.Security.getHTTPSProxyHeaders(),
STSSeconds: s.binding.Security.STSSeconds,
STSIncludeSubdomains: s.binding.Security.STSIncludeSubdomains,
STSPreload: s.binding.Security.STSPreload,
ContentTypeNosniff: s.binding.Security.ContentTypeNosniff,
ContentSecurityPolicy: s.binding.Security.ContentSecurityPolicy,
PermissionsPolicy: s.binding.Security.PermissionsPolicy,
CrossOriginOpenerPolicy: s.binding.Security.CrossOriginOpenerPolicy,
AllowedHosts: s.binding.Security.AllowedHosts,
AllowedHostsAreRegex: s.binding.Security.AllowedHostsAreRegex,
HostsProxyHeaders: s.binding.Security.HostsProxyHeaders,
SSLProxyHeaders: s.binding.Security.getHTTPSProxyHeaders(),
STSSeconds: s.binding.Security.STSSeconds,
STSIncludeSubdomains: s.binding.Security.STSIncludeSubdomains,
STSPreload: s.binding.Security.STSPreload,
ContentTypeNosniff: s.binding.Security.ContentTypeNosniff,
ContentSecurityPolicy: s.binding.Security.ContentSecurityPolicy,
PermissionsPolicy: s.binding.Security.PermissionsPolicy,
CrossOriginOpenerPolicy: s.binding.Security.CrossOriginOpenerPolicy,
CrossOriginResourcePolicy: s.binding.Security.CrossOriginResourcePolicy,
CrossOriginEmbedderPolicy: s.binding.Security.CrossOriginEmbedderPolicy,
ReferrerPolicy: s.binding.Security.ReferrerPolicy,
})
secureMiddleware.SetBadHostHandler(http.HandlerFunc(s.badHostHandler))
if s.binding.Security.CacheControl == "private" {
@ -1295,23 +1299,55 @@ func (s *httpdServer) initializeRouter() {
}
}
if s.enableRESTAPI {
// share API available to external users
s.router.Get(sharesPath+"/{id}", s.downloadFromShare) //nolint:goconst
s.router.Post(sharesPath+"/{id}", s.uploadFilesToShare)
s.router.Post(sharesPath+"/{id}/{name}", s.uploadFileToShare)
s.router.With(compressor.Handler).Get(sharesPath+"/{id}/dirs", s.readBrowsableShareContents)
s.router.Get(sharesPath+"/{id}/files", s.downloadBrowsableSharedFile)
s.setupRESTAPIRoutes()
s.router.Get(tokenPath, s.getToken)
s.router.Post(adminPath+"/{username}/forgot-password", forgotAdminPassword)
s.router.Post(adminPath+"/{username}/reset-password", resetAdminPassword)
s.router.Post(userPath+"/{username}/forgot-password", forgotUserPassword)
s.router.Post(userPath+"/{username}/reset-password", resetUserPassword)
if s.enableWebAdmin || s.enableWebClient {
s.router.Group(func(router chi.Router) {
router.Use(cleanCacheControlMiddleware)
router.Use(compressor.Handler)
serveStaticDir(router, webStaticFilesPath, s.staticFilesPath, true)
})
if s.binding.OIDC.isEnabled() {
s.router.Get(webOIDCRedirectPath, s.handleOIDCRedirect)
}
if s.enableWebClient {
s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webClientLoginPath)
})
s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webClientLoginPath)
})
} else {
s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webAdminLoginPath)
})
s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webAdminLoginPath)
})
}
}
s.setupWebClientRoutes()
s.setupWebAdminRoutes()
}
func (s *httpdServer) setupRESTAPIRoutes() {
if s.enableRESTAPI {
if !s.binding.isAdminTokenEndpointDisabled() {
s.router.Get(tokenPath, s.getToken)
s.router.Post(adminPath+"/{username}/forgot-password", forgotAdminPassword)
s.router.Post(adminPath+"/{username}/reset-password", resetAdminPassword)
}
s.router.Group(func(router chi.Router) {
router.Use(checkNodeToken(s.tokenAuth))
router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeAdmin))
if !s.binding.isAdminAPIKeyAuthDisabled() {
router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeAdmin))
}
router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromHeader))
router.Use(jwtAuthenticatorAPI)
@ -1426,10 +1462,23 @@ func (s *httpdServer) initializeRouter() {
})
})
s.router.Get(userTokenPath, s.getUserToken)
// share API available to external users
s.router.Get(sharesPath+"/{id}", s.downloadFromShare)
s.router.Post(sharesPath+"/{id}", s.uploadFilesToShare)
s.router.Post(sharesPath+"/{id}/{name}", s.uploadFileToShare)
s.router.With(compressor.Handler).Get(sharesPath+"/{id}/dirs", s.readBrowsableShareContents)
s.router.Get(sharesPath+"/{id}/files", s.downloadBrowsableSharedFile)
if !s.binding.isUserTokenEndpointDisabled() {
s.router.Get(userTokenPath, s.getUserToken)
s.router.Post(userPath+"/{username}/forgot-password", forgotUserPassword)
s.router.Post(userPath+"/{username}/reset-password", resetUserPassword)
}
s.router.Group(func(router chi.Router) {
router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeUser))
if !s.binding.isUserAPIKeyAuthDisabled() {
router.Use(checkAPIKeyAuth(s.tokenAuth, dataprovider.APIKeyScopeUser))
}
router.Use(jwtauth.Verify(s.tokenAuth, jwtauth.TokenFromHeader))
router.Use(jwtAuthenticatorAPIUser)
@ -1495,39 +1544,6 @@ func (s *httpdServer) initializeRouter() {
})
}
}
if s.enableWebAdmin || s.enableWebClient {
s.router.Group(func(router chi.Router) {
router.Use(cleanCacheControlMiddleware)
router.Use(compressor.Handler)
serveStaticDir(router, webStaticFilesPath, s.staticFilesPath, true)
})
if s.binding.OIDC.isEnabled() {
s.router.Get(webOIDCRedirectPath, s.handleOIDCRedirect)
}
if s.enableWebClient {
s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webClientLoginPath)
})
s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webClientLoginPath)
})
} else {
s.router.Get(webRootPath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webAdminLoginPath)
})
s.router.Get(webBasePath, func(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
s.redirectToWebPath(w, r, webAdminLoginPath)
})
}
}
s.setupWebClientRoutes()
s.setupWebAdminRoutes()
}
func (s *httpdServer) setupWebClientRoutes() {

View file

@ -86,7 +86,7 @@ func (m *dbTokenManager) Add(token string, expiresAt time.Time) {
func (m *dbTokenManager) Get(token string) bool {
key := m.getKey(token)
_, err := dataprovider.GetSharedSession(key)
_, err := dataprovider.GetSharedSession(key, dataprovider.SessionTypeInvalidToken)
return err == nil
}

View file

@ -27,23 +27,20 @@ import (
)
const (
pageMFATitle = "Two-factor authentication"
pageTwoFactorTitle = "Two-Factor authentication"
pageTwoFactorRecoveryTitle = "Two-Factor recovery"
webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
redactedSecret = "[**redacted**]"
csrfFormToken = "_form_token"
csrfHeaderToken = "X-CSRF-TOKEN"
templateCommonDir = "common"
templateTwoFactor = "twofactor.html"
templateTwoFactorRecovery = "twofactor-recovery.html"
templateForgotPassword = "forgot-password.html"
templateResetPassword = "reset-password.html"
templateChangePwd = "changepassword.html"
templateMessage = "message.html"
templateCommonBase = "base.html"
templateCommonBaseLogin = "baselogin.html"
templateCommonLogin = "login.html"
webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
redactedSecret = "[**redacted**]"
csrfFormToken = "_form_token"
csrfHeaderToken = "X-CSRF-TOKEN"
templateCommonDir = "common"
templateTwoFactor = "twofactor.html"
templateTwoFactorRecovery = "twofactor-recovery.html"
templateForgotPassword = "forgot-password.html"
templateResetPassword = "reset-password.html"
templateChangePwd = "changepassword.html"
templateMessage = "message.html"
templateCommonBase = "base.html"
templateCommonBaseLogin = "baselogin.html"
templateCommonLogin = "login.html"
)
var (

View file

@ -752,7 +752,7 @@ func (s *httpdServer) renderResetPwdPage(w http.ResponseWriter, r *http.Request,
func (s *httpdServer) renderTwoFactorPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
data := twoFactorPage{
commonBasePage: getCommonBasePage(r),
Title: pageTwoFactorTitle,
Title: util.I18n2FATitle,
CurrentURL: webAdminTwoFactorPath,
Error: err,
CSRFToken: createCSRFToken(w, r, s.csrfTokenAuth, "", webBaseAdminPath),
@ -766,7 +766,7 @@ func (s *httpdServer) renderTwoFactorPage(w http.ResponseWriter, r *http.Request
func (s *httpdServer) renderTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
data := twoFactorPage{
commonBasePage: getCommonBasePage(r),
Title: pageTwoFactorRecoveryTitle,
Title: util.I18n2FATitle,
CurrentURL: webAdminTwoFactorRecoveryPath,
Error: err,
CSRFToken: createCSRFToken(w, r, s.csrfTokenAuth, "", webBaseAdminPath),
@ -778,7 +778,7 @@ func (s *httpdServer) renderTwoFactorRecoveryPage(w http.ResponseWriter, r *http
func (s *httpdServer) renderMFAPage(w http.ResponseWriter, r *http.Request) {
data := mfaPage{
basePage: s.getBasePageData(pageMFATitle, webAdminMFAPath, w, r),
basePage: s.getBasePageData(util.I18n2FATitle, webAdminMFAPath, w, r),
TOTPConfigs: mfa.GetAvailableTOTPConfigNames(),
GenerateTOTPURL: webAdminTOTPGeneratePath,
ValidateTOTPURL: webAdminTOTPValidatePath,
@ -3111,7 +3111,11 @@ func (s *httpdServer) handleWebAddAdminPost(w http.ResponseWriter, r *http.Reque
s.renderAddUpdateAdminPage(w, r, &admin, err, true)
return
}
if admin.Password == "" && s.binding.isWebAdminLoginFormDisabled() {
if admin.Password == "" {
// Administrators can be used with OpenID Connect or for authentication
// via API key, in these cases the password is not necessary, we create
// a non-usable one. This feature is only useful for WebAdmin, in REST
// API you can create an unusable password externally.
admin.Password = util.GenerateUniqueID()
}
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)

View file

@ -145,16 +145,18 @@ type filesPage struct {
Error *util.I18nError
Paths []dirMapping
QuotaUsage *userQuotaUsage
KeepAliveInterval int
}
type shareLoginPage struct {
commonBasePage
CurrentURL string
Error *util.I18nError
CSRFToken string
Title string
Branding UIBranding
Languages []string
CurrentURL string
Error *util.I18nError
CSRFToken string
Title string
Branding UIBranding
Languages []string
CheckRedirect bool
}
type shareDownloadPage struct {
@ -598,6 +600,7 @@ func (s *httpdServer) renderShareLoginPage(w http.ResponseWriter, r *http.Reques
CSRFToken: createCSRFToken(w, r, s.csrfTokenAuth, xid.New().String(), webBaseClientPath),
Branding: s.binding.webClientBranding(),
Languages: s.binding.languages(),
CheckRedirect: false,
}
renderClientTemplate(w, templateShareLogin, data)
}
@ -642,7 +645,7 @@ func (s *httpdServer) renderClientNotFoundPage(w http.ResponseWriter, r *http.Re
func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
data := twoFactorPage{
commonBasePage: getCommonBasePage(r),
Title: pageTwoFactorTitle,
Title: util.I18n2FATitle,
CurrentURL: webClientTwoFactorPath,
Error: err,
CSRFToken: createCSRFToken(w, r, s.csrfTokenAuth, "", webBaseClientPath),
@ -659,7 +662,7 @@ func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.R
func (s *httpdServer) renderClientTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, err *util.I18nError) {
data := twoFactorPage{
commonBasePage: getCommonBasePage(r),
Title: pageTwoFactorRecoveryTitle,
Title: util.I18n2FATitle,
CurrentURL: webClientTwoFactorRecoveryPath,
Error: err,
CSRFToken: createCSRFToken(w, r, s.csrfTokenAuth, "", webBaseClientPath),
@ -781,6 +784,7 @@ func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Reque
CanCopy: false,
Paths: getDirMapping(dirName, currentURL),
QuotaUsage: newUserQuotaUsage(&dataprovider.User{}),
KeepAliveInterval: int(cookieRefreshThreshold / time.Millisecond),
}
renderClientTemplate(w, templateClientFiles, data)
}
@ -837,6 +841,7 @@ func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, di
ShareUploadBaseURL: "",
Paths: getDirMapping(dirName, webClientFilesPath),
QuotaUsage: newUserQuotaUsage(user),
KeepAliveInterval: int(cookieRefreshThreshold / time.Millisecond),
}
renderClientTemplate(w, templateClientFiles, data)
}
@ -897,7 +902,7 @@ func (s *httpdServer) handleWebClientDownloadZip(w http.ResponseWriter, r *http.
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%v_%v", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
s.renderClientForbiddenPage(w, r, err)
return
}
@ -1187,7 +1192,7 @@ func (s *httpdServer) handleClientGetDirContents(w http.ResponseWriter, r *http.
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%s_%s", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nErrorDirList403), http.StatusForbidden)
return
}
@ -1276,7 +1281,7 @@ func (s *httpdServer) handleClientGetFiles(w http.ResponseWriter, r *http.Reques
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%v_%v", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
s.renderClientForbiddenPage(w, r, err)
return
}
@ -1337,7 +1342,7 @@ func (s *httpdServer) handleClientEditFile(w http.ResponseWriter, r *http.Reques
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%v_%v", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
s.renderClientForbiddenPage(w, r, err)
return
}
@ -1833,7 +1838,7 @@ func (s *httpdServer) handleClientGetPDF(w http.ResponseWriter, r *http.Request)
connID := xid.New().String()
protocol := getProtocolFromRequest(r)
connectionID := fmt.Sprintf("%v_%v", protocol, connID)
if err := checkHTTPClientUser(&user, r, connectionID, false); err != nil {
if err := checkHTTPClientUser(&user, r, connectionID, false, false); err != nil {
s.renderClientForbiddenPage(w, r, err)
return
}

View file

@ -93,7 +93,7 @@ func (m *dbTaskManager) Add(data webTaskData) error {
}
func (m *dbTaskManager) Get(ID string) (webTaskData, error) {
sess, err := dataprovider.GetSharedSession(ID)
sess, err := dataprovider.GetSharedSession(ID, dataprovider.SessionTypeWebTask)
if err != nil {
return webTaskData{}, err
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build linux
// +build linux
package logger

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !linux
// +build !linux
package logger

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !nometrics
// +build !nometrics
// Package metric provides Prometheus metrics support
package metric

View file

@ -1,5 +1,18 @@
// Copyright (C) 2019 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build nometrics
// +build nometrics
package metric

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build awscontainer
// +build awscontainer
package service

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !awscontainer
// +build !awscontainer
package service

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !noportable
// +build !noportable
package service

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !windows
// +build !windows
package service

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !windows
// +build !windows
package sftpd

View file

@ -831,7 +831,7 @@ func TestRsyncOptions(t *testing.T) {
user := dataprovider.User{
BaseUser: sdk.BaseUser{
Permissions: permissions,
HomeDir: os.TempDir(),
HomeDir: filepath.Clean(os.TempDir()),
},
}
conn := &Connection{
@ -844,7 +844,7 @@ func TestRsyncOptions(t *testing.T) {
}
cmd, err := sshCmd.getSystemCommand()
assert.NoError(t, err)
assert.True(t, slices.Contains(cmd.cmd.Args, "--safe-links"),
assert.Equal(t, []string{"rsync", "--server", "-vlogDtprze.iLsfxC", "--safe-links", ".", user.HomeDir + string(os.PathSeparator)}, cmd.cmd.Args,
"--safe-links must be added if the user has the create symlinks permission")
permissions["/"] = []string{dataprovider.PermDownload, dataprovider.PermUpload, dataprovider.PermCreateDirs,
@ -854,6 +854,12 @@ func TestRsyncOptions(t *testing.T) {
conn = &Connection{
BaseConnection: common.NewBaseConnection("", common.ProtocolSFTP, "", "", user),
}
sshCmd = sshCommand{
command: "rsync",
connection: conn,
}
_, err = sshCmd.getSystemCommand()
assert.Error(t, err)
sshCmd = sshCommand{
command: "rsync",
connection: conn,
@ -861,8 +867,8 @@ func TestRsyncOptions(t *testing.T) {
}
cmd, err = sshCmd.getSystemCommand()
assert.NoError(t, err)
assert.True(t, slices.Contains(cmd.cmd.Args, "--munge-links"),
"--munge-links must be added if the user has the create symlinks permission")
assert.Equal(t, []string{"rsync", "--server", "-vlogDtprze.iLsfxC", "--munge-links", ".", user.HomeDir + string(os.PathSeparator)}, cmd.cmd.Args,
"--munge-links must be added if the user hasn't the create symlinks permission")
sshCmd.connection.User.VirtualFolders = append(sshCmd.connection.User.VirtualFolders, vfs.VirtualFolder{
BaseVirtualFolder: vfs.BaseVirtualFolder{
@ -2241,3 +2247,43 @@ func TestAuthenticationErrors(t *testing.T) {
assert.ErrorIs(t, err, sftpAuthError)
assert.NotErrorIs(t, err, util.ErrNotFound)
}
func TestRsyncArguments(t *testing.T) {
assert.False(t, canAcceptRsyncArgs(nil))
args := []string{"-e", "--server"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.iLsfxCIvu", ".", "."}
assert.True(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "--server", "-vlogDtpre.iLsfxCIvu", ".", "."}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "..", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.iLsfxCIvu", ".", "."}
assert.True(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.iLsfxCIvu", "--delete", ".", "/"}
assert.True(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.iLsfxCIvu", "--delete", ".", "/"}
assert.True(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.iLsfxCIvu", "--delete", "/", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.iLsfxCIvu", ".", "path1", "path2"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.iLsfxCIvu", "."}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--sender", "-vlogDtpre.iLsfxCIvu", "--delete", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.", "--delete", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-vlogDtpre.", "--delete", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "--sender", "-e.iLsfxCIvu", ".", "/"}
assert.True(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.iLsfxCIvu", "--delete", "/"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.iLsfxCIvu", "--delete", "--safe-links"}
assert.False(t, canAcceptRsyncArgs(args))
args = []string{"--server", "-vlogDtpre.iLsfxCIvu", "--unsupported-option", ".", "/"}
assert.False(t, canAcceptRsyncArgs(args))
}

View file

@ -13,7 +13,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//go:build !windows
// +build !windows
package sftpd

View file

@ -130,7 +130,7 @@ type Configuration struct {
// preference order.
KexAlgorithms []string `json:"kex_algorithms" mapstructure:"kex_algorithms"`
// MinDHGroupExchangeKeySize defines the minimum key size to allow for the
// key exchanges when using diffie-ellman-group-exchange-sha1 or sha256 key
// key exchanges when using diffie-hellman-group-exchange-sha1 or sha256 key
// exchange algorithms.
MinDHGroupExchangeKeySize int `json:"min_dh_group_exchange_key_size" mapstructure:"min_dh_group_exchange_key_size"`
// Ciphers specifies the ciphers allowed
@ -327,7 +327,7 @@ func (c *Configuration) Initialize(configDir string) error {
}
ssh.SetDHKexServerMinBits(uint32(c.MinDHGroupExchangeKeySize))
logger.Debug(logSender, "", "minimum key size allowed for diffie-ellman-group-exchange: %d",
logger.Debug(logSender, "", "minimum key size allowed for diffie-hellman-group-exchange: %d",
ssh.GetDHKexServerMinBits())
sftp.SetSFTPExtensions(sftpExtensions...) //nolint:errcheck // we configure valid SFTP Extensions so we cannot get an error
sftp.MaxFilelist = 250
@ -592,7 +592,7 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
conn.SetDeadline(time.Time{}) //nolint:errcheck
go ssh.DiscardRequests(reqs)
defer conn.Close()
defer sconn.Close()
var user dataprovider.User
@ -615,7 +615,7 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
dataprovider.UpdateLastLogin(&user)
sshConnection := common.NewSSHConnection(connectionID, conn)
sshConnection := common.NewSSHConnection(connectionID, sconn)
common.Connections.AddSSHConnection(sshConnection)
defer common.Connections.RemoveSSHConnection(connectionID)
@ -857,11 +857,12 @@ func (c *Configuration) generateDefaultHostKeys(configDir string) error {
if _, err = os.Stat(autoFile); errors.Is(err, fs.ErrNotExist) {
logger.Info(logSender, "", "No host keys configured and %q does not exist; try to create a new host key", autoFile)
logger.InfoToConsole("No host keys configured and %q does not exist; try to create a new host key", autoFile)
if k == defaultPrivateRSAKeyName {
switch k {
case defaultPrivateRSAKeyName:
err = util.GenerateRSAKeys(autoFile)
} else if k == defaultPrivateECDSAKeyName {
case defaultPrivateECDSAKeyName:
err = util.GenerateECDSAKeys(autoFile)
} else {
default:
err = util.GenerateEd25519Keys(autoFile)
}
if err != nil {

View file

@ -3011,13 +3011,46 @@ func TestPreLoginScript(t *testing.T) {
err = dataprovider.Initialize(providerConf, configDir, true)
assert.NoError(t, err)
mappedPath := filepath.Join(os.TempDir(), "vdir")
folderName := filepath.Base(mappedPath)
f := vfs.BaseVirtualFolder{
Name: folderName,
MappedPath: mappedPath,
FsConfig: vfs.Filesystem{
Provider: sdk.CryptedFilesystemProvider,
CryptConfig: vfs.CryptFsConfig{
Passphrase: kms.NewPlainSecret(defaultPassword),
},
},
}
_, _, err = httpdtest.AddFolder(f, http.StatusCreated)
assert.NoError(t, err)
folderMountPath := "/vpath"
u.VirtualFolders = append(u.VirtualFolders, vfs.VirtualFolder{
BaseVirtualFolder: vfs.BaseVirtualFolder{
Name: folderName,
},
VirtualPath: folderMountPath,
})
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
assert.NoError(t, err)
conn, client, err := getSftpClient(u, usePubKey)
if assert.NoError(t, err) {
defer conn.Close()
defer client.Close()
assert.NoError(t, checkBasicSFTP(client))
testFilePath := filepath.Join(homeBasePath, testFileName)
testData := []byte("test data")
err = os.WriteFile(testFilePath, testData, 0666)
assert.NoError(t, err)
err = sftpUploadFile(testFilePath, path.Join(folderMountPath, testFileName), int64(len(testData)), client)
assert.NoError(t, err)
info, err := os.Stat(filepath.Join(mappedPath, testFileName))
if assert.NoError(t, err) {
assert.Greater(t, info.Size(), int64(len(testData)))
}
}
err = os.WriteFile(preLoginPath, getPreLoginScriptContent(user, true), os.ModePerm)
assert.NoError(t, err)
@ -3054,6 +3087,10 @@ func TestPreLoginScript(t *testing.T) {
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveFolder(vfs.BaseVirtualFolder{Name: folderName}, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(mappedPath)
assert.NoError(t, err)
err = dataprovider.Close()
assert.NoError(t, err)
err = config.LoadConfig(configDir, "")

Some files were not shown because too many files have changed in this diff Show more