mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
sftpd: remove folder_prefix
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
1fc0f21506
commit
aa426016f2
9 changed files with 2 additions and 881 deletions
1
go.mod
1
go.mod
|
@ -30,7 +30,6 @@ require (
|
||||||
github.com/go-chi/jwtauth/v5 v5.3.1
|
github.com/go-chi/jwtauth/v5 v5.3.1
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/go-sql-driver/mysql v1.8.1
|
github.com/go-sql-driver/mysql v1.8.1
|
||||||
github.com/golang/mock v1.6.0
|
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hashicorp/go-hclog v1.6.3
|
github.com/hashicorp/go-hclog v1.6.3
|
||||||
|
|
13
go.sum
13
go.sum
|
@ -175,8 +175,6 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4er
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
@ -406,7 +404,6 @@ github.com/wneessen/go-mail v0.4.1 h1:m2rSg/sc8FZQCdtrV5M8ymHYOFrC6KJAQAIcgrXvqo
|
||||||
github.com/wneessen/go-mail v0.4.1/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
|
github.com/wneessen/go-mail v0.4.1/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
|
||||||
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a h1:XfF01GyP+0eWCaVp0y6rNN+kFp7pt9Da4UUYrJ5XPWA=
|
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a h1:XfF01GyP+0eWCaVp0y6rNN+kFp7pt9Da4UUYrJ5XPWA=
|
||||||
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a/go.mod h1:aXb8yZQEWo1XHGMf1qQfnb83GR/EJ2EBlwtUgAaNBoE=
|
github.com/yl2chen/cidranger v1.0.3-0.20210928021809-d1cb2c52f37a/go.mod h1:aXb8yZQEWo1XHGMf1qQfnb83GR/EJ2EBlwtUgAaNBoE=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
@ -438,7 +435,6 @@ golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSO
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
|
@ -449,7 +445,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
@ -461,7 +456,6 @@ golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
|
@ -472,10 +466,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -494,7 +485,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
@ -514,15 +504,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||||
google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4=
|
google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4=
|
||||||
|
|
|
@ -269,7 +269,6 @@ func Init() {
|
||||||
KeyboardInteractiveAuthentication: true,
|
KeyboardInteractiveAuthentication: true,
|
||||||
KeyboardInteractiveHook: "",
|
KeyboardInteractiveHook: "",
|
||||||
PasswordAuthentication: true,
|
PasswordAuthentication: true,
|
||||||
FolderPrefix: "",
|
|
||||||
},
|
},
|
||||||
FTPD: ftpd.Configuration{
|
FTPD: ftpd.Configuration{
|
||||||
Bindings: []ftpd.Binding{defaultFTPDBinding},
|
Bindings: []ftpd.Binding{defaultFTPDBinding},
|
||||||
|
@ -2024,7 +2023,6 @@ func setViperDefaults() {
|
||||||
viper.SetDefault("sftpd.keyboard_interactive_authentication", globalConf.SFTPD.KeyboardInteractiveAuthentication)
|
viper.SetDefault("sftpd.keyboard_interactive_authentication", globalConf.SFTPD.KeyboardInteractiveAuthentication)
|
||||||
viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook)
|
viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook)
|
||||||
viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication)
|
viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication)
|
||||||
viper.SetDefault("sftpd.folder_prefix", globalConf.SFTPD.FolderPrefix)
|
|
||||||
viper.SetDefault("ftpd.banner", globalConf.FTPD.Banner)
|
viper.SetDefault("ftpd.banner", globalConf.FTPD.Banner)
|
||||||
viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile)
|
viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile)
|
||||||
viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20)
|
viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20)
|
||||||
|
|
|
@ -2109,29 +2109,6 @@ func newFakeListener(err error) net.Listener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFolderPrefix(t *testing.T) {
|
|
||||||
c := Configuration{
|
|
||||||
FolderPrefix: "files",
|
|
||||||
}
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Equal(t, "/files", c.FolderPrefix)
|
|
||||||
c.FolderPrefix = ""
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Empty(t, c.FolderPrefix)
|
|
||||||
c.FolderPrefix = "/"
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Empty(t, c.FolderPrefix)
|
|
||||||
c.FolderPrefix = "/."
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Empty(t, c.FolderPrefix)
|
|
||||||
c.FolderPrefix = "."
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Empty(t, c.FolderPrefix)
|
|
||||||
c.FolderPrefix = ".."
|
|
||||||
c.checkFolderPrefix()
|
|
||||||
assert.Empty(t, c.FolderPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoadRevokedUserCertsFile(t *testing.T) {
|
func TestLoadRevokedUserCertsFile(t *testing.T) {
|
||||||
r := revokedCertificates{
|
r := revokedCertificates{
|
||||||
certs: map[string]bool{},
|
certs: map[string]bool{},
|
||||||
|
|
|
@ -1,245 +0,0 @@
|
||||||
// Copyright (C) 2019 Nicola Murino
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published
|
|
||||||
// by the Free Software Foundation, version 3.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package sftpd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/sftp"
|
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/internal/vfs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Middleware defines the interface for SFTP middlewares
|
|
||||||
type Middleware interface {
|
|
||||||
sftp.FileReader
|
|
||||||
sftp.FileWriter
|
|
||||||
sftp.OpenFileWriter
|
|
||||||
sftp.FileCmder
|
|
||||||
sftp.StatVFSFileCmder
|
|
||||||
sftp.FileLister
|
|
||||||
sftp.LstatFileLister
|
|
||||||
}
|
|
||||||
|
|
||||||
type prefixMatch uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
pathContainsPrefix prefixMatch = iota
|
|
||||||
pathIsPrefixParent
|
|
||||||
pathDiverged
|
|
||||||
|
|
||||||
methodList = "List"
|
|
||||||
methodStat = "Stat"
|
|
||||||
)
|
|
||||||
|
|
||||||
type prefixMiddleware struct {
|
|
||||||
prefix string
|
|
||||||
next Middleware
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPrefixMiddleware(prefix string, next Middleware) Middleware {
|
|
||||||
return &prefixMiddleware{
|
|
||||||
prefix: prefix,
|
|
||||||
next: next,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) Lstat(request *sftp.Request) (sftp.ListerAt, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.Lstat(request)
|
|
||||||
case pathIsPrefixParent:
|
|
||||||
return listerAt([]os.FileInfo{
|
|
||||||
vfs.NewFileInfo(request.Filepath, true, 0, time.Unix(0, 0), false),
|
|
||||||
}), nil
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) OpenFile(request *sftp.Request) (sftp.WriterAtReaderAt, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.OpenFile(request)
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) Filelist(request *sftp.Request) (sftp.ListerAt, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.Filelist(request)
|
|
||||||
case pathIsPrefixParent:
|
|
||||||
switch request.Method {
|
|
||||||
case methodList:
|
|
||||||
modTime := time.Unix(0, 0)
|
|
||||||
fileName := p.nextListFolder(request.Filepath)
|
|
||||||
files := make([]os.FileInfo, 0, 3)
|
|
||||||
files = append(files, vfs.NewFileInfo(".", true, 0, modTime, false))
|
|
||||||
if request.Filepath != "/" {
|
|
||||||
files = append(files, vfs.NewFileInfo("..", true, 0, modTime, false))
|
|
||||||
}
|
|
||||||
files = append(files, vfs.NewFileInfo(fileName, true, 0, modTime, false))
|
|
||||||
return listerAt(files), nil
|
|
||||||
case methodStat:
|
|
||||||
return listerAt([]os.FileInfo{
|
|
||||||
vfs.NewFileInfo(request.Filepath, true, 0, time.Unix(0, 0), false),
|
|
||||||
}), nil
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxOpUnsupported
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) Filewrite(request *sftp.Request) (io.WriterAt, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
// forward to next handler
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.Filewrite(request)
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) Fileread(request *sftp.Request) (io.ReaderAt, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.Fileread(request)
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) Filecmd(request *sftp.Request) error {
|
|
||||||
switch request.Method {
|
|
||||||
case "Rename", "Symlink":
|
|
||||||
if getPrefixHierarchy(p.prefix, request.Filepath) == pathContainsPrefix &&
|
|
||||||
getPrefixHierarchy(p.prefix, request.Target) == pathContainsPrefix {
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
request.Target, _ = p.removeFolderPrefix(request.Target)
|
|
||||||
return p.next.Filecmd(request)
|
|
||||||
}
|
|
||||||
return sftp.ErrSSHFxPermissionDenied
|
|
||||||
// commands have a source and destination (file path and target path)
|
|
||||||
case "Setstat", "Rmdir", "Mkdir", "Remove":
|
|
||||||
// commands just the file path
|
|
||||||
if getPrefixHierarchy(p.prefix, request.Filepath) == pathContainsPrefix {
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.Filecmd(request)
|
|
||||||
}
|
|
||||||
return sftp.ErrSSHFxPermissionDenied
|
|
||||||
default:
|
|
||||||
return sftp.ErrSSHFxOpUnsupported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) StatVFS(request *sftp.Request) (*sftp.StatVFS, error) {
|
|
||||||
switch getPrefixHierarchy(p.prefix, request.Filepath) {
|
|
||||||
case pathContainsPrefix:
|
|
||||||
// forward to next handler
|
|
||||||
request.Filepath, _ = p.removeFolderPrefix(request.Filepath)
|
|
||||||
return p.next.StatVFS(request)
|
|
||||||
default:
|
|
||||||
return nil, sftp.ErrSSHFxPermissionDenied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) nextListFolder(requestPath string) string {
|
|
||||||
cleanPath := path.Clean(`/` + requestPath)
|
|
||||||
cleanPrefix := path.Clean(`/` + p.prefix)
|
|
||||||
|
|
||||||
fileName := cleanPrefix[len(cleanPath):]
|
|
||||||
fileName = strings.TrimLeft(fileName, `/`)
|
|
||||||
slashIndex := strings.Index(fileName, `/`)
|
|
||||||
if slashIndex > 0 {
|
|
||||||
return fileName[0:slashIndex]
|
|
||||||
}
|
|
||||||
return fileName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) containsPrefix(virtualPath string) bool {
|
|
||||||
if !path.IsAbs(virtualPath) {
|
|
||||||
virtualPath = path.Clean(`/` + virtualPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.prefix == `/` || p.prefix == `` {
|
|
||||||
return true
|
|
||||||
} else if p.prefix == virtualPath {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.HasPrefix(virtualPath, p.prefix+`/`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prefixMiddleware) removeFolderPrefix(virtualPath string) (string, bool) {
|
|
||||||
if p.prefix == `/` || p.prefix == `` {
|
|
||||||
return virtualPath, true
|
|
||||||
}
|
|
||||||
|
|
||||||
virtualPath = path.Clean(`/` + virtualPath)
|
|
||||||
if p.containsPrefix(virtualPath) {
|
|
||||||
effectivePath := virtualPath[len(p.prefix):]
|
|
||||||
if effectivePath == `` {
|
|
||||||
effectivePath = `/`
|
|
||||||
}
|
|
||||||
return effectivePath, true
|
|
||||||
}
|
|
||||||
return virtualPath, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPrefixHierarchy(prefix, virtualPath string) prefixMatch {
|
|
||||||
prefixSplit := strings.Split(path.Clean(`/`+prefix), `/`)
|
|
||||||
pathSplit := strings.Split(path.Clean(`/`+virtualPath), `/`)
|
|
||||||
|
|
||||||
for {
|
|
||||||
// stop if either slice is empty of the current head elements do not match
|
|
||||||
if len(prefixSplit) == 0 || len(pathSplit) == 0 ||
|
|
||||||
prefixSplit[0] != pathSplit[0] {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
prefixSplit = prefixSplit[1:]
|
|
||||||
pathSplit = pathSplit[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// The entire Prefix is included in Test Path
|
|
||||||
// Example: Prefix (/files) with Test Path (/files/test.csv)
|
|
||||||
if len(prefixSplit) == 0 ||
|
|
||||||
(len(prefixSplit) == 1 && prefixSplit[0] == ``) {
|
|
||||||
return pathContainsPrefix
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Path is part of the Prefix Hierarchy
|
|
||||||
// Example: Prefix (/files) with Test Path (/)
|
|
||||||
if len(pathSplit) == 0 ||
|
|
||||||
(len(pathSplit) == 1 && pathSplit[0] == ``) {
|
|
||||||
return pathIsPrefixParent
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test Path is not with the Prefix Hierarchy
|
|
||||||
// Example: Prefix (/files) with Test Path (/files2)
|
|
||||||
return pathDiverged
|
|
||||||
}
|
|
|
@ -1,370 +0,0 @@
|
||||||
// Copyright (C) 2019 Nicola Murino
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published
|
|
||||||
// by the Free Software Foundation, version 3.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package sftpd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/pkg/sftp"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
|
|
||||||
"github.com/drakkan/sftpgo/v2/internal/sftpd/mocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PrefixMiddlewareSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
MockCtl *gomock.Controller
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) BeforeTest(_, _ string) {
|
|
||||||
Suite.MockCtl = gomock.NewController(Suite.T())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) AfterTest(_, _ string) {
|
|
||||||
Suite.MockCtl.Finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileWriter() {
|
|
||||||
prefix := prefixMiddleware{prefix: `/files`}
|
|
||||||
|
|
||||||
// parent of prefix
|
|
||||||
WriterAt, err := prefix.Filewrite(&sftp.Request{Filepath: `/`})
|
|
||||||
Suite.Nil(WriterAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path and prefix are unrelated
|
|
||||||
WriterAt, err = prefix.Filewrite(&sftp.Request{Filepath: `/random`})
|
|
||||||
Suite.Nil(WriterAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path is sub path of configured prefix
|
|
||||||
// mocked returns are not import, just the call to the next file writer
|
|
||||||
mockedWriter := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
mockedWriter.EXPECT().
|
|
||||||
Filewrite(&sftp.Request{Filepath: `/data`}).
|
|
||||||
Return(nil, nil)
|
|
||||||
prefix.next = mockedWriter
|
|
||||||
WriterAt, err = prefix.Filewrite(&sftp.Request{Filepath: `/files/data`})
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.Nil(WriterAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileReader() {
|
|
||||||
middleware := prefixMiddleware{prefix: `/files`}
|
|
||||||
|
|
||||||
// parent of prefix
|
|
||||||
ReaderAt, err := middleware.Fileread(&sftp.Request{Filepath: `/`})
|
|
||||||
Suite.Nil(ReaderAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path and prefix are unrelated
|
|
||||||
ReaderAt, err = middleware.Fileread(&sftp.Request{Filepath: `/random`})
|
|
||||||
Suite.Nil(ReaderAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path is sub path of configured prefix
|
|
||||||
// mocked returns are not import, just the call to the next file writer
|
|
||||||
mockedReader := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
mockedReader.EXPECT().
|
|
||||||
Fileread(&sftp.Request{Filepath: `/data`}).
|
|
||||||
Return(nil, nil)
|
|
||||||
middleware.next = mockedReader
|
|
||||||
ReaderAt, err = middleware.Fileread(&sftp.Request{Filepath: `/files/data`})
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.Nil(ReaderAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestOpenFile() {
|
|
||||||
middleware := prefixMiddleware{prefix: `/files`}
|
|
||||||
|
|
||||||
ReadWriteAt, err := middleware.OpenFile(&sftp.Request{Filepath: `/`})
|
|
||||||
Suite.Nil(ReadWriteAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path and prefix are unrelated
|
|
||||||
ReadWriteAt, err = middleware.OpenFile(&sftp.Request{Filepath: `/random`})
|
|
||||||
Suite.Nil(ReadWriteAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
RequestPath string
|
|
||||||
NextPath string
|
|
||||||
}{
|
|
||||||
// test normalization of various request paths
|
|
||||||
{RequestPath: `/files/data.csv`, NextPath: `/data.csv`},
|
|
||||||
{RequestPath: `files/data.csv`, NextPath: `/data.csv`},
|
|
||||||
{RequestPath: `//files/./data.csv`, NextPath: `/data.csv`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
OpenFileMock := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
OpenFileMock.EXPECT().
|
|
||||||
OpenFile(&sftp.Request{Filepath: test.NextPath}).
|
|
||||||
Return(nil, nil)
|
|
||||||
middleware.next = OpenFileMock
|
|
||||||
|
|
||||||
ReadWriteAt, err = middleware.OpenFile(&sftp.Request{Filepath: test.RequestPath})
|
|
||||||
Suite.Nil(ReadWriteAt)
|
|
||||||
Suite.Nil(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestStatVFS() {
|
|
||||||
prefix := prefixMiddleware{prefix: `/files`}
|
|
||||||
|
|
||||||
// parent of prefix
|
|
||||||
res, err := prefix.StatVFS(&sftp.Request{Filepath: `/`})
|
|
||||||
Suite.Nil(res)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path and prefix are unrelated
|
|
||||||
res, err = prefix.StatVFS(&sftp.Request{Filepath: `/random`})
|
|
||||||
Suite.Nil(res)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
// file path is sub path of configured prefix
|
|
||||||
// mocked returns are not import, just the call to the next file writer
|
|
||||||
statVFSMock := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
statVFSMock.EXPECT().
|
|
||||||
StatVFS(&sftp.Request{Filepath: `/data`}).
|
|
||||||
Return(nil, nil)
|
|
||||||
prefix.next = statVFSMock
|
|
||||||
res, err = prefix.StatVFS(&sftp.Request{Filepath: `/files/data`})
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.Nil(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileListForwarding() {
|
|
||||||
var tests = []struct {
|
|
||||||
Method string
|
|
||||||
FilePath string
|
|
||||||
FwdPath string
|
|
||||||
}{
|
|
||||||
{Method: `List`, FilePath: `/files/data`, FwdPath: `/data`},
|
|
||||||
{Method: `List`, FilePath: `/./files/data`, FwdPath: `/data`},
|
|
||||||
{Method: `List`, FilePath: `files/data`, FwdPath: `/data`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
FileListMock := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
FileListMock.EXPECT().
|
|
||||||
Filelist(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.FwdPath,
|
|
||||||
}).Return(nil, nil)
|
|
||||||
|
|
||||||
handlers := newPrefixMiddleware(`/files`, FileListMock)
|
|
||||||
ListerAt, err := handlers.Filelist(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.FilePath,
|
|
||||||
})
|
|
||||||
Suite.Nil(ListerAt)
|
|
||||||
Suite.Nil(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileList() {
|
|
||||||
var tests = []struct {
|
|
||||||
Method string
|
|
||||||
FilePath string
|
|
||||||
ExpectedErr error
|
|
||||||
ExpectedPath string
|
|
||||||
ExpectedItems int
|
|
||||||
}{
|
|
||||||
{Method: `List`, FilePath: `/random`, ExpectedErr: sftp.ErrSSHFxPermissionDenied, ExpectedItems: 0},
|
|
||||||
{Method: `List`, FilePath: `/`, ExpectedPath: `files`, ExpectedItems: 2},
|
|
||||||
{Method: `Stat`, FilePath: `/`, ExpectedPath: `/`, ExpectedItems: 1},
|
|
||||||
{Method: `NotAnOp`, ExpectedErr: sftp.ErrSSHFxOpUnsupported},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
middleware := prefixMiddleware{prefix: `/files`}
|
|
||||||
ListerAt, err := middleware.Filelist(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.FilePath,
|
|
||||||
})
|
|
||||||
if test.ExpectedErr != nil {
|
|
||||||
Suite.Equal(test.ExpectedErr, err)
|
|
||||||
Suite.Nil(ListerAt)
|
|
||||||
} else {
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.IsType(listerAt{}, ListerAt)
|
|
||||||
if directList, ok := ListerAt.(listerAt); ok {
|
|
||||||
Suite.Len(directList, test.ExpectedItems)
|
|
||||||
if test.ExpectedItems > 1 {
|
|
||||||
Suite.Equal(".", directList[0].Name())
|
|
||||||
}
|
|
||||||
Suite.Equal(test.ExpectedPath, directList[test.ExpectedItems-1].Name())
|
|
||||||
Suite.Equal(int64(0), directList[test.ExpectedItems-1].ModTime().Unix())
|
|
||||||
Suite.True(directList[test.ExpectedItems-1].IsDir())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestLstat() {
|
|
||||||
middleware := prefixMiddleware{prefix: `/files`}
|
|
||||||
ListerAt, err := middleware.Lstat(&sftp.Request{Filepath: `/`})
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.IsType(listerAt{}, ListerAt)
|
|
||||||
if directList, ok := ListerAt.(listerAt); ok {
|
|
||||||
Suite.Len(directList, 1)
|
|
||||||
Suite.Equal(`/`, directList[0].Name())
|
|
||||||
Suite.Equal(int64(0), directList[0].ModTime().Unix())
|
|
||||||
Suite.True(directList[0].IsDir())
|
|
||||||
}
|
|
||||||
|
|
||||||
middleware = prefixMiddleware{prefix: `/files`}
|
|
||||||
ListerAt, err = middleware.Lstat(&sftp.Request{Filepath: `/random`})
|
|
||||||
Suite.Nil(ListerAt)
|
|
||||||
Suite.Equal(sftp.ErrSSHFxPermissionDenied, err)
|
|
||||||
|
|
||||||
MockLstat := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
MockLstat.EXPECT().
|
|
||||||
Lstat(&sftp.Request{Filepath: "/data"}).
|
|
||||||
Return(nil, nil)
|
|
||||||
middleware = prefixMiddleware{prefix: `/files`}
|
|
||||||
middleware.next = MockLstat
|
|
||||||
|
|
||||||
ListerAt, err = middleware.Lstat(&sftp.Request{Filepath: `/files/data`})
|
|
||||||
Suite.Nil(err)
|
|
||||||
Suite.Nil(ListerAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileCmdForwarding() {
|
|
||||||
var tests = []struct {
|
|
||||||
Method string
|
|
||||||
FilePath string
|
|
||||||
TargetPath string
|
|
||||||
FwdFilePath string
|
|
||||||
FwdTargetPath string
|
|
||||||
}{
|
|
||||||
{Method: `Rename`, FilePath: `/files/data.csv`, TargetPath: `/files/new-data.csv`, FwdFilePath: `/data.csv`, FwdTargetPath: `/new-data.csv`},
|
|
||||||
{Method: `Rename`, FilePath: `files/data.csv`, TargetPath: `files/new-data.csv`, FwdFilePath: `/data.csv`, FwdTargetPath: `/new-data.csv`},
|
|
||||||
{Method: `Symlink`, FilePath: `/./files/data.csv`, TargetPath: `files/new-data.csv`, FwdFilePath: `/data.csv`, FwdTargetPath: `/new-data.csv`},
|
|
||||||
|
|
||||||
{Method: `Setstat`, FilePath: `files/data.csv`, FwdFilePath: `/data.csv`},
|
|
||||||
{Method: `Remove`, FilePath: `/./files/data.csv`, FwdFilePath: `/data.csv`},
|
|
||||||
{Method: `Rmdir`, FilePath: `files/data`, FwdFilePath: `/data`},
|
|
||||||
{Method: `Mkdir`, FilePath: `/./files/data`, FwdFilePath: `/data`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
FileCmdMock := mocks.NewMockMiddleware(Suite.MockCtl)
|
|
||||||
FileCmdMock.EXPECT().
|
|
||||||
Filecmd(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.FwdFilePath,
|
|
||||||
Target: test.FwdTargetPath,
|
|
||||||
}).Return(nil)
|
|
||||||
|
|
||||||
middleware := prefixMiddleware{
|
|
||||||
prefix: `/files`,
|
|
||||||
next: FileCmdMock,
|
|
||||||
}
|
|
||||||
|
|
||||||
Suite.Nil(middleware.Filecmd(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.FilePath,
|
|
||||||
Target: test.TargetPath,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestFileCmdErrors() {
|
|
||||||
middleware := prefixMiddleware{prefix: `/files`}
|
|
||||||
|
|
||||||
var tests = []struct {
|
|
||||||
Method string
|
|
||||||
RequestPath string
|
|
||||||
TargetPath string
|
|
||||||
ExpectedErr error
|
|
||||||
}{
|
|
||||||
// two path methods
|
|
||||||
{Method: `Rename`, RequestPath: `/`, TargetPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Rename`, RequestPath: `/random`, TargetPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Rename`, RequestPath: `/random`, TargetPath: `/files`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Symlink`, RequestPath: `/`, TargetPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Symlink`, RequestPath: `/random`, TargetPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Symlink`, RequestPath: `/random`, TargetPath: `/files`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
|
|
||||||
// single path methods
|
|
||||||
{Method: `Setstat`, RequestPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Setstat`, RequestPath: `/unrelated`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Rmdir`, RequestPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Rmdir`, RequestPath: `/unrelated`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Mkdir`, RequestPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Mkdir`, RequestPath: `/unrelated`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Remove`, RequestPath: `/`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
{Method: `Remove`, RequestPath: `/unrelated`, ExpectedErr: sftp.ErrSSHFxPermissionDenied},
|
|
||||||
|
|
||||||
{Method: `NotACmd`, ExpectedErr: sftp.ErrSSHFxOpUnsupported},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
err := middleware.Filecmd(&sftp.Request{
|
|
||||||
Method: test.Method,
|
|
||||||
Filepath: test.RequestPath,
|
|
||||||
Target: test.TargetPath,
|
|
||||||
})
|
|
||||||
Suite.Equal(test.ExpectedErr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestNextFolder() {
|
|
||||||
prefix := prefixMiddleware{prefix: `/files/data`}
|
|
||||||
Suite.Equal(`files`, prefix.nextListFolder(`/`))
|
|
||||||
Suite.Equal(`files`, prefix.nextListFolder(``))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`/files`))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`files`))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`files/`))
|
|
||||||
|
|
||||||
prefix = prefixMiddleware{prefix: `files/data`}
|
|
||||||
Suite.Equal(`files`, prefix.nextListFolder(`/`))
|
|
||||||
Suite.Equal(`files`, prefix.nextListFolder(``))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`/files`))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`files`))
|
|
||||||
Suite.Equal(`data`, prefix.nextListFolder(`files/`))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestContainsPrefix() {
|
|
||||||
prefix := prefixMiddleware{prefix: `/`}
|
|
||||||
Suite.True(prefix.containsPrefix(`/data`))
|
|
||||||
Suite.True(prefix.containsPrefix(`/`))
|
|
||||||
|
|
||||||
prefix = prefixMiddleware{prefix: `/files`}
|
|
||||||
Suite.True(prefix.containsPrefix(`files`))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Suite *PrefixMiddlewareSuite) TestRemoveFolderPrefix() {
|
|
||||||
prefix := prefixMiddleware{prefix: `/`}
|
|
||||||
path, ok := prefix.removeFolderPrefix(`/files`)
|
|
||||||
Suite.Equal(`/files`, path)
|
|
||||||
Suite.True(ok)
|
|
||||||
|
|
||||||
prefix = prefixMiddleware{prefix: `/files`}
|
|
||||||
path, ok = prefix.removeFolderPrefix(`files`)
|
|
||||||
Suite.Equal(`/`, path)
|
|
||||||
Suite.True(ok)
|
|
||||||
|
|
||||||
path, ok = prefix.removeFolderPrefix(`/random`)
|
|
||||||
Suite.Equal(`/random`, path)
|
|
||||||
Suite.False(ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFolderPrefixSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(PrefixMiddlewareSuite))
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: ../middleware.go
|
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import (
|
|
||||||
io "io"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
sftp "github.com/pkg/sftp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockMiddleware is a mock of Middleware interface.
|
|
||||||
type MockMiddleware struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockMiddlewareMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockMiddlewareMockRecorder is the mock recorder for MockMiddleware.
|
|
||||||
type MockMiddlewareMockRecorder struct {
|
|
||||||
mock *MockMiddleware
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockMiddleware creates a new mock instance.
|
|
||||||
func NewMockMiddleware(ctrl *gomock.Controller) *MockMiddleware {
|
|
||||||
mock := &MockMiddleware{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockMiddlewareMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockMiddleware) EXPECT() *MockMiddlewareMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filecmd mocks base method.
|
|
||||||
func (m *MockMiddleware) Filecmd(arg0 *sftp.Request) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Filecmd", arg0)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filecmd indicates an expected call of Filecmd.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) Filecmd(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filecmd", reflect.TypeOf((*MockMiddleware)(nil).Filecmd), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filelist mocks base method.
|
|
||||||
func (m *MockMiddleware) Filelist(arg0 *sftp.Request) (sftp.ListerAt, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Filelist", arg0)
|
|
||||||
ret0, _ := ret[0].(sftp.ListerAt)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filelist indicates an expected call of Filelist.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) Filelist(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filelist", reflect.TypeOf((*MockMiddleware)(nil).Filelist), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fileread mocks base method.
|
|
||||||
func (m *MockMiddleware) Fileread(arg0 *sftp.Request) (io.ReaderAt, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Fileread", arg0)
|
|
||||||
ret0, _ := ret[0].(io.ReaderAt)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fileread indicates an expected call of Fileread.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) Fileread(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Fileread", reflect.TypeOf((*MockMiddleware)(nil).Fileread), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filewrite mocks base method.
|
|
||||||
func (m *MockMiddleware) Filewrite(arg0 *sftp.Request) (io.WriterAt, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Filewrite", arg0)
|
|
||||||
ret0, _ := ret[0].(io.WriterAt)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filewrite indicates an expected call of Filewrite.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) Filewrite(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filewrite", reflect.TypeOf((*MockMiddleware)(nil).Filewrite), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lstat mocks base method.
|
|
||||||
func (m *MockMiddleware) Lstat(arg0 *sftp.Request) (sftp.ListerAt, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Lstat", arg0)
|
|
||||||
ret0, _ := ret[0].(sftp.ListerAt)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lstat indicates an expected call of Lstat.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) Lstat(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lstat", reflect.TypeOf((*MockMiddleware)(nil).Lstat), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFile mocks base method.
|
|
||||||
func (m *MockMiddleware) OpenFile(arg0 *sftp.Request) (sftp.WriterAtReaderAt, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "OpenFile", arg0)
|
|
||||||
ret0, _ := ret[0].(sftp.WriterAtReaderAt)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenFile indicates an expected call of OpenFile.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) OpenFile(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenFile", reflect.TypeOf((*MockMiddleware)(nil).OpenFile), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatVFS mocks base method.
|
|
||||||
func (m *MockMiddleware) StatVFS(arg0 *sftp.Request) (*sftp.StatVFS, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "StatVFS", arg0)
|
|
||||||
ret0, _ := ret[0].(*sftp.StatVFS)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatVFS indicates an expected call of StatVFS.
|
|
||||||
func (mr *MockMiddlewareMockRecorder) StatVFS(arg0 any) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StatVFS", reflect.TypeOf((*MockMiddleware)(nil).StatVFS), arg0)
|
|
||||||
}
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -180,14 +179,8 @@ type Configuration struct {
|
||||||
KeyboardInteractiveHook string `json:"keyboard_interactive_auth_hook" mapstructure:"keyboard_interactive_auth_hook"`
|
KeyboardInteractiveHook string `json:"keyboard_interactive_auth_hook" mapstructure:"keyboard_interactive_auth_hook"`
|
||||||
// PasswordAuthentication specifies whether password authentication is allowed.
|
// PasswordAuthentication specifies whether password authentication is allowed.
|
||||||
PasswordAuthentication bool `json:"password_authentication" mapstructure:"password_authentication"`
|
PasswordAuthentication bool `json:"password_authentication" mapstructure:"password_authentication"`
|
||||||
// Virtual root folder prefix to include in all file operations (ex: /files).
|
certChecker *ssh.CertChecker
|
||||||
// The virtual paths used for per-directory permissions, file patterns etc. must not include the folder prefix.
|
parsedUserCAKeys []ssh.PublicKey
|
||||||
// The prefix is only applied to SFTP requests, SCP and other SSH commands will be automatically disabled if
|
|
||||||
// you configure a prefix.
|
|
||||||
// This setting can help some migrations from OpenSSH. It is not recommended for general usage.
|
|
||||||
FolderPrefix string `json:"folder_prefix" mapstructure:"folder_prefix"`
|
|
||||||
certChecker *ssh.CertChecker
|
|
||||||
parsedUserCAKeys []ssh.PublicKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type authenticationError struct {
|
type authenticationError struct {
|
||||||
|
@ -353,7 +346,6 @@ func (c *Configuration) Initialize(configDir string) error {
|
||||||
c.configureKeyboardInteractiveAuth(serverConfig)
|
c.configureKeyboardInteractiveAuth(serverConfig)
|
||||||
c.configureLoginBanner(serverConfig, configDir)
|
c.configureLoginBanner(serverConfig, configDir)
|
||||||
c.checkSSHCommands()
|
c.checkSSHCommands()
|
||||||
c.checkFolderPrefix()
|
|
||||||
|
|
||||||
exitChannel := make(chan error, 1)
|
exitChannel := make(chan error, 1)
|
||||||
serviceStatus.Bindings = nil
|
serviceStatus.Bindings = nil
|
||||||
|
@ -665,7 +657,6 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
|
||||||
RemoteAddr: conn.RemoteAddr(),
|
RemoteAddr: conn.RemoteAddr(),
|
||||||
LocalAddr: conn.LocalAddr(),
|
LocalAddr: conn.LocalAddr(),
|
||||||
channel: channel,
|
channel: channel,
|
||||||
folderPrefix: c.FolderPrefix,
|
|
||||||
}
|
}
|
||||||
go c.handleSftpConnection(channel, connection)
|
go c.handleSftpConnection(channel, connection)
|
||||||
}
|
}
|
||||||
|
@ -678,7 +669,6 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
|
||||||
RemoteAddr: conn.RemoteAddr(),
|
RemoteAddr: conn.RemoteAddr(),
|
||||||
LocalAddr: conn.LocalAddr(),
|
LocalAddr: conn.LocalAddr(),
|
||||||
channel: channel,
|
channel: channel,
|
||||||
folderPrefix: c.FolderPrefix,
|
|
||||||
}
|
}
|
||||||
ok = processSSHCommand(req.Payload, &connection, c.EnabledSSHCommands)
|
ok = processSSHCommand(req.Payload, &connection, c.EnabledSSHCommands)
|
||||||
}
|
}
|
||||||
|
@ -718,17 +708,6 @@ func (c *Configuration) handleSftpConnection(channel ssh.Channel, connection *Co
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Configuration) createHandlers(connection *Connection) sftp.Handlers {
|
func (c *Configuration) createHandlers(connection *Connection) sftp.Handlers {
|
||||||
if c.FolderPrefix != "" {
|
|
||||||
prefixMiddleware := newPrefixMiddleware(c.FolderPrefix, connection)
|
|
||||||
|
|
||||||
return sftp.Handlers{
|
|
||||||
FileGet: prefixMiddleware,
|
|
||||||
FilePut: prefixMiddleware,
|
|
||||||
FileCmd: prefixMiddleware,
|
|
||||||
FileList: prefixMiddleware,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sftp.Handlers{
|
return sftp.Handlers{
|
||||||
FileGet: connection,
|
FileGet: connection,
|
||||||
FilePut: connection,
|
FilePut: connection,
|
||||||
|
@ -870,19 +849,6 @@ func (c *Configuration) checkSSHCommands() {
|
||||||
logger.Debug(logSender, "", "enabled SSH commands %v", c.EnabledSSHCommands)
|
logger.Debug(logSender, "", "enabled SSH commands %v", c.EnabledSSHCommands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Configuration) checkFolderPrefix() {
|
|
||||||
if c.FolderPrefix != "" {
|
|
||||||
c.FolderPrefix = path.Join("/", c.FolderPrefix)
|
|
||||||
if c.FolderPrefix == "/" {
|
|
||||||
c.FolderPrefix = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.FolderPrefix != "" {
|
|
||||||
c.EnabledSSHCommands = nil
|
|
||||||
logger.Debug(logSender, "", "folder prefix %q configured, SSH commands are disabled", c.FolderPrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Configuration) generateDefaultHostKeys(configDir string) error {
|
func (c *Configuration) generateDefaultHostKeys(configDir string) error {
|
||||||
var err error
|
var err error
|
||||||
defaultHostKeys := []string{defaultPrivateRSAKeyName, defaultPrivateECDSAKeyName, defaultPrivateEd25519KeyName}
|
defaultHostKeys := []string{defaultPrivateRSAKeyName, defaultPrivateECDSAKeyName, defaultPrivateEd25519KeyName}
|
||||||
|
|
|
@ -320,7 +320,6 @@ func TestMain(m *testing.M) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sftpdConf.PasswordAuthentication = true
|
sftpdConf.PasswordAuthentication = true
|
||||||
sftpdConf.FolderPrefix = "/prefix/files"
|
|
||||||
go func(cfg sftpd.Configuration) {
|
go func(cfg sftpd.Configuration) {
|
||||||
logger.Debug(logSender, "", "initializing SFTP server with config %+v and proxy protocol %v",
|
logger.Debug(logSender, "", "initializing SFTP server with config %+v and proxy protocol %v",
|
||||||
cfg, common.Config.ProxyProtocol)
|
cfg, common.Config.ProxyProtocol)
|
||||||
|
@ -888,56 +887,6 @@ func TestStartDirectory(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFolderPrefix(t *testing.T) {
|
|
||||||
usePubKey := true
|
|
||||||
u := getTestUser(usePubKey)
|
|
||||||
u.QuotaFiles = 1000
|
|
||||||
user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = os.RemoveAll(user.GetHomeDir())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
conn, client, err := getSftpClientWithAddr(user, usePubKey, "127.0.0.1:2226")
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
defer conn.Close()
|
|
||||||
defer client.Close()
|
|
||||||
err = checkBasicSFTP(client)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = client.Stat("path")
|
|
||||||
assert.ErrorIs(t, err, os.ErrPermission)
|
|
||||||
_, err = client.Stat("/prefix/path")
|
|
||||||
assert.ErrorIs(t, err, os.ErrPermission)
|
|
||||||
_, err = client.Stat("/prefix/files1")
|
|
||||||
assert.ErrorIs(t, err, os.ErrPermission)
|
|
||||||
contents, err := client.ReadDir("/")
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
if assert.Len(t, contents, 1) {
|
|
||||||
assert.Equal(t, "prefix", contents[0].Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contents, err = client.ReadDir("/prefix")
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
if assert.Len(t, contents, 1) {
|
|
||||||
assert.Equal(t, "files", contents[0].Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = client.OpenFile(testFileName, os.O_WRONLY|os.O_CREATE)
|
|
||||||
assert.ErrorIs(t, err, os.ErrPermission)
|
|
||||||
_, err = client.OpenFile(testFileName, os.O_RDONLY)
|
|
||||||
assert.ErrorIs(t, err, os.ErrPermission)
|
|
||||||
|
|
||||||
f, err := client.OpenFile(path.Join("prefix", "files", testFileName), os.O_WRONLY|os.O_CREATE)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = f.Write([]byte("test"))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = f.Close()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = os.RemoveAll(user.GetHomeDir())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoginNonExistentUser(t *testing.T) {
|
func TestLoginNonExistentUser(t *testing.T) {
|
||||||
usePubKey := true
|
usePubKey := true
|
||||||
user := getTestUser(usePubKey)
|
user := getTestUser(usePubKey)
|
||||||
|
|
Loading…
Reference in a new issue