diff --git a/go.mod b/go.mod
index 03a92dc1..9e7009fc 100644
--- a/go.mod
+++ b/go.mod
@@ -34,17 +34,17 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.3.0
github.com/hashicorp/go-hclog v1.5.0
- github.com/hashicorp/go-plugin v1.4.10-0.20230321181155-4b35dc2fedaa
+ github.com/hashicorp/go-plugin v1.4.10-0.20230403150917-e889c1ba1044
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
- github.com/klauspost/compress v1.16.3
+ github.com/klauspost/compress v1.16.4
github.com/lestrrat-go/jwx/v2 v2.0.9
github.com/lithammer/shortuuid/v3 v3.0.7
github.com/mattn/go-sqlite3 v1.14.16
github.com/mhale/smtpd v0.8.0
github.com/minio/sio v0.3.1
- github.com/otiai10/copy v1.9.0
+ github.com/otiai10/copy v1.10.0
github.com/pires/go-proxyproto v0.7.0
github.com/pkg/sftp v1.13.6-0.20230213180117-971c283182b6
github.com/pquerna/otp v1.4.0
@@ -53,10 +53,10 @@ require (
github.com/rs/cors v1.8.3
github.com/rs/xid v1.4.0
github.com/rs/zerolog v1.29.0
- github.com/sftpgo/sdk v0.1.3-0.20230331165215-d823c5f1b120
+ github.com/sftpgo/sdk v0.1.3-0.20230406132142-15f26d806282
github.com/shirou/gopsutil/v3 v3.23.3
github.com/spf13/afero v1.9.5
- github.com/spf13/cobra v1.6.1
+ github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.2
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2
@@ -71,10 +71,10 @@ require (
golang.org/x/crypto v0.7.0
golang.org/x/net v0.8.0
golang.org/x/oauth2 v0.6.0
- golang.org/x/sys v0.6.0
- golang.org/x/term v0.6.0
+ golang.org/x/sys v0.7.0
+ golang.org/x/term v0.7.0
golang.org/x/time v0.3.0
- google.golang.org/api v0.114.0
+ google.golang.org/api v0.116.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
@@ -82,8 +82,8 @@ require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.19.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
- cloud.google.com/go/iam v0.13.0 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 // indirect
+ cloud.google.com/go/iam v1.0.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 // indirect
@@ -146,7 +146,7 @@ require (
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/shoenig/go-m1cpu v0.1.4 // indirect
+ github.com/shoenig/go-m1cpu v0.1.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
@@ -154,8 +154,8 @@ require (
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
- golang.org/x/mod v0.9.0 // indirect
- golang.org/x/text v0.8.0 // indirect
+ golang.org/x/mod v0.10.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 34fd9602..376de299 100644
--- a/go.sum
+++ b/go.sum
@@ -218,8 +218,8 @@ cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHD
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
-cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
-cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
+cloud.google.com/go/iam v1.0.0 h1:hlQJMovyJJwYjZcTohUH4o1L8Z8kYz+E+W/zktiLCBc=
+cloud.google.com/go/iam v1.0.0/go.mod h1:ikbQ4f1r91wTmBmmOtBCOtuEOei6taatNXytzB7Cxew=
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=
@@ -440,8 +440,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0 h1:leh5DwKv6Ihwi+h60uHtn6UWAxBbZ0q8DwQVMzf61zw=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.9.0/go.mod h1:EAyXOW1F6BTJPiK2pDvmnvxOHPxoTYWoqBeIlql+QhI=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1/go.mod h1:9V2j0jn9jDEkCkv8w/bKTNppX/d0FVA1ud77xCIP4KA=
@@ -1291,8 +1291,8 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-plugin v1.4.10-0.20230321181155-4b35dc2fedaa h1:Sal5OjHO1V800z3WahvH5C8QLlu8UFDZAQXA8o2QVvE=
-github.com/hashicorp/go-plugin v1.4.10-0.20230321181155-4b35dc2fedaa/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0=
+github.com/hashicorp/go-plugin v1.4.10-0.20230403150917-e889c1ba1044 h1:dEFpX4X++vjyeh0mqp0rGbTF2/gXfSc8bOKSTrh0ucg=
+github.com/hashicorp/go-plugin v1.4.10-0.20230403150917-e889c1ba1044/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
@@ -1346,7 +1346,6 @@ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
@@ -1441,8 +1440,8 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
-github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU=
+github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
@@ -1693,13 +1692,9 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
-github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4=
-github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI=
-github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
-github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
-github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
-github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4=
-github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI=
+github.com/otiai10/copy v1.10.0 h1:znyI7l134wNg/wDktoVQPxPkgvhDfGCYUasey+h0rDQ=
+github.com/otiai10/copy v1.10.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
+github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/ovh/go-ovh v1.3.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -1840,12 +1835,13 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
-github.com/sftpgo/sdk v0.1.3-0.20230331165215-d823c5f1b120 h1:HSuWbDcEQidm1nXwxLBAUzEYtG0Zy93I+w0PV7DxAGs=
-github.com/sftpgo/sdk v0.1.3-0.20230331165215-d823c5f1b120/go.mod h1:3IX7CxQwZM27z3NCZYgEQGIglTKWWfHlatxdp3wGzv4=
+github.com/sftpgo/sdk v0.1.3-0.20230406132142-15f26d806282 h1:6RTcCXSQ0+8G4T4eGF6aCkK9+tQ96dhpDb7tAaMRhdU=
+github.com/sftpgo/sdk v0.1.3-0.20230406132142-15f26d806282/go.mod h1:NZ7CYZf0VuQ2sQHhBEnUVmzQJ/N0l0Nyq9tGLAKf14s=
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU=
-github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs=
github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
+github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ=
+github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ=
github.com/shoenig/test v0.6.0/go.mod h1:xYtyGBC5Q3kzCNyJg/SjgNpfAa2kvmgA0i5+lQso8x0=
github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c=
github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
@@ -1886,8 +1882,8 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -2158,8 +2154,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2441,7 +2437,6 @@ golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -2453,8 +2448,9 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2464,8 +2460,9 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2480,8 +2477,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2663,8 +2661,8 @@ google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
-google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
-google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
+google.golang.org/api v0.116.0 h1:09tOPVufPwfm5W4aA8EizGHJ7BcoRDsIareM2a15gO4=
+google.golang.org/api v0.116.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
diff --git a/internal/common/connection.go b/internal/common/connection.go
index da9ad4f6..8d0629b2 100644
--- a/internal/common/connection.go
+++ b/internal/common/connection.go
@@ -342,6 +342,17 @@ func (c *BaseConnection) CheckParentDirs(virtualPath string) error {
return nil
}
+// GetCreateChecks returns the checks for creating new files
+func (c *BaseConnection) GetCreateChecks(virtualPath string, isNewFile bool) int {
+ if !isNewFile {
+ return 0
+ }
+ if !c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(virtualPath)) {
+ return vfs.CheckParentDir
+ }
+ return 0
+}
+
// CreateDir creates a new directory at the specified fsPath
func (c *BaseConnection) CreateDir(virtualPath string, checkFilePatterns bool) error {
if !c.User.HasPerm(dataprovider.PermCreateDirs, path.Dir(virtualPath)) {
diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go
index 5d1ca8e8..66b59420 100644
--- a/internal/common/eventmanager.go
+++ b/internal/common/eventmanager.go
@@ -917,8 +917,7 @@ func getFileWriter(conn *BaseConnection, virtualPath string, expectedSize int64)
if err := checkWriterPermsAndQuota(conn, virtualPath, numFiles, expectedSize, truncatedSize); err != nil {
return nil, numFiles, truncatedSize, nil, err
}
-
- f, w, cancelFn, err := fs.Create(fsPath, 0)
+ f, w, cancelFn, err := fs.Create(fsPath, 0, conn.GetCreateChecks(virtualPath, numFiles == 1))
if err != nil {
return nil, numFiles, truncatedSize, nil, conn.GetFsError(fs, err)
}
diff --git a/internal/dataprovider/dataprovider.go b/internal/dataprovider/dataprovider.go
index e310518d..b5919898 100644
--- a/internal/dataprovider/dataprovider.go
+++ b/internal/dataprovider/dataprovider.go
@@ -3019,6 +3019,7 @@ func ValidateFolder(folder *vfs.BaseVirtualFolder) error {
// FIXME: this should be defined as User struct method
func ValidateUser(user *User) error {
user.OIDCCustomFields = nil
+ user.HasPassword = false
user.SetEmptySecretsIfNil()
buildUserHomeDir(user)
if err := validateBaseParams(user); err != nil {
diff --git a/internal/dataprovider/user.go b/internal/dataprovider/user.go
index 63e114f2..1793ae92 100644
--- a/internal/dataprovider/user.go
+++ b/internal/dataprovider/user.go
@@ -1566,6 +1566,7 @@ func (u *User) CountUnusedRecoveryCodes() int {
// SetEmptySecretsIfNil sets the secrets to empty if nil
func (u *User) SetEmptySecretsIfNil() {
+ u.HasPassword = u.Password != ""
u.FsConfig.SetEmptySecretsIfNil()
for idx := range u.VirtualFolders {
vfolder := &u.VirtualFolders[idx]
@@ -1931,6 +1932,7 @@ func (u *User) getACopy() User {
Email: u.Email,
Password: u.Password,
PublicKeys: pubKeys,
+ HasPassword: u.HasPassword,
HomeDir: u.HomeDir,
UID: u.UID,
GID: u.GID,
diff --git a/internal/ftpd/handler.go b/internal/ftpd/handler.go
index cd7449b9..92099357 100644
--- a/internal/ftpd/handler.go
+++ b/internal/ftpd/handler.go
@@ -408,7 +408,7 @@ func (c *Connection) handleFTPUploadToNewFile(fs vfs.Fs, flags int, resolvedPath
c.Log(logger.LevelDebug, "upload for file %q denied by pre action: %v", requestPath, err)
return nil, ftpserver.ErrFileNameNotAllowed
}
- file, w, cancelFn, err := fs.Create(filePath, flags)
+ file, w, cancelFn, err := fs.Create(filePath, flags, c.GetCreateChecks(requestPath, true))
if err != nil {
c.Log(logger.LevelError, "error creating file %q, flags %v: %+v", resolvedPath, flags, err)
return nil, c.GetFsError(fs, err)
@@ -463,7 +463,7 @@ func (c *Connection) handleFTPUploadToExistingFile(fs vfs.Fs, flags int, resolve
}
}
- file, w, cancelFn, err := fs.Create(filePath, flags)
+ file, w, cancelFn, err := fs.Create(filePath, flags, c.GetCreateChecks(requestPath, false))
if err != nil {
c.Log(logger.LevelError, "error opening existing file, flags: %v, source: %q, err: %+v", flags, filePath, err)
return nil, c.GetFsError(fs, err)
diff --git a/internal/httpd/handler.go b/internal/httpd/handler.go
index 0415c057..1121c659 100644
--- a/internal/httpd/handler.go
+++ b/internal/httpd/handler.go
@@ -201,7 +201,7 @@ func (c *Connection) handleUploadFile(fs vfs.Fs, resolvedPath, filePath, request
maxWriteSize, _ := c.GetMaxWriteSize(diskQuota, false, fileSize, fs.IsUploadResumeSupported())
- file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
+ file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, c.GetCreateChecks(requestPath, isNewFile))
if err != nil {
c.Log(logger.LevelError, "error opening existing file, source: %q, err: %+v", filePath, err)
return nil, c.GetFsError(fs, err)
diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go
index be4fb2f3..566c9082 100644
--- a/internal/httpd/httpd_test.go
+++ b/internal/httpd/httpd_test.go
@@ -613,6 +613,7 @@ func TestBasicUserHandling(t *testing.T) {
user, _, err = httpdtest.GetUserByUsername(defaultUsername, http.StatusOK)
assert.NoError(t, err)
assert.Nil(t, user.OIDCCustomFields)
+ assert.True(t, user.HasPassword)
user.Email = "invalid@email"
_, body, err := httpdtest.UpdateUser(user, http.StatusBadRequest, "")
@@ -5578,6 +5579,7 @@ func TestUserHiddenFields(t *testing.T) {
user, _, err := httpdtest.GetUserByUsername(username, http.StatusOK)
assert.NoError(t, err)
assert.Empty(t, user.Password)
+ assert.True(t, user.HasPassword)
}
user1, _, err = httpdtest.GetUserByUsername(user1.Username, http.StatusOK)
assert.NoError(t, err)
@@ -5949,6 +5951,11 @@ func TestGetUsers(t *testing.T) {
users, _, err := httpdtest.GetUsers(0, 0, http.StatusOK)
assert.NoError(t, err)
assert.GreaterOrEqual(t, len(users), 2)
+ for _, user := range users {
+ if u.Username == user.Username {
+ assert.True(t, user.HasPassword)
+ }
+ }
users, _, err = httpdtest.GetUsers(1, 0, http.StatusOK)
assert.NoError(t, err)
assert.Equal(t, 1, len(users))
@@ -6628,6 +6635,59 @@ func TestNamingRules(t *testing.T) {
require.NoError(t, err)
}
+func TestUserPassword(t *testing.T) {
+ u := getTestUser()
+ u.Password = ""
+ user, _, err := httpdtest.AddUser(u, http.StatusCreated)
+ assert.NoError(t, err)
+ assert.False(t, user.HasPassword)
+ user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
+ assert.NoError(t, err)
+ assert.False(t, user.HasPassword)
+
+ user.Password = defaultPassword
+ user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
+ assert.NoError(t, err)
+ assert.True(t, user.HasPassword)
+
+ token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
+ assert.NoError(t, err)
+
+ rawUser := map[string]any{
+ "username": user.Username,
+ "home_dir": filepath.Join(homeBasePath, defaultUsername),
+ "permissions": map[string][]string{
+ "/": {"*"},
+ },
+ }
+ userAsJSON, err := json.Marshal(rawUser)
+ assert.NoError(t, err)
+ req, err := http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
+ assert.NoError(t, err)
+ setBearerForReq(req, token)
+ rr := executeRequest(req)
+ checkResponseCode(t, http.StatusOK, rr)
+ // the previous password must be preserved
+ user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
+ assert.NoError(t, err)
+ assert.True(t, user.HasPassword)
+ // update the user with an empty password field, the password will be unset
+ rawUser["password"] = ""
+ userAsJSON, err = json.Marshal(rawUser)
+ assert.NoError(t, err)
+ req, err = http.NewRequest(http.MethodPut, path.Join(userPath, user.Username), bytes.NewBuffer(userAsJSON))
+ assert.NoError(t, err)
+ setBearerForReq(req, token)
+ rr = executeRequest(req)
+ checkResponseCode(t, http.StatusOK, rr)
+ user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
+ assert.NoError(t, err)
+ assert.False(t, user.HasPassword)
+
+ _, err = httpdtest.RemoveUser(user, http.StatusOK)
+ assert.NoError(t, err)
+}
+
func TestSaveErrors(t *testing.T) {
err := dataprovider.Close()
assert.NoError(t, err)
diff --git a/internal/httpdtest/httpdtest.go b/internal/httpdtest/httpdtest.go
index 9796a7d2..8e5d3508 100644
--- a/internal/httpdtest/httpdtest.go
+++ b/internal/httpdtest/httpdtest.go
@@ -175,7 +175,7 @@ func AddUser(user dataprovider.User, expectedStatusCode int) (dataprovider.User,
func UpdateUserWithJSON(user dataprovider.User, expectedStatusCode int, disconnect string, userAsJSON []byte) (dataprovider.User, []byte, error) {
var newUser dataprovider.User
var body []byte
- url, err := addDisconnectQueryParam(buildURLRelativeToBase(userPath, url.PathEscape(user.Username)), disconnect)
+ url, err := addUpdateUserQueryParams(buildURLRelativeToBase(userPath, url.PathEscape(user.Username)), disconnect)
if err != nil {
return user, body, err
}
@@ -2900,13 +2900,13 @@ func addModeQueryParam(rawurl, mode string) (*url.URL, error) {
return url, err
}
-func addDisconnectQueryParam(rawurl, disconnect string) (*url.URL, error) {
+func addUpdateUserQueryParams(rawurl, disconnect string) (*url.URL, error) {
url, err := url.Parse(rawurl)
if err != nil {
return nil, err
}
q := url.Query()
- if len(disconnect) > 0 {
+ if disconnect != "" {
q.Add("disconnect", disconnect)
}
url.RawQuery = q.Encode()
diff --git a/internal/sftpd/handler.go b/internal/sftpd/handler.go
index af8659be..a396f6aa 100644
--- a/internal/sftpd/handler.go
+++ b/internal/sftpd/handler.go
@@ -407,7 +407,7 @@ func (c *Connection) handleSFTPUploadToNewFile(fs vfs.Fs, pflags sftp.FileOpenFl
}
osFlags := getOSOpenFlags(pflags)
- file, w, cancelFn, err := fs.Create(filePath, osFlags)
+ file, w, cancelFn, err := fs.Create(filePath, osFlags, c.GetCreateChecks(requestPath, true))
if err != nil {
c.Log(logger.LevelError, "error creating file %q, os flags %d, pflags %+v: %+v", resolvedPath, osFlags, pflags, err)
return nil, c.GetFsError(fs, err)
@@ -463,7 +463,7 @@ func (c *Connection) handleSFTPUploadToExistingFile(fs vfs.Fs, pflags sftp.FileO
}
}
- file, w, cancelFn, err := fs.Create(filePath, osFlags)
+ file, w, cancelFn, err := fs.Create(filePath, osFlags, c.GetCreateChecks(requestPath, false))
if err != nil {
c.Log(logger.LevelError, "error opening existing file, os flags %v, pflags: %+v, source: %q, err: %+v",
osFlags, pflags, filePath, err)
diff --git a/internal/sftpd/scp.go b/internal/sftpd/scp.go
index 586bb80e..04ac8457 100644
--- a/internal/sftpd/scp.go
+++ b/internal/sftpd/scp.go
@@ -245,7 +245,7 @@ func (c *scpCommand) handleUploadFile(fs vfs.Fs, resolvedPath, filePath string,
maxWriteSize, _ := c.connection.GetMaxWriteSize(diskQuota, false, fileSize, fs.IsUploadResumeSupported())
- file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
+ file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, c.connection.GetCreateChecks(requestPath, isNewFile))
if err != nil {
c.connection.Log(logger.LevelError, "error creating file %q: %v", resolvedPath, err)
c.sendErrorMessage(fs, err)
diff --git a/internal/vfs/azblobfs.go b/internal/vfs/azblobfs.go
index 1189670e..34809aaf 100644
--- a/internal/vfs/azblobfs.go
+++ b/internal/vfs/azblobfs.go
@@ -227,7 +227,13 @@ func (fs *AzureBlobFs) Open(name string, offset int64) (File, *pipeat.PipeReader
}
// Create creates or opens the named file for writing
-func (fs *AzureBlobFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *AzureBlobFs) Create(name string, flag, checks int) (File, *PipeWriter, func(), error) {
+ if checks&CheckParentDir != 0 {
+ _, err := fs.Stat(path.Dir(name))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ }
r, w, err := pipeat.PipeInDir(fs.localTempDir)
if err != nil {
return nil, nil, nil, err
@@ -269,6 +275,10 @@ func (fs *AzureBlobFs) Rename(source, target string) (int, int64, error) {
if source == target {
return -1, -1, nil
}
+ _, err := fs.Stat(path.Dir(target))
+ if err != nil {
+ return -1, -1, err
+ }
fi, err := fs.Stat(source)
if err != nil {
return -1, -1, err
@@ -880,7 +890,7 @@ func (fs *AzureBlobFs) renameInternal(source, target string, fi os.FileInfo) (in
}
func (fs *AzureBlobFs) mkdirInternal(name string) error {
- _, w, _, err := fs.Create(name, -1)
+ _, w, _, err := fs.Create(name, -1, 0)
if err != nil {
return err
}
diff --git a/internal/vfs/cryptfs.go b/internal/vfs/cryptfs.go
index f39f9816..fc0560d7 100644
--- a/internal/vfs/cryptfs.go
+++ b/internal/vfs/cryptfs.go
@@ -150,7 +150,7 @@ func (fs *CryptFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt,
}
// Create creates or opens the named file for writing
-func (fs *CryptFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *CryptFs) Create(name string, flag, _ int) (File, *PipeWriter, func(), error) {
var err error
var f *os.File
if flag == 0 {
diff --git a/internal/vfs/gcsfs.go b/internal/vfs/gcsfs.go
index 6e898e80..cf2b2aef 100644
--- a/internal/vfs/gcsfs.go
+++ b/internal/vfs/gcsfs.go
@@ -161,7 +161,13 @@ func (fs *GCSFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, fu
}
// Create creates or opens the named file for writing
-func (fs *GCSFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *GCSFs) Create(name string, flag, checks int) (File, *PipeWriter, func(), error) {
+ if checks&CheckParentDir != 0 {
+ _, err := fs.Stat(path.Dir(name))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ }
r, w, err := pipeat.PipeInDir(fs.localTempDir)
if err != nil {
return nil, nil, nil, err
@@ -227,6 +233,10 @@ func (fs *GCSFs) Rename(source, target string) (int, int64, error) {
if source == target {
return -1, -1, nil
}
+ _, err := fs.Stat(path.Dir(target))
+ if err != nil {
+ return -1, -1, err
+ }
fi, err := fs.getObjectStat(source)
if err != nil {
return -1, -1, err
@@ -704,7 +714,7 @@ func (fs *GCSFs) resolve(name, prefix, contentType string) (string, bool) {
return result, isDir
}
-// getObjectStat returns the stat result and the real object name as first value
+// getObjectStat returns the stat result
func (fs *GCSFs) getObjectStat(name string) (os.FileInfo, error) {
attrs, err := fs.headObject(name)
if err == nil {
@@ -823,7 +833,7 @@ func (fs *GCSFs) mkdirInternal(name string) error {
if !strings.HasSuffix(name, "/") {
name += "/"
}
- _, w, _, err := fs.Create(name, -1)
+ _, w, _, err := fs.Create(name, -1, 0)
if err != nil {
return err
}
diff --git a/internal/vfs/httpfs.go b/internal/vfs/httpfs.go
index ab5c1b2e..6bf5a56c 100644
--- a/internal/vfs/httpfs.go
+++ b/internal/vfs/httpfs.go
@@ -330,7 +330,7 @@ func (fs *HTTPFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, f
}
// Create creates or opens the named file for writing
-func (fs *HTTPFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *HTTPFs) Create(name string, flag, checks int) (File, *PipeWriter, func(), error) {
r, w, err := pipeat.PipeInDir(fs.localTempDir)
if err != nil {
return nil, nil, nil, err
@@ -338,15 +338,11 @@ func (fs *HTTPFs) Create(name string, flag int) (File, *PipeWriter, func(), erro
p := NewPipeWriter(w)
ctx, cancelFn := context.WithCancel(context.Background())
- var queryString string
- if flag > 0 {
- queryString = fmt.Sprintf("?flags=%d", flag)
- }
-
go func() {
defer cancelFn()
contentType := mime.TypeByExtension(path.Ext(name))
+ queryString := fmt.Sprintf("?flags=%d&checks=%d", flag, checks)
resp, err := fs.sendHTTPRequest(ctx, http.MethodPost, "create", name, queryString, contentType,
&wrapReader{reader: r})
if err != nil {
diff --git a/internal/vfs/osfs.go b/internal/vfs/osfs.go
index 1bf59870..5ee7b61c 100644
--- a/internal/vfs/osfs.go
+++ b/internal/vfs/osfs.go
@@ -104,7 +104,7 @@ func (*OsFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, func()
}
// Create creates or opens the named file for writing
-func (*OsFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (*OsFs) Create(name string, flag, _ int) (File, *PipeWriter, func(), error) {
var err error
var f *os.File
if flag == 0 {
diff --git a/internal/vfs/s3fs.go b/internal/vfs/s3fs.go
index 472f25e7..e48195ac 100644
--- a/internal/vfs/s3fs.go
+++ b/internal/vfs/s3fs.go
@@ -242,7 +242,13 @@ func (fs *S3Fs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, fun
}
// Create creates or opens the named file for writing
-func (fs *S3Fs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *S3Fs) Create(name string, flag, checks int) (File, *PipeWriter, func(), error) {
+ if checks&CheckParentDir != 0 {
+ _, err := fs.Stat(path.Dir(name))
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ }
r, w, err := pipeat.PipeInDir(fs.localTempDir)
if err != nil {
return nil, nil, nil, err
@@ -290,6 +296,10 @@ func (fs *S3Fs) Rename(source, target string) (int, int64, error) {
if source == target {
return -1, -1, nil
}
+ _, err := fs.Stat(path.Dir(target))
+ if err != nil {
+ return -1, -1, err
+ }
fi, err := fs.Stat(source)
if err != nil {
return -1, -1, err
@@ -803,7 +813,7 @@ func (fs *S3Fs) mkdirInternal(name string) error {
if !strings.HasSuffix(name, "/") {
name += "/"
}
- _, w, _, err := fs.Create(name, -1)
+ _, w, _, err := fs.Create(name, -1, 0)
if err != nil {
return err
}
diff --git a/internal/vfs/sftpfs.go b/internal/vfs/sftpfs.go
index 88833fd0..81a0bbd5 100644
--- a/internal/vfs/sftpfs.go
+++ b/internal/vfs/sftpfs.go
@@ -374,7 +374,7 @@ func (fs *SFTPFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, f
}
// Create creates or opens the named file for writing
-func (fs *SFTPFs) Create(name string, flag int) (File, *PipeWriter, func(), error) {
+func (fs *SFTPFs) Create(name string, flag, _ int) (File, *PipeWriter, func(), error) {
client, err := fs.conn.getClient()
if err != nil {
return nil, nil, nil, err
diff --git a/internal/vfs/vfs.go b/internal/vfs/vfs.go
index 1cb2cfd5..3022a97b 100644
--- a/internal/vfs/vfs.go
+++ b/internal/vfs/vfs.go
@@ -45,6 +45,11 @@ const (
azBlobFsName = "AzureBlobFs"
)
+// Additional checks for files
+const (
+ CheckParentDir = 1
+)
+
var (
validAzAccessTier = []string{"", "Archive", "Hot", "Cool"}
// ErrStorageSizeUnavailable is returned if the storage backend does not support getting the size
@@ -89,7 +94,7 @@ type Fs interface {
Stat(name string) (os.FileInfo, error)
Lstat(name string) (os.FileInfo, error)
Open(name string, offset int64) (File, *pipeat.PipeReaderAt, func(), error)
- Create(name string, flag int) (File, *PipeWriter, func(), error)
+ Create(name string, flag, checks int) (File, *PipeWriter, func(), error)
Rename(source, target string) (int, int64, error)
Remove(name string, isDir bool) error
Mkdir(name string) error
diff --git a/internal/webdavd/handler.go b/internal/webdavd/handler.go
index e2074fdc..bc5d7379 100644
--- a/internal/webdavd/handler.go
+++ b/internal/webdavd/handler.go
@@ -215,7 +215,7 @@ func (c *Connection) handleUploadToNewFile(fs vfs.Fs, resolvedPath, filePath, re
c.Log(logger.LevelDebug, "upload for file %q denied by pre action: %v", requestPath, err)
return nil, c.GetPermissionDeniedError()
}
- file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
+ file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, c.GetCreateChecks(requestPath, true))
if err != nil {
c.Log(logger.LevelError, "error creating file %q: %+v", resolvedPath, err)
return nil, c.GetFsError(fs, err)
@@ -262,7 +262,7 @@ func (c *Connection) handleUploadToExistingFile(fs vfs.Fs, resolvedPath, filePat
}
}
- file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
+ file, w, cancelFn, err := fs.Create(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, c.GetCreateChecks(requestPath, false))
if err != nil {
c.Log(logger.LevelError, "error creating file %q: %+v", resolvedPath, err)
return nil, c.GetFsError(fs, err)
diff --git a/openapi/httpfs.yaml b/openapi/httpfs.yaml
index ca556192..32420622 100644
--- a/openapi/httpfs.yaml
+++ b/openapi/httpfs.yaml
@@ -101,6 +101,13 @@ paths:
schema:
type: integer
format: int32
+ - name: checks
+ in: query
+ description: 'If set to `1`, the parent directory must exist before creating the file'
+ required: false
+ schema:
+ type: integer
+ format: int32
post:
tags:
- fs
diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml
index 3879d323..1ad06254 100644
--- a/openapi/openapi.yaml
+++ b/openapi/openapi.yaml
@@ -3368,7 +3368,7 @@ paths:
tags:
- users
summary: Update user
- description: 'Updates an existing user and optionally disconnects it, if connected, to apply the new settings. Recovery codes and TOTP configuration cannot be set/updated using this API: each user must use the specific APIs'
+ description: 'Updates an existing user and optionally disconnects it, if connected, to apply the new settings. The current password will be preserved if the password field is omitted in the request body. Recovery codes and TOTP configuration cannot be set/updated using this API: each user must use the specific APIs'
operationId: update_user
parameters:
- in: query
@@ -5682,13 +5682,16 @@ components:
password:
type: string
format: password
- description: password or public key/SSH user certificate are mandatory. If the password has no known hashing algo prefix it will be stored, by default, using bcrypt, argon2id is supported too. You can send a password hashed as bcrypt ($2a$ prefix), argon2id, pbkdf2 or unix crypt and it will be stored as is. For security reasons this field is omitted when you search/get users
+ description: If the password has no known hashing algo prefix it will be stored, by default, using bcrypt, argon2id is supported too. You can send a password hashed as bcrypt ($2a$ prefix), argon2id, pbkdf2 or unix crypt and it will be stored as is. For security reasons this field is omitted when you search/get users
public_keys:
type: array
items:
type: string
example: ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEUWwDwEWhTbF0MqAsp/oXK1HR2cElhM8oo1uVmL3ZeDKDiTm4ljMr92wfTgIGDqIoxmVqgYIkAOAhuykAVWBzc= user@host
- description: Public keys in OpenSSH format. A password or at least one public key/SSH user certificate are mandatory.
+ description: Public keys in OpenSSH format.
+ has_password:
+ type: boolean
+ description: Indicates whether the password is set
home_dir:
type: string
description: path to the user home directory. The user cannot upload or download files outside this directory. SFTPGo tries to automatically create this folder if missing. Must be an absolute path
diff --git a/pkgs/build.sh b/pkgs/build.sh
index 9c342d11..d2b9173b 100755
--- a/pkgs/build.sh
+++ b/pkgs/build.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-NFPM_VERSION=2.27.1
+NFPM_VERSION=2.28.0
NFPM_ARCH=${NFPM_ARCH:-amd64}
if [ -z ${SFTPGO_VERSION} ]
then
diff --git a/templates/webclient/files.html b/templates/webclient/files.html
index 0e47cb55..f3fa7397 100644
--- a/templates/webclient/files.html
+++ b/templates/webclient/files.html
@@ -159,7 +159,10 @@ along with this program. If not, see