Compare commits
46 commits
Author | SHA1 | Date | |
---|---|---|---|
|
66c14bebd8 | ||
|
b4ef24e23d | ||
|
e617dc9c0a | ||
|
80fb56bc48 | ||
|
b65fc0bdc2 | ||
|
cb98f8fd6d | ||
|
cbef217cfa | ||
|
4a34ae6662 | ||
|
3ff7acc1b4 | ||
|
fc648454df | ||
|
836b36b816 | ||
|
2d19817431 | ||
|
df84c42e7e | ||
|
c304143eb3 | ||
|
d2acc6f5c1 | ||
|
531ed852f5 | ||
|
303a723b04 | ||
|
88bfdb9910 | ||
|
d3a523ba13 | ||
|
665016ed1e | ||
|
97d5680d1e | ||
|
e7866047aa | ||
|
5f313cc6be | ||
|
3c2c703408 | ||
|
78a399eed4 | ||
|
e6d434654d | ||
|
d34446e6e9 | ||
|
2da19ef233 | ||
|
b34bc2b818 | ||
|
378995147b | ||
|
6b995db864 | ||
|
371012a46e | ||
|
d3d788c8d0 | ||
|
756b122ab8 | ||
|
e244ba37b2 | ||
|
5610b98d19 | ||
|
b3ca20b5e6 | ||
|
d0b6ca8d2f | ||
|
550158ff4b | ||
|
14a3803c8f | ||
|
ca4da2f64e | ||
|
049c2b7430 | ||
|
7fd5558400 | ||
|
b60255752f | ||
|
37f79650c8 | ||
|
8988d6542b |
274 changed files with 7496 additions and 1272 deletions
78
.github/workflows/development.yml
vendored
78
.github/workflows/development.yml
vendored
|
@ -2,7 +2,7 @@ name: CI
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [2.3.x]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
@ -32,7 +32,7 @@ jobs:
|
|||
- name: Build for Linux/macOS x86_64
|
||||
if: startsWith(matrix.os, 'windows-') != true
|
||||
run: |
|
||||
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
cd tests/eventsearcher
|
||||
go build -trimpath -ldflags "-s -w" -o eventsearcher
|
||||
cd -
|
||||
|
@ -42,12 +42,12 @@ jobs:
|
|||
|
||||
- name: Build for macOS arm64
|
||||
if: startsWith(matrix.os, 'macos-') == true
|
||||
run: CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 SDKROOT=$(xcrun --sdk macosx --show-sdk-path) go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
|
||||
run: CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 SDKROOT=$(xcrun --sdk macosx --show-sdk-path) go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
|
||||
|
||||
- name: Build for Windows
|
||||
if: startsWith(matrix.os, 'windows-')
|
||||
run: |
|
||||
$GIT_COMMIT = (git describe --always --dirty) | Out-String
|
||||
$GIT_COMMIT = (git describe --always --abbrev=8 --dirty) | Out-String
|
||||
$DATE_TIME = ([datetime]::Now.ToUniversalTime().toString("yyyy-MM-ddTHH:mm:ssZ")) | Out-String
|
||||
$LATEST_TAG = ((git describe --tags $(git rev-list --tags --max-count=1)) | Out-String).Trim()
|
||||
$REV_LIST=$LATEST_TAG+"..HEAD"
|
||||
|
@ -283,6 +283,21 @@ jobs:
|
|||
ports:
|
||||
- 3307:3306
|
||||
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: mysql
|
||||
MYSQL_DATABASE: sftpgo
|
||||
MYSQL_USER: sftpgo
|
||||
MYSQL_PASSWORD: sftpgo
|
||||
options: >-
|
||||
--health-cmd "mysqladmin status -h 127.0.0.1 -P 3306 -u root -p$MYSQL_ROOT_PASSWORD"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 6
|
||||
ports:
|
||||
- 3308:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
@ -312,6 +327,17 @@ jobs:
|
|||
SFTPGO_DATA_PROVIDER__PASSWORD: postgres
|
||||
|
||||
- name: Run tests using MySQL provider
|
||||
run: |
|
||||
go test -v -p 1 -timeout 15m ./... -covermode=atomic
|
||||
env:
|
||||
SFTPGO_DATA_PROVIDER__DRIVER: mysql
|
||||
SFTPGO_DATA_PROVIDER__NAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__HOST: localhost
|
||||
SFTPGO_DATA_PROVIDER__PORT: 3308
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD: sftpgo
|
||||
|
||||
- name: Run tests using MariaDB provider
|
||||
run: |
|
||||
go test -v -p 1 -timeout 15m ./... -covermode=atomic
|
||||
env:
|
||||
|
@ -321,6 +347,7 @@ jobs:
|
|||
SFTPGO_DATA_PROVIDER__PORT: 3307
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD: sftpgo
|
||||
SFTPGO_DATA_PROVIDER__SQL_TABLES_PREFIX: prefix_
|
||||
|
||||
- name: Run tests using CockroachDB provider
|
||||
run: |
|
||||
|
@ -336,15 +363,17 @@ jobs:
|
|||
SFTPGO_DATA_PROVIDER__PORT: 26257
|
||||
SFTPGO_DATA_PROVIDER__USERNAME: root
|
||||
SFTPGO_DATA_PROVIDER__PASSWORD:
|
||||
SFTPGO_DATA_PROVIDER__SQL_TABLES_PREFIX: prefix_
|
||||
|
||||
build-linux-packages:
|
||||
name: Build Linux packages
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
go: 1.18
|
||||
distro: ubuntu:18.04
|
||||
go: latest
|
||||
go-arch: amd64
|
||||
- arch: aarch64
|
||||
distro: ubuntu18.04
|
||||
|
@ -362,16 +391,36 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
if: ${{ matrix.arch == 'amd64' }}
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Get commit SHA
|
||||
id: get_commit
|
||||
run: echo ::set-output name=COMMIT::${GITHUB_SHA::8}
|
||||
shell: bash
|
||||
|
||||
- name: Build on amd64
|
||||
if: ${{ matrix.arch == 'amd64' }}
|
||||
run: |
|
||||
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
echo '#!/bin/bash' > build.sh
|
||||
echo '' >> build.sh
|
||||
echo 'set -e' >> build.sh
|
||||
echo 'apt-get update -q -y' >> build.sh
|
||||
echo 'apt-get install -q -y curl gcc' >> build.sh
|
||||
if [ ${{ matrix.go }} == 'latest' ]
|
||||
then
|
||||
echo 'GO_VERSION=$(curl -L https://go.dev/VERSION?m=text)' >> build.sh
|
||||
else
|
||||
echo 'GO_VERSION=${{ matrix.go }}' >> build.sh
|
||||
fi
|
||||
echo 'GO_DOWNLOAD_ARCH=${{ matrix.go-arch }}' >> build.sh
|
||||
echo 'curl --retry 5 --retry-delay 2 --connect-timeout 10 -o go.tar.gz -L https://go.dev/dl/${GO_VERSION}.linux-${GO_DOWNLOAD_ARCH}.tar.gz' >> build.sh
|
||||
echo 'tar -C /usr/local -xzf go.tar.gz' >> build.sh
|
||||
echo 'export PATH=$PATH:/usr/local/go/bin' >> build.sh
|
||||
echo 'go version' >> build.sh
|
||||
echo 'cd /usr/local/src' >> build.sh
|
||||
echo 'go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${{ steps.get_commit.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo' >> build.sh
|
||||
|
||||
chmod 755 build.sh
|
||||
docker run --rm --name ubuntu-build --mount type=bind,source=`pwd`,target=/usr/local/src ${{ matrix.distro }} /usr/local/src/build.sh
|
||||
mkdir -p output/{init,bash_completion,zsh_completion}
|
||||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
|
@ -398,7 +447,7 @@ jobs:
|
|||
shell: /bin/bash
|
||||
install: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y curl gcc git
|
||||
apt-get install -q -y curl gcc
|
||||
if [ ${{ matrix.go }} == 'latest' ]
|
||||
then
|
||||
GO_VERSION=$(curl -L https://go.dev/VERSION?m=text)
|
||||
|
@ -414,11 +463,12 @@ jobs:
|
|||
tar -C /usr/local -xzf go.tar.gz
|
||||
run: |
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
go version
|
||||
if [ ${{ matrix.arch}} == 'armv7' ]
|
||||
then
|
||||
export GOARM=7
|
||||
fi
|
||||
go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${{ steps.get_commit.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
mkdir -p output/{init,bash_completion,zsh_completion}
|
||||
cp sftpgo.json output/
|
||||
cp -r templates output/
|
||||
|
|
21
.github/workflows/docker.yml
vendored
21
.github/workflows/docker.yml
vendored
|
@ -5,7 +5,7 @@ on:
|
|||
# - cron: '0 4 * * *' # everyday at 4:00 AM UTC
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 2.3.x
|
||||
tags:
|
||||
- v*
|
||||
pull_request:
|
||||
|
@ -28,6 +28,9 @@ jobs:
|
|||
- os: ubuntu-latest
|
||||
docker_pkg: distroless
|
||||
optional_deps: false
|
||||
- os: ubuntu-latest
|
||||
docker_pkg: debian-plugins
|
||||
optional_deps: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
@ -64,6 +67,9 @@ jobs:
|
|||
VERSION="${VERSION}-distroless"
|
||||
VERSION_SLIM="${VERSION}-slim"
|
||||
DOCKERFILE=Dockerfile.distroless
|
||||
elif [[ $DOCKER_PKG == debian-plugins ]]; then
|
||||
VERSION="${VERSION}-plugins"
|
||||
VERSION_SLIM="${VERSION}-slim"
|
||||
fi
|
||||
DOCKER_IMAGES=("drakkan/sftpgo" "ghcr.io/drakkan/sftpgo")
|
||||
TAGS="${DOCKER_IMAGES[0]}:${VERSION}"
|
||||
|
@ -89,6 +95,13 @@ jobs:
|
|||
fi
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:distroless"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:distroless-slim"
|
||||
elif [[ $DOCKER_PKG == debian-plugins ]]; then
|
||||
if [[ -n $MAJOR && -n $MINOR ]]; then
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR}-plugins,${DOCKER_IMAGE}:${MAJOR}-plugins"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${MINOR}-plugins-slim,${DOCKER_IMAGE}:${MAJOR}-plugins-slim"
|
||||
fi
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:plugins"
|
||||
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:plugins-slim"
|
||||
else
|
||||
if [[ -n $MAJOR && -n $MINOR ]]; then
|
||||
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR}-alpine,${DOCKER_IMAGE}:${MAJOR}-alpine"
|
||||
|
@ -109,6 +122,11 @@ jobs:
|
|||
echo ::set-output name=tags::${TAGS_SLIM}
|
||||
echo ::set-output name=full::false
|
||||
fi
|
||||
if [[ $DOCKER_PKG == debian-plugins ]]; then
|
||||
echo ::set-output name=plugins::true
|
||||
else
|
||||
echo ::set-output name=plugins::false
|
||||
fi
|
||||
echo ::set-output name=dockerfile::${DOCKERFILE}
|
||||
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
echo ::set-output name=sha::${GITHUB_SHA::8}
|
||||
|
@ -150,6 +168,7 @@ jobs:
|
|||
build-args: |
|
||||
COMMIT_SHA=${{ steps.info.outputs.sha }}
|
||||
INSTALL_OPTIONAL_PACKAGES=${{ steps.info.outputs.full }}
|
||||
DOWNLOAD_PLUGINS=${{ steps.info.outputs.plugins }}
|
||||
labels: |
|
||||
org.opencontainers.image.title=SFTPGo
|
||||
org.opencontainers.image.description=Fully featured and highly configurable SFTP server with optional HTTP, FTP/S and WebDAV support
|
||||
|
|
38
.github/workflows/release.yml
vendored
38
.github/workflows/release.yml
vendored
|
@ -5,7 +5,7 @@ on:
|
|||
tags: 'v*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.18.3
|
||||
GO_VERSION: 1.18.7
|
||||
|
||||
jobs:
|
||||
prepare-sources-with-deps:
|
||||
|
@ -26,6 +26,7 @@ jobs:
|
|||
run: |
|
||||
go mod vendor
|
||||
echo "${SFTPGO_VERSION}" > VERSION.txt
|
||||
echo "${GITHUB_SHA::8}" >> VERSION.txt
|
||||
tar cJvf sftpgo_${SFTPGO_VERSION}_src_with_deps.tar.xz *
|
||||
env:
|
||||
SFTPGO_VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
|
@ -71,16 +72,16 @@ jobs:
|
|||
|
||||
- name: Build for macOS x86_64
|
||||
if: startsWith(matrix.os, 'windows-') != true
|
||||
run: go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
run: go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
|
||||
- name: Build for macOS arm64
|
||||
if: startsWith(matrix.os, 'macos-') == true
|
||||
run: CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 SDKROOT=$(xcrun --sdk macosx --show-sdk-path) go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
|
||||
run: CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 SDKROOT=$(xcrun --sdk macosx --show-sdk-path) go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
|
||||
|
||||
- name: Build for Windows
|
||||
if: startsWith(matrix.os, 'windows-')
|
||||
run: |
|
||||
$GIT_COMMIT = (git describe --always --dirty) | Out-String
|
||||
$GIT_COMMIT = (git describe --always --abbrev=8 --dirty) | Out-String
|
||||
$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
|
||||
|
@ -254,11 +255,12 @@ jobs:
|
|||
|
||||
prepare-linux:
|
||||
name: Prepare Linux binaries
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
distro: ubuntu:18.04
|
||||
go-arch: amd64
|
||||
deb-arch: amd64
|
||||
rpm-arch: x86_64
|
||||
|
@ -284,17 +286,13 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Go
|
||||
if: ${{ matrix.arch == 'amd64' }}
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Get versions
|
||||
id: get_version
|
||||
run: |
|
||||
echo ::set-output name=SFTPGO_VERSION::${GITHUB_REF/refs\/tags\//}
|
||||
echo ::set-output name=GO_VERSION::${GO_VERSION}
|
||||
echo ::set-output name=COMMIT::${GITHUB_SHA::8}
|
||||
shell: bash
|
||||
env:
|
||||
GO_VERSION: ${{ env.GO_VERSION }}
|
||||
|
@ -302,7 +300,20 @@ jobs:
|
|||
- name: Build on amd64
|
||||
if: ${{ matrix.arch == 'amd64' }}
|
||||
run: |
|
||||
go build -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
echo '#!/bin/bash' > build.sh
|
||||
echo '' >> build.sh
|
||||
echo 'set -e' >> build.sh
|
||||
echo 'apt-get update -q -y' >> build.sh
|
||||
echo 'apt-get install -q -y curl gcc' >> build.sh
|
||||
echo 'curl --retry 5 --retry-delay 2 --connect-timeout 10 -o go.tar.gz -L https://go.dev/dl/go${{ steps.get_version.outputs.GO_VERSION }}.linux-${{ matrix.go-arch }}.tar.gz' >> build.sh
|
||||
echo 'tar -C /usr/local -xzf go.tar.gz' >> build.sh
|
||||
echo 'export PATH=$PATH:/usr/local/go/bin' >> build.sh
|
||||
echo 'go version' >> build.sh
|
||||
echo 'cd /usr/local/src' >> build.sh
|
||||
echo 'go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${{ steps.get_version.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo' >> build.sh
|
||||
|
||||
chmod 755 build.sh
|
||||
docker run --rm --name ubuntu-build --mount type=bind,source=`pwd`,target=/usr/local/src ${{ matrix.distro }} /usr/local/src/build.sh
|
||||
mkdir -p output/{init,sqlite,bash_completion,zsh_completion}
|
||||
echo "For documentation please take a look here:" > output/README.txt
|
||||
echo "" >> output/README.txt
|
||||
|
@ -340,7 +351,7 @@ jobs:
|
|||
shell: /bin/bash
|
||||
install: |
|
||||
apt-get update -q -y
|
||||
apt-get install -q -y curl gcc git xz-utils
|
||||
apt-get install -q -y curl gcc xz-utils
|
||||
GO_DOWNLOAD_ARCH=${{ matrix.go-arch }}
|
||||
if [ ${{ matrix.arch}} == 'armv7' ]
|
||||
then
|
||||
|
@ -350,7 +361,8 @@ jobs:
|
|||
tar -C /usr/local -xzf go.tar.gz
|
||||
run: |
|
||||
export PATH=$PATH:/usr/local/go/bin
|
||||
go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
go version
|
||||
go build -buildvcs=false -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${{ steps.get_version.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
mkdir -p output/{init,sqlite,bash_completion,zsh_completion}
|
||||
echo "For documentation please take a look here:" > output/README.txt
|
||||
echo "" >> output/README.txt
|
||||
|
|
|
@ -20,9 +20,14 @@ ARG FEATURES
|
|||
COPY . .
|
||||
|
||||
RUN set -xe && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --dirty)} && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --abbrev=8 --dirty)} && \
|
||||
go build $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -v -o sftpgo
|
||||
|
||||
# Set to "true" to download the "official" plugins in /usr/local/bin
|
||||
ARG DOWNLOAD_PLUGINS=false
|
||||
|
||||
RUN if [ "${DOWNLOAD_PLUGINS}" = "true" ]; then apt-get update && apt-get install --no-install-recommends -y curl && ./docker/scripts/download-plugins.sh; fi
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
# Set to "true" to install jq and the optional git and rsync dependencies
|
||||
|
@ -43,7 +48,7 @@ COPY --from=builder /workspace/sftpgo.json /etc/sftpgo/sftpgo.json
|
|||
COPY --from=builder /workspace/templates /usr/share/sftpgo/templates
|
||||
COPY --from=builder /workspace/static /usr/share/sftpgo/static
|
||||
COPY --from=builder /workspace/openapi /usr/share/sftpgo/openapi
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/
|
||||
COPY --from=builder /workspace/sftpgo /usr/local/bin/sftpgo-plugin-* /usr/local/bin/
|
||||
|
||||
# Log to the stdout so the logs will be available using docker logs
|
||||
ENV SFTPGO_LOG_FILE_PATH=""
|
||||
|
|
|
@ -22,7 +22,7 @@ ARG FEATURES
|
|||
COPY . .
|
||||
|
||||
RUN set -xe && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --dirty)} && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --abbrev=8 --dirty)} && \
|
||||
go build $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -v -o sftpgo
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ ARG FEATURES=nosqlite
|
|||
COPY . .
|
||||
|
||||
RUN set -xe && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --dirty)} && \
|
||||
export COMMIT_SHA=${COMMIT_SHA:-$(git describe --always --abbrev=8 --dirty)} && \
|
||||
go build $(if [ -n "${FEATURES}" ]; then echo "-tags ${FEATURES}"; fi) -trimpath -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -v -o sftpgo
|
||||
|
||||
# Modify the default configuration file
|
||||
|
|
29
README.md
29
README.md
|
@ -11,6 +11,25 @@
|
|||
Fully featured and highly configurable SFTP server with optional HTTP/S, FTP/S and WebDAV support.
|
||||
Several storage backends are supported: local filesystem, encrypted local filesystem, S3 (compatible) Object Storage, Google Cloud Storage, Azure Blob Storage, SFTP.
|
||||
|
||||
## Sponsors
|
||||
|
||||
If you find SFTPGo useful please consider supporting this Open Source project.
|
||||
|
||||
Maintaining and evolving SFTPGo is a lot of work - easily the equivalent of a full time job - for me.
|
||||
|
||||
I'd like to make SFTPGo into a sustainable long term project and would not like to introduce a dual licensing option and limit some features to the proprietary version only.
|
||||
|
||||
If you use SFTPGo, it is in your best interest to ensure that the project you rely on stays healthy and well maintained.
|
||||
This can only happen with your donations and [sponsorships](https://github.com/sponsors/drakkan) :heart:
|
||||
|
||||
If you just take and don't return anything back, the project will die in the long run and you will be forced to pay for a similar proprietary solution.
|
||||
|
||||
More [info](https://github.com/drakkan/sftpgo/issues/452).
|
||||
|
||||
Thank you to our sponsors!
|
||||
|
||||
[<img src="https://www.7digital.com/wp-content/themes/sevendigital/images/top_logo.png" alt="7digital logo">](https://www.7digital.com/)
|
||||
|
||||
## Features
|
||||
|
||||
- Support for serving local filesystem, encrypted local filesystem, S3 Compatible Object Storage, Google Cloud Storage, Azure Blob Storage or other SFTP accounts over SFTP/SCP/FTP/WebDAV.
|
||||
|
@ -93,6 +112,8 @@ An official Docker image is available. Documentation is [here](./docker/README.m
|
|||
|
||||
</details>
|
||||
|
||||
APT and YUM repositories are [available](./docs/repo.md).
|
||||
|
||||
SFTPGo is also available on [AWS Marketplace](https://aws.amazon.com/marketplace/seller-profile?id=6e849ab8-70a6-47de-9a43-13c3fa849335) and [Azure Marketplace](https://azuremarketplace.microsoft.com/en-us/marketplace/apps/prasselsrl1645470739547.sftpgo_linux), purchasing from there will help keep SFTPGo a long-term sustainable project.
|
||||
|
||||
<details><summary>Windows packages</summary>
|
||||
|
@ -308,14 +329,6 @@ We are very grateful to all the people who contributed with ideas and/or pull re
|
|||
|
||||
Thank you [ysura](https://www.ysura.com/) for granting me stable access to a test AWS S3 account.
|
||||
|
||||
## Sponsors
|
||||
|
||||
I'd like to make SFTPGo into a sustainable long term project and your [sponsorship](https://github.com/sponsors/drakkan) will really help :heart:
|
||||
|
||||
Thank you to our sponsors!
|
||||
|
||||
[<img src="https://www.7digital.com/wp-content/themes/sevendigital/images/top_logo.png" alt="7digital logo">](https://www.7digital.com/)
|
||||
|
||||
## License
|
||||
|
||||
GNU AGPLv3
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 acme
|
||||
|
||||
import (
|
||||
|
|
14
acme/acme.go
14
acme/acme.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 acme provides automatic access to certificates from Let's Encrypt and any other ACME-based CA
|
||||
// The code here is largely coiped from https://github.com/go-acme/lego/tree/master/cmd
|
||||
// This package is intended to provide basic functionality for obtaining and renewing certificates
|
||||
|
|
14
cmd/acme.go
14
cmd/acme.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 awscontainer
|
||||
// +build awscontainer
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !awscontainer
|
||||
// +build !awscontainer
|
||||
|
||||
|
|
14
cmd/gen.go
14
cmd/gen.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 "github.com/spf13/cobra"
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
@ -34,6 +48,7 @@ To initialize/update the data provider from the configuration directory simply u
|
|||
|
||||
$ sftpgo initprovider
|
||||
|
||||
Any defined action is ignored.
|
||||
Please take a look at the usage below to customize the options.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logger.DisableLogger()
|
||||
|
@ -51,6 +66,10 @@ Please take a look at the usage below to customize the options.`,
|
|||
os.Exit(1)
|
||||
}
|
||||
providerConf := config.GetProviderConf()
|
||||
// ignore actions
|
||||
providerConf.Actions.Hook = ""
|
||||
providerConf.Actions.ExecuteFor = nil
|
||||
providerConf.Actions.ExecuteOn = nil
|
||||
logger.InfoToConsole("Initializing provider: %#v config file: %#v", providerConf.Driver, viper.ConfigFileUsed())
|
||||
err = dataprovider.InitializeDatabase(providerConf, configDir)
|
||||
if err == nil {
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !noportable
|
||||
// +build !noportable
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 noportable
|
||||
// +build noportable
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
14
cmd/root.go
14
cmd/root.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 provides Command Line Interface support
|
||||
package cmd
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
14
cmd/serve.go
14
cmd/serve.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Copyright (C) 2019-2022 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 command provides command configuration for SFTPGo hooks
|
||||
package command
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 command
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
@ -28,8 +42,17 @@ var (
|
|||
errUnconfiguredAction = errors.New("no hook is configured for this action")
|
||||
errNoHook = errors.New("unable to execute action, no hook defined")
|
||||
errUnexpectedHTTResponse = errors.New("unexpected HTTP response code")
|
||||
hooksConcurrencyGuard = make(chan struct{}, 150)
|
||||
)
|
||||
|
||||
func startNewHook() {
|
||||
hooksConcurrencyGuard <- struct{}{}
|
||||
}
|
||||
|
||||
func hookEnded() {
|
||||
<-hooksConcurrencyGuard
|
||||
}
|
||||
|
||||
// ProtocolActions defines the action to execute on file operations and SSH commands
|
||||
type ProtocolActions struct {
|
||||
// Valid values are download, upload, pre-delete, delete, rename, ssh_cmd. Empty slice to disable
|
||||
|
@ -102,7 +125,12 @@ func ExecuteActionNotification(conn *BaseConnection, operation, filePath, virtua
|
|||
return
|
||||
}
|
||||
|
||||
go actionHandler.Handle(notification) //nolint:errcheck
|
||||
go func() {
|
||||
startNewHook()
|
||||
defer hookEnded()
|
||||
|
||||
actionHandler.Handle(notification) //nolint:errcheck
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common defines code shared among file transfer packages and protocols
|
||||
package common
|
||||
|
||||
|
@ -593,6 +607,9 @@ func (c *Configuration) ExecuteStartupHook() error {
|
|||
}
|
||||
|
||||
func (c *Configuration) executePostDisconnectHook(remoteAddr, protocol, username, connID string, connectionTime time.Time) {
|
||||
startNewHook()
|
||||
defer hookEnded()
|
||||
|
||||
ipAddr := util.GetIPFromRemoteAddress(remoteAddr)
|
||||
connDuration := int64(time.Since(connectionTime) / time.Millisecond)
|
||||
|
||||
|
@ -845,6 +862,15 @@ func (conns *ActiveConnections) Remove(connectionID string) {
|
|||
metric.UpdateActiveConnectionsSize(lastIdx)
|
||||
logger.Debug(conn.GetProtocol(), conn.GetID(), "connection removed, local address %#v, remote address %#v close fs error: %v, num open connections: %v",
|
||||
conn.GetLocalAddress(), conn.GetRemoteAddress(), err, lastIdx)
|
||||
if conn.GetProtocol() == ProtocolFTP && conn.GetUsername() == "" {
|
||||
ip := util.GetIPFromRemoteAddress(conn.GetRemoteAddress())
|
||||
logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTryed, conn.GetProtocol(),
|
||||
dataprovider.ErrNoAuthTryed.Error())
|
||||
metric.AddNoAuthTryed()
|
||||
AddDefenderEvent(ip, HostEventNoLoginTried)
|
||||
dataprovider.ExecutePostLoginHook(&dataprovider.User{}, dataprovider.LoginMethodNoAuthTryed, ip,
|
||||
conn.GetProtocol(), dataprovider.ErrNoAuthTryed)
|
||||
}
|
||||
Config.checkPostDisconnectHook(conn.GetRemoteAddress(), conn.GetProtocol(), conn.GetUsername(),
|
||||
conn.GetID(), conn.GetConnectionTime())
|
||||
return
|
||||
|
@ -933,19 +959,11 @@ func (conns *ActiveConnections) checkIdles() {
|
|||
isUnauthenticatedFTPUser := (c.GetProtocol() == ProtocolFTP && c.GetUsername() == "")
|
||||
|
||||
if idleTime > Config.idleTimeoutAsDuration || (isUnauthenticatedFTPUser && idleTime > Config.idleLoginTimeout) {
|
||||
defer func(conn ActiveConnection, isFTPNoAuth bool) {
|
||||
defer func(conn ActiveConnection) {
|
||||
err := conn.Disconnect()
|
||||
logger.Debug(conn.GetProtocol(), conn.GetID(), "close idle connection, idle time: %v, username: %#v close err: %v",
|
||||
time.Since(conn.GetLastActivity()), conn.GetUsername(), err)
|
||||
if isFTPNoAuth {
|
||||
ip := util.GetIPFromRemoteAddress(c.GetRemoteAddress())
|
||||
logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTryed, c.GetProtocol(), "client idle")
|
||||
metric.AddNoAuthTryed()
|
||||
AddDefenderEvent(ip, HostEventNoLoginTried)
|
||||
dataprovider.ExecutePostLoginHook(&dataprovider.User{}, dataprovider.LoginMethodNoAuthTryed, ip, c.GetProtocol(),
|
||||
dataprovider.ErrNoAuthTryed)
|
||||
}
|
||||
}(c, isUnauthenticatedFTPUser)
|
||||
}(c)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
@ -248,7 +262,7 @@ func (c *RetentionCheck) cleanupFolder(folderPath string) error {
|
|||
result.Elapsed = time.Since(startTime)
|
||||
result.Info = "data retention check skipped: no permissions"
|
||||
c.conn.Log(logger.LevelInfo, "user %#v does not have permissions to check retention on %#v, retention check skipped",
|
||||
c.conn.User, folderPath)
|
||||
c.conn.User.Username, folderPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -400,6 +414,9 @@ func (c *RetentionCheck) sendEmailNotification(elapsed time.Duration, errCheck e
|
|||
}
|
||||
|
||||
func (c *RetentionCheck) sendHookNotification(elapsed time.Duration, errCheck error) error {
|
||||
startNewHook()
|
||||
defer hookEnded()
|
||||
|
||||
data := make(map[string]any)
|
||||
totalDeletedFiles := 0
|
||||
totalDeletedSize := int64(0)
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common_test
|
||||
|
||||
import (
|
||||
|
@ -2699,7 +2713,7 @@ func TestDelayedQuotaUpdater(t *testing.T) {
|
|||
Name: "folder",
|
||||
MappedPath: filepath.Join(os.TempDir(), "p"),
|
||||
}
|
||||
err = dataprovider.AddFolder(&folder)
|
||||
err = dataprovider.AddFolder(&folder, "", "")
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = dataprovider.UpdateVirtualFolderQuota(&folder, 10, 6000, false)
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 common
|
||||
|
||||
import (
|
||||
|
|
171
config/config.go
171
config/config.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 config manages the configuration
|
||||
package config
|
||||
|
||||
|
@ -85,6 +99,7 @@ var (
|
|||
Port: 8080,
|
||||
EnableWebAdmin: true,
|
||||
EnableWebClient: true,
|
||||
EnabledLoginMethods: 0,
|
||||
EnableHTTPS: false,
|
||||
CertificateFile: "",
|
||||
CertificateKeyFile: "",
|
||||
|
@ -105,7 +120,9 @@ var (
|
|||
UsernameField: "",
|
||||
RoleField: "",
|
||||
ImplicitRoles: false,
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
CustomFields: []string{},
|
||||
Debug: false,
|
||||
},
|
||||
Security: httpd.SecurityConf{
|
||||
Enabled: false,
|
||||
|
@ -374,6 +391,7 @@ func Init() {
|
|||
InstallationCode: "",
|
||||
InstallationCodeHint: defaultInstallCodeHint,
|
||||
},
|
||||
HideSupportLink: false,
|
||||
},
|
||||
HTTPConfig: httpclient.Config{
|
||||
Timeout: 20,
|
||||
|
@ -957,9 +975,7 @@ func getPluginsFromEnv(idx int) {
|
|||
}
|
||||
|
||||
func getSFTPDBindindFromEnv(idx int) {
|
||||
binding := sftpd.Binding{
|
||||
ApplyProxyConfig: true,
|
||||
}
|
||||
binding := defaultSFTPDBinding
|
||||
if len(globalConf.SFTPD.Bindings) > idx {
|
||||
binding = globalConf.SFTPD.Bindings[idx]
|
||||
}
|
||||
|
@ -995,9 +1011,17 @@ func getSFTPDBindindFromEnv(idx int) {
|
|||
|
||||
func getFTPDPassiveIPOverridesFromEnv(idx int) []ftpd.PassiveIPOverride {
|
||||
var overrides []ftpd.PassiveIPOverride
|
||||
if len(globalConf.FTPD.Bindings) > idx {
|
||||
overrides = globalConf.FTPD.Bindings[idx].PassiveIPOverrides
|
||||
}
|
||||
|
||||
for subIdx := 0; subIdx < 10; subIdx++ {
|
||||
var override ftpd.PassiveIPOverride
|
||||
var replace bool
|
||||
if len(globalConf.FTPD.Bindings) > idx && len(globalConf.FTPD.Bindings[idx].PassiveIPOverrides) > subIdx {
|
||||
override = globalConf.FTPD.Bindings[idx].PassiveIPOverrides[subIdx]
|
||||
replace = true
|
||||
}
|
||||
|
||||
ip, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PASSIVE_IP_OVERRIDES__%v__IP", idx, subIdx))
|
||||
if ok {
|
||||
|
@ -1011,18 +1035,19 @@ func getFTPDPassiveIPOverridesFromEnv(idx int) []ftpd.PassiveIPOverride {
|
|||
}
|
||||
|
||||
if len(override.Networks) > 0 {
|
||||
if replace {
|
||||
overrides[subIdx] = override
|
||||
} else {
|
||||
overrides = append(overrides, override)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return overrides
|
||||
}
|
||||
|
||||
func getDefaultFTPDBinding(idx int) ftpd.Binding {
|
||||
binding := ftpd.Binding{
|
||||
ApplyProxyConfig: true,
|
||||
MinTLSVersion: 12,
|
||||
}
|
||||
binding := defaultFTPDBinding
|
||||
if len(globalConf.FTPD.Bindings) > idx {
|
||||
binding = globalConf.FTPD.Bindings[idx]
|
||||
}
|
||||
|
@ -1155,9 +1180,7 @@ func getWebDAVDBindingProxyConfigsFromEnv(idx int, binding *webdavd.Binding) boo
|
|||
}
|
||||
|
||||
func getWebDAVDBindingFromEnv(idx int) {
|
||||
binding := webdavd.Binding{
|
||||
MinTLSVersion: 12,
|
||||
}
|
||||
binding := defaultWebDAVDBinding
|
||||
if len(globalConf.WebDAVD.Bindings) > idx {
|
||||
binding = globalConf.WebDAVD.Bindings[idx]
|
||||
}
|
||||
|
@ -1233,22 +1256,44 @@ func getWebDAVDBindingFromEnv(idx int) {
|
|||
|
||||
func getHTTPDSecurityProxyHeadersFromEnv(idx int) []httpd.HTTPSProxyHeader {
|
||||
var httpsProxyHeaders []httpd.HTTPSProxyHeader
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
httpsProxyHeaders = globalConf.HTTPDConfig.Bindings[idx].Security.HTTPSProxyHeaders
|
||||
}
|
||||
|
||||
for subIdx := 0; subIdx < 10; subIdx++ {
|
||||
proxyKey, _ := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__HTTPS_PROXY_HEADERS__%v__KEY", idx, subIdx))
|
||||
proxyVal, _ := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__HTTPS_PROXY_HEADERS__%v__VALUE", idx, subIdx))
|
||||
if proxyKey != "" && proxyVal != "" {
|
||||
httpsProxyHeaders = append(httpsProxyHeaders, httpd.HTTPSProxyHeader{
|
||||
Key: proxyKey,
|
||||
Value: proxyVal,
|
||||
})
|
||||
var httpsProxyHeader httpd.HTTPSProxyHeader
|
||||
var replace bool
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx &&
|
||||
len(globalConf.HTTPDConfig.Bindings[idx].Security.HTTPSProxyHeaders) > subIdx {
|
||||
httpsProxyHeader = httpsProxyHeaders[subIdx]
|
||||
replace = true
|
||||
}
|
||||
proxyKey, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__HTTPS_PROXY_HEADERS__%v__KEY",
|
||||
idx, subIdx))
|
||||
if ok {
|
||||
httpsProxyHeader.Key = proxyKey
|
||||
}
|
||||
proxyVal, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__HTTPS_PROXY_HEADERS__%v__VALUE",
|
||||
idx, subIdx))
|
||||
if ok {
|
||||
httpsProxyHeader.Value = proxyVal
|
||||
}
|
||||
if httpsProxyHeader.Key != "" && httpsProxyHeader.Value != "" {
|
||||
if replace {
|
||||
httpsProxyHeaders[subIdx] = httpsProxyHeader
|
||||
} else {
|
||||
httpsProxyHeaders = append(httpsProxyHeaders, httpsProxyHeader)
|
||||
}
|
||||
}
|
||||
}
|
||||
return httpsProxyHeaders
|
||||
}
|
||||
|
||||
func getHTTPDSecurityConfFromEnv(idx int) (httpd.SecurityConf, bool) { //nolint:gocyclo
|
||||
var result httpd.SecurityConf
|
||||
result := defaultHTTPDBinding.Security
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
result = globalConf.HTTPDConfig.Bindings[idx].Security
|
||||
}
|
||||
isSet := false
|
||||
|
||||
enabled, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__ENABLED", idx))
|
||||
|
@ -1296,6 +1341,7 @@ func getHTTPDSecurityConfFromEnv(idx int) (httpd.SecurityConf, bool) { //nolint:
|
|||
stsSeconds, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__STS_SECONDS", idx))
|
||||
if ok {
|
||||
result.STSSeconds = stsSeconds
|
||||
isSet = true
|
||||
}
|
||||
|
||||
stsIncludeSubDomains, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__SECURITY__STS_INCLUDE_SUBDOMAINS", idx))
|
||||
|
@ -1344,7 +1390,10 @@ func getHTTPDSecurityConfFromEnv(idx int) (httpd.SecurityConf, bool) { //nolint:
|
|||
}
|
||||
|
||||
func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
|
||||
var result httpd.OIDC
|
||||
result := defaultHTTPDBinding.OIDC
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
result = globalConf.HTTPDConfig.Bindings[idx].OIDC
|
||||
}
|
||||
isSet := false
|
||||
|
||||
clientID, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__CLIENT_ID", idx))
|
||||
|
@ -1377,6 +1426,12 @@ func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
|
|||
isSet = true
|
||||
}
|
||||
|
||||
scopes, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__SCOPES", idx))
|
||||
if ok {
|
||||
result.Scopes = scopes
|
||||
isSet = true
|
||||
}
|
||||
|
||||
roleField, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__ROLE_FIELD", idx))
|
||||
if ok {
|
||||
result.RoleField = roleField
|
||||
|
@ -1395,81 +1450,90 @@ func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
|
|||
isSet = true
|
||||
}
|
||||
|
||||
debug, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__OIDC__DEBUG", idx))
|
||||
if ok {
|
||||
result.Debug = debug
|
||||
isSet = true
|
||||
}
|
||||
|
||||
return result, isSet
|
||||
}
|
||||
|
||||
func getHTTPDUIBrandingFromEnv(prefix string) (httpd.UIBranding, bool) {
|
||||
var result httpd.UIBranding
|
||||
func getHTTPDUIBrandingFromEnv(prefix string, branding httpd.UIBranding) (httpd.UIBranding, bool) {
|
||||
isSet := false
|
||||
|
||||
name, ok := os.LookupEnv(fmt.Sprintf("%s__NAME", prefix))
|
||||
if ok {
|
||||
result.Name = name
|
||||
branding.Name = name
|
||||
isSet = true
|
||||
}
|
||||
|
||||
shortName, ok := os.LookupEnv(fmt.Sprintf("%s__SHORT_NAME", prefix))
|
||||
if ok {
|
||||
result.ShortName = shortName
|
||||
branding.ShortName = shortName
|
||||
isSet = true
|
||||
}
|
||||
|
||||
faviconPath, ok := os.LookupEnv(fmt.Sprintf("%s__FAVICON_PATH", prefix))
|
||||
if ok {
|
||||
result.FaviconPath = faviconPath
|
||||
branding.FaviconPath = faviconPath
|
||||
isSet = true
|
||||
}
|
||||
|
||||
logoPath, ok := os.LookupEnv(fmt.Sprintf("%s__LOGO_PATH", prefix))
|
||||
if ok {
|
||||
result.LogoPath = logoPath
|
||||
branding.LogoPath = logoPath
|
||||
isSet = true
|
||||
}
|
||||
|
||||
loginImagePath, ok := os.LookupEnv(fmt.Sprintf("%s__LOGIN_IMAGE_PATH", prefix))
|
||||
if ok {
|
||||
result.LoginImagePath = loginImagePath
|
||||
branding.LoginImagePath = loginImagePath
|
||||
isSet = true
|
||||
}
|
||||
|
||||
disclaimerName, ok := os.LookupEnv(fmt.Sprintf("%s__DISCLAIMER_NAME", prefix))
|
||||
if ok {
|
||||
result.DisclaimerName = disclaimerName
|
||||
branding.DisclaimerName = disclaimerName
|
||||
isSet = true
|
||||
}
|
||||
|
||||
disclaimerPath, ok := os.LookupEnv(fmt.Sprintf("%s__DISCLAIMER_PATH", prefix))
|
||||
if ok {
|
||||
result.DisclaimerPath = disclaimerPath
|
||||
branding.DisclaimerPath = disclaimerPath
|
||||
isSet = true
|
||||
}
|
||||
|
||||
defaultCSSPath, ok := os.LookupEnv(fmt.Sprintf("%s__DEFAULT_CSS", prefix))
|
||||
if ok {
|
||||
result.DefaultCSS = defaultCSSPath
|
||||
branding.DefaultCSS = defaultCSSPath
|
||||
isSet = true
|
||||
}
|
||||
|
||||
extraCSS, ok := lookupStringListFromEnv(fmt.Sprintf("%s__EXTRA_CSS", prefix))
|
||||
if ok {
|
||||
result.ExtraCSS = extraCSS
|
||||
branding.ExtraCSS = extraCSS
|
||||
isSet = true
|
||||
}
|
||||
|
||||
return result, isSet
|
||||
return branding, isSet
|
||||
}
|
||||
|
||||
func getHTTPDBrandingFromEnv(idx int) (httpd.Branding, bool) {
|
||||
var result httpd.Branding
|
||||
result := defaultHTTPDBinding.Branding
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
result = globalConf.HTTPDConfig.Bindings[idx].Branding
|
||||
}
|
||||
isSet := false
|
||||
|
||||
webAdmin, ok := getHTTPDUIBrandingFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__BRANDING__WEB_ADMIN", idx))
|
||||
webAdmin, ok := getHTTPDUIBrandingFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__BRANDING__WEB_ADMIN", idx),
|
||||
result.WebAdmin)
|
||||
if ok {
|
||||
result.WebAdmin = webAdmin
|
||||
isSet = true
|
||||
}
|
||||
|
||||
webClient, ok := getHTTPDUIBrandingFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__BRANDING__WEB_CLIENT", idx))
|
||||
webClient, ok := getHTTPDUIBrandingFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__BRANDING__WEB_CLIENT", idx),
|
||||
result.WebClient)
|
||||
if ok {
|
||||
result.WebClient = webClient
|
||||
isSet = true
|
||||
|
@ -1480,9 +1544,18 @@ func getHTTPDBrandingFromEnv(idx int) (httpd.Branding, bool) {
|
|||
|
||||
func getHTTPDWebClientIntegrationsFromEnv(idx int) []httpd.WebClientIntegration {
|
||||
var integrations []httpd.WebClientIntegration
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
integrations = globalConf.HTTPDConfig.Bindings[idx].WebClientIntegrations
|
||||
}
|
||||
|
||||
for subIdx := 0; subIdx < 10; subIdx++ {
|
||||
var integration httpd.WebClientIntegration
|
||||
var replace bool
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx &&
|
||||
len(globalConf.HTTPDConfig.Bindings[idx].WebClientIntegrations) > subIdx {
|
||||
integration = integrations[subIdx]
|
||||
replace = true
|
||||
}
|
||||
|
||||
url, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__WEB_CLIENT_INTEGRATIONS__%v__URL", idx, subIdx))
|
||||
if ok {
|
||||
|
@ -1495,21 +1568,20 @@ func getHTTPDWebClientIntegrationsFromEnv(idx int) []httpd.WebClientIntegration
|
|||
integration.FileExtensions = extensions
|
||||
}
|
||||
|
||||
if url != "" && len(extensions) > 0 {
|
||||
if integration.URL != "" && len(integration.FileExtensions) > 0 {
|
||||
if replace {
|
||||
integrations[subIdx] = integration
|
||||
} else {
|
||||
integrations = append(integrations, integration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return integrations
|
||||
}
|
||||
|
||||
func getDefaultHTTPBinding(idx int) httpd.Binding {
|
||||
binding := httpd.Binding{
|
||||
EnableWebAdmin: true,
|
||||
EnableWebClient: true,
|
||||
RenderOpenAPI: true,
|
||||
MinTLSVersion: 12,
|
||||
}
|
||||
binding := defaultHTTPDBinding
|
||||
if len(globalConf.HTTPDConfig.Bindings) > idx {
|
||||
binding = globalConf.HTTPDConfig.Bindings[idx]
|
||||
}
|
||||
|
@ -1540,6 +1612,7 @@ func getHTTPDNestedObjectsFromEnv(idx int, binding *httpd.Binding) bool {
|
|||
brandingConf, ok := getHTTPDBrandingFromEnv(idx)
|
||||
if ok {
|
||||
binding.Branding = brandingConf
|
||||
isSet = true
|
||||
}
|
||||
|
||||
return isSet
|
||||
|
@ -1569,7 +1642,7 @@ func getHTTPDBindingProxyConfigsFromEnv(idx int, binding *httpd.Binding) bool {
|
|||
return isSet
|
||||
}
|
||||
|
||||
func getHTTPDBindingFromEnv(idx int) {
|
||||
func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo
|
||||
binding := getDefaultHTTPBinding(idx)
|
||||
isSet := false
|
||||
|
||||
|
@ -1609,6 +1682,12 @@ func getHTTPDBindingFromEnv(idx int) {
|
|||
isSet = true
|
||||
}
|
||||
|
||||
enabledLoginMethods, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLED_LOGIN_METHODS", idx))
|
||||
if ok {
|
||||
binding.EnabledLoginMethods = int(enabledLoginMethods)
|
||||
isSet = true
|
||||
}
|
||||
|
||||
renderOpenAPI, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__RENDER_OPENAPI", idx))
|
||||
if ok {
|
||||
binding.RenderOpenAPI = renderOpenAPI
|
||||
|
@ -1668,6 +1747,9 @@ func setHTTPDBinding(isSet bool, binding httpd.Binding, idx int) {
|
|||
|
||||
func getHTTPClientCertificatesFromEnv(idx int) {
|
||||
tlsCert := httpclient.TLSKeyPair{}
|
||||
if len(globalConf.HTTPConfig.Certificates) > idx {
|
||||
tlsCert = globalConf.HTTPConfig.Certificates[idx]
|
||||
}
|
||||
|
||||
cert, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_HTTP__CERTIFICATES__%v__CERT", idx))
|
||||
if ok {
|
||||
|
@ -1893,9 +1975,10 @@ func setViperDefaults() {
|
|||
viper.SetDefault("httpd.cors.allowed_headers", globalConf.HTTPDConfig.Cors.AllowedHeaders)
|
||||
viper.SetDefault("httpd.cors.exposed_headers", globalConf.HTTPDConfig.Cors.ExposedHeaders)
|
||||
viper.SetDefault("httpd.cors.allow_credentials", globalConf.HTTPDConfig.Cors.AllowCredentials)
|
||||
viper.SetDefault("httpd.cors.max_age", globalConf.HTTPDConfig.Cors.MaxAge)
|
||||
viper.SetDefault("httpd.setup.installation_code", globalConf.HTTPDConfig.Setup.InstallationCode)
|
||||
viper.SetDefault("httpd.setup.installation_code_hint", globalConf.HTTPDConfig.Setup.InstallationCodeHint)
|
||||
viper.SetDefault("httpd.cors.max_age", globalConf.HTTPDConfig.Cors.MaxAge)
|
||||
viper.SetDefault("httpd.hide_support_link", globalConf.HTTPDConfig.HideSupportLink)
|
||||
viper.SetDefault("http.timeout", globalConf.HTTPConfig.Timeout)
|
||||
viper.SetDefault("http.retry_wait_min", globalConf.HTTPConfig.RetryWaitMin)
|
||||
viper.SetDefault("http.retry_wait_max", globalConf.HTTPConfig.RetryWaitMax)
|
||||
|
|
25
config/config_darwin.go
Normal file
25
config/config_darwin.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (C) 2019-2022 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 darwin
|
||||
// +build darwin
|
||||
|
||||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
// macOS specific config search path
|
||||
func setViperAdditionalConfigPaths() {
|
||||
viper.AddConfigPath("/usr/local/etc/sftpgo")
|
||||
}
|
20
config/config_fallback.go
Normal file
20
config/config_fallback.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (C) 2019-2022 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 !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
|
||||
package config
|
||||
|
||||
func setViperAdditionalConfigPaths() {}
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 linux
|
||||
// +build linux
|
||||
|
||||
|
@ -9,4 +23,5 @@ import "github.com/spf13/viper"
|
|||
func setViperAdditionalConfigPaths() {
|
||||
viper.AddConfigPath("$HOME/.config/sftpgo")
|
||||
viper.AddConfigPath("/etc/sftpgo")
|
||||
viper.AddConfigPath("/usr/local/etc/sftpgo")
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package config
|
||||
|
||||
func setViperAdditionalConfigPaths() {}
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 config_test
|
||||
|
||||
import (
|
||||
|
@ -485,6 +499,126 @@ func TestDisabledMFAConfig(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestFTPDOverridesFromEnv(t *testing.T) {
|
||||
reset()
|
||||
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__IP", "192.168.1.1")
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__NETWORKS", "192.168.1.0/24, 192.168.3.0/25")
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__1__IP", "192.168.2.1")
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__1__NETWORKS", "192.168.2.0/24")
|
||||
cleanup := func() {
|
||||
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__IP")
|
||||
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__NETWORKS")
|
||||
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__1__IP")
|
||||
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__1__NETWORKS")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
configDir := ".."
|
||||
err := config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
ftpdConf := config.GetFTPDConfig()
|
||||
require.Len(t, ftpdConf.Bindings, 1)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides, 2)
|
||||
require.Equal(t, "192.168.1.1", ftpdConf.Bindings[0].PassiveIPOverrides[0].IP)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides[0].Networks, 2)
|
||||
require.Equal(t, "192.168.2.1", ftpdConf.Bindings[0].PassiveIPOverrides[1].IP)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides[1].Networks, 1)
|
||||
|
||||
cleanup()
|
||||
cfg := make(map[string]any)
|
||||
cfg["ftpd"] = ftpdConf
|
||||
configAsJSON, err := json.Marshal(cfg)
|
||||
require.NoError(t, err)
|
||||
confName := tempConfigName + ".json"
|
||||
configFilePath := filepath.Join(configDir, confName)
|
||||
err = os.WriteFile(configFilePath, configAsJSON, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__IP", "192.168.1.2")
|
||||
os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__1__NETWORKS", "192.168.2.0/24,192.168.4.0/25")
|
||||
err = config.LoadConfig(configDir, confName)
|
||||
assert.NoError(t, err)
|
||||
ftpdConf = config.GetFTPDConfig()
|
||||
require.Len(t, ftpdConf.Bindings, 1)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides, 2)
|
||||
require.Equal(t, "192.168.1.2", ftpdConf.Bindings[0].PassiveIPOverrides[0].IP)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides[0].Networks, 2)
|
||||
require.Equal(t, "192.168.2.1", ftpdConf.Bindings[0].PassiveIPOverrides[1].IP)
|
||||
require.Len(t, ftpdConf.Bindings[0].PassiveIPOverrides[1].Networks, 2)
|
||||
|
||||
err = os.Remove(configFilePath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestHTTPDSubObjectsFromEnv(t *testing.T) {
|
||||
reset()
|
||||
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__SECURITY__HTTPS_PROXY_HEADERS__0__KEY", "X-Forwarded-Proto")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__SECURITY__HTTPS_PROXY_HEADERS__0__VALUE", "https")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__WEB_CLIENT_INTEGRATIONS__0__URL", "http://127.0.0.1/")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__WEB_CLIENT_INTEGRATIONS__0__FILE_EXTENSIONS", ".pdf, .txt")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_ID", "client_id")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_SECRET", "client_secret")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CONFIG_URL", "config_url")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__REDIRECT_BASE_URL", "redirect_base_url")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__USERNAME_FIELD", "email")
|
||||
cleanup := func() {
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__SECURITY__HTTPS_PROXY_HEADERS__0__KEY")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__SECURITY__HTTPS_PROXY_HEADERS__0__VALUE")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__WEB_CLIENT_INTEGRATIONS__0__URL")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__WEB_CLIENT_INTEGRATIONS__0__FILE_EXTENSIONS")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_ID")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_SECRET")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CONFIG_URL")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__REDIRECT_BASE_URL")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__USERNAME_FIELD")
|
||||
}
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
configDir := ".."
|
||||
err := config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
httpdConf := config.GetHTTPDConfig()
|
||||
require.Len(t, httpdConf.Bindings, 1)
|
||||
require.Len(t, httpdConf.Bindings[0].Security.HTTPSProxyHeaders, 1)
|
||||
require.Len(t, httpdConf.Bindings[0].WebClientIntegrations, 1)
|
||||
require.Equal(t, "client_id", httpdConf.Bindings[0].OIDC.ClientID)
|
||||
require.Equal(t, "client_secret", httpdConf.Bindings[0].OIDC.ClientSecret)
|
||||
require.Equal(t, "config_url", httpdConf.Bindings[0].OIDC.ConfigURL)
|
||||
require.Equal(t, "redirect_base_url", httpdConf.Bindings[0].OIDC.RedirectBaseURL)
|
||||
require.Equal(t, "email", httpdConf.Bindings[0].OIDC.UsernameField)
|
||||
|
||||
cleanup()
|
||||
cfg := make(map[string]any)
|
||||
cfg["httpd"] = httpdConf
|
||||
configAsJSON, err := json.Marshal(cfg)
|
||||
require.NoError(t, err)
|
||||
confName := tempConfigName + ".json"
|
||||
configFilePath := filepath.Join(configDir, confName)
|
||||
err = os.WriteFile(configFilePath, configAsJSON, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__SECURITY__HTTPS_PROXY_HEADERS__0__VALUE", "http")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__WEB_CLIENT_INTEGRATIONS__0__URL", "http://127.0.1.1/")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__0__OIDC__CLIENT_SECRET", "new_client_secret")
|
||||
err = config.LoadConfig(configDir, confName)
|
||||
assert.NoError(t, err)
|
||||
httpdConf = config.GetHTTPDConfig()
|
||||
require.Len(t, httpdConf.Bindings, 1)
|
||||
require.Len(t, httpdConf.Bindings[0].Security.HTTPSProxyHeaders, 1)
|
||||
require.Equal(t, "http", httpdConf.Bindings[0].Security.HTTPSProxyHeaders[0].Value)
|
||||
require.Len(t, httpdConf.Bindings[0].WebClientIntegrations, 1)
|
||||
require.Equal(t, "http://127.0.1.1/", httpdConf.Bindings[0].WebClientIntegrations[0].URL)
|
||||
require.Equal(t, "client_id", httpdConf.Bindings[0].OIDC.ClientID)
|
||||
require.Equal(t, "new_client_secret", httpdConf.Bindings[0].OIDC.ClientSecret)
|
||||
require.Equal(t, "config_url", httpdConf.Bindings[0].OIDC.ConfigURL)
|
||||
require.Equal(t, "redirect_base_url", httpdConf.Bindings[0].OIDC.RedirectBaseURL)
|
||||
require.Equal(t, "email", httpdConf.Bindings[0].OIDC.UsernameField)
|
||||
|
||||
err = os.Remove(configFilePath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestPluginsFromEnv(t *testing.T) {
|
||||
reset()
|
||||
|
||||
|
@ -545,7 +679,9 @@ func TestPluginsFromEnv(t *testing.T) {
|
|||
require.Equal(t, kms.SecretStatusAWS, pluginConf.KMSOptions.EncryptedStatus)
|
||||
require.Equal(t, 14, pluginConf.AuthOptions.Scope)
|
||||
|
||||
configAsJSON, err := json.Marshal(pluginsConf)
|
||||
cfg := make(map[string]any)
|
||||
cfg["plugins"] = pluginConf
|
||||
configAsJSON, err := json.Marshal(cfg)
|
||||
require.NoError(t, err)
|
||||
confName := tempConfigName + ".json"
|
||||
configFilePath := filepath.Join(configDir, confName)
|
||||
|
@ -919,6 +1055,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PORT", "9000")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS", "3")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI", "0")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_HTTPS", "1 ")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION", "13")
|
||||
|
@ -938,8 +1075,10 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__REDIRECT_BASE_URL", "redirect base url")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__USERNAME_FIELD", "preferred_username")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__ROLE_FIELD", "sftpgo_role")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES", "openid")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES", "1")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS", "field1,field2")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG", "1")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED", "true")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS", "*.example.com,*.example.net")
|
||||
os.Setenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX", "1")
|
||||
|
@ -985,6 +1124,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__MIN_TLS_VERSION")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_ADMIN")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLE_WEB_CLIENT")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__ENABLED_LOGIN_METHODS")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__RENDER_OPENAPI")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES")
|
||||
|
@ -1002,8 +1142,10 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__REDIRECT_BASE_URL")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__USERNAME_FIELD")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__ROLE_FIELD")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__SCOPES")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__IMPLICIT_ROLES")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__CUSTOM_FIELDS")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__OIDC__DEBUG")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ENABLED")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS")
|
||||
os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__SECURITY__ALLOWED_HOSTS_ARE_REGEX")
|
||||
|
@ -1044,6 +1186,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
||||
require.True(t, bindings[0].EnableWebAdmin)
|
||||
require.True(t, bindings[0].EnableWebClient)
|
||||
require.Equal(t, 0, bindings[0].EnabledLoginMethods)
|
||||
require.True(t, bindings[0].RenderOpenAPI)
|
||||
require.Len(t, bindings[0].TLSCipherSuites, 1)
|
||||
require.Empty(t, bindings[0].OIDC.ConfigURL)
|
||||
|
@ -1051,16 +1194,21 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.Equal(t, 0, bindings[0].HideLoginURL)
|
||||
require.False(t, bindings[0].Security.Enabled)
|
||||
require.Equal(t, 0, bindings[0].ClientIPHeaderDepth)
|
||||
require.Len(t, bindings[0].OIDC.Scopes, 3)
|
||||
require.False(t, bindings[0].OIDC.Debug)
|
||||
require.Equal(t, 8000, bindings[1].Port)
|
||||
require.Equal(t, "127.0.0.1", bindings[1].Address)
|
||||
require.False(t, bindings[1].EnableHTTPS)
|
||||
require.Equal(t, 12, bindings[0].MinTLSVersion)
|
||||
require.True(t, bindings[1].EnableWebAdmin)
|
||||
require.True(t, bindings[1].EnableWebClient)
|
||||
require.Equal(t, 0, bindings[1].EnabledLoginMethods)
|
||||
require.True(t, bindings[1].RenderOpenAPI)
|
||||
require.Nil(t, bindings[1].TLSCipherSuites)
|
||||
require.Equal(t, 1, bindings[1].HideLoginURL)
|
||||
require.Empty(t, bindings[1].OIDC.ClientID)
|
||||
require.Len(t, bindings[1].OIDC.Scopes, 3)
|
||||
require.False(t, bindings[1].OIDC.Debug)
|
||||
require.False(t, bindings[1].Security.Enabled)
|
||||
require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name)
|
||||
require.Equal(t, "WebClient", bindings[1].Branding.WebClient.ShortName)
|
||||
|
@ -1071,6 +1219,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.Equal(t, 13, bindings[2].MinTLSVersion)
|
||||
require.False(t, bindings[2].EnableWebAdmin)
|
||||
require.False(t, bindings[2].EnableWebClient)
|
||||
require.Equal(t, 3, bindings[2].EnabledLoginMethods)
|
||||
require.False(t, bindings[2].RenderOpenAPI)
|
||||
require.Equal(t, 1, bindings[2].ClientAuthType)
|
||||
require.Len(t, bindings[2].TLSCipherSuites, 2)
|
||||
|
@ -1091,10 +1240,13 @@ func TestHTTPDBindingsFromEnv(t *testing.T) {
|
|||
require.Equal(t, "redirect base url", bindings[2].OIDC.RedirectBaseURL)
|
||||
require.Equal(t, "preferred_username", bindings[2].OIDC.UsernameField)
|
||||
require.Equal(t, "sftpgo_role", bindings[2].OIDC.RoleField)
|
||||
require.Len(t, bindings[2].OIDC.Scopes, 1)
|
||||
require.Equal(t, "openid", bindings[2].OIDC.Scopes[0])
|
||||
require.True(t, bindings[2].OIDC.ImplicitRoles)
|
||||
require.Len(t, bindings[2].OIDC.CustomFields, 2)
|
||||
require.Equal(t, "field1", bindings[2].OIDC.CustomFields[0])
|
||||
require.Equal(t, "field2", bindings[2].OIDC.CustomFields[1])
|
||||
require.True(t, bindings[2].OIDC.Debug)
|
||||
require.True(t, bindings[2].Security.Enabled)
|
||||
require.Len(t, bindings[2].Security.AllowedHosts, 2)
|
||||
require.Equal(t, "*.example.com", bindings[2].Security.AllowedHosts[0])
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
@ -29,12 +43,17 @@ const (
|
|||
|
||||
const (
|
||||
actionObjectUser = "user"
|
||||
actionObjectFolder = "folder"
|
||||
actionObjectGroup = "group"
|
||||
actionObjectAdmin = "admin"
|
||||
actionObjectAPIKey = "api_key"
|
||||
actionObjectShare = "share"
|
||||
)
|
||||
|
||||
var (
|
||||
actionsConcurrencyGuard = make(chan struct{}, 100)
|
||||
)
|
||||
|
||||
func executeAction(operation, executor, ip, objectType, objectName string, object plugin.Renderer) {
|
||||
if plugin.Handler.HasNotifiers() {
|
||||
plugin.Handler.NotifyProviderEvent(¬ifier.ProviderEvent{
|
||||
|
@ -55,6 +74,11 @@ func executeAction(operation, executor, ip, objectType, objectName string, objec
|
|||
}
|
||||
|
||||
go func() {
|
||||
actionsConcurrencyGuard <- struct{}{}
|
||||
defer func() {
|
||||
<-actionsConcurrencyGuard
|
||||
}()
|
||||
|
||||
dataAsJSON, err := object.RenderAsJSON(operation != operationDelete)
|
||||
if err != nil {
|
||||
providerLog(logger.LevelError, "unable to serialize user as JSON for operation %#v: %v", operation, err)
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !nobolt
|
||||
// +build !nobolt
|
||||
|
||||
|
@ -1030,9 +1044,7 @@ func (p *BoltProvider) updateFolder(folder *vfs.BaseVirtualFolder) error {
|
|||
})
|
||||
}
|
||||
|
||||
func (p *BoltProvider) deleteFolderMappings(tx *bolt.Tx, folder vfs.BaseVirtualFolder, usersBucket,
|
||||
groupsBucket *bolt.Bucket,
|
||||
) error {
|
||||
func (p *BoltProvider) deleteFolderMappings(folder vfs.BaseVirtualFolder, usersBucket, groupsBucket *bolt.Bucket) error {
|
||||
for _, username := range folder.Users {
|
||||
var u []byte
|
||||
if u = usersBucket.Get([]byte(username)); u == nil {
|
||||
|
@ -1112,7 +1124,7 @@ func (p *BoltProvider) deleteFolder(folder vfs.BaseVirtualFolder) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = p.deleteFolderMappings(tx, folder, usersBucket, groupsBucket); err != nil {
|
||||
if err = p.deleteFolderMappings(folder, usersBucket, groupsBucket); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 nobolt
|
||||
// +build nobolt
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider provides data access.
|
||||
// It abstracts different data providers and exposes a common API.
|
||||
package dataprovider
|
||||
|
@ -90,7 +104,7 @@ const (
|
|||
operationDelete = "delete"
|
||||
sqlPrefixValidChars = "abcdefghijklmnopqrstuvwxyz_0123456789"
|
||||
maxHookResponseSize = 1048576 // 1MB
|
||||
iso8601UTCFormat = "2006-01-02 15:04:05Z"
|
||||
iso8601UTCFormat = "2006-01-02T15:04:05Z"
|
||||
)
|
||||
|
||||
// Supported algorithms for hashing passwords.
|
||||
|
@ -156,6 +170,28 @@ var (
|
|||
sharedProviders = []string{PGSQLDataProviderName, MySQLDataProviderName, CockroachDataProviderName}
|
||||
logSender = "dataprovider"
|
||||
credentialsDirPath string
|
||||
sqlTableUsers string
|
||||
sqlTableFolders string
|
||||
sqlTableFoldersMapping string
|
||||
sqlTableUsersFoldersMapping string
|
||||
sqlTableAdmins string
|
||||
sqlTableAPIKeys string
|
||||
sqlTableShares string
|
||||
sqlTableDefenderHosts string
|
||||
sqlTableDefenderEvents string
|
||||
sqlTableActiveTransfers string
|
||||
sqlTableGroups string
|
||||
sqlTableUsersGroupsMapping string
|
||||
sqlTableGroupsFoldersMapping string
|
||||
sqlTableSharedSessions string
|
||||
sqlTableSchemaVersion string
|
||||
argon2Params *argon2id.Params
|
||||
lastLoginMinDelay = 10 * time.Minute
|
||||
usernameRegex = regexp.MustCompile("^[a-zA-Z0-9-_.~]+$")
|
||||
tempPath string
|
||||
)
|
||||
|
||||
func initSQLTables() {
|
||||
sqlTableUsers = "users"
|
||||
sqlTableFolders = "folders"
|
||||
sqlTableFoldersMapping = "folders_mapping"
|
||||
|
@ -171,11 +207,7 @@ var (
|
|||
sqlTableGroupsFoldersMapping = "groups_folders_mapping"
|
||||
sqlTableSharedSessions = "shared_sessions"
|
||||
sqlTableSchemaVersion = "schema_version"
|
||||
argon2Params *argon2id.Params
|
||||
lastLoginMinDelay = 10 * time.Minute
|
||||
usernameRegex = regexp.MustCompile("^[a-zA-Z0-9-_.~]+$")
|
||||
tempPath string
|
||||
)
|
||||
}
|
||||
|
||||
type schemaVersion struct {
|
||||
Version int
|
||||
|
@ -218,6 +250,24 @@ type PasswordValidation struct {
|
|||
Users PasswordValidationRules `json:"users" mapstructure:"users"`
|
||||
}
|
||||
|
||||
type wrappedFolder struct {
|
||||
Folder vfs.BaseVirtualFolder
|
||||
}
|
||||
|
||||
func (w *wrappedFolder) RenderAsJSON(reload bool) ([]byte, error) {
|
||||
if reload {
|
||||
folder, err := provider.getFolderByName(w.Folder.Name)
|
||||
if err != nil {
|
||||
providerLog(logger.LevelError, "unable to reload folder before rendering as json: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
folder.PrepareForRendering()
|
||||
return json.Marshal(folder)
|
||||
}
|
||||
w.Folder.PrepareForRendering()
|
||||
return json.Marshal(w.Folder)
|
||||
}
|
||||
|
||||
// ObjectsActions defines the action to execute on user create, update, delete for the specified objects
|
||||
type ObjectsActions struct {
|
||||
// Valid values are add, update, delete. Empty slice to disable
|
||||
|
@ -707,11 +757,18 @@ func SetTempPath(fsPath string) {
|
|||
tempPath = fsPath
|
||||
}
|
||||
|
||||
func checkSharedMode() {
|
||||
if !util.Contains(sharedProviders, config.Driver) {
|
||||
config.IsShared = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the data provider.
|
||||
// An error is returned if the configured driver is invalid or if the data provider cannot be initialized
|
||||
func Initialize(cnf Config, basePath string, checkAdmins bool) error {
|
||||
var err error
|
||||
config = cnf
|
||||
checkSharedMode()
|
||||
config.Actions.ExecuteOn = util.RemoveDuplicates(config.Actions.ExecuteOn, true)
|
||||
config.Actions.ExecuteFor = util.RemoveDuplicates(config.Actions.ExecuteFor, true)
|
||||
|
||||
|
@ -822,6 +879,7 @@ func initializeHashingAlgo(cnf *Config) error {
|
|||
}
|
||||
|
||||
func validateSQLTablesPrefix() error {
|
||||
initSQLTables()
|
||||
if config.SQLTablesPrefix != "" {
|
||||
for _, char := range config.SQLTablesPrefix {
|
||||
if !strings.Contains(sqlPrefixValidChars, strings.ToLower(string(char))) {
|
||||
|
@ -831,17 +889,24 @@ func validateSQLTablesPrefix() error {
|
|||
sqlTableUsers = config.SQLTablesPrefix + sqlTableUsers
|
||||
sqlTableFolders = config.SQLTablesPrefix + sqlTableFolders
|
||||
sqlTableFoldersMapping = config.SQLTablesPrefix + sqlTableFoldersMapping
|
||||
sqlTableUsersFoldersMapping = config.SQLTablesPrefix + sqlTableUsersFoldersMapping
|
||||
sqlTableAdmins = config.SQLTablesPrefix + sqlTableAdmins
|
||||
sqlTableAPIKeys = config.SQLTablesPrefix + sqlTableAPIKeys
|
||||
sqlTableShares = config.SQLTablesPrefix + sqlTableShares
|
||||
sqlTableDefenderEvents = config.SQLTablesPrefix + sqlTableDefenderEvents
|
||||
sqlTableDefenderHosts = config.SQLTablesPrefix + sqlTableDefenderHosts
|
||||
sqlTableActiveTransfers = config.SQLTablesPrefix + sqlTableActiveTransfers
|
||||
sqlTableGroups = config.SQLTablesPrefix + sqlTableGroups
|
||||
sqlTableUsersGroupsMapping = config.SQLTablesPrefix + sqlTableUsersGroupsMapping
|
||||
sqlTableGroupsFoldersMapping = config.SQLTablesPrefix + sqlTableGroupsFoldersMapping
|
||||
sqlTableSharedSessions = config.SQLTablesPrefix + sqlTableSharedSessions
|
||||
sqlTableSchemaVersion = config.SQLTablesPrefix + sqlTableSchemaVersion
|
||||
providerLog(logger.LevelDebug, "sql table for users %#v, folders %#v folders mapping %#v admins %#v "+
|
||||
"api keys %#v shares %#v defender hosts %#v defender events %#v transfers %#v schema version %#v",
|
||||
sqlTableUsers, sqlTableFolders, sqlTableFoldersMapping, sqlTableAdmins, sqlTableAPIKeys,
|
||||
sqlTableShares, sqlTableDefenderHosts, sqlTableDefenderEvents, sqlTableActiveTransfers, sqlTableSchemaVersion)
|
||||
providerLog(logger.LevelDebug, "sql table for users %q, folders %q users folders mapping %q admins %q "+
|
||||
"api keys %q shares %q defender hosts %q defender events %q transfers %q groups %q "+
|
||||
"users groups mapping %q groups folders mapping %q shared sessions %q schema version %q",
|
||||
sqlTableUsers, sqlTableFolders, sqlTableUsersFoldersMapping, sqlTableAdmins, sqlTableAPIKeys,
|
||||
sqlTableShares, sqlTableDefenderHosts, sqlTableDefenderEvents, sqlTableActiveTransfers, sqlTableGroups,
|
||||
sqlTableUsersGroupsMapping, sqlTableGroupsFoldersMapping, sqlTableSharedSessions, sqlTableSchemaVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -934,6 +999,9 @@ func CheckAdminAndPass(username, password, ip string) (Admin, error) {
|
|||
|
||||
// CheckCachedUserCredentials checks the credentials for a cached user
|
||||
func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protocol string, tlsCert *x509.Certificate) error {
|
||||
if err := user.User.CheckLoginConditions(); err != nil {
|
||||
return err
|
||||
}
|
||||
if loginMethod != LoginMethodPassword {
|
||||
_, err := checkUserAndTLSCertificate(&user.User, protocol, tlsCert)
|
||||
if err != nil {
|
||||
|
@ -946,9 +1014,6 @@ func CheckCachedUserCredentials(user *CachedUser, password, loginMethod, protoco
|
|||
return nil
|
||||
}
|
||||
}
|
||||
if err := user.User.CheckLoginConditions(); err != nil {
|
||||
return err
|
||||
}
|
||||
if password == "" {
|
||||
return ErrInvalidCredentials
|
||||
}
|
||||
|
@ -1726,15 +1791,20 @@ func GetUsersForQuotaCheck(toFetch map[string]bool) ([]User, error) {
|
|||
}
|
||||
|
||||
// AddFolder adds a new virtual folder.
|
||||
func AddFolder(folder *vfs.BaseVirtualFolder) error {
|
||||
func AddFolder(folder *vfs.BaseVirtualFolder, executor, ipAddress string) error {
|
||||
folder.Name = config.convertName(folder.Name)
|
||||
return provider.addFolder(folder)
|
||||
err := provider.addFolder(folder)
|
||||
if err == nil {
|
||||
executeAction(operationAdd, executor, ipAddress, actionObjectFolder, folder.Name, &wrappedFolder{Folder: *folder})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateFolder updates the specified virtual folder
|
||||
func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string, groups []string, executor, ipAddress string) error {
|
||||
err := provider.updateFolder(folder)
|
||||
if err == nil {
|
||||
executeAction(operationUpdate, executor, ipAddress, actionObjectFolder, folder.Name, &wrappedFolder{Folder: *folder})
|
||||
usersInGroups, errGrp := provider.getUsersInGroups(groups)
|
||||
if errGrp == nil {
|
||||
users = append(users, usersInGroups...)
|
||||
|
@ -1765,6 +1835,7 @@ func DeleteFolder(folderName, executor, ipAddress string) error {
|
|||
}
|
||||
err = provider.deleteFolder(folder)
|
||||
if err == nil {
|
||||
executeAction(operationDelete, executor, ipAddress, actionObjectFolder, folder.Name, &wrappedFolder{Folder: folder})
|
||||
users := folder.Users
|
||||
usersInGroups, errGrp := provider.getUsersInGroups(folder.Groups)
|
||||
if errGrp == nil {
|
||||
|
@ -3326,6 +3397,11 @@ func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err erro
|
|||
}
|
||||
|
||||
go func() {
|
||||
actionsConcurrencyGuard <- struct{}{}
|
||||
defer func() {
|
||||
<-actionsConcurrencyGuard
|
||||
}()
|
||||
|
||||
status := "0"
|
||||
if err == nil {
|
||||
status = "1"
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
@ -1993,7 +2007,7 @@ func (p *MemoryProvider) restoreFolders(dump *BackupData) error {
|
|||
}
|
||||
} else {
|
||||
folder.Users = nil
|
||||
err = AddFolder(&folder)
|
||||
err = AddFolder(&folder, ActionExecutorSystem, "")
|
||||
if err != nil {
|
||||
providerLog(logger.LevelError, "error adding folder %#v: %v", folder.Name, err)
|
||||
return err
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !nomysql
|
||||
// +build !nomysql
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 nomysql
|
||||
// +build nomysql
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !nopgsql
|
||||
// +build !nopgsql
|
||||
|
||||
|
@ -145,7 +159,7 @@ ALTER TABLE "{{groups_folders_mapping}}" ADD CONSTRAINT "{{prefix}}groups_folder
|
|||
FOREIGN KEY ("folder_id") REFERENCES "{{folders}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE;
|
||||
CREATE INDEX "{{prefix}}groups_folders_mapping_group_id_idx" ON "{{groups_folders_mapping}}" ("group_id");
|
||||
ALTER TABLE "{{groups_folders_mapping}}" ADD CONSTRAINT "{{prefix}}groups_folders_mapping_group_id_fk_groups_id"
|
||||
FOREIGN KEY ("group_id") REFERENCES "groups" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE;
|
||||
FOREIGN KEY ("group_id") REFERENCES "{{groups}}" ("id") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE;
|
||||
CREATE INDEX "{{prefix}}groups_updated_at_idx" ON "{{groups}}" ("updated_at");
|
||||
`
|
||||
pgsqlV17DownSQL = `DROP TABLE "{{users_groups_mapping}}" CASCADE;
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 nopgsql
|
||||
// +build nopgsql
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
@ -55,6 +69,7 @@ func sqlReplaceAll(sql string) string {
|
|||
sql = strings.ReplaceAll(sql, "{{defender_events}}", sqlTableDefenderEvents)
|
||||
sql = strings.ReplaceAll(sql, "{{defender_hosts}}", sqlTableDefenderHosts)
|
||||
sql = strings.ReplaceAll(sql, "{{active_transfers}}", sqlTableActiveTransfers)
|
||||
sql = strings.ReplaceAll(sql, "{{shared_sessions}}", sqlTableSharedSessions)
|
||||
sql = strings.ReplaceAll(sql, "{{prefix}}", config.SQLTablesPrefix)
|
||||
return sql
|
||||
}
|
||||
|
@ -655,10 +670,13 @@ func sqlCommonDumpGroups(dbHandle sqlQuerier) ([]Group, error) {
|
|||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
group.PrepareForRendering()
|
||||
groups = append(groups, group)
|
||||
}
|
||||
return groups, rows.Err()
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return groups, err
|
||||
}
|
||||
return getGroupsWithVirtualFolders(ctx, groups, dbHandle)
|
||||
}
|
||||
|
||||
func sqlCommonGetUsersInGroups(names []string, dbHandle sqlQuerier) ([]string, error) {
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 !nosqlite
|
||||
// +build !nosqlite
|
||||
|
||||
|
@ -115,12 +129,12 @@ CREATE TABLE "{{groups_folders_mapping}}" ("id" integer NOT NULL PRIMARY KEY AUT
|
|||
"virtual_path" text NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
|
||||
CONSTRAINT "{{prefix}}unique_group_folder_mapping" UNIQUE ("group_id", "folder_id"));
|
||||
CREATE TABLE "{{users_groups_mapping}}" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" integer NOT NULL REFERENCES "users" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"group_id" integer NOT NULL REFERENCES "groups" ("id") ON DELETE NO ACTION,
|
||||
"user_id" integer NOT NULL REFERENCES "{{users}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"group_id" integer NOT NULL REFERENCES "{{groups}}" ("id") ON DELETE NO ACTION,
|
||||
"group_type" integer NOT NULL, CONSTRAINT "{{prefix}}unique_user_group_mapping" UNIQUE ("user_id", "group_id"));
|
||||
CREATE TABLE "new__folders_mapping" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" integer NOT NULL REFERENCES "users" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"folder_id" integer NOT NULL REFERENCES "folders" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"user_id" integer NOT NULL REFERENCES "{{users}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"folder_id" integer NOT NULL REFERENCES "{{folders}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"virtual_path" text NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
|
||||
CONSTRAINT "{{prefix}}unique_user_folder_mapping" UNIQUE ("user_id", "folder_id"));
|
||||
INSERT INTO "new__folders_mapping" ("id", "virtual_path", "quota_size", "quota_files", "folder_id", "user_id") SELECT "id",
|
||||
|
@ -139,8 +153,8 @@ CREATE INDEX "{{prefix}}groups_folders_mapping_group_id_idx" ON "{{groups_folder
|
|||
DROP TABLE "{{groups_folders_mapping}}";
|
||||
DROP TABLE "{{groups}}";
|
||||
CREATE TABLE "new__folders_mapping" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" integer NOT NULL REFERENCES "users" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"folder_id" integer NOT NULL REFERENCES "folders" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"user_id" integer NOT NULL REFERENCES "{{users}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"folder_id" integer NOT NULL REFERENCES "{{folders}}" ("id") ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
"virtual_path" text NOT NULL, "quota_size" bigint NOT NULL, "quota_files" integer NOT NULL,
|
||||
CONSTRAINT "{{prefix}}unique_folder_mapping" UNIQUE ("user_id", "folder_id"));
|
||||
INSERT INTO "new__folders_mapping" ("id", "virtual_path", "quota_size", "quota_files", "folder_id", "user_id") SELECT "id",
|
||||
|
@ -652,6 +666,7 @@ func updateSQLiteDatabaseFrom16To17(dbHandle *sql.DB) error {
|
|||
}
|
||||
sql := strings.ReplaceAll(sqliteV17SQL, "{{users}}", sqlTableUsers)
|
||||
sql = strings.ReplaceAll(sql, "{{groups}}", sqlTableGroups)
|
||||
sql = strings.ReplaceAll(sql, "{{users}}", sqlTableUsers)
|
||||
sql = strings.ReplaceAll(sql, "{{folders}}", sqlTableFolders)
|
||||
sql = strings.ReplaceAll(sql, "{{folders_mapping}}", sqlTableFoldersMapping)
|
||||
sql = strings.ReplaceAll(sql, "{{users_folders_mapping}}", sqlTableUsersFoldersMapping)
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 nosqlite
|
||||
// +build nosqlite
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
@ -33,6 +47,14 @@ func getSQLPlaceholders() []string {
|
|||
return placeholders
|
||||
}
|
||||
|
||||
func getSQLTableGroups() string {
|
||||
if config.Driver == MySQLDataProviderName {
|
||||
return fmt.Sprintf("`%s`", sqlTableGroups)
|
||||
}
|
||||
|
||||
return sqlTableGroups
|
||||
}
|
||||
|
||||
func getAddSessionQuery() string {
|
||||
if config.Driver == MySQLDataProviderName {
|
||||
return fmt.Sprintf("INSERT INTO %s (`key`,`data`,`type`,`timestamp`) VALUES (%s,%s,%s,%s) "+
|
||||
|
@ -139,7 +161,7 @@ func getDefenderEventsCleanupQuery() string {
|
|||
}
|
||||
|
||||
func getGroupByNameQuery() string {
|
||||
return fmt.Sprintf(`SELECT %s FROM %s WHERE name = %s`, selectGroupFields, sqlTableGroups, sqlPlaceholders[0])
|
||||
return fmt.Sprintf(`SELECT %s FROM %s WHERE name = %s`, selectGroupFields, getSQLTableGroups(), sqlPlaceholders[0])
|
||||
}
|
||||
|
||||
func getGroupsQuery(order string, minimal bool) string {
|
||||
|
@ -149,7 +171,7 @@ func getGroupsQuery(order string, minimal bool) string {
|
|||
} else {
|
||||
fieldSelection = selectGroupFields
|
||||
}
|
||||
return fmt.Sprintf(`SELECT %s FROM %s ORDER BY name %s LIMIT %v OFFSET %v`, fieldSelection, sqlTableGroups,
|
||||
return fmt.Sprintf(`SELECT %s FROM %s ORDER BY name %s LIMIT %v OFFSET %v`, fieldSelection, getSQLTableGroups(),
|
||||
order, sqlPlaceholders[0], sqlPlaceholders[1])
|
||||
}
|
||||
|
||||
|
@ -168,7 +190,7 @@ func getGroupsWithNamesQuery(numArgs int) string {
|
|||
} else {
|
||||
sb.WriteString("('')")
|
||||
}
|
||||
return fmt.Sprintf(`SELECT %s FROM %s WHERE name in %s`, selectGroupFields, sqlTableGroups, sb.String())
|
||||
return fmt.Sprintf(`SELECT %s FROM %s WHERE name in %s`, selectGroupFields, getSQLTableGroups(), sb.String())
|
||||
}
|
||||
|
||||
func getUsersInGroupsQuery(numArgs int) string {
|
||||
|
@ -187,27 +209,27 @@ func getUsersInGroupsQuery(numArgs int) string {
|
|||
sb.WriteString("('')")
|
||||
}
|
||||
return fmt.Sprintf(`SELECT username FROM %s WHERE id IN (SELECT user_id from %s WHERE group_id IN (SELECT id FROM %s WHERE name IN (%s)))`,
|
||||
sqlTableUsers, sqlTableUsersGroupsMapping, sqlTableGroups, sb.String())
|
||||
sqlTableUsers, sqlTableUsersGroupsMapping, getSQLTableGroups(), sb.String())
|
||||
}
|
||||
|
||||
func getDumpGroupsQuery() string {
|
||||
return fmt.Sprintf(`SELECT %s FROM %s`, selectGroupFields, sqlTableGroups)
|
||||
return fmt.Sprintf(`SELECT %s FROM %s`, selectGroupFields, getSQLTableGroups())
|
||||
}
|
||||
|
||||
func getAddGroupQuery() string {
|
||||
return fmt.Sprintf(`INSERT INTO %s (name,description,created_at,updated_at,user_settings)
|
||||
VALUES (%v,%v,%v,%v,%v)`, sqlTableGroups, sqlPlaceholders[0], sqlPlaceholders[1],
|
||||
VALUES (%v,%v,%v,%v,%v)`, getSQLTableGroups(), sqlPlaceholders[0], sqlPlaceholders[1],
|
||||
sqlPlaceholders[2], sqlPlaceholders[3], sqlPlaceholders[4])
|
||||
}
|
||||
|
||||
func getUpdateGroupQuery() string {
|
||||
return fmt.Sprintf(`UPDATE %s SET description=%v,user_settings=%v,updated_at=%v
|
||||
WHERE name = %s`, sqlTableGroups, sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2],
|
||||
WHERE name = %s`, getSQLTableGroups(), sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2],
|
||||
sqlPlaceholders[3])
|
||||
}
|
||||
|
||||
func getDeleteGroupQuery() string {
|
||||
return fmt.Sprintf(`DELETE FROM %s WHERE name = %s`, sqlTableGroups, sqlPlaceholders[0])
|
||||
return fmt.Sprintf(`DELETE FROM %s WHERE name = %s`, getSQLTableGroups(), sqlPlaceholders[0])
|
||||
}
|
||||
|
||||
func getAdminByUsernameQuery() string {
|
||||
|
@ -519,19 +541,19 @@ func getClearUserGroupMappingQuery() string {
|
|||
func getAddUserGroupMappingQuery() string {
|
||||
return fmt.Sprintf(`INSERT INTO %v (user_id,group_id,group_type) VALUES ((SELECT id FROM %v WHERE username = %v),
|
||||
(SELECT id FROM %v WHERE name = %v),%v)`,
|
||||
sqlTableUsersGroupsMapping, sqlTableUsers, sqlPlaceholders[0], sqlTableGroups, sqlPlaceholders[1], sqlPlaceholders[2])
|
||||
sqlTableUsersGroupsMapping, sqlTableUsers, sqlPlaceholders[0], getSQLTableGroups(), sqlPlaceholders[1], sqlPlaceholders[2])
|
||||
}
|
||||
|
||||
func getClearGroupFolderMappingQuery() string {
|
||||
return fmt.Sprintf(`DELETE FROM %v WHERE group_id = (SELECT id FROM %v WHERE name = %v)`, sqlTableGroupsFoldersMapping,
|
||||
sqlTableGroups, sqlPlaceholders[0])
|
||||
getSQLTableGroups(), sqlPlaceholders[0])
|
||||
}
|
||||
|
||||
func getAddGroupFolderMappingQuery() string {
|
||||
return fmt.Sprintf(`INSERT INTO %v (virtual_path,quota_size,quota_files,folder_id,group_id)
|
||||
VALUES (%v,%v,%v,(SELECT id FROM %v WHERE name = %v),(SELECT id FROM %v WHERE name = %v))`,
|
||||
sqlTableGroupsFoldersMapping, sqlPlaceholders[0], sqlPlaceholders[1], sqlPlaceholders[2], sqlTableFolders,
|
||||
sqlPlaceholders[3], sqlTableGroups, sqlPlaceholders[4])
|
||||
sqlPlaceholders[3], getSQLTableGroups(), sqlPlaceholders[4])
|
||||
}
|
||||
|
||||
func getClearUserFolderMappingQuery() string {
|
||||
|
@ -585,7 +607,7 @@ func getRelatedGroupsForUsersQuery(users []User) string {
|
|||
sb.WriteString(")")
|
||||
}
|
||||
return fmt.Sprintf(`SELECT g.name,ug.group_type,ug.user_id FROM %v g INNER JOIN %v ug ON g.id = ug.group_id WHERE
|
||||
ug.user_id IN %v ORDER BY ug.user_id`, sqlTableGroups, sqlTableUsersGroupsMapping, sb.String())
|
||||
ug.user_id IN %v ORDER BY ug.user_id`, getSQLTableGroups(), sqlTableUsersGroupsMapping, sb.String())
|
||||
}
|
||||
|
||||
func getRelatedFoldersForUsersQuery(users []User) string {
|
||||
|
@ -637,7 +659,7 @@ func getRelatedGroupsForFoldersQuery(folders []vfs.BaseVirtualFolder) string {
|
|||
sb.WriteString(")")
|
||||
}
|
||||
return fmt.Sprintf(`SELECT fm.folder_id,g.name FROM %v fm INNER JOIN %v g ON fm.group_id = g.id
|
||||
WHERE fm.folder_id IN %v ORDER BY fm.folder_id`, sqlTableGroupsFoldersMapping, sqlTableGroups, sb.String())
|
||||
WHERE fm.folder_id IN %v ORDER BY fm.folder_id`, sqlTableGroupsFoldersMapping, getSQLTableGroups(), sb.String())
|
||||
}
|
||||
|
||||
func getRelatedUsersForGroupsQuery(groups []Group) string {
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 dataprovider
|
||||
|
||||
import (
|
||||
|
@ -194,6 +208,26 @@ func (u *User) checkDirWithParents(virtualDirPath, connectionID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (u *User) checkLocalHomeDir(connectionID string) {
|
||||
switch u.FsConfig.Provider {
|
||||
case sdk.LocalFilesystemProvider, sdk.CryptedFilesystemProvider:
|
||||
return
|
||||
default:
|
||||
osFs := vfs.NewOsFs(connectionID, u.GetHomeDir(), "")
|
||||
osFs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) checkRootPath(connectionID string) error {
|
||||
fs, err := u.GetFilesystemForPath("/", connectionID)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, connectionID, "could not create main filesystem for user %q err: %v", u.Username, err)
|
||||
return fmt.Errorf("could not create root filesystem: %w", err)
|
||||
}
|
||||
fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckFsRoot check the root directory for the main fs and the virtual folders.
|
||||
// It returns an error if the main filesystem cannot be created
|
||||
func (u *User) CheckFsRoot(connectionID string) error {
|
||||
|
@ -209,15 +243,16 @@ func (u *User) CheckFsRoot(connectionID string) error {
|
|||
}
|
||||
if isLastActivityRecent(u.LastLogin, delay) {
|
||||
if u.LastLogin > u.UpdatedAt {
|
||||
if config.IsShared == 1 {
|
||||
u.checkLocalHomeDir(connectionID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
fs, err := u.GetFilesystemForPath("/", connectionID)
|
||||
err := u.checkRootPath(connectionID)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, connectionID, "could not create main filesystem for user %#v err: %v", u.Username, err)
|
||||
return err
|
||||
}
|
||||
fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
|
||||
if u.Filters.StartDirectory != "" {
|
||||
err = u.checkDirWithParents(u.Filters.StartDirectory, connectionID)
|
||||
if err != nil {
|
||||
|
@ -227,7 +262,7 @@ func (u *User) CheckFsRoot(connectionID string) error {
|
|||
}
|
||||
for idx := range u.VirtualFolders {
|
||||
v := &u.VirtualFolders[idx]
|
||||
fs, err = u.GetFilesystemForPath(v.VirtualPath, connectionID)
|
||||
fs, err := u.GetFilesystemForPath(v.VirtualPath, connectionID)
|
||||
if err == nil {
|
||||
fs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ SFTPGo provides an official Docker image, it is available on both [Docker Hub](h
|
|||
|
||||
## Supported tags and respective Dockerfile links
|
||||
|
||||
- [v2.3.0, v2.3, v2, latest](https://github.com/drakkan/sftpgo/blob/v2.3.0/Dockerfile)
|
||||
- [v2.3.0-alpine, v2.3-alpine, v2-alpine, alpine](https://github.com/drakkan/sftpgo/blob/v2.3.0/Dockerfile.alpine)
|
||||
- [v2.3.0-slim, v2.3-slim, v2-slim, slim](https://github.com/drakkan/sftpgo/blob/v2.3.0/Dockerfile)
|
||||
- [v2.3.0-alpine-slim, v2.3-alpine-slim, v2-alpine-slim, alpine-slim](https://github.com/drakkan/sftpgo/blob/v2.3.0/Dockerfile.alpine)
|
||||
- [v2.3.0-distroless-slim, v2.3-distroless-slim, v2-distroless-slim, distroless-slim](https://github.com/drakkan/sftpgo/blob/v2.3.0/Dockerfile.distroless)
|
||||
- [v2.3.6, v2.3, v2, latest](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile)
|
||||
- [v2.3.6-plugins, v2.3-plugins, v2-plugins, plugins](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile)
|
||||
- [v2.3.6-alpine, v2.3-alpine, v2-alpine, alpine](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile.alpine)
|
||||
- [v2.3.6-slim, v2.3-slim, v2-slim, slim](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile)
|
||||
- [v2.3.6-alpine-slim, v2.3-alpine-slim, v2-alpine-slim, alpine-slim](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile.alpine)
|
||||
- [v2.3.6-distroless-slim, v2.3-distroless-slim, v2-distroless-slim, distroless-slim](https://github.com/drakkan/sftpgo/blob/v2.3.6/Dockerfile.distroless)
|
||||
- [edge](../Dockerfile)
|
||||
- [edge-plugins](../Dockerfile)
|
||||
- [edge-alpine](../Dockerfile.alpine)
|
||||
- [edge-slim](../Dockerfile)
|
||||
- [edge-alpine-slim](../Dockerfile.alpine)
|
||||
|
@ -197,7 +199,11 @@ We only provide the slim variant and so the optional `git` dependency is not ava
|
|||
|
||||
### `sftpgo:<suite>-slim`
|
||||
|
||||
These tags provide a slimmer image that does not include the optional `git` dependency.
|
||||
These tags provide a slimmer image that does not include `jq` and the optional `git` and `rsync` dependencies.
|
||||
|
||||
### `sftpgo:<suite>-plugins`
|
||||
|
||||
These tags provide the standard image with the addition of all "official" plugins installed in `/usr/local/bin`.
|
||||
|
||||
## Helm Chart
|
||||
|
||||
|
|
25
docker/scripts/download-plugins.sh
Executable file
25
docker/scripts/download-plugins.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ARCH=`uname -m`
|
||||
|
||||
case ${ARCH} in
|
||||
"x86_64")
|
||||
SUFFIX=amd64
|
||||
;;
|
||||
"aarch64")
|
||||
SUFFIX=arm64
|
||||
;;
|
||||
*)
|
||||
SUFFIX=ppc64le
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "download plugins for arch ${SUFFIX}"
|
||||
|
||||
for PLUGIN in geoipfilter kms pubsub eventstore eventsearch metadata
|
||||
do
|
||||
echo "download plugin from https://github.com/sftpgo/sftpgo-plugin-${PLUGIN}/releases/latest/download/sftpgo-plugin-${PLUGIN}-linux-${SUFFIX}"
|
||||
curl -L "https://github.com/sftpgo/sftpgo-plugin-${PLUGIN}/releases/latest/download/sftpgo-plugin-${PLUGIN}-linux-${SUFFIX}" --output "/usr/local/bin/sftpgo-plugin-${PLUGIN}"
|
||||
chmod 755 "/usr/local/bin/sftpgo-plugin-${PLUGIN}"
|
||||
done
|
|
@ -29,7 +29,7 @@ Version info, such as git commit and build date, can be embedded setting the fol
|
|||
For example, you can build using the following command:
|
||||
|
||||
```bash
|
||||
go build -tags nogcs,nos3,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
go build -tags nogcs,nos3,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/version.date=`date -u +%FT%TZ`" -o sftpgo
|
||||
```
|
||||
|
||||
You should get a version that includes git commit, build date and available features like this one:
|
||||
|
|
|
@ -83,6 +83,8 @@ The `actions` struct inside the `data_provider` configuration section allows you
|
|||
The supported object types are:
|
||||
|
||||
- `user`
|
||||
- `folder`
|
||||
- `group`
|
||||
- `admin`
|
||||
- `api_key`
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ The configuration file contains the following sections:
|
|||
- `users_base_dir`, string. Users default base directory. If no home dir is defined while adding a new user, and this value is a valid absolute path, then the user home dir will be automatically defined as the path obtained joining the base dir and the username
|
||||
- `actions`, struct. It contains the command to execute and/or the HTTP URL to notify and the trigger conditions. See [Custom Actions](./custom-actions.md) for more details
|
||||
- `execute_on`, list of strings. Valid values are `add`, `update`, `delete`. `update` action will not be fired for internal updates such as the last login or the user quota fields.
|
||||
- `execute_for`, list of strings. Defines the provider objects that trigger the action. Valid values are `user`, `admin`, `api_key`.
|
||||
- `execute_for`, list of strings. Defines the provider objects that trigger the action. Valid values are `user`, `folder`, `group`, `admin`, `api_key`, `share`.
|
||||
- `hook`, string. Absolute path to the command to execute or HTTP URL to notify.
|
||||
- `external_auth_hook`, string. Absolute path to an external program or an HTTP URL to invoke for users authentication. See [External Authentication](./external-auth.md) for more details. Leave empty to disable.
|
||||
- `external_auth_scope`, integer. 0 means all supported authentication scopes (passwords, public keys and keyboard interactive). 1 means passwords only. 2 means public keys only. 4 means key keyboard interactive only. 8 means TLS certificate. The flags can be combined, for example 6 means public keys and keyboard interactive
|
||||
|
@ -260,6 +260,8 @@ The configuration file contains the following sections:
|
|||
- `enable_web_admin`, boolean. Set to `false` to disable the built-in web admin for this binding. You also need to define `templates_path` and `static_files_path` to use the built-in web admin interface. Default `true`.
|
||||
- `enable_web_client`, boolean. Set to `false` to disable the built-in web client for this binding. You also need to define `templates_path` and `static_files_path` to use the built-in web client interface. Default `true`.
|
||||
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
|
||||
- `enabled_login_methods`, integer. Defines the login methods available for the WebAdmin and WebClient UIs. `0` means any configured method: username/password login form and OIDC, if enabled. `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. You can combine the values. For example `3` means that you can only login using OIDC on both WebClient and WebAdmin UI. Default: `0`.
|
||||
- `enable_https`, boolean. Set to `true` and provide both a certificate and a key file to enable HTTPS connection for this binding. Default `false`.
|
||||
- `certificate_file`, string. Binding specific TLS certificate. This can be an absolute path or a path relative to the config dir.
|
||||
- `certificate_key_file`, string. Binding specific private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If not set the global ones will be used, if any.
|
||||
- `min_tls_version`, integer. Defines the minimum version of TLS to be enabled. `12` means TLS 1.2 (and therefore TLS 1.2 and TLS 1.3 will be enabled),`13` means TLS 1.3. Default: `12`.
|
||||
|
@ -279,9 +281,11 @@ The configuration file contains the following sections:
|
|||
- `client_secret`, string. Defines the application's secret. Default: blank.
|
||||
- `redirect_base_url`, string. Defines the base URL to redirect to after OpenID authentication. The suffix `/web/oidc/redirect` will be added to this base URL, adding also the `web_root` if configured. Default: blank.
|
||||
- `username_field`, string. Defines the ID token claims field to map to the SFTPGo username. Default: blank.
|
||||
- `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. Default: blank.
|
||||
- `scopes`, list of strings. Request the OAuth provider to provide the scope information from an authenticated users. The `openid` scope is mandatory. Default: `"openid", "profile", "email"`.
|
||||
- `role_field`, string. Defines the optional ID token claims field to map to a SFTPGo role. If the defined ID token claims field is set to `admin` the authenticated user is mapped to an SFTPGo admin. You don't need to specify this field if you want to use OpenID only for the Web Client UI. If the field is inside a nested structure, you can use the dot notation to traverse the structures. Default: blank.
|
||||
- `implicit_roles`, boolean. If set, the `role_field` is ignored and the SFTPGo role is assumed based on the login link used. Default: `false`.
|
||||
- `custom_fields`, list of strings. Custom token claims fields to pass to the pre-login hook. Default: empty.
|
||||
- `debug`, boolean. If set, the received id tokens will be logged at debug level. Default: `false`.
|
||||
- `security`, struct. Defines security headers to add to HTTP responses and allows to restrict allowed hosts. The following parameters are supported:
|
||||
- `enabled`, boolean. Set to `true` to enable security configurations. Default: `false`.
|
||||
- `allowed_hosts`, list of strings. Fully qualified domain names that are allowed. An empty list allows any and all host names. Default: empty.
|
||||
|
@ -330,6 +334,7 @@ The configuration file contains the following sections:
|
|||
- `setup` struct containing configurations for the initial setup screen
|
||||
- `installation_code`, string. If set, this installation code will be required when creating the first admin account. Please note that even if set using an environment variable this field is read at SFTPGo startup and not at runtime. This is not a license key or similar, the purpose here is to prevent anyone who can access to the initial setup screen from creating an admin user. Default: blank.
|
||||
- `installation_code_hint`, string. Description for the installation code input field. Default: `Installation code`.
|
||||
- `hide_support_link`, boolean. If set, the link to the [sponsors section](../README.md#sponsors) will not appear on the setup screen page. Default: `false`.
|
||||
- **"telemetry"**, the configuration for the telemetry server, more details [below](#telemetry-server)
|
||||
- `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 0
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: `127.0.0.1`
|
||||
|
|
17
docs/oidc.md
17
docs/oidc.md
|
@ -1,6 +1,7 @@
|
|||
# OpenID Connect
|
||||
|
||||
OpenID Connect integration allows you to map your identity provider users to SFTPGo admins/users and so you can login to SFTPGo Web Client and Web Admin user interfaces using your identity provider.
|
||||
OpenID Connect integration allows you to map your identity provider users to SFTPGo admins/users,
|
||||
so you can login to SFTPGo Web Client and Web Admin user interfaces, using your own identity provider.
|
||||
|
||||
SFTPGo allows to configure per-binding OpenID Connect configurations. The supported configuration parameters are documented within the `oidc` section [here](./full-configuration.md).
|
||||
|
||||
|
@ -41,6 +42,11 @@ Add the following configuration parameters to the SFTPGo configuration file (or
|
|||
"client_secret": "jRsmE0SWnuZjP7djBqNq0mrf8QN77j2c",
|
||||
"config_url": "http://192.168.1.12:8086/auth/realms/sftpgo",
|
||||
"redirect_base_url": "http://192.168.1.50:8080",
|
||||
"scopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email"
|
||||
],
|
||||
"username_field": "preferred_username",
|
||||
"role_field": "sftpgo_role",
|
||||
"implicit_roles": false,
|
||||
|
@ -104,8 +110,12 @@ And the following is an example ID token which allows the SFTPGo user `user1` to
|
|||
```
|
||||
|
||||
SFTPGo users (not admins) can be created/updated after successful OpenID authentication by defining a [pre-login hook](./dynamic-user-mod.md).
|
||||
You can use the `custom_fields` configuration parameter to define the token claims field names to pass to the pre-login hook, these fields are useful for implementing custom logic when creating/updating the SFTPGo user within the hook.
|
||||
For example you can set the field `sftpgo_home_dir` in your identity provider and add it to the `custom_fields` in the SFTPGo configuration like this:
|
||||
You can use `scopes` configuration to request additional information (claims) about authenticated users (See your provider's own documentation for more information).
|
||||
By default the scopes `"openid", "profile", "email"` are retrieved.
|
||||
The `custom_fields` configuration parameter can be used to define claim field names to pass to the pre-login hook,
|
||||
these fields can be used e.g. for implementing custom logic when creating/updating the SFTPGo user within the hook.
|
||||
For example, if you have created a scope with name `sftpgo` in your identity provider to provide a claim for `sftpgo_home_dir` ,
|
||||
then you can add it to the `custom_fields` in the SFTPGo configuration like this:
|
||||
|
||||
```json
|
||||
...
|
||||
|
@ -115,6 +125,7 @@ For example you can set the field `sftpgo_home_dir` in your identity provider an
|
|||
"config_url": "http://192.168.1.12:8086/auth/realms/sftpgo",
|
||||
"redirect_base_url": "http://192.168.1.50:8080",
|
||||
"username_field": "preferred_username",
|
||||
"scopes": [ "openid", "profile", "email", "sftpgo" ],
|
||||
"role_field": "sftpgo_role",
|
||||
"custom_fields": ["sftpgo_home_dir"]
|
||||
}
|
||||
|
|
61
docs/repo.md
Normal file
61
docs/repo.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# SFTPGo repositories
|
||||
|
||||
These repositories are available through Oregon State University's free mirroring service. Special thanks to Lance Albertson, Director of the Oregon State University Open Source Lab, who helped me with the initial setup.
|
||||
|
||||
## APT repo
|
||||
|
||||
Supported distributions:
|
||||
|
||||
- Debian 10 "buster"
|
||||
- Debian 11 "bullseye"
|
||||
|
||||
Import the public key used by the package management system using the following command:
|
||||
|
||||
```shell
|
||||
curl -sS https://ftp.osuosl.org/pub/sftpgo/apt/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/sftpgo-archive-keyring.gpg
|
||||
```
|
||||
|
||||
If you receive an error indicating that `gnupg` is not installed, you can install it using the following command:
|
||||
|
||||
```shell
|
||||
sudo apt install gnupg
|
||||
```
|
||||
|
||||
Create the SFTPGo source list file:
|
||||
|
||||
```shell
|
||||
CODENAME=`lsb_release -c -s`
|
||||
echo "deb [signed-by=/usr/share/keyrings/sftpgo-archive-keyring.gpg] https://ftp.osuosl.org/pub/sftpgo/apt ${CODENAME} main" | sudo tee /etc/apt/sources.list.d/sftpgo.list
|
||||
```
|
||||
|
||||
Reload the package database and install SFTPGo:
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
sudo apt install sftpgo
|
||||
```
|
||||
|
||||
## YUM repo
|
||||
|
||||
The YUM repository supports generic Red Hat based distributions.
|
||||
|
||||
Create the SFTPGo repository using the following command:
|
||||
|
||||
```shell
|
||||
ARCH=`uname -m`
|
||||
curl -sS https://ftp.osuosl.org/pub/sftpgo/yum/${ARCH}/sftpgo.repo | sudo tee /etc/yum.repos.d/sftpgo.repo
|
||||
```
|
||||
|
||||
Reload the package database and install SFTPGo:
|
||||
|
||||
```shell
|
||||
sudo yum update
|
||||
sudo yum install sftpgo
|
||||
```
|
||||
|
||||
Start the SFTPGo service and enable it to start at system boot:
|
||||
|
||||
```shell
|
||||
sudo systemctl start sftpgo
|
||||
sudo systemctl enable sftpgo
|
||||
```
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 ftpd_test
|
||||
|
||||
import (
|
||||
|
|
14
ftpd/ftpd.go
14
ftpd/ftpd.go
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 ftpd implements the FTP protocol
|
||||
package ftpd
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 ftpd_test
|
||||
|
||||
import (
|
||||
|
@ -77,154 +91,154 @@ CzgWkxiz7XE4lgUwX44FCXZM3+JeUbI=
|
|||
-----END EC PRIVATE KEY-----`
|
||||
caCRT = `-----BEGIN CERTIFICATE-----
|
||||
MIIE5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0
|
||||
QXV0aDAeFw0yMTAxMDIyMTIwNTVaFw0yMjA3MDIyMTMwNTJaMBMxETAPBgNVBAMT
|
||||
CENlcnRBdXRoMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4Tiho5xW
|
||||
AC15JRkMwfp3/TJwI2As7MY5dele5cmdr5bHAE+sRKqC+Ti88OJWCV5saoyax/1S
|
||||
CjxJlQMZMl169P1QYJskKjdG2sdv6RLWLMgwSNRRjxp/Bw9dHdiEb9MjLgu28Jro
|
||||
9peQkHcRHeMf5hM9WvlIJGrdzbC4hUehmqggcqgARainBkYjf0SwuWxHeu4nMqkp
|
||||
Ak5tcSTLCjHfEFHZ9Te0TIPG5YkWocQKyeLgu4lvuU+DD2W2lym+YVUtRMGs1Env
|
||||
k7p+N0DcGU26qfzZ2sF5ZXkqm7dBsGQB9pIxwc2Q8T1dCIyP9OQCKVILdc5aVFf1
|
||||
cryQFHYzYNNZXFlIBims5VV5Mgfp8ESHQSue+v6n6ykecLEyKt1F1Y/MWY/nWUSI
|
||||
8zdq83jdBAZVjo9MSthxVn57/06s/hQca65IpcTZV2gX0a+eRlAVqaRbAhL3LaZe
|
||||
bYsW3WHKoUOftwemuep3nL51TzlXZVL7Oz/ClGaEOsnGG9KFO6jh+W768qC0zLQI
|
||||
CdE7v2Zex98sZteHCg9fGJHIaYoF0aJG5P3WI5oZf2fy7UIYN9ADLFZiorCXAZEh
|
||||
CSU6mDoRViZ4RGR9GZxbDZ9KYn7O8M/KCR72bkQg73TlMsk1zSXEw0MKLUjtsw6c
|
||||
rZ0Jt8t3sRatHO3JrYHALMt9vZfyNCZp0IsCAwEAAaNFMEMwDgYDVR0PAQH/BAQD
|
||||
AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO1yCNAGr/zQTJIi8lw3
|
||||
w5OiuBvMMA0GCSqGSIb3DQEBCwUAA4ICAQA6gCNuM7r8mnx674dm31GxBjQy5ZwB
|
||||
7CxDzYEvL/oiZ3Tv3HlPfN2LAAsJUfGnghh9DOytenL2CTZWjl/emP5eijzmlP+9
|
||||
zva5I6CIMCf/eDDVsRdO244t0o4uG7+At0IgSDM3bpVaVb4RHZNjEziYChsEYY8d
|
||||
HK6iwuRSvFniV6yhR/Vj1Ymi9yZ5xclqseLXiQnUB0PkfIk23+7s42cXB16653fH
|
||||
O/FsPyKBLiKJArizLYQc12aP3QOrYoYD9+fAzIIzew7A5C0aanZCGzkuFpO6TRlD
|
||||
Tb7ry9Gf0DfPpCgxraH8tOcmnqp/ka3hjqo/SRnnTk0IFrmmLdarJvjD46rKwBo4
|
||||
MjyAIR1mQ5j8GTlSFBmSgETOQ/EYvO3FPLmra1Fh7L+DvaVzTpqI9fG3TuyyY+Ri
|
||||
Fby4ycTOGSZOe5Fh8lqkX5Y47mCUJ3zHzOA1vUJy2eTlMRGpu47Eb1++Vm6EzPUP
|
||||
2EF5aD+zwcssh+atZvQbwxpgVqVcyLt91RSkKkmZQslh0rnlTb68yxvUnD3zw7So
|
||||
o6TAf9UvwVMEvdLT9NnFd6hwi2jcNte/h538GJwXeBb8EkfpqLKpTKyicnOdkamZ
|
||||
7E9zY8SHNRYMwB9coQ/W8NvufbCgkvOoLyMXk5edbXofXl3PhNGOlraWbghBnzf5
|
||||
r3rwjFsQOoZotA==
|
||||
QXV0aDAeFw0yMjA3MDQxNTQzMTFaFw0yNDAxMDQxNTUzMDhaMBMxETAPBgNVBAMT
|
||||
CENlcnRBdXRoMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4eyDJkmW
|
||||
D4OVYo7ddgiZkd6QQdPyLcsa31Wc9jdR2/peEabyNT8jSWteS6ouY84GRlnhfFeZ
|
||||
mpXgbaUJu/Z8Y/8riPxwL8XF4vCScQDMywpQnVUd6E9x2/+/uaD4p/BBswgKqKPe
|
||||
uDcHZn7MkD4QlquUhMElDrBUi1Dv/AVHnQ6iP4vd5Jlv0F+40jdq/8Wa7yhW7Pu5
|
||||
iNvPwCk8HjENBKVur/re+Acif8A2TlbCsuOnVduSQNmnWH+iZmB9upyBZtUszGS0
|
||||
JhUwtSnwUX/JapF70Pwte/PV3RK8cJ5FjuAPNeTyJvSuMTELFSAyCeiNynFGgyhW
|
||||
cqbEiPu6BURLculyVkmh4dOrhTrYZv/n3UJAhyxkdYrbh3INHmTa4izvclcuwoEo
|
||||
lFlJp3l77D0lIi+pbtcBV6ys7reyuxUAkBNwnpt2pWfCQoi4QYKcNbHm47c2phOb
|
||||
QSojQ8SsNU5bnlY2MDzkKo5DPav/i4d0HpndphUpx4f8hA0KylLevDRkMz9TAH7H
|
||||
uDssn0CxFOGHiveEAGGbn+doHjNWM339x/cdLbK0vuieDKby8YYcBY1JML57Dl9f
|
||||
rs52ySnDZbMqOb9zF66mQpC2FZoAj713xSkDSnSCUekrqgck1EA1ifxAviHt+p26
|
||||
JwaEDL7Lk01EEdYN4csSd1fezbCqTrG8ffUCAwEAAaNFMEMwDgYDVR0PAQH/BAQD
|
||||
AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFPirPBPO01zUuf7xC+ds
|
||||
bOOY5QvAMA0GCSqGSIb3DQEBCwUAA4ICAQBUYa+ydfTPKjTN4lXyEZgchZQ+juny
|
||||
aMy1xosLz6Evj0us2Bwczmy6X2Zvaw/KteFlgKaU1Ex2UkU7FfAlaH0HtwTLFMVM
|
||||
p9nB7ZzStvg0n8zFM29SEkOFwZ9FRonxx4sY3FdvI4QvAWyDyqgOl8+Eedg0kC4+
|
||||
M7hxarTFmZZ7POZl8Hio592yx3asMmSCcmb7oUCKVI98qsf9fuL+LIZSpn4fE7av
|
||||
AiNBcOqCZ10CRnl4VSgAW2LH4oqROYdUv+me1u1YRwh7fCF/R7VjOLuaDzv0mp/g
|
||||
hzG9U+Yso3WV4b28MsctwUmGTK8Zc5QaANKgmI3ulkta37wN5KjrUuescHC7MqZg
|
||||
vN9n60801be1EoUL83KUx57Bix95YZR02Zge0gYdYTb+E2bwaZ4GMlf7cs6qmC6A
|
||||
ZPLR7Tffw2J4dPTcfEx3rPZ91s3MkAdPzYYGdGlbKp8RCFnezZ7rw2z57rnT0zDr
|
||||
LuL3Q6ADBfothoos/EBIC5ekXb9czp8gig+nJXLC6jlqcQpCLrV88oS3+8zACmx1
|
||||
d6tje9uuAqPgiQGddKZj4b4BlHmAMXq0PufQsZVoyzboTewZiLVCtTR9/iF7Cepg
|
||||
6EVv57p61pFhPu8lNRAi0aH/po9yt+7435FGpn2kan6k9aDIVdaqeuxxITwsqJ4R
|
||||
WwSa13hh6yjoDQ==
|
||||
-----END CERTIFICATE-----`
|
||||
caCRL = `-----BEGIN X509 CRL-----
|
||||
MIICpzCBkAIBATANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDEwhDZXJ0QXV0aBcN
|
||||
MjEwMTAyMjEzNDA1WhcNMjMwMTAyMjEzNDA1WjAkMCICEQC+l04DbHWMyC3fG09k
|
||||
VXf+Fw0yMTAxMDIyMTM0MDVaoCMwITAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJc
|
||||
N8OTorgbzDANBgkqhkiG9w0BAQsFAAOCAgEAEJ7z+uNc8sqtxlOhSdTGDzX/xput
|
||||
E857kFQkSlMnU2whQ8c+XpYrBLA5vIZJNSSwohTpM4+zVBX/bJpmu3wqqaArRO9/
|
||||
YcW5mQk9Anvb4WjQW1cHmtNapMTzoC9AiYt/OWPfy+P6JCgCr4Hy6LgQyIRL6bM9
|
||||
VYTalolOm1qa4Y5cIeT7iHq/91mfaqo8/6MYRjLl8DOTROpmw8OS9bCXkzGKdCat
|
||||
AbAzwkQUSauyoCQ10rpX+Y64w9ng3g4Dr20aCqPf5osaqplEJ2HTK8ljDTidlslv
|
||||
9anQj8ax3Su89vI8+hK+YbfVQwrThabgdSjQsn+veyx8GlP8WwHLAQ379KjZjWg+
|
||||
OlOSwBeU1vTdP0QcB8X5C2gVujAyuQekbaV86xzIBOj7vZdfHZ6ee30TZ2FKiMyg
|
||||
7/N2OqW0w77ChsjB4MSHJCfuTgIeg62GzuZXLM+Q2Z9LBdtm4Byg+sm/P52adOEg
|
||||
gVb2Zf4KSvsAmA0PIBlu449/QXUFcMxzLFy7mwTeZj2B4Ln0Hm0szV9f9R8MwMtB
|
||||
SyLYxVH+mgqaR6Jkk22Q/yYyLPaELfafX5gp/AIXG8n0zxfVaTvK3auSgb1Q6ZLS
|
||||
5QH9dSIsmZHlPq7GoSXmKpMdjUL8eaky/IMteioyXgsBiATzl5L2dsw6MTX3MDF0
|
||||
QbDK+MzhmbKfDxs=
|
||||
MjIwNzA0MTU1MzU4WhcNMjQwNzAzMTU1MzU4WjAkMCICEQDZo5Q3lhxFuDUsxGNm
|
||||
794YFw0yMjA3MDQxNTUzNThaoCMwITAfBgNVHSMEGDAWgBT4qzwTztNc1Ln+8Qvn
|
||||
bGzjmOULwDANBgkqhkiG9w0BAQsFAAOCAgEA1lK6g8qmhyY6myx8342dDuaauY03
|
||||
0iojkxpasuYcytK6XRm96YqjZK9EETxsHHViVU0vCXES60D6wJ9gw4fTWn3WxEdx
|
||||
nIwbGyjUGHh2y+R3uQsfvwxsdYvDsTLAnOLwOo68dAHWmMDZRmgTuGNoYFxVQRGR
|
||||
Cn90ZR7LPLpCScclWM8FE/W1B90x3ZE8EhJiCI/WyyTh3EgshmB7A5GoDrFZfmvR
|
||||
dzoTKO+F9p2XjtmgfiBE3czWQysfATmbutZUbG/ZRb89u+ZEUyPoC94mg8fhNWoX
|
||||
1d5G9QAkZFHp957/5QHLq9OHNfnWXoohhebjF4VWqZH7w+RtLc8t0PIog2lX4t1o
|
||||
5N/xFk9akvuoyNGg/fYuJBmN162Q0MdeYfYKDGWdXxf6fpHxVr5v2JrIx6gOwubb
|
||||
cIKP22ZBv/PYOeFsAZ755lTl4OTFUjU5ZJEPD6pUc1daaIqfxsxu8gDZP92FZjsB
|
||||
zaalMbh30n2OhagSMBzSLg5rE6WmBzlQX0ZN8YrW4l2Vq6twnnFHY+UyblRZS+d4
|
||||
oHBaoOaxPEkLxNZ8ulzJS4B6c4D1CXOaBEf++snVzRRUOEdX3x7TvkkrLvIsm06R
|
||||
ux0L1zJb9LbZ/1rhuv70z/kIlD55sqYuRqu3RpgTgZuTERU//rYIqWd03Y5Qon8i
|
||||
VoC6Yp9DPldQJrk=
|
||||
-----END X509 CRL-----`
|
||||
client1Crt = `-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAgmgAwIBAgIRAIppZHoj1hM80D7WzTEKLuAwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzEwWhcNMjIwNzAyMjEz
|
||||
MDUxWjASMRAwDgYDVQQDEwdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiVbJtH
|
||||
XVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd20jP
|
||||
yhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1UHw4
|
||||
3Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZmH859
|
||||
DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0habT
|
||||
cDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBSJ5GIv
|
||||
zIrE4ZSQt2+CGblKTDswizAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb
|
||||
zDANBgkqhkiG9w0BAQsFAAOCAgEALh4f5GhvNYNou0Ab04iQBbLEdOu2RlbK1B5n
|
||||
K9P/umYenBHMY/z6HT3+6tpcHsDuqE8UVdq3f3Gh4S2Gu9m8PRitT+cJ3gdo9Plm
|
||||
3rD4ufn/s6rGg3ppydXcedm17492tbccUDWOBZw3IO/ASVq13WPgT0/Kev7cPq0k
|
||||
sSdSNhVeXqx8Myc2/d+8GYyzbul2Kpfa7h9i24sK49E9ftnSmsIvngONo08eT1T0
|
||||
3wAOyK2981LIsHaAWcneShKFLDB6LeXIT9oitOYhiykhFlBZ4M1GNlSNfhQ8IIQP
|
||||
xbqMNXCLkW4/BtLhGEEcg0QVso6Kudl9rzgTfQknrdF7pHp6rS46wYUjoSyIY6dl
|
||||
oLmnoAVJX36J3QPWelePI9e07X2wrTfiZWewwgw3KNRWjd6/zfPLe7GoqXnK1S2z
|
||||
PT8qMfCaTwKTtUkzXuTFvQ8bAo2My/mS8FOcpkt2oQWeOsADHAUX7fz5BCoa2DL3
|
||||
k/7Mh4gVT+JYZEoTwCFuYHgMWFWe98naqHi9lB4yR981p1QgXgxO7qBeipagKY1F
|
||||
LlH1iwXUqZ3MZnkNA+4e1Fglsw3sa/rC+L98HnznJ/YbTfQbCP6aQ1qcOymrjMud
|
||||
7MrFwqZjtd/SK4Qx1VpK6jGEAtPgWBTUS3p9ayg6lqjMBjsmySWfvRsDQbq6P5Ct
|
||||
O/e3EH8=
|
||||
MIIEITCCAgmgAwIBAgIRAJla/m/UkZMifNwG+DxFr2MwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjIwNzA0MTU0MzM3WhcNMjQwMTA0MTU1
|
||||
MzA3WjASMRAwDgYDVQQDEwdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA8xM5v+2QfdzfwnNT5cl+6oEy2fZoI2YG6L6c25rG0pr+yl1IHKdM
|
||||
Zcvn93uat7hlbzxeOLfJRM7+QK1lLaxuppq9p+gT+1x9eG3E4X7e0pdbjrpJGbvN
|
||||
ji0hwDBLDWD8mHNq/SCk9FKtGnfZqrNB5BLw2uIKjJzVGXVlsjN6geBDm2hVjTSm
|
||||
zMr39CfLUdtvMaZhpIPJzbH+sNfp1zKavFIpmwCd77p/z0QAiQ9NaIvzv4PZDDEE
|
||||
MUHzmVAU6bUjD8GToXaMbRiz694SU8aAwvvcdjGexdbHnfSAfLOl2wTPPxvePncR
|
||||
aa656ZeZWxY9pRCItP+v43nm7d4sAyRD4QIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBQbwDqF
|
||||
aja3ifZHm6mtSeTK9IHc+zAfBgNVHSMEGDAWgBT4qzwTztNc1Ln+8QvnbGzjmOUL
|
||||
wDANBgkqhkiG9w0BAQsFAAOCAgEAprE/zV6u8UIH8g4Jb73wtUD/eIL3iBJ7mNYa
|
||||
lqwCyJrWH7/F9fcovJnF9WO1QPTeHxhoD9rlQK70GitUAeboYw611yNWDS4tDlaL
|
||||
sjpJKykUxBgBR7QSLZCrPtQ3fP2WvlZzLGqB28rASTLphShqTuGp4gJaxGHfbCU7
|
||||
mlV9QYi+InQxOICJJPebXUOwx5wYkFQWJ9qE1AK3QrWPi8QYFznJvHgkNAaMBEmI
|
||||
jAlggOzpveVvy8f4z3QG9o29LIwp7JvtJQs7QXL80FZK98/8US/3gONwTrBz2Imx
|
||||
28ywvwCq7fpMyPgxX4sXtxphCNim+vuHcqDn2CvLS9p/6L6zzqbFNxpmMkJDLrOc
|
||||
YqtHE4TLWIaXpb5JNrYJgNCZyJuYDICVTbivtMacHpSwYtXQ4iuzY2nIr0+4y9i9
|
||||
MNpqv3W47xnvgUQa5vbTbIqo2NSY24A84mF5EyjhaNgNtDlN56+qTQ6HLZNVr6pv
|
||||
eUCCWnY4GkaZUEU1M8/uNtKaZKv1WA7gJxZDQHj8+R110mPtzm1C5jqg7jSjGy9C
|
||||
8PhAwBqIXkVLNayFEtyZZobTxMH5qY1yFkI3sic7S9ZyXt3quY1Q1UT3liRteIm/
|
||||
sZHC5zEoidsHObkTeU44hqZVPkbvrfmgW01xTJjddnMPBH+yqjCCc94yCbW79j/2
|
||||
7LEmxYg=
|
||||
-----END CERTIFICATE-----`
|
||||
client1Key = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAoKbYY9MdF2kF/nhBESIiZTdVYtA8XL9xrIZyDj9EnCiTxHiV
|
||||
bJtHXVwszqSl5TRrotPmnmAQcX3r8OCk+z+RQZ0QQj257P3kG6q4rNnOcWCS5xEd
|
||||
20jPyhQ3m+hMGfZsotNTQze1ochuQgLUN6IPyPxZkH22ia3jX4iu1eo/QxeLYHj1
|
||||
UHw43Cii9yE+j5kPUC21xmnrGKdUrB55NYLXHx6yTIqYR5znSOVB8oJi18/hwdZm
|
||||
H859DHhm0Hx1HrS+jbjI3+CMorZJ3WUyNf+CkiVLD3xYutPbxzEpwiqkG/XYzLH0
|
||||
habTcDcILo18n+o3jvem2KWBrDhyairjIDscwQIDAQABAoIBAEBSjVFqtbsp0byR
|
||||
aXvyrtLX1Ng7h++at2jca85Ihq//jyqbHTje8zPuNAKI6eNbmb0YGr5OuEa4pD9N
|
||||
ssDmMsKSoG/lRwwcm7h4InkSvBWpFShvMgUaohfHAHzsBYxfnh+TfULsi0y7c2n6
|
||||
t/2OZcOTRkkUDIITnXYiw93ibHHv2Mv2bBDu35kGrcK+c2dN5IL5ZjTjMRpbJTe2
|
||||
44RBJbdTxHBVSgoGBnugF+s2aEma6Ehsj70oyfoVpM6Aed5kGge0A5zA1JO7WCn9
|
||||
Ay/DzlULRXHjJIoRWd2NKvx5n3FNppUc9vJh2plRHalRooZ2+MjSf8HmXlvG2Hpb
|
||||
ScvmWgECgYEA1G+A/2KnxWsr/7uWIJ7ClcGCiNLdk17Pv3DZ3G4qUsU2ITftfIbb
|
||||
tU0Q/b19na1IY8Pjy9ptP7t74/hF5kky97cf1FA8F+nMj/k4+wO8QDI8OJfzVzh9
|
||||
PwielA5vbE+xmvis5Hdp8/od1Yrc/rPSy2TKtPFhvsqXjqoUmOAjDP8CgYEAwZjH
|
||||
9dt1sc2lx/rMxihlWEzQ3JPswKW9/LJAmbRBoSWF9FGNjbX7uhWtXRKJkzb8ZAwa
|
||||
88azluNo2oftbDD/+jw8b2cDgaJHlLAkSD4O1D1RthW7/LKD15qZ/oFsRb13NV85
|
||||
ZNKtwslXGbfVNyGKUVFm7fVA8vBAOUey+LKDFj8CgYEAg8WWstOzVdYguMTXXuyb
|
||||
ruEV42FJaDyLiSirOvxq7GTAKuLSQUg1yMRBIeQEo2X1XU0JZE3dLodRVhuO4EXP
|
||||
g7Dn4X7Th9HSvgvNuIacowWGLWSz4Qp9RjhGhXhezUSx2nseY6le46PmFavJYYSR
|
||||
4PBofMyt4PcyA6Cknh+KHmkCgYEAnTriG7ETE0a7v4DXUpB4TpCEiMCy5Xs2o8Z5
|
||||
ZNva+W+qLVUWq+MDAIyechqeFSvxK6gRM69LJ96lx+XhU58wJiFJzAhT9rK/g+jS
|
||||
bsHH9WOfu0xHkuHA5hgvvV2Le9B2wqgFyva4HJy82qxMxCu/VG/SMqyfBS9OWbb7
|
||||
ibQhdq0CgYAl53LUWZsFSZIth1vux2LVOsI8C3X1oiXDGpnrdlQ+K7z57hq5EsRq
|
||||
GC+INxwXbvKNqp5h0z2MvmKYPDlGVTgw8f8JjM7TkN17ERLcydhdRrMONUryZpo8
|
||||
1xTob+8blyJgfxZUIAKbMbMbIiU0WAF0rfD/eJJwS4htOW/Hfv4TGA==
|
||||
MIIEpAIBAAKCAQEA8xM5v+2QfdzfwnNT5cl+6oEy2fZoI2YG6L6c25rG0pr+yl1I
|
||||
HKdMZcvn93uat7hlbzxeOLfJRM7+QK1lLaxuppq9p+gT+1x9eG3E4X7e0pdbjrpJ
|
||||
GbvNji0hwDBLDWD8mHNq/SCk9FKtGnfZqrNB5BLw2uIKjJzVGXVlsjN6geBDm2hV
|
||||
jTSmzMr39CfLUdtvMaZhpIPJzbH+sNfp1zKavFIpmwCd77p/z0QAiQ9NaIvzv4PZ
|
||||
DDEEMUHzmVAU6bUjD8GToXaMbRiz694SU8aAwvvcdjGexdbHnfSAfLOl2wTPPxve
|
||||
PncRaa656ZeZWxY9pRCItP+v43nm7d4sAyRD4QIDAQABAoIBADE17zcgDWSt1s8z
|
||||
MgUPahZn2beu3x5rhXKRRIhhKWdx4atufy7t39WsFmZQK96OAlsmyZyJ+MFpdqf5
|
||||
csZwZmZsZYEcxw7Yhr5e2sEcQlg4NF0M8ce38cGa+X5DSK6IuBrVIw/kEAE2y7zU
|
||||
Dsk0SV63RvPJV4FoLuxcjB4rtd2c+JBduNUXQYVppz/KhsXN+9CbPbZ7wo1cB5fo
|
||||
Iu/VswvvW6EAxVx39zZcwSGdkss9XUktU8akx7T/pepIH6fwkm7uXSNez6GH9d1I
|
||||
8qOiORk/gAtqPL1TJgConyYheWMM9RbXP/IwL0BV8U4ZVG53S8jx2XpP4OJQ+k35
|
||||
WYvz8JECgYEA+9OywKOG2lMiiUB1qZfmXB80PngNsz+L6xUWkrw58gSqYZIg0xyH
|
||||
Sfr7HBo0yn/PB0oMMWPpNfYvG8/kSMIWiVlsYz9fdsUuqIvN+Kh9VF6o2wn+gnJk
|
||||
sBE3KVMofcgwgLE6eMVv2MSQlBoXhGPNlCBHS1gorQdYE82dxDPBBzsCgYEA9xpm
|
||||
c3C9LxiVbw9ZZ5D2C+vzwIG2+ZeDwKSizM1436MAnzNQgQTMzQ20uFGNBD562VjI
|
||||
rHFlZYr3KCtSIw5gvCSuox0YB64Yq/WAtGZtH9JyKRz4h4juq6iM4FT7nUwM4DF9
|
||||
3CUiDS8DGoqvCNpY50GvzSR5QVT1DKTZsMunh5MCgYEAyIWMq7pK0iQqtvG9/3o1
|
||||
8xrhxfBgsF+kcV+MZvE8jstKRIFQY+oujCkutPTlHm3hE2PSC64L8G0Em/fRRmJO
|
||||
AbZUCT9YK8HdYlZYf2zix0DM4gW2RHcEV/KNYvmVn3q9rGvzLGHCqu/yVAvmuAOk
|
||||
mhON0Z/0W7siVjp/KtEvHisCgYA/cfTaMRkyDXLY6C0BbXPvTa7xP5z2atO2U89F
|
||||
HICrkxOmzKsf5VacU6eSJ8Y4T76FLcmglSD+uHaLRsw5Ggj2Zci9MswntKi7Bjb8
|
||||
msvr/sG3EqwxSJRXWNiLBObx1UP9EFgLfTFIB0kZuIAGmuF2xyPXXUUQ5Dpi+7S1
|
||||
MyUZpwKBgQDg+AIPvk41vQ4Cz2CKrQX5/uJSW4bOhgP1yk7ruIH4Djkag3ZzTnHM
|
||||
zA9/pLzRfz1ENc5I/WaYSh92eKw3j6tUtMJlE2AbfCpgOQtRUNs3IBmzCWrY8J01
|
||||
W/8bwB+KhfFxNYwvszYsvvOq51NgahYQkgThVm38UixB3PFpEf+NiQ==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
// client 2 crt is revoked
|
||||
client2Crt = `-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAgmgAwIBAgIRAL6XTgNsdYzILd8bT2RVd/4wDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjEwMTAyMjEyMzIwWhcNMjIwNzAyMjEz
|
||||
MDUxWjASMRAwDgYDVQQDEwdjbGllbnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY+6hi
|
||||
jcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN/4jQ
|
||||
tNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2HkO/xG
|
||||
oZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB1YFM
|
||||
s8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhtsC871
|
||||
nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTB84v5
|
||||
t9HqhLhMODbn6oYkEQt3KzAfBgNVHSMEGDAWgBTtcgjQBq/80EySIvJcN8OTorgb
|
||||
zDANBgkqhkiG9w0BAQsFAAOCAgEALGtBCve5k8tToL3oLuXp/oSik6ovIB/zq4I/
|
||||
4zNMYPU31+ZWz6aahysgx1JL1yqTa3Qm8o2tu52MbnV10dM7CIw7c/cYa+c+OPcG
|
||||
5LF97kp13X+r2axy+CmwM86b4ILaDGs2Qyai6VB6k7oFUve+av5o7aUrNFpqGCJz
|
||||
HWdtHZSVA3JMATzy0TfWanwkzreqfdw7qH0yZ9bDURlBKAVWrqnCstva9jRuv+AI
|
||||
eqxr/4Ro986TFjJdoAP3Vr16CPg7/B6GA/KmsBWJrpeJdPWq4i2gpLKvYZoy89qD
|
||||
mUZf34RbzcCtV4NvV1DadGnt4us0nvLrvS5rL2+2uWD09kZYq9RbLkvgzF/cY0fz
|
||||
i7I1bi5XQ+alWe0uAk5ZZL/D+GTRYUX1AWwCqwJxmHrMxcskMyO9pXvLyuSWRDLo
|
||||
YNBrbX9nLcfJzVCp+X+9sntTHjs4l6Cw+fLepJIgtgqdCHtbhTiv68vSM6cgb4br
|
||||
6n2xrXRKuioiWFOrTSRr+oalZh8dGJ/xvwY8IbWknZAvml9mf1VvfE7Ma5P777QM
|
||||
fsbYVTq0Y3R/5hIWsC3HA5z6MIM8L1oRe/YyhP3CTmrCHkVKyDOosGXpGz+JVcyo
|
||||
cfYkY5A3yFKB2HaCwZSfwFmRhxkrYWGEbHv3Cd9YkZs1J3hNhGFZyVMC9Uh0S85a
|
||||
6zdDidU=
|
||||
MIIEITCCAgmgAwIBAgIRANmjlDeWHEW4NSzEY2bv3hgwDQYJKoZIhvcNAQELBQAw
|
||||
EzERMA8GA1UEAxMIQ2VydEF1dGgwHhcNMjIwNzA0MTU0MzUxWhcNMjQwMTA0MTU1
|
||||
MzA3WjASMRAwDgYDVQQDEwdjbGllbnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAzNl7q7yS8MSaQs6zRbuqrsUuwEJ5ZH85vf7zHZKgOW3zNniXLOmH
|
||||
JdtQ3jKZQ1BCIsJFvez2GxGIMWbXaSPw4bL0J3vl5oItChsjGg34IvqcDxWuIk2a
|
||||
muRdMh7r1ryVs2ir2cQ5YHzI59BEpUWKQg3bD4yragdkb6BRc7lVgzCbrM1Eq758
|
||||
HHbaLwlsfpqOvheaum4IG113CeD/HHrw42W6g/qQWL+FHlYqV3plHZ8Bj+bhcZI5
|
||||
jdU4paGEzeY0a0NlnyH4gXGPjLKvPKFZHy4D6RiRlLHvHeiRyDtTu4wFkAiXxzGs
|
||||
E4UBbykmYUB85zgwpjaktOaoe36IM1T8CQIDAQABo3EwbzAOBgNVHQ8BAf8EBAMC
|
||||
A7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRdYIEk
|
||||
gxh+vTaMpAbqaPGRKGGBpTAfBgNVHSMEGDAWgBT4qzwTztNc1Ln+8QvnbGzjmOUL
|
||||
wDANBgkqhkiG9w0BAQsFAAOCAgEABSR/PbPfiNZ6FOrt91/I0g6LviwICDcuXhfr
|
||||
re4UsWp1kxXeS3CB2G71qXv3hswN8phG2hdsij0/FBEGUTLS3FTCmLmqmcVqPj3/
|
||||
677PMFDoACBKgT5iIwpnNvdD+4ROM8JFjUwy7aTWx85a5yoPFGnB+ORMfLCYjr2S
|
||||
D02KFvKuSXWCjXphqJ41cFGne4oeh/JMkN0RNArm7wTT8yWCGgO1k4OON8dphuTV
|
||||
48Wm6I9UBSWuLk1vcIlgb/8YWVwy9rBNmjOBDGuroL6PSmfZD+e9Etii0X2znZ+t
|
||||
qDpXJB7V5U0DbsBCtGM/dHaFz/LCoBYX9z6th1iPUHksUTM3RzN9L24r9/28dY/a
|
||||
shBpn5rK3ui/2mPBpO26wX14Kl/DUkdKUV9dJllSlmwo8Z0RluY9S4xnCrna/ODH
|
||||
FbhWmlTSs+odCZl6Lc0nuw+WQ2HnlTVJYBSFAGfsGQQ3pzk4DC5VynnxY0UniUgD
|
||||
WYPR8JEYa+BpH3rIQ9jmnOKWLtyc7lFPB9ab63pQBBiwRvWo+tZ2vybqjeHPuu5N
|
||||
BuKvvtu3RKKdSCnIo5Rs5zw4JYCjvlx/NVk9jtpa1lIHYHilvBmCcRX5DkE/yH/x
|
||||
IjEKhCOQpGR6D5Kkca9xNL7zNcat3bzLn+d7Wo4m09uWi9ifPdchxed0w5d9ihx1
|
||||
enqNrFI=
|
||||
-----END CERTIFICATE-----`
|
||||
client2Key = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA6xjW5KQR3/OFQtV5M75WINqQ4AzXSu6DhSz/yumaaQZP/UxY
|
||||
+6hijcrFzGo9MMie/Sza8DhkXOFAl2BelUubrOeB2cl+/Gr8OCyRi2Gv6j3zCsuN
|
||||
/4jQtNaoez/IbkDvI3l/ZpzBtnuNY2RiemGgHuORXHRVf3qVlsw+npBIRW5rM2Hk
|
||||
O/xGoZjeBErWVu390Lyn+Gvk2TqQDnkutWnxUC60/zPlHhXZ4BwaFAekbSnjsSDB
|
||||
1YFMs8HwW4oBryoxdj3/+/qLrBHt75IdLw3T7/V1UDJQM3EvSQOr12w4egpldhts
|
||||
C871nnBQZeY6qA5feffIwwg/6lJm70o6S6OX6wIDAQABAoIBAFatstVb1KdQXsq0
|
||||
cFpui8zTKOUiduJOrDkWzTygAmlEhYtrccdfXu7OWz0x0lvBLDVGK3a0I/TGrAzj
|
||||
4BuFY+FM/egxTVt9in6fmA3et4BS1OAfCryzUdfK6RV//8L+t+zJZ/qKQzWnugpy
|
||||
QYjDo8ifuMFwtvEoXizaIyBNLAhEp9hnrv+Tyi2O2gahPvCHsD48zkyZRCHYRstD
|
||||
NH5cIrwz9/RJgPO1KI+QsJE7Nh7stR0sbr+5TPU4fnsL2mNhMUF2TJrwIPrc1yp+
|
||||
YIUjdnh3SO88j4TQT3CIrWi8i4pOy6N0dcVn3gpCRGaqAKyS2ZYUj+yVtLO4KwxZ
|
||||
SZ1lNvECgYEA78BrF7f4ETfWSLcBQ3qxfLs7ibB6IYo2x25685FhZjD+zLXM1AKb
|
||||
FJHEXUm3mUYrFJK6AFEyOQnyGKBOLs3S6oTAswMPbTkkZeD1Y9O6uv0AHASLZnK6
|
||||
pC6ub0eSRF5LUyTQ55Jj8D7QsjXJueO8v+G5ihWhNSN9tB2UA+8NBmkCgYEA+weq
|
||||
cvoeMIEMBQHnNNLy35bwfqrceGyPIRBcUIvzQfY1vk7KW6DYOUzC7u+WUzy/hA52
|
||||
DjXVVhua2eMQ9qqtOav7djcMc2W9RbLowxvno7K5qiCss013MeWk64TCWy+WMp5A
|
||||
AVAtOliC3hMkIKqvR2poqn+IBTh1449agUJQqTMCgYEAu06IHGq1GraV6g9XpGF5
|
||||
wqoAlMzUTdnOfDabRilBf/YtSr+J++ThRcuwLvXFw7CnPZZ4TIEjDJ7xjj3HdxeE
|
||||
fYYjineMmNd40UNUU556F1ZLvJfsVKizmkuCKhwvcMx+asGrmA+tlmds4p3VMS50
|
||||
KzDtpKzLWlmU/p/RINWlRmkCgYBy0pHTn7aZZx2xWKqCDg+L2EXPGqZX6wgZDpu7
|
||||
OBifzlfM4ctL2CmvI/5yPmLbVgkgBWFYpKUdiujsyyEiQvWTUKhn7UwjqKDHtcsk
|
||||
G6p7xS+JswJrzX4885bZJ9Oi1AR2yM3sC9l0O7I4lDbNPmWIXBLeEhGMmcPKv/Kc
|
||||
91Ff4wKBgQCF3ur+Vt0PSU0ucrPVHjCe7tqazm0LJaWbPXL1Aw0pzdM2EcNcW/MA
|
||||
w0kqpr7MgJ94qhXCBcVcfPuFN9fBOadM3UBj1B45Cz3pptoK+ScI8XKno6jvVK/p
|
||||
xr5cb9VBRBtB9aOKVfuRhpatAfS2Pzm2Htae9lFn7slGPUmu2hkjDw==
|
||||
MIIEowIBAAKCAQEAzNl7q7yS8MSaQs6zRbuqrsUuwEJ5ZH85vf7zHZKgOW3zNniX
|
||||
LOmHJdtQ3jKZQ1BCIsJFvez2GxGIMWbXaSPw4bL0J3vl5oItChsjGg34IvqcDxWu
|
||||
Ik2amuRdMh7r1ryVs2ir2cQ5YHzI59BEpUWKQg3bD4yragdkb6BRc7lVgzCbrM1E
|
||||
q758HHbaLwlsfpqOvheaum4IG113CeD/HHrw42W6g/qQWL+FHlYqV3plHZ8Bj+bh
|
||||
cZI5jdU4paGEzeY0a0NlnyH4gXGPjLKvPKFZHy4D6RiRlLHvHeiRyDtTu4wFkAiX
|
||||
xzGsE4UBbykmYUB85zgwpjaktOaoe36IM1T8CQIDAQABAoIBAETHMJK0udFE8VZE
|
||||
+EQNgn0zj0LWDtQDM2vrUc04Ebu2gtZjHr7hmZLIVBqGepbzN4FcIPZnvSnRdRzB
|
||||
HsoaWyIsZ3VqUAJY6q5d9iclUY7M/eDCsripvaML0Y6meyCaKNkX57sx+uG+g+Xx
|
||||
M1saQhVzeX17CYKMANjJxw9HxsJI0aBPyiBbILHMwfRfsJU8Ou72HH1sIQuPdH2H
|
||||
/c9ru8YZAno6oVq1zuC/pCis+h50U9HzTnt3/4NNS6cWG/y2YLztCvm9uGo4MTd/
|
||||
mA9s4cxVhvQW6gCDHgGn6zj661OL/d2rpak1eWizhZvZ8jsIN/sM87b0AJeVT4zH
|
||||
6xA3egECgYEA1nI5EsCetQbFBp7tDovSp3fbitwoQtdtHtLn2u4DfvmbLrgSoq0Z
|
||||
L+9N13xML/l8lzWai2gI69uA3c2+y1O64LkaiSeDqbeBp9b6fKMlmwIVbklEke1w
|
||||
XVTIWOYTTF5/8+tUOlsgme5BhLAWnQ7+SoitzHtl5e1vEYaAGamE2DECgYEA9Is2
|
||||
BbTk2YCqkcsB7D9q95JbY0SZpecvTv0rLR+acz3T8JrAASdmvqdBOlPWc+0ZaEdS
|
||||
PcJaOEw3yxYJ33cR/nLBaR2/Uu5qQebyPALs3B2pjjTFdGvcpeFxO55fowwsfR/e
|
||||
0H+HeiFj5Y4S+kFWT+3FRmJ6GUB828LJYaVhQ1kCgYEA1bdsTdYN1Vfzz89fbZnH
|
||||
zQLUl6UlssfDhm6mhzeh4E+eaocke1+LtIwHxfOocj9v/bp8VObPzU8rNOIxfa3q
|
||||
lr+jRIFO5DtwSfckGEb32W3QMeNvJQe/biRqrr5NCVU8q7kibi4XZZFfVn+vacNh
|
||||
hqKEoz9vpCBnCs5CqFCbhmECgYAG8qWYR+lwnI08Ey58zdh2LDxYd6x94DGh5uOB
|
||||
JrK2r30ECwGFht8Ob6YUyCkBpizgn5YglxMFInU7Webx6GokdpI0MFotOwTd1nfv
|
||||
aI3eOyGEHs+1XRMpy1vyO6+v7DqfW3ZzKgxpVeWGsiCr54tSPgkq1MVvTju96qza
|
||||
D17SEQKBgCKC0GjDjnt/JvujdzHuBt1sWdOtb+B6kQvA09qVmuDF/Dq36jiaHDjg
|
||||
XMf5HU3ThYqYn3bYypZZ8nQ7BXVh4LqGNqG29wR4v6l+dLO6odXnLzfApGD9e+d4
|
||||
2tmlLP54LaN35hQxRjhT8lCN0BkrNF44+bh8frwm/kuxSd8wT2S+
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
testFileName = "test_file_ftp.dat"
|
||||
testDLFileName = "test_download_ftp.dat"
|
||||
|
@ -600,8 +614,18 @@ func TestListDirWithWildcards(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveUser(localUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(localUser.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
|
||||
for _, user := range []dataprovider.User{localUser, sftpUser} {
|
||||
client, err := getFTPClient(user, true, nil)
|
||||
client, err := getFTPClient(user, true, nil, ftp.DialWithDisabledMLSD(true))
|
||||
if assert.NoError(t, err) {
|
||||
dir1 := "test.dir"
|
||||
dir2 := "test.dir1"
|
||||
|
@ -619,58 +643,96 @@ func TestListDirWithWildcards(t *testing.T) {
|
|||
localDownloadPath := filepath.Join(homeBasePath, testDLFileName)
|
||||
err = ftpDownloadFile(fileName, localDownloadPath, testFileSize, client, 0)
|
||||
assert.NoError(t, err)
|
||||
entries, err := client.NameList(fileName)
|
||||
if assert.NoError(t, err) {
|
||||
entries, err := client.List(fileName)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Equal(t, fileName, entries[0].Name)
|
||||
nListEntries, err := client.NameList(fileName)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, fileName)
|
||||
entries, err = client.List(".")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 3)
|
||||
nListEntries, err = client.NameList(".")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nListEntries, 3)
|
||||
entries, err = client.List("/test.*")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 2)
|
||||
found := 0
|
||||
for _, e := range entries {
|
||||
switch e.Name {
|
||||
case dir1, dir2:
|
||||
found++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, 2, found)
|
||||
nListEntries, err = client.NameList("/test.*")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 2)
|
||||
assert.Contains(t, nListEntries, dir1)
|
||||
assert.Contains(t, nListEntries, dir2)
|
||||
entries, err = client.List("/*.dir?")
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, fileName)
|
||||
}
|
||||
entries, err = client.NameList(".")
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, entries, 3)
|
||||
entries, err = client.NameList("/test.*")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 2)
|
||||
assert.Contains(t, entries, dir1)
|
||||
assert.Contains(t, entries, dir2)
|
||||
}
|
||||
entries, err = client.NameList("/*.dir?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, dir2)
|
||||
}
|
||||
entries, err = client.NameList("/test.???")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, dir1)
|
||||
}
|
||||
assert.Equal(t, dir2, entries[0].Name)
|
||||
nListEntries, err = client.NameList("/*.dir?")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, dir2)
|
||||
entries, err = client.List("/test.???")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Equal(t, dir1, entries[0].Name)
|
||||
nListEntries, err = client.NameList("/test.???")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, dir1)
|
||||
_, err = client.NameList("/missingdir/test.*")
|
||||
assert.Error(t, err)
|
||||
_, err = client.List("/missingdir/test.*")
|
||||
assert.Error(t, err)
|
||||
_, err = client.NameList("test[-]")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), path.ErrBadPattern.Error())
|
||||
}
|
||||
_, err = client.List("test[-]")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), path.ErrBadPattern.Error())
|
||||
}
|
||||
subDir := path.Join(dir1, "sub.d")
|
||||
err = client.MakeDir(subDir)
|
||||
assert.NoError(t, err)
|
||||
err = client.ChangeDir(path.Dir(subDir))
|
||||
assert.NoError(t, err)
|
||||
entries, err = client.NameList("sub.?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Base(subDir))
|
||||
}
|
||||
entries, err = client.NameList("../*.dir?")
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Join("../", dir2))
|
||||
}
|
||||
entries, err = client.List("sub.?")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, path.Base(subDir), entries[0].Name)
|
||||
nListEntries, err = client.NameList("sub.?")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, path.Base(subDir))
|
||||
entries, err = client.List("../*.dir?")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Equal(t, path.Join("../", dir2), entries[0].Name)
|
||||
nListEntries, err = client.NameList("../*.dir?")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, path.Join("../", dir2))
|
||||
|
||||
err = client.ChangeDir("/")
|
||||
assert.NoError(t, err)
|
||||
entries, err = client.NameList(path.Join(dir1, "sub.*"))
|
||||
if assert.NoError(t, err) {
|
||||
assert.Len(t, entries, 1)
|
||||
assert.Contains(t, entries, path.Join(dir1, "sub.d"))
|
||||
}
|
||||
entries, err = client.List(path.Join(dir1, "sub.*"))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Equal(t, path.Join(dir1, "sub.d"), entries[0].Name)
|
||||
nListEntries, err = client.NameList(path.Join(dir1, "sub.*"))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, entries, 1)
|
||||
assert.Contains(t, nListEntries, path.Join(dir1, "sub.d"))
|
||||
err = client.RemoveDir(subDir)
|
||||
assert.NoError(t, err)
|
||||
err = client.RemoveDir(dir1)
|
||||
|
@ -685,13 +747,6 @@ func TestListDirWithWildcards(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveUser(localUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(localUser.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestStartDirectory(t *testing.T) {
|
||||
|
@ -875,7 +930,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
|||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf := config.GetProviderConf()
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
providerConf.ExternalAuthHook = extAuthPath
|
||||
providerConf.ExternalAuthScope = 0
|
||||
|
@ -899,7 +954,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
|||
Type: sdk.GroupTypePrimary,
|
||||
},
|
||||
}
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
_, err = getFTPClient(u, true, nil)
|
||||
if !assert.Error(t, err) {
|
||||
|
@ -910,7 +965,7 @@ func TestLoginExternalAuth(t *testing.T) {
|
|||
}
|
||||
|
||||
u.Groups = nil
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
u.Username = defaultUsername + "1"
|
||||
client, err = getFTPClient(u, true, nil)
|
||||
|
@ -1651,6 +1706,23 @@ func TestResume(t *testing.T) {
|
|||
expected := append(data, data...)
|
||||
assert.Equal(t, expected, readed)
|
||||
}
|
||||
// append to a new file
|
||||
srcFile, err = os.Open(testFilePath)
|
||||
if assert.NoError(t, err) {
|
||||
newFileName := testFileName + "_new"
|
||||
err = client.Append(newFileName, srcFile)
|
||||
assert.NoError(t, err)
|
||||
err = srcFile.Close()
|
||||
assert.NoError(t, err)
|
||||
size, err := client.FileSize(newFileName)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(len(data)), size)
|
||||
err = ftpDownloadFile(newFileName, localDownloadPath, int64(len(data)), client, 0)
|
||||
assert.NoError(t, err)
|
||||
readed, err = os.ReadFile(localDownloadPath)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, readed)
|
||||
}
|
||||
err = client.Quit()
|
||||
assert.NoError(t, err)
|
||||
err = os.Remove(testFilePath)
|
||||
|
@ -3008,7 +3080,7 @@ func TestExternalAuthWithClientCert(t *testing.T) {
|
|||
err = config.LoadConfig(configDir, "")
|
||||
assert.NoError(t, err)
|
||||
providerConf := config.GetProviderConf()
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u, false, ""), os.ModePerm)
|
||||
err = os.WriteFile(extAuthPath, getExtAuthScriptContent(u), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
providerConf.ExternalAuthHook = extAuthPath
|
||||
providerConf.ExternalAuthScope = 8
|
||||
|
@ -3340,8 +3412,10 @@ func getFTPClientImplicitTLS(user dataprovider.User) (*ftp.ServerConn, error) {
|
|||
return client, err
|
||||
}
|
||||
|
||||
func getFTPClient(user dataprovider.User, useTLS bool, tlsConfig *tls.Config) (*ftp.ServerConn, error) {
|
||||
func getFTPClient(user dataprovider.User, useTLS bool, tlsConfig *tls.Config, dialOptions ...ftp.DialOption,
|
||||
) (*ftp.ServerConn, error) {
|
||||
ftpOptions := []ftp.DialOption{ftp.DialWithTimeout(5 * time.Second)}
|
||||
ftpOptions = append(ftpOptions, dialOptions...)
|
||||
if useTLS {
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = &tls.Config{
|
||||
|
@ -3422,24 +3496,13 @@ func getTestSFTPUser() dataprovider.User {
|
|||
return u
|
||||
}
|
||||
|
||||
func getExtAuthScriptContent(user dataprovider.User, nonJSONResponse bool, username string) []byte {
|
||||
func getExtAuthScriptContent(user dataprovider.User) []byte {
|
||||
extAuthContent := []byte("#!/bin/sh\n\n")
|
||||
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("if test \"$SFTPGO_AUTHD_USERNAME\" = \"%v\"; then\n", user.Username))...)
|
||||
if len(username) > 0 {
|
||||
user.Username = username
|
||||
}
|
||||
u, _ := json.Marshal(user)
|
||||
if nonJSONResponse {
|
||||
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
|
||||
} else {
|
||||
extAuthContent = append(extAuthContent, []byte(fmt.Sprintf("echo '%v'\n", string(u)))...)
|
||||
}
|
||||
extAuthContent = append(extAuthContent, []byte("else\n")...)
|
||||
if nonJSONResponse {
|
||||
extAuthContent = append(extAuthContent, []byte("echo 'text response'\n")...)
|
||||
} else {
|
||||
extAuthContent = append(extAuthContent, []byte("echo '{\"username\":\"\"}'\n")...)
|
||||
}
|
||||
extAuthContent = append(extAuthContent, []byte("fi\n")...)
|
||||
return extAuthContent
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
// Copyright (C) 2019-2022 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 ftpd
|
||||
|
||||
import (
|
||||
|
@ -28,6 +42,7 @@ var (
|
|||
type Connection struct {
|
||||
*common.BaseConnection
|
||||
clientContext ftpserver.ClientContext
|
||||
doWildcardListDir bool
|
||||
}
|
||||
|
||||
func (c *Connection) getFTPMode() string {
|
||||
|
@ -140,6 +155,7 @@ func (c *Connection) Rename(oldname, newname string) error {
|
|||
// if any happens
|
||||
func (c *Connection) Stat(name string) (os.FileInfo, error) {
|
||||
c.UpdateLastActivity()
|
||||
c.doWildcardListDir = false
|
||||
|
||||
if !c.User.HasPerm(dataprovider.PermListItems, path.Dir(name)) {
|
||||
return nil, c.GetPermissionDeniedError()
|
||||
|
@ -147,7 +163,8 @@ func (c *Connection) Stat(name string) (os.FileInfo, error) {
|
|||
|
||||
fi, err := c.DoStat(name, 0, true)
|
||||
if err != nil {
|
||||
if c.isListDirWithWildcards(path.Base(name), os.ErrNotExist) {
|
||||
if c.isListDirWithWildcards(path.Base(name)) {
|
||||
c.doWildcardListDir = true
|
||||
return vfs.NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
return nil, err
|
||||
|
@ -277,17 +294,18 @@ func (c *Connection) Symlink(oldname, newname string) error {
|
|||
func (c *Connection) ReadDir(name string) ([]os.FileInfo, error) {
|
||||
c.UpdateLastActivity()
|
||||
|
||||
files, err := c.ListDir(name)
|
||||
if err != nil {
|
||||
if c.doWildcardListDir {
|
||||
c.doWildcardListDir = false
|
||||
baseName := path.Base(name)
|
||||
if c.isListDirWithWildcards(baseName, err) {
|
||||
// we only support wildcards for the last path level, for example:
|
||||
// - *.xml is supported
|
||||
// - dir*/*.xml is not supported
|
||||
return c.getListDirWithWildcards(path.Dir(name), baseName)
|
||||
name = path.Dir(name)
|
||||
c.clientContext.SetListPath(name)
|
||||
return c.getListDirWithWildcards(name, baseName)
|
||||
}
|
||||
}
|
||||
return files, err
|
||||
|
||||
return c.ListDir(name)
|
||||
}
|
||||
|
||||
// GetHandle implements ClientDriverExtentionFileTransfer
|
||||
|
@ -494,7 +512,10 @@ func (c *Connection) getListDirWithWildcards(dirName, pattern string) ([]os.File
|
|||
return files, err
|
||||
}
|
||||
validIdx := 0
|
||||
relativeBase := getPathRelativeTo(c.clientContext.Path(), dirName)
|
||||
var relativeBase string
|
||||
if c.clientContext.GetLastCommand() != "NLST" {
|
||||
relativeBase = getPathRelativeTo(c.clientContext.Path(), dirName)
|
||||
}
|
||||
for _, fi := range files {
|
||||
match, err := path.Match(pattern, fi.Name())
|
||||
if err != nil {
|
||||
|
@ -510,12 +531,10 @@ func (c *Connection) getListDirWithWildcards(dirName, pattern string) ([]os.File
|
|||
return files[:validIdx], nil
|
||||
}
|
||||
|
||||
func (c *Connection) isListDirWithWildcards(name string, err error) bool {
|
||||
if errors.Is(err, c.GetNotExistError()) {
|
||||
func (c *Connection) isListDirWithWildcards(name string) bool {
|
||||
if strings.ContainsAny(name, "*?[]") {
|
||||
lastCommand := c.clientContext.GetLastCommand()
|
||||
if lastCommand == "LIST" || lastCommand == "NLST" {
|
||||
return strings.ContainsAny(name, "*?[]")
|
||||
}
|
||||
return lastCommand == "LIST" || lastCommand == "NLST"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue