Copy disabled (too large)
Download .txt
Showing preview only (13,264K chars total). Download the full file to get everything.
Repository: drakkan/sftpgo
Branch: main
Commit: dda97dd75835
Files: 405
Total size: 12.6 MB
Directory structure:
gitextract_3usfk2f2/
├── .cirrus.yml
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── .editorconfig
│ ├── codeql.yml
│ ├── development.yml
│ ├── docker.yml
│ └── release.yml
├── .gitignore
├── .golangci.yml
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── Dockerfile.alpine
├── Dockerfile.distroless
├── LICENSE
├── NOTICE
├── README.md
├── SECURITY.md
├── crowdin.yml
├── docker/
│ └── scripts/
│ └── download-plugins.sh
├── examples/
│ ├── OTP/
│ │ └── authy/
│ │ ├── README.md
│ │ ├── checkpwd/
│ │ │ ├── README.md
│ │ │ ├── go.mod
│ │ │ └── main.go
│ │ ├── extauth/
│ │ │ ├── README.md
│ │ │ ├── go.mod
│ │ │ └── main.go
│ │ └── keyint/
│ │ ├── README.md
│ │ ├── go.mod
│ │ └── main.go
│ ├── backup/
│ │ ├── README.md
│ │ └── backup
│ ├── bulkupdate/
│ │ ├── README.md
│ │ └── bulkuserupdate
│ ├── convertusers/
│ │ ├── README.md
│ │ └── convertusers
│ ├── ldapauth/
│ │ ├── README.md
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ ├── ldapauthserver/
│ │ ├── README.md
│ │ ├── cmd/
│ │ │ ├── root.go
│ │ │ └── serve.go
│ │ ├── config/
│ │ │ └── config.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── httpd/
│ │ │ ├── auth.go
│ │ │ ├── httpd.go
│ │ │ ├── ldapauth.go
│ │ │ ├── models.go
│ │ │ └── tlsutils.go
│ │ ├── ldapauth.toml
│ │ ├── logger/
│ │ │ ├── logger.go
│ │ │ ├── request_logger.go
│ │ │ └── sync_wrapper.go
│ │ ├── main.go
│ │ └── utils/
│ │ ├── utils.go
│ │ └── version.go
│ ├── php-activedirectory-http-server/
│ │ └── README.md
│ └── quotascan/
│ ├── README.md
│ └── scanuserquota
├── go.mod
├── go.sum
├── init/
│ ├── com.github.drakkan.sftpgo.plist
│ └── sftpgo.service
├── internal/
│ ├── acme/
│ │ ├── account.go
│ │ └── acme.go
│ ├── bundle/
│ │ └── bundle.go
│ ├── cmd/
│ │ ├── acme.go
│ │ ├── gen.go
│ │ ├── gencompletion.go
│ │ ├── genman.go
│ │ ├── initprovider.go
│ │ ├── install_windows.go
│ │ ├── ping.go
│ │ ├── portable.go
│ │ ├── portable_disabled.go
│ │ ├── reload_windows.go
│ │ ├── resetprovider.go
│ │ ├── resetpwd.go
│ │ ├── revertprovider.go
│ │ ├── root.go
│ │ ├── rotatelogs_windows.go
│ │ ├── serve.go
│ │ ├── service_windows.go
│ │ ├── signals_unix.go
│ │ ├── signals_windows.go
│ │ ├── smtptest.go
│ │ ├── start_windows.go
│ │ ├── status_windows.go
│ │ ├── stop_windows.go
│ │ └── uninstall_windows.go
│ ├── command/
│ │ ├── command.go
│ │ └── command_test.go
│ ├── common/
│ │ ├── actions.go
│ │ ├── actions_test.go
│ │ ├── clientsmap.go
│ │ ├── clientsmap_test.go
│ │ ├── common.go
│ │ ├── common_test.go
│ │ ├── connection.go
│ │ ├── connection_test.go
│ │ ├── dataretention.go
│ │ ├── dataretention_test.go
│ │ ├── defender.go
│ │ ├── defender_test.go
│ │ ├── defenderdb.go
│ │ ├── defenderdb_test.go
│ │ ├── defendermem.go
│ │ ├── eventmanager.go
│ │ ├── eventmanager_test.go
│ │ ├── eventscheduler.go
│ │ ├── httpauth.go
│ │ ├── httpauth_test.go
│ │ ├── protocol_test.go
│ │ ├── ratelimiter.go
│ │ ├── ratelimiter_test.go
│ │ ├── tlsutils.go
│ │ ├── tlsutils_test.go
│ │ ├── transfer.go
│ │ ├── transfer_test.go
│ │ ├── transferschecker.go
│ │ └── transferschecker_test.go
│ ├── config/
│ │ ├── config.go
│ │ ├── config_darwin.go
│ │ ├── config_fallback.go
│ │ ├── config_linux.go
│ │ └── config_test.go
│ ├── dataprovider/
│ │ ├── actions.go
│ │ ├── admin.go
│ │ ├── apikey.go
│ │ ├── bolt.go
│ │ ├── bolt_disabled.go
│ │ ├── cachedpassword.go
│ │ ├── cacheduser.go
│ │ ├── configs.go
│ │ ├── dataprovider.go
│ │ ├── eventrule.go
│ │ ├── group.go
│ │ ├── iplist.go
│ │ ├── memory.go
│ │ ├── mysql.go
│ │ ├── mysql_disabled.go
│ │ ├── node.go
│ │ ├── pgsql.go
│ │ ├── pgsql_disabled.go
│ │ ├── quotaupdater.go
│ │ ├── role.go
│ │ ├── scheduler.go
│ │ ├── session.go
│ │ ├── share.go
│ │ ├── sqlcommon.go
│ │ ├── sqlite.go
│ │ ├── sqlite_disabled.go
│ │ ├── sqlqueries.go
│ │ ├── unixcrypt.go
│ │ ├── unixcrypt_disabled.go
│ │ └── user.go
│ ├── ftpd/
│ │ ├── cryptfs_test.go
│ │ ├── ftpd.go
│ │ ├── ftpd_test.go
│ │ ├── handler.go
│ │ ├── internal_test.go
│ │ ├── server.go
│ │ └── transfer.go
│ ├── httpclient/
│ │ └── httpclient.go
│ ├── httpd/
│ │ ├── api_admin.go
│ │ ├── api_configs.go
│ │ ├── api_defender.go
│ │ ├── api_eventrule.go
│ │ ├── api_events.go
│ │ ├── api_folder.go
│ │ ├── api_group.go
│ │ ├── api_http_user.go
│ │ ├── api_iplist.go
│ │ ├── api_keys.go
│ │ ├── api_maintenance.go
│ │ ├── api_mfa.go
│ │ ├── api_quota.go
│ │ ├── api_retention.go
│ │ ├── api_role.go
│ │ ├── api_shares.go
│ │ ├── api_user.go
│ │ ├── api_utils.go
│ │ ├── auth_utils.go
│ │ ├── file.go
│ │ ├── flash.go
│ │ ├── flash_test.go
│ │ ├── handler.go
│ │ ├── httpd.go
│ │ ├── httpd_test.go
│ │ ├── internal_test.go
│ │ ├── middleware.go
│ │ ├── oauth2.go
│ │ ├── oauth2_test.go
│ │ ├── oidc.go
│ │ ├── oidc_test.go
│ │ ├── oidcmanager.go
│ │ ├── resetcode.go
│ │ ├── resources.go
│ │ ├── resources_embedded.go
│ │ ├── server.go
│ │ ├── token.go
│ │ ├── web.go
│ │ ├── webadmin.go
│ │ ├── webclient.go
│ │ ├── webtask.go
│ │ └── webtask_test.go
│ ├── httpdtest/
│ │ ├── httpdtest.go
│ │ └── httpfsimpl.go
│ ├── jwt/
│ │ ├── jwt.go
│ │ └── jwt_test.go
│ ├── kms/
│ │ ├── basesecret.go
│ │ ├── builtin.go
│ │ ├── kms.go
│ │ └── local.go
│ ├── logger/
│ │ ├── hclog.go
│ │ ├── lego.go
│ │ ├── logger.go
│ │ ├── mail.go
│ │ ├── request_logger.go
│ │ ├── slog.go
│ │ └── sync_wrapper.go
│ ├── metric/
│ │ ├── metric.go
│ │ └── metric_disabled.go
│ ├── mfa/
│ │ ├── mfa.go
│ │ ├── mfa_test.go
│ │ └── totp.go
│ ├── plugin/
│ │ ├── auth.go
│ │ ├── ipfilter.go
│ │ ├── kms.go
│ │ ├── notifier.go
│ │ ├── plugin.go
│ │ ├── searcher.go
│ │ └── util.go
│ ├── service/
│ │ ├── service.go
│ │ ├── service_portable.go
│ │ ├── service_windows.go
│ │ ├── signals_unix.go
│ │ └── signals_windows.go
│ ├── sftpd/
│ │ ├── cryptfs_test.go
│ │ ├── handler.go
│ │ ├── httpfs_test.go
│ │ ├── internal_test.go
│ │ ├── lister.go
│ │ ├── scp.go
│ │ ├── server.go
│ │ ├── sftpd.go
│ │ ├── sftpd_test.go
│ │ ├── ssh_cmd.go
│ │ └── transfer.go
│ ├── smtp/
│ │ ├── oauth2.go
│ │ └── smtp.go
│ ├── telemetry/
│ │ ├── router.go
│ │ ├── telemetry.go
│ │ └── telemetry_test.go
│ ├── util/
│ │ ├── errors.go
│ │ ├── i18n.go
│ │ ├── resources.go
│ │ ├── resources_embedded.go
│ │ ├── util.go
│ │ ├── util_fallback.go
│ │ └── util_unix.go
│ ├── version/
│ │ └── version.go
│ ├── vfs/
│ │ ├── azblobfs.go
│ │ ├── azblobfs_disabled.go
│ │ ├── cryptfs.go
│ │ ├── fileinfo.go
│ │ ├── filesystem.go
│ │ ├── folder.go
│ │ ├── gcsfs.go
│ │ ├── gcsfs_disabled.go
│ │ ├── httpfs.go
│ │ ├── osfs.go
│ │ ├── s3fs.go
│ │ ├── s3fs_disabled.go
│ │ ├── sftpfs.go
│ │ ├── statvfs_fallback.go
│ │ ├── statvfs_linux.go
│ │ ├── statvfs_unix.go
│ │ ├── sys_unix.go
│ │ ├── sys_windows.go
│ │ └── vfs.go
│ └── webdavd/
│ ├── file.go
│ ├── handler.go
│ ├── internal_test.go
│ ├── mimecache.go
│ ├── server.go
│ ├── webdavd.go
│ └── webdavd_test.go
├── main.go
├── openapi/
│ ├── httpfs.yaml
│ ├── openapi.yaml
│ └── swagger-ui/
│ ├── index.css
│ ├── index.html
│ ├── swagger-initializer.js
│ ├── swagger-ui-bundle.js
│ ├── swagger-ui-standalone-preset.js
│ └── swagger-ui.css
├── pkgs/
│ ├── build.sh
│ ├── choco/
│ │ ├── sftpgo.nuspec
│ │ └── tools/
│ │ └── ChocolateyInstall.ps1
│ ├── debian/
│ │ ├── changelog
│ │ ├── compat
│ │ ├── control
│ │ ├── copyright
│ │ ├── patches/
│ │ │ ├── config.diff
│ │ │ └── series
│ │ ├── postinst
│ │ ├── rules
│ │ ├── sftpgo-docs.docs
│ │ ├── sftpgo.dirs
│ │ ├── sftpgo.install
│ │ ├── sftpgo.install.arm64
│ │ ├── sftpgo.install.armhf
│ │ ├── sftpgo.install.ppc64el
│ │ └── source/
│ │ └── format
│ └── scripts/
│ ├── deb/
│ │ ├── postinstall.sh
│ │ ├── postremove.sh
│ │ └── preremove.sh
│ └── rpm/
│ ├── postinstall
│ ├── postremove
│ └── preremove
├── sftpgo.json
├── static/
│ ├── assets/
│ │ ├── css/
│ │ │ └── style.bundle.css
│ │ ├── js/
│ │ │ └── scripts.bundle.js
│ │ └── plugins/
│ │ ├── custom/
│ │ │ ├── datatables/
│ │ │ │ ├── datatables.bundle.css
│ │ │ │ └── datatables.bundle.js
│ │ │ ├── flatpickr/
│ │ │ │ └── l10n/
│ │ │ │ ├── de.js
│ │ │ │ ├── es.js
│ │ │ │ ├── fr.js
│ │ │ │ ├── it.js
│ │ │ │ └── zh.js
│ │ │ └── formrepeater/
│ │ │ └── formrepeater.bundle.js
│ │ └── global/
│ │ ├── plugins.bundle.css
│ │ └── plugins.bundle.js
│ └── locales/
│ ├── de/
│ │ └── translation.json
│ ├── en/
│ │ └── translation.json
│ ├── es/
│ │ └── translation.json
│ ├── fr/
│ │ └── translation.json
│ ├── it/
│ │ └── translation.json
│ └── zh-CN/
│ └── translation.json
├── templates/
│ ├── common/
│ │ ├── base.html
│ │ ├── baselogin.html
│ │ ├── changepassword.html
│ │ ├── forgot-password.html
│ │ ├── login.html
│ │ ├── message.html
│ │ ├── reset-password.html
│ │ ├── twofactor-recovery.html
│ │ └── twofactor.html
│ ├── email/
│ │ ├── password-expiration.html
│ │ └── reset-password.html
│ ├── webadmin/
│ │ ├── admin.html
│ │ ├── admins.html
│ │ ├── adminsetup.html
│ │ ├── base.html
│ │ ├── configs.html
│ │ ├── connections.html
│ │ ├── defender.html
│ │ ├── eventaction.html
│ │ ├── eventactions.html
│ │ ├── eventrule.html
│ │ ├── eventrules.html
│ │ ├── events.html
│ │ ├── folder.html
│ │ ├── folders.html
│ │ ├── fsconfig.html
│ │ ├── group.html
│ │ ├── groups.html
│ │ ├── iplist.html
│ │ ├── iplists.html
│ │ ├── maintenance.html
│ │ ├── mfa.html
│ │ ├── profile.html
│ │ ├── role.html
│ │ ├── roles.html
│ │ ├── status.html
│ │ ├── user.html
│ │ └── users.html
│ └── webclient/
│ ├── base.html
│ ├── editfile.html
│ ├── files.html
│ ├── mfa.html
│ ├── profile.html
│ ├── share.html
│ ├── sharedownload.html
│ ├── sharelogin.html
│ ├── shares.html
│ ├── shareupload.html
│ └── viewpdf.html
├── tests/
│ ├── eventsearcher/
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ └── ipfilter/
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── windows-installer/
├── LICENSE_with_NOTICE.txt
├── README.txt
└── sftpgo.iss
================================================
FILE CONTENTS
================================================
================================================
FILE: .cirrus.yml
================================================
freebsd_task:
name: FreeBSD
matrix:
- name: FreeBSD 14.3
freebsd_instance:
image_family: freebsd-14-3
pkginstall_script:
- pkg update -f
- pkg install -y go125
- pkg install -y git
setup_script:
- ln -s /usr/local/bin/go125 /usr/local/bin/go
- pw groupadd sftpgo
- pw useradd sftpgo -g sftpgo -w none -m
- mkdir /home/sftpgo/sftpgo
- cp -R . /home/sftpgo/sftpgo
- chown -R sftpgo:sftpgo /home/sftpgo/sftpgo
compile_script:
- su sftpgo -c 'cd ~/sftpgo && go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo'
- su sftpgo -c 'cd ~/sftpgo/tests/eventsearcher && go build -trimpath -ldflags "-s -w" -o eventsearcher'
- su sftpgo -c 'cd ~/sftpgo/tests/ipfilter && go build -trimpath -ldflags "-s -w" -o ipfilter'
check_script:
- su sftpgo -c 'cd ~/sftpgo && ./sftpgo initprovider && ./sftpgo resetprovider --force'
test_script:
- su sftpgo -c 'cd ~/sftpgo && go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 20m ./... -coverprofile=coverage.txt -covermode=atomic'
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [drakkan] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Open Source Bug Report
description: "Submit a report and help us improve SFTPGo"
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
### 👍 Thank you for contributing to our project!
Before asking for help please check our [support policy](https://github.com/drakkan/sftpgo?tab=readme-ov-file#support).
If you are a [commercial user](https://sftpgo.com/) please contact us using the dedicated [email address](mailto:support@sftpgo.com).
If you'd like to contribute code, please make sure to read and understand our [Contributor License Agreement (CLA)](https://sftpgo.com/cla.html).
You’ll be asked to accept it when submitting a pull request.
- type: checkboxes
id: before-posting
attributes:
label: "⚠️ This issue respects the following points: ⚠️"
description: All conditions are **required**.
options:
- label: This is a **bug**, not a question or a configuration issue.
required: true
- label: This issue is **not** already reported on Github _(I've searched it)_.
required: true
- type: textarea
id: bug-description
attributes:
label: Bug description
description: |
Provide a description of the bug you're experiencing.
Don't just expect someone will guess what your specific problem is and provide full details.
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Steps to reproduce
description: |
Describe the steps to reproduce the bug.
The better your description is the fastest you'll get an _(accurate)_ answer.
value: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: Describe what you expected to happen instead.
validations:
required: true
- type: input
id: version
attributes:
label: SFTPGo version
validations:
required: true
- type: input
id: data-provider
attributes:
label: Data provider
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: Installation method
description: |
Select installation method you've used.
_Describe the method in the "Additional info" section if you chose "Other"._
options:
- "Community Docker image"
- "Community Deb package"
- "Community RPM package"
- "Other"
validations:
required: true
- type: textarea
attributes:
label: Configuration
description: "Describe your customizations to the configuration: both config file changes and overrides via environment variables"
value: config
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell
- type: dropdown
id: usecase
attributes:
label: What are you using SFTPGo for?
description: We'd like to understand your SFTPGo usecase more
multiple: true
options:
- "Private user, home usecase (home backup/VPS)"
- "Professional user, 1 person business"
- "Small business (3-person firm with file exchange?)"
- "Medium business"
- "Enterprise"
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional info
description: Any additional information related to the issue.
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Commercial Support
url: https://sftpgo.com/
about: >
If you need Professional support, so your reports are prioritized and resolved more quickly.
- name: GitHub Community Discussions
url: https://github.com/drakkan/sftpgo/discussions
about: Please ask and answer questions here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 🚀 Feature request
description: Suggest an idea for SFTPGo
labels: ["suggestion"]
body:
- type: markdown
attributes:
value: |
### 👍 Thank you for contributing to our project!
Before asking for help please check our [support policy](https://github.com/drakkan/sftpgo?tab=readme-ov-file#support).
If you are a [commercial user](https://sftpgo.com/) please contact us using the dedicated [email address](mailto:support@sftpgo.com).
If you'd like to contribute code, please make sure to read and understand our [Contributor License Agreement (CLA)](https://sftpgo.com/cla.html).
You’ll be asked to accept it when submitting a pull request.
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is.
validations:
required: false
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: dropdown
id: usecase
attributes:
label: What are you using SFTPGo for?
description: We'd like to understand your SFTPGo usecase more
multiple: true
options:
- "Private user, home usecase (home backup/VPS)"
- "Professional user, 1 person business"
- "Small business (3-person firm with file exchange?)"
- "Medium business"
- "Enterprise"
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
validations:
required: false
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
# Checklist for Pull Requests
- [ ] Have you signed the [Contributor License Agreement](https://sftpgo.com/cla.html)?
---
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
#- package-ecosystem: "gomod"
# directory: "/"
# schedule:
# interval: "weekly"
# open-pull-requests-limit: 2
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 2
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 2
================================================
FILE: .github/workflows/.editorconfig
================================================
[*.yml]
indent_size = 2
================================================
FILE: .github/workflows/codeql.yml
================================================
name: "Code scanning - action"
on:
push:
pull_request:
schedule:
- cron: '30 1 * * 6'
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.25'
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: go
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
================================================
FILE: .github/workflows/development.yml
================================================
name: CI
on:
push:
branches: [main]
pull_request:
permissions:
id-token: write
contents: read
jobs:
test-deploy:
name: Test and deploy
runs-on: ${{ matrix.os }}
strategy:
matrix:
go: ['1.26']
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
- name: Build for Linux/macOS x86_64
run: |
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
cd tests/eventsearcher
go build -trimpath -ldflags "-s -w" -o eventsearcher
cd -
cd tests/ipfilter
go build -trimpath -ldflags "-s -w" -o ipfilter
cd -
./sftpgo initprovider
./sftpgo resetprovider --force
- 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 -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
- name: Run test cases using SQLite provider
run: go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -coverprofile=coverage.txt -covermode=atomic
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: ./coverage.txt
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: Run test cases using bolt provider
run: |
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/config -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/common -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/httpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 8m ./internal/sftpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/ftpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/webdavd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/telemetry -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/mfa -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/command -covermode=atomic
env:
SFTPGO_DATA_PROVIDER__DRIVER: bolt
SFTPGO_DATA_PROVIDER__NAME: 'sftpgo_bolt.db'
- name: Run test cases using memory provider
run: go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -covermode=atomic
env:
SFTPGO_DATA_PROVIDER__DRIVER: memory
SFTPGO_DATA_PROVIDER__NAME: ''
- name: Prepare build artifact for macOS
if: startsWith(matrix.os, 'macos-') == true
run: |
mkdir -p output/{init,bash_completion,zsh_completion}
cp sftpgo output/sftpgo_x86_64
cp sftpgo_arm64 output/
cp sftpgo.json output/
cp -r templates output/
cp -r static output/
cp -r openapi output/
cp init/com.github.drakkan.sftpgo.plist output/init/
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
- name: Upload build artifact
if: startsWith(matrix.os, 'ubuntu-') != true
uses: actions/upload-artifact@v7
with:
name: sftpgo-${{ matrix.os }}-go-${{ matrix.go }}
path: output
test-deploy-windows:
name: Test and deploy Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26'
- name: Run test cases using SQLite provider
run: |
cd tests/eventsearcher
go build -trimpath -ldflags "-s -w" -o eventsearcher.exe
cd ../..
cd tests/ipfilter
go build -trimpath -ldflags "-s -w" -o ipfilter.exe
cd ../..
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -coverprofile=coverage.txt -covermode=atomic
- name: Run test cases using bolt provider
run: |
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/config -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/common -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/httpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 8m ./internal/sftpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/ftpd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 5m ./internal/webdavd -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/telemetry -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/mfa -covermode=atomic
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 2m ./internal/command -covermode=atomic
env:
SFTPGO_DATA_PROVIDER__DRIVER: bolt
SFTPGO_DATA_PROVIDER__NAME: 'sftpgo_bolt.db'
- name: Run test cases using memory provider
run: go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -covermode=atomic
env:
SFTPGO_DATA_PROVIDER__DRIVER: memory
SFTPGO_DATA_PROVIDER__NAME: ''
- name: Build
run: |
$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"
$COMMITS_FROM_TAG= ((git rev-list $REV_LIST --count) | Out-String).Trim()
$FILE_VERSION = $LATEST_TAG.substring(1) + "." + $COMMITS_FROM_TAG
go install github.com/tc-hib/go-winres@latest
go-winres simply --arch amd64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o sftpgo.exe
mkdir arm64
$Env:CGO_ENABLED='0'
$Env:GOOS='windows'
$Env:GOARCH='arm64'
go-winres simply --arch arm64 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
mkdir x86
$Env:GOARCH='386'
go-winres simply --arch 386 --product-version $LATEST_TAG-dev-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
Remove-Item Env:\CGO_ENABLED
Remove-Item Env:\GOOS
Remove-Item Env:\GOARCH
- name: Initialize data provider
run: |
rm sftpgo.db
./sftpgo initprovider
shell: bash
- name: Prepare Windows installers
if: ${{ github.event_name != 'pull_request' }}
run: |
choco install innosetup
Remove-Item -LiteralPath "output" -Force -Recurse -ErrorAction Ignore
mkdir output
copy .\sftpgo.exe .\output
copy .\sftpgo.json .\output
copy .\sftpgo.db .\output
copy .\LICENSE .\output\LICENSE.txt
copy .\NOTICE .\output\NOTICE.txt
mkdir output\templates
xcopy .\templates .\output\templates\ /E
mkdir output\static
xcopy .\static .\output\static\ /E
mkdir output\openapi
xcopy .\openapi .\output\openapi\ /E
$LATEST_TAG = ((git describe --tags $(git rev-list --tags --max-count=1)) | Out-String).Trim()
$REV_LIST=$LATEST_TAG+"..HEAD"
$COMMITS_FROM_TAG= ((git rev-list $REV_LIST --count) | Out-String).Trim()
$Env:SFTPGO_ISS_DEV_VERSION = $LATEST_TAG + "." + $COMMITS_FROM_TAG
iscc .\windows-installer\sftpgo.iss
rm .\output\sftpgo.exe
rm .\output\sftpgo.db
copy .\arm64\sftpgo.exe .\output
(Get-Content .\output\sftpgo.json).replace('"sqlite"', '"bolt"') | Set-Content .\output\sftpgo.json
$Env:SFTPGO_DATA_PROVIDER__DRIVER='bolt'
$Env:SFTPGO_DATA_PROVIDER__NAME='.\output\sftpgo.db'
.\sftpgo.exe initprovider
Remove-Item Env:\SFTPGO_DATA_PROVIDER__DRIVER
Remove-Item Env:\SFTPGO_DATA_PROVIDER__NAME
$Env:SFTPGO_ISS_ARCH='arm64'
iscc .\windows-installer\sftpgo.iss
rm .\output\sftpgo.exe
copy .\x86\sftpgo.exe .\output
$Env:SFTPGO_ISS_ARCH='x86'
iscc .\windows-installer\sftpgo.iss
- name: Upload Windows installer x86_64 artifact
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v7
with:
name: sftpgo_windows_installer_x86_64
path: ./sftpgo_windows_x86_64.exe
- name: Upload Windows installer arm64 artifact
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v7
with:
name: sftpgo_windows_installer_arm64
path: ./sftpgo_windows_arm64.exe
- name: Upload Windows installer x86 artifact
if: ${{ github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v7
with:
name: sftpgo_windows_installer_x86
path: ./sftpgo_windows_x86.exe
- name: Prepare build artifact for Windows
run: |
Remove-Item -LiteralPath "output" -Force -Recurse -ErrorAction Ignore
mkdir output
copy .\sftpgo.exe .\output
mkdir output\arm64
copy .\arm64\sftpgo.exe .\output\arm64
mkdir output\x86
copy .\x86\sftpgo.exe .\output\x86
copy .\sftpgo.json .\output
(Get-Content .\output\sftpgo.json).replace('"sqlite"', '"bolt"') | Set-Content .\output\sftpgo.json
mkdir output\templates
xcopy .\templates .\output\templates\ /E
mkdir output\static
xcopy .\static .\output\static\ /E
mkdir output\openapi
xcopy .\openapi .\output\openapi\ /E
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo-windows-portable
path: output
test-build-flags:
name: Test build flags
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26'
- name: Build
run: |
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nogcs,nos3,noportable,nobolt,nomysql,nopgsql,nosqlite,nometrics,noazblob,unixcrypt -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
./sftpgo -v
cp -r openapi static templates internal/bundle/
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,bundle -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
./sftpgo -v
test-postgresql-mysql-crdb:
name: Test with PgSQL/MySQL/Cockroach
runs-on: ubuntu-latest
services:
postgres:
image: postgres:latest
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: sftpgo
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
mariadb:
image: mariadb:latest
env:
MYSQL_ROOT_PASSWORD: mysql
MYSQL_DATABASE: sftpgo
MYSQL_USER: sftpgo
MYSQL_PASSWORD: sftpgo
options: >-
--health-cmd "mariadb-admin status -h 127.0.0.1 -P 3306 -u root -p$MYSQL_ROOT_PASSWORD"
--health-interval 10s
--health-timeout 5s
--health-retries 6
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@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26'
- name: Build
run: |
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
cd tests/eventsearcher
go build -trimpath -ldflags "-s -w" -o eventsearcher
cd -
cd tests/ipfilter
go build -trimpath -ldflags "-s -w" -o ipfilter
cd -
- name: Run tests using MySQL provider
run: |
./sftpgo initprovider
./sftpgo resetprovider --force
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -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 PostgreSQL provider
run: |
./sftpgo initprovider
./sftpgo resetprovider --force
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -covermode=atomic
env:
SFTPGO_DATA_PROVIDER__DRIVER: postgresql
SFTPGO_DATA_PROVIDER__NAME: sftpgo
SFTPGO_DATA_PROVIDER__HOST: localhost
SFTPGO_DATA_PROVIDER__PORT: 5432
SFTPGO_DATA_PROVIDER__USERNAME: postgres
SFTPGO_DATA_PROVIDER__PASSWORD: postgres
- name: Run tests using MariaDB provider
run: |
./sftpgo initprovider
./sftpgo resetprovider --force
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -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: 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: |
docker run --rm --name crdb --health-cmd "curl -I http://127.0.0.1:8080" --health-interval 10s --health-timeout 5s --health-retries 6 -p 26257:26257 -d cockroachdb/cockroach:latest start-single-node --insecure --listen-addr :26257
sleep 10
docker exec crdb cockroach sql --insecure -e 'create database "sftpgo"'
./sftpgo initprovider
./sftpgo resetprovider --force
go test -v -tags nopgxregisterdefaulttypes,disable_grpc_modules -p 1 -timeout 15m ./... -covermode=atomic
docker stop crdb
env:
SFTPGO_DATA_PROVIDER__DRIVER: cockroachdb
SFTPGO_DATA_PROVIDER__NAME: sftpgo
SFTPGO_DATA_PROVIDER__HOST: localhost
SFTPGO_DATA_PROVIDER__PORT: 26257
SFTPGO_DATA_PROVIDER__USERNAME: root
SFTPGO_DATA_PROVIDER__PASSWORD:
SFTPGO_DATA_PROVIDER__TARGET_SESSION_ATTRS: any
SFTPGO_DATA_PROVIDER__SQL_TABLES_PREFIX: prefix_
build-linux-packages:
name: Build Linux packages
runs-on: ubuntu-latest
strategy:
matrix:
include:
- arch: amd64
distro: ubuntu:18.04
go: latest
go-arch: amd64
- arch: aarch64
distro: ubuntu18.04
go: latest
go-arch: arm64
- arch: ppc64le
distro: ubuntu18.04
go: latest
go-arch: ppc64le
- arch: armv7
distro: ubuntu18.04
go: latest
go-arch: arm7
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get commit SHA
id: get_commit
run: echo "COMMIT=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
shell: bash
- name: Build on amd64
if: ${{ matrix.arch == 'amd64' }}
run: |
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 | head -n 1)' >> 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 -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=${{ steps.get_commit.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/internal/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/
cp -r static output/
cp -r openapi output/
cp init/sftpgo.service output/init/
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
cp sftpgo output/
- uses: uraimo/run-on-arch-action@v3
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build
with:
arch: ${{ matrix.arch }}
distro: ${{ matrix.distro }}
setup: |
mkdir -p "${PWD}/output"
dockerRunArgs: |
--volume "${PWD}/output:/output"
shell: /bin/bash
install: |
apt-get update -q -y
apt-get install -q -y curl gcc
if [ ${{ matrix.go }} == 'latest' ]
then
GO_VERSION=$(curl -L https://go.dev/VERSION?m=text | head -n 1)
else
GO_VERSION=${{ matrix.go }}
fi
GO_DOWNLOAD_ARCH=${{ matrix.go-arch }}
if [ ${{ matrix.arch}} == 'armv7' ]
then
GO_DOWNLOAD_ARCH=armv6l
fi
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
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 -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=${{ steps.get_commit.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
mkdir -p output/{init,bash_completion,zsh_completion}
cp sftpgo.json output/
cp -r templates output/
cp -r static output/
cp -r openapi output/
cp init/sftpgo.service output/init/
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
cp sftpgo output/
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo-linux-${{ matrix.arch }}-go-${{ matrix.go }}
path: output
- name: Build Packages
id: build_linux_pkgs
run: |
export NFPM_ARCH=${{ matrix.go-arch }}
cd pkgs
./build.sh
PKG_VERSION=$(cat dist/version)
echo "pkg-version=${PKG_VERSION}" >> $GITHUB_OUTPUT
- name: Upload Debian Package
uses: actions/upload-artifact@v7
with:
name: sftpgo-${{ steps.build_linux_pkgs.outputs.pkg-version }}-${{ matrix.go-arch }}-deb
path: pkgs/dist/deb/*
- name: Upload RPM Package
uses: actions/upload-artifact@v7
with:
name: sftpgo-${{ steps.build_linux_pkgs.outputs.pkg-version }}-${{ matrix.go-arch }}-rpm
path: pkgs/dist/rpm/*
golangci-lint:
name: golangci-lint
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.26'
- uses: actions/checkout@v6
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: latest
================================================
FILE: .github/workflows/docker.yml
================================================
name: Docker
on:
#schedule:
# - cron: '0 4 * * *' # everyday at 4:00 AM UTC
push:
branches:
- main
tags:
- v*
pull_request:
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
docker_pkg:
- debian
- alpine
optional_deps:
- true
- false
include:
- 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@v6
- name: Gather image information
id: info
run: |
VERSION=noop
DOCKERFILE=Dockerfile
MINOR=""
MAJOR=""
FEATURES="nopgxregisterdefaulttypes,disable_grpc_modules"
if [ "${{ github.event_name }}" = "schedule" ]; then
VERSION=nightly
elif [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
elif [[ $GITHUB_REF == refs/heads/* ]]; then
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then
VERSION=edge
fi
elif [[ $GITHUB_REF == refs/pull/* ]]; then
VERSION=pr-${{ github.event.number }}
fi
if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
MINOR=${VERSION%.*}
MAJOR=${MINOR%.*}
fi
VERSION_SLIM="${VERSION}-slim"
if [[ $DOCKER_PKG == alpine ]]; then
VERSION="${VERSION}-alpine"
VERSION_SLIM="${VERSION}-slim"
DOCKERFILE=Dockerfile.alpine
elif [[ $DOCKER_PKG == distroless ]]; then
VERSION="${VERSION}-distroless"
VERSION_SLIM="${VERSION}-slim"
DOCKERFILE=Dockerfile.distroless
FEATURES="${FEATURES},nosqlite"
elif [[ $DOCKER_PKG == debian-plugins ]]; then
VERSION="${VERSION}-plugins"
VERSION_SLIM="${VERSION}-slim"
FEATURES="${FEATURES},unixcrypt"
elif [[ $DOCKER_PKG == debian ]]; then
FEATURES="${FEATURES},unixcrypt"
fi
DOCKER_IMAGES=("drakkan/sftpgo" "ghcr.io/drakkan/sftpgo")
TAGS="${DOCKER_IMAGES[0]}:${VERSION}"
TAGS_SLIM="${DOCKER_IMAGES[0]}:${VERSION_SLIM}"
for DOCKER_IMAGE in ${DOCKER_IMAGES[@]}; do
if [[ ${DOCKER_IMAGE} != ${DOCKER_IMAGES[0]} ]]; then
TAGS="${TAGS},${DOCKER_IMAGE}:${VERSION}"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${VERSION_SLIM}"
fi
if [[ $GITHUB_REF == refs/tags/* ]]; then
if [[ $DOCKER_PKG == debian ]]; then
if [[ -n $MAJOR && -n $MINOR ]]; then
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR},${DOCKER_IMAGE}:${MAJOR}"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${MINOR}-slim,${DOCKER_IMAGE}:${MAJOR}-slim"
fi
TAGS="${TAGS},${DOCKER_IMAGE}:latest"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:slim"
elif [[ $DOCKER_PKG == distroless ]]; then
if [[ -n $MAJOR && -n $MINOR ]]; then
TAGS="${TAGS},${DOCKER_IMAGE}:${MINOR}-distroless,${DOCKER_IMAGE}:${MAJOR}-distroless"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${MINOR}-distroless-slim,${DOCKER_IMAGE}:${MAJOR}-distroless-slim"
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"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:${MINOR}-alpine-slim,${DOCKER_IMAGE}:${MAJOR}-alpine-slim"
fi
TAGS="${TAGS},${DOCKER_IMAGE}:alpine"
TAGS_SLIM="${TAGS_SLIM},${DOCKER_IMAGE}:alpine-slim"
fi
fi
done
if [[ $OPTIONAL_DEPS == true ]]; then
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
echo "full=true" >> $GITHUB_OUTPUT
else
echo "version=${VERSION_SLIM}" >> $GITHUB_OUTPUT
echo "tags=${TAGS_SLIM}" >> $GITHUB_OUTPUT
echo "full=false" >> $GITHUB_OUTPUT
fi
if [[ $DOCKER_PKG == debian-plugins ]]; then
echo "plugins=true" >> $GITHUB_OUTPUT
else
echo "plugins=false" >> $GITHUB_OUTPUT
fi
echo "dockerfile=${DOCKERFILE}" >> $GITHUB_OUTPUT
echo "features=${FEATURES}" >> $GITHUB_OUTPUT
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
echo "sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
env:
DOCKER_PKG: ${{ matrix.docker_pkg }}
OPTIONAL_DEPS: ${{ matrix.optional_deps }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up builder
uses: docker/setup-buildx-action@v4
id: builder
- name: Login to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
if: ${{ github.event_name != 'pull_request' }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
if: ${{ github.event_name != 'pull_request' }}
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
builder: ${{ steps.builder.outputs.name }}
file: ./${{ steps.info.outputs.dockerfile }}
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v7
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.info.outputs.tags }}
build-args: |
COMMIT_SHA=${{ steps.info.outputs.sha }}
INSTALL_OPTIONAL_PACKAGES=${{ steps.info.outputs.full }}
DOWNLOAD_PLUGINS=${{ steps.info.outputs.plugins }}
FEATURES=${{ steps.info.outputs.features }}
labels: |
org.opencontainers.image.title=SFTPGo
org.opencontainers.image.description=Full-featured and highly configurable file transfer server: SFTP, HTTP/S,FTP/S, WebDAV
org.opencontainers.image.url=https://github.com/drakkan/sftpgo
org.opencontainers.image.documentation=https://github.com/drakkan/sftpgo/blob/${{ github.sha }}/docker/README.md
org.opencontainers.image.source=https://github.com/drakkan/sftpgo
org.opencontainers.image.version=${{ steps.info.outputs.version }}
org.opencontainers.image.created=${{ steps.info.outputs.created }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.licenses=AGPL-3.0-only
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags: 'v*'
permissions:
id-token: write
contents: write
env:
GO_VERSION: 1.25.8
jobs:
prepare-sources-with-deps:
name: Prepare sources with deps
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Get SFTPGo version
id: get_version
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Prepare release
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 }}
- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_src_with_deps.tar.xz
path: ./sftpgo_${{ steps.get_version.outputs.VERSION }}_src_with_deps.tar.xz
retention-days: 1
prepare-windows:
name: Prepare Windows binaries
runs-on: windows-2022
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Get SFTPGo version
id: get_version
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
shell: bash
- name: Build
run: |
$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
go-winres simply --arch amd64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o sftpgo.exe
mkdir arm64
$Env:CGO_ENABLED='0'
$Env:GOOS='windows'
$Env:GOARCH='arm64'
go-winres simply --arch arm64 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\arm64\sftpgo.exe
mkdir x86
$Env:GOARCH='386'
go-winres simply --arch 386 --product-version $Env:SFTPGO_VERSION-$GIT_COMMIT --file-version "$FILE_VERSION" --file-description "SFTPGo server" --product-name SFTPGo --copyright "2019-2025 Nicola Murino" --original-filename sftpgo.exe --icon .\windows-installer\icon.ico
go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules,nosqlite -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=$GIT_COMMIT -X github.com/drakkan/sftpgo/v2/internal/version.date=$DATE_TIME" -o .\x86\sftpgo.exe
Remove-Item Env:\CGO_ENABLED
Remove-Item Env:\GOOS
Remove-Item Env:\GOARCH
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.VERSION }}
- name: Initialize data provider
run: ./sftpgo initprovider
shell: bash
- name: Prepare Release
run: |
mkdir output
copy .\sftpgo.exe .\output
copy .\sftpgo.json .\output
copy .\sftpgo.db .\output
copy .\LICENSE .\output\LICENSE.txt
copy .\NOTICE .\output\NOTICE.txt
mkdir output\templates
xcopy .\templates .\output\templates\ /E
mkdir output\static
xcopy .\static .\output\static\ /E
mkdir output\openapi
xcopy .\openapi .\output\openapi\ /E
iscc .\windows-installer\sftpgo.iss
rm .\output\sftpgo.exe
rm .\output\sftpgo.db
copy .\arm64\sftpgo.exe .\output
(Get-Content .\output\sftpgo.json).replace('"sqlite"', '"bolt"') | Set-Content .\output\sftpgo.json
$Env:SFTPGO_DATA_PROVIDER__DRIVER='bolt'
$Env:SFTPGO_DATA_PROVIDER__NAME='.\output\sftpgo.db'
.\sftpgo.exe initprovider
Remove-Item Env:\SFTPGO_DATA_PROVIDER__DRIVER
Remove-Item Env:\SFTPGO_DATA_PROVIDER__NAME
$Env:SFTPGO_ISS_ARCH='arm64'
iscc .\windows-installer\sftpgo.iss
rm .\output\sftpgo.exe
copy .\x86\sftpgo.exe .\output
$Env:SFTPGO_ISS_ARCH='x86'
iscc .\windows-installer\sftpgo.iss
env:
SFTPGO_ISS_VERSION: ${{ steps.get_version.outputs.VERSION }}
- name: Prepare Portable Release
run: |
mkdir win-portable
copy .\sftpgo.exe .\win-portable
mkdir win-portable\arm64
copy .\arm64\sftpgo.exe .\win-portable\arm64
mkdir win-portable\x86
copy .\x86\sftpgo.exe .\win-portable\x86
copy .\sftpgo.json .\win-portable
(Get-Content .\win-portable\sftpgo.json).replace('"sqlite"', '"bolt"') | Set-Content .\win-portable\sftpgo.json
copy .\output\sftpgo.db .\win-portable
copy .\LICENSE .\win-portable\LICENSE.txt
copy .\NOTICE .\win-portable\NOTICE.txt
mkdir win-portable\templates
xcopy .\templates .\win-portable\templates\ /E
mkdir win-portable\static
xcopy .\static .\win-portable\static\ /E
mkdir win-portable\openapi
xcopy .\openapi .\win-portable\openapi\ /E
Compress-Archive .\win-portable\* sftpgo_portable.zip
- name: Upload Windows installer x86_64 artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_windows_x86_64.exe
path: ./sftpgo_windows_x86_64.exe
retention-days: 1
- name: Upload Windows installer arm64 artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_windows_arm64.exe
path: ./sftpgo_windows_arm64.exe
retention-days: 1
- name: Upload Windows installer x86 artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_windows_x86.exe
path: ./sftpgo_windows_x86.exe
retention-days: 1
- name: Upload Windows portable artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_windows_portable.zip
path: ./sftpgo_portable.zip
retention-days: 1
prepare-mac:
name: Prepare macOS binaries
runs-on: macos-14
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: Get SFTPGo version
id: get_version
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
shell: bash
- name: Build for macOS x86_64
run: go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo
- name: Build for macOS arm64
run: CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 SDKROOT=$(xcrun --sdk macosx --show-sdk-path) go build -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=`git describe --always --abbrev=8 --dirty` -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -o sftpgo_arm64
- name: Initialize data provider
run: ./sftpgo initprovider
shell: bash
- name: Prepare Release
run: |
mkdir -p output/{init,sqlite,bash_completion,zsh_completion}
echo "For documentation please take a look here:" > output/README.txt
echo "" >> output/README.txt
echo "https://docs.sftpgo.com" >> output/README.txt
cp LICENSE output/
cp NOTICE output/
cp sftpgo output/
cp sftpgo.json output/
cp sftpgo.db output/sqlite/
cp -r static output/
cp -r openapi output/
cp -r templates output/
cp init/com.github.drakkan.sftpgo.plist output/init/
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
cd output
tar cJvf ../sftpgo_${SFTPGO_VERSION}_macOS_x86_64.tar.xz *
cd ..
cp sftpgo_arm64 output/sftpgo
cd output
tar cJvf ../sftpgo_${SFTPGO_VERSION}_macOS_arm64.tar.xz *
cd ..
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.VERSION }}
- name: Upload macOS x86_64 artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_macOS_x86_64.tar.xz
path: ./sftpgo_${{ steps.get_version.outputs.VERSION }}_macOS_x86_64.tar.xz
retention-days: 1
- name: Upload macOS arm64 artifact
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.VERSION }}_macOS_arm64.tar.xz
path: ./sftpgo_${{ steps.get_version.outputs.VERSION }}_macOS_arm64.tar.xz
retention-days: 1
prepare-linux:
name: Prepare Linux binaries
runs-on: ubuntu-latest
strategy:
matrix:
include:
- arch: amd64
distro: ubuntu:18.04
go-arch: amd64
deb-arch: amd64
rpm-arch: x86_64
tar-arch: x86_64
- arch: aarch64
distro: ubuntu18.04
go-arch: arm64
deb-arch: arm64
rpm-arch: aarch64
tar-arch: arm64
- arch: ppc64le
distro: ubuntu18.04
go-arch: ppc64le
deb-arch: ppc64el
rpm-arch: ppc64le
tar-arch: ppc64le
- arch: armv7
distro: ubuntu18.04
go-arch: arm7
deb-arch: armhf
rpm-arch: armv7hl
tar-arch: armv7
steps:
- uses: actions/checkout@v6
- name: Get versions
id: get_version
run: |
echo "SFTPGO_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
echo "GO_VERSION=${GO_VERSION}" >> $GITHUB_OUTPUT
echo "COMMIT=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
shell: bash
env:
GO_VERSION: ${{ env.GO_VERSION }}
- name: Build on amd64
if: ${{ matrix.arch == 'amd64' }}
run: |
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 -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=${{ steps.get_version.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/internal/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
echo "https://github.com/drakkan/sftpgo/blob/${SFTPGO_VERSION}/README.md" >> output/README.txt
cp LICENSE output/
cp NOTICE output/
cp sftpgo.json output/
cp -r templates output/
cp -r static output/
cp -r openapi output/
cp init/sftpgo.service output/init/
./sftpgo initprovider
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
cp sftpgo output/
cp sftpgo.db output/sqlite/
cd output
tar cJvf sftpgo_${SFTPGO_VERSION}_linux_${{ matrix.tar-arch }}.tar.xz *
cd ..
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
- uses: uraimo/run-on-arch-action@v3
if: ${{ matrix.arch != 'amd64' }}
name: Build for ${{ matrix.arch }}
id: build
with:
arch: ${{ matrix.arch }}
distro: ${{ matrix.distro }}
setup: |
mkdir -p "${PWD}/output"
dockerRunArgs: |
--volume "${PWD}/output:/output"
shell: /bin/bash
install: |
apt-get update -q -y
apt-get install -q -y curl gcc xz-utils
GO_DOWNLOAD_ARCH=${{ matrix.go-arch }}
if [ ${{ matrix.arch}} == 'armv7' ]
then
GO_DOWNLOAD_ARCH=armv6l
fi
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-${GO_DOWNLOAD_ARCH}.tar.gz
tar -C /usr/local -xzf go.tar.gz
run: |
export PATH=$PATH:/usr/local/go/bin
go version
go build -buildvcs=false -trimpath -tags nopgxregisterdefaulttypes,disable_grpc_modules -ldflags "-s -w -X github.com/drakkan/sftpgo/v2/internal/version.commit=${{ steps.get_version.outputs.COMMIT }} -X github.com/drakkan/sftpgo/v2/internal/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
echo "https://github.com/drakkan/sftpgo/blob/${{ steps.get_version.outputs.SFTPGO_VERSION }}/README.md" >> output/README.txt
cp LICENSE output/
cp NOTICE output/
cp sftpgo.json output/
cp -r templates output/
cp -r static output/
cp -r openapi output/
cp init/sftpgo.service output/init/
./sftpgo initprovider
./sftpgo gen completion bash > output/bash_completion/sftpgo
./sftpgo gen completion zsh > output/zsh_completion/_sftpgo
./sftpgo gen man -d output/man/man1
gzip output/man/man1/*
cp sftpgo output/
cp sftpgo.db output/sqlite/
cd output
tar cJvf sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_${{ matrix.tar-arch }}.tar.xz *
cd ..
- name: Upload build artifact for ${{ matrix.arch }}
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_${{ matrix.tar-arch }}.tar.xz
path: ./output/sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_${{ matrix.tar-arch }}.tar.xz
retention-days: 1
- name: Build Packages
id: build_linux_pkgs
run: |
export NFPM_ARCH=${{ matrix.go-arch }}
cd pkgs
./build.sh
PKG_VERSION=${SFTPGO_VERSION:1}
echo "pkg-version=${PKG_VERSION}" >> $GITHUB_OUTPUT
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
- name: Upload Deb Package
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.build_linux_pkgs.outputs.pkg-version }}-1_${{ matrix.deb-arch}}.deb
path: ./pkgs/dist/deb/sftpgo_${{ steps.build_linux_pkgs.outputs.pkg-version }}-1_${{ matrix.deb-arch}}.deb
retention-days: 1
- name: Upload RPM Package
uses: actions/upload-artifact@v7
with:
name: sftpgo-${{ steps.build_linux_pkgs.outputs.pkg-version }}-1.${{ matrix.rpm-arch}}.rpm
path: ./pkgs/dist/rpm/sftpgo-${{ steps.build_linux_pkgs.outputs.pkg-version }}-1.${{ matrix.rpm-arch}}.rpm
retention-days: 1
prepare-linux-bundle:
name: Prepare Linux bundle
needs: prepare-linux
runs-on: ubuntu-latest
steps:
- name: Get versions
id: get_version
run: |
echo "SFTPGO_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
shell: bash
- name: Download amd64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_x86_64.tar.xz
- name: Download arm64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_arm64.tar.xz
- name: Download ppc64le artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_ppc64le.tar.xz
- name: Download armv7 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_armv7.tar.xz
- name: Build bundle
shell: bash
run: |
mkdir -p bundle/{arm64,ppc64le,armv7}
cd bundle
tar xvf ../sftpgo_${SFTPGO_VERSION}_linux_x86_64.tar.xz
cd arm64
tar xvf ../../sftpgo_${SFTPGO_VERSION}_linux_arm64.tar.xz sftpgo
cd ../ppc64le
tar xvf ../../sftpgo_${SFTPGO_VERSION}_linux_ppc64le.tar.xz sftpgo
cd ../armv7
tar xvf ../../sftpgo_${SFTPGO_VERSION}_linux_armv7.tar.xz sftpgo
cd ..
tar cJvf sftpgo_${SFTPGO_VERSION}_linux_bundle.tar.xz *
cd ..
env:
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
- name: Upload Linux bundle
uses: actions/upload-artifact@v7
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_bundle.tar.xz
path: ./bundle/sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_bundle.tar.xz
retention-days: 1
create-release:
name: Release
needs: [prepare-linux-bundle, prepare-sources-with-deps, prepare-mac, prepare-windows]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Get versions
id: get_version
run: |
SFTPGO_VERSION=${GITHUB_REF/refs\/tags\//}
PKG_VERSION=${SFTPGO_VERSION:1}
echo "SFTPGO_VERSION=${SFTPGO_VERSION}" >> $GITHUB_OUTPUT
echo "PKG_VERSION=${PKG_VERSION}" >> $GITHUB_OUTPUT
shell: bash
- name: Download amd64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_x86_64.tar.xz
- name: Download arm64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_arm64.tar.xz
- name: Download ppc64le artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_ppc64le.tar.xz
- name: Download armv7 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_armv7.tar.xz
- name: Download Linux bundle artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_linux_bundle.tar.xz
- name: Download Deb amd64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.PKG_VERSION }}-1_amd64.deb
- name: Download Deb arm64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.PKG_VERSION }}-1_arm64.deb
- name: Download Deb ppc64le artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.PKG_VERSION }}-1_ppc64el.deb
- name: Download Deb armv7 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.PKG_VERSION }}-1_armhf.deb
- name: Download RPM x86_64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo-${{ steps.get_version.outputs.PKG_VERSION }}-1.x86_64.rpm
- name: Download RPM aarch64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo-${{ steps.get_version.outputs.PKG_VERSION }}-1.aarch64.rpm
- name: Download RPM ppc64le artifact
uses: actions/download-artifact@v8
with:
name: sftpgo-${{ steps.get_version.outputs.PKG_VERSION }}-1.ppc64le.rpm
- name: Download RPM armv7 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo-${{ steps.get_version.outputs.PKG_VERSION }}-1.armv7hl.rpm
- name: Download macOS x86_64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_macOS_x86_64.tar.xz
- name: Download macOS arm64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_macOS_arm64.tar.xz
- name: Download Windows installer x86_64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_windows_x86_64.exe
- name: Download Windows installer arm64 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_windows_arm64.exe
- name: Download Windows installer x86 artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_windows_x86.exe
- name: Download Windows portable artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_windows_portable.zip
- name: Download source with deps artifact
uses: actions/download-artifact@v8
with:
name: sftpgo_${{ steps.get_version.outputs.SFTPGO_VERSION }}_src_with_deps.tar.xz
- name: Create release
run: |
mv sftpgo_windows_x86_64.exe sftpgo_${SFTPGO_VERSION}_windows_x86_64.exe
mv sftpgo_windows_arm64.exe sftpgo_${SFTPGO_VERSION}_windows_arm64.exe
mv sftpgo_windows_x86.exe sftpgo_${SFTPGO_VERSION}_windows_x86.exe
mv sftpgo_portable.zip sftpgo_${SFTPGO_VERSION}_windows_portable.zip
gh release create "${SFTPGO_VERSION}" -t "${SFTPGO_VERSION}"
gh release upload "${SFTPGO_VERSION}" sftpgo_*.xz --clobber
gh release upload "${SFTPGO_VERSION}" sftpgo-*.rpm --clobber
gh release upload "${SFTPGO_VERSION}" sftpgo_*.deb --clobber
gh release upload "${SFTPGO_VERSION}" sftpgo_*.exe --clobber
gh release upload "${SFTPGO_VERSION}" sftpgo_*.zip --clobber
gh release view "${SFTPGO_VERSION}"
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
SFTPGO_VERSION: ${{ steps.get_version.outputs.SFTPGO_VERSION }}
================================================
FILE: .gitignore
================================================
# compilation output
sftpgo
sftpgo.exe
================================================
FILE: .golangci.yml
================================================
version: "2"
run:
issues-exit-code: 1
tests: true
linters:
enable:
- bodyclose
- dogsled
- dupl
- goconst
- gocyclo
- misspell
- revive
- rowserrcheck
- unconvert
- unparam
- whitespace
settings:
dupl:
threshold: 150
errcheck:
check-type-assertions: false
check-blank: false
goconst:
min-len: 3
min-occurrences: 3
gocyclo:
min-complexity: 15
# https://golangci-lint.run/usage/linters/#revive
revive:
rules:
- name: var-naming
severity: warning
disabled: true
exclude: [""]
arguments:
- ["ID"] # AllowList
- ["VM"] # DenyList
- - upper-case-const: true
- - skip-package-name-checks: true
exclusions:
generated: lax
presets:
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
simplify: true
goimports:
local-prefixes:
- github.com/drakkan/sftpgo
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
================================================
FILE: CODEOWNERS
================================================
* @drakkan
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
support@sftpgo.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: Dockerfile
================================================
FROM golang:1.26-trixie AS builder
ENV GOFLAGS="-mod=readonly"
RUN apt-get update && apt-get -y upgrade && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /workspace
WORKDIR /workspace
ARG GOPROXY
COPY go.mod go.sum ./
RUN go mod download && go mod verify
ARG COMMIT_SHA
# This ARG allows to disable some optional features and it might be useful if you build the image yourself.
# For example you can disable S3 and GCS support like this:
# --build-arg FEATURES=nos3,nogcs
ARG FEATURES
COPY . .
RUN set -xe && \
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/internal/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/internal/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:trixie-slim
# Set to "true" to install jq
ARG INSTALL_OPTIONAL_PACKAGES=false
RUN apt-get update && apt-get -y upgrade && apt-get install --no-install-recommends -y ca-certificates media-types && rm -rf /var/lib/apt/lists/*
RUN if [ "${INSTALL_OPTIONAL_PACKAGES}" = "true" ]; then apt-get update && apt-get install --no-install-recommends -y jq && rm -rf /var/lib/apt/lists/*; fi
RUN mkdir -p /etc/sftpgo /var/lib/sftpgo /usr/share/sftpgo /srv/sftpgo/data /srv/sftpgo/backups
RUN groupadd --system -g 1000 sftpgo && \
useradd --system --gid sftpgo --no-create-home \
--home-dir /var/lib/sftpgo --shell /usr/sbin/nologin \
--comment "SFTPGo user" --uid 1000 sftpgo
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/sftpgo-plugin-* /usr/local/bin/
# Log to the stdout so the logs will be available using docker logs
ENV SFTPGO_LOG_FILE_PATH=""
# Modify the default configuration file
RUN sed -i 's|"users_base_dir": "",|"users_base_dir": "/srv/sftpgo/data",|' /etc/sftpgo/sftpgo.json && \
sed -i 's|"backups"|"/srv/sftpgo/backups"|' /etc/sftpgo/sftpgo.json
RUN chown -R sftpgo:sftpgo /etc/sftpgo /srv/sftpgo && chown sftpgo:sftpgo /var/lib/sftpgo && chmod 700 /srv/sftpgo/backups
WORKDIR /var/lib/sftpgo
USER 1000:1000
CMD ["sftpgo", "serve"]
================================================
FILE: Dockerfile.alpine
================================================
FROM golang:1.26-alpine3.23 AS builder
ENV GOFLAGS="-mod=readonly"
RUN apk -U upgrade --no-cache && apk add --update --no-cache bash ca-certificates curl git gcc g++
RUN mkdir -p /workspace
WORKDIR /workspace
ARG GOPROXY
COPY go.mod go.sum ./
RUN go mod download && go mod verify
ARG COMMIT_SHA
# This ARG allows to disable some optional features and it might be useful if you build the image yourself.
# For example you can disable S3 and GCS support like this:
# --build-arg FEATURES=nos3,nogcs
ARG FEATURES
COPY . .
RUN set -xe && \
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/internal/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -v -o sftpgo
FROM alpine:3.23
# Set to "true" to install jq
ARG INSTALL_OPTIONAL_PACKAGES=false
RUN apk -U upgrade --no-cache && apk add --update --no-cache ca-certificates tzdata mailcap
RUN if [ "${INSTALL_OPTIONAL_PACKAGES}" = "true" ]; then apk add --update --no-cache jq; fi
RUN mkdir -p /etc/sftpgo /var/lib/sftpgo /usr/share/sftpgo /srv/sftpgo/data /srv/sftpgo/backups
RUN addgroup -g 1000 -S sftpgo && \
adduser -u 1000 -h /var/lib/sftpgo -s /sbin/nologin -G sftpgo -S -D -H -g "SFTPGo user" sftpgo
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/
# Log to the stdout so the logs will be available using docker logs
ENV SFTPGO_LOG_FILE_PATH=""
# Modify the default configuration file
RUN sed -i 's|"users_base_dir": "",|"users_base_dir": "/srv/sftpgo/data",|' /etc/sftpgo/sftpgo.json && \
sed -i 's|"backups"|"/srv/sftpgo/backups"|' /etc/sftpgo/sftpgo.json
RUN chown -R sftpgo:sftpgo /etc/sftpgo /srv/sftpgo && chown sftpgo:sftpgo /var/lib/sftpgo && chmod 700 /srv/sftpgo/backups
WORKDIR /var/lib/sftpgo
USER 1000:1000
CMD ["sftpgo", "serve"]
================================================
FILE: Dockerfile.distroless
================================================
FROM golang:1.26-trixie AS builder
ENV CGO_ENABLED=0 GOFLAGS="-mod=readonly"
RUN apt-get update && apt-get -y upgrade && apt-get install --no-install-recommends -y media-types && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /workspace
WORKDIR /workspace
ARG GOPROXY
COPY go.mod go.sum ./
RUN go mod download && go mod verify
ARG COMMIT_SHA
# This ARG allows to disable some optional features and it might be useful if you build the image yourself.
# For this variant we disable SQLite support since it requires CGO and so a C runtime which is not installed
# in distroless/static-* images
ARG FEATURES
COPY . .
RUN set -xe && \
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/internal/version.commit=${COMMIT_SHA} -X github.com/drakkan/sftpgo/v2/internal/version.date=`date -u +%FT%TZ`" -v -o sftpgo
# Modify the default configuration file
RUN sed -i 's|"users_base_dir": "",|"users_base_dir": "/srv/sftpgo/data",|' sftpgo.json && \
sed -i 's|"backups"|"/srv/sftpgo/backups"|' sftpgo.json && \
sed -i 's|"sqlite"|"bolt"|' sftpgo.json
RUN mkdir /etc/sftpgo /var/lib/sftpgo /srv/sftpgo
FROM gcr.io/distroless/static-debian13
COPY --from=builder --chown=1000:1000 /etc/sftpgo /etc/sftpgo
COPY --from=builder --chown=1000:1000 /srv/sftpgo /srv/sftpgo
COPY --from=builder --chown=1000:1000 /var/lib/sftpgo /var/lib/sftpgo
COPY --from=builder --chown=1000:1000 /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 /etc/mime.types /etc/mime.types
# Log to the stdout so the logs will be available using docker logs
ENV SFTPGO_LOG_FILE_PATH=""
# These env vars are required to avoid the following error when calling user.Current():
# unable to get the current user: user: Current requires cgo or $USER set in environment
ENV USER=sftpgo
ENV HOME=/var/lib/sftpgo
WORKDIR /var/lib/sftpgo
USER 1000:1000
CMD ["sftpgo", "serve"]
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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, either version 3 of the License, or
(at your option) any later version.
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/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: NOTICE
================================================
Additional terms under GNU AGPL version 3 section 7.3(b) and 13.1:
If you have included SFTPGo so that it is offered through any network
interactions, including by means of an external user interface, or
any other integration, even without modifying its source code and then
SFTPGo is partially, fully or optionally configured via your frontend,
you must provide reasonable but clear attribution to the SFTPGo project
and its author(s), not imply any endorsement by or affiliation with the
SFTPGo project, and you must prominently offer all users interacting
with it remotely through a computer network an opportunity to receive
the Corresponding Source of the SFTPGo version you include by providing
a link to the Corresponding Source in the SFTPGo source code repository.
================================================
FILE: README.md
================================================
# SFTPGo
[](https://github.com/drakkan/sftpgo/workflows/CI/badge.svg)
[](https://www.gnu.org/licenses/agpl-3.0)
[](https://github.com/avelino/awesome-go)
Full-featured and highly configurable event-driven file transfer solution. Server protocols: SFTP, HTTP/S, FTP/S, WebDAV. Storage backends: local filesystem, encrypted local filesystem, S3 (compatible) Object Storage, Google Cloud Storage, Azure Blob Storage, other SFTP servers.
With SFTPGo you can leverage local and cloud storage backends for exchanging and storing files internally or with business partners using the same tools and processes you are already familiar with.
## Project Status & Editions
SFTPGo is an open-source project with a sustainable business model. We offer two editions to suit different requirements, ensuring the project remains healthy and maintained for everyone.
### Open Source (Community)
Free, Copyleft (AGPLv3), Community Supported. The Community edition is a fully functional, production-ready solution widely adopted worldwide. It includes all the core protocols, storage backends, and the WebAdmin/WebClient UIs. It is ideal for:
- Standard file transfer needs.
- Integrating storage backends (S3, GCS, Azure Blob) with legacy protocols.
- Projects that are comfortable with AGPLv3 licensing.
### SFTPGo Enterprise
Commercial License, Professional Support, ISO 27001 Vendor. The Enterprise edition is built on the same core but extends it for mission-critical environments, compliance-heavy industries, and advanced workflows. It is a drop-in replacement (seamless upgrade).
| Feature | Open Source (Community) | Enterprise Edition |
| :--- | :--- | :--- |
| **License Type** | AGPLv3 (Copyleft) | **Commercial License**<br/>Proprietary/No Copyleft |
| **Vendor Compliance** | Not Applicable<br/>Community Project | **Certified Vendor**<br/>ISO 27001 & Supply Chain Validation |
| **Support** | Community (GitHub) | **Direct from Authors** |
| **Cloud Storage Engine** | Standard | **High Performance & Scalable**<br/>In-memory streaming (no local temp files) and up to 70% faster |
| **High Availability (HA)** | Standard<br/>Shared DB & Storage | **Advanced**<br/>Enhanced event handling and optimized instance coordination |
| **Automation Logic** | Simple Placeholders | **Dynamic Logic & Virtual Folders**<br/>Conditions, loops, route data across storage backends |
| **Data Lifecycle** | Delete / Retain | **Smart Archiving**<br/>Move data to external Cloud/SFTP storage via Virtual Folders |
| **Email Data Ingestion** | - | **Native IMAP Integration**<br/>Auto-extract attachments from email to storage |
| **Public Sharing** | Standard Links | **Advanced & Collaborative**<br/>Email Authentication & Group Delegation |
| **Data Protection** | - | **Encryption & Scanning**<br/>Automated PGP, Antivirus & DLP via ICAP |
| **Advanced Identity (SSO)** | Standard | **Extended Controls**<br/>Advanced Single Sign-On parameters |
| **Document Editing** | - | **Included**<br/>View, edit, and co-author in browser |
**Note**: We are committed to keeping the Open Source edition powerful and maintained. The Enterprise edition helps fund the development of the entire SFTPGo ecosystem.
## Sponsors
If you rely on SFTPGo in your projects, consider becoming a [sponsor](https://github.com/sponsors/drakkan).
Your sponsorship helps cover maintenance, security updates and ongoing development of the open-source edition.
### Thank you to our sponsors
#### Platinum sponsors
[<img src="./img/Aledade_logo.png" alt="Aledade logo" width="202" height="70">](https://www.aledade.com/)
</br></br>
[<img src="./img/jumptrading.png" alt="Jump Trading logo" width="362" height="63">](https://www.jumptrading.com/)
</br></br>
[<img src="./img/wpengine.png" alt="WP Engine logo" width="331" height="63">](https://wpengine.com/)
#### Silver sponsors
[<img src="./img/IDCS.png" alt="IDCS logo" width="212" height="51">](https://idcs.ip-paris.fr/)
#### Bronze sponsors
[<img src="./img/7digital.png" alt="7digital logo" width="178" height="56">](https://www.7digital.com/)
</br></br>
[<img src="./img/servinga.png" alt="servinga logo" width="258" height="56">](https://servinga.com/)
</br></br>
[<img src="./img/reui.png" alt="ReUI logo" width="151" height="56">](https://www.reui.io/)
## Documentation
You can explore all supported features and configuration options at [docs.sftpgo.com](https://docs.sftpgo.com/latest/).
**Note:** The link above refers to the **Community Edition**.
For details on **Enterprise Edition**, please refer to the [Enterprise Documentation](https://docs.sftpgo.com/enterprise/).
## Support
- **Community Support**: use [GitHub Discussions](https://github.com/drakkan/sftpgo/discussions) to ask questions, share feedback, and engage with other users.
- **Commercial Support**: If you require guaranteed SLAs, expert guidance, or the advanced features listed above, check out [SFTPGo Enterprise](https://sftpgo.com).
SFTPGo Enterprise is available as:
- On-premises: Full control on your infrastructure. More details: [sftpgo.com/on-premises](https://sftpgo.com/on-premises)
- Fully managed SaaS: We handle the infrastructure. More details: [sftpgo.com/saas](https://sftpgo.com/saas)
## Internationalization
The translations are available via [Crowdin](https://crowdin.com/project/sftpgo), who have granted us an open source license.
Before translating please take a look at our contribution [guidelines](https://docs.sftpgo.com/latest/web-interfaces/#internationalization).
## Release Cadence
SFTPGo follows a feature-driven release cycle.
- Enterprise Edition: Receives major new features first and follows a faster [release cadence](https://docs.sftpgo.com/enterprise/changelog/).
- Community Edition: Remains maintained, receiving bug fixes, security updates, and updates to core features.
## Acknowledgements
SFTPGo makes use of the third party libraries listed inside [go.mod](./go.mod).
We are very grateful to all the people who contributed with ideas and/or pull requests.
Thank you to [ysura](https://www.ysura.com/) for granting us stable access to a test AWS S3 account.
Thank you to [KeenThemes](https://keenthemes.com/) for granting us a custom license to use their amazing [themes](https://keenthemes.com/bootstrap-templates) for the SFTPGo WebAdmin and WebClient user interfaces, across both the Open Source and Open Core versions.
Thank you to [Crowdin](https://crowdin.com/) for granting us an Open Source License.
Thank you to [Incode](https://www.incode.it/) for helping us to improve the UI/UX.
## License
SFTPGo source code is licensed under the GNU AGPL-3.0-only with [additional terms](./NOTICE).
The [theme](https://keenthemes.com/bootstrap-templates) used in WebAdmin and WebClient user interfaces is proprietary, this means:
- KeenThemes HTML/CSS/JS components are allowed for use only within the SFTPGo product and restricted to be used in a resealable HTML template that can compete with KeenThemes products anyhow.
- The SFTPGo WebAdmin and WebClient user interfaces (HTML, CSS and JS components) based on this theme are allowed for use only within the SFTPGo product and therefore cannot be used in derivative works/products without an explicit grant from the [SFTPGo Team](mailto:support@sftpgo.com).
More information about [compliance](https://sftpgo.com/compliance.html).
**Note:** We do not provide legal advice. If you have questions about license compliance or whether your use case is permitted under the license terms, please consult your legal team.
## Copyright
Copyright (C) 2019 - 2026 Nicola Murino
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
We actively maintain the latest stable release of SFTPGo. While we strive to keep the Open Source version secure and up-to-date, maintenance is performed on a best-effort basis by the community and contributors.
## Scope and Dependency Policy
Our security advisories focus on vulnerabilities found within the **SFTPGo codebase itself**.
To ensure the long-term sustainability of the project, we handle upstream dependencies (like the Go standard library, external packages, or Docker base images) as follows:
- Community Updates: For the Open Source version, vulnerabilities in upstream components (such as the Go standard library or third-party packages) are addressed during our **regular release cycles**. We generally do not provide immediate, out-of-band or ad-hoc releases to address dependency-only CVEs.
- Empowering Users: One of the strengths of SFTPGo being open-source is that you have full control. If your security scanners require an immediate fix, you can always rebuild the project using the latest patched Go toolchain or updated dependencies.
- Compatibility: We are committed to keeping SFTPGo compatible with the latest stable Go compiler. If an upstream fix breaks SFTPGo, fixing that becomes a priority for us.
- Professional Needs: We understand that some organizations have strict compliance requirements or internal SLAs that require guaranteed, immediate response times and out-of-band patches. For these cases, we offer [SFTPGo Enterprise](https://sftpgo.com/on-premises) to cover the additional maintenance and support overhead.
## Reporting a Vulnerability
To report (possible) security issues in SFTPGo, please either send a mail to the [SFTPGo Team](mailto:support@sftpgo.com) or use Github's [private reporting feature](https://github.com/drakkan/sftpgo/security/advisories/new).
================================================
FILE: crowdin.yml
================================================
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
files:
- source: /static/locales/en/translation.json
translation: /static/locales/%two_letters_code%/%original_file_name%
type: i18next_json
================================================
FILE: docker/scripts/download-plugins.sh
================================================
#!/usr/bin/env bash
set -euo pipefail
ARCH=$(uname -m)
case ${ARCH} in
x86_64)
SUFFIX=amd64
;;
aarch64)
SUFFIX=arm64
;;
*)
SUFFIX=ppc64le
;;
esac
echo "Downloading plugins for arch ${SUFFIX}"
PLUGINS=(geoipfilter kms pubsub eventstore eventsearch auth)
for PLUGIN in "${PLUGINS[@]}"; do
URL="https://github.com/sftpgo/sftpgo-plugin-${PLUGIN}/releases/latest/download/sftpgo-plugin-${PLUGIN}-linux-${SUFFIX}"
DEST="/usr/local/bin/sftpgo-plugin-${PLUGIN}"
echo "Downloading ${PLUGIN}..."
if curl --fail --silent --show-error -L "${URL}" --output "${DEST}"; then
chmod 755 "${DEST}"
else
echo "Error: Failed to download ${PLUGIN}" >&2
exit 1
fi
done
echo "All plugins downloaded successfully"
================================================
FILE: examples/OTP/authy/README.md
================================================
# Authy
These example show how-to integrate [Twilio Authy API](https://www.twilio.com/docs/authy/api) for One-Time-Password logins.
The examples assume that the user has the free [Authy app](https://authy.com/) installed and uses it to generate offline [TOTP](https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm) codes (soft tokens).
You first need to [create an Authy Application in the Twilio Console](https://twilio.com/console/authy/applications?_ga=2.205553366.451688189.1597667213-1526360003.1597667213), then you can create a new Authy user and store a reference to the matching SFTPGo account.
Verify that your Authy application is successfully registered:
```bash
export AUTHY_API_KEY=<your api key here>
curl 'https://api.authy.com/protected/json/app/details' -H "X-Authy-API-Key: $AUTHY_API_KEY"
```
now create an Authy user:
```bash
curl -XPOST "https://api.authy.com/protected/json/users/new" \
-H "X-Authy-API-Key: $AUTHY_API_KEY" \
--data-urlencode user[email]="user@domain.com" \
--data-urlencode user[cellphone]="317-338-9302" \
--data-urlencode user[country_code]="54"
```
The response is something like this:
```json
{"message":"User created successfully.","user":{"id":xxxxxxxx},"success":true}
```
Save the user id somewhere and add a reference to the matching SFTPGo account. You could also store this ID in the `additional_info` SFTPGo user field.
After this step you can use the Authy app installed on your phone to generate TOTP codes.
Now you can verify the token using an HTTP GET request:
```bash
export TOKEN=<TOTP you read from Authy app>
export AUTHY_ID=<user id>
curl -i "https://api.authy.com/protected/json/verify/${TOKEN}/${AUTHY_ID}" \
-H "X-Authy-API-Key: $AUTHY_API_KEY"
```
So inside your hook you need to check:
- the HTTP response code for the verify request, it must be `200`
- the JSON response body, it must contains the key `success` with the value `true` (as string)
If these conditions are met the token is valid and you allow the user to login.
We provide the following examples:
- [Keyboard interactive authentication](./keyint/README.md) for 2FA using password + Authy one time token.
- [External authentication](./extauth/README.md) using Authy one time tokens as passwords.
- [Check password hook](./checkpwd/README.md) for 2FA using a password consisting of a fixed string and a One Time Token.
Please note that these are sample programs not intended for production use, you should write your own hook based on them and you should prefer HTTP based hooks if performance is a concern.
:warning: SFTPGo has also built-in 2FA support.
================================================
FILE: examples/OTP/authy/checkpwd/README.md
================================================
# Authy 2FA via check password hook
This example shows how to use 2FA via the check password hook using a password consisting of a fixed part and an Authy TOTP token. The hook will check the TOTP token using the Authy API and SFTPGo will check the fixed part. Please read the [sample code](./main.go), it should be self explanatory.
================================================
FILE: examples/OTP/authy/checkpwd/go.mod
================================================
module github.com/drakkan/sftpgo/authy/checkpwd
go 1.22.2
================================================
FILE: examples/OTP/authy/checkpwd/main.go
================================================
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
)
type userMapping struct {
SFTPGoUsername string
AuthyID int64
AuthyAPIKey string
}
type checkPasswordResponse struct {
// 0 KO, 1 OK, 2 partial success
Status int `json:"status"`
// for status == 2 this is the password that SFTPGo will check against the one stored
// inside the data provider
ToVerify string `json:"to_verify"`
}
var (
mapping []userMapping
)
func init() {
// this is for demo only, you probably want to get this mapping dynamically, for example using a database query
mapping = append(mapping, userMapping{
SFTPGoUsername: "<SFTPGo username>",
AuthyID: 1234567,
AuthyAPIKey: "<your api key>",
})
}
func printResponse(status int, toVerify string) {
r := checkPasswordResponse{
Status: status,
ToVerify: toVerify,
}
resp, _ := json.Marshal(r)
fmt.Printf("%v\n", string(resp))
if status > 0 {
os.Exit(0)
} else {
os.Exit(1)
}
}
func main() {
// get credentials from env vars
username := os.Getenv("SFTPGO_AUTHD_USERNAME")
password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
for _, m := range mapping {
if m.SFTPGoUsername == username {
// Authy token len is 7, we assume that we have the password followed by the token
pwdLen := len(password)
if pwdLen <= 7 {
printResponse(0, "")
}
pwd := password[:pwdLen-7]
authyToken := password[pwdLen-7:]
// now verify the authy token and instruct SFTPGo to check the password if the token is OK
url := fmt.Sprintf("https://api.authy.com/protected/json/verify/%v/%v", authyToken, m.AuthyID)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("X-Authy-API-Key", m.AuthyAPIKey)
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := httpClient.Do(req)
if err != nil {
printResponse(0, "")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// status code 200 is expected
printResponse(0, "")
}
var authyResponse map[string]interface{}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
printResponse(0, "")
}
err = json.Unmarshal(respBody, &authyResponse)
if err != nil {
printResponse(0, "")
}
if authyResponse["success"].(string) == "true" {
printResponse(2, pwd)
}
printResponse(0, "")
break
}
}
// no mapping found
printResponse(0, "")
}
================================================
FILE: examples/OTP/authy/extauth/README.md
================================================
# Authy external authentication
This example shows how to use Authy TOTP token as password for SFTPGo users. Please read the [sample code](./main.go), it should be self explanatory.
================================================
FILE: examples/OTP/authy/extauth/go.mod
================================================
module github.com/drakkan/sftpgo/authy/extauth
go 1.22.2
================================================
FILE: examples/OTP/authy/extauth/main.go
================================================
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"time"
)
type userMapping struct {
SFTPGoUsername string
AuthyID int64
AuthyAPIKey string
}
// we assume that the SFTPGo already exists, we only check the one time token.
// If you need to create the SFTPGo user more fields are needed here
type minimalSFTPGoUser struct {
Status int `json:"status,omitempty"`
Username string `json:"username"`
HomeDir string `json:"home_dir,omitempty"`
Permissions map[string][]string `json:"permissions"`
}
var (
mapping []userMapping
)
func init() {
// this is for demo only, you probably want to get this mapping dynamically, for example using a database query
mapping = append(mapping, userMapping{
SFTPGoUsername: "<SFTPGo username>",
AuthyID: 1234567,
AuthyAPIKey: "<your api key>",
})
}
func printResponse(username string) {
u := minimalSFTPGoUser{
Username: username,
Status: 1,
HomeDir: filepath.Join(os.TempDir(), username),
}
u.Permissions = make(map[string][]string)
u.Permissions["/"] = []string{"*"}
resp, _ := json.Marshal(u)
fmt.Printf("%v\n", string(resp))
if len(username) > 0 {
os.Exit(0)
} else {
os.Exit(1)
}
}
func main() {
// get credentials from env vars
username := os.Getenv("SFTPGO_AUTHD_USERNAME")
password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
if len(password) == 0 {
// login method is not password
printResponse("")
return
}
for _, m := range mapping {
if m.SFTPGoUsername == username {
// mapping found we can now verify the token
url := fmt.Sprintf("https://api.authy.com/protected/json/verify/%v/%v", password, m.AuthyID)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("X-Authy-API-Key", m.AuthyAPIKey)
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := httpClient.Do(req)
if err != nil {
printResponse("")
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// status code 200 is expected
printResponse("")
}
var authyResponse map[string]interface{}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
printResponse("")
}
err = json.Unmarshal(respBody, &authyResponse)
if err != nil {
printResponse("")
}
if authyResponse["success"].(string) == "true" {
printResponse(username)
}
printResponse("")
break
}
}
// no mapping found
printResponse("")
}
================================================
FILE: examples/OTP/authy/keyint/README.md
================================================
# Authy 2FA using keyboard interactive authentication
This example shows how to authenticate SFTP users using 2FA (password + Authy token). Please read the [sample code](./main.go), it should be self explanatory.
================================================
FILE: examples/OTP/authy/keyint/go.mod
================================================
module github.com/drakkan/sftpgo/authy/keyint
go 1.22.2
================================================
FILE: examples/OTP/authy/keyint/main.go
================================================
package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type userMapping struct {
SFTPGoUsername string
AuthyID int64
AuthyAPIKey string
}
type keyboardAuthHookResponse struct {
Instruction string `json:"instruction,omitempty"`
Questions []string `json:"questions,omitempty"`
Echos []bool `json:"echos,omitempty"`
AuthResult int `json:"auth_result"`
CheckPwd int `json:"check_password,omitempty"`
}
var (
mapping []userMapping
)
func init() {
// this is for demo only, you probably want to get this mapping dynamically, for example using a database query
mapping = append(mapping, userMapping{
SFTPGoUsername: "<SFTPGo username>",
AuthyID: 1234567,
AuthyAPIKey: "<your api key>",
})
}
func printAuthResponse(result int) {
resp, _ := json.Marshal(keyboardAuthHookResponse{
AuthResult: result,
})
fmt.Printf("%v\n", string(resp))
if result == 1 {
os.Exit(0)
} else {
os.Exit(1)
}
}
func main() {
// get credentials from env vars
username := os.Getenv("SFTPGO_AUTHD_USERNAME")
var userMap userMapping
for _, m := range mapping {
if m.SFTPGoUsername == username {
userMap = m
break
}
}
if userMap.SFTPGoUsername != username {
// no mapping found
os.Exit(1)
}
checkPwdQuestion := keyboardAuthHookResponse{
Instruction: "This is a sample keyboard authentication program that ask for your password + Authy token",
Questions: []string{"Your password: "},
Echos: []bool{false},
CheckPwd: 1,
AuthResult: 0,
}
q, _ := json.Marshal(checkPwdQuestion)
fmt.Printf("%v\n", string(q))
// in a real world app you probably want to use a read timeout
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Err() != nil {
printAuthResponse(-1)
}
response := scanner.Text()
if response != "OK" {
printAuthResponse(-1)
}
checkTokenQuestion := keyboardAuthHookResponse{
Instruction: "",
Questions: []string{"Authy token: "},
Echos: []bool{false},
CheckPwd: 0,
AuthResult: 0,
}
q, _ = json.Marshal(checkTokenQuestion)
fmt.Printf("%v\n", string(q))
scanner.Scan()
if scanner.Err() != nil {
printAuthResponse(-1)
}
authyToken := scanner.Text()
url := fmt.Sprintf("https://api.authy.com/protected/json/verify/%v/%v", authyToken, userMap.AuthyID)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
printAuthResponse(-1)
}
req.Header.Set("X-Authy-API-Key", userMap.AuthyAPIKey)
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := httpClient.Do(req)
if err != nil {
printAuthResponse(-1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// status code 200 is expected
printAuthResponse(-1)
}
var authyResponse map[string]interface{}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
printAuthResponse(-1)
}
err = json.Unmarshal(respBody, &authyResponse)
if err != nil {
printAuthResponse(-1)
}
if authyResponse["success"].(string) == "true" {
printAuthResponse(1)
}
printAuthResponse(-1)
}
================================================
FILE: examples/backup/README.md
================================================
# Data Backup
:warning: Since v2.4.0 you can use the [EventManager](https://docs.sftpgo.com/latest/eventmanager/) to schedule backups.
The `backup` example script shows how to use the SFTPGo REST API to backup your data.
The script is written in Python and has the following requirements:
- python3 or python2
- python [Requests](https://requests.readthedocs.io/en/master/) module
The provided example tries to connect to an SFTPGo instance running on `127.0.0.1:8080` using the following credentials:
- username: `admin`
- password: `password`
and, if you execute it daily, it saves a different backup file for each day of the week. The backups will be saved within the configured `backups_path`.
Please edit the script according to your needs.
================================================
FILE: examples/backup/backup
================================================
#!/usr/bin/env python
from datetime import datetime
import sys
import requests
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
# change base_url to point to your SFTPGo installation
base_url = "http://127.0.0.1:8080"
# set to False if you want to skip TLS certificate validation
verify_tls_cert = True
# set the credentials for a valid admin here
admin_user = "admin"
admin_password = "password"
# get a JWT token
auth = requests.auth.HTTPBasicAuth(admin_user, admin_password)
r = requests.get(urlparse.urljoin(base_url, "api/v2/token"), auth=auth, verify=verify_tls_cert, timeout=10)
if r.status_code != 200:
print("error getting access token: {}".format(r.text))
sys.exit(1)
access_token = r.json()["access_token"]
auth_header = {"Authorization": "Bearer " + access_token}
r = requests.get(urlparse.urljoin(base_url, "api/v2/dumpdata"),
params={"output-file":"backup_{}.json".format(datetime.today().strftime('%w'))},
headers=auth_header, verify=verify_tls_cert, timeout=10)
if r.status_code == 200:
print("backup OK")
else:
print("backup error, status {}, response: {}".format(r.status_code, r.text))
================================================
FILE: examples/bulkupdate/README.md
================================================
# Bulk user update
The `bulkuserupdate` example script shows how to use the SFTPGo REST API to easily update some common parameters for multiple users while preserving the others.
The script is written in Python and has the following requirements:
- python3 or python2
- python [Requests](https://requests.readthedocs.io/en/master/) module
The provided example tries to connect to an SFTPGo instance running on `127.0.0.1:8080` using the following credentials:
- username: `admin`
- password: `password`
and it updates some fields for `user1`, `user2` and `user3`.
Please edit the script according to your needs.
================================================
FILE: examples/bulkupdate/bulkuserupdate
================================================
#!/usr/bin/env python
import posixpath
import sys
import requests
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
# change base_url to point to your SFTPGo installation
base_url = "http://127.0.0.1:8080"
# set to False if you want to skip TLS certificate validation
verify_tls_cert = True
# set the credentials for a valid admin here
admin_user = "admin"
admin_password = "password"
# insert here the users you want to update
users_to_update = ["user1", "user2", "user3"]
# set here the fields you need to update
fields_to_update = {"status":0, "quota_files": 1000, "additional_info":"updated using the bulkuserupdate example script"}
# get a JWT token
auth = requests.auth.HTTPBasicAuth(admin_user, admin_password)
r = requests.get(urlparse.urljoin(base_url, "api/v2/token"), auth=auth, verify=verify_tls_cert, timeout=10)
if r.status_code != 200:
print("error getting access token: {}".format(r.text))
sys.exit(1)
access_token = r.json()["access_token"]
auth_header = {"Authorization": "Bearer " + access_token}
for username in users_to_update:
r = requests.get(urlparse.urljoin(base_url, posixpath.join("api/v2/users", username)),
headers=auth_header, verify=verify_tls_cert, timeout=10)
if r.status_code != 200:
print("error getting user {}: {}".format(username, r.text))
continue
user = r.json()
user.update(fields_to_update)
r = requests.put(urlparse.urljoin(base_url, posixpath.join("api/v2/users", username)),
headers=auth_header, verify=verify_tls_cert, json=user, timeout=10)
if r.status_code == 200:
print("user {} updated".format(username))
else:
print("error updating user {}, response code: {} response text: {}".format(username,
r.status_code,
r.text))
================================================
FILE: examples/convertusers/README.md
================================================
# Import users from other stores
`convertusers` is a very simple command line client, written in python, to import users from other stores. It requires `python3` or `python2`.
Here is the usage:
```console
usage: convertusers [-h] [--min-uid MIN_UID] [--max-uid MAX_UID] [--usernames USERNAMES [USERNAMES ...]]
[--force-uid FORCE_UID] [--force-gid FORCE_GID]
input_file {unix-passwd,pure-ftpd,proftpd} output_file
Convert users to a JSON format suitable to use with loadddata
positional arguments:
input_file
{unix-passwd,pure-ftpd,proftpd}
To import from unix-passwd format you need the permission to read /etc/shadow that is typically
granted to the root user only
output_file
optional arguments:
-h, --help show this help message and exit
--min-uid MIN_UID if >= 0 only import users with UID greater or equal to this value. Default: -1
--max-uid MAX_UID if >= 0 only import users with UID lesser or equal to this value. Default: -1
--usernames USERNAMES [USERNAMES ...]
Only import users with these usernames. Default: []
--force-uid FORCE_UID
if >= 0 the imported users will have this UID in SFTPGo. Default: -1
--force-gid FORCE_GID
if >= 0 the imported users will have this GID in SFTPGo. Default: -1
```
Let's see some examples:
```console
python convertusers "" unix-passwd unix_users.json --min-uid 500 --force-uid 1000 --force-gid 1000
```
```console
python convertusers pureftpd.passwd pure-ftpd pure_users.json --usernames "user1" "user2"
```
```console
python convertusers proftpd.passwd proftpd pro_users.json
```
The generated json file can be used as input for the `loaddata` REST API.
Please note that when importing Linux/Unix users the input file is not required: `/etc/passwd` and `/etc/shadow` are automatically parsed. `/etc/shadow` read permission is typically granted to the `root` user only, so you need to execute `convertusers` as `root`.
:warning: SFTPGo does not currently support `yescrypt` hashed passwords.
================================================
FILE: examples/convertusers/convertusers
================================================
#!/usr/bin/env python
import argparse
import json
import sys
import time
try:
import pwd
import spwd
except ImportError:
pwd = None
class ConvertUsers:
def __init__(self, input_file, users_format, output_file, min_uid, max_uid, usernames, force_uid, force_gid):
self.input_file = input_file
self.users_format = users_format
self.output_file = output_file
self.min_uid = min_uid
self.max_uid = max_uid
self.usernames = usernames
self.force_uid = force_uid
self.force_gid = force_gid
self.SFTPGoUsers = []
def buildUserObject(self, username, password, home_dir, uid, gid, max_sessions, quota_size, quota_files, upload_bandwidth,
download_bandwidth, status, expiration_date, allowed_ip=[], denied_ip=[]):
return {'id':0, 'username':username, 'password':password, 'home_dir':home_dir, 'uid':uid, 'gid':gid,
'max_sessions':max_sessions, 'quota_size':quota_size, 'quota_files':quota_files, 'permissions':{'/':["*"]},
'upload_bandwidth':upload_bandwidth, 'download_bandwidth':download_bandwidth,
'status':status, 'expiration_date':expiration_date,
'filters':{'allowed_ip':allowed_ip, 'denied_ip':denied_ip}}
def addUser(self, user):
user['id'] = len(self.SFTPGoUsers) + 1
print('')
print('New user imported: {}'.format(user))
print('')
self.SFTPGoUsers.append(user)
def saveUsers(self):
if self.SFTPGoUsers:
data = {'users':self.SFTPGoUsers}
jsonData = json.dumps(data)
with open(self.output_file, 'w') as f:
f.write(jsonData)
print()
print('Number of users saved to "{}": {}. You can import them using loaddata'.format(self.output_file,
len(self.SFTPGoUsers)))
print()
sys.exit(0)
else:
print('No user imported')
sys.exit(1)
def convert(self):
if self.users_format == 'unix-passwd':
self.convertFromUnixPasswd()
elif self.users_format == 'pure-ftpd':
self.convertFromPureFTPD()
else:
self.convertFromProFTPD()
self.saveUsers()
def isUserValid(self, username, uid):
if self.usernames and not username in self.usernames:
return False
if self.min_uid >= 0 and uid < self.min_uid:
return False
if self.max_uid >= 0 and uid > self.max_uid:
return False
return True
def convertFromUnixPasswd(self):
days_from_epoch_time = time.time() / 86400
for user in pwd.getpwall():
username = user.pw_name
password = user.pw_passwd
uid = user.pw_uid
gid = user.pw_gid
home_dir = user.pw_dir
status = 1
expiration_date = 0
if not self.isUserValid(username, uid):
continue
if self.force_uid >= 0:
uid = self.force_uid
if self.force_gid >= 0:
gid = self.force_gid
# FIXME: if the passwords aren't in /etc/shadow they are probably DES encrypted and we don't support them
if password == 'x' or password == '*':
user_info = spwd.getspnam(username)
password = user_info.sp_pwdp
if not password or password == '!!' or password == '!*':
print('cannot import user "{}" without a password'.format(username))
continue
if user_info.sp_inact > 0:
last_pwd_change_diff = days_from_epoch_time - user_info.sp_lstchg
if last_pwd_change_diff > user_info.sp_inact:
status = 0
if user_info.sp_expire > 0:
expiration_date = user_info.sp_expire * 86400
self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, 0, 0, 0, 0, 0, status,
expiration_date))
def convertFromProFTPD(self):
with open(self.input_file, 'r') as f:
for line in f:
fields = line.split(':')
if len(fields) > 6:
username = fields[0]
password = fields[1]
uid = int(fields[2])
gid = int(fields[3])
home_dir = fields[5]
if not self.isUserValid(username, uid):
continue
if self.force_uid >= 0:
uid = self.force_uid
if self.force_gid >= 0:
gid = self.force_gid
self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, 0, 0, 0, 0, 0, 1, 0))
def convertPureFTPDIP(self, fields):
result = []
if not fields:
return result
for v in fields.split(','):
ip_mask = v.strip()
if not ip_mask:
continue
if ip_mask.count('.') < 3 and ip_mask.count(':') < 3:
print('cannot import pure-ftpd IP: {}'.format(ip_mask))
continue
if '/' not in ip_mask:
ip_mask += '/32'
result.append(ip_mask)
return result
def convertFromPureFTPD(self):
with open(self.input_file, 'r') as f:
for line in f:
fields = line.split(':')
if len(fields) > 16:
username = fields[0]
password = fields[1]
uid = int(fields[2])
gid = int(fields[3])
home_dir = fields[5]
upload_bandwidth = 0
if fields[6]:
upload_bandwidth = int(int(fields[6]) / 1024)
download_bandwidth = 0
if fields[7]:
download_bandwidth = int(int(fields[7]) / 1024)
max_sessions = 0
if fields[10]:
max_sessions = int(fields[10])
quota_files = 0
if fields[11]:
quota_files = int(fields[11])
quota_size = 0
if fields[12]:
quota_size = int(fields[12])
allowed_ip = self.convertPureFTPDIP(fields[15])
denied_ip = self.convertPureFTPDIP(fields[16])
if not self.isUserValid(username, uid):
continue
if self.force_uid >= 0:
uid = self.force_uid
if self.force_gid >= 0:
gid = self.force_gid
self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, max_sessions, quota_size,
quota_files, upload_bandwidth, download_bandwidth, 1, 0, allowed_ip,
denied_ip))
if __name__ == '__main__':
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=
'Convert users to a JSON format suitable to use with loadddata')
supportedUsersFormats = []
help_text = ''
if pwd is not None:
supportedUsersFormats.append('unix-passwd')
help_text = 'To import from unix-passwd format you need the permission to read /etc/shadow that is typically granted to the root user only'
supportedUsersFormats.append('pure-ftpd')
supportedUsersFormats.append('proftpd')
parser.add_argument('input_file', type=str)
parser.add_argument('users_format', type=str, choices=supportedUsersFormats, help=help_text)
parser.add_argument('output_file', type=str)
parser.add_argument('--min-uid', type=int, default=-1, help='if >= 0 only import users with UID greater or equal ' +
'to this value. Default: %(default)s')
parser.add_argument('--max-uid', type=int, default=-1, help='if >= 0 only import users with UID lesser or equal ' +
'to this value. Default: %(default)s')
parser.add_argument('--usernames', type=str, nargs='+', default=[], help='Only import users with these usernames. ' +
'Default: %(default)s')
parser.add_argument('--force-uid', type=int, default=-1, help='if >= 0 the imported users will have this UID in ' +
'SFTPGo. Default: %(default)s')
parser.add_argument('--force-gid', type=int, default=-1, help='if >= 0 the imported users will have this GID in ' +
'SFTPGo. Default: %(default)s')
args = parser.parse_args()
convertUsers = ConvertUsers(args.input_file, args.users_format, args.output_file, args.min_uid, args.max_uid,
args.usernames, args.force_uid, args.force_gid)
convertUsers.convert()
================================================
FILE: examples/ldapauth/README.md
================================================
# LDAPAuth
This is an example for an external authentication program. It performs authentication against an LDAP server.
It is tested against [389ds](https://directory.fedoraproject.org/) and can be used as starting point to authenticate using any LDAP server including Active Directory.
You need to change the LDAP connection parameters and the user search query to match your environment.
You can build this example using the following command:
```console
go build -ldflags "-s -w" -o ldapauth
```
This program assumes that the 389ds schema was extended to add support for public keys using the following ldif file placed in `/etc/dirsrv/schema/98openssh-ldap.ldif`:
```console
dn: cn=schema
changetype: modify
add: attributetypes
attributetypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
-
add: objectclasses
objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY DESC 'MANDATORY: OpenSSH LPK objectclass' MUST ( uid ) MAY ( sshPublicKey ) )
-
dn: cn=sshpublickey,cn=default indexes,cn=config,cn=ldbm database,cn=plugins,cn=config
changetype: add
cn: sshpublickey
nsIndexType: eq
nsIndexType: pres
nsSystemIndex: false
objectClass: top
objectClass: nsIndex
dn: cn=sshpublickey_self_manage,ou=groups,dc=example,dc=com
changetype: add
objectClass: top
objectClass: groupofuniquenames
cn: sshpublickey_self_manage
description: Members of this group gain the ability to edit their own sshPublicKey field
dn: dc=example,dc=com
changetype: modify
add: aci
aci: (targetattr = "sshPublicKey") (version 3.0; acl "Allow members of sshpublickey_self_manage to edit their keys"; allow(write) (groupdn = "ldap:///cn=sshpublickey_self_manage,ou=groups,dc=example,dc=com" and userdn="ldap:///self" ); )
-
```
:warning: A plugin for LDAP/Active Directory authentication is also [available](https://github.com/sftpgo/sftpgo-plugin-auth).
================================================
FILE: examples/ldapauth/go.mod
================================================
module github.com/drakkan/ldapauth
go 1.25.0
require (
github.com/go-ldap/ldap/v3 v3.4.13
golang.org/x/crypto v0.49.0
)
require (
github.com/Azure/go-ntlmssp v0.1.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/google/uuid v1.6.0 // indirect
golang.org/x/sys v0.42.0 // indirect
)
================================================
FILE: examples/ldapauth/go.sum
================================================
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ=
github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: examples/ldapauth/main.go
================================================
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"log/syslog"
"os"
"strconv"
"strings"
"github.com/go-ldap/ldap/v3"
"golang.org/x/crypto/ssh"
)
const (
rootDN = "dc=example,dc=com"
bindUsername = "cn=sftpgo," + rootDN
bindURL = "ldap:///" // That is, the server on the default port of localhost.
passwordFile = "/etc/sftpgo/admin-password.txt" // make this file readable only by the server
publicDir = "/var/www/webdav/public"
)
type userFilters struct {
DeniedLoginMethods []string `json:"denied_login_methods,omitempty"`
}
type minimalSFTPGoUser struct {
Status int `json:"status,omitempty"`
Username string `json:"username"`
HomeDir string `json:"home_dir,omitempty"`
UID int `json:"uid,omitempty"`
GID int `json:"gid,omitempty"`
Permissions map[string][]string `json:"permissions"`
Filters userFilters `json:"filters"`
}
func exitError() {
log.Printf("exitError\n")
u := minimalSFTPGoUser{
Username: "",
}
resp, _ := json.Marshal(u)
fmt.Printf("%v\n", string(resp))
os.Exit(1)
}
func printSuccessResponse(username, homeDir string, uid, gid int, permissions []string) {
u := minimalSFTPGoUser{
Username: username,
HomeDir: homeDir,
UID: uid,
GID: gid,
Status: 1,
}
u.Permissions = make(map[string][]string)
u.Permissions["/"] = permissions
// uncomment the next line to require publickey+password authentication
//u.Filters.DeniedLoginMethods = []string{"publickey", "password", "keyboard-interactive", "publickey+keyboard-interactive"}
resp, _ := json.Marshal(u)
log.Printf("%v\n", string(resp))
fmt.Printf("%v\n", string(resp))
os.Exit(0)
}
func main() {
logWriter, err := syslog.New(syslog.LOG_NOTICE, "sftpgo")
if err == nil {
log.SetOutput(logWriter)
}
// get credentials from env vars
username := os.Getenv("SFTPGO_AUTHD_USERNAME")
password := os.Getenv("SFTPGO_AUTHD_PASSWORD")
publickey := os.Getenv("SFTPGO_AUTHD_PUBLIC_KEY")
if strings.ToLower(username) == "anonymous" {
printSuccessResponse("anonymous", publicDir, 0, 0, []string{"list", "download"})
return
}
l, err := ldap.DialURL(bindURL)
if err != nil {
log.Printf("DialURL: %s\n", err.Error())
exitError()
}
defer l.Close()
// bind to the ldap server with an account that can read users
bindPassword, err := os.ReadFile(passwordFile)
if err != nil {
log.Printf("ReadFile(%s): %s\n", passwordFile, err.Error())
exitError()
}
err = l.Bind(bindUsername, string(bindPassword))
if err != nil {
log.Printf("Bind(%s): %s\n", bindUsername, err.Error())
exitError()
}
// search the user trying to login and fetch some attributes, this search string is tested against 389ds using the default configuration
log.Printf("username=%s\n", username)
searchFilter := fmt.Sprintf("(uid=%s)", ldap.EscapeFilter(username))
searchRequest := ldap.NewSearchRequest(
"ou=people," + rootDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
searchFilter,
[]string{"dn", "uid", "homeDirectory", "uidNumber", "gidNumber", "nsSshPublicKey"},
nil,
)
sr, err := l.Search(searchRequest)
if err != nil {
log.Printf("Search(%s): %s\n", searchFilter, err.Error())
exitError()
}
// we expect exactly one user
if len(sr.Entries) != 1 {
log.Printf("Search(%s): %d entries\n", searchFilter, len(sr.Entries))
exitError()
}
if len(publickey) > 0 {
// check public key
userKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(publickey))
if err != nil {
log.Printf("ParseAuthorizedKey(%s): %s\n", publickey, err.Error())
exitError()
}
authOk := false
for _, k := range sr.Entries[0].GetAttributeValues("nsSshPublicKey") {
key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k))
// we skip an invalid public key stored inside the LDAP server
if err != nil {
continue
}
if bytes.Equal(key.Marshal(), userKey.Marshal()) {
authOk = true
break
}
}
if !authOk {
log.Printf("publickey %s !authOk\n", publickey)
exitError()
}
} else {
// bind to the LDAP server with the user dn and the given password to check the password
userdn := sr.Entries[0].DN
// log.Printf("password=%s\n", password)
err = l.Bind(userdn, password)
if err != nil {
log.Printf("Bind(%s): %s\n", userdn, err.Error())
exitError()
}
}
// People in the LDAP directory aren't necessarily Linux users;
// so they might not have a uidNumber or gidNumber.
uidNumber := sr.Entries[0].GetAttributeValue("uidNumber")
uid, err := strconv.Atoi(uidNumber)
if err != nil {
//log.Printf("uid Atoi(%s) = %s\n", uidNumber, err.Error())
uid = 0
}
gidNumber := sr.Entries[0].GetAttributeValue("gidNumber")
gid, err := strconv.Atoi(gidNumber)
if err != nil {
//log.Printf("gid Atoi(%s) = %s\n", gidNumber, err.Error())
gid = 0
}
homeDir := sr.Entries[0].GetAttributeValue("homeDirectory")
if (len(homeDir) <= 0) {
homeDir = publicDir // homeDir is a required attribute.
}
// return the authenticated user
printSuccessResponse(sr.Entries[0].GetAttributeValue("uid"), homeDir, uid, gid, []string{"*"})
}
================================================
FILE: examples/ldapauthserver/README.md
================================================
# LDAPAuthServer
This is an example for an HTTP server to use as external authentication HTTP hook. It performs authentication against an LDAP server.
It is tested against [389ds](https://directory.fedoraproject.org/) and can be used as starting point to authenticate using any LDAP server including Active Directory.
You can configure the server using the [ldapauth.toml](./ldapauth.toml) configuration file.
You can build this example using the following command:
```console
go build -ldflags "-s -w" -o ldapauthserver
```
:warning: A plugin for LDAP/Active Directory authentication is also [available](https://github.com/sftpgo/sftpgo-plugin-auth).
================================================
FILE: examples/ldapauthserver/cmd/root.go
================================================
package cmd
import (
"fmt"
"os"
"github.com/drakkan/sftpgo/ldapauthserver/config"
"github.com/drakkan/sftpgo/ldapauthserver/utils"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
logSender = "cmd"
configDirFlag = "config-dir"
configDirKey = "config_dir"
configFileFlag = "config-file"
configFileKey = "config_file"
logFilePathFlag = "log-file-path"
logFilePathKey = "log_file_path"
logMaxSizeFlag = "log-max-size"
logMaxSizeKey = "log_max_size"
logMaxBackupFlag = "log-max-backups"
logMaxBackupKey = "log_max_backups"
logMaxAgeFlag = "log-max-age"
logMaxAgeKey = "log_max_age"
logCompressFlag = "log-compress"
logCompressKey = "log_compress"
logVerboseFlag = "log-verbose"
logVerboseKey = "log_verbose"
profilerFlag = "profiler"
profilerKey = "profiler"
defaultConfigDir = "."
defaultConfigName = config.DefaultConfigName
defaultLogFile = "ldapauth.log"
defaultLogMaxSize = 10
defaultLogMaxBackup = 5
defaultLogMaxAge = 28
defaultLogCompress = false
defaultLogVerbose = true
)
var (
configDir string
configFile string
logFilePath string
logMaxSize int
logMaxBackups int
logMaxAge int
logCompress bool
logVerbose bool
rootCmd = &cobra.Command{
Use: "ldapauthserver",
Short: "LDAP Authentication Server for SFTPGo",
}
)
func init() {
version := utils.GetAppVersion()
rootCmd.Flags().BoolP("version", "v", false, "")
rootCmd.Version = version.GetVersionAsString()
rootCmd.SetVersionTemplate(`{{printf "LDAP Authentication Server version: "}}{{printf "%s" .Version}}
`)
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func addConfigFlags(cmd *cobra.Command) {
viper.SetDefault(configDirKey, defaultConfigDir)
viper.BindEnv(configDirKey, "LDAPAUTH_CONFIG_DIR")
cmd.Flags().StringVarP(&configDir, configDirFlag, "c", viper.GetString(configDirKey),
`Location for the config dir. This directory
should contain the "ldapauth" configuration
file or the configured config-file. This flag
can be set using LDAPAUTH_CONFIG_DIR env var too.
`)
viper.BindPFlag(configDirKey, cmd.Flags().Lookup(configDirFlag))
viper.SetDefault(configFileKey, defaultConfigName)
viper.BindEnv(configFileKey, "LDAPAUTH_CONFIG_FILE")
cmd.Flags().StringVarP(&configFile, configFileFlag, "f", viper.GetString(configFileKey),
`Name for the configuration file. It must be
the name of a file stored in config-dir not
the absolute path to the configuration file.
The specified file name must have no extension
we automatically load JSON, YAML, TOML, HCL and
Java properties. Therefore if you set \"ldapauth\"
then \"ldapauth.toml\", \"ldapauth.yaml\" and
so on are searched. This flag can be set using
LDAPAUTH_CONFIG_FILE env var too.
`)
viper.BindPFlag(configFileKey, cmd.Flags().Lookup(configFileFlag))
}
func addServeFlags(cmd *cobra.Command) {
addConfigFlags(cmd)
viper.SetDefault(logFilePathKey, defaultLogFile)
viper.BindEnv(logFilePathKey, "LDAPAUTH_LOG_FILE_PATH")
cmd.Flags().StringVarP(&logFilePath, logFilePathFlag, "l", viper.GetString(logFilePathKey),
`Location for the log file. Leave empty to write
logs to the standard output. This flag can be
set using LDAPAUTH_LOG_FILE_PATH env var too.
`)
viper.BindPFlag(logFilePathKey, cmd.Flags().Lookup(logFilePathFlag))
viper.SetDefault(logMaxSizeKey, defaultLogMaxSize)
viper.BindEnv(logMaxSizeKey, "LDAPAUTH_LOG_MAX_SIZE")
cmd.Flags().IntVarP(&logMaxSize, logMaxSizeFlag, "s", viper.GetInt(logMaxSizeKey),
`Maximum size in megabytes of the log file
before it gets rotated. This flag can be set
using LDAPAUTH_LOG_MAX_SIZE env var too. It
is unused if log-file-path is empty.`)
viper.BindPFlag(logMaxSizeKey, cmd.Flags().Lookup(logMaxSizeFlag))
viper.SetDefault(logMaxBackupKey, defaultLogMaxBackup)
viper.BindEnv(logMaxBackupKey, "LDAPAUTH_LOG_MAX_BACKUPS")
cmd.Flags().IntVarP(&logMaxBackups, "log-max-backups", "b", viper.GetInt(logMaxBackupKey),
`Maximum number of old log files to retain.
This flag can be set using LDAPAUTH_LOG_MAX_BACKUPS
env var too. It is unused if log-file-path is
empty.`)
viper.BindPFlag(logMaxBackupKey, cmd.Flags().Lookup(logMaxBackupFlag))
viper.SetDefault(logMaxAgeKey, defaultLogMaxAge)
viper.BindEnv(logMaxAgeKey, "LDAPAUTH_LOG_MAX_AGE")
cmd.Flags().IntVarP(&logMaxAge, "log-max-age", "a", viper.GetInt(logMaxAgeKey),
`Maximum number of days to retain old log files.
This flag can be set using LDAPAUTH_LOG_MAX_AGE
env var too. It is unused if log-file-path is
empty.`)
viper.BindPFlag(logMaxAgeKey, cmd.Flags().Lookup(logMaxAgeFlag))
viper.SetDefault(logCompressKey, defaultLogCompress)
viper.BindEnv(logCompressKey, "LDAPAUTH_LOG_COMPRESS")
cmd.Flags().BoolVarP(&logCompress, logCompressFlag, "z", viper.GetBool(logCompressKey),
`Determine if the rotated log files
should be compressed using gzip. This flag can
be set using LDAPAUTH_LOG_COMPRESS env var too.
It is unused if log-file-path is empty.`)
viper.BindPFlag(logCompressKey, cmd.Flags().Lookup(logCompressFlag))
viper.SetDefault(logVerboseKey, defaultLogVerbose)
viper.BindEnv(logVerboseKey, "LDAPAUTH_LOG_VERBOSE")
cmd.Flags().BoolVarP(&logVerbose, logVerboseFlag, "v", viper.GetBool(logVerboseKey),
`Enable verbose logs. This flag can be set
using LDAPAUTH_LOG_VERBOSE env var too.
`)
viper.BindPFlag(logVerboseKey, cmd.Flags().Lookup(logVerboseFlag))
}
================================================
FILE: examples/ldapauthserver/cmd/serve.go
================================================
package cmd
import (
"path/filepath"
"github.com/drakkan/sftpgo/ldapauthserver/config"
"github.com/drakkan/sftpgo/ldapauthserver/httpd"
"github.com/drakkan/sftpgo/ldapauthserver/logger"
"github.com/drakkan/sftpgo/ldapauthserver/utils"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)
var (
serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the LDAP Authentication Server",
Long: `To start the server with the default values for the command line flags simply use:
ldapauthserver serve
Please take a look at the usage below to customize the startup options`,
Run: func(cmd *cobra.Command, args []string) {
startServer()
},
}
)
func init() {
rootCmd.AddCommand(serveCmd)
addServeFlags(serveCmd)
}
func startServer() error {
logLevel := zerolog.DebugLevel
if !logVerbose {
logLevel = zerolog.InfoLevel
}
if !filepath.IsAbs(logFilePath) && utils.IsFileInputValid(logFilePath) {
logFilePath = filepath.Join(configDir, logFilePath)
}
logger.InitLogger(logFilePath, logMaxSize, logMaxBackups, logMaxAge, logCompress, logLevel)
version := utils.GetAppVersion()
logger.Info(logSender, "", "starting LDAP Auth Server %v, config dir: %v, config file: %v, log max size: %v log max backups: %v "+
"log max age: %v log verbose: %v, log compress: %v", version.GetVersionAsString(), configDir, configFile, logMaxSize,
logMaxBackups, logMaxAge, logVerbose, logCompress)
config.LoadConfig(configDir, configFile)
return httpd.StartHTTPServer(configDir, config.GetHTTPDConfig())
}
================================================
FILE: examples/ldapauthserver/config/config.go
================================================
package config
import (
"strings"
"github.com/drakkan/sftpgo/ldapauthserver/logger"
"github.com/spf13/viper"
)
const (
logSender = "config"
// DefaultConfigName defines the name for the default config file.
// This is the file name without extension, we use viper and so we
// support all the config files format supported by viper
DefaultConfigName = "ldapauth"
// ConfigEnvPrefix defines a prefix that ENVIRONMENT variables will use
configEnvPrefix = "ldapauth"
)
// HTTPDConfig defines configuration for the HTTPD server
type HTTPDConfig struct {
BindAddress string `mapstructure:"bind_address"`
BindPort int `mapstructure:"bind_port"`
AuthUserFile string `mapstructure:"auth_user_file"`
CertificateFile string `mapstructure:"certificate_file"`
CertificateKeyFile string `mapstructure:"certificate_key_file"`
}
// LDAPConfig defines the configuration parameters for LDAP connections and searches
type LDAPConfig struct {
BaseDN string `mapstructure:"basedn"`
BindURL string `mapstructure:"bind_url"`
BindUsername string `mapstructure:"bind_username"`
BindPassword string `mapstructure:"bind_password"`
SearchFilter string `mapstructure:"search_filter"`
SearchBaseAttrs []string `mapstructure:"search_base_attrs"`
DefaultUID int `mapstructure:"default_uid"`
DefaultGID int `mapstructure:"default_gid"`
ForceDefaultUID bool `mapstructure:"force_default_uid"`
ForceDefaultGID bool `mapstructure:"force_default_gid"`
InsecureSkipVerify bool `mapstructure:"insecure_skip_verify"`
CACertificates []string `mapstructure:"ca_certificates"`
}
type appConfig struct {
HTTPD HTTPDConfig `mapstructure:"httpd"`
LDAP LDAPConfig `mapstructure:"ldap"`
}
var conf appConfig
func init() {
conf = appConfig{
HTTPD: HTTPDConfig{
BindAddress: "",
BindPort: 9000,
AuthUserFile: "",
CertificateFile: "",
CertificateKeyFile: "",
},
LDAP: LDAPConfig{
BaseDN: "dc=example,dc=com",
BindURL: "ldap://192.168.1.103:389",
BindUsername: "cn=Directory Manager",
BindPassword: "YOUR_ADMIN_PASSWORD_HERE",
SearchFilter: "(&(objectClass=nsPerson)(uid=%s))",
SearchBaseAttrs: []string{
"dn",
"homeDirectory",
"uidNumber",
"gidNumber",
"nsSshPublicKey",
},
DefaultUID: 0,
DefaultGID: 0,
ForceDefaultUID: true,
ForceDefaultGID: true,
InsecureSkipVerify: false,
CACertificates: nil,
},
}
viper.SetEnvPrefix(configEnvPrefix)
replacer := strings.NewReplacer(".", "__")
viper.SetEnvKeyReplacer(replacer)
viper.SetConfigName(DefaultConfigName)
viper.AutomaticEnv()
viper.AllowEmptyEnv(true)
}
// GetHomeDirectory returns the configured name for the LDAP field to use as home directory
func (l *LDAPConfig) GetHomeDirectory() string {
if len(l.SearchBaseAttrs) > 1 {
return l.SearchBaseAttrs[1]
}
return "homeDirectory"
}
// GetUIDNumber returns the configured name for the LDAP field to use as UID
func (l *LDAPConfig) GetUIDNumber() string {
if len(l.SearchBaseAttrs) > 2 {
return l.SearchBaseAttrs[2]
}
return "uidNumber"
}
// GetGIDNumber returns the configured name for the LDAP field to use as GID
func (l *LDAPConfig) GetGIDNumber() string {
if len(l.SearchBaseAttrs) > 3 {
return l.SearchBaseAttrs[3]
}
return "gidNumber"
}
// GetPublicKey returns the configured name for the LDAP field to use as public keys
func (l *LDAPConfig) GetPublicKey() string {
if len(l.SearchBaseAttrs) > 4 {
return l.SearchBaseAttrs[4]
}
return "nsSshPublicKey"
}
// GetHTTPDConfig returns the configuration for the HTTP server
func GetHTTPDConfig() HTTPDConfig {
return conf.HTTPD
}
// GetLDAPConfig returns LDAP related settings
func GetLDAPConfig() LDAPConfig {
return conf.LDAP
}
func getRedactedConf() appConfig {
c := conf
return c
}
// LoadConfig loads the configuration
func LoadConfig(configDir, configName string) error {
var err error
viper.AddConfigPath(configDir)
viper.AddConfigPath(".")
viper.SetConfigName(configName)
if err = viper.ReadInConfig(); err != nil {
logger.Warn(logSender, "", "error loading configuration file: %v. Default configuration will be used: %+v",
err, getRedactedConf())
logger.WarnToConsole("error loading configuration file: %v. Default configuration will be used.", err)
return err
}
err = viper.Unmarshal(&conf)
if err != nil {
logger.Warn(logSender, "", "error parsing configuration file: %v. Default configuration will be used: %+v",
err, getRedactedConf())
logger.WarnToConsole("error parsing configuration file: %v. Default configuration will be used.", err)
return err
}
logger.Debug(logSender, "", "config file used: '%q', config loaded: %+v", viper.ConfigFileUsed(), getRedactedConf())
return err
}
================================================
FILE: examples/ldapauthserver/go.mod
================================================
module github.com/drakkan/sftpgo/ldapauthserver
go 1.25.0
require (
github.com/go-chi/chi/v5 v5.2.5
github.com/go-chi/render v1.0.3
github.com/go-ldap/ldap/v3 v3.4.13
github.com/nathanaelle/password/v2 v2.0.1
github.com/rs/zerolog v1.34.0
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
golang.org/x/crypto v0.49.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require (
github.com/Azure/go-ntlmssp v0.1.0 // indirect
github.com/ajg/form v1.7.1 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
================================================
FILE: examples/ldapauthserver/go.sum
================================================
github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=
github.com/Azure/go-ntlmssp v0.1.0/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajg/form v1.7.1 h1:OsnBDzTkrWdrxvEnO68I72ZVGJGNaMwPhoAm0V+llgc=
github.com/ajg/form v1.7.1/go.mod h1:HL757PzLyNkj5AIfptT6L+iGNeXTlnrr/oDePGc/y7Q=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ=
github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/nathanaelle/password/v2 v2.0.1 h1:ItoCTdsuIWzilYmllQPa3DR3YoCXcpfxScWLqr8Ii2s=
github.com/nathanaelle/password/v2 v2.0.1/go.mod h1:eaoT+ICQEPNtikBRIAatN8ThWwMhVG+r1jTw60BvPJk=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: examples/ldapauthserver/httpd/auth.go
================================================
package httpd
import (
"encoding/csv"
"errors"
"fmt"
"net/http"
"os"
"sync"
unixcrypt "github.com/nathanaelle/password/v2"
"github.com/drakkan/sftpgo/ldapauthserver/logger"
"github.com/drakkan/sftpgo/ldapauthserver/utils"
"golang.org/x/crypto/bcrypt"
)
const (
authenticationHeader = "WWW-Authenticate"
authenticationRealm = "LDAP Auth Server"
unauthResponse = "Unauthorized"
)
var (
md5CryptPwdPrefixes = []string{"$1$", "$apr1$"}
bcryptPwdPrefixes = []string{"$2a$", "$2$", "$2x$", "$2y$", "$2b$"}
)
type httpAuthProvider interface {
getHashedPassword(username string) (string, bool)
isEnabled() bool
}
type basicAuthProvider struct {
Path string
sync.RWMutex
Info os.FileInfo
Users map[string]string
}
func newBasicAuthProvider(authUserFile string) (httpAuthProvider, error) {
basicAuthProvider := basicAuthProvider{
Path: authUserFile,
Info: nil,
Users: make(map[string]string),
}
return &basicAuthProvider, basicAuthProvider.loadUsers()
}
func (p *basicAuthProvider) isEnabled() bool {
return len(p.Path) > 0
}
func (p *basicAuthProvider) isReloadNeeded(info os.FileInfo) bool {
p.RLock()
defer p.RUnlock()
return p.Info == nil || p.Info.ModTime() != info.ModTime() || p.Info.Size() != info.Size()
}
func (p *basicAuthProvider) loadUsers() error {
if !p.isEnabled() {
return nil
}
info, err := os.Stat(p.Path)
if err != nil {
logger.Debug(logSender, "", "unable to stat basic auth users file: %v", err)
return err
}
if p.isReloadNeeded(info) {
r, err := os.Open(p.Path)
if err != nil {
logger.Debug(logSender, "", "unable to open basic auth users file: %v", err)
return err
}
defer r.Close()
reader := csv.NewReader(r)
reader.Comma = ':'
reader.Comment = '#'
reader.TrimLeadingSpace = true
records, err := reader.ReadAll()
if err != nil {
logger.Debug(logSender, "", "unable to parse basic auth users file: %v", err)
return err
}
p.Lock()
defer p.Unlock()
p.Users = make(map[string]string)
for _, record := range records {
if len(record) == 2 {
p.Users[record[0]] = record[1]
}
}
logger.Debug(logSender, "", "number of users loaded for httpd basic auth: %v", len(p.Users))
p.Info = info
}
return nil
}
func (p *basicAuthProvider) getHashedPassword(username string) (string, bool) {
err := p.loadUsers()
if err != nil {
return "", false
}
p.RLock()
defer p.RUnlock()
pwd, ok := p.Users[username]
return pwd, ok
}
func checkAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !validateCredentials(r) {
w.Header().Set(authenticationHeader, fmt.Sprintf("Basic realm=\"%v\"", authenticationRealm))
sendAPIResponse(w, r, errors.New(unauthResponse), "", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func validateCredentials(r *http.Request) bool {
if !httpAuth.isEnabled() {
return true
}
username, password, ok := r.BasicAuth()
if !ok {
return false
}
if hashedPwd, ok := httpAuth.getHashedPassword(username); ok {
if utils.IsStringPrefixInSlice(hashedPwd, bcryptPwdPrefixes) {
err := bcrypt.CompareHashAndPassword([]byte(hashedPwd), []byte(password))
return err == nil
}
if utils.IsStringPrefixInSlice(hashedPwd, md5CryptPwdPrefixes) {
crypter, ok := unixcrypt.MD5.CrypterFound(hashedPwd)
if !ok {
err := errors.New("cannot found matching MD5 crypter")
logger.Debug(logSender, "", "error comparing password with MD5 crypt hash: %v", err)
return false
}
return crypter.Verify([]byte(password))
}
}
return false
}
================================================
FILE: examples/ldapauthserver/httpd/httpd.go
================================================
package httpd
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
"os"
"path/filepath"
"time"
"github.com/drakkan/sftpgo/ldapauthserver/config"
"github.com/drakkan/sftpgo/ldapauthserver/logger"
"github.com/drakkan/sftpgo/ldapauthserver/utils"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
)
const (
logSender = "httpd"
versionPath = "/api/v1/version"
checkAuthPath = "/api/v1/check_auth"
maxRequestSize = 1 << 18 // 256KB
)
var (
ldapConfig config.LDAPConfig
httpAuth httpAuthProvider
certMgr *certManager
rootCAs *x509.CertPool
)
// StartHTTPServer initializes and starts the HTTP Server
func StartHTTPServer(configDir string, httpConfig config.HTTPDConfig) error {
var err error
authUserFile := getConfigPath(httpConfig.AuthUserFile, configDir)
httpAuth, err = newBasicAuthProvider(authUserFile)
if err != nil {
return err
}
router := chi.NewRouter()
router.Use(middleware.RequestID)
router.Use(middleware.RealIP)
router.Use(logger.NewStructuredLogger(logger.GetLogger()))
router.Use(middleware.Recoverer)
router.NotFound(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, nil, "Not Found", http.StatusNotFound)
}))
router.MethodNotAllowed(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, nil, "Method not allowed", http.StatusMethodNotAllowed)
}))
router.Get(versionPath, func(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, utils.GetAppVersion())
})
router.Group(func(router chi.Router) {
router.Use(checkAuth)
router.Post(checkAuthPath, checkSFTPGoUserAuth)
})
ldapConfig = config.GetLDAPConfig()
loadCACerts(configDir)
certificateFile := getConfigPath(httpConfig.CertificateFile, configDir)
certificateKeyFile := getConfigPath(httpConfig.CertificateKeyFile, configDir)
httpServer := &http.Server{
Addr: fmt.Sprintf("%s:%d", httpConfig.BindAddress, httpConfig.BindPort),
Handler: router,
ReadTimeout: 70 * time.Second,
WriteTimeout: 70 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 16, // 64KB
}
if len(certificateFile) > 0 && len(certificateKeyFile) > 0 {
certMgr, err = newCertManager(certificateFile, certificateKeyFile)
if err != nil {
return err
}
config := &tls.Config{
GetCertificate: certMgr.GetCertificateFunc(),
MinVersion: tls.VersionTLS12,
}
httpServer.TLSConfig = config
return httpServer.ListenAndServeTLS("", "")
}
return httpServer.ListenAndServe()
}
func sendAPIResponse(w http.ResponseWriter, r *http.Request, err error, message string, code int) {
var errorString string
if err != nil {
errorString = err.Error()
}
resp := apiResponse{
gitextract_3usfk2f2/
├── .cirrus.yml
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── .editorconfig
│ ├── codeql.yml
│ ├── development.yml
│ ├── docker.yml
│ └── release.yml
├── .gitignore
├── .golangci.yml
├── CODEOWNERS
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── Dockerfile.alpine
├── Dockerfile.distroless
├── LICENSE
├── NOTICE
├── README.md
├── SECURITY.md
├── crowdin.yml
├── docker/
│ └── scripts/
│ └── download-plugins.sh
├── examples/
│ ├── OTP/
│ │ └── authy/
│ │ ├── README.md
│ │ ├── checkpwd/
│ │ │ ├── README.md
│ │ │ ├── go.mod
│ │ │ └── main.go
│ │ ├── extauth/
│ │ │ ├── README.md
│ │ │ ├── go.mod
│ │ │ └── main.go
│ │ └── keyint/
│ │ ├── README.md
│ │ ├── go.mod
│ │ └── main.go
│ ├── backup/
│ │ ├── README.md
│ │ └── backup
│ ├── bulkupdate/
│ │ ├── README.md
│ │ └── bulkuserupdate
│ ├── convertusers/
│ │ ├── README.md
│ │ └── convertusers
│ ├── ldapauth/
│ │ ├── README.md
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ ├── ldapauthserver/
│ │ ├── README.md
│ │ ├── cmd/
│ │ │ ├── root.go
│ │ │ └── serve.go
│ │ ├── config/
│ │ │ └── config.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── httpd/
│ │ │ ├── auth.go
│ │ │ ├── httpd.go
│ │ │ ├── ldapauth.go
│ │ │ ├── models.go
│ │ │ └── tlsutils.go
│ │ ├── ldapauth.toml
│ │ ├── logger/
│ │ │ ├── logger.go
│ │ │ ├── request_logger.go
│ │ │ └── sync_wrapper.go
│ │ ├── main.go
│ │ └── utils/
│ │ ├── utils.go
│ │ └── version.go
│ ├── php-activedirectory-http-server/
│ │ └── README.md
│ └── quotascan/
│ ├── README.md
│ └── scanuserquota
├── go.mod
├── go.sum
├── init/
│ ├── com.github.drakkan.sftpgo.plist
│ └── sftpgo.service
├── internal/
│ ├── acme/
│ │ ├── account.go
│ │ └── acme.go
│ ├── bundle/
│ │ └── bundle.go
│ ├── cmd/
│ │ ├── acme.go
│ │ ├── gen.go
│ │ ├── gencompletion.go
│ │ ├── genman.go
│ │ ├── initprovider.go
│ │ ├── install_windows.go
│ │ ├── ping.go
│ │ ├── portable.go
│ │ ├── portable_disabled.go
│ │ ├── reload_windows.go
│ │ ├── resetprovider.go
│ │ ├── resetpwd.go
│ │ ├── revertprovider.go
│ │ ├── root.go
│ │ ├── rotatelogs_windows.go
│ │ ├── serve.go
│ │ ├── service_windows.go
│ │ ├── signals_unix.go
│ │ ├── signals_windows.go
│ │ ├── smtptest.go
│ │ ├── start_windows.go
│ │ ├── status_windows.go
│ │ ├── stop_windows.go
│ │ └── uninstall_windows.go
│ ├── command/
│ │ ├── command.go
│ │ └── command_test.go
│ ├── common/
│ │ ├── actions.go
│ │ ├── actions_test.go
│ │ ├── clientsmap.go
│ │ ├── clientsmap_test.go
│ │ ├── common.go
│ │ ├── common_test.go
│ │ ├── connection.go
│ │ ├── connection_test.go
│ │ ├── dataretention.go
│ │ ├── dataretention_test.go
│ │ ├── defender.go
│ │ ├── defender_test.go
│ │ ├── defenderdb.go
│ │ ├── defenderdb_test.go
│ │ ├── defendermem.go
│ │ ├── eventmanager.go
│ │ ├── eventmanager_test.go
│ │ ├── eventscheduler.go
│ │ ├── httpauth.go
│ │ ├── httpauth_test.go
│ │ ├── protocol_test.go
│ │ ├── ratelimiter.go
│ │ ├── ratelimiter_test.go
│ │ ├── tlsutils.go
│ │ ├── tlsutils_test.go
│ │ ├── transfer.go
│ │ ├── transfer_test.go
│ │ ├── transferschecker.go
│ │ └── transferschecker_test.go
│ ├── config/
│ │ ├── config.go
│ │ ├── config_darwin.go
│ │ ├── config_fallback.go
│ │ ├── config_linux.go
│ │ └── config_test.go
│ ├── dataprovider/
│ │ ├── actions.go
│ │ ├── admin.go
│ │ ├── apikey.go
│ │ ├── bolt.go
│ │ ├── bolt_disabled.go
│ │ ├── cachedpassword.go
│ │ ├── cacheduser.go
│ │ ├── configs.go
│ │ ├── dataprovider.go
│ │ ├── eventrule.go
│ │ ├── group.go
│ │ ├── iplist.go
│ │ ├── memory.go
│ │ ├── mysql.go
│ │ ├── mysql_disabled.go
│ │ ├── node.go
│ │ ├── pgsql.go
│ │ ├── pgsql_disabled.go
│ │ ├── quotaupdater.go
│ │ ├── role.go
│ │ ├── scheduler.go
│ │ ├── session.go
│ │ ├── share.go
│ │ ├── sqlcommon.go
│ │ ├── sqlite.go
│ │ ├── sqlite_disabled.go
│ │ ├── sqlqueries.go
│ │ ├── unixcrypt.go
│ │ ├── unixcrypt_disabled.go
│ │ └── user.go
│ ├── ftpd/
│ │ ├── cryptfs_test.go
│ │ ├── ftpd.go
│ │ ├── ftpd_test.go
│ │ ├── handler.go
│ │ ├── internal_test.go
│ │ ├── server.go
│ │ └── transfer.go
│ ├── httpclient/
│ │ └── httpclient.go
│ ├── httpd/
│ │ ├── api_admin.go
│ │ ├── api_configs.go
│ │ ├── api_defender.go
│ │ ├── api_eventrule.go
│ │ ├── api_events.go
│ │ ├── api_folder.go
│ │ ├── api_group.go
│ │ ├── api_http_user.go
│ │ ├── api_iplist.go
│ │ ├── api_keys.go
│ │ ├── api_maintenance.go
│ │ ├── api_mfa.go
│ │ ├── api_quota.go
│ │ ├── api_retention.go
│ │ ├── api_role.go
│ │ ├── api_shares.go
│ │ ├── api_user.go
│ │ ├── api_utils.go
│ │ ├── auth_utils.go
│ │ ├── file.go
│ │ ├── flash.go
│ │ ├── flash_test.go
│ │ ├── handler.go
│ │ ├── httpd.go
│ │ ├── httpd_test.go
│ │ ├── internal_test.go
│ │ ├── middleware.go
│ │ ├── oauth2.go
│ │ ├── oauth2_test.go
│ │ ├── oidc.go
│ │ ├── oidc_test.go
│ │ ├── oidcmanager.go
│ │ ├── resetcode.go
│ │ ├── resources.go
│ │ ├── resources_embedded.go
│ │ ├── server.go
│ │ ├── token.go
│ │ ├── web.go
│ │ ├── webadmin.go
│ │ ├── webclient.go
│ │ ├── webtask.go
│ │ └── webtask_test.go
│ ├── httpdtest/
│ │ ├── httpdtest.go
│ │ └── httpfsimpl.go
│ ├── jwt/
│ │ ├── jwt.go
│ │ └── jwt_test.go
│ ├── kms/
│ │ ├── basesecret.go
│ │ ├── builtin.go
│ │ ├── kms.go
│ │ └── local.go
│ ├── logger/
│ │ ├── hclog.go
│ │ ├── lego.go
│ │ ├── logger.go
│ │ ├── mail.go
│ │ ├── request_logger.go
│ │ ├── slog.go
│ │ └── sync_wrapper.go
│ ├── metric/
│ │ ├── metric.go
│ │ └── metric_disabled.go
│ ├── mfa/
│ │ ├── mfa.go
│ │ ├── mfa_test.go
│ │ └── totp.go
│ ├── plugin/
│ │ ├── auth.go
│ │ ├── ipfilter.go
│ │ ├── kms.go
│ │ ├── notifier.go
│ │ ├── plugin.go
│ │ ├── searcher.go
│ │ └── util.go
│ ├── service/
│ │ ├── service.go
│ │ ├── service_portable.go
│ │ ├── service_windows.go
│ │ ├── signals_unix.go
│ │ └── signals_windows.go
│ ├── sftpd/
│ │ ├── cryptfs_test.go
│ │ ├── handler.go
│ │ ├── httpfs_test.go
│ │ ├── internal_test.go
│ │ ├── lister.go
│ │ ├── scp.go
│ │ ├── server.go
│ │ ├── sftpd.go
│ │ ├── sftpd_test.go
│ │ ├── ssh_cmd.go
│ │ └── transfer.go
│ ├── smtp/
│ │ ├── oauth2.go
│ │ └── smtp.go
│ ├── telemetry/
│ │ ├── router.go
│ │ ├── telemetry.go
│ │ └── telemetry_test.go
│ ├── util/
│ │ ├── errors.go
│ │ ├── i18n.go
│ │ ├── resources.go
│ │ ├── resources_embedded.go
│ │ ├── util.go
│ │ ├── util_fallback.go
│ │ └── util_unix.go
│ ├── version/
│ │ └── version.go
│ ├── vfs/
│ │ ├── azblobfs.go
│ │ ├── azblobfs_disabled.go
│ │ ├── cryptfs.go
│ │ ├── fileinfo.go
│ │ ├── filesystem.go
│ │ ├── folder.go
│ │ ├── gcsfs.go
│ │ ├── gcsfs_disabled.go
│ │ ├── httpfs.go
│ │ ├── osfs.go
│ │ ├── s3fs.go
│ │ ├── s3fs_disabled.go
│ │ ├── sftpfs.go
│ │ ├── statvfs_fallback.go
│ │ ├── statvfs_linux.go
│ │ ├── statvfs_unix.go
│ │ ├── sys_unix.go
│ │ ├── sys_windows.go
│ │ └── vfs.go
│ └── webdavd/
│ ├── file.go
│ ├── handler.go
│ ├── internal_test.go
│ ├── mimecache.go
│ ├── server.go
│ ├── webdavd.go
│ └── webdavd_test.go
├── main.go
├── openapi/
│ ├── httpfs.yaml
│ ├── openapi.yaml
│ └── swagger-ui/
│ ├── index.css
│ ├── index.html
│ ├── swagger-initializer.js
│ ├── swagger-ui-bundle.js
│ ├── swagger-ui-standalone-preset.js
│ └── swagger-ui.css
├── pkgs/
│ ├── build.sh
│ ├── choco/
│ │ ├── sftpgo.nuspec
│ │ └── tools/
│ │ └── ChocolateyInstall.ps1
│ ├── debian/
│ │ ├── changelog
│ │ ├── compat
│ │ ├── control
│ │ ├── copyright
│ │ ├── patches/
│ │ │ ├── config.diff
│ │ │ └── series
│ │ ├── postinst
│ │ ├── rules
│ │ ├── sftpgo-docs.docs
│ │ ├── sftpgo.dirs
│ │ ├── sftpgo.install
│ │ ├── sftpgo.install.arm64
│ │ ├── sftpgo.install.armhf
│ │ ├── sftpgo.install.ppc64el
│ │ └── source/
│ │ └── format
│ └── scripts/
│ ├── deb/
│ │ ├── postinstall.sh
│ │ ├── postremove.sh
│ │ └── preremove.sh
│ └── rpm/
│ ├── postinstall
│ ├── postremove
│ └── preremove
├── sftpgo.json
├── static/
│ ├── assets/
│ │ ├── css/
│ │ │ └── style.bundle.css
│ │ ├── js/
│ │ │ └── scripts.bundle.js
│ │ └── plugins/
│ │ ├── custom/
│ │ │ ├── datatables/
│ │ │ │ ├── datatables.bundle.css
│ │ │ │ └── datatables.bundle.js
│ │ │ ├── flatpickr/
│ │ │ │ └── l10n/
│ │ │ │ ├── de.js
│ │ │ │ ├── es.js
│ │ │ │ ├── fr.js
│ │ │ │ ├── it.js
│ │ │ │ └── zh.js
│ │ │ └── formrepeater/
│ │ │ └── formrepeater.bundle.js
│ │ └── global/
│ │ ├── plugins.bundle.css
│ │ └── plugins.bundle.js
│ └── locales/
│ ├── de/
│ │ └── translation.json
│ ├── en/
│ │ └── translation.json
│ ├── es/
│ │ └── translation.json
│ ├── fr/
│ │ └── translation.json
│ ├── it/
│ │ └── translation.json
│ └── zh-CN/
│ └── translation.json
├── templates/
│ ├── common/
│ │ ├── base.html
│ │ ├── baselogin.html
│ │ ├── changepassword.html
│ │ ├── forgot-password.html
│ │ ├── login.html
│ │ ├── message.html
│ │ ├── reset-password.html
│ │ ├── twofactor-recovery.html
│ │ └── twofactor.html
│ ├── email/
│ │ ├── password-expiration.html
│ │ └── reset-password.html
│ ├── webadmin/
│ │ ├── admin.html
│ │ ├── admins.html
│ │ ├── adminsetup.html
│ │ ├── base.html
│ │ ├── configs.html
│ │ ├── connections.html
│ │ ├── defender.html
│ │ ├── eventaction.html
│ │ ├── eventactions.html
│ │ ├── eventrule.html
│ │ ├── eventrules.html
│ │ ├── events.html
│ │ ├── folder.html
│ │ ├── folders.html
│ │ ├── fsconfig.html
│ │ ├── group.html
│ │ ├── groups.html
│ │ ├── iplist.html
│ │ ├── iplists.html
│ │ ├── maintenance.html
│ │ ├── mfa.html
│ │ ├── profile.html
│ │ ├── role.html
│ │ ├── roles.html
│ │ ├── status.html
│ │ ├── user.html
│ │ └── users.html
│ └── webclient/
│ ├── base.html
│ ├── editfile.html
│ ├── files.html
│ ├── mfa.html
│ ├── profile.html
│ ├── share.html
│ ├── sharedownload.html
│ ├── sharelogin.html
│ ├── shares.html
│ ├── shareupload.html
│ └── viewpdf.html
├── tests/
│ ├── eventsearcher/
│ │ ├── go.mod
│ │ ├── go.sum
│ │ └── main.go
│ └── ipfilter/
│ ├── go.mod
│ ├── go.sum
│ └── main.go
└── windows-installer/
├── LICENSE_with_NOTICE.txt
├── README.txt
└── sftpgo.iss
Showing preview only (1,225K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (12155 symbols across 250 files)
FILE: examples/OTP/authy/checkpwd/main.go
type userMapping (line 13) | type userMapping struct
type checkPasswordResponse (line 19) | type checkPasswordResponse struct
function init (line 31) | func init() {
function printResponse (line 40) | func printResponse(status int, toVerify string) {
function main (line 54) | func main() {
FILE: examples/OTP/authy/extauth/main.go
type userMapping (line 14) | type userMapping struct
type minimalSFTPGoUser (line 22) | type minimalSFTPGoUser struct
function init (line 33) | func init() {
function printResponse (line 42) | func printResponse(username string) {
function main (line 59) | func main() {
FILE: examples/OTP/authy/keyint/main.go
type userMapping (line 13) | type userMapping struct
type keyboardAuthHookResponse (line 19) | type keyboardAuthHookResponse struct
function init (line 31) | func init() {
function printAuthResponse (line 40) | func printAuthResponse(result int) {
function main (line 52) | func main() {
FILE: examples/ldapauth/main.go
constant rootDN (line 18) | rootDN = "dc=example,dc=com"
constant bindUsername (line 19) | bindUsername = "cn=sftpgo," + rootDN
constant bindURL (line 20) | bindURL = "ldap:///"
constant passwordFile (line 21) | passwordFile = "/etc/sftpgo/admin-password.txt"
constant publicDir (line 22) | publicDir = "/var/www/webdav/public"
type userFilters (line 25) | type userFilters struct
type minimalSFTPGoUser (line 29) | type minimalSFTPGoUser struct
function exitError (line 39) | func exitError() {
function printSuccessResponse (line 49) | func printSuccessResponse(username, homeDir string, uid, gid int, permis...
function main (line 67) | func main() {
FILE: examples/ldapauthserver/cmd/root.go
constant logSender (line 14) | logSender = "cmd"
constant configDirFlag (line 15) | configDirFlag = "config-dir"
constant configDirKey (line 16) | configDirKey = "config_dir"
constant configFileFlag (line 17) | configFileFlag = "config-file"
constant configFileKey (line 18) | configFileKey = "config_file"
constant logFilePathFlag (line 19) | logFilePathFlag = "log-file-path"
constant logFilePathKey (line 20) | logFilePathKey = "log_file_path"
constant logMaxSizeFlag (line 21) | logMaxSizeFlag = "log-max-size"
constant logMaxSizeKey (line 22) | logMaxSizeKey = "log_max_size"
constant logMaxBackupFlag (line 23) | logMaxBackupFlag = "log-max-backups"
constant logMaxBackupKey (line 24) | logMaxBackupKey = "log_max_backups"
constant logMaxAgeFlag (line 25) | logMaxAgeFlag = "log-max-age"
constant logMaxAgeKey (line 26) | logMaxAgeKey = "log_max_age"
constant logCompressFlag (line 27) | logCompressFlag = "log-compress"
constant logCompressKey (line 28) | logCompressKey = "log_compress"
constant logVerboseFlag (line 29) | logVerboseFlag = "log-verbose"
constant logVerboseKey (line 30) | logVerboseKey = "log_verbose"
constant profilerFlag (line 31) | profilerFlag = "profiler"
constant profilerKey (line 32) | profilerKey = "profiler"
constant defaultConfigDir (line 33) | defaultConfigDir = "."
constant defaultConfigName (line 34) | defaultConfigName = config.DefaultConfigName
constant defaultLogFile (line 35) | defaultLogFile = "ldapauth.log"
constant defaultLogMaxSize (line 36) | defaultLogMaxSize = 10
constant defaultLogMaxBackup (line 37) | defaultLogMaxBackup = 5
constant defaultLogMaxAge (line 38) | defaultLogMaxAge = 28
constant defaultLogCompress (line 39) | defaultLogCompress = false
constant defaultLogVerbose (line 40) | defaultLogVerbose = true
function init (line 59) | func init() {
function Execute (line 69) | func Execute() {
function addConfigFlags (line 76) | func addConfigFlags(cmd *cobra.Command) {
function addServeFlags (line 103) | func addServeFlags(cmd *cobra.Command) {
FILE: examples/ldapauthserver/cmd/serve.go
function init (line 29) | func init() {
function startServer (line 34) | func startServer() error {
FILE: examples/ldapauthserver/config/config.go
constant logSender (line 11) | logSender = "config"
constant DefaultConfigName (line 15) | DefaultConfigName = "ldapauth"
constant configEnvPrefix (line 17) | configEnvPrefix = "ldapauth"
type HTTPDConfig (line 21) | type HTTPDConfig struct
type LDAPConfig (line 30) | type LDAPConfig struct
method GetHomeDirectory (line 91) | func (l *LDAPConfig) GetHomeDirectory() string {
method GetUIDNumber (line 99) | func (l *LDAPConfig) GetUIDNumber() string {
method GetGIDNumber (line 107) | func (l *LDAPConfig) GetGIDNumber() string {
method GetPublicKey (line 115) | func (l *LDAPConfig) GetPublicKey() string {
type appConfig (line 45) | type appConfig struct
function init (line 52) | func init() {
function GetHTTPDConfig (line 123) | func GetHTTPDConfig() HTTPDConfig {
function GetLDAPConfig (line 128) | func GetLDAPConfig() LDAPConfig {
function getRedactedConf (line 132) | func getRedactedConf() appConfig {
function LoadConfig (line 138) | func LoadConfig(configDir, configName string) error {
FILE: examples/ldapauthserver/httpd/auth.go
constant authenticationHeader (line 19) | authenticationHeader = "WWW-Authenticate"
constant authenticationRealm (line 20) | authenticationRealm = "LDAP Auth Server"
constant unauthResponse (line 21) | unauthResponse = "Unauthorized"
type httpAuthProvider (line 29) | type httpAuthProvider interface
type basicAuthProvider (line 34) | type basicAuthProvider struct
method isEnabled (line 50) | func (p *basicAuthProvider) isEnabled() bool {
method isReloadNeeded (line 54) | func (p *basicAuthProvider) isReloadNeeded(info os.FileInfo) bool {
method loadUsers (line 60) | func (p *basicAuthProvider) loadUsers() error {
method getHashedPassword (line 99) | func (p *basicAuthProvider) getHashedPassword(username string) (string...
function newBasicAuthProvider (line 41) | func newBasicAuthProvider(authUserFile string) (httpAuthProvider, error) {
function checkAuth (line 110) | func checkAuth(next http.Handler) http.Handler {
function validateCredentials (line 121) | func validateCredentials(r *http.Request) bool {
FILE: examples/ldapauthserver/httpd/httpd.go
constant logSender (line 22) | logSender = "httpd"
constant versionPath (line 23) | versionPath = "/api/v1/version"
constant checkAuthPath (line 24) | checkAuthPath = "/api/v1/check_auth"
constant maxRequestSize (line 25) | maxRequestSize = 1 << 18
function StartHTTPServer (line 36) | func StartHTTPServer(configDir string, httpConfig config.HTTPDConfig) er...
function sendAPIResponse (line 97) | func sendAPIResponse(w http.ResponseWriter, r *http.Request, err error, ...
function loadCACerts (line 111) | func loadCACerts(configDir string) error {
function ReloadTLSCertificate (line 135) | func ReloadTLSCertificate() {
function getConfigPath (line 141) | func getConfigPath(name, configDir string) string {
FILE: examples/ldapauthserver/httpd/ldapauth.go
function getSFTPGoUser (line 18) | func getSFTPGoUser(entry *ldap.Entry, username string) (SFTPGoUser, erro...
function checkSFTPGoUserAuth (line 51) | func checkSFTPGoUserAuth(w http.ResponseWriter, r *http.Request) {
FILE: examples/ldapauthserver/httpd/models.go
type apiResponse (line 3) | type apiResponse struct
type externalAuthRequest (line 9) | type externalAuthRequest struct
type SFTPGoExtensionsFilter (line 16) | type SFTPGoExtensionsFilter struct
type SFTPGoUserFilters (line 23) | type SFTPGoUserFilters struct
type S3FsConfig (line 31) | type S3FsConfig struct
type GCSFsConfig (line 44) | type GCSFsConfig struct
type SFTPGoFilesystem (line 53) | type SFTPGoFilesystem struct
type virtualFolder (line 60) | type virtualFolder struct
type SFTPGoUser (line 66) | type SFTPGoUser struct
FILE: examples/ldapauthserver/httpd/tlsutils.go
type certManager (line 10) | type certManager struct
method loadCertificate (line 17) | func (m *certManager) loadCertificate() error {
method GetCertificateFunc (line 30) | func (m *certManager) GetCertificateFunc() func(*tls.ClientHelloInfo) ...
function newCertManager (line 38) | func newCertManager(certificateFile, certificateKeyFile string) (*certMa...
FILE: examples/ldapauthserver/logger/logger.go
constant dateFormat (line 14) | dateFormat = "2006-01-02T15:04:05.000"
function GetLogger (line 23) | func GetLogger() *zerolog.Logger {
function InitLogger (line 28) | func InitLogger(logFilePath string, logMaxSize, logMaxBackups, logMaxAge...
function DisableLogger (line 50) | func DisableLogger() {
function EnableConsoleLogger (line 55) | func EnableConsoleLogger(level zerolog.Level) {
function Debug (line 65) | func Debug(prefix, requestID string, format string, v ...interface{}) {
function Info (line 74) | func Info(prefix, requestID string, format string, v ...interface{}) {
function Warn (line 83) | func Warn(prefix, requestID string, format string, v ...interface{}) {
function Error (line 92) | func Error(prefix, requestID string, format string, v ...interface{}) {
function DebugToConsole (line 101) | func DebugToConsole(format string, v ...interface{}) {
function InfoToConsole (line 106) | func InfoToConsole(format string, v ...interface{}) {
function WarnToConsole (line 111) | func WarnToConsole(format string, v ...interface{}) {
function ErrorToConsole (line 116) | func ErrorToConsole(format string, v ...interface{}) {
function isLogFilePathValid (line 120) | func isLogFilePathValid(logFilePath string) bool {
FILE: examples/ldapauthserver/logger/request_logger.go
type StructuredLogger (line 14) | type StructuredLogger struct
method NewLogEntry (line 31) | func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.Log...
type StructuredLoggerEntry (line 19) | type StructuredLoggerEntry struct
method Write (line 53) | func (l *StructuredLoggerEntry) Write(status, bytes int, header http.H...
method Panic (line 65) | func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
function NewStructuredLogger (line 26) | func NewStructuredLogger(logger *zerolog.Logger) func(next http.Handler)...
FILE: examples/ldapauthserver/logger/sync_wrapper.go
type logSyncWrapper (line 8) | type logSyncWrapper struct
method Write (line 13) | func (l *logSyncWrapper) Write(b []byte) (n int, err error) {
FILE: examples/ldapauthserver/main.go
function main (line 5) | func main() {
FILE: examples/ldapauthserver/utils/utils.go
function IsFileInputValid (line 11) | func IsFileInputValid(fileInput string) bool {
function IsStringPrefixInSlice (line 21) | func IsStringPrefixInSlice(obj string, list []string) bool {
FILE: examples/ldapauthserver/utils/version.go
constant version (line 3) | version = "0.1.0-dev"
type VersionInfo (line 12) | type VersionInfo struct
method GetVersionAsString (line 27) | func (v *VersionInfo) GetVersionAsString() string {
function init (line 18) | func init() {
function GetAppVersion (line 39) | func GetAppVersion() VersionInfo {
FILE: internal/acme/account.go
type account (line 23) | type account struct
method GetEmail (line 32) | func (a *account) GetEmail() string {
method GetRegistration (line 37) | func (a *account) GetRegistration() *registration.Resource {
method GetPrivateKey (line 42) | func (a *account) GetPrivateKey() crypto.PrivateKey {
FILE: internal/acme/acme.go
constant logSender (line 61) | logSender = "acme"
function SetReloadHTTPDCertsFn (line 81) | func SetReloadHTTPDCertsFn(fn func() error) {
function GetCertificates (line 86) | func GetCertificates() error {
function GetCertificatesForConfig (line 95) | func GetCertificatesForConfig(c *dataprovider.ACMEConfigs, configDir str...
function GetHTTP01WebRoot (line 115) | func GetHTTP01WebRoot() string {
function mergeConfig (line 119) | func mergeConfig(config Configuration, c *dataprovider.ACMEConfigs) Conf...
function getConfiguration (line 128) | func getConfiguration() Configuration {
function loadProviderConf (line 132) | func loadProviderConf(c Configuration) (Configuration, error) {
function Initialize (line 145) | func Initialize(c Configuration, configDir string, checkRenew bool) error {
type HTTP01Challenge (line 171) | type HTTP01Challenge struct
method isEnabled (line 177) | func (c *HTTP01Challenge) isEnabled() bool {
method validate (line 181) | func (c *HTTP01Challenge) validate() error {
type TLSALPN01Challenge (line 202) | type TLSALPN01Challenge struct
method isEnabled (line 206) | func (c *TLSALPN01Challenge) isEnabled() bool {
method validate (line 210) | func (c *TLSALPN01Challenge) validate() error {
type Configuration (line 221) | type Configuration struct
method Initialize (line 239) | func (c *Configuration) Initialize(configDir string) error {
method validateChallenges (line 289) | func (c *Configuration) validateChallenges() error {
method checkDomains (line 299) | func (c *Configuration) checkDomains() {
method setLockTime (line 313) | func (c *Configuration) setLockTime() error {
method getLockTime (line 324) | func (c *Configuration) getLockTime() (time.Time, error) {
method saveAccount (line 342) | func (c *Configuration) saveAccount(account *account) error {
method getAccount (line 355) | func (c *Configuration) getAccount(privateKey crypto.PrivateKey) (acco...
method loadPrivateKey (line 390) | func (c *Configuration) loadPrivateKey() (crypto.PrivateKey, error) {
method generatePrivateKey (line 419) | func (c *Configuration) generatePrivateKey() (crypto.PrivateKey, error) {
method getPrivateKey (line 443) | func (c *Configuration) getPrivateKey() (crypto.PrivateKey, error) {
method loadCertificatesForDomain (line 453) | func (c *Configuration) loadCertificatesForDomain(domain string) ([]*x...
method needRenewal (line 469) | func (c *Configuration) needRenewal(x509Cert *x509.Certificate, domain...
method setup (line 482) | func (c *Configuration) setup() (*account, *lego.Client, error) {
method setupChalleges (line 516) | func (c *Configuration) setupChalleges(client *lego.Client) error {
method register (line 562) | func (c *Configuration) register(client *lego.Client) (*registration.R...
method tryRecoverRegistration (line 566) | func (c *Configuration) tryRecoverRegistration(privateKey crypto.Priva...
method getCrtPath (line 587) | func (c *Configuration) getCrtPath(domain string) string {
method getKeyPath (line 591) | func (c *Configuration) getKeyPath(domain string) string {
method getResourcePath (line 595) | func (c *Configuration) getResourcePath(domain string) string {
method obtainAndSaveCertificate (line 599) | func (c *Configuration) obtainAndSaveCertificate(client *lego.Client, ...
method hasCertificates (line 641) | func (c *Configuration) hasCertificates(domain string) (bool, error) {
method getCertificates (line 659) | func (c *Configuration) getCertificates() error {
method notifyCertificateRenewal (line 685) | func (c *Configuration) notifyCertificateRenewal(domain string, err er...
method renewCertificates (line 703) | func (c *Configuration) renewCertificates() error {
function isDomainValid (line 768) | func isDomainValid(domain string) (string, bool) {
function getDomains (line 780) | func getDomains(domain string) []string {
function stopScheduler (line 797) | func stopScheduler() {
function startScheduler (line 804) | func startScheduler() error {
function renewCertificates (line 825) | func renewCertificates() {
function setLogMode (line 833) | func setLogMode(checkRenew bool) {
function acmeLog (line 844) | func acmeLog(level logger.LogLevel, format string, v ...any) {
FILE: internal/bundle/bundle.go
function init (line 28) | func init() {
function GetTemplatesFs (line 42) | func GetTemplatesFs() embed.FS {
function GetStaticFs (line 47) | func GetStaticFs() http.FileSystem {
function GetOpenAPIFs (line 57) | func GetOpenAPIFs() http.FileSystem {
FILE: internal/cmd/acme.go
function init (line 95) | func init() {
FILE: internal/cmd/gen.go
function init (line 24) | func init() {
FILE: internal/cmd/gencompletion.go
function init (line 126) | func init() {
FILE: internal/cmd/genman.go
function init (line 66) | func init() {
FILE: internal/cmd/initprovider.go
function init (line 121) | func init() {
FILE: internal/cmd/install_windows.go
function init (line 70) | func init() {
function getCustomServeFlags (line 75) | func getCustomServeFlags() []string {
FILE: internal/cmd/ping.go
function getHealthzURLFromBindings (line 32) | func getHealthzURLFromBindings(bindings []httpd.Binding) string {
function init (line 117) | func init() {
FILE: internal/cmd/portable.go
function init (line 305) | func init() {
function parsePatternsFilesFilters (line 458) | func parsePatternsFilesFilters() []sdk.PatternsFilter {
function getPatternsFilterValues (line 493) | func getPatternsFilterValues(value string) (string, []string) {
function getFileContents (line 513) | func getFileContents(name string) (string, error) {
function convertFsProvider (line 528) | func convertFsProvider() string {
FILE: internal/cmd/portable_disabled.go
function init (line 21) | func init() {
FILE: internal/cmd/reload_windows.go
function init (line 47) | func init() {
FILE: internal/cmd/resetprovider.go
function init (line 84) | func init() {
FILE: internal/cmd/resetpwd.go
function init (line 122) | func init() {
FILE: internal/cmd/revertprovider.go
function init (line 88) | func init() {
FILE: internal/cmd/root.go
constant configDirFlag (line 29) | configDirFlag = "config-dir"
constant configDirKey (line 30) | configDirKey = "config_dir"
constant configFileFlag (line 31) | configFileFlag = "config-file"
constant configFileKey (line 32) | configFileKey = "config_file"
constant logFilePathFlag (line 33) | logFilePathFlag = "log-file-path"
constant logFilePathKey (line 34) | logFilePathKey = "log_file_path"
constant logMaxSizeFlag (line 35) | logMaxSizeFlag = "log-max-size"
constant logMaxSizeKey (line 36) | logMaxSizeKey = "log_max_size"
constant logMaxBackupFlag (line 37) | logMaxBackupFlag = "log-max-backups"
constant logMaxBackupKey (line 38) | logMaxBackupKey = "log_max_backups"
constant logMaxAgeFlag (line 39) | logMaxAgeFlag = "log-max-age"
constant logMaxAgeKey (line 40) | logMaxAgeKey = "log_max_age"
constant logCompressFlag (line 41) | logCompressFlag = "log-compress"
constant logCompressKey (line 42) | logCompressKey = "log_compress"
constant logLevelFlag (line 43) | logLevelFlag = "log-level"
constant logLevelKey (line 44) | logLevelKey = "log_level"
constant logUTCTimeFlag (line 45) | logUTCTimeFlag = "log-utc-time"
constant logUTCTimeKey (line 46) | logUTCTimeKey = "log_utc_time"
constant loadDataFromFlag (line 47) | loadDataFromFlag = "loaddata-from"
constant loadDataFromKey (line 48) | loadDataFromKey = "loaddata_from"
constant loadDataModeFlag (line 49) | loadDataModeFlag = "loaddata-mode"
constant loadDataModeKey (line 50) | loadDataModeKey = "loaddata_mode"
constant loadDataQuotaScanFlag (line 51) | loadDataQuotaScanFlag = "loaddata-scan"
constant loadDataQuotaScanKey (line 52) | loadDataQuotaScanKey = "loaddata_scan"
constant loadDataCleanFlag (line 53) | loadDataCleanFlag = "loaddata-clean"
constant loadDataCleanKey (line 54) | loadDataCleanKey = "loaddata_clean"
constant graceTimeFlag (line 55) | graceTimeFlag = "grace-time"
constant graceTimeKey (line 56) | graceTimeKey = "grace_time"
constant defaultConfigDir (line 57) | defaultConfigDir = "."
constant defaultConfigFile (line 58) | defaultConfigFile = ""
constant defaultLogFile (line 59) | defaultLogFile = "sftpgo.log"
constant defaultLogMaxSize (line 60) | defaultLogMaxSize = 10
constant defaultLogMaxBackup (line 61) | defaultLogMaxBackup = 5
constant defaultLogMaxAge (line 62) | defaultLogMaxAge = 28
constant defaultLogCompress (line 63) | defaultLogCompress = false
constant defaultLogLevel (line 64) | defaultLogLevel = "debug"
constant defaultLogUTCTime (line 65) | defaultLogUTCTime = false
constant defaultLoadDataFrom (line 66) | defaultLoadDataFrom = ""
constant defaultLoadDataMode (line 67) | defaultLoadDataMode = 1
constant defaultLoadDataQuotaScan (line 68) | defaultLoadDataQuotaScan = 0
constant defaultLoadDataClean (line 69) | defaultLoadDataClean = false
constant defaultGraceTime (line 70) | defaultGraceTime = 0
function init (line 95) | func init() {
function Execute (line 105) | func Execute() {
function addConfigFlags (line 112) | func addConfigFlags(cmd *cobra.Command) {
function addBaseLoadDataFlags (line 147) | func addBaseLoadDataFlags(cmd *cobra.Command) {
function addServeFlags (line 184) | func addServeFlags(cmd *cobra.Command) {
FILE: internal/cmd/rotatelogs_windows.go
function init (line 47) | func init() {
FILE: internal/cmd/serve.go
constant envFileMaxSize (line 31) | envFileMaxSize = 1048576
function setIntFromEnv (line 75) | func setIntFromEnv(receiver *int, val string) {
function setBoolFromEnv (line 82) | func setBoolFromEnv(receiver *bool, val string) {
function checkServeParamsFromEnvFiles (line 89) | func checkServeParamsFromEnvFiles(configDir string) { //nolint:gocyclo
function init (line 144) | func init() {
FILE: internal/cmd/service_windows.go
function init (line 28) | func init() {
FILE: internal/cmd/signals_unix.go
function registerSignals (line 28) | func registerSignals() {
FILE: internal/cmd/signals_windows.go
function registerSignals (line 25) | func registerSignals() {
FILE: internal/cmd/smtptest.go
function init (line 70) | func init() {
FILE: internal/cmd/start_windows.go
function init (line 65) | func init() {
FILE: internal/cmd/status_windows.go
function init (line 47) | func init() {
FILE: internal/cmd/stop_windows.go
function init (line 47) | func init() {
FILE: internal/cmd/uninstall_windows.go
function init (line 47) | func init() {
FILE: internal/command/command.go
constant minTimeout (line 26) | minTimeout = 1
constant maxTimeout (line 27) | maxTimeout = 300
constant defaultTimeout (line 28) | defaultTimeout = 30
constant HookFsActions (line 33) | HookFsActions = "fs_actions"
constant HookProviderActions (line 34) | HookProviderActions = "provider_actions"
constant HookStartup (line 35) | HookStartup = "startup"
constant HookPostConnect (line 36) | HookPostConnect = "post_connect"
constant HookPostDisconnect (line 37) | HookPostDisconnect = "post_disconnect"
constant HookCheckPassword (line 38) | HookCheckPassword = "check_password"
constant HookPreLogin (line 39) | HookPreLogin = "pre_login"
constant HookPostLogin (line 40) | HookPostLogin = "post_login"
constant HookExternalAuth (line 41) | HookExternalAuth = "external_auth"
constant HookKeyboardInteractive (line 42) | HookKeyboardInteractive = "keyboard_interactive"
type Command (line 52) | type Command struct
type Config (line 72) | type Config struct
method Initialize (line 91) | func (c Config) Initialize() error {
function init (line 84) | func init() {
function GetConfig (line 128) | func GetConfig(command, hook string) (time.Duration, []string, []string) {
FILE: internal/command/command_test.go
function TestCommandConfig (line 25) | func TestCommandConfig(t *testing.T) {
function TestConfigErrors (line 126) | func TestConfigErrors(t *testing.T) {
FILE: internal/common/actions.go
function startNewHook (line 49) | func startNewHook() {
function hookEnded (line 54) | func hookEnded() {
type ProtocolActions (line 60) | type ProtocolActions struct
function InitializeActionHandler (line 77) | func InitializeActionHandler(handler ActionHandler) {
function ExecutePreAction (line 86) | func ExecutePreAction(conn *BaseConnection, operation, filePath, virtual...
function ExecuteActionNotification (line 132) | func ExecuteActionNotification(conn *BaseConnection, operation, filePath...
type ActionHandler (line 193) | type ActionHandler interface
function newActionNotification (line 197) | func newActionNotification(
type defaultActionHandler (line 250) | type defaultActionHandler struct
method Handle (line 252) | func (h *defaultActionHandler) Handle(event *notifier.FsEvent) (int, e...
method handleHTTP (line 272) | func (h *defaultActionHandler) handleHTTP(event *notifier.FsEvent) err...
method handleCommand (line 302) | func (h *defaultActionHandler) handleCommand(event *notifier.FsEvent) ...
function notificationAsEnvVars (line 326) | func notificationAsEnvVars(event *notifier.FsEvent) []string {
FILE: internal/common/actions_test.go
function TestNewActionNotification (line 38) | func TestNewActionNotification(t *testing.T) {
function TestActionHTTP (line 126) | func TestActionHTTP(t *testing.T) {
function TestActionCMD (line 159) | func TestActionCMD(t *testing.T) {
function TestWrongActions (line 194) | func TestWrongActions(t *testing.T) {
function TestPreDeleteAction (line 243) | func TestPreDeleteAction(t *testing.T) {
function TestUnconfiguredHook (line 287) | func TestUnconfiguredHook(t *testing.T) {
type actionHandlerStub (line 321) | type actionHandlerStub struct
method Handle (line 325) | func (h *actionHandlerStub) Handle(_ *notifier.FsEvent) (int, error) {
function TestInitializeActionHandler (line 331) | func TestInitializeActionHandler(t *testing.T) {
FILE: internal/common/clientsmap.go
type clientsMap (line 25) | type clientsMap struct
method add (line 31) | func (c *clientsMap) add(source string) {
method remove (line 40) | func (c *clientsMap) remove(source string) {
method getTotal (line 56) | func (c *clientsMap) getTotal() int32 {
method getTotalFrom (line 60) | func (c *clientsMap) getTotalFrom(source string) int {
FILE: internal/common/clientsmap_test.go
function TestClientsMap (line 23) | func TestClientsMap(t *testing.T) {
FILE: internal/common/common.go
constant logSender (line 53) | logSender = "common"
constant uploadLogSender (line 54) | uploadLogSender = "Upload"
constant downloadLogSender (line 55) | downloadLogSender = "Download"
constant renameLogSender (line 56) | renameLogSender = "Rename"
constant rmdirLogSender (line 57) | rmdirLogSender = "Rmdir"
constant mkdirLogSender (line 58) | mkdirLogSender = "Mkdir"
constant symlinkLogSender (line 59) | symlinkLogSender = "Symlink"
constant removeLogSender (line 60) | removeLogSender = "Remove"
constant chownLogSender (line 61) | chownLogSender = "Chown"
constant chmodLogSender (line 62) | chmodLogSender = "Chmod"
constant chtimesLogSender (line 63) | chtimesLogSender = "Chtimes"
constant copyLogSender (line 64) | copyLogSender = "Copy"
constant truncateLogSender (line 65) | truncateLogSender = "Truncate"
constant operationDownload (line 66) | operationDownload = "download"
constant operationUpload (line 67) | operationUpload = "upload"
constant operationFirstDownload (line 68) | operationFirstDownload = "first-download"
constant operationFirstUpload (line 69) | operationFirstUpload = "first-upload"
constant operationDelete (line 70) | operationDelete = "delete"
constant operationCopy (line 71) | operationCopy = "copy"
constant OperationPreDownload (line 73) | OperationPreDownload = "pre-download"
constant OperationPreUpload (line 75) | OperationPreUpload = "pre-upload"
constant operationPreDelete (line 76) | operationPreDelete = "pre-delete"
constant operationRename (line 77) | operationRename = "rename"
constant operationMkdir (line 78) | operationMkdir = "mkdir"
constant operationRmdir (line 79) | operationRmdir = "rmdir"
constant OperationSSHCmd (line 81) | OperationSSHCmd = "ssh_cmd"
constant chtimesFormat (line 82) | chtimesFormat = "2006-01-02T15:04:05"
constant idleTimeoutCheckInterval (line 83) | idleTimeoutCheckInterval = 3 * time.Minute
constant periodicTimeoutCheckInterval (line 84) | periodicTimeoutCheckInterval = 1 * time.Minute
constant StatAttrUIDGID (line 89) | StatAttrUIDGID = 1
constant StatAttrPerms (line 90) | StatAttrPerms = 2
constant StatAttrTimes (line 91) | StatAttrTimes = 4
constant StatAttrSize (line 92) | StatAttrSize = 8
constant TransferUpload (line 97) | TransferUpload = iota
constant TransferDownload (line 98) | TransferDownload
constant ProtocolSFTP (line 103) | ProtocolSFTP = "SFTP"
constant ProtocolSCP (line 104) | ProtocolSCP = "SCP"
constant ProtocolSSH (line 105) | ProtocolSSH = "SSH"
constant ProtocolFTP (line 106) | ProtocolFTP = "FTP"
constant ProtocolWebDAV (line 107) | ProtocolWebDAV = "DAV"
constant ProtocolHTTP (line 108) | ProtocolHTTP = "HTTP"
constant ProtocolHTTPShare (line 109) | ProtocolHTTPShare = "HTTPShare"
constant ProtocolDataRetention (line 110) | ProtocolDataRetention = "DataRetention"
constant ProtocolOIDC (line 111) | ProtocolOIDC = "OIDC"
constant protocolEventAction (line 112) | protocolEventAction = "EventAction"
constant UploadModeStandard (line 117) | UploadModeStandard = 0
constant UploadModeAtomic (line 118) | UploadModeAtomic = 1
constant UploadModeAtomicWithResume (line 119) | UploadModeAtomicWithResume = 2
constant UploadModeS3StoreOnError (line 120) | UploadModeS3StoreOnError = 4
constant UploadModeGCSStoreOnError (line 121) | UploadModeGCSStoreOnError = 8
constant UploadModeAzureBlobStoreOnError (line 122) | UploadModeAzureBlobStoreOnError = 16
function init (line 125) | func init() {
function SetUpdateBrandingFn (line 175) | func SetUpdateBrandingFn(fn func(*dataprovider.BrandingConfigs)) {
function Initialize (line 180) | func Initialize(c Configuration, isShared int) error {
function CheckClosing (line 260) | func CheckClosing() error {
function WaitForTransfers (line 270) | func WaitForTransfers(graceTime int) {
function getActiveConnections (line 305) | func getActiveConnections() int {
function LimitRate (line 324) | func LimitRate(protocol, ip string) (time.Duration, error) {
function Reload (line 341) | func Reload() error {
function DelayLogin (line 347) | func DelayLogin(err error) {
function IsBanned (line 354) | func IsBanned(ip, protocol string) bool {
function GetDefenderBanTime (line 367) | func GetDefenderBanTime(ip string) (*time.Time, error) {
function GetDefenderHosts (line 376) | func GetDefenderHosts() ([]dataprovider.DefenderEntry, error) {
function GetDefenderHost (line 385) | func GetDefenderHost(ip string) (dataprovider.DefenderEntry, error) {
function DeleteDefenderHost (line 394) | func DeleteDefenderHost(ip string) bool {
function GetDefenderScore (line 403) | func GetDefenderScore(ip string) (int, error) {
function AddDefenderEvent (line 413) | func AddDefenderEvent(ip, protocol string, event HostEvent) bool {
function reloadProviderConfigs (line 421) | func reloadProviderConfigs() {
function startPeriodicChecks (line 438) | func startPeriodicChecks(duration time.Duration, isShared int) {
type ActiveTransfer (line 459) | type ActiveTransfer interface
type ActiveConnection (line 477) | type ActiveConnection interface
type StatAttributes (line 499) | type StatAttributes struct
type ConnectionTransfer (line 510) | type ConnectionTransfer struct
type EventManagerConfig (line 522) | type EventManagerConfig struct
method validate (line 529) | func (c *EventManagerConfig) validate() error {
type MetadataConfig (line 539) | type MetadataConfig struct
type Configuration (line 546) | type Configuration struct
method IsAtomicUploadEnabled (line 657) | func (c *Configuration) IsAtomicUploadEnabled() bool {
method initializeProxyProtocol (line 661) | func (c *Configuration) initializeProxyProtocol() error {
method GetProxyListener (line 679) | func (c *Configuration) GetProxyListener(listener net.Listener) (net.L...
method GetRateLimitersStatus (line 696) | func (c *Configuration) GetRateLimitersStatus() (bool, []string) {
method IsAllowListEnabled (line 709) | func (c *Configuration) IsAllowListEnabled() bool {
method ExecuteStartupHook (line 714) | func (c *Configuration) ExecuteStartupHook() error {
method executePostDisconnectHook (line 752) | func (c *Configuration) executePostDisconnectHook(remoteAddr, protocol...
method checkPostDisconnectHook (line 802) | func (c *Configuration) checkPostDisconnectHook(remoteAddr, protocol, ...
method ExecutePostConnectHook (line 813) | func (c *Configuration) ExecutePostConnectHook(ipAddr, protocol string...
function getProxyPolicy (line 863) | func getProxyPolicy(allowed, skipped []func(net.IP) bool, def proxyproto...
type SSHConnection (line 899) | type SSHConnection struct
method GetID (line 916) | func (c *SSHConnection) GetID() string {
method UpdateLastActivity (line 921) | func (c *SSHConnection) UpdateLastActivity() {
method GetLastActivity (line 926) | func (c *SSHConnection) GetLastActivity() time.Time {
method Close (line 931) | func (c *SSHConnection) Close() error {
function NewSSHConnection (line 906) | func NewSSHConnection(id string, conn io.Closer) *SSHConnection {
type ActiveConnections (line 936) | type ActiveConnections struct
method addUserConnection (line 952) | func (conns *ActiveConnections) addUserConnection(username string) {
method removeUserConnection (line 960) | func (conns *ActiveConnections) removeUserConnection(username string) {
method GetActiveSessions (line 975) | func (conns *ActiveConnections) GetActiveSessions(username string) int {
method Add (line 983) | func (conns *ActiveConnections) Add(c ActiveConnection) error {
method Swap (line 1010) | func (conns *ActiveConnections) Swap(c ActiveConnection) error {
method Remove (line 1037) | func (conns *ActiveConnections) Remove(connectionID string) {
method Close (line 1077) | func (conns *ActiveConnections) Close(connectionID, role string) bool {
method AddSSHConnection (line 1099) | func (conns *ActiveConnections) AddSSHConnection(c *SSHConnection) {
method RemoveSSHConnection (line 1109) | func (conns *ActiveConnections) RemoveSSHConnection(connectionID strin...
method checkIdles (line 1128) | func (conns *ActiveConnections) checkIdles() {
method checkTransfers (line 1177) | func (conns *ActiveConnections) checkTransfers() {
method AddClientConnection (line 1241) | func (conns *ActiveConnections) AddClientConnection(ipAddr string) {
method RemoveClientConnection (line 1246) | func (conns *ActiveConnections) RemoveClientConnection(ipAddr string) {
method GetClientConnections (line 1251) | func (conns *ActiveConnections) GetClientConnections() int32 {
method GetTotalTransfers (line 1256) | func (conns *ActiveConnections) GetTotalTransfers() int32 {
method IsNewTransferAllowed (line 1262) | func (conns *ActiveConnections) IsNewTransferAllowed(username string) ...
method IsNewConnectionAllowed (line 1287) | func (conns *ActiveConnections) IsNewConnectionAllowed(ipAddr, protoco...
method GetStats (line 1343) | func (conns *ActiveConnections) GetStats(role string) []ConnectionStat...
type ConnectionStatus (line 1371) | type ConnectionStatus struct
type ActiveQuotaScan (line 1397) | type ActiveQuotaScan struct
type ActiveVirtualFolderQuotaScan (line 1406) | type ActiveVirtualFolderQuotaScan struct
type ActiveScans (line 1414) | type ActiveScans struct
method GetUsersQuotaScans (line 1421) | func (s *ActiveScans) GetUsersQuotaScans(role string) []ActiveQuotaScan {
method AddUserQuotaScan (line 1440) | func (s *ActiveScans) AddUserQuotaScan(username, role string) bool {
method RemoveUserQuotaScan (line 1459) | func (s *ActiveScans) RemoveUserQuotaScan(username string) bool {
method GetVFoldersQuotaScans (line 1476) | func (s *ActiveScans) GetVFoldersQuotaScans() []ActiveVirtualFolderQuo...
method AddVFolderQuotaScan (line 1486) | func (s *ActiveScans) AddVFolderQuotaScan(folderName string) bool {
method RemoveVFolderQuotaScan (line 1504) | func (s *ActiveScans) RemoveVFolderQuotaScan(folderName string) bool {
FILE: internal/common/common_test.go
constant logSenderTest (line 47) | logSenderTest = "common_test"
constant httpAddr (line 48) | httpAddr = "127.0.0.1:9999"
constant osWindows (line 49) | osWindows = "windows"
constant userTestUsername (line 50) | userTestUsername = "common_test_username"
type fakeConnection (line 57) | type fakeConnection struct
method AddUser (line 62) | func (c *fakeConnection) AddUser(user dataprovider.User) error {
method Disconnect (line 71) | func (c *fakeConnection) Disconnect() error {
method GetClientVersion (line 76) | func (c *fakeConnection) GetClientVersion() string {
method GetCommand (line 80) | func (c *fakeConnection) GetCommand() string {
method GetLocalAddress (line 84) | func (c *fakeConnection) GetLocalAddress() string {
method GetRemoteAddress (line 88) | func (c *fakeConnection) GetRemoteAddress() string {
type customNetConn (line 92) | type customNetConn struct
method Close (line 98) | func (c *customNetConn) Close() error {
function TestConnections (line 104) | func TestConnections(t *testing.T) {
function TestEventManagerCommandsInitialization (line 220) | func TestEventManagerCommandsInitialization(t *testing.T) {
function TestInitializationProxyErrors (line 247) | func TestInitializationProxyErrors(t *testing.T) {
function TestInitializationClosedProvider (line 277) | func TestInitializationClosedProvider(t *testing.T) {
function TestSSHConnections (line 337) | func TestSSHConnections(t *testing.T) {
function TestDefenderIntegration (line 397) | func TestDefenderIntegration(t *testing.T) {
function TestRateLimitersIntegration (line 524) | func TestRateLimitersIntegration(t *testing.T) {
function TestUserMaxSessions (line 625) | func TestUserMaxSessions(t *testing.T) {
function TestMaxConnections (line 648) | func TestMaxConnections(t *testing.T) {
function TestConnectionRoles (line 698) | func TestConnectionRoles(t *testing.T) {
function TestMaxConnectionPerHost (line 725) | func TestMaxConnectionPerHost(t *testing.T) {
function TestIdleConnections (line 783) | func TestIdleConnections(t *testing.T) {
function TestCloseConnection (line 886) | func TestCloseConnection(t *testing.T) {
function TestSwapConnection (line 903) | func TestSwapConnection(t *testing.T) {
function TestAtomicUpload (line 947) | func TestAtomicUpload(t *testing.T) {
function TestConnectionStatus (line 960) | func TestConnectionStatus(t *testing.T) {
function TestQuotaScans (line 1033) | func TestQuotaScans(t *testing.T) {
function TestQuotaScansRole (line 1062) | func TestQuotaScansRole(t *testing.T) {
function TestProxyPolicy (line 1080) | func TestProxyPolicy(t *testing.T) {
function TestProxyProtocolVersion (line 1149) | func TestProxyProtocolVersion(t *testing.T) {
function TestStartupHook (line 1172) | func TestStartupHook(t *testing.T) {
function TestPostDisconnectHook (line 1199) | func TestPostDisconnectHook(t *testing.T) {
function TestPostConnectHook (line 1230) | func TestPostConnectHook(t *testing.T) {
function TestCryptoConvertFileInfo (line 1268) | func TestCryptoConvertFileInfo(t *testing.T) {
function TestFolderCopy (line 1285) | func TestFolderCopy(t *testing.T) {
function TestCachedFs (line 1326) | func TestCachedFs(t *testing.T) {
function TestParseAllowedIPAndRanges (line 1361) | func TestParseAllowedIPAndRanges(t *testing.T) {
function TestHideConfidentialData (line 1374) | func TestHideConfidentialData(_ *testing.T) {
function TestUserPerms (line 1396) | func TestUserPerms(t *testing.T) {
function TestGetTLSVersion (line 1413) | func TestGetTLSVersion(t *testing.T) {
function TestCleanPath (line 1424) | func TestCleanPath(t *testing.T) {
function TestUserRecentActivity (line 1443) | func TestUserRecentActivity(t *testing.T) {
function TestVfsSameResource (line 1458) | func TestVfsSameResource(t *testing.T) {
function TestUpdateTransferTimestamps (line 1587) | func TestUpdateTransferTimestamps(t *testing.T) {
function TestIPList (line 1626) | func TestIPList(t *testing.T) {
function TestSQLPlaceholderLimits (line 1738) | func TestSQLPlaceholderLimits(t *testing.T) {
function TestALPNProtocols (line 1844) | func TestALPNProtocols(t *testing.T) {
function TestServerVersion (line 1855) | func TestServerVersion(t *testing.T) {
function BenchmarkBcryptHashing (line 1870) | func BenchmarkBcryptHashing(b *testing.B) {
function BenchmarkCompareBcryptPassword (line 1880) | func BenchmarkCompareBcryptPassword(b *testing.B) {
function BenchmarkArgon2Hashing (line 1890) | func BenchmarkArgon2Hashing(b *testing.B) {
function BenchmarkCompareArgon2Password (line 1900) | func BenchmarkCompareArgon2Password(b *testing.B) {
function BenchmarkAddRemoveConnections (line 1910) | func BenchmarkAddRemoveConnections(b *testing.B) {
function BenchmarkAddRemoveSSHConnections (line 1940) | func BenchmarkAddRemoveSSHConnections(b *testing.B) {
FILE: internal/common/connection.go
type BaseConnection (line 41) | type BaseConnection struct
method Log (line 85) | func (c *BaseConnection) Log(level logger.LogLevel, format string, v ....
method GetTransferID (line 90) | func (c *BaseConnection) GetTransferID() int64 {
method GetID (line 95) | func (c *BaseConnection) GetID() string {
method GetUsername (line 100) | func (c *BaseConnection) GetUsername() string {
method GetRole (line 105) | func (c *BaseConnection) GetRole() string {
method GetMaxSessions (line 110) | func (c *BaseConnection) GetMaxSessions() int {
method isAccessAllowed (line 115) | func (c *BaseConnection) isAccessAllowed() bool {
method GetProtocol (line 123) | func (c *BaseConnection) GetProtocol() string {
method GetRemoteIP (line 128) | func (c *BaseConnection) GetRemoteIP() string {
method SetProtocol (line 133) | func (c *BaseConnection) SetProtocol(protocol string) {
method GetConnectionTime (line 141) | func (c *BaseConnection) GetConnectionTime() time.Time {
method UpdateLastActivity (line 146) | func (c *BaseConnection) UpdateLastActivity() {
method GetLastActivity (line 151) | func (c *BaseConnection) GetLastActivity() time.Time {
method CloseFS (line 156) | func (c *BaseConnection) CloseFS() error {
method AddTransfer (line 161) | func (c *BaseConnection) AddTransfer(t ActiveTransfer) {
method RemoveTransfer (line 194) | func (c *BaseConnection) RemoveTransfer(t ActiveTransfer) {
method SignalTransferClose (line 219) | func (c *BaseConnection) SignalTransferClose(transferID int64, err err...
method GetTransfers (line 232) | func (c *BaseConnection) GetTransfers() []ConnectionTransfer {
method SignalTransfersAbort (line 261) | func (c *BaseConnection) SignalTransfersAbort() error {
method getRealFsPath (line 275) | func (c *BaseConnection) getRealFsPath(fsPath string) string {
method setTimes (line 287) | func (c *BaseConnection) setTimes(fsPath string, atime time.Time, mtim...
method getInfoForOngoingUpload (line 301) | func (c *BaseConnection) getInfoForOngoingUpload(fsPath string) (os.Fi...
method truncateOpenHandle (line 313) | func (c *BaseConnection) truncateOpenHandle(fsPath string, size int64)...
method ListDir (line 328) | func (c *BaseConnection) ListDir(virtualPath string) (*DirListerAt, er...
method CheckParentDirs (line 351) | func (c *BaseConnection) CheckParentDirs(virtualPath string) error {
method GetCreateChecks (line 380) | func (c *BaseConnection) GetCreateChecks(virtualPath string, isNewFile...
method CreateDir (line 396) | func (c *BaseConnection) CreateDir(virtualPath string, checkFilePatter...
method IsRemoveFileAllowed (line 428) | func (c *BaseConnection) IsRemoveFileAllowed(virtualPath string) error {
method RemoveFile (line 440) | func (c *BaseConnection) RemoveFile(fs vfs.Fs, fsPath, virtualPath str...
method IsRemoveDirAllowed (line 480) | func (c *BaseConnection) IsRemoveDirAllowed(fs vfs.Fs, fsPath, virtual...
method RemoveDir (line 509) | func (c *BaseConnection) RemoveDir(virtualPath string) error {
method doRecursiveRemoveDirEntry (line 545) | func (c *BaseConnection) doRecursiveRemoveDirEntry(virtualPath string,...
method doRecursiveRemove (line 553) | func (c *BaseConnection) doRecursiveRemove(fs vfs.Fs, fsPath, virtualP...
method RemoveAll (line 589) | func (c *BaseConnection) RemoveAll(virtualPath string) error {
method checkCopy (line 609) | func (c *BaseConnection) checkCopy(srcInfo, dstInfo os.FileInfo, virtu...
method copyFile (line 642) | func (c *BaseConnection) copyFile(virtualSourcePath, virtualTargetPath...
method doRecursiveCopy (line 689) | func (c *BaseConnection) doRecursiveCopy(virtualSourcePath, virtualTar...
method recursiveCopyEntries (line 731) | func (c *BaseConnection) recursiveCopyEntries(virtualSourcePath, virtu...
method Copy (line 760) | func (c *BaseConnection) Copy(virtualSourcePath, virtualTargetPath str...
method Rename (line 804) | func (c *BaseConnection) Rename(virtualSourcePath, virtualTargetPath s...
method renameInternal (line 808) | func (c *BaseConnection) renameInternal(virtualSourcePath, virtualTarg...
method CreateSymlink (line 884) | func (c *BaseConnection) CreateSymlink(virtualSourcePath, virtualTarge...
method doStatInternal (line 939) | func (c *BaseConnection) doStatInternal(virtualPath string, mode int, ...
method DoStat (line 993) | func (c *BaseConnection) DoStat(virtualPath string, mode int, checkFil...
method createDirIfMissing (line 997) | func (c *BaseConnection) createDirIfMissing(name string) error {
method ignoreSetStat (line 1005) | func (c *BaseConnection) ignoreSetStat(fs vfs.Fs) bool {
method handleChmod (line 1015) | func (c *BaseConnection) handleChmod(fs vfs.Fs, fsPath, pathForPerms s...
method handleChown (line 1033) | func (c *BaseConnection) handleChown(fs vfs.Fs, fsPath, pathForPerms s...
method handleChtimes (line 1052) | func (c *BaseConnection) handleChtimes(fs vfs.Fs, fsPath, pathForPerms...
method SetStat (line 1079) | func (c *BaseConnection) SetStat(virtualPath string, attributes *StatA...
method truncateFile (line 1124) | func (c *BaseConnection) truncateFile(fs vfs.Fs, fsPath, virtualPath s...
method checkRecursiveRenameDirPermissions (line 1152) | func (c *BaseConnection) checkRecursiveRenameDirPermissions(fsSrc, fsD...
method hasRenamePerms (line 1190) | func (c *BaseConnection) hasRenamePerms(virtualSourcePath, virtualTarg...
method checkFolderRename (line 1216) | func (c *BaseConnection) checkFolderRename(fsSrc, fsDst vfs.Fs, fsSour...
method isRenamePermitted (line 1248) | func (c *BaseConnection) isRenamePermitted(fsSrc, fsDst vfs.Fs, fsSour...
method hasSpaceForRename (line 1282) | func (c *BaseConnection) hasSpaceForRename(fs vfs.Fs, virtualSourcePat...
method hasSpaceForCrossRename (line 1317) | func (c *BaseConnection) hasSpaceForCrossRename(fs vfs.Fs, quotaResult...
method GetMaxWriteSize (line 1372) | func (c *BaseConnection) GetMaxWriteSize(quotaResult vfs.QuotaCheckRes...
method GetTransferQuota (line 1403) | func (c *BaseConnection) GetTransferQuota() dataprovider.TransferQuota {
method checkUserQuota (line 1408) | func (c *BaseConnection) checkUserQuota() (dataprovider.TransferQuota,...
method HasSpace (line 1441) | func (c *BaseConnection) HasSpace(checkFiles, getUsage bool, requestPa...
method IsSameResource (line 1500) | func (c *BaseConnection) IsSameResource(virtualSourcePath, virtualTarg...
method isCrossFoldersRequest (line 1519) | func (c *BaseConnection) isCrossFoldersRequest(virtualSourcePath, virt...
method updateQuotaMoveBetweenVFolders (line 1531) | func (c *BaseConnection) updateQuotaMoveBetweenVFolders(sourceFolder, ...
method updateQuotaMoveFromVFolder (line 1550) | func (c *BaseConnection) updateQuotaMoveFromVFolder(sourceFolder *vfs....
method updateQuotaMoveToVFolder (line 1561) | func (c *BaseConnection) updateQuotaMoveToVFolder(dstFolder *vfs.Virtu...
method updateQuotaAfterRename (line 1572) | func (c *BaseConnection) updateQuotaAfterRename(fs vfs.Fs, virtualSour...
method IsNotExistError (line 1630) | func (c *BaseConnection) IsNotExistError(err error) bool {
method GetErrorForDeniedFile (line 1642) | func (c *BaseConnection) GetErrorForDeniedFile(policy int) error {
method GetPermissionDeniedError (line 1652) | func (c *BaseConnection) GetPermissionDeniedError() error {
method GetNotExistError (line 1657) | func (c *BaseConnection) GetNotExistError() error {
method GetOpUnsupportedError (line 1669) | func (c *BaseConnection) GetOpUnsupportedError() error {
method GetQuotaExceededError (line 1699) | func (c *BaseConnection) GetQuotaExceededError() error {
method GetReadQuotaExceededError (line 1704) | func (c *BaseConnection) GetReadQuotaExceededError() error {
method IsQuotaExceededError (line 1709) | func (c *BaseConnection) IsQuotaExceededError(err error) bool {
method GetGenericError (line 1733) | func (c *BaseConnection) GetGenericError(err error) error {
method GetFsError (line 1761) | func (c *BaseConnection) GetFsError(fs vfs.Fs, err error) error {
method getNotificationStatus (line 1774) | func (c *BaseConnection) getNotificationStatus(err error) int {
method GetFsAndResolvedPath (line 1785) | func (c *BaseConnection) GetFsAndResolvedPath(virtualPath string) (vfs...
function NewBaseConnection (line 64) | func NewBaseConnection(id, protocol, localAddr, remoteAddr string, user ...
function getQuotaExceededError (line 1678) | func getQuotaExceededError(protocol string) error {
function getReadQuotaExceededError (line 1689) | func getReadQuotaExceededError(protocol string) error {
function isSFTPGoError (line 1726) | func isSFTPGoError(err error) bool {
type DirListerAt (line 1809) | type DirListerAt struct
method Prepend (line 1819) | func (l *DirListerAt) Prepend(fi os.FileInfo) {
method ListAt (line 1827) | func (l *DirListerAt) ListAt(f []os.FileInfo, _ int64) (int, error) {
method Next (line 1852) | func (l *DirListerAt) Next(limit int) ([]os.FileInfo, error) {
method Close (line 1871) | func (l *DirListerAt) Close() error {
method convertError (line 1878) | func (l *DirListerAt) convertError(err error) error {
function getPermissionDeniedError (line 1885) | func getPermissionDeniedError(protocol string) error {
function keepConnectionAlive (line 1896) | func keepConnectionAlive(c *BaseConnection, interval time.Duration) func...
FILE: internal/common/connection_test.go
type MockOsFs (line 47) | type MockOsFs struct
method Name (line 55) | func (fs *MockOsFs) Name() string {
method HasVirtualFolders (line 63) | func (fs *MockOsFs) HasVirtualFolders() bool {
method IsUploadResumeSupported (line 67) | func (fs *MockOsFs) IsUploadResumeSupported() bool {
method Chtimes (line 71) | func (fs *MockOsFs) Chtimes(_ string, _, _ time.Time, _ bool) error {
method Lstat (line 75) | func (fs *MockOsFs) Lstat(name string) (os.FileInfo, error) {
method Walk (line 83) | func (fs *MockOsFs) Walk(_ string, walkFn filepath.WalkFunc) error {
function newMockOsFs (line 92) | func newMockOsFs(hasVirtualFolders bool, connectionID, rootDir, name str...
function TestRemoveErrors (line 101) | func TestRemoveErrors(t *testing.T) {
function TestSetStatMode (line 133) | func TestSetStatMode(t *testing.T) {
function TestRecursiveRenameWalkError (line 166) | func TestRecursiveRenameWalkError(t *testing.T) {
function TestCrossRenameFsErrors (line 200) | func TestCrossRenameFsErrors(t *testing.T) {
function TestRenameVirtualFolders (line 221) | func TestRenameVirtualFolders(t *testing.T) {
function TestRenamePerms (line 237) | func TestRenamePerms(t *testing.T) {
function TestRenameNestedFolders (line 285) | func TestRenameNestedFolders(t *testing.T) {
function TestUpdateQuotaAfterRename (line 305) | func TestUpdateQuotaAfterRename(t *testing.T) {
function TestErrorsMapping (line 382) | func TestErrorsMapping(t *testing.T) {
function TestMaxWriteSize (line 457) | func TestMaxWriteSize(t *testing.T) {
function TestCheckParentDirsErrors (line 509) | func TestCheckParentDirsErrors(t *testing.T) {
function TestErrorResolvePath (line 582) | func TestErrorResolvePath(t *testing.T) {
function TestConnectionKeepAlive (line 627) | func TestConnectionKeepAlive(t *testing.T) {
function TestFsFileCopier (line 638) | func TestFsFileCopier(t *testing.T) {
function TestFilePatterns (line 656) | func TestFilePatterns(t *testing.T) {
function TestStatForOngoingTransfers (line 1049) | func TestStatForOngoingTransfers(t *testing.T) {
function TestListerAt (line 1080) | func TestListerAt(t *testing.T) {
function TestGetFsAndResolvedPath (line 1193) | func TestGetFsAndResolvedPath(t *testing.T) {
function TestOsFsGetRelativePath (line 1401) | func TestOsFsGetRelativePath(t *testing.T) {
FILE: internal/common/dataretention.go
type ActiveRetentionChecks (line 38) | type ActiveRetentionChecks struct
method Get (line 44) | func (c *ActiveRetentionChecks) Get(role string) []RetentionCheck {
method Add (line 65) | func (c *ActiveRetentionChecks) Add(check RetentionCheck, user *datapr...
method remove (line 91) | func (c *ActiveRetentionChecks) remove(username string) bool {
type folderRetentionCheckResult (line 107) | type folderRetentionCheckResult struct
type RetentionCheck (line 118) | type RetentionCheck struct
method updateUserPermissions (line 131) | func (c *RetentionCheck) updateUserPermissions() {
method getFolderRetention (line 137) | func (c *RetentionCheck) getFolderRetention(folderPath string) (datapr...
method removeFile (line 150) | func (c *RetentionCheck) removeFile(virtualPath string, info os.FileIn...
method cleanupFolder (line 158) | func (c *RetentionCheck) cleanupFolder(folderPath string, recursion in...
method checkEmptyDirRemoval (line 254) | func (c *RetentionCheck) checkEmptyDirRemoval(folderPath string, check...
method Start (line 281) | func (c *RetentionCheck) Start() error {
FILE: internal/common/dataretention_test.go
function TestRetentionPermissionsAndGetFolder (line 29) | func TestRetentionPermissionsAndGetFolder(t *testing.T) {
function TestRetentionCheckAddRemove (line 87) | func TestRetentionCheckAddRemove(t *testing.T) {
function TestRetentionCheckRole (line 119) | func TestRetentionCheckRole(t *testing.T) {
function TestCleanupErrors (line 153) | func TestCleanupErrors(t *testing.T) {
FILE: internal/common/defender.go
type HostEvent (line 26) | type HostEvent
constant HostEventLoginFailed (line 30) | HostEventLoginFailed HostEvent = "LoginFailed"
constant HostEventUserNotFound (line 31) | HostEventUserNotFound HostEvent = "UserNotFound"
constant HostEventNoLoginTried (line 32) | HostEventNoLoginTried HostEvent = "NoLoginTried"
constant HostEventLimitExceeded (line 33) | HostEventLimitExceeded HostEvent = "LimitExceeded"
constant DefenderDriverMemory (line 38) | DefenderDriverMemory = "memory"
constant DefenderDriverProvider (line 39) | DefenderDriverProvider = "provider"
type Defender (line 47) | type Defender interface
type DefenderConfig (line 60) | type DefenderConfig struct
method checkScores (line 200) | func (c *DefenderConfig) checkScores() error {
method validate (line 220) | func (c *DefenderConfig) validate() error {
type LoginDelay (line 99) | type LoginDelay struct
type baseDefender (line 106) | type baseDefender struct
method isBanned (line 111) | func (d *baseDefender) isBanned(ip, protocol string) bool {
method IsSafe (line 123) | func (d *baseDefender) IsSafe(ip, protocol string) bool {
method getScore (line 131) | func (d *baseDefender) getScore(event HostEvent) int {
method logEvent (line 148) | func (d *baseDefender) logEvent(ip, protocol string, event HostEvent, ...
method logBan (line 167) | func (d *baseDefender) logBan(ip, protocol string) {
method DelayLogin (line 178) | func (d *baseDefender) DelayLogin(err error) {
type hostEvent (line 190) | type hostEvent struct
type hostScore (line 195) | type hostScore struct
FILE: internal/common/defender_test.go
function TestBasicDefender (line 31) | func TestBasicDefender(t *testing.T) {
function TestExpiredHostBans (line 274) | func TestExpiredHostBans(t *testing.T) {
function TestDefenderCleanup (line 350) | func TestDefenderCleanup(t *testing.T) {
function TestDefenderDelay (line 438) | func TestDefenderDelay(t *testing.T) {
function TestDefenderConfig (line 463) | func TestDefenderConfig(t *testing.T) {
function BenchmarkDefenderBannedSearch (line 535) | func BenchmarkDefenderBannedSearch(b *testing.B) {
function BenchmarkCleanup (line 554) | func BenchmarkCleanup(b *testing.B) {
function BenchmarkCIDRanger (line 577) | func BenchmarkCIDRanger(b *testing.B) {
function BenchmarkNetContains (line 597) | func BenchmarkNetContains(b *testing.B) {
function getDefenderForBench (line 615) | func getDefenderForBench() *memoryDefender {
function inc (line 636) | func inc(ip net.IP) {
FILE: internal/common/defenderdb.go
type dbDefender (line 26) | type dbDefender struct
method GetHosts (line 52) | func (d *dbDefender) GetHosts() ([]dataprovider.DefenderEntry, error) {
method GetHost (line 57) | func (d *dbDefender) GetHost(ip string) (dataprovider.DefenderEntry, e...
method IsBanned (line 64) | func (d *dbDefender) IsBanned(ip, protocol string) bool {
method DeleteHost (line 83) | func (d *dbDefender) DeleteHost(ip string) bool {
method AddEvent (line 93) | func (d *dbDefender) AddEvent(ip, protocol string, event HostEvent) bo...
method GetBanTime (line 126) | func (d *dbDefender) GetBanTime(ip string) (*time.Time, error) {
method GetScore (line 138) | func (d *dbDefender) GetScore(ip string) (int, error) {
method cleanup (line 146) | func (d *dbDefender) cleanup() {
method getStartObservationTime (line 162) | func (d *dbDefender) getStartObservationTime() int64 {
method getLastCleanup (line 167) | func (d *dbDefender) getLastCleanup() time.Time {
method setLastCleanup (line 175) | func (d *dbDefender) setLastCleanup(when time.Time) {
function newDBDefender (line 31) | func newDBDefender(config *DefenderConfig) (Defender, error) {
FILE: internal/common/defenderdb_test.go
function TestBasicDbDefender (line 28) | func TestBasicDbDefender(t *testing.T) {
function TestDbDefenderCleanup (line 263) | func TestDbDefenderCleanup(t *testing.T) {
function isDbDefenderSupported (line 310) | func isDbDefenderSupported() bool {
FILE: internal/common/defendermem.go
type memoryDefender (line 26) | type memoryDefender struct
method GetHosts (line 59) | func (d *memoryDefender) GetHosts() ([]dataprovider.DefenderEntry, err...
method GetHost (line 91) | func (d *memoryDefender) GetHost(ip string) (dataprovider.DefenderEntr...
method IsBanned (line 125) | func (d *memoryDefender) IsBanned(ip, protocol string) bool {
method DeleteHost (line 155) | func (d *memoryDefender) DeleteHost(ip string) bool {
method AddEvent (line 175) | func (d *memoryDefender) AddEvent(ip, protocol string, event HostEvent...
method countBanned (line 238) | func (d *memoryDefender) countBanned() int {
method countHosts (line 245) | func (d *memoryDefender) countHosts() int {
method GetBanTime (line 253) | func (d *memoryDefender) GetBanTime(ip string) (*time.Time, error) {
method GetScore (line 265) | func (d *memoryDefender) GetScore(ip string) (int, error) {
method cleanupBanned (line 282) | func (d *memoryDefender) cleanupBanned() {
method cleanupHosts (line 316) | func (d *memoryDefender) cleanupHosts() {
function newInMemoryDefender (line 37) | func newInMemoryDefender(config *DefenderConfig) (Defender, error) {
type kv (line 345) | type kv struct
type kvList (line 350) | type kvList
method Len (line 352) | func (p kvList) Len() int { return len(p) }
method Less (line 353) | func (p kvList) Less(i, j int) bool { return p[i].Value < p[j].Value }
method Swap (line 354) | func (p kvList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
FILE: internal/common/eventmanager.go
constant ipBlockedEventName (line 58) | ipBlockedEventName = "IP Blocked"
constant maxAttachmentsSize (line 59) | maxAttachmentsSize = int64(10 * 1024 * 1024)
constant objDataPlaceholder (line 60) | objDataPlaceholder = "{{.ObjectData}}"
constant objDataPlaceholderString (line 61) | objDataPlaceholderString = "{{.ObjectDataString}}"
constant dateTimeMillisFormat (line 62) | dateTimeMillisFormat = "2006-01-02T15:04:05.000"
constant IDPLoginUser (line 67) | IDPLoginUser = "IDP login user"
constant IDPLoginAdmin (line 68) | IDPLoginAdmin = "IDP login admin"
function init (line 80) | func init() {
function HandleCertificateEvent (line 111) | func HandleCertificateEvent(params EventParams) {
function HandleIDPLoginEvent (line 116) | func HandleIDPLoginEvent(params EventParams, customFields *map[string]an...
type eventRulesContainer (line 121) | type eventRulesContainer struct
method addAsyncTask (line 134) | func (r *eventRulesContainer) addAsyncTask() {
method removeAsyncTask (line 139) | func (r *eventRulesContainer) removeAsyncTask() {
method getLastLoadTime (line 144) | func (r *eventRulesContainer) getLastLoadTime() int64 {
method setLastLoadTime (line 148) | func (r *eventRulesContainer) setLastLoadTime(modTime int64) {
method RemoveRule (line 153) | func (r *eventRulesContainer) RemoveRule(name string) {
method removeRuleInternal (line 162) | func (r *eventRulesContainer) removeRuleInternal(name string) {
method addUpdateRuleInternal (line 227) | func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider....
method loadRules (line 278) | func (r *eventRulesContainer) loadRules() {
method checkIPDLoginEventMatch (line 303) | func (*eventRulesContainer) checkIPDLoginEventMatch(conditions *datapr...
method checkProviderEventMatch (line 317) | func (*eventRulesContainer) checkProviderEventMatch(conditions *datapr...
method checkFsEventMatch (line 336) | func (*eventRulesContainer) checkFsEventMatch(conditions *dataprovider...
method hasFsRules (line 371) | func (r *eventRulesContainer) hasFsRules() bool {
method handleFsEvent (line 380) | func (r *eventRulesContainer) handleFsEvent(params EventParams) (bool,...
method handleIDPLoginEvent (line 423) | func (r *eventRulesContainer) handleIDPLoginEvent(params EventParams, ...
method handleProviderEvent (line 478) | func (r *eventRulesContainer) handleProviderEvent(params EventParams) {
method handleIPBlockedEvent (line 500) | func (r *eventRulesContainer) handleIPBlockedEvent(params EventParams) {
method handleCertificateEvent (line 522) | func (r *eventRulesContainer) handleCertificateEvent(params EventParam...
type executedRetentionCheck (line 544) | type executedRetentionCheck struct
type EventParams (line 551) | type EventParams struct
method getACopy (line 580) | func (p *EventParams) getACopy() *EventParams {
method addIDPCustomFields (line 613) | func (p *EventParams) addIDPCustomFields(customFields *map[string]any) {
method AddError (line 629) | func (p *EventParams) AddError(err error) {
method addUID (line 639) | func (p *EventParams) addUID() {
method setBackupParams (line 645) | func (p *EventParams) setBackupParams(backupPath string) {
method getStatusString (line 660) | func (p *EventParams) getStatusString() string {
method getUsers (line 670) | func (p *EventParams) getUsers() ([]dataprovider.User, error) {
method getUserFromSender (line 686) | func (p *EventParams) getUserFromSender() (dataprovider.User, error) {
method getFolders (line 707) | func (p *EventParams) getFolders() ([]vfs.BaseVirtualFolder, error) {
method getCompressedDataRetentionReport (line 719) | func (p *EventParams) getCompressedDataRetentionReport() ([]byte, erro...
method writeCompressedDataRetentionReports (line 730) | func (p *EventParams) writeCompressedDataRetentionReports(w io.Writer)...
method getRetentionReportsAsMailAttachment (line 768) | func (p *EventParams) getRetentionReportsAsMailAttachment() (*mail.Fil...
method getStringReplacement (line 779) | func (*EventParams) getStringReplacement(val string, escapeMode int) s...
method getStringReplacements (line 790) | func (p *EventParams) getStringReplacements(addObjectData bool, escape...
function getCSVRetentionReport (line 872) | func getCSVRetentionReport(results []folderRetentionCheckResult) ([]byte...
function closeWriterAndUpdateQuota (line 895) | func closeWriterAndUpdateQuota(w io.WriteCloser, conn *BaseConnection, v...
function updateUserQuotaAfterFileWrite (line 940) | func updateUserQuotaAfterFileWrite(conn *BaseConnection, virtualPath str...
function checkWriterPermsAndQuota (line 949) | func checkWriterPermsAndQuota(conn *BaseConnection, virtualPath string, ...
function getFileWriter (line 975) | func getFileWriter(conn *BaseConnection, virtualPath string, expectedSiz...
function addZipEntry (line 1023) | func addZipEntry(wr *zipWriterWrapper, conn *BaseConnection, entryPath, ...
function addFileToZip (line 1096) | func addFileToZip(wr *zipWriterWrapper, conn *BaseConnection, entryPath,...
function getZipEntryName (line 1118) | func getZipEntryName(entryPath, baseDir string) (string, error) {
function getFileReader (line 1126) | func getFileReader(conn *BaseConnection, virtualPath string) (io.ReadClo...
function writeFileContent (line 1148) | func writeFileContent(conn *BaseConnection, virtualPath string, w io.Wri...
function getFileContentFn (line 1161) | func getFileContentFn(conn *BaseConnection, virtualPath string, size int...
function getMailAttachments (line 1175) | func getMailAttachments(conn *BaseConnection, attachments []string, repl...
function replaceWithReplacer (line 1200) | func replaceWithReplacer(input string, replacer *strings.Replacer) string {
function checkEventConditionPattern (line 1207) | func checkEventConditionPattern(p dataprovider.ConditionPattern, name st...
function checkUserConditionOptions (line 1225) | func checkUserConditionOptions(user *dataprovider.User, conditions *data...
function checkEventConditionPatterns (line 1239) | func checkEventConditionPatterns(name string, patterns []dataprovider.Co...
function checkEventGroupConditionPatterns (line 1259) | func checkEventGroupConditionPatterns(groups []sdk.GroupMapping, pattern...
function getHTTPRuleActionEndpoint (line 1283) | func getHTTPRuleActionEndpoint(c *dataprovider.EventActionHTTPConfig, re...
function writeHTTPPart (line 1311) | func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h te...
function getHTTPRuleActionBody (line 1354) | func getHTTPRuleActionBody(c *dataprovider.EventActionHTTPConfig, replac...
function setHTTPReqHeaders (line 1434) | func setHTTPReqHeaders(req *http.Request, c *dataprovider.EventActionHTT...
function executeHTTPRuleAction (line 1448) | func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params ...
function executeCommandRuleAction (line 1515) | func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, p...
function getEmailAddressesWithReplacer (line 1562) | func getEmailAddressesWithReplacer(addrs []string, replacer *strings.Rep...
function executeEmailRuleAction (line 1576) | func executeEmailRuleAction(c dataprovider.EventActionEmailConfig, param...
function getUserForEventAction (line 1642) | func getUserForEventAction(user *dataprovider.User) error {
function replacePathsPlaceholders (line 1660) | func replacePathsPlaceholders(paths []string, replacer *strings.Replacer...
function executeDeleteFileFsAction (line 1668) | func executeDeleteFileFsAction(conn *BaseConnection, item string, info o...
function executeDeleteFsActionForUser (line 1676) | func executeDeleteFsActionForUser(deletes []string, replacer *strings.Re...
function executeDeleteFsRuleAction (line 1711) | func executeDeleteFsRuleAction(deletes []string, replacer *strings.Repla...
function executeMkDirsFsActionForUser (line 1745) | func executeMkDirsFsActionForUser(dirs []string, replacer *strings.Repla...
function executeMkdirFsRuleAction (line 1770) | func executeMkdirFsRuleAction(dirs []string, replacer *strings.Replacer,
function executeRenameFsActionForUser (line 1803) | func executeRenameFsActionForUser(renames []dataprovider.RenameConfig, r...
function executeCopyFsActionForUser (line 1833) | func executeCopyFsActionForUser(keyVals []dataprovider.KeyValue, replace...
function executeExistFsActionForUser (line 1865) | func executeExistFsActionForUser(exist []string, replacer *strings.Repla...
function executeRenameFsRuleAction (line 1889) | func executeRenameFsRuleAction(renames []dataprovider.RenameConfig, repl...
function executeCopyFsRuleAction (line 1923) | func executeCopyFsRuleAction(keyVals []dataprovider.KeyValue, replacer *...
function getArchiveBaseDir (line 1957) | func getArchiveBaseDir(paths []string) string {
function getSizeForPath (line 1970) | func getSizeForPath(conn *BaseConnection, p string, info os.FileInfo) (i...
function estimateZipSize (line 2002) | func estimateZipSize(conn *BaseConnection, zipPath string, paths []strin...
function executeCompressFsActionForUser (line 2024) | func executeCompressFsActionForUser(c dataprovider.EventActionFsCompress...
function executeExistFsRuleAction (line 2085) | func executeExistFsRuleAction(exist []string, replacer *strings.Replacer...
function executeCompressFsRuleAction (line 2119) | func executeCompressFsRuleAction(c dataprovider.EventActionFsCompress, r...
function executeFsRuleAction (line 2153) | func executeFsRuleAction(c dataprovider.EventActionFilesystemConfig, con...
function executeQuotaResetForUser (line 2177) | func executeQuotaResetForUser(user *dataprovider.User) error {
function executeUsersQuotaResetRuleAction (line 2202) | func executeUsersQuotaResetRuleAction(conditions dataprovider.ConditionO...
function executeFoldersQuotaResetRuleAction (line 2234) | func executeFoldersQuotaResetRuleAction(conditions dataprovider.Conditio...
function executeTransferQuotaResetRuleAction (line 2284) | func executeTransferQuotaResetRuleAction(conditions dataprovider.Conditi...
function executeDataRetentionCheckForUser (line 2318) | func executeDataRetentionCheckForUser(user dataprovider.User, folders []...
function executeDataRetentionCheckRuleAction (line 2348) | func executeDataRetentionCheckRuleAction(config dataprovider.EventAction...
function executeUserExpirationCheckRuleAction (line 2382) | func executeUserExpirationCheckRuleAction(conditions dataprovider.Condit...
function executeInactivityCheckForUser (line 2416) | func executeInactivityCheckForUser(user *dataprovider.User, config datap...
function executeUserInactivityCheckRuleAction (line 2444) | func executeUserInactivityCheckRuleAction(config dataprovider.EventActio...
function executePwdExpirationCheckForUser (line 2475) | func executePwdExpirationCheckForUser(user *dataprovider.User, config da...
function executePwdExpirationCheckRuleAction (line 2519) | func executePwdExpirationCheckRuleAction(config dataprovider.EventAction...
function executeAdminCheckAction (line 2547) | func executeAdminCheckAction(c *dataprovider.EventActionIDPAccountCheck,...
function preserveUserProfile (line 2585) | func preserveUserProfile(user, newUser *dataprovider.User) {
function executeUserCheckAction (line 2617) | func executeUserCheckAction(c *dataprovider.EventActionIDPAccountCheck, ...
function executeRuleAction (line 2651) | func executeRuleAction(action dataprovider.BaseEventAction, params *Even...
function executeIDPAccountCheckRule (line 2703) | func executeIDPAccountCheckRule(rule dataprovider.EventRule, params Even...
function executeSyncRulesActions (line 2740) | func executeSyncRulesActions(rules []dataprovider.EventRule, params Even...
function executeAsyncRulesActions (line 2771) | func executeAsyncRulesActions(rules []dataprovider.EventRule, params Eve...
function executeRuleAsyncActions (line 2781) | func executeRuleAsyncActions(rule dataprovider.EventRule, params *EventP...
type eventCronJob (line 2819) | type eventCronJob struct
method getTask (line 2823) | func (j *eventCronJob) getTask(rule *dataprovider.EventRule) (dataprov...
method getEventParams (line 2849) | func (j *eventCronJob) getEventParams() EventParams {
method Run (line 2859) | func (j *eventCronJob) Run() {
function RunOnDemandRule (line 2917) | func RunOnDemandRule(name string) error {
type zipWriterWrapper (line 2941) | type zipWriterWrapper struct
function eventManagerLog (line 2947) | func eventManagerLog(level logger.LogLevel, format string, v ...any) {
FILE: internal/common/eventmanager_test.go
function TestEventRuleMatch (line 47) | func TestEventRuleMatch(t *testing.T) {
function TestDoubleStarMatching (line 326) | func TestDoubleStarMatching(t *testing.T) {
function TestMutlipleDoubleStarMatching (line 377) | func TestMutlipleDoubleStarMatching(t *testing.T) {
function TestMultipleDoubleStarMatchingInverse (line 398) | func TestMultipleDoubleStarMatchingInverse(t *testing.T) {
function TestGroupConditionPatterns (line 419) | func TestGroupConditionPatterns(t *testing.T) {
function TestEventManager (line 479) | func TestEventManager(t *testing.T) {
function TestEventManagerErrors (line 588) | func TestEventManagerErrors(t *testing.T) {
function TestDateTimePlaceholder (line 803) | func TestDateTimePlaceholder(t *testing.T) {
function TestEventRuleActions (line 829) | func TestEventRuleActions(t *testing.T) {
function TestIDPAccountCheckRule (line 1365) | func TestIDPAccountCheckRule(t *testing.T) {
function TestUserExpirationCheck (line 1483) | func TestUserExpirationCheck(t *testing.T) {
function TestEventRuleActionsNoGroupMatching (line 1520) | func TestEventRuleActionsNoGroupMatching(t *testing.T) {
function TestGetFileContent (line 1580) | func TestGetFileContent(t *testing.T) {
function TestFilesystemActionErrors (line 1655) | func TestFilesystemActionErrors(t *testing.T) {
function TestQuotaActionsWithQuotaTrackDisabled (line 1868) | func TestQuotaActionsWithQuotaTrackDisabled(t *testing.T) {
function TestScheduledActions (line 1952) | func TestScheduledActions(t *testing.T) {
function TestEventParamsCopy (line 2047) | func TestEventParamsCopy(t *testing.T) {
function TestEventParamsStatusFromError (line 2120) | func TestEventParamsStatusFromError(t *testing.T) {
type testWriter (line 2130) | type testWriter struct
method Write (line 2135) | func (w *testWriter) Write(p []byte) (int, error) {
function TestWriteHTTPPartsError (line 2145) | func TestWriteHTTPPartsError(t *testing.T) {
function TestReplacePathsPlaceholders (line 2161) | func TestReplacePathsPlaceholders(t *testing.T) {
function TestEstimateZipSizeErrors (line 2171) | func TestEstimateZipSizeErrors(t *testing.T) {
function TestOnDemandRule (line 2210) | func TestOnDemandRule(t *testing.T) {
function getErrorString (line 2291) | func getErrorString(err error) string {
function TestHTTPEndpointWithPlaceholders (line 2298) | func TestHTTPEndpointWithPlaceholders(t *testing.T) {
function TestMetadataReplacement (line 2328) | func TestMetadataReplacement(t *testing.T) {
function TestUserInactivityCheck (line 2343) | func TestUserInactivityCheck(t *testing.T) {
FILE: internal/common/eventscheduler.go
function stopEventScheduler (line 31) | func stopEventScheduler() {
function startEventScheduler (line 38) | func startEventScheduler() {
FILE: internal/common/httpauth.go
constant HTTPAuthenticationHeader (line 33) | HTTPAuthenticationHeader = "WWW-Authenticate"
constant md5CryptPwdPrefix (line 34) | md5CryptPwdPrefix = "$1$"
constant apr1CryptPwdPrefix (line 35) | apr1CryptPwdPrefix = "$apr1$"
type HTTPAuthProvider (line 43) | type HTTPAuthProvider interface
type basicAuthProvider (line 48) | type basicAuthProvider struct
method IsEnabled (line 65) | func (p *basicAuthProvider) IsEnabled() bool {
method isReloadNeeded (line 69) | func (p *basicAuthProvider) isReloadNeeded(info os.FileInfo) bool {
method loadUsers (line 76) | func (p *basicAuthProvider) loadUsers() error {
method getHashedPassword (line 116) | func (p *basicAuthProvider) getHashedPassword(username string) (string...
method ValidateCredentials (line 129) | func (p *basicAuthProvider) ValidateCredentials(username, password str...
function NewBasicAuthProvider (line 56) | func NewBasicAuthProvider(authUserFile string) (HTTPAuthProvider, error) {
FILE: internal/common/httpauth_test.go
function TestBasicAuth (line 26) | func TestBasicAuth(t *testing.T) {
FILE: internal/common/protocol_test.go
constant httpAddr (line 72) | httpAddr = "127.0.0.1:9999"
constant httpProxyAddr (line 73) | httpProxyAddr = "127.0.0.1:7777"
constant sftpServerAddr (line 74) | sftpServerAddr = "127.0.0.1:4022"
constant smtpServerAddr (line 75) | smtpServerAddr = "127.0.0.1:2525"
constant webDavServerPort (line 76) | webDavServerPort = 9191
constant httpFsPort (line 77) | httpFsPort = 34567
constant defaultUsername (line 78) | defaultUsername = "test_common_sftp"
constant defaultPassword (line 79) | defaultPassword = "test_password"
constant defaultSFTPUsername (line 80) | defaultSFTPUsername = "test_common_sftpfs_user"
constant defaultHTTPFsUsername (line 81) | defaultHTTPFsUsername = "httpfs_ftp_user"
constant httpFsWellKnowDir (line 82) | httpFsWellKnowDir = "/wellknow"
constant osWindows (line 83) | osWindows = "windows"
constant testFileName (line 84) | testFileName = "test_file_common_sftp.dat"
constant testDir (line 85) | testDir = "test_dir_common"
constant testPubKey (line 86) | testPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC03jj0D+d...
constant testPrivateKey (line 87) | testPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
function TestMain (line 137) | func TestMain(m *testing.M) {
function TestBaseConnection (line 296) | func TestBaseConnection(t *testing.T) {
function TestRemoveAll (line 387) | func TestRemoveAll(t *testing.T) {
function TestRelativeSymlinks (line 431) | func TestRelativeSymlinks(t *testing.T) {
function TestCheckFsAfterUpdate (line 496) | func TestCheckFsAfterUpdate(t *testing.T) {
function TestLoginAccessTime (line 536) | func TestLoginAccessTime(t *testing.T) {
function TestSetStat (line 575) | func TestSetStat(t *testing.T) {
function TestCryptFsUserUploadErrorOverwrite (line 635) | func TestCryptFsUserUploadErrorOverwrite(t *testing.T) {
function TestChtimesOpenHandle (line 700) | func TestChtimesOpenHandle(t *testing.T) {
function TestWaitForConnections (line 751) | func TestWaitForConnections(t *testing.T) {
function TestCheckParentDirs (line 837) | func TestCheckParentDirs(t *testing.T) {
function TestPermissionErrors (line 882) | func TestPermissionErrors(t *testing.T) {
function TestHiddenPatternFilter (line 936) | func TestHiddenPatternFilter(t *testing.T) {
function TestHiddenRoot (line 1044) | func TestHiddenRoot(t *testing.T) {
function TestFileNotAllowedErrors (line 1112) | func TestFileNotAllowedErrors(t *testing.T) {
function TestRootDirVirtualFolder (line 1144) | func TestRootDirVirtualFolder(t *testing.T) {
function TestTruncateQuotaLimits (line 1257) | func TestTruncateQuotaLimits(t *testing.T) {
function TestVirtualFoldersQuotaRenameOverwrite (line 1509) | func TestVirtualFoldersQuotaRenameOverwrite(t *testing.T) {
function TestQuotaRenameOverwrite (line 1660) | func TestQuotaRenameOverwrite(t *testing.T) {
function TestVirtualFoldersQuotaValues (line 1717) | func TestVirtualFoldersQuotaValues(t *testing.T) {
function TestQuotaRenameInsideSameVirtualFolder (line 1817) | func TestQuotaRenameInsideSameVirtualFolder(t *testing.T) {
function TestQuotaRenameBetweenVirtualFolder (line 2007) | func TestQuotaRenameBetweenVirtualFolder(t *testing.T) {
function TestQuotaRenameFromVirtualFolder (line 2213) | func TestQuotaRenameFromVirtualFolder(t *testing.T) {
function TestQuotaRenameToVirtualFolder (line 2422) | func TestQuotaRenameToVirtualFolder(t *testing.T) {
function TestTransferQuotaLimits (line 2647) | func TestTransferQuotaLimits(t *testing.T) {
function TestVirtualFoldersLink (line 2725) | func TestVirtualFoldersLink(t *testing.T) {
function TestCrossFolderRename (line 2850) | func TestCrossFolderRename(t *testing.T) {
function TestDirs (line 3095) | func TestDirs(t *testing.T) {
function TestCryptFsStat (line 3177) | func TestCryptFsStat(t *testing.T) {
function TestFsPermissionErrors (line 3202) | func TestFsPermissionErrors(t *testing.T) {
function TestRenameErrorOutsideHomeDir (line 3232) | func TestRenameErrorOutsideHomeDir(t *testing.T) {
function TestResolvePathError (line 3284) | func TestResolvePathError(t *testing.T) {
function TestUserPasswordHashing (line 3338) | func TestUserPasswordHashing(t *testing.T) {
function TestAllowList (line 3402) | func TestAllowList(t *testing.T) {
function TestDbDefenderErrors (line 3483) | func TestDbDefenderErrors(t *testing.T) {
function TestDelayedQuotaUpdater (line 3535) | func TestDelayedQuotaUpdater(t *testing.T) {
function TestPasswordCaching (line 3635) | func TestPasswordCaching(t *testing.T) {
function TestEventRule (line 3752) | func TestEventRule(t *testing.T) {
function TestEventRuleStatues (line 4115) | func TestEventRuleStatues(t *testing.T) {
function TestEventRuleDisabledCommand (line 4216) | func TestEventRuleDisabledCommand(t *testing.T) {
function TestEventRuleProviderEvents (line 4360) | func TestEventRuleProviderEvents(t *testing.T) {
function TestEventRuleFsActions (line 4539) | func TestEventRuleFsActions(t *testing.T) {
function TestEventActionObjectBaseName (line 4824) | func TestEventActionObjectBaseName(t *testing.T) {
function TestUploadEventRule (line 4898) | func TestUploadEventRule(t *testing.T) {
function TestEventRulePreDelete (line 5039) | func TestEventRulePreDelete(t *testing.T) {
function TestEventRulePreDownloadUpload (line 5191) | func TestEventRulePreDownloadUpload(t *testing.T) {
function TestEventActionCommandEnvVars (line 5313) | func TestEventActionCommandEnvVars(t *testing.T) {
function TestFsActionCopy (line 5404) | func TestFsActionCopy(t *testing.T) {
function TestEventFsActionsGroupFilters (line 5504) | func TestEventFsActionsGroupFilters(t *testing.T) {
function TestEventProviderActionGroupFilters (line 5635) | func TestEventProviderActionGroupFilters(t *testing.T) {
function TestBackupAsAttachment (line 5767) | func TestBackupAsAttachment(t *testing.T) {
function TestEventActionHTTPMultipart (line 5851) | func TestEventActionHTTPMultipart(t *testing.T) {
function TestEventActionCompress (line 5940) | func TestEventActionCompress(t *testing.T) {
function TestEventActionCompressQuotaErrors (line 6085) | func TestEventActionCompressQuotaErrors(t *testing.T) {
function TestEventActionCompressQuotaFolder (line 6254) | func TestEventActionCompressQuotaFolder(t *testing.T) {
function TestEventActionCompressErrors (line 6381) | func TestEventActionCompressErrors(t *testing.T) {
function TestEventActionEmailAttachments (line 6480) | func TestEventActionEmailAttachments(t *testing.T) {
function TestEventActionsRetentionReports (line 6598) | func TestEventActionsRetentionReports(t *testing.T) {
function TestEventRuleFirstUploadDownloadActions (line 6845) | func TestEventRuleFirstUploadDownloadActions(t *testing.T) {
function TestEventRuleRenameEvent (line 6976) | func TestEventRuleRenameEvent(t *testing.T) {
function TestEventRuleIDPLogin (line 7062) | func TestEventRuleIDPLogin(t *testing.T) {
function TestEventRuleEmailField (line 7342) | func TestEventRuleEmailField(t *testing.T) {
function TestEventRuleCertificate (line 7490) | func TestEventRuleCertificate(t *testing.T) {
function TestEventRuleIPBlocked (line 7621) | func TestEventRuleIPBlocked(t *testing.T) {
function TestEventRuleRotateLog (line 7746) | func TestEventRuleRotateLog(t *testing.T) {
function TestEventRuleInactivityCheck (line 7843) | func TestEventRuleInactivityCheck(t *testing.T) {
function TestEventRulePasswordExpiration (line 7946) | func TestEventRulePasswordExpiration(t *testing.T) {
function TestSyncUploadAction (line 8152) | func TestSyncUploadAction(t *testing.T) {
function TestQuotaTrackDisabled (line 8234) | func TestQuotaTrackDisabled(t *testing.T) {
function TestGetQuotaError (line 8269) | func TestGetQuotaError(t *testing.T) {
function TestRetentionAPI (line 8323) | func TestRetentionAPI(t *testing.T) {
function TestPerUserTransferLimits (line 8563) | func TestPerUserTransferLimits(t *testing.T) {
function TestMaxSessionsSameConnection (line 8605) | func TestMaxSessionsSameConnection(t *testing.T) {
function TestRenameDir (line 8646) | func TestRenameDir(t *testing.T) {
function TestBuiltinKeyboardInteractiveAuthentication (line 8669) | func TestBuiltinKeyboardInteractiveAuthentication(t *testing.T) {
function TestMultiStepBuiltinKeyboardAuth (line 8732) | func TestMultiStepBuiltinKeyboardAuth(t *testing.T) {
function TestRenameSymlink (line 8795) | func TestRenameSymlink(t *testing.T) {
function TestSplittedDeletePerms (line 8822) | func TestSplittedDeletePerms(t *testing.T) {
function TestSplittedRenamePerms (line 8865) | func TestSplittedRenamePerms(t *testing.T) {
function TestSFTPLoopError (line 8908) | func TestSFTPLoopError(t *testing.T) {
function TestNonLocalCrossRename (line 9052) | func TestNonLocalCrossRename(t *testing.T) {
function TestNonLocalCrossRenameNonLocalBaseUser (line 9175) | func TestNonLocalCrossRenameNonLocalBaseUser(t *testing.T) {
function TestCopyAndRemoveSSHCommands (line 9281) | func TestCopyAndRemoveSSHCommands(t *testing.T) {
function TestCopyAndRemovePermissions (line 9432) | func TestCopyAndRemovePermissions(t *testing.T) {
function TestCrossFoldersCopy (line 9507) | func TestCrossFoldersCopy(t *testing.T) {
function TestHTTPFs (line 9661) | func TestHTTPFs(t *testing.T) {
function TestProxyProtocol (line 9685) | func TestProxyProtocol(t *testing.T) {
function TestSetProtocol (line 9692) | func TestSetProtocol(t *testing.T) {
function TestGetFsError (line 9698) | func TestGetFsError(t *testing.T) {
function waitTCPListening (line 9708) | func waitTCPListening(address string) {
function checkBasicSFTP (line 9722) | func checkBasicSFTP(client *sftp.Client) error {
function getCustomAuthSftpClient (line 9731) | func getCustomAuthSftpClient(user dataprovider.User, authMethods []ssh.A...
function getSftpClient (line 9750) | func getSftpClient(user dataprovider.User) (*ssh.Client, *sftp.Client, e...
function runSSHCommand (line 9774) | func runSSHCommand(command string, user dataprovider.User) ([]byte, erro...
function getWebDavClient (line 9807) | func getWebDavClient(user dataprovider.User) *gowebdav.Client {
function getTestUser (line 9818) | func getTestUser() dataprovider.User {
function getTestSFTPUser (line 9833) | func getTestSFTPUser() dataprovider.User {
function getCryptFsUser (line 9843) | func getCryptFsUser() dataprovider.User {
function getTestUserWithHTTPFs (line 9851) | func getTestUserWithHTTPFs() dataprovider.User {
function writeSFTPFile (line 9863) | func writeSFTPFile(name string, size int64, client *sftp.Client) error {
function writeSFTPFileNoCheck (line 9878) | func writeSFTPFileNoCheck(name string, size int64, client *sftp.Client) ...
function getUploadScriptEnvContent (line 9896) | func getUploadScriptEnvContent(envVar string) []byte {
function getUploadScriptContent (line 9907) | func getUploadScriptContent(movedPath, logFilePath string, exitStatus in...
function getSaveProviderObjectScriptContent (line 9918) | func getSaveProviderObjectScriptContent(outFilePath string, exitStatus i...
function generateTOTPPasscode (line 9925) | func generateTOTPPasscode(secret string, algo otp.Algorithm) (string, er...
function isDbDefenderSupported (line 9934) | func isDbDefenderSupported() bool {
function getEncryptedFileSize (line 9946) | func getEncryptedFileSize(size int64) (int64, error) {
function printLatestLogs (line 9951) | func printLatestLogs(maxNumberOfLines int) {
type receivedEmail (line 9974) | type receivedEmail struct
method set (line 9981) | func (e *receivedEmail) set(from string, to []string, data []byte) {
method reset (line 9990) | func (e *receivedEmail) reset() {
method get (line 9999) | func (e *receivedEmail) get() receivedEmail {
function startHTTPFs (line 10010) | func startHTTPFs() {
FILE: internal/common/ratelimiter.go
type RateLimiterType (line 38) | type RateLimiterType
constant rateLimiterTypeGlobal (line 42) | rateLimiterTypeGlobal RateLimiterType = iota + 1
constant rateLimiterTypeSource (line 43) | rateLimiterTypeSource
type RateLimiterConfig (line 47) | type RateLimiterConfig struct
method isEnabled (line 74) | func (r *RateLimiterConfig) isEnabled() bool {
method validate (line 78) | func (r *RateLimiterConfig) validate() error {
method getLimiter (line 105) | func (r *RateLimiterConfig) getLimiter() *rateLimiter {
type rateLimiter (line 136) | type rateLimiter struct
method Wait (line 148) | func (rl *rateLimiter) Wait(source, protocol string) (time.Duration, e...
type sourceRateLimiter (line 175) | type sourceRateLimiter struct
method updateLastActivity (line 180) | func (s *sourceRateLimiter) updateLastActivity() {
method getLastActivity (line 184) | func (s *sourceRateLimiter) getLastActivity() int64 {
type sourceBuckets (line 188) | type sourceBuckets struct
method reserve (line 195) | func (b *sourceBuckets) reserve(source string) (*rate.Reservation, err...
method addAndReserve (line 207) | func (b *sourceBuckets) addAndReserve(r *rate.Limiter, source string) ...
method cleanup (line 222) | func (b *sourceBuckets) cleanup() {
FILE: internal/common/ratelimiter_test.go
function TestRateLimiterConfig (line 25) | func TestRateLimiterConfig(t *testing.T) {
function TestRateLimiter (line 71) | func TestRateLimiter(t *testing.T) {
function TestLimiterCleanup (line 106) | func TestLimiterCleanup(t *testing.T) {
FILE: internal/common/tlsutils.go
constant DefaultTLSKeyPaidID (line 37) | DefaultTLSKeyPaidID = "default"
constant pemCRLType (line 38) | pemCRLType = "X509 CRL"
type TLSKeyPair (line 46) | type TLSKeyPair struct
type CertManager (line 53) | type CertManager struct
method Reload (line 68) | func (m *CertManager) Reload() error {
method loadCertificates (line 79) | func (m *CertManager) loadCertificates() error {
method HasCertificate (line 113) | func (m *CertManager) HasCertificate(certID string) bool {
method GetCertificateFunc (line 122) | func (m *CertManager) GetCertificateFunc(certID string) func(*tls.Clie...
method IsRevoked (line 138) | func (m *CertManager) IsRevoked(crt *x509.Certificate, caCrt *x509.Cer...
method LoadCRLs (line 161) | func (m *CertManager) LoadCRLs() error {
method GetRootCAs (line 209) | func (m *CertManager) GetRootCAs() *x509.CertPool {
method LoadRootCAs (line 217) | func (m *CertManager) LoadRootCAs() error {
method SetCACertificates (line 254) | func (m *CertManager) SetCACertificates(caCertificates []string) {
method SetCARevocationLists (line 260) | func (m *CertManager) SetCARevocationLists(caRevocationLists []string) {
method monitor (line 264) | func (m *CertManager) monitor() {
function NewCertManager (line 298) | func NewCertManager(keyPairs []TLSKeyPair, configDir, logSender string) ...
FILE: internal/common/tlsutils_test.go
constant serverCert (line 30) | serverCert = `-----BEGIN CERTIFICATE-----
constant serverKey (line 55) | serverKey = `-----BEGIN RSA PRIVATE KEY-----
constant caCRT (line 82) | caCRT = `-----BEGIN CERTIFICATE-----
constant caKey (line 111) | caKey = `-----BEGIN RSA PRIVATE KEY-----
constant caCRL (line 162) | caCRL = `-----BEGIN X509 CRL-----
constant client1Crt (line 179) | client1Crt = `-----BEGIN CERTIFICATE-----
constant client1Key (line 204) | client1Key = `-----BEGIN RSA PRIVATE KEY-----
constant client2Crt (line 232) | client2Crt = `-----BEGIN CERTIFICATE-----
constant client2Key (line 257) | client2Key = `-----BEGIN RSA PRIVATE KEY-----
function TestLoadCertificate (line 286) | func TestLoadCertificate(t *testing.T) {
function TestLoadInvalidCert (line 438) | func TestLoadInvalidCert(t *testing.T) {
function TestCertificateMonitor (line 471) | func TestCertificateMonitor(t *testing.T) {
FILE: internal/common/transfer.go
type BaseTransfer (line 38) | type BaseTransfer struct
method GetTransferQuota (line 99) | func (t *BaseTransfer) GetTransferQuota() dataprovider.TransferQuota {
method SetFtpMode (line 104) | func (t *BaseTransfer) SetFtpMode(mode string) {
method GetID (line 109) | func (t *BaseTransfer) GetID() int64 {
method GetType (line 114) | func (t *BaseTransfer) GetType() int {
method GetSize (line 119) | func (t *BaseTransfer) GetSize() int64 {
method GetDownloadedSize (line 127) | func (t *BaseTransfer) GetDownloadedSize() int64 {
method GetUploadedSize (line 132) | func (t *BaseTransfer) GetUploadedSize() int64 {
method GetStartTime (line 137) | func (t *BaseTransfer) GetStartTime() time.Time {
method GetAbortError (line 142) | func (t *BaseTransfer) GetAbortError() error {
method SignalClose (line 155) | func (t *BaseTransfer) SignalClose(err error) {
method GetTruncatedSize (line 164) | func (t *BaseTransfer) GetTruncatedSize() int64 {
method HasSizeLimit (line 169) | func (t *BaseTransfer) HasSizeLimit() bool {
method GetVirtualPath (line 181) | func (t *BaseTransfer) GetVirtualPath() string {
method GetFsPath (line 186) | func (t *BaseTransfer) GetFsPath() string {
method SetTimes (line 191) | func (t *BaseTransfer) SetTimes(fsPath string, atime time.Time, mtime ...
method GetRealFsPath (line 202) | func (t *BaseTransfer) GetRealFsPath(fsPath string) string {
method SetMetadata (line 213) | func (t *BaseTransfer) SetMetadata(val map[string]string) {
method SetCancelFn (line 218) | func (t *BaseTransfer) SetCancelFn(cancelFn func()) {
method ConvertError (line 225) | func (t *BaseTransfer) ConvertError(err error) error {
method CheckRead (line 234) | func (t *BaseTransfer) CheckRead() error {
method CheckWrite (line 251) | func (t *BaseTransfer) CheckWrite() error {
method Truncate (line 272) | func (t *BaseTransfer) Truncate(fsPath string, size int64) (int64, err...
method TransferError (line 312) | func (t *BaseTransfer) TransferError(err error) {
method getUploadFileSize (line 328) | func (t *BaseTransfer) getUploadFileSize() (int64, int, error) {
method checkUploadOutsideHomeDir (line 366) | func (t *BaseTransfer) checkUploadOutsideHomeDir(err error) int {
method Close (line 390) | func (t *BaseTransfer) Close() error {
method isAtomicUpload (line 466) | func (t *BaseTransfer) isAtomicUpload() bool {
method updateTransferTimestamps (line 470) | func (t *BaseTransfer) updateTransferTimestamps(uploadFileSize, elapse...
method executeUploadHook (line 493) | func (t *BaseTransfer) executeUploadHook(numFiles int, fileSize, elaps...
method getUploadedFiles (line 514) | func (t *BaseTransfer) getUploadedFiles() int {
method updateTimes (line 522) | func (t *BaseTransfer) updateTimes() {
method updateQuota (line 530) | func (t *BaseTransfer) updateQuota(numFiles int, fileSize int64) bool {
method HandleThrottle (line 550) | func (t *BaseTransfer) HandleThrottle() {
function NewBaseTransfer (line 68) | func NewBaseTransfer(file vfs.File, conn *BaseConnection, cancelFn func(...
FILE: internal/common/transfer_test.go
function TestTransferUpdateQuota (line 35) | func TestTransferUpdateQuota(t *testing.T) {
function TestTransferThrottling (line 71) | func TestTransferThrottling(t *testing.T) {
function TestRealPath (line 109) | func TestRealPath(t *testing.T) {
function TestTruncate (line 143) | func TestTruncate(t *testing.T) {
function TestTransferErrors (line 208) | func TestTransferErrors(t *testing.T) {
function TestRemovePartialCryptoFile (line 303) | func TestRemovePartialCryptoFile(t *testing.T) {
function TestFTPMode (line 332) | func TestFTPMode(t *testing.T) {
function TestTransferQuota (line 345) | func TestTransferQuota(t *testing.T) {
function TestUploadOutsideHomeRenameError (line 448) | func TestUploadOutsideHomeRenameError(t *testing.T) {
FILE: internal/common/transferschecker.go
type overquotaTransfer (line 27) | type overquotaTransfer struct
type uploadAggregationKey (line 33) | type uploadAggregationKey struct
type TransfersChecker (line 41) | type TransfersChecker interface
function getTransfersChecker (line 48) | func getTransfersChecker(isShared int) TransfersChecker {
type baseTransferChecker (line 57) | type baseTransferChecker struct
method isDataTransferExceeded (line 61) | func (t *baseTransferChecker) isDataTransferExceeded(user dataprovider...
method getRemainingDiskQuota (line 86) | func (t *baseTransferChecker) getRemainingDiskQuota(user dataprovider....
method aggregateTransfersByUser (line 106) | func (t *baseTransferChecker) aggregateTransfersByUser(usersToFetch ma...
method aggregateUploadTransfers (line 121) | func (t *baseTransferChecker) aggregateUploadTransfers() (map[string]b...
method getUsersToCheck (line 160) | func (t *baseTransferChecker) getUsersToCheck(usersToFetch map[string]...
method getOverquotaTransfers (line 175) | func (t *baseTransferChecker) getOverquotaTransfers(usersToFetch map[s...
type transfersCheckerMem (line 240) | type transfersCheckerMem struct
method AddTransfer (line 245) | func (t *transfersCheckerMem) AddTransfer(transfer dataprovider.Active...
method RemoveTransfer (line 252) | func (t *transfersCheckerMem) RemoveTransfer(ID int64, connectionID st...
method UpdateTransferCurrentSizes (line 266) | func (t *transfersCheckerMem) UpdateTransferCurrentSizes(ulSize, dlSiz...
method GetOverquotaTransfers (line 280) | func (t *transfersCheckerMem) GetOverquotaTransfers() []overquotaTrans...
type transfersCheckerDB (line 291) | type transfersCheckerDB struct
method AddTransfer (line 296) | func (t *transfersCheckerDB) AddTransfer(transfer dataprovider.ActiveT...
method RemoveTransfer (line 300) | func (t *transfersCheckerDB) RemoveTransfer(ID int64, connectionID str...
method UpdateTransferCurrentSizes (line 304) | func (t *transfersCheckerDB) UpdateTransferCurrentSizes(ulSize, dlSize...
method GetOverquotaTransfers (line 308) | func (t *transfersCheckerDB) GetOverquotaTransfers() []overquotaTransf...
FILE: internal/common/transferschecker_test.go
function TestTransfersCheckerDiskQuota (line 36) | func TestTransfersCheckerDiskQuota(t *testing.T) {
function TestTransferCheckerTransferQuota (line 268) | func TestTransferCheckerTransferQuota(t *testing.T) {
function TestAggregateTransfers (line 389) | func TestAggregateTransfers(t *testing.T) {
function TestDataTransferExceeded (line 557) | func TestDataTransferExceeded(t *testing.T) {
function TestGetUsersForQuotaCheck (line 604) | func TestGetUsersForQuotaCheck(t *testing.T) {
function TestDBTransferChecker (line 686) | func TestDBTransferChecker(t *testing.T) {
function isDbTransferCheckerSupported (line 758) | func isDbTransferCheckerSupported() bool {
FILE: internal/config/config.go
constant logSender (line 50) | logSender = "config"
constant configName (line 54) | configName = "sftpgo"
constant configEnvPrefix (line 56) | configEnvPrefix = "sftpgo"
constant envFileMaxSize (line 57) | envFileMaxSize = 1048576
type globalConfig (line 177) | type globalConfig struct
function init (line 194) | func init() {
function Init (line 201) | func Init() {
function GetCommonConfig (line 494) | func GetCommonConfig() common.Configuration {
function SetCommonConfig (line 499) | func SetCommonConfig(config common.Configuration) {
function GetSFTPDConfig (line 504) | func GetSFTPDConfig() sftpd.Configuration {
function SetSFTPDConfig (line 509) | func SetSFTPDConfig(config sftpd.Configuration) {
function GetFTPDConfig (line 514) | func GetFTPDConfig() ftpd.Configuration {
function SetFTPDConfig (line 519) | func SetFTPDConfig(config ftpd.Configuration) {
function GetWebDAVDConfig (line 524) | func GetWebDAVDConfig() webdavd.Configuration {
function SetWebDAVDConfig (line 529) | func SetWebDAVDConfig(config webdavd.Configuration) {
function GetHTTPDConfig (line 534) | func GetHTTPDConfig() httpd.Conf {
function SetHTTPDConfig (line 539) | func SetHTTPDConfig(config httpd.Conf) {
function GetProviderConf (line 544) | func GetProviderConf() dataprovider.Config {
function SetProviderConf (line 549) | func SetProviderConf(config dataprovider.Config) {
function GetHTTPConfig (line 554) | func GetHTTPConfig() httpclient.Config {
function GetCommandConfig (line 559) | func GetCommandConfig() command.Config {
function GetKMSConfig (line 564) | func GetKMSConfig() kms.Configuration {
function SetKMSConfig (line 569) | func SetKMSConfig(config kms.Configuration) {
function GetTelemetryConfig (line 574) | func GetTelemetryConfig() telemetry.Conf {
function SetTelemetryConfig (line 579) | func SetTelemetryConfig(config telemetry.Conf) {
function GetPluginsConfig (line 584) | func GetPluginsConfig() []plugin.Config {
function SetPluginsConfig (line 589) | func SetPluginsConfig(config []plugin.Config) {
function HasKMSPlugin (line 594) | func HasKMSPlugin() bool {
function GetMFAConfig (line 604) | func GetMFAConfig() mfa.Config {
function GetSMTPConfig (line 609) | func GetSMTPConfig() smtp.Config {
function GetACMEConfig (line 614) | func GetACMEConfig() acme.Configuration {
function HasServicesToStart (line 620) | func HasServicesToStart() bool {
function getRedactedPassword (line 636) | func getRedactedPassword(value string) string {
function getRedactedGlobalConf (line 643) | func getRedactedGlobalConf() globalConfig {
function setConfigFile (line 678) | func setConfigFile(configDir, configFile string) {
function readEnvFiles (line 691) | func readEnvFiles(configDir string) {
function checkOverrideDefaultSettings (line 716) | func checkOverrideDefaultSettings() {
function LoadConfig (line 755) | func LoadConfig(configDir, configFile string) error {
function isProxyProtocolValid (line 789) | func isProxyProtocolValid() bool {
function isExternalAuthScopeValid (line 793) | func isExternalAuthScopeValid() bool {
function resetInvalidConfigs (line 797) | func resetInvalidConfigs() {
function loadBindingsFromEnv (line 839) | func loadBindingsFromEnv() {
function getTOTPFromEnv (line 854) | func getTOTPFromEnv(idx int) {
function getRateLimitersFromEnv (line 889) | func getRateLimitersFromEnv(idx int) {
function getKMSPluginFromEnv (line 954) | func getKMSPluginFromEnv(idx int, pluginConfig *plugin.Config) bool {
function getAuthPluginFromEnv (line 972) | func getAuthPluginFromEnv(idx int, pluginConfig *plugin.Config) bool {
function getNotifierPluginFromEnv (line 984) | func getNotifierPluginFromEnv(idx int, pluginConfig *plugin.Config) bool {
function getPluginsFromEnv (line 1035) | func getPluginsFromEnv(idx int) {
function getSFTPDBindindFromEnv (line 1106) | func getSFTPDBindindFromEnv(idx int) {
function getFTPDPassiveIPOverridesFromEnv (line 1141) | func getFTPDPassiveIPOverridesFromEnv(idx int) []ftpd.PassiveIPOverride {
function getDefaultFTPDBinding (line 1178) | func getDefaultFTPDBinding(idx int) ftpd.Binding {
function getFTPDBindingSecurityFromEnv (line 1186) | func getFTPDBindingSecurityFromEnv(idx int, binding *ftpd.Binding) bool {
function getFTPDBindingFromEnv (line 1240) | func getFTPDBindingFromEnv(idx int) {
function applyFTPDBindingFromEnv (line 1293) | func applyFTPDBindingFromEnv(idx int, isSet bool, binding ftpd.Binding) {
function getWebDAVBindingHTTPSConfigsFromEnv (line 1303) | func getWebDAVBindingHTTPSConfigsFromEnv(idx int, binding *webdavd.Bindi...
function getWebDAVDBindingProxyConfigsFromEnv (line 1351) | func getWebDAVDBindingProxyConfigsFromEnv(idx int, binding *webdavd.Bind...
function loadWebDAVCacheMappingsFromEnv (line 1381) | func loadWebDAVCacheMappingsFromEnv() []webdavd.CustomMimeMapping {
function getWebDAVDBindingFromEnv (line 1402) | func getWebDAVDBindingFromEnv(idx int) {
function getHTTPDSecurityProxyHeadersFromEnv (line 1451) | func getHTTPDSecurityProxyHeadersFromEnv(idx int) []httpd.HTTPSProxyHead...
function getHTTPDSecurityConfFromEnv (line 1486) | func getHTTPDSecurityConfFromEnv(idx int) (httpd.SecurityConf, bool) { /...
function getHTTPDOIDCFromEnv (line 1604) | func getHTTPDOIDCFromEnv(idx int) (httpd.OIDC, bool) {
function getHTTPDUIBrandingFromEnv (line 1686) | func getHTTPDUIBrandingFromEnv(prefix string, branding httpd.UIBranding)...
function getHTTPDBrandingFromEnv (line 1740) | func getHTTPDBrandingFromEnv(idx int) (httpd.Branding, bool) {
function getDefaultHTTPBinding (line 1764) | func getDefaultHTTPBinding(idx int) httpd.Binding {
function getHTTPDNestedObjectsFromEnv (line 1772) | func getHTTPDNestedObjectsFromEnv(idx int, binding *httpd.Binding) bool {
function getHTTPDBindingProxyConfigsFromEnv (line 1796) | func getHTTPDBindingProxyConfigsFromEnv(idx int, binding *httpd.Binding)...
function getHTTPDBindingFromEnv (line 1826) | func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo
function setHTTPDBinding (line 1949) | func setHTTPDBinding(isSet bool, binding httpd.Binding, idx int) {
function getHTTPClientCertificatesFromEnv (line 1959) | func getHTTPClientCertificatesFromEnv(idx int) {
function getHTTPClientHeadersFromEnv (line 1984) | func getHTTPClientHeadersFromEnv(idx int) {
function getCommandConfigsFromEnv (line 2014) | func getCommandConfigsFromEnv(idx int) {
function setViperDefaults (line 2049) | func setViperDefaults() {
function lookupBoolFromEnv (line 2248) | func lookupBoolFromEnv(envName string) (bool, bool) {
function lookupIntFromEnv (line 2260) | func lookupIntFromEnv(envName string, bitSize int) (int64, bool) {
function lookupStringListFromEnv (line 2272) | func lookupStringListFromEnv(envName string) ([]string, bool) {
FILE: internal/config/config_darwin.go
function setViperAdditionalConfigPaths (line 22) | func setViperAdditionalConfigPaths() {
FILE: internal/config/config_fallback.go
function setViperAdditionalConfigPaths (line 19) | func setViperAdditionalConfigPaths() {}
FILE: internal/config/config_linux.go
function setViperAdditionalConfigPaths (line 22) | func setViperAdditionalConfigPaths() {
FILE: internal/config/config_test.go
constant tempConfigName (line 44) | tempConfigName = "temp"
function reset (line 51) | func reset() {
function TestLoadConfigTest (line 56) | func TestLoadConfigTest(t *testing.T) {
function TestLoadConfigFileNotFound (line 82) | func TestLoadConfigFileNotFound(t *testing.T) {
function TestReadEnvFiles (line 96) | func TestReadEnvFiles(t *testing.T) {
function TestEnabledSSHCommands (line 125) | func TestEnabledSSHCommands(t *testing.T) {
function TestInvalidExternalAuthScope (line 153) | func TestInvalidExternalAuthScope(t *testing.T) {
function TestInvalidProxyProtocol (line 175) | func TestInvalidProxyProtocol(t *testing.T) {
function TestInvalidUsersBaseDir (line 197) | func TestInvalidUsersBaseDir(t *testing.T) {
function TestInvalidInstallationHint (line 219) | func TestInvalidInstallationHint(t *testing.T) {
function TestInvalidRenameMode (line 246) | func TestInvalidRenameMode(t *testing.T) {
function TestDefenderProviderDriver (line 266) | func TestDefenderProviderDriver(t *testing.T) {
function TestSetGetConfig (line 294) | func TestSetGetConfig(t *testing.T) {
function TestServiceToStart (line 360) | func TestServiceToStart(t *testing.T) {
function TestSSHCommandsFromEnv (line 392) | func TestSSHCommandsFromEnv(t *testing.T) {
function TestSMTPFromEnv (line 410) | func TestSMTPFromEnv(t *testing.T) {
function TestMFAFromEnv (line 427) | func TestMFAFromEnv(t *testing.T) {
function TestDisabledMFAConfig (line 453) | func TestDisabledMFAConfig(t *testing.T) {
function TestOverrideSliceValues (line 480) | func TestOverrideSliceValues(t *testing.T) {
function TestFTPDOverridesFromEnv (line 555) | func TestFTPDOverridesFromEnv(t *testing.T) {
function TestHTTPDSubObjectsFromEnv (line 605) | func TestHTTPDSubObjectsFromEnv(t *testing.T) {
function TestPluginsFromEnv (line 668) | func TestPluginsFromEnv(t *testing.T) {
function TestRateLimitersFromEnv (line 788) | func TestRateLimitersFromEnv(t *testing.T) {
function TestSFTPDBindingsFromEnv (line 843) | func TestSFTPDBindingsFromEnv(t *testing.T) {
function TestCommandsFromEnv (line 871) | func TestCommandsFromEnv(t *testing.T) {
function TestFTPDBindingsFromEnv (line 938) | func TestFTPDBindingsFromEnv(t *testing.T) {
function TestWebDAVMimeCache (line 1029) | func TestWebDAVMimeCache(t *testing.T) {
function TestWebDAVBindingsFromEnv (line 1095) | func TestWebDAVBindingsFromEnv(t *testing.T) {
function TestHTTPDBindingsFromEnv (line 1181) | func TestHTTPDBindingsFromEnv(t *testing.T) {
function TestHTTPClientCertificatesFromEnv (line 1459) | func TestHTTPClientCertificatesFromEnv(t *testing.T) {
function TestHTTPClientHeadersFromEnv (line 1519) | func TestHTTPClientHeadersFromEnv(t *testing.T) {
function TestConfigFromEnv (line 1589) | func TestConfigFromEnv(t *testing.T) {
FILE: internal/dataprovider/actions.go
constant ActionExecutorSelf (line 39) | ActionExecutorSelf = "__self__"
constant ActionExecutorSystem (line 42) | ActionExecutorSystem = "__system__"
constant actionObjectUser (line 46) | actionObjectUser = "user"
constant actionObjectFolder (line 47) | actionObjectFolder = "folder"
constant actionObjectGroup (line 48) | actionObjectGroup = "group"
constant actionObjectAdmin (line 49) | actionObjectAdmin = "admin"
constant actionObjectAPIKey (line 50) | actionObjectAPIKey = "api_key"
constant actionObjectShare (line 51) | actionObjectShare = "share"
constant actionObjectEventAction (line 52) | actionObjectEventAction = "event_action"
constant actionObjectEventRule (line 53) | actionObjectEventRule = "event_rule"
constant actionObjectRole (line 54) | actionObjectRole = "role"
constant actionObjectIPListEntry (line 55) | actionObjectIPListEntry = "ip_list_entry"
constant actionObjectConfigs (line 56) | actionObjectConfigs = "configs"
function executeAction (line 64) | func executeAction(operation, executor, ip, objectType, objectName, role...
function executeNotificationCommand (line 132) | func executeNotificationCommand(operation, executor, ip, objectType, obj...
FILE: internal/dataprovider/admin.go
constant PermAdminAny (line 40) | PermAdminAny = "*"
constant PermAdminAddUsers (line 41) | PermAdminAddUsers = "add_users"
constant PermAdminChangeUsers (line 42) | PermAdminChangeUsers = "edit_users"
constant PermAdminDeleteUsers (line 43) | PermAdminDeleteUsers = "del_users"
constant PermAdminViewUsers (line 44) | PermAdminViewUsers = "view_users"
constant PermAdminViewConnections (line 45) | PermAdminViewConnections = "view_conns"
constant PermAdminCloseConnections (line 46) | PermAdminCloseConnections = "close_conns"
constant PermAdminViewServerStatus (line 47) | PermAdminViewServerStatus = "view_status"
constant PermAdminManageGroups (line 48) | PermAdminManageGroups = "manage_groups"
constant PermAdminManageFolders (line 49) | PermAdminManageFolders = "manage_folders"
constant PermAdminQuotaScans (line 50) | PermAdminQuotaScans = "quota_scans"
constant PermAdminManageDefender (line 51) | PermAdminManageDefender = "manage_defender"
constant PermAdminViewDefender (line 52) | PermAdminViewDefender = "view_defender"
constant PermAdminViewEvents (line 53) | PermAdminViewEvents = "view_events"
constant PermAdminDisableMFA (line 54) | PermAdminDisableMFA = "disable_mfa"
constant GroupAddToUsersAsMembership (line 59) | GroupAddToUsersAsMembership = iota
constant GroupAddToUsersAsPrimary (line 61) | GroupAddToUsersAsPrimary
constant GroupAddToUsersAsSecondary (line 63) | GroupAddToUsersAsSecondary
type AdminTOTPConfig (line 75) | type AdminTOTPConfig struct
method validate (line 81) | func (c *AdminTOTPConfig) validate(username string) error {
type AdminPreferences (line 106) | type AdminPreferences struct
method HideGroups (line 128) | func (p *AdminPreferences) HideGroups() bool {
method HideFilesystem (line 133) | func (p *AdminPreferences) HideFilesystem() bool {
method HideVirtualFolders (line 138) | func (p *AdminPreferences) HideVirtualFolders() bool {
method HideProfile (line 143) | func (p *AdminPreferences) HideProfile() bool {
method HideACLs (line 148) | func (p *AdminPreferences) HideACLs() bool {
method HideDiskQuotaAndBandwidthLimits (line 154) | func (p *AdminPreferences) HideDiskQuotaAndBandwidthLimits() bool {
method HideAdvancedSettings (line 159) | func (p *AdminPreferences) HideAdvancedSettings() bool {
method VisibleUserPageSections (line 165) | func (p *AdminPreferences) VisibleUserPageSections() int {
type AdminFilters (line 186) | type AdminFilters struct
type AdminGroupMappingOptions (line 207) | type AdminGroupMappingOptions struct
method validate (line 211) | func (o *AdminGroupMappingOptions) validate() error {
method GetUserGroupType (line 219) | func (o *AdminGroupMappingOptions) GetUserGroupType() int {
type AdminGroupMapping (line 231) | type AdminGroupMapping struct
type Admin (line 237) | type Admin struct
method CountUnusedRecoveryCodes (line 264) | func (a *Admin) CountUnusedRecoveryCodes() int {
method hashPassword (line 274) | func (a *Admin) hashPassword() error {
method hasRedactedSecret (line 298) | func (a *Admin) hasRedactedSecret() bool {
method validateRecoveryCodes (line 302) | func (a *Admin) validateRecoveryCodes() error {
method validatePermissions (line 318) | func (a *Admin) validatePermissions() error {
method validateGroups (line 345) | func (a *Admin) validateGroups() error {
method applyNamingRules (line 367) | func (a *Admin) applyNamingRules() {
method validate (line 375) | func (a *Admin) validate() error { //nolint:gocyclo
method CheckPassword (line 433) | func (a *Admin) CheckPassword(password string) (bool, error) {
method CanLoginFromIP (line 461) | func (a *Admin) CanLoginFromIP(ip string) bool {
method CanLogin (line 483) | func (a *Admin) CanLogin(ip string) error {
method checkUserAndPass (line 493) | func (a *Admin) checkUserAndPass(password, ip string) error {
method RenderAsJSON (line 511) | func (a *Admin) RenderAsJSON(reload bool) ([]byte, error) {
method HideConfidentialData (line 526) | func (a *Admin) HideConfidentialData() {
method SetEmptySecretsIfNil (line 540) | func (a *Admin) SetEmptySecretsIfNil() {
method SetNilSecretsIfEmpty (line 549) | func (a *Admin) SetNilSecretsIfEmpty() {
method HasPermission (line 556) | func (a *Admin) HasPermission(perm string) bool {
method HasPermissions (line 564) | func (a *Admin) HasPermissions(perms ...string) bool {
method GetAllowedIPAsString (line 574) | func (a *Admin) GetAllowedIPAsString() string {
method GetValidPerms (line 579) | func (a *Admin) GetValidPerms() []string {
method CanManageMFA (line 584) | func (a *Admin) CanManageMFA() bool {
method GetSignature (line 590) | func (a *Admin) GetSignature() string {
method getACopy (line 594) | func (a *Admin) getACopy() Admin {
method setFromEnv (line 649) | func (a *Admin) setFromEnv() error {
FILE: internal/dataprovider/apikey.go
type APIKeyScope (line 31) | type APIKeyScope
constant APIKeyScopeAdmin (line 36) | APIKeyScopeAdmin APIKeyScope = iota + 1
constant APIKeyScopeUser (line 38) | APIKeyScopeUser
type APIKey (line 44) | type APIKey struct
method getACopy (line 75) | func (k *APIKey) getACopy() APIKey {
method RenderAsJSON (line 95) | func (k *APIKey) RenderAsJSON(reload bool) ([]byte, error) {
method HideConfidentialData (line 110) | func (k *APIKey) HideConfidentialData() {
method hashKey (line 114) | func (k *APIKey) hashKey() error {
method generateKey (line 133) | func (k *APIKey) generateKey() {
method DisplayKey (line 143) | func (k *APIKey) DisplayKey() string {
method validate (line 147) | func (k *APIKey) validate() error {
method Authenticate (line 186) | func (k *APIKey) Authenticate(plainKey string) error {
FILE: internal/dataprovider/bolt.go
constant boltDatabaseVersion (line 42) | boltDatabaseVersion = 34
type BoltProvider (line 65) | type BoltProvider struct
method checkAvailability (line 106) | func (p *BoltProvider) checkAvailability() error {
method validateUserAndTLSCert (line 111) | func (p *BoltProvider) validateUserAndTLSCert(username, protocol strin...
method validateUserAndPass (line 124) | func (p *BoltProvider) validateUserAndPass(username, password, ip, pro...
method validateAdminAndPass (line 133) | func (p *BoltProvider) validateAdminAndPass(username, password, ip str...
method validateUserAndPubKey (line 143) | func (p *BoltProvider) validateUserAndPubKey(username string, pubKey [...
method updateAPIKeyLastUse (line 156) | func (p *BoltProvider) updateAPIKeyLastUse(keyID string) error {
method getAdminSignature (line 186) | func (p *BoltProvider) getAdminSignature(username string) (string, err...
method getUserSignature (line 208) | func (p *BoltProvider) getUserSignature(username string) (string, erro...
method setUpdatedAt (line 230) | func (p *BoltProvider) setUpdatedAt(username string) {
method updateLastLogin (line 261) | func (p *BoltProvider) updateLastLogin(username string) error {
method updateAdminLastLogin (line 291) | func (p *BoltProvider) updateAdminLastLogin(username string) error {
method updateTransferQuota (line 321) | func (p *BoltProvider) updateTransferQuota(username string, uploadSize...
method updateQuota (line 356) | func (p *BoltProvider) updateQuota(username string, filesAdd int, size...
method getUsedQuota (line 390) | func (p *BoltProvider) getUsedQuota(username string) (int, int64, int6...
method adminExists (line 399) | func (p *BoltProvider) adminExists(username string) (Admin, error) {
method addAdmin (line 417) | func (p *BoltProvider) addAdmin(admin *Admin) error {
method updateAdmin (line 467) | func (p *BoltProvider) updateAdmin(admin *Admin) error {
method deleteAdmin (line 525) | func (p *BoltProvider) deleteAdmin(admin Admin) error {
method getAdmins (line 571) | func (p *BoltProvider) getAdmins(limit int, offset int, order string) ...
method dumpAdmins (line 622) | func (p *BoltProvider) dumpAdmins() ([]Admin, error) {
method userExists (line 645) | func (p *BoltProvider) userExists(username, role string) (User, error) {
method addUser (line 672) | func (p *BoltProvider) addUser(user *User) error {
method updateUser (line 738) | func (p *BoltProvider) updateUser(user *User) error {
method deleteUser (line 784) | func (p *BoltProvider) deleteUser(user User, _ bool) error {
method updateUserPassword (line 836) | func (p *BoltProvider) updateUserPassword(username, password string) e...
method dumpUsers (line 861) | func (p *BoltProvider) dumpUsers() ([]User, error) {
method getRecentlyUpdatedUsers (line 885) | func (p *BoltProvider) getRecentlyUpdatedUsers(after int64) ([]User, e...
method getUsersForQuotaCheck (line 945) | func (p *BoltProvider) getUsersForQuotaCheck(toFetch map[string]bool) ...
method getUsers (line 1005) | func (p *BoltProvider) getUsers(limit int, offset int, order, role str...
method dumpFolders (line 1066) | func (p *BoltProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
method getFolders (line 1087) | func (p *BoltProvider) getFolders(limit, offset int, order string, _ b...
method getFolderByName (line 1140) | func (p *BoltProvider) getFolderByName(name string) (vfs.BaseVirtualFo...
method addFolder (line 1153) | func (p *BoltProvider) addFolder(folder *vfs.BaseVirtualFolder) error {
method updateFolder (line 1175) | func (p *BoltProvider) updateFolder(folder *vfs.BaseVirtualFolder) err...
method deleteFolderMappings (line 1210) | func (p *BoltProvider) deleteFolderMappings(folder vfs.BaseVirtualFold...
method deleteFolder (line 1266) | func (p *BoltProvider) deleteFolder(baseFolder vfs.BaseVirtualFolder) ...
method updateFolderQuota (line 1298) | func (p *BoltProvider) updateFolderQuota(name string, filesAdd int, si...
method getUsedFolderQuota (line 1329) | func (p *BoltProvider) getUsedFolderQuota(name string) (int, int64, er...
method getGroups (line 1338) | func (p *BoltProvider) getGroups(limit, offset int, order string, _ bo...
method getGroupsWithNames (line 1395) | func (p *BoltProvider) getGroupsWithNames(names []string) ([]Group, er...
method getUsersInGroups (line 1422) | func (p *BoltProvider) getUsersInGroups(names []string) ([]string, err...
method groupExists (line 1446) | func (p *BoltProvider) groupExists(name string) (Group, error) {
method addGroup (line 1467) | func (p *BoltProvider) addGroup(group *Group) error {
method updateGroup (line 1509) | func (p *BoltProvider) updateGroup(group *Group) error {
method deleteGroup (line 1556) | func (p *BoltProvider) deleteGroup(group Group) error {
method dumpGroups (line 1603) | func (p *BoltProvider) dumpGroups() ([]Group, error) {
method apiKeyExists (line 1627) | func (p *BoltProvider) apiKeyExists(keyID string) (APIKey, error) {
method addAPIKey (line 1644) | func (p *BoltProvider) addAPIKey(apiKey *APIKey) error {
method updateAPIKey (line 1683) | func (p *BoltProvider) updateAPIKey(apiKey *APIKey) error {
method deleteAPIKey (line 1728) | func (p *BoltProvider) deleteAPIKey(apiKey APIKey) error {
method getAPIKeys (line 1743) | func (p *BoltProvider) getAPIKeys(limit int, offset int, order string)...
method dumpAPIKeys (line 1794) | func (p *BoltProvider) dumpAPIKeys() ([]APIKey, error) {
method shareExists (line 1817) | func (p *BoltProvider) shareExists(shareID, username string) (Share, e...
method addShare (line 1840) | func (p *BoltProvider) addShare(share *Share) error {
method updateShare (line 1881) | func (p *BoltProvider) updateShare(share *Share) error {
method deleteShare (line 1929) | func (p *BoltProvider) deleteShare(share Share) error {
method getShares (line 1953) | func (p *BoltProvider) getShares(limit int, offset int, order, usernam...
method dumpShares (line 2009) | func (p *BoltProvider) dumpShares() ([]Share, error) {
method updateShareLastUse (line 2032) | func (p *BoltProvider) updateShareLastUse(shareID string, numTokens in...
method getDefenderHosts (line 2063) | func (p *BoltProvider) getDefenderHosts(_ int64, _ int) ([]DefenderEnt...
method getDefenderHostByIP (line 2067) | func (p *BoltProvider) getDefenderHostByIP(_ string, _ int64) (Defende...
method isDefenderHostBanned (line 2071) | func (p *BoltProvider) isDefenderHostBanned(_ string) (DefenderEntry, ...
method updateDefenderBanTime (line 2075) | func (p *BoltProvider) updateDefenderBanTime(_ string, _ int) error {
method deleteDefenderHost (line 2079) | func (p *BoltProvider) deleteDefenderHost(_ string) error {
method addDefenderEvent (line 2083) | func (p *BoltProvider) addDefenderEvent(_ string, _ int) error {
method setDefenderBanTime (line 2087) | func (p *BoltProvider) setDefenderBanTime(_ string, _ int64) error {
method cleanupDefender (line 2091) | func (p *BoltProvider) cleanupDefender(_ int64) error {
method addActiveTransfer (line 2095) | func (p *BoltProvider) addActiveTransfer(_ ActiveTransfer) error {
method updateActiveTransferSizes (line 2099) | func (p *BoltProvider) updateActiveTransferSizes(_, _, _ int64, _ stri...
method removeActiveTransfer (line 2103) | func (p *BoltProvider) removeActiveTransfer(_ int64, _ string) error {
method cleanupActiveTransfers (line 2107) | func (p *BoltProvider) cleanupActiveTransfers(_ time.Time) error {
method getActiveTransfers (line 2111) | func (p *BoltProvider) getActiveTransfers(_ time.Time) ([]ActiveTransf...
method addSharedSession (line 2115) | func (p *BoltProvider) addSharedSession(_ Session) error {
method deleteSharedSession (line 2119) | func (p *BoltProvider) deleteSharedSession(_ string, _ SessionType) er...
method getSharedSession (line 2123) | func (p *BoltProvider) getSharedSession(_ string, _ SessionType) (Sess...
method cleanupSharedSessions (line 2127) | func (p *BoltProvider) cleanupSharedSessions(_ SessionType, _ int64) e...
method getEventActions (line 2131) | func (p *BoltProvider) getEventActions(limit, offset int, order string...
method dumpEventActions (line 2183) | func (p *BoltProvider) dumpEventActions() ([]BaseEventAction, error) {
method eventActionExists (line 2204) | func (p *BoltProvider) eventActionExists(name string) (BaseEventAction...
method addEventAction (line 2220) | func (p *BoltProvider) addEventAction(action *BaseEventAction) error {
method updateEventAction (line 2250) | func (p *BoltProvider) updateEventAction(action *BaseEventAction) error {
method deleteEventAction (line 2309) | func (p *BoltProvider) deleteEventAction(action BaseEventAction) error {
method getEventRules (line 2332) | func (p *BoltProvider) getEventRules(limit, offset int, order string) ...
method dumpEventRules (line 2388) | func (p *BoltProvider) dumpEventRules() ([]EventRule, error) {
method getRecentlyUpdatedRules (line 2412) | func (p *BoltProvider) getRecentlyUpdatedRules(after int64) ([]EventRu...
method eventRuleExists (line 2460) | func (p *BoltProvider) eventRuleExists(name string) (EventRule, error) {
method addEventRule (line 2481) | func (p *BoltProvider) addEventRule(rule *EventRule) error {
method updateEventRule (line 2527) | func (p *BoltProvider) updateEventRule(rule *EventRule) error {
method deleteEventRule (line 2576) | func (p *BoltProvider) deleteEventRule(rule EventRule, _ bool) error {
method getTaskByName (line 2605) | func (*BoltProvider) getTaskByName(_ string) (Task, error) {
method addTask (line 2609) | func (*BoltProvider) addTask(_ string) error {
method updateTask (line 2613) | func (*BoltProvider) updateTask(_ string, _ int64) error {
method updateTaskTimestamp (line 2617) | func (*BoltProvider) updateTaskTimestamp(_ string) error {
method addNode (line 2621) | func (*BoltProvider) addNode() error {
method getNodeByName (line 2625) | func (*BoltProvider) getNodeByName(_ string) (Node, error) {
method getNodes (line 2629) | func (*BoltProvider) getNodes() ([]Node, error) {
method updateNodeTimestamp (line 2633) | func (*BoltProvider) updateNodeTimestamp() error {
method cleanupNodes (line 2637) | func (*BoltProvider) cleanupNodes() error {
method roleExists (line 2641) | func (p *BoltProvider) roleExists(name string) (Role, error) {
method addRole (line 2657) | func (p *BoltProvider) addRole(role *Role) error {
method updateRole (line 2689) | func (p *BoltProvider) updateRole(role *Role) error {
method deleteRole (line 2720) | func (p *BoltProvider) deleteRole(role Role) error {
method getRoles (line 2754) | func (p *BoltProvider) getRoles(limit int, offset int, order string, _...
method dumpRoles (line 2804) | func (p *BoltProvider) dumpRoles() ([]Role, error) {
method ipListEntryExists (line 2825) | func (p *BoltProvider) ipListEntryExists(ipOrNet string, listType IPLi...
method addIPListEntry (line 2848) | func (p *BoltProvider) addIPListEntry(entry *IPListEntry) error {
method updateIPListEntry (line 2873) | func (p *BoltProvider) updateIPListEntry(entry *IPListEntry) error {
method deleteIPListEntry (line 2901) | func (p *BoltProvider) deleteIPListEntry(entry IPListEntry, _ bool) er...
method getIPListEntries (line 2914) | func (p *BoltProvider) getIPListEntries(listType IPListType, filter, f...
method getRecentlyUpdatedIPListEntries (line 2962) | func (p *BoltProvider) getRecentlyUpdatedIPListEntries(_ int64) ([]IPL...
method dumpIPListEntries (line 2966) | func (p *BoltProvider) dumpIPListEntries() ([]IPListEntry, error) {
method countIPListEntries (line 2992) | func (p *BoltProvider) countIPListEntries(listType IPListType) (int64,...
method getListEntriesForIP (line 3013) | func (p *BoltProvider) getListEntriesForIP(ip string, listType IPListT...
method getConfigs (line 3053) | func (p *BoltProvider) getConfigs() (Configs, error) {
method setConfigs (line 3069) | func (p *BoltProvider) setConfigs(configs *Configs) error {
method setFirstDownloadTimestamp (line 3086) | func (p *BoltProvider) setFirstDownloadTimestamp(username string) error {
method setFirstUploadTimestamp (line 3115) | func (p *BoltProvider) setFirstUploadTimestamp(username string) error {
method close (line 3143) | func (p *BoltProvider) close() error {
method reloadConfig (line 3147) | func (p *BoltProvider) reloadConfig() error {
method initializeDatabase (line 3152) | func (p *BoltProvider) initializeDatabase() error {
method migrateDatabase (line 3156) | func (p *BoltProvider) migrateDatabase() error {
method revertDatabase (line 3187) | func (p *BoltProvider) revertDatabase(targetVersion int) error {
method resetDatabase (line 3206) | func (p *BoltProvider) resetDatabase() error {
method joinRuleAndActions (line 3218) | func (p *BoltProvider) joinRuleAndActions(r []byte, actionsBucket *bol...
method joinGroupAndFolders (line 3244) | func (p *BoltProvider) joinGroupAndFolders(g []byte, foldersBucket *bo...
method joinUserAndFolders (line 3267) | func (p *BoltProvider) joinUserAndFolders(u []byte, foldersBucket *bol...
method groupExistsInternal (line 3290) | func (p *BoltProvider) groupExistsInternal(name string, bucket *bolt.B...
method folderExistsInternal (line 3301) | func (p *BoltProvider) folderExistsInternal(name string, bucket *bolt....
method addFolderInternal (line 3312) | func (p *BoltProvider) addFolderInternal(folder vfs.BaseVirtualFolder,...
method removeRoleFromUser (line 3325) | func (p *BoltProvider) removeRoleFromUser(username, role string, bucke...
method addAdminToRole (line 3348) | func (p *BoltProvider) addAdminToRole(username, roleName string, bucke...
method removeAdminFromRole (line 3372) | func (p *BoltProvider) removeAdminFromRole(username, roleName string, ...
method addUserToRole (line 3403) | func (p *BoltProvider) addUserToRole(username, roleName string, bucket...
method removeUserFromRole (line 3427) | func (p *BoltProvider) removeUserFromRole(username, roleName string, b...
method addRuleToActionMapping (line 3459) | func (p *BoltProvider) addRuleToActionMapping(ruleName, actionName str...
method removeRuleFromActionMapping (line 3480) | func (p *BoltProvider) removeRuleFromActionMapping(ruleName, actionNam...
method addUserToGroupMapping (line 3508) | func (p *BoltProvider) addUserToGroupMapping(username, groupname strin...
method removeUserFromGroupMapping (line 3529) | func (p *BoltProvider) removeUserFromGroupMapping(username, groupname ...
method addAdminToGroupMapping (line 3553) | func (p *BoltProvider) addAdminToGroupMapping(username, groupname stri...
method removeAdminFromGroupMapping (line 3574) | func (p *BoltProvider) removeAdminFromGroupMapping(username, groupname...
method removeGroupFromAdminMapping (line 3598) | func (p *BoltProvider) removeGroupFromAdminMapping(groupName, adminNam...
method addRelationToFolderMapping (line 3623) | func (p *BoltProvider) addRelationToFolderMapping(folderName string, u...
method removeRelationFromFolderMapping (line 3652) | func (p *BoltProvider) removeRelationFromFolderMapping(folder vfs.Virt...
method updateUserRelations (line 3696) | func (p *BoltProvider) updateUserRelations(tx *bolt.Tx, user *User, ol...
method adminExistsInternal (line 3739) | func (p *BoltProvider) adminExistsInternal(tx *bolt.Tx, username strin...
method userExistsInternal (line 3751) | func (p *BoltProvider) userExistsInternal(tx *bolt.Tx, username string...
method deleteRelatedShares (line 3763) | func (p *BoltProvider) deleteRelatedShares(tx *bolt.Tx, username strin...
method deleteRelatedAPIKey (line 3790) | func (p *BoltProvider) deleteRelatedAPIKey(tx *bolt.Tx, username strin...
method getSharesBucket (line 3823) | func (p *BoltProvider) getSharesBucket(tx *bolt.Tx) (*bolt.Bucket, err...
method getAPIKeysBucket (line 3833) | func (p *BoltProvider) getAPIKeysBucket(tx *bolt.Tx) (*bolt.Bucket, er...
method getAdminsBucket (line 3843) | func (p *BoltProvider) getAdminsBucket(tx *bolt.Tx) (*bolt.Bucket, err...
method getUsersBucket (line 3853) | func (p *BoltProvider) getUsersBucket(tx *bolt.Tx) (*bolt.Bucket, erro...
method getGroupsBucket (line 3862) | func (p *BoltProvider) getGroupsBucket(tx *bolt.Tx) (*bolt.Bucket, err...
method getRolesBucket (line 3871) | func (p *BoltProvider) getRolesBucket(tx *bolt.Tx) (*bolt.Bucket, erro...
method getIPListsBucket (line 3880) | func (p *BoltProvider) getIPListsBucket(tx *bolt.Tx) (*bolt.Bucket, er...
method getFoldersBucket (line 3889) | func (p *BoltProvider) getFoldersBucket(tx *bolt.Tx) (*bolt.Bucket, er...
method getActionsBucket (line 3898) | func (p *BoltProvider) getActionsBucket(tx *bolt.Tx) (*bolt.Bucket, er...
method getRulesBucket (line 3907) | func (p *BoltProvider) getRulesBucket(tx *bolt.Tx) (*bolt.Bucket, erro...
function init (line 69) | func init() {
function initializeBoltProvider (line 73) | func initializeBoltProvider(basePath string) error {
function getBoltDatabaseVersion (line 3916) | func getBoltDatabaseVersion(dbHandle *bolt.DB) (schemaVersion, error) {
function updateBoltDatabaseVersion (line 3935) | func updateBoltDatabaseVersion(dbHandle *bolt.DB, version int) error {
FILE: internal/dataprovider/bolt_disabled.go
function init (line 25) | func init() {
function initializeBoltProvider (line 29) | func initializeBoltProvider(_ string) error {
FILE: internal/dataprovider/cachedpassword.go
function init (line 33) | func init() {
function CheckCachedUserPassword (line 52) | func CheckCachedUserPassword(username, password, hash string) (bool, boo...
type credentialObject (line 56) | type credentialObject struct
type credentialsCache (line 63) | type credentialsCache struct
method Add (line 70) | func (c *credentialsCache) Add(username, password, hash string) {
method Remove (line 89) | func (c *credentialsCache) Remove(username string) {
method Check (line 101) | func (c *credentialsCache) Check(username, password, hash string) (boo...
method count (line 124) | func (c *credentialsCache) count() int {
method cleanup (line 131) | func (c *credentialsCache) cleanup() {
FILE: internal/dataprovider/cacheduser.go
function init (line 31) | func init() {
function InitializeWebDAVUserCache (line 38) | func InitializeWebDAVUserCache(maxSize int) {
type CachedUser (line 46) | type CachedUser struct
method IsExpired (line 54) | func (c *CachedUser) IsExpired() bool {
type usersCache (line 61) | type usersCache struct
method updateLastLogin (line 67) | func (cache *usersCache) updateLastLogin(username string) {
method swap (line 80) | func (cache *usersCache) swap(userRef *User, plainPassword string) {
method add (line 120) | func (cache *usersCache) add(cachedUser *CachedUser) {
method remove (line 149) | func (cache *usersCache) remove(username string) {
method get (line 156) | func (cache *usersCache) get(username string) (*CachedUser, bool) {
function CacheWebDAVUser (line 168) | func CacheWebDAVUser(cachedUser *CachedUser) {
function GetCachedWebDAVUser (line 173) | func GetCachedWebDAVUser(username string) (*CachedUser, bool) {
function RemoveCachedWebDAVUser (line 178) | func RemoveCachedWebDAVUser(username string) {
FILE: internal/dataprovider/configs.go
type SFTPDConfigs (line 51) | type SFTPDConfigs struct
method isEmpty (line 59) | func (c *SFTPDConfigs) isEmpty() bool {
method GetSupportedHostKeyAlgos (line 79) | func (*SFTPDConfigs) GetSupportedHostKeyAlgos() []string {
method GetSupportedPublicKeyAlgos (line 84) | func (*SFTPDConfigs) GetSupportedPublicKeyAlgos() []string {
method GetSupportedKEXAlgos (line 89) | func (*SFTPDConfigs) GetSupportedKEXAlgos() []string {
method GetSupportedCiphers (line 94) | func (*SFTPDConfigs) GetSupportedCiphers() []string {
method GetSupportedMACs (line 99) | func (*SFTPDConfigs) GetSupportedMACs() []string {
method validate (line 103) | func (c *SFTPDConfigs) validate() error {
method getACopy (line 147) | func (c *SFTPDConfigs) getACopy() *SFTPDConfigs {
function validateSMTPSecret (line 168) | func validateSMTPSecret(secret *kms.Secret, name string) error {
type SMTPOAuth2 (line 188) | type SMTPOAuth2 struct
method validate (line 196) | func (c *SMTPOAuth2) validate() error {
method getACopy (line 224) | func (c *SMTPOAuth2) getACopy() SMTPOAuth2 {
type SMTPConfigs (line 242) | type SMTPConfigs struct
method IsEmpty (line 256) | func (c *SMTPConfigs) IsEmpty() bool {
method validate (line 260) | func (c *SMTPConfigs) validate() error {
method TryDecrypt (line 293) | func (c *SMTPConfigs) TryDecrypt() error {
method prepareForRendering (line 315) | func (c *SMTPConfigs) prepareForRendering() {
method getACopy (line 336) | func (c *SMTPConfigs) getACopy() *SMTPConfigs {
type ACMEHTTP01Challenge (line 356) | type ACMEHTTP01Challenge struct
type ACMEConfigs (line 361) | type ACMEConfigs struct
method isEmpty (line 375) | func (c *ACMEConfigs) isEmpty() bool {
method validate (line 379) | func (c *ACMEConfigs) validate() error {
method HasProtocol (line 396) | func (c *ACMEConfigs) HasProtocol(protocol string) bool {
method getACopy (line 409) | func (c *ACMEConfigs) getACopy() *ACMEConfigs {
type BrandingConfig (line 419) | type BrandingConfig struct
method isEmpty (line 428) | func (c *BrandingConfig) isEmpty() bool {
method validatePNG (line 447) | func (*BrandingConfig) validatePNG(b []byte, maxWidth, maxHeight int) ...
method validateDisclaimerURL (line 471) | func (c *BrandingConfig) validateDisclaimerURL() error {
method validate (line 491) | func (c *BrandingConfig) validate() error {
method getACopy (line 501) | func (c *BrandingConfig) getACopy() BrandingConfig {
type BrandingConfigs (line 518) | type BrandingConfigs struct
method isEmpty (line 523) | func (c *BrandingConfigs) isEmpty() bool {
method validate (line 527) | func (c *BrandingConfigs) validate() error {
method getACopy (line 534) | func (c *BrandingConfigs) getACopy() *BrandingConfigs {
type Configs (line 543) | type Configs struct
method validate (line 551) | func (c *Configs) validate() error {
method PrepareForRendering (line 578) | func (c *Configs) PrepareForRendering() {
method SetNilsToEmpty (line 597) | func (c *Configs) SetNilsToEmpty() {
method RenderAsJSON (line 622) | func (c *Configs) RenderAsJSON(reload bool) ([]byte, error) {
method getACopy (line 636) | func (c *Configs) getACopy() Configs {
FILE: internal/dataprovider/dataprovider.go
constant SQLiteDataProviderName (line 80) | SQLiteDataProviderName = "sqlite"
constant PGSQLDataProviderName (line 82) | PGSQLDataProviderName = "postgresql"
constant MySQLDataProviderName (line 84) | MySQLDataProviderName = "mysql"
constant BoltDataProviderName (line 86) | BoltDataProviderName = "bolt"
constant MemoryDataProviderName (line 88) | MemoryDataProviderName = "memory"
constant CockroachDataProviderName (line 90) | CockroachDataProviderName = "cockroachdb"
constant DumpVersion (line 93) | DumpVersion = 17
constant argonPwdPrefix (line 95) | argonPwdPrefix = "$argon2id$"
constant bcryptPwdPrefix (line 96) | bcryptPwdPrefix = "$2a$"
constant pbkdf2SHA1Prefix (line 97) | pbkdf2SHA1Prefix = "$pbkdf2-sha1$"
constant pbkdf2SHA256Prefix (line 98) | pbkdf2SHA256Prefix = "$pbkdf2-sha256$"
constant pbkdf2SHA512Prefix (line 99) | pbkdf2SHA512Prefix = "$pbkdf2-sha512$"
constant pbkdf2SHA256B64SaltPrefix (line 100) | pbkdf2SHA256B64SaltPrefix = "$pbkdf2-b64salt-sha256$"
constant md5cryptPwdPrefix (line 101) | md5cryptPwdPrefix = "$1$"
constant md5cryptApr1PwdPrefix (line 102) | md5cryptApr1PwdPrefix = "$apr1$"
constant sha256cryptPwdPrefix (line 103) | sha256cryptPwdPrefix = "$5$"
constant sha512cryptPwdPrefix (line 104) | sha512cryptPwdPrefix = "$6$"
constant yescryptPwdPrefix (line 105) | yescryptPwdPrefix = "$y$"
constant md5DigestPwdPrefix (line 106) | md5DigestPwdPrefix = "{MD5}"
constant sha256DigestPwdPrefix (line 107) | sha256DigestPwdPrefix = "{SHA256}"
constant sha512DigestPwdPrefix (line 108) | sha512DigestPwdPrefix = "{SHA512}"
constant trackQuotaDisabledError (line 109) | trackQuotaDisabledError = "please enable track_quota in your configura...
constant operationAdd (line 110) | operationAdd = "add"
constant operationUpdate (line 111) | operationUpdate = "update"
constant operationDelete (line 112) | operationDelete = "delete"
constant sqlPrefixValidChars (line 113) | sqlPrefixValidChars = "abcdefghijklmnopqrstuvwxyz_0123456789"
constant maxHookResponseSize (line 114) | maxHookResponseSize = 1048576
constant HashingAlgoBcrypt (line 120) | HashingAlgoBcrypt = "bcrypt"
constant HashingAlgoArgon2ID (line 121) | HashingAlgoArgon2ID = "argon2id"
constant OrderASC (line 126) | OrderASC = "ASC"
constant OrderDESC (line 127) | OrderDESC = "DESC"
constant protocolSSH (line 131) | protocolSSH = "SSH"
constant protocolFTP (line 132) | protocolFTP = "FTP"
constant protocolWebDAV (line 133) | protocolWebDAV = "DAV"
constant protocolHTTP (line 134) | protocolHTTP = "HTTP"
constant DumpScopeUsers (line 139) | DumpScopeUsers = "users"
constant DumpScopeFolders (line 140) | DumpScopeFolders = "folders"
constant DumpScopeGroups (line 141) | DumpScopeGroups = "groups"
constant DumpScopeAdmins (line 142) | DumpScopeAdmins = "admins"
constant DumpScopeAPIKeys (line 143) | DumpScopeAPIKeys = "api_keys"
constant DumpScopeShares (line 144) | DumpScopeShares = "shares"
constant DumpScopeActions (line 145) | DumpScopeActions = "actions"
constant DumpScopeRules (line 146) | DumpScopeRules = "rules"
constant DumpScopeRoles (line 147) | DumpScopeRoles = "roles"
constant DumpScopeIPLists (line 148) | DumpScopeIPLists = "ip_lists"
constant DumpScopeConfigs (line 149) | DumpScopeConfigs = "configs"
constant fieldUsername (line 153) | fieldUsername = 1
constant fieldName (line 154) | fieldName = 2
constant fieldIPNet (line 155) | fieldIPNet = 3
function initSQLTables (line 242) | func initSQLTables() {
type FnReloadRules (line 270) | type FnReloadRules
type FnRemoveRule (line 273) | type FnRemoveRule
type FnHandleRuleForProviderEvent (line 276) | type FnHandleRuleForProviderEvent
function SetEventRulesCallbacks (line 279) | func SetEventRulesCallbacks(reload FnReloadRules, remove FnRemoveRule, h...
type schemaVersion (line 285) | type schemaVersion struct
type BcryptOptions (line 290) | type BcryptOptions struct
type Argon2Options (line 295) | type Argon2Options struct
type PasswordHashing (line 302) | type PasswordHashing struct
type PasswordValidationRules (line 310) | type PasswordValidationRules struct
type PasswordValidation (line 319) | type PasswordValidation struct
type wrappedFolder (line 326) | type wrappedFolder struct
method RenderAsJSON (line 330) | func (w *wrappedFolder) RenderAsJSON(reload bool) ([]byte, error) {
type ObjectsActions (line 345) | type ObjectsActions struct
type ProviderStatus (line 355) | type ProviderStatus struct
type Config (line 362) | type Config struct
method GetShared (line 524) | func (c *Config) GetShared() int {
method convertName (line 531) | func (c *Config) convertName(name string) string {
method IsDefenderSupported (line 546) | func (c *Config) IsDefenderSupported() bool {
method requireCustomTLSForMySQL (line 555) | func (c *Config) requireCustomTLSForMySQL() bool {
method doBackup (line 569) | func (c *Config) doBackup() (string, error) {
function SetTZ (line 598) | func SetTZ(val string) {
function UseLocalTime (line 603) | func UseLocalTime() bool {
function ExecuteBackup (line 608) | func ExecuteBackup() (string, error) {
function ConvertName (line 613) | func ConvertName(name string) string {
type ActiveTransfer (line 618) | type ActiveTransfer struct
type TransferQuota (line 633) | type TransferQuota struct
method HasSizeLimits (line 643) | func (q *TransferQuota) HasSizeLimits() bool {
method HasUploadSpace (line 648) | func (q *TransferQuota) HasUploadSpace() bool {
method HasDownloadSpace (line 659) | func (q *TransferQuota) HasDownloadSpace() bool {
type DefenderEntry (line 670) | type DefenderEntry struct
method GetID (line 678) | func (d *DefenderEntry) GetID() string {
method GetBanTime (line 683) | func (d *DefenderEntry) GetBanTime() string {
method MarshalJSON (line 691) | func (d *DefenderEntry) MarshalJSON() ([]byte, error) {
type BackupData (line 706) | type BackupData struct
method HasFolder (line 722) | func (d *BackupData) HasFolder(name string) bool {
type checkPasswordRequest (line 731) | type checkPasswordRequest struct
type checkPasswordResponse (line 738) | type checkPasswordResponse struct
function GetQuotaTracking (line 747) | func GetQuotaTracking() int {
function HasUsersBaseDir (line 752) | func HasUsersBaseDir() bool {
type Provider (line 757) | type Provider interface
function SetAllowSelfConnections (line 883) | func SetAllowSelfConnections(value int) {
function SetTempPath (line 888) | func SetTempPath(fsPath string) {
function checkSharedMode (line 892) | func checkSharedMode() {
function Initialize (line 900) | func Initialize(cnf Config, basePath string, checkAdmins bool) error {
function checkDatabase (line 944) | func checkDatabase(checkAdmins bool) error {
function validateHooks (line 974) | func validateHooks() error {
function GetBackupsPath (line 1004) | func GetBackupsPath() string {
function GetProviderFromValue (line 1010) | func GetProviderFromValue(value string) sdk.FilesystemProvider {
function initializeHashingAlgo (line 1022) | func initializeHashingAlgo(cnf *Config) error {
function validateSQLTablesPrefix (line 1046) | func validateSQLTablesPrefix() error {
function checkDefaultAdmin (line 1092) | func checkDefaultAdmin() error {
function InitializeDatabase (line 1110) | func InitializeDatabase(cnf Config, basePath string) error {
function RevertDatabase (line 1129) | func RevertDatabase(cnf Config, basePath string, targetVersion int) error {
function ResetDatabase (line 1144) | func ResetDatabase(cnf Config, basePath string) error {
function CheckAdminAndPass (line 1154) | func CheckAdminAndPass(username, password, ip string) (Admin, error) {
function CheckCachedUserCredentials (line 1160) | func CheckCachedUserCredentials(user *CachedUser, password, ip, loginMet...
function CheckCompositeCredentials (line 1205) | func CheckCompositeCredentials(username, password, ip, loginMethod, prot...
function CheckUserBeforeTLSAuth (line 1245) | func CheckUserBeforeTLSAuth(username, ip, protocol string, tlsCert *x509...
function CheckUserAndTLSCert (line 1281) | func CheckUserAndTLSCert(username, ip, protocol string, tlsCert *x509.Ce...
function CheckUserAndPass (line 1308) | func CheckUserAndPass(username, password, ip, protocol string) (User, er...
function CheckUserAndPubKey (line 1335) | func CheckUserAndPubKey(username string, pubKey []byte, ip, protocol str...
function CheckKeyboardInteractiveAuth (line 1363) | func CheckKeyboardInteractiveAuth(username, authHook string, client ssh....
function GetFTPPreAuthUser (line 1388) | func GetFTPPreAuthUser(username, ip string) (User, error) {
function GetUserAfterIDPAuth (line 1407) | func GetUserAfterIDPAuth(username, ip, protocol string, oidcTokenFields ...
function GetDefenderHosts (line 1424) | func GetDefenderHosts(from int64, limit int) ([]DefenderEntry, error) {
function GetDefenderHostByIP (line 1429) | func GetDefenderHostByIP(ip string, from int64) (DefenderEntry, error) {
function IsDefenderHostBanned (line 1434) | func IsDefenderHostBanned(ip string) (DefenderEntry, error) {
function UpdateDefenderBanTime (line 1439) | func UpdateDefenderBanTime(ip string, minutes int) error {
function DeleteDefenderHost (line 1444) | func DeleteDefenderHost(ip string) error {
function AddDefenderEvent (line 1450) | func AddDefenderEvent(ip string, score int, from int64) (DefenderEntry, ...
function SetDefenderBanTime (line 1458) | func SetDefenderBanTime(ip string, banTime int64) error {
function CleanupDefender (line 1463) | func CleanupDefender(from int64) error {
function UpdateShareLastUse (line 1468) | func UpdateShareLastUse(share *Share, numTokens int) error {
function UpdateAPIKeyLastUse (line 1473) | func UpdateAPIKeyLastUse(apiKey *APIKey) error {
function UpdateLastLogin (line 1483) | func UpdateLastLogin(user *User) {
function UpdateAdminLastLogin (line 1497) | func UpdateAdminLastLogin(admin *Admin) {
function UpdateUserQuota (line 1505) | func UpdateUserQuota(user *User, filesAdd int, sizeAdd int64, reset bool...
function UpdateUserFolderQuota (line 1525) | func UpdateUserFolderQuota(folder *vfs.VirtualFolder, user *User, filesA...
function UpdateVirtualFolderQuota (line 1535) | func UpdateVirtualFolderQuota(vfolder *vfs.BaseVirtualFolder, filesAdd i...
function UpdateUserTransferQuota (line 1554) | func UpdateUserTransferQuota(user *User, uploadSize, downloadSize int64,...
function UpdateUserTransferTimestamps (line 1574) | func UpdateUserTransferTimestamps(username string, isUpload bool) error {
function GetUsedQuota (line 1590) | func GetUsedQuota(username string) (int, int64, int64, int64, error) {
function GetUsedVirtualFolderQuota (line 1606) | func GetUsedVirtualFolderQuota(name string) (int, int64, error) {
function GetConfigs (line 1619) | func GetConfigs() (Configs, error) {
function UpdateConfigs (line 1624) | func UpdateConfigs(configs *Configs, executor, ipAddress, role string) e...
function AddShare (line 1638) | func AddShare(share *Share, executor, ipAddress, role string) error {
function UpdateShare (line 1647) | func UpdateShare(share *Share, executor, ipAddress, role string) error {
function DeleteShare (line 1656) | func DeleteShare(shareID string, executor, ipAddress, role string) error {
function ShareExists (line 1669) | func ShareExists(shareID, username string) (Share, error) {
function AddIPListEntry (line 1677) | func AddIPListEntry(entry *IPListEntry, executor, ipAddress, executorRol...
function UpdateIPListEntry (line 1689) | func UpdateIPListEntry(entry *IPListEntry, executor, ipAddress, executor...
function DeleteIPListEntry (line 1701) | func DeleteIPListEntry(ipOrNet string, listType IPListType, executor, ip...
function IPListEntryExists (line 1717) | func IPListEntryExists(ipOrNet string, listType IPListType) (IPListEntry...
function GetIPListEntries (line 1722) | func GetIPListEntries(listType IPListType, filter, from, order string, l...
function AddRole (line 1730) | func AddRole(role *Role, executor, ipAddress, executorRole string) error {
function UpdateRole (line 1740) | func UpdateRole(role *Role, executor, ipAddress, executorRole string) er...
function DeleteRole (line 1749) | func DeleteRole(name string, executor, ipAddress, executorRole string) e...
function RoleExists (line 1775) | func RoleExists(name string) (Role, error) {
function AddGroup (line 1781) | func AddGroup(group *Group, executor, ipAddress, role string) error {
function UpdateGroup (line 1791) | func UpdateGroup(group *Group, users []string, executor, ipAddress, role...
function DeleteGroup (line 1809) | func DeleteGroup(name string, executor, ipAddress, role string) error {
function GroupExists (line 1835) | func GroupExists(name string) (Group, error) {
function AddAPIKey (line 1841) | func AddAPIKey(apiKey *APIKey, executor, ipAddress, role string) error {
function UpdateAPIKey (line 1850) | func UpdateAPIKey(apiKey *APIKey, executor, ipAddress, role string) error {
function DeleteAPIKey (line 1859) | func DeleteAPIKey(keyID string, executor, ipAddress, role string) error {
function APIKeyExists (line 1873) | func APIKeyExists(keyID string) (APIKey, error) {
function GetEventActions (line 1881) | func GetEventActions(limit, offset int, order string, minimal bool) ([]B...
function EventActionExists (line 1886) | func EventActionExists(name string) (BaseEventAction, error) {
function AddEventAction (line 1892) | func AddEventAction(action *BaseEventAction, executor, ipAddress, role s...
function UpdateEventAction (line 1902) | func UpdateEventAction(action *BaseEventAction, executor, ipAddress, rol...
function DeleteEventAction (line 1914) | func DeleteEventAction(name string, executor, ipAddress, role string) er...
function GetEventRules (line 1932) | func GetEventRules(limit, offset int, order string) ([]EventRule, error) {
function GetRecentlyUpdatedRules (line 1937) | func GetRecentlyUpdatedRules(after int64) ([]EventRule, error) {
function EventRuleExists (line 1942) | func EventRuleExists(name string) (EventRule, error) {
function AddEventRule (line 1948) | func AddEventRule(rule *EventRule, executor, ipAddress, role string) err...
function UpdateEventRule (line 1961) | func UpdateEventRule(rule *EventRule, executor, ipAddress, role string) ...
function DeleteEventRule (line 1973) | func DeleteEventRule(name string, executor, ipAddress, role string) error {
function RemoveEventRule (line 1990) | func RemoveEventRule(rule EventRule) error {
function GetTaskByName (line 1995) | func GetTaskByName(name string) (Task, error) {
function AddTask (line 2000) | func AddTask(name string) error {
function UpdateTask (line 2005) | func UpdateTask(name string, version int64) error {
function UpdateTaskTimestamp (line 2010) | func UpdateTaskTimestamp(name string) error {
function GetNodes (line 2015) | func GetNodes() ([]Node, error) {
function GetNodeByName (line 2027) | func GetNodeByName(name string) (Node, error) {
function HasAdmin (line 2039) | func HasAdmin() bool {
function AddAdmin (line 2044) | func AddAdmin(admin *Admin, executor, ipAddress, role string) error {
function UpdateAdmin (line 2059) | func UpdateAdmin(admin *Admin, executor, ipAddress, role string) error {
function DeleteAdmin (line 2068) | func DeleteAdmin(username, executor, ipAddress, role string) error {
function AdminExists (line 2083) | func AdminExists(username string) (Admin, error) {
function UserExists (line 2089) | func UserExists(username, role string) (User, error) {
function GetAdminSignature (line 2096) | func GetAdminSignature(username string) (string, error) {
function GetUserSignature (line 2103) | func GetUserSignature(username string) (string, error) {
function GetUserWithGroupSettings (line 2110) | func GetUserWithGroupSettings(username, role string) (User, error) {
function GetUserVariants (line 2122) | func GetUserVariants(username, role string) (User, User, error) {
function AddUser (line 2134) | func AddUser(user *User, executor, ipAddress, role string) error {
function UpdateUserPassword (line 2144) | func UpdateUserPassword(username, plainPwd, executor, ipAddress, role st...
function UpdateUser (line 2167) | func UpdateUser(user *User, executor, ipAddress, role string) error {
function DeleteUser (line 2180) | func DeleteUser(username, executor, ipAddress, role string) error {
function AddActiveTransfer (line 2197) | func AddActiveTransfer(transfer ActiveTransfer) {
function UpdateActiveTransferSizes (line 2205) | func UpdateActiveTransferSizes(ulSize, dlSize, transferID int64, connect...
function RemoveActiveTransfer (line 2213) | func RemoveActiveTransfer(transferID int64, connectionID string) {
function CleanupActiveTransfers (line 2221) | func CleanupActiveTransfers(before time.Time) error {
function GetActiveTransfers (line 2232) | func GetActiveTransfers(from time.Time) ([]ActiveTransfer, error) {
function AddSharedSession (line 2237) | func AddSharedSession(session Session) error {
function DeleteSharedSession (line 2247) | func DeleteSharedSession(key string, sessionType SessionType) error {
function GetSharedSession (line 2256) | func GetSharedSession(key string, sessionType SessionType) (Session, err...
function CleanupSharedSessions (line 2262) | func CleanupSharedSessions(sessionType SessionType, before time.Time) er...
function ReloadConfig (line 2275) | func ReloadConfig() error {
function GetShares (line 2280) | func GetShares(limit, offset int, order, username string) ([]Share, erro...
function GetAPIKeys (line 2285) | func GetAPIKeys(limit, offset int, order string) ([]APIKey, error) {
function GetAdmins (line 2290) | func GetAdmins(limit, offset int, order string) ([]Admin, error) {
function GetRoles (line 2295) | func GetRoles(limit, offset int, order string, minimal bool) ([]Role, er...
function GetGroups (line 2300) | func GetGroups(limit, offset int, order string, minimal bool) ([]Group, ...
function GetUsers (line 2305) | func GetUsers(limit, offset int, order, role string) ([]User, error) {
function GetUsersForQuotaCheck (line 2310) | func GetUsersForQuotaCheck(toFetch map[string]bool) ([]User, error) {
function AddFolder (line 2315) | func AddFolder(folder *vfs.BaseVirtualFolder, executor, ipAddress, role ...
function UpdateFolder (line 2325) | func UpdateFolder(folder *vfs.BaseVirtualFolder, users []string, groups ...
function DeleteFolder (line 2351) | func DeleteFolder(folderName, executor, ipAddress, role string) error {
function GetFolderByName (line 2382) | func GetFolderByName(name string) (vfs.BaseVirtualFolder, error) {
function GetFolders (line 2388) | func GetFolders(limit, offset int, order string, minimal bool) ([]vfs.Ba...
function dumpUsers (line 2392) | func dumpUsers(data *BackupData, scopes []string) error {
function dumpFolders (line 2403) | func dumpFolders(data *BackupData, scopes []string) error {
function dumpGroups (line 2414) | func dumpGroups(data *BackupData, scopes []string) error {
function dumpAdmins (line 2425) | func dumpAdmins(data *BackupData, scopes []string) error {
function dumpAPIKeys (line 2436) | func dumpAPIKeys(data *BackupData, scopes []string) error {
function dumpShares (line 2447) | func dumpShares(data *BackupData, scopes []string) error {
function dumpActions (line 2458) | func dumpActions(data *BackupData, scopes []string) error {
function dumpRules (line 2469) | func dumpRules(data *BackupData, scopes []string) error {
function dumpRoles (line 2480) | func dumpRoles(data *BackupData, scopes []string) error {
function dumpIPLists (line 2491) | func dumpIPLists(data *BackupData, scopes []string) error {
function dumpConfigs (line 2502) | func dumpConfigs(data *BackupData, scopes []string) error {
function DumpData (line 2515) | func DumpData(scopes []string) (BackupData, error) {
function ParseDumpData (line 2557) | func ParseDumpData(data []byte) (BackupData, error) {
function GetProviderConfig (line 2575) | func GetProviderConfig() Config {
function GetProviderStatus (line 2580) | func GetProviderStatus() ProviderStatus {
function Close (line 2597) | func Close() error {
function createProvider (line 2602) | func createProvider(basePath string) error {
function copyBaseUserFilters (line 2629) | func copyBaseUserFilters(in sdk.BaseUserFilters) sdk.BaseUserFilters {
function buildUserHomeDir (line 2685) | func buildUserHomeDir(user *User) {
function validateFolderQuotaLimits (line 2704) | func validateFolderQuotaLimits(folder vfs.VirtualFolder) error {
function validateUserGroups (line 2727) | func validateUserGroups(user *User) error {
function validateAssociatedVirtualFolders (line 2758) | func validateAssociatedVirtualFolders(vfolders []vfs.VirtualFolder) ([]v...
function validateUserTOTPConfig (line 2808) | func validateUserTOTPConfig(c *UserTOTPConfig, username string) error {
function validateUserRecoveryCodes (line 2841) | func validateUserRecoveryCodes(user *User) error {
function validateUserPermissions (line 2857) | func validateUserPermissions(permsToCheck map[string][]string) (map[stri...
function validatePermissions (line 2891) | func validatePermissions(user *User) error {
function validatePublicKeys (line 2906) | func validatePublicKeys(user *User) error {
function validateFiltersPatternExtensions (line 2949) | func validateFiltersPatternExtensions(baseFilters *sdk.BaseUserFilters) ...
function checkEmptyFiltersStruct (line 3008) | func checkEmptyFiltersStruct(filters *sdk.BaseUserFilters) {
function validateIPFilters (line 3023) | func validateIPFilters(filters *sdk.BaseUserFilters) error {
function validateBandwidthLimit (line 3041) | func validateBandwidthLimit(bl sdk.BandwidthLimit) error {
function validateBandwidthLimitsFilter (line 3054) | func validateBandwidthLimitsFilter(filters *sdk.BaseUserFilters) error {
function updateFiltersValues (line 3069) | func updateFiltersValues(filters *sdk.BaseUserFilters) {
function validateFilterProtocols (line 3078) | func validateFilterProtocols(filters *sdk.BaseUserFilters) error {
function validateTLSCerts (line 3096) | func validateTLSCerts(certs []string) ([]string, error) {
function validateBaseFilters (line 3133) | func validateBaseFilters(filters *sdk.BaseUserFilters) error {
function isTimeOfDayValid (line 3183) | func isTimeOfDayValid(value string) bool {
function validateAccessTimeFilters (line 3208) | func validateAccessTimeFilters(filters *sdk.BaseUserFilters) error {
function validateCombinedUserFilters (line 3230) | func validateCombinedUserFilters(user *User) error {
function validateEmails (line 3252) | func validateEmails(user *User) error {
function validateBaseParams (line 3270) | func validateBaseParams(user *User) error {
function hashPlainPassword (line 3326) | func hashPlainPassword(plainPwd string) (string, error) {
function createUserPasswordHash (line 3341) | func createUserPasswordHash(user *User) error {
function ValidateFolder (line 3373) | func ValidateFolder(folder *vfs.BaseVirtualFolder) error {
function ValidateUser (line 3406) | func ValidateUser(user *User) error {
function isPasswordOK (line 3450) | func isPasswordOK(user *User, password string) (bool, error) {
function convertUserPassword (line 3499) | func convertUserPassword(username, plainPwd string) {
function checkUserAndTLSCertificate (line 3511) | func checkUserAndTLSCertificate(user *User, protocol string, tlsCert *x5...
function checkUserAndPass (line 3540) | func checkUserAndPass(user *User, password, ip, protocol string) (User, ...
function checkUserPasscode (line 3595) | func checkUserPasscode(user *User, password, protocol string) (string, e...
function checkUserAndPubKey (line 3629) | func checkUserAndPubKey(user *User, pubKey []byte, isSSHCert bool) (User...
function compareDigestPasswordAndHash (line 3657) | func compareDigestPasswordAndHash(user *User, password string) bool {
function compareUnixPasswordAndHash (line 3676) | func compareUnixPasswordAndHash(user *User, password string) (bool, erro...
function comparePbkdf2PasswordAndHash (line 3698) | func comparePbkdf2PasswordAndHash(password, hashedPassword string) (bool...
function getSSLMode (line 3734) | func getSSLMode() string {
function terminateInteractiveAuthProgram (line 3769) | func terminateInteractiveAuthProgram(cmd *exec.Cmd, isFinished bool) {
function sendKeyboardAuthHTTPReq (line 3780) | func sendKeyboardAuthHTTPReq(url string, request *plugin.KeyboardAuthReq...
function doBuiltinKeyboardInteractiveAuth (line 3800) | func doBuiltinKeyboardInteractiveAuth(user *User, client ssh.KeyboardInt...
function checkKeyboardInteractiveSecondFactor (line 3823) | func checkKeyboardInteractiveSecondFactor(user *User, client ssh.Keyboar...
function executeKeyboardInteractivePlugin (line 3850) | func executeKeyboardInteractivePlugin(user *User, client ssh.KeyboardInt...
function executeKeyboardInteractiveHTTPHook (line 3891) | func executeKeyboardInteractiveHTTPHook(user *User, authHook string, cli...
function getKeyboardInteractiveAnswers (line 3932) | func getKeyboardInteractiveAnswers(client ssh.KeyboardInteractiveChallen...
function handleProgramInteractiveQuestions (line 3979) | func handleProgramInteractiveQuestions(client ssh.KeyboardInteractiveCha...
function executeKeyboardInteractiveProgram (line 4000) | func executeKeyboardInteractiveProgram(user *User, authHook string, clie...
function doKeyboardInteractiveAuth (line 4061) | func doKeyboardInteractiveAuth(user *User, authHook string, client ssh.K...
function isCheckPasswordHookDefined (line 4100) | func isCheckPasswordHookDefined(protocol string) bool {
function getPasswordHookResponse (line 4119) | func getPasswordHookResponse(username, password, ip, protocol string) ([...
function executeCheckPasswordHook (line 4157) | func executeCheckPasswordHook(username, password, ip, protocol string) (...
function getPreLoginHookResponse (line 4175) | func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSO...
function executePreLoginHook (line 4218) | func executePreLoginHook(username, loginMethod, ip, protocol string, oid...
function ExecutePostLoginHook (line 4281) | func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, ...
function getExternalAuthResponse (line 4352) | func getExternalAuthResponse(username, password, pkey, keyboardInteracti...
function updateUserFromExtAuthResponse (line 4420) | func updateUserFromExtAuthResponse(user *User, password, pkey string) {
function checkPasswordAfterEmptyExtAuthResponse (line 4430) | func checkPasswordAfterEmptyExtAuthResponse(user *User, plainPwd, protoc...
function doExternalAuth (line 4459) | func doExternalAuth(username, password string, pubKey []byte, keyboardIn...
function doPluginAuth (line 4541) | func doPluginAuth(username, password string, pubKey []byte, ip, protocol...
function updateUserAfterExternalAuth (line 4613) | func updateUserAfterExternalAuth(user *User) (User, error) {
function getUserForHook (line 4620) | func getUserForHook(username string, oidcTokenFields *map[string]any) (U...
function getUserAndJSONForHook (line 4643) | func getUserAndJSONForHook(username string, oidcTokenFields *map[string]...
function isLastActivityRecent (line 4655) | func isLastActivityRecent(lastActivity int64, minDelay time.Duration) bo...
function isExternalAuthConfigured (line 4664) | func isExternalAuthConfigured(loginMethod string) bool {
function replaceTemplateVars (line 4691) | func replaceTemplateVars(input string) string {
function updateEventActionPlaceholders (line 4723) | func updateEventActionPlaceholders(actions []BaseEventAction) ([]BaseEve...
function getConfigPath (line 4744) | func getConfigPath(name, configDir string) string {
function checkReservedUsernames (line 4754) | func checkReservedUsernames(username string) error {
function errSchemaVersionTooOld (line 4761) | func errSchemaVersionTooOld(version int) error {
function getCmdOutput (line 4765) | func getCmdOutput(cmd *exec.Cmd, sender string) ([]byte, error) {
function providerLog (line 4796) | func providerLog(level logger.LogLevel, format string, v ...any) {
FILE: internal/dataprovider/eventrule.go
constant ActionTypeHTTP (line 39) | ActionTypeHTTP = iota + 1
constant ActionTypeCommand (line 40) | ActionTypeCommand
constant ActionTypeEmail (line 41) | ActionTypeEmail
constant ActionTypeBackup (line 42) | ActionTypeBackup
constant ActionTypeUserQuotaReset (line 43) | ActionTypeUserQuotaReset
constant ActionTypeFolderQuotaReset (line 44) | ActionTypeFolderQuotaReset
constant ActionTypeTransferQuotaReset (line 45) | ActionTypeTransferQuotaReset
constant ActionTypeDataRetentionCheck (line 46) | ActionTypeDataRetentionCheck
constant ActionTypeFilesystem (line 47) | ActionTypeFilesystem
constant actionTypeReserved (line 48) | actionTypeReserved
constant ActionTypePasswordExpirationCheck (line 49) | ActionTypePasswordExpirationCheck
constant ActionTypeUserExpirationCheck (line 50) | ActionTypeUserExpirationCheck
constant ActionTypeIDPAccountCheck (line 51) | ActionTypeIDPAccountCheck
constant ActionTypeUserInactivityCheck (line 52) | ActionTypeUserInactivityCheck
constant ActionTypeRotateLogs (line 53) | ActionTypeRotateLogs
function isActionTypeValid (line 66) | func isActionTypeValid(action int) bool {
function getActionTypeAsString (line 70) | func getActionTypeAsString(action int) string {
constant EventTriggerFsEvent (line 106) | EventTriggerFsEvent = iota + 1
constant EventTriggerProviderEvent (line 108) | EventTriggerProviderEvent
constant EventTriggerSchedule (line 109) | EventTriggerSchedule
constant EventTriggerIPBlocked (line 110) | EventTriggerIPBlocked
constant EventTriggerCertificate (line 111) | EventTriggerCertificate
constant EventTriggerOnDemand (line 112) | EventTriggerOnDemand
constant EventTriggerIDPLogin (line 113) | EventTriggerIDPLogin
function isEventTriggerValid (line 121) | func isEventTriggerValid(trigger int) bool {
function getTriggerTypeAsString (line 125) | func getTriggerTypeAsString(trigger int) string {
constant IDPLoginAny (line 146) | IDPLoginAny = iota
constant IDPLoginUser (line 147) | IDPLoginUser
constant IDPLoginAdmin (line 148) | IDPLoginAdmin
constant FilesystemActionRename (line 157) | FilesystemActionRename = iota + 1
constant FilesystemActionDelete (line 158) | FilesystemActionDelete
constant FilesystemActionMkdirs (line 159) | FilesystemActionMkdirs
constant FilesystemActionExist (line 160) | FilesystemActionExist
constant FilesystemActionCompress (line 161) | FilesystemActionCompress
constant FilesystemActionCopy (line 162) | FilesystemActionCopy
constant RetentionReportPlaceHolder (line 167) | RetentionReportPlaceHolder = "{{RetentionReports}}"
function isFilesystemActionValid (line 175) | func isFilesystemActionValid(value int) bool {
function getFsActionTypeAsString (line 179) | func getFsActionTypeAsString(value int) string {
function init (line 222) | func init() {
type EnumMapping (line 244) | type EnumMapping struct
type KeyValue (line 250) | type KeyValue struct
method isNotValid (line 255) | func (k *KeyValue) isNotValid() bool {
type HTTPPart (line 260) | type HTTPPart struct
method validate (line 268) | func (p *HTTPPart) validate() error {
type EventActionHTTPConfig (line 294) | type EventActionHTTPConfig struct
method HasJSONBody (line 308) | func (c *EventActionHTTPConfig) HasJSONBody() bool {
method isTimeoutNotValid (line 317) | func (c *EventActionHTTPConfig) isTimeoutNotValid() bool {
method validateMultiparts (line 324) | func (c *EventActionHTTPConfig) validateMultiparts() error {
method validate (line 356) | func (c *EventActionHTTPConfig) validate(additionalData string) error {
method GetContext (line 399) | func (c *EventActionHTTPConfig) GetContext() (context.Context, context...
method HasObjectData (line 407) | func (c *EventActionHTTPConfig) HasObjectData() bool {
method HasMultipartFiles (line 420) | func (c *EventActionHTTPConfig) HasMultipartFiles() bool {
method TryDecryptPassword (line 430) | func (c *EventActionHTTPConfig) TryDecryptPassword() error {
method GetHTTPClient (line 440) | func (c *EventActionHTTPConfig) GetHTTPClient() *http.Client {
function IsActionCommandAllowed (line 457) | func IsActionCommandAllowed(cmd string) bool {
type EventActionCommandConfig (line 462) | type EventActionCommandConfig struct
method validate (line 469) | func (c *EventActionCommandConfig) validate() error {
method GetArgumentsAsString (line 500) | func (c EventActionCommandConfig) GetArgumentsAsString() string {
type EventActionEmailConfig (line 505) | type EventActionEmailConfig struct
method GetRecipientsAsString (line 515) | func (c EventActionEmailConfig) GetRecipientsAsString() string {
method GetBccAsString (line 520) | func (c EventActionEmailConfig) GetBccAsString() string {
method GetAttachmentsAsString (line 525) | func (c EventActionEmailConfig) GetAttachmentsAsString() string {
method hasFilesAttachments (line 529) | func (c *EventActionEmailConfig) hasFilesAttachments() bool {
method validate (line 538) | func (c *EventActionEmailConfig) validate() error {
type FolderRetention (line 588) | type FolderRetention struct
method Validate (line 602) | func (f *FolderRetention) Validate() error {
type EventActionDataRetentionConfig (line 612) | type EventActionDataRetentionConfig struct
method validate (line 616) | func (c *EventActionDataRetentionConfig) validate() error {
type EventActionFsCompress (line 645) | type EventActionFsCompress struct
method validate (line 652) | func (c *EventActionFsCompress) validate() error {
type RenameConfig (line 675) | type RenameConfig struct
type EventActionFilesystemConfig (line 684) | type EventActionFilesystemConfig struct
method GetDeletesAsString (line 703) | func (c EventActionFilesystemConfig) GetDeletesAsString() string {
method GetMkDirsAsString (line 709) | func (c EventActionFilesystemConfig) GetMkDirsAsString() string {
method GetExistAsString (line 715) | func (c EventActionFilesystemConfig) GetExistAsString() string {
method GetCompressPathsAsString (line 721) | func (c EventActionFilesystemConfig) GetCompressPathsAsString() string {
method validateRenames (line 725) | func (c *EventActionFilesystemConfig) validateRenames() error {
method validateCopy (line 760) | func (c *EventActionFilesystemConfig) validateCopy() error {
method validateDeletes (line 798) | func (c *EventActionFilesystemConfig) validateDeletes() error {
method validateMkdirs (line 813) | func (c *EventActionFilesystemConfig) validateMkdirs() error {
method validateExist (line 828) | func (c *EventActionFilesystemConfig) validateExist() error {
method validate (line 843) | func (c *EventActionFilesystemConfig) validate() error {
method getACopy (line 906) | func (c *EventActionFilesystemConfig) getACopy() EventActionFilesystem...
type EventActionPasswordExpiration (line 931) | type EventActionPasswordExpiration struct
method validate (line 937) | func (c *EventActionPasswordExpiration) validate() error {
type EventActionUserInactivity (line 945) | type EventActionUserInactivity struct
method validate (line 952) | func (c *EventActionUserInactivity) validate() error {
type EventActionIDPAccountCheck (line 977) | type EventActionIDPAccountCheck struct
method validate (line 984) | func (c *EventActionIDPAccountCheck) validate() error {
type BaseEventActionOptions (line 998) | type BaseEventActionOptions struct
method getACopy (line 1009) | func (o *BaseEventActionOptions) getACopy() BaseEventActionOptions {
method SetEmptySecretsIfNil (line 1084) | func (o *BaseEventActionOptions) SetEmptySecretsIfNil() {
method setNilSecretsIfEmpty (line 1090) | func (o *BaseEventActionOptions) setNilSecretsIfEmpty() {
method hideConfidentialData (line 1096) | func (o *BaseEventActionOptions) hideConfidentialData() {
method validate (line 1102) | func (o *BaseEventActionOptions) validate(action int, name string) err...
type BaseEventAction (line 1191) | type BaseEventAction struct
method getACopy (line 1206) | func (a *BaseEventAction) getACopy() BaseEventAction {
method GetTypeAsString (line 1220) | func (a *BaseEventAction) GetTypeAsString() string {
method GetRulesAsString (line 1225) | func (a *BaseEventAction) GetRulesAsString() string {
method PrepareForRendering (line 1232) | func (a *BaseEventAction) PrepareForRendering() {
method RenderAsJSON (line 1238) | func (a *BaseEventAction) RenderAsJSON(reload bool) ([]byte, error) {
method validate (line 1252) | func (a *BaseEventAction) validate() error {
type EventActionOptions (line 1272) | type EventActionOptions struct
type EventAction (line 1279) | type EventAction struct
method getACopy (line 1286) | func (a *EventAction) getACopy() EventAction {
method validateAssociation (line 1298) | func (a *EventAction) validateAssociation(trigger int, fsEvents []stri...
type ConditionPattern (line 1329) | type ConditionPattern struct
method validate (line 1334) | func (p *ConditionPattern) validate() error {
type ConditionOptions (line 1346) | type ConditionOptions struct
method getACopy (line 1364) | func (f *ConditionOptions) getACopy() ConditionOptions {
method validateStatuses (line 1386) | func (f *ConditionOptions) validateStatuses() error {
method validate (line 1395) | func (f *ConditionOptions) validate() error {
type Schedule (line 1435) | type Schedule struct
method GetCronSpec (line 1443) | func (s *Schedule) GetCronSpec() string {
method validate (line 1447) | func (s *Schedule) validate() error {
type EventConditions (line 1457) | type EventConditions struct
method getACopy (line 1467) | func (c *EventConditions) getACopy() EventConditions {
method validateSchedules (line 1491) | func (c *EventConditions) validateSchedules() error {
method validate (line 1506) | func (c *EventConditions) validate(trigger int) error {
type EventRule (line 1614) | type EventRule struct
method getACopy (line 1637) | func (r *EventRule) getACopy() EventRule {
method GuardFromConcurrentExecution (line 1659) | func (r *EventRule) GuardFromConcurrentExecution() bool {
method GetTriggerAsString (line 1667) | func (r *EventRule) GetTriggerAsString() string {
method GetActionsAsString (line 1672) | func (r *EventRule) GetActionsAsString() string {
method isStatusValid (line 1680) | func (r *EventRule) isStatusValid() bool {
method validate (line 1684) | func (r *EventRule) validate() error { //nolint:gocyclo
method validateMandatorySyncActions (line 1751) | func (r *EventRule) validateMandatorySyncActions() error {
method checkIPBlockedAndCertificateActions (line 1769) | func (r *EventRule) checkIPBlockedAndCertificateActions() error {
method checkProviderEventActions (line 1782) | func (r *EventRule) checkProviderEventActions(providerObjectType strin...
method hasUserAssociated (line 1802) | func (r *EventRule) hasUserAssociated(providerObjectType string) bool {
method checkActions (line 1818) | func (r *EventRule) checkActions(providerObjectType string) error {
method CheckActionsConsistency (line 1852) | func (r *EventRule) CheckActionsConsistency(providerObjectType string)...
method PrepareForRendering (line 1877) | func (r *EventRule) PrepareForRendering() {
method RenderAsJSON (line 1884) | func (r *EventRule) RenderAsJSON(reload bool) ([]byte, error) {
function cloneRenameConfigs (line 1898) | func cloneRenameConfigs(renames []RenameConfig) []RenameConfig {
function cloneKeyValues (line 1912) | func cloneKeyValues(keyVals []KeyValue) []KeyValue {
function cloneConditionPatterns (line 1923) | func cloneConditionPatterns(patterns []ConditionPattern) []ConditionPatt...
function validateConditionPatterns (line 1934) | func validateConditionPatterns(patterns []ConditionPattern) error {
type Task (line 1944) | type Task struct
FILE: internal/dataprovider/group.go
type GroupUserSettings (line 32) | type GroupUserSettings struct
type Group (line 40) | type Group struct
method GetPermissions (line 49) | func (g *Group) GetPermissions() []sdk.DirectoryPermissions {
method GetAllowedIPAsString (line 61) | func (g *Group) GetAllowedIPAsString() string {
method GetDeniedIPAsString (line 66) | func (g *Group) GetDeniedIPAsString() string {
method HasExternalAuth (line 72) | func (g *Group) HasExternalAuth() bool {
method SetEmptySecretsIfNil (line 83) | func (g *Group) SetEmptySecretsIfNil() {
method PrepareForRendering (line 94) | func (g *Group) PrepareForRendering() {
method RenderAsJSON (line 104) | func (g *Group) RenderAsJSON(reload bool) ([]byte, error) {
method GetEncryptionAdditionalData (line 119) | func (g *Group) GetEncryptionAdditionalData() string {
method hasRedactedSecret (line 124) | func (g *Group) hasRedactedSecret() bool {
method applyNamingRules (line 135) | func (g *Group) applyNamingRules() {
method validate (line 142) | func (g *Group) validate() error {
method validateUserSettings (line 168) | func (g *Group) validateUserSettings() error {
method getACopy (line 204) | func (g *Group) getACopy() Group {
FILE: internal/dataprovider/iplist.go
constant ipListMemoryLimit (line 37) | ipListMemoryLimit = 15000
function init (line 44) | func init() {
type IPListType (line 49) | type IPListType
method AsString (line 52) | func (t IPListType) AsString() string {
constant IPListTypeAllowList (line 67) | IPListTypeAllowList IPListType = iota + 1
constant IPListTypeDefender (line 68) | IPListTypeDefender
constant IPListTypeRateLimiterSafeList (line 69) | IPListTypeRateLimiterSafeList
constant ListModeAllow (line 74) | ListModeAllow = iota + 1
constant ListModeDeny (line 75) | ListModeDeny
constant ipTypeV4 (line 79) | ipTypeV4 = iota + 1
constant ipTypeV6 (line 80) | ipTypeV6
function CheckIPListType (line 88) | func CheckIPListType(t IPListType) error {
type IPListEntry (line 96) | type IPListEntry struct
method PrepareForRendering (line 122) | func (e *IPListEntry) PrepareForRendering() {
method HasProtocol (line 129) | func (e *IPListEntry) HasProtocol(proto string) bool {
method RenderAsJSON (line 145) | func (e *IPListEntry) RenderAsJSON(reload bool) ([]byte, error) {
method getKey (line 159) | func (e *IPListEntry) getKey() string {
method getName (line 163) | func (e *IPListEntry) getName() string {
method getFirst (line 167) | func (e *IPListEntry) getFirst() netip.Addr {
method getLast (line 178) | func (e *IPListEntry) getLast() netip.Addr {
method checkProtocols (line 189) | func (e *IPListEntry) checkProtocols() {
method validate (line 198) | func (e *IPListEntry) validate() error {
method getACopy (line 257) | func (e *IPListEntry) getACopy() IPListEntry {
method getAsRangerEntry (line 279) | func (e *IPListEntry) getAsRangerEntry() (cidranger.RangerEntry, error) {
method satisfySearchConstraints (line 291) | func (e IPListEntry) satisfySearchConstraints(filter, from, order stri...
type rangerEntry (line 304) | type rangerEntry struct
method Network (line 309) | func (e *rangerEntry) Network() net.IPNet {
type IPList (line 314) | type IPList struct
method addEntry (line 321) | func (l *IPList) addEntry(e *IPListEntry) {
method removeEntry (line 350) | func (l *IPList) removeEntry(e *IPListEntry) {
method updateEntry (line 374) | func (l *IPList) updateEntry(e *IPListEntry) {
method DisableMemoryMode (line 409) | func (l *IPList) DisableMemoryMode() {
method IsListed (line 416) | func (l *IPList) IsListed(ip, protocol string) (bool, int, error) {
function NewIPList (line 460) | func NewIPList(listType IPListType) (*IPList, error) {
FILE: internal/dataprovider/memory.go
type memoryProviderHandle (line 40) | type memoryProviderHandle struct
type MemoryProvider (line 90) | type MemoryProvider struct
method checkAvailability (line 132) | func (p *MemoryProvider) checkAvailability() error {
method close (line 141) | func (p *MemoryProvider) close() error {
method validateUserAndTLSCert (line 151) | func (p *MemoryProvider) validateUserAndTLSCert(username, protocol str...
method validateUserAndPass (line 164) | func (p *MemoryProvider) validateUserAndPass(username, password, ip, p...
method validateUserAndPubKey (line 173) | func (p *MemoryProvider) validateUserAndPubKey(username string, pubKey...
method validateAdminAndPass (line 186) | func (p *MemoryProvider) validateAdminAndPass(username, password, ip s...
method updateAPIKeyLastUse (line 196) | func (p *MemoryProvider) updateAPIKeyLastUse(keyID string) error {
method getAdminSignature (line 211) | func (p *MemoryProvider) getAdminSignature(username string) (string, e...
method getUserSignature (line 224) | func (p *MemoryProvider) getUserSignature(username string) (string, er...
method setUpdatedAt (line 237) | func (p *MemoryProvider) setUpdatedAt(username string) {
method updateLastLogin (line 252) | func (p *MemoryProvider) updateLastLogin(username string) error {
method updateAdminLastLogin (line 267) | func (p *MemoryProvider) updateAdminLastLogin(username string) error {
method updateTransferQuota (line 282) | func (p *MemoryProvider) updateTransferQuota(username string, uploadSi...
method updateQuota (line 307) | func (p *MemoryProvider) updateQuota(username string, filesAdd int, si...
method getUsedQuota (line 332) | func (p *MemoryProvider) getUsedQuota(username string) (int, int64, in...
method addUser (line 346) | func (p *MemoryProvider) addUser(user *User) error {
method updateUser (line 407) | func (p *MemoryProvider) updateUser(user *User) error { //nolint:gocyclo
method deleteUser (line 479) | func (p *MemoryProvider) deleteUser(user User, _ bool) error {
method updateUserPassword (line 508) | func (p *MemoryProvider) updateUserPassword(username, password string)...
method dumpUsers (line 525) | func (p *MemoryProvider) dumpUsers() ([]User, error) {
method dumpFolders (line 542) | func (p *MemoryProvider) dumpFolders() ([]vfs.BaseVirtualFolder, error) {
method getRecentlyUpdatedUsers (line 555) | func (p *MemoryProvider) getRecentlyUpdatedUsers(after int64) ([]User,...
method getUsersForQuotaCheck (line 591) | func (p *MemoryProvider) getUsersForQuotaCheck(toFetch map[string]bool...
method getUsers (line 625) | func (p *MemoryProvider) getUsers(limit int, offset int, order, role s...
method userExists (line 678) | func (p *MemoryProvider) userExists(username, role string) (User, erro...
method userExistsInternal (line 695) | func (p *MemoryProvider) userExistsInternal(username string) (User, er...
method groupExistsInternal (line 702) | func (p *MemoryProvider) groupExistsInternal(name string) (Group, erro...
method actionExistsInternal (line 709) | func (p *MemoryProvider) actionExistsInternal(name string) (BaseEventA...
method ruleExistsInternal (line 716) | func (p *MemoryProvider) ruleExistsInternal(name string) (EventRule, e...
method roleExistsInternal (line 723) | func (p *MemoryProvider) roleExistsInternal(name string) (Role, error) {
method ipListEntryExistsInternal (line 730) | func (p *MemoryProvider) ipListEntryExistsInternal(entry *IPListEntry)...
method addAdmin (line 737) | func (p *MemoryProvider) addAdmin(admin *Admin) error {
method updateAdmin (line 778) | func (p *MemoryProvider) updateAdmin(admin *Admin) error {
method deleteAdmin (line 824) | func (p *MemoryProvider) deleteAdmin(admin Admin) error {
method adminExists (line 850) | func (p *MemoryProvider) adminExists(username string) (Admin, error) {
method adminExistsInternal (line 859) | func (p *MemoryProvider) adminExistsInternal(username string) (Admin, ...
method dumpAdmins (line 866) | func (p *MemoryProvider) dumpAdmins() ([]Admin, error) {
method getAdmins (line 880) | func (p *MemoryProvider) getAdmins(limit int, offset int, order string...
method updateFolderQuota (line 927) | func (p *MemoryProvider) updateFolderQuota(name string, filesAdd int, ...
method getGroups (line 950) | func (p *MemoryProvider) getGroups(limit, offset int, order string, _ ...
method getGroupsWithNames (line 996) | func (p *MemoryProvider) getGroupsWithNames(names []string) ([]Group, ...
method getUsersInGroups (line 1014) | func (p *MemoryProvider) getUsersInGroups(names []string) ([]string, e...
method groupExists (line 1031) | func (p *MemoryProvider) groupExists(name string) (Group, error) {
method addGroup (line 1045) | func (p *MemoryProvider) addGroup(group *Group) error {
method updateGroup (line 1084) | func (p *MemoryProvider) updateGroup(group *Group) error {
method deleteGroup (line 1121) | func (p *MemoryProvider) deleteGroup(group Group) error {
method dumpGroups (line 1150) | func (p *MemoryProvider) dumpGroups() ([]Group, error) {
method getUsedFolderQuota (line 1167) | func (p *MemoryProvider) getUsedFolderQuota(name string) (int, int64, ...
method addVirtualFoldersToGroup (line 1181) | func (p *MemoryProvider) addVirtualFoldersToGroup(group *Group) {
method addActionsToRule (line 1197) | func (p *MemoryProvider) addActionsToRule(rule *EventRule) {
method addRuleToActionMapping (line 1212) | func (p *MemoryProvider) addRuleToActionMapping(ruleName, actionName s...
method removeRuleFromActionMapping (line 1224) | func (p *MemoryProvider) removeRuleFromActionMapping(ruleName, actionN...
method addAdminToGroupMapping (line 1242) | func (p *MemoryProvider) addAdminToGroupMapping(username, groupname st...
method removeAdminFromGroupMapping (line 1254) | func (p *MemoryProvider) removeAdminFromGroupMapping(username, groupna...
method removeGroupFromAdminMapping (line 1269) | func (p *MemoryProvider) removeGroupFromAdminMapping(groupname, userna...
method addUserToGroupMapping (line 1285) | func (p *MemoryProvider) addUserToGroupMapping(username, groupname str...
method removeUserFromGroupMapping (line 1297) | func (p *MemoryProvider) removeUserFromGroupMapping(username, groupnam...
method addAdminToRole (line 1312) | func (p *MemoryProvider) addAdminToRole(username, role string) error {
method removeAdminFromRole (line 1327) | func (p *MemoryProvider) removeAdminFromRole(username, role string) {
method addUserToRole (line 1346) | func (p *MemoryProvider) addUserToRole(username, role string) error {
method removeUserFromRole (line 1361) | func (p *MemoryProvider) removeUserFromRole(username, role string) {
method addUserToFolderMapping (line 1380) | func (p *MemoryProvider) addUserToFolderMapping(username, foldername s...
method addGroupToFolderMapping (line 1392) | func (p *MemoryProvider) addGroupToFolderMapping(name, foldername stri...
method addVirtualFoldersToUser (line 1404) | func (p *MemoryProvider) addVirtualFoldersToUser(user *User) {
method removeRelationFromFolderMapping (line 1420) | func (p *MemoryProvider) removeRelationFromFolderMapping(folderName, u...
method folderExistsInternal (line 1446) | func (p *MemoryProvider) folderExistsInternal(name string) (vfs.BaseVi...
method getFolders (line 1453) | func (p *MemoryProvider) getFolders(limit, offset int, order string, _...
method getFolderByName (line 1498) | func (p *MemoryProvider) getFolderByName(name string) (vfs.BaseVirtual...
method addFolder (line 1511) | func (p *MemoryProvider) addFolder(folder *vfs.BaseVirtualFolder) error {
method updateFolder (line 1539) | func (p *MemoryProvider) updateFolder(folder *vfs.BaseVirtualFolder) e...
method deleteFolder (line 1580) | func (p *MemoryProvider) deleteFolder(f vfs.BaseVirtualFolder) error {
method apiKeyExistsInternal (line 1628) | func (p *MemoryProvider) apiKeyExistsInternal(keyID string) (APIKey, e...
method apiKeyExists (line 1635) | func (p *MemoryProvider) apiKeyExists(keyID string) (APIKey, error) {
method addAPIKey (line 1644) | func (p *MemoryProvider) addAPIKey(apiKey *APIKey) error {
method updateAPIKey (line 1679) | func (p *MemoryProvider) updateAPIKey(apiKey *APIKey) error {
method deleteAPIKey (line 1714) | func (p *MemoryProvider) deleteAPIKey(apiKey APIKey) error {
method getAPIKeys (line 1731) | func (p *MemoryProvider) getAPIKeys(limit int, offset int, order strin...
method dumpAPIKeys (line 1778) | func (p *MemoryProvider) dumpAPIKeys() ([]APIKey, error) {
method deleteAPIKeysWithUser (line 1792) | func (p *MemoryProvider) deleteAPIKeysWithUser(username string) {
method deleteAPIKeysWithAdmin (line 1805) | func (p *MemoryProvider) deleteAPIKeysWithAdmin(username string) {
method deleteSharesWithUser (line 1818) | func (p *MemoryProvider) deleteSharesWithUser(username string) {
method updateAPIKeysOrdering (line 1831) | func (p *MemoryProvider) updateAPIKeysOrdering() {
method updateSharesOrdering (line 1840) | func (p *MemoryProvider) updateSharesOrdering() {
method shareExistsInternal (line 1849) | func (p *MemoryProvider) shareExistsInternal(shareID, username string)...
method shareExists (line 1859) | func (p *MemoryProvider) shareExists(shareID, username string) (Share,...
method addShare (line 1868) | func (p *MemoryProvider) addShare(share *Share) error {
method updateShare (line 1905) | func (p *MemoryProvider) updateShare(share *Share) error {
method deleteShare (line 1941) | func (p *MemoryProvider) deleteShare(share Share) error {
method getShares (line 1958) | func (p *MemoryProvider) getShares(limit int, offset int, order, usern...
method dumpShares (line 2010) | func (p *MemoryProvider) dumpShares() ([]Share, error) {
method updateShareLastUse (line 2024) | func (p *MemoryProvider) updateShareLastUse(shareID string, numTokens ...
method getDefenderHosts (line 2040) | func (p *MemoryProvider) getDefenderHosts(_ int64, _ int) ([]DefenderE...
method getDefenderHostByIP (line 2044) | func (p *MemoryProvider) getDefenderHostByIP(_ string, _ int64) (Defen...
method isDefenderHostBanned (line 2048) | func (p *MemoryProvider) isDefenderHostBanned(_ string) (DefenderEntry...
method updateDefenderBanTime (line 2052) | func (p *MemoryProvider) updateDefenderBanTime(_ string, _ int) error {
method deleteDefenderHost (line 2056) | func (p *MemoryProvider) deleteDefenderHost(_ string) error {
method addDefenderEvent (line 2060) | func (p *MemoryProvider) addDefenderEvent(_ string, _ int) error {
method setDefenderBanTime (line 2064) | func (p *MemoryProvider) setDefenderBanTime(_ string, _ int64) error {
method cleanupDefender (line 2068) | func (p *MemoryProvider) cleanupDefender(_ int64) error {
method addActiveTransfer (line 2072) | func (p *MemoryProvider) addActiveTransfer(_ ActiveTransfer) error {
method updateActiveTransferSizes (line 2076) | func (p *MemoryProvider) updateActiveTransferSizes(_, _, _ int64, _ st...
method removeActiveTransfer (line 2080) | func (p *MemoryProvider) removeActiveTransfer(_ int64, _ string) error {
method cleanupActiveTransfers (line 2084) | func (p *MemoryProvider) cleanupActiveTransfers(_ time.Time) error {
method getActiveTransfers (line 2088) | func (p *MemoryProvider) getActiveTransfers(_ time.Time) ([]ActiveTran...
method addSharedSession (line 2092) | func (p *MemoryProvider) addSharedSession(_ Session) error {
method deleteSharedSession (line 2096) | func (p *MemoryProvider) deleteSharedSession(_ string, _ SessionType) ...
method getSharedSession (line 2100) | func (p *MemoryProvider) getSharedSession(_ string, _ SessionType) (Se...
method cleanupSharedSessions (line 2104) | func (p *MemoryProvider) cleanupSharedSessions(_ SessionType, _ int64)...
method getEventActions (line 2108) | func (p *MemoryProvider) getEventActions(limit, offset int, order stri...
method dumpEventActions (line 2152) | func (p *MemoryProvider) dumpEventActions() ([]BaseEventAction, error) {
method eventActionExists (line 2167) | func (p *MemoryProvider) eventActionExists(name string) (BaseEventActi...
method addEventAction (line 2176) | func (p *MemoryProvider) addEventAction(action *BaseEventAction) error {
method updateEventAction (line 2201) | func (p *MemoryProvider) updateEventAction(action *BaseEventAction) er...
method deleteEventAction (line 2235) | func (p *MemoryProvider) deleteEventAction(action BaseEventAction) err...
method getEventRules (line 2258) | func (p *MemoryProvider) getEventRules(limit, offset int, order string...
method dumpEventRules (line 2304) | func (p *MemoryProvider) dumpEventRules() ([]EventRule, error) {
method getRecentlyUpdatedRules (line 2320) | func (p *MemoryProvider) getRecentlyUpdatedRules(after int64) ([]Event...
method eventRuleExists (line 2342) | func (p *MemoryProvider) eventRuleExists(name string) (EventRule, erro...
method addEventRule (line 2356) | func (p *MemoryProvider) addEventRule(rule *EventRule) error {
method updateEventRule (line 2396) | func (p *MemoryProvider) updateEventRule(rule *EventRul
Copy disabled (too large)
Download .json
Condensed preview — 405 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (13,899K chars).
[
{
"path": ".cirrus.yml",
"chars": 1299,
"preview": "freebsd_task:\n name: FreeBSD\n\n matrix:\n - name: FreeBSD 14.3\n freebsd_instance:\n image_family: freebsd-"
},
{
"path": ".github/FUNDING.yml",
"chars": 721,
"preview": "# These are supported funding model platforms\n\ngithub: [drakkan] # Replace with up to 4 GitHub Sponsors-enabled username"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 3725,
"preview": "name: Open Source Bug Report\ndescription: \"Submit a report and help us improve SFTPGo\"\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 356,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Commercial Support\n url: https://sftpgo.com/\n about: >\n "
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 2004,
"preview": "name: 🚀 Feature request\ndescription: Suggest an idea for SFTPGo\nlabels: [\"suggestion\"]\nbody:\n - type: markdown\n attr"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 124,
"preview": "# Checklist for Pull Requests\n\n- [ ] Have you signed the [Contributor License Agreement](https://sftpgo.com/cla.html)?\n\n"
},
{
"path": ".github/dependabot.yml",
"chars": 401,
"preview": "version: 2\n\nupdates:\n #- package-ecosystem: \"gomod\"\n # directory: \"/\"\n # schedule:\n # interval: \"weekly\"\n # o"
},
{
"path": ".github/workflows/.editorconfig",
"chars": 24,
"preview": "[*.yml]\nindent_size = 2\n"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 654,
"preview": "name: \"Code scanning - action\"\n\non:\n push:\n pull_request:\n schedule:\n - cron: '30 1 * * 6'\n\njobs:\n CodeQL-Build:\n"
},
{
"path": ".github/workflows/development.yml",
"chars": 24520,
"preview": "name: CI\n\non:\n push:\n branches: [main]\n pull_request:\n\npermissions:\n id-token: write\n contents: read\n\njobs:\n tes"
},
{
"path": ".github/workflows/docker.yml",
"chars": 7778,
"preview": "name: Docker\n\non:\n #schedule:\n # - cron: '0 4 * * *' # everyday at 4:00 AM UTC\n push:\n branches:\n - main\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 24698,
"preview": "name: Release\n\non:\n push:\n tags: 'v*'\n\npermissions:\n id-token: write\n contents: write\n\nenv:\n GO_VERSION: 1.25.8\n\n"
},
{
"path": ".gitignore",
"chars": 39,
"preview": "# compilation output\nsftpgo\nsftpgo.exe\n"
},
{
"path": ".golangci.yml",
"chars": 1254,
"preview": "version: \"2\"\nrun:\n issues-exit-code: 1\n tests: true\nlinters:\n enable:\n - bodyclose\n - dogsled\n - dupl\n - "
},
{
"path": "CODEOWNERS",
"chars": 11,
"preview": "* @drakkan\n"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5220,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "Dockerfile",
"chars": 2661,
"preview": "FROM golang:1.26-trixie AS builder\n\nENV GOFLAGS=\"-mod=readonly\"\n\nRUN apt-get update && apt-get -y upgrade && rm -rf /var"
},
{
"path": "Dockerfile.alpine",
"chars": 2215,
"preview": "FROM golang:1.26-alpine3.23 AS builder\n\nENV GOFLAGS=\"-mod=readonly\"\n\nRUN apk -U upgrade --no-cache && apk add --update -"
},
{
"path": "Dockerfile.distroless",
"chars": 2272,
"preview": "FROM golang:1.26-trixie AS builder\n\nENV CGO_ENABLED=0 GOFLAGS=\"-mod=readonly\"\n\nRUN apt-get update && apt-get -y upgrade "
},
{
"path": "LICENSE",
"chars": 34522,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "NOTICE",
"chars": 775,
"preview": "Additional terms under GNU AGPL version 3 section 7.3(b) and 13.1:\n\nIf you have included SFTPGo so that it is offered th"
},
{
"path": "README.md",
"chars": 7813,
"preview": "# SFTPGo\n\n[](https://github.com/drakkan/sftpgo/wor"
},
{
"path": "SECURITY.md",
"chars": 1861,
"preview": "# Security Policy\n\n## Supported Versions\n\nWe actively maintain the latest stable release of SFTPGo. While we strive to k"
},
{
"path": "crowdin.yml",
"chars": 224,
"preview": "project_id_env: CROWDIN_PROJECT_ID\napi_token_env: CROWDIN_PERSONAL_TOKEN\nfiles:\n - source: /static/locales/en/translati"
},
{
"path": "docker/scripts/download-plugins.sh",
"chars": 805,
"preview": "#!/usr/bin/env bash\nset -euo pipefail\n\nARCH=$(uname -m)\n\ncase ${ARCH} in\n x86_64)\n SUFFIX=amd64\n ;;\n "
},
{
"path": "examples/OTP/authy/README.md",
"chars": 2626,
"preview": "# Authy\n\nThese example show how-to integrate [Twilio Authy API](https://www.twilio.com/docs/authy/api) for One-Time-Pass"
},
{
"path": "examples/OTP/authy/checkpwd/README.md",
"chars": 334,
"preview": "# Authy 2FA via check password hook\n\nThis example shows how to use 2FA via the check password hook using a password cons"
},
{
"path": "examples/OTP/authy/checkpwd/go.mod",
"chars": 59,
"preview": "module github.com/drakkan/sftpgo/authy/checkpwd\n\ngo 1.22.2\n"
},
{
"path": "examples/OTP/authy/checkpwd/main.go",
"chars": 2463,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\ntype userMapping struct {\n\tSFTP"
},
{
"path": "examples/OTP/authy/extauth/README.md",
"chars": 183,
"preview": "# Authy external authentication\n\nThis example shows how to use Authy TOTP token as password for SFTPGo users. Please rea"
},
{
"path": "examples/OTP/authy/extauth/go.mod",
"chars": 58,
"preview": "module github.com/drakkan/sftpgo/authy/extauth\n\ngo 1.22.2\n"
},
{
"path": "examples/OTP/authy/extauth/main.go",
"chars": 2548,
"preview": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\ntype userMappi"
},
{
"path": "examples/OTP/authy/keyint/README.md",
"chars": 214,
"preview": "# Authy 2FA using keyboard interactive authentication\n\nThis example shows how to authenticate SFTP users using 2FA (pass"
},
{
"path": "examples/OTP/authy/keyint/go.mod",
"chars": 57,
"preview": "module github.com/drakkan/sftpgo/authy/keyint\n\ngo 1.22.2\n"
},
{
"path": "examples/OTP/authy/keyint/main.go",
"chars": 3078,
"preview": "package main\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\ntype userMapping struct {\n\tSF"
},
{
"path": "examples/backup/README.md",
"chars": 754,
"preview": "# Data Backup\n\n:warning: Since v2.4.0 you can use the [EventManager](https://docs.sftpgo.com/latest/eventmanager/) to sc"
},
{
"path": "examples/backup/backup",
"chars": 1151,
"preview": "#!/usr/bin/env python\n\nfrom datetime import datetime\nimport sys\n\nimport requests\n\ntry:\n\timport urllib.parse as urlparse\n"
},
{
"path": "examples/bulkupdate/README.md",
"chars": 620,
"preview": "# Bulk user update\n\nThe `bulkuserupdate` example script shows how to use the SFTPGo REST API to easily update some commo"
},
{
"path": "examples/bulkupdate/bulkuserupdate",
"chars": 1768,
"preview": "#!/usr/bin/env python\n\nimport posixpath\nimport sys\n\nimport requests\n\ntry:\n\timport urllib.parse as urlparse\nexcept Import"
},
{
"path": "examples/convertusers/README.md",
"chars": 2149,
"preview": "# Import users from other stores\n\n`convertusers` is a very simple command line client, written in python, to import user"
},
{
"path": "examples/convertusers/convertusers",
"chars": 7276,
"preview": "#!/usr/bin/env python\n\nimport argparse\nimport json\nimport sys\nimport time\n\ntry:\n\timport pwd\n\timport spwd\nexcept ImportEr"
},
{
"path": "examples/ldapauth/README.md",
"chars": 1977,
"preview": "# LDAPAuth\n\nThis is an example for an external authentication program. It performs authentication against an LDAP server"
},
{
"path": "examples/ldapauth/go.mod",
"chars": 348,
"preview": "module github.com/drakkan/ldapauth\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/go-ldap/ldap/v3 v3.4.13\n\tgolang.org/x/crypto v0.49."
},
{
"path": "examples/ldapauth/go.sum",
"chars": 3522,
"preview": "github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=\ngithub.com/Azure/go-ntlmssp v0.1.0/go"
},
{
"path": "examples/ldapauth/main.go",
"chars": 5169,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"log/syslog\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/g"
},
{
"path": "examples/ldapauthserver/README.md",
"chars": 656,
"preview": "# LDAPAuthServer\n\nThis is an example for an HTTP server to use as external authentication HTTP hook. It performs authent"
},
{
"path": "examples/ldapauthserver/cmd/root.go",
"chars": 5664,
"preview": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/drakkan/sftpgo/ldapauthserver/config\"\n\t\"github.com/drakkan/sftpgo/ldapa"
},
{
"path": "examples/ldapauthserver/cmd/serve.go",
"chars": 1518,
"preview": "package cmd\n\nimport (\n\t\"path/filepath\"\n\n\t\"github.com/drakkan/sftpgo/ldapauthserver/config\"\n\t\"github.com/drakkan/sftpgo/l"
},
{
"path": "examples/ldapauthserver/config/config.go",
"chars": 4879,
"preview": "package config\n\nimport (\n\t\"strings\"\n\n\t\"github.com/drakkan/sftpgo/ldapauthserver/logger\"\n\t\"github.com/spf13/viper\"\n)\n\ncon"
},
{
"path": "examples/ldapauthserver/go.mod",
"chars": 1336,
"preview": "module github.com/drakkan/sftpgo/ldapauthserver\n\ngo 1.25.0\n\nrequire (\n\tgithub.com/go-chi/chi/v5 v5.2.5\n\tgithub.com/go-ch"
},
{
"path": "examples/ldapauthserver/go.sum",
"chars": 10152,
"preview": "github.com/Azure/go-ntlmssp v0.1.0 h1:DjFo6YtWzNqNvQdrwEyr/e4nhU3vRiwenz5QX7sFz+A=\ngithub.com/Azure/go-ntlmssp v0.1.0/go"
},
{
"path": "examples/ldapauthserver/httpd/auth.go",
"chars": 3596,
"preview": "package httpd\n\nimport (\n\t\"encoding/csv\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"sync\"\n\n\tunixcrypt \"github.com/nathanaelle/p"
},
{
"path": "examples/ldapauthserver/httpd/httpd.go",
"chars": 3959,
"preview": "package httpd\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"git"
},
{
"path": "examples/ldapauthserver/httpd/ldapauth.go",
"chars": 4660,
"preview": "package httpd\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/drakkan/sftpgo/lda"
},
{
"path": "examples/ldapauthserver/httpd/models.go",
"chars": 4542,
"preview": "package httpd\n\ntype apiResponse struct {\n\tError string `json:\"error\"`\n\tMessage string `json:\"message\"`\n\tHTTPStat"
},
{
"path": "examples/ldapauthserver/httpd/tlsutils.go",
"chars": 1070,
"preview": "package httpd\n\nimport (\n\t\"crypto/tls\"\n\t\"sync\"\n\n\t\"github.com/drakkan/sftpgo/ldapauthserver/logger\"\n)\n\ntype certManager st"
},
{
"path": "examples/ldapauthserver/ldapauth.toml",
"chars": 1225,
"preview": "[httpd]\nbind_address = \"\"\nbind_port = 9000\n# Path to a file used to store usernames and passwords for basic authenticati"
},
{
"path": "examples/ldapauthserver/logger/logger.go",
"chars": 3224,
"preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/rs/zerolog\"\n\tlumberjack \"gopkg.in/natefi"
},
{
"path": "examples/ldapauthserver/logger/request_logger.go",
"chars": 1980,
"preview": "package logger\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/go-chi/chi/v5/middleware\"\n\t\"github.com/rs/zerolog\"\n)\n\n"
},
{
"path": "examples/ldapauthserver/logger/sync_wrapper.go",
"chars": 221,
"preview": "package logger\n\nimport (\n\t\"os\"\n\t\"sync\"\n)\n\ntype logSyncWrapper struct {\n\tsync.Mutex\n\toutput *os.File\n}\n\nfunc (l *logSyncW"
},
{
"path": "examples/ldapauthserver/main.go",
"chars": 100,
"preview": "package main\n\nimport \"github.com/drakkan/sftpgo/ldapauthserver/cmd\"\n\nfunc main() {\n\tcmd.Execute()\n}\n"
},
{
"path": "examples/ldapauthserver/utils/utils.go",
"chars": 662,
"preview": "package utils\n\nimport (\n\t\"path/filepath\"\n\t\"strings\"\n)\n\n// IsFileInputValid returns true this is a valid file name.\n// Th"
},
{
"path": "examples/ldapauthserver/utils/version.go",
"chars": 834,
"preview": "package utils\n\nconst version = \"0.1.0-dev\"\n\nvar (\n\tcommit = \"\"\n\tdate = \"\"\n\tversionInfo VersionInfo\n)\n\n// Ver"
},
{
"path": "examples/php-activedirectory-http-server/README.md",
"chars": 8329,
"preview": "# SFTPGo on Windows with Active Directory Integration + Caddy Static File Server Example\n\n["
},
{
"path": "examples/quotascan/scanuserquota",
"chars": 3807,
"preview": "#!/usr/bin/env python\n\nfrom datetime import datetime\nimport sys\nimport time\n\nimport pytz\nimport requests\n\ntry:\n\timport u"
},
{
"path": "go.mod",
"chars": 8905,
"preview": "module github.com/drakkan/sftpgo/v2\n\ngo 1.25.0\n\nrequire (\n\tcloud.google.com/go/storage v1.60.0\n\tgithub.com/Azure/azure-s"
},
{
"path": "go.sum",
"chars": 44856,
"preview": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ"
},
{
"path": "init/com.github.drakkan.sftpgo.plist",
"chars": 1404,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "init/sftpgo.service",
"chars": 625,
"preview": "[Unit]\nDescription=SFTPGo Server\nAfter=network.target\n\n[Service]\nUser=sftpgo\nGroup=sftpgo\nType=simple\nWorkingDirectory=/"
},
{
"path": "internal/acme/account.go",
"chars": 1333,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/acme/acme.go",
"chars": 27614,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/bundle/bundle.go",
"chars": 1679,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/acme.go",
"chars": 3174,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/gen.go",
"chars": 824,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/gencompletion.go",
"chars": 4106,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/genman.go",
"chars": 2044,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/initprovider.go",
"chars": 4332,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/install_windows.go",
"chars": 3424,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/ping.go",
"chars": 3402,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/portable.go",
"chars": 21978,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/portable_disabled.go",
"chars": 783,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/reload_windows.go",
"chars": 1275,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/resetprovider.go",
"chars": 3068,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/resetpwd.go",
"chars": 4347,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/revertprovider.go",
"chars": 3243,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/root.go",
"chars": 11070,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/rotatelogs_windows.go",
"chars": 1306,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/serve.go",
"chars": 3898,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/service_windows.go",
"chars": 849,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/signals_unix.go",
"chars": 1130,
"preview": "// Copyright (C) 2025 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/signals_windows.go",
"chars": 1008,
"preview": "// Copyright (C) 2025 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/smtptest.go",
"chars": 2672,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/start_windows.go",
"chars": 1932,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/status_windows.go",
"chars": 1284,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/stop_windows.go",
"chars": 1223,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/cmd/uninstall_windows.go",
"chars": 1251,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/command/command.go",
"chars": 4759,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/command/command_test.go",
"chars": 5005,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/actions.go",
"chars": 11680,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/actions_test.go",
"chars": 10701,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/clientsmap.go",
"chars": 1585,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/clientsmap_test.go",
"chars": 2030,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/common.go",
"chars": 53443,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/common_test.go",
"chars": 61693,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/connection.go",
"chars": 66241,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/connection_test.go",
"chars": 51142,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/dataretention.go",
"chars": 9622,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/dataretention_test.go",
"chars": 5384,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/defender.go",
"chars": 8470,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/defender_test.go",
"chars": 16689,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/defenderdb.go",
"chars": 5419,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/defenderdb_test.go",
"chars": 10552,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/defendermem.go",
"chars": 8118,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/eventmanager.go",
"chars": 98140,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/eventmanager_test.go",
"chars": 75934,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/eventscheduler.go",
"chars": 1477,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/httpauth.go",
"chars": 3975,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/httpauth_test.go",
"chars": 3397,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/protocol_test.go",
"chars": 328163,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/ratelimiter.go",
"chars": 7257,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/ratelimiter_test.go",
"chars": 4395,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/tlsutils.go",
"chars": 8506,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/tlsutils_test.go",
"chars": 23234,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/transfer.go",
"chars": 18614,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/transfer_test.go",
"chars": 16026,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/transferschecker.go",
"chars": 9885,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/common/transferschecker_test.go",
"chars": 25445,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/config/config.go",
"chars": 77784,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/config/config_darwin.go",
"chars": 834,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/config/config_fallback.go",
"chars": 728,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/config/config_linux.go",
"chars": 914,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/config/config_test.go",
"chars": 72588,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/actions.go",
"chars": 5439,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/admin.go",
"chars": 21211,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/apikey.go",
"chars": 6254,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/bolt.go",
"chars": 101084,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/bolt_disabled.go",
"chars": 899,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/cachedpassword.go",
"chars": 4036,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/cacheduser.go",
"chars": 4828,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/configs.go",
"chars": 17643,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/dataprovider.go",
"chars": 168167,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/eventrule.go",
"chars": 62404,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/group.go",
"chars": 8048,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/iplist.go",
"chars": 13058,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/memory.go",
"chars": 89345,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/mysql.go",
"chars": 40922,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/mysql_disabled.go",
"chars": 895,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/node.go",
"chars": 7892,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/pgsql.go",
"chars": 42334,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/pgsql_disabled.go",
"chars": 900,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/quotaupdater.go",
"chars": 6685,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/role.go",
"chars": 2768,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/scheduler.go",
"chars": 6018,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/session.go",
"chars": 1388,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/share.go",
"chars": 9885,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/sqlcommon.go",
"chars": 114732,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/sqlite.go",
"chars": 36656,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/sqlite_disabled.go",
"chars": 915,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/sqlqueries.go",
"chars": 49397,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/unixcrypt.go",
"chars": 1097,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/unixcrypt_disabled.go",
"chars": 957,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/dataprovider/user.go",
"chars": 59145,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/cryptfs_test.go",
"chars": 12103,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/ftpd.go",
"chars": 16520,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/ftpd_test.go",
"chars": 146897,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/handler.go",
"chars": 19310,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/internal_test.go",
"chars": 43141,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/server.go",
"chars": 17127,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/ftpd/transfer.go",
"chars": 3837,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpclient/httpclient.go",
"chars": 9039,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_admin.go",
"chars": 11201,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_configs.go",
"chars": 4298,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_defender.go",
"chars": 2486,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_eventrule.go",
"chars": 8914,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_events.go",
"chars": 14785,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_folder.go",
"chars": 4695,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_group.go",
"chars": 4619,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_http_user.go",
"chars": 19982,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_iplist.go",
"chars": 5056,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_keys.go",
"chars": 4305,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_maintenance.go",
"chars": 19650,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_mfa.go",
"chars": 10847,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_quota.go",
"chars": 9877,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_retention.go",
"chars": 1164,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_role.go",
"chars": 4141,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_shares.go",
"chars": 20533,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_user.go",
"chars": 11466,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/api_utils.go",
"chars": 30102,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/auth_utils.go",
"chars": 13571,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/file.go",
"chars": 3238,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/flash.go",
"chars": 2338,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/flash_test.go",
"chars": 1716,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/handler.go",
"chars": 10989,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/httpd.go",
"chars": 58565,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/httpd_test.go",
"chars": 1092956,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/internal_test.go",
"chars": 162756,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/middleware.go",
"chars": 20985,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/oauth2.go",
"chars": 5178,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
},
{
"path": "internal/httpd/oauth2_test.go",
"chars": 4772,
"preview": "// Copyright (C) 2019 Nicola Murino\n//\n// This program is free software: you can redistribute it and/or modify\n// it und"
}
]
// ... and 205 more files (download for full content)
About this extraction
This page contains the full source code of the drakkan/sftpgo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 405 files (12.6 MB), approximately 3.3M tokens, and a symbol index with 12155 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.