mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 15:10:23 +00:00
Web: allow to require password change and two-factor for admins
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
51ae2d7301
commit
de089e51fd
18 changed files with 650 additions and 333 deletions
50
go.mod
50
go.mod
|
@ -9,15 +9,15 @@ require (
|
|||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.2
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.2
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.1
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.4
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.3
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.3
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.3
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.2
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.6
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
|
@ -38,7 +38,7 @@ require (
|
|||
github.com/hashicorp/go-retryablehttp v0.7.5
|
||||
github.com/jackc/pgx/v5 v5.5.3
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/klauspost/compress v1.17.6
|
||||
github.com/klauspost/compress v1.17.7
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.20
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
|
@ -74,7 +74,7 @@ require (
|
|||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/term v0.17.0
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/api v0.165.0
|
||||
google.golang.org/api v0.166.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
|
@ -85,18 +85,18 @@ require (
|
|||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 // indirect
|
||||
github.com/aws/smithy-go v1.20.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.2 // indirect
|
||||
github.com/aws/smithy-go v1.20.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
|
@ -173,9 +173,9 @@ require (
|
|||
golang.org/x/tools v0.18.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/grpc v1.61.1 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
100
go.sum
100
go.sum
|
@ -33,48 +33,48 @@ github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHc
|
|||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.0 h1:sv7+1JVJxOu/dD/sz/csHX7jFqmP001TIY7aytBWDSQ=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.0/go.mod h1:G104G1Aho5WqF+SR3mDIobTABQzpYV0WxMsKxlMggOA=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0 h1:2UO6/nT1lCZq1LqM67Oa4tdgP1CvL1sLSxvuD+VrOeE=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.0/go.mod h1:5zGj2eA85ClyedTDK+Whsu+w9yimnVIZvhvBKrDquM8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.0 h1:J5sdGCAHuWKIXLeXiqr8II/adSvetkx0qdZwdbXXpb0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.0/go.mod h1:cfh8v69nuSUohNFMbIISP2fhmblGmYEOKs5V53HiHnk=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.0 h1:lMW2x6sKBsiAJrpi1doOXqWFyEPoE886DTb1X0wb7So=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.0/go.mod h1:uT41FIH8cCIxOdUYIL0PYyHlL1NoneDuDSCwg5VE/5o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0 h1:xWCwjjvVz2ojYTP4kBKUuUh9ZrXfcAXpflhOUUeXg1k=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.0/go.mod h1:j3fACuqXg4oMTQOR2yY7m0NmJY0yBK4L4sLsRXq1Ins=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2 h1:VEekE/fJWqAWYozxFQ07B+h8NdvTPAYhV13xIBenuO0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.2/go.mod h1:8vozqAHmDNmoD4YbuDKIfpnLbByzngczL4My1RELLVo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0 h1:NPs/EqVO+ajwOoq56EfcGKa3L3ruWuazkIw1BqxwOPw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.0/go.mod h1:D+duLy2ylgatV+yTlQ8JTuLfDD0BnFvnQRc+o6tbZ4M=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0 h1:ks7KGMVUMoDzcxNWUlEdI+/lokMFD136EL6DWmUOV80=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.0/go.mod h1:hL6BWM/d/qz113fVitZjbXR0E+RCTU1+x+1Idyn5NgE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.1 h1:P7hU6A5qEdmajGwvae/zDkOq+ULLC9tQBTwqqiwFGpI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.1/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.2 h1:XnMKB9JRjfnxg9ZkUic4MiapnWJISWRo8HVM+7nx9qQ=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.2/go.mod h1:z/XIktFoVIKNEqX/811vx4eHetrC3tAkgJKL1ZY/KM4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.2 h1:tCZXWtH0HiIEZ50NJ7/QEaXmuzEd36L+2JUiZkp2nsc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.2/go.mod h1:7Zo+D6q4auSIo3p4EItuTKTk7J+RqjASISZqLvmUgpc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.1 h1:lk1ZZFbdb24qpOwVC1AwYNrswUjAxeyey6kFBVANudQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.1/go.mod h1:/xJ6x1NehNGCX4tvGzzj2bq5TBOT/Yxq+qbL9Jpx2Vk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.4 h1:yuhSpqtahkrC8kRCU5v4gEaTDy/ccTIPIkufIRF7YTk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.4/go.mod h1:q3SxgP2WD9YRLCybtyse8EgO3vKKWVmxlTmBNeRXPyk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1 h1:evvi7FbTAoFxdP/mixmP7LIYzQWAmzBcwNB/es9XPNc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.1/go.mod h1:rH61DT6FDdikhPghymripNUCsf+uVF4Cnk4c4DBKH64=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1 h1:RAnaIrbxPtlXNVI/OIlh1sidTQ3e1qM6LRjs7N0bE0I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.1/go.mod h1:nbgAGkH5lk0RZRMh6A4K/oG6Xj11eC/1CyDow+DUAFI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0 h1:TkbRExyKSVHELwG9gz2+gql37jjec2R5vus9faTomwE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.0/go.mod h1:T3/9xMKudHhnj8it5EqIrhvv11tVZqWYkKcot+BFStc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0 h1:a33HuFlO0KsveiP90IUJh8Xr/cx9US2PqkSroaLc+o8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.0/go.mod h1:SxIkWpByiGbhbHYTo9CMTUnx2G4p4ZQMrDPcRRy//1c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0 h1:UiSyK6ent6OKpkMJN3+k5HZ4sk4UfchEaaW5wv7SblQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.0/go.mod h1:l7kzl8n8DXoRyFz5cIMG70HnPauWa649TUhgw8Rq6lo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0 h1:SHN/umDLTmFTmYfI+gkanz6da3vK8Kvj/5wkqnTHbuA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.0/go.mod h1:l8gPU5RYGOFHJqWEpPMoRTP0VoaWQSkJdKo+hwWnnDA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0 h1:l5puwOHr7IxECuPMIuZG7UKOzAnF24v6t4l+Z5Moay4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.0/go.mod h1:Oov79flWa/n7Ni+lQC3z+VM7PoRM47omRqbJU9B5Y7E=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1 h1:eHNChn4Sp+g1hdz4rkx96n1l/LpJEQLDuFB0V+fA/yg=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.1/go.mod h1:9ev55pJx9xNX3UAOKzZmbmaTbwwuLTCemOJPsd7rUz8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1 h1:bjpWJEXch7moIt3PX2r5XpGROsletl7enqG1Q3Te1Dc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.1/go.mod h1:1o/W6JFUuREj2ExoQ21vHJgO7wakvjhol91M9eknFgs=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1 h1:ss/HbHbONu0uscM549++4YanT6MnjNN0BGhE5pZRfG4=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.1/go.mod h1:JsJDZFHwLGZu6dxhV9EV1gJrMnCeE4GEXubSZA59xdA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.0 h1:u6OkVDxtBPnxPkZ9/63ynEe+8kHbtS5IfaC4PzVxzWM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.0/go.mod h1:YqbU3RS/pkDVu+v+Nwxvn0i1WB0HkNWEePWbmODEbbs=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0 h1:6DL0qu5+315wbsAEEmzK+P9leRwNbkp+lGjPC+CEvb8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.0/go.mod h1:olUAyg+FaoFaL/zFaeQQONjOZ9HXoxgvI/c7mQTYz7M=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.0 h1:cjTRjh700H36MQ8M0LnDn33W3JmwC77mdxIIyPWCdpM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.0/go.mod h1:nXfOBMWPokIbOY+Gi7a1psWMSvskUCemZzI+SMB7Akc=
|
||||
github.com/aws/smithy-go v1.20.0 h1:6+kZsCXZwKxZS9RfISnPc4EXlHoyAkm2hPuM8X2BrrQ=
|
||||
github.com/aws/smithy-go v1.20.0/go.mod h1:uo5RKksAl4PzhqaAbjd4rLgFoq5koTsQKYuGe7dklGc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1 h1:rtYJd3w6IWCTVS8vmMaiXjW198noh2PBm5CiXyJea9o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.1/go.mod h1:zvXu+CTlib30LUy4LTNFc6HTZ/K6zCae5YIHTdX9wIo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.1 h1:5Wxh862HkXL9CbQ83BIkWKLIgQapGeuh5zG2G9OZtQk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.1/go.mod h1:V7GLA01pNUxMCYSQsibdVrqUrNIYIT/9lCOyR8ExNvQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.1 h1:cVP8mng1RjDyI3JN/AXFCn5FHNlsBaBH0/MBtG1bg0o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.1/go.mod h1:C8sQjoyAsdfjC7hpy4+S6B92hnFzx0d0UAyHicaOTIE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.1 h1:OYmmIcyw19f7x0qLBLQ3XsrCZSSyLhxd9GXng5evsN4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.1/go.mod h1:s5rqdn74Vdg10k61Pwf4ZHEApOSD6CKRe6qpeHDq32I=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.3 h1:uOHZ8HCjUHrRUi+sezA1yCeJVwa4Yy91tZDrWn1sT8w=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.3/go.mod h1:TzgisXFoXgCssgP11SzC6KrvcyCErz5c3w++m3xFOfo=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.3 h1:Cv/HH7sLzEdJMYQi4MCNHxZeyubQNOOIdVc0VU0lo3Q=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.3/go.mod h1:lTW7O4iMAnO2o7H3XJTvqaWFZCH6zIPs+eP7RdG/yp0=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.3 h1:LP38dk6XSNKyWAr3ZNEVECBPjEnoP+/SGvOfX0tRy+U=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.3/go.mod h1:RA3ERghFSivbTf0Sbsxv/grUuLMcyAjm0F/PylJMmEs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.2 h1:pnj8llQoBAHD4UmbM8UM5GdfycFJKMhgPSeaOyRaZ34=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.19.2/go.mod h1:x6/tCd1o/AOKQR+iYnjrzhJxD+w0xRN34asGPaSV7ew=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.2 h1:L4yhKxW6HbTSQ08OsvPJuaspaLE40qMgprgXUNFUiMg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.2/go.mod h1:lZB123q0SVQ3dfIbEOcGzhQHrwVBcHVReNS9tm20oU4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.2 h1:Dr+7r/p20XpN+1U5tVNZfA2bLq0kQ9IjVBM0iAyMMLg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.27.2/go.mod h1:ozhhG9/NB5c9jcmhGq6tX9dpp21LYdmRWRQVppASim4=
|
||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
|
@ -252,8 +252,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
@ -529,8 +529,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
|||
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/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.165.0 h1:zd5d4JIIIaYYsfVy1HzoXYZ9rWCSBxxAglbczzo7Bgc=
|
||||
google.golang.org/api v0.165.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o=
|
||||
google.golang.org/api v0.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24=
|
||||
google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA=
|
||||
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.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
|
@ -538,12 +538,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ
|
|||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9 h1:4++qSzdWBUy9/2x8L5KZgwZw+mjJZ2yDSCGMVM0YzRs=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:PVreiBMirk8ypES6aw9d4p6iiBNSIfZEBqr3UGoAi2E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
|
||||
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c h1:Zmyn5CV/jxzKnF+3d+xzbomACPwLQqVpLTpyXN5uTaQ=
|
||||
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
|
|
@ -199,6 +199,10 @@ type AdminFilters struct {
|
|||
AllowList []string `json:"allow_list,omitempty"`
|
||||
// API key auth allows to impersonate this administrator with an API key
|
||||
AllowAPIKeyAuth bool `json:"allow_api_key_auth,omitempty"`
|
||||
// A password change is required at the next login
|
||||
RequirePasswordChange bool `json:"require_password_change,omitempty"`
|
||||
// Require two factor authentication
|
||||
RequireTwoFactor bool `json:"require_two_factor"`
|
||||
// Time-based one time passwords configuration
|
||||
TOTPConfig AdminTOTPConfig `json:"totp_config,omitempty"`
|
||||
// Recovery codes to use if the user loses access to their second factor auth device.
|
||||
|
@ -615,6 +619,8 @@ func (a *Admin) getACopy() Admin {
|
|||
filters := AdminFilters{}
|
||||
filters.AllowList = make([]string, len(a.Filters.AllowList))
|
||||
filters.AllowAPIKeyAuth = a.Filters.AllowAPIKeyAuth
|
||||
filters.RequirePasswordChange = a.Filters.RequirePasswordChange
|
||||
filters.RequireTwoFactor = a.Filters.RequireTwoFactor
|
||||
filters.TOTPConfig.Enabled = a.Filters.TOTPConfig.Enabled
|
||||
filters.TOTPConfig.ConfigName = a.Filters.TOTPConfig.ConfigName
|
||||
filters.TOTPConfig.Secret = a.Filters.TOTPConfig.Secret.Clone()
|
||||
|
|
|
@ -150,6 +150,8 @@ func updateAdmin(w http.ResponseWriter, r *http.Request) {
|
|||
sendAPIResponse(w, r, errors.New("you cannot add/change your role"), "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
updatedAdmin.Filters.RequirePasswordChange = admin.Filters.RequirePasswordChange
|
||||
updatedAdmin.Filters.RequireTwoFactor = admin.Filters.RequireTwoFactor
|
||||
}
|
||||
updatedAdmin.ID = admin.ID
|
||||
updatedAdmin.Username = admin.Username
|
||||
|
@ -317,6 +319,7 @@ func doChangeAdminPassword(r *http.Request, currentPassword, newPassword, confir
|
|||
}
|
||||
|
||||
admin.Password = newPassword
|
||||
admin.Filters.RequirePasswordChange = false
|
||||
|
||||
return dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr), claims.Role)
|
||||
}
|
||||
|
|
|
@ -122,23 +122,25 @@ func saveTOTPConfig(w http.ResponseWriter, r *http.Request) {
|
|||
code := getNewRecoveryCode()
|
||||
recoveryCodes = append(recoveryCodes, dataprovider.RecoveryCode{Secret: kms.NewPlainSecret(code)})
|
||||
}
|
||||
baseURL := webBaseClientPath
|
||||
if claims.hasUserAudience() {
|
||||
if err := saveUserTOTPConfig(claims.Username, r, recoveryCodes); err != nil {
|
||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||
return
|
||||
}
|
||||
if claims.MustSetTwoFactorAuth {
|
||||
// force logout
|
||||
defer func() {
|
||||
c := jwtTokenClaims{}
|
||||
c.removeCookie(w, r, webBaseClientPath)
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
if err := saveAdminTOTPConfig(claims.Username, r, recoveryCodes); err != nil {
|
||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||
return
|
||||
}
|
||||
baseURL = webBasePath
|
||||
}
|
||||
if claims.MustSetTwoFactorAuth {
|
||||
// force logout
|
||||
defer func() {
|
||||
c := jwtTokenClaims{}
|
||||
c.removeCookie(w, r, baseURL)
|
||||
}()
|
||||
}
|
||||
|
||||
sendAPIResponse(w, r, nil, "TOTP configuration saved", http.StatusOK)
|
||||
|
@ -303,6 +305,9 @@ func saveAdminTOTPConfig(username string, r *http.Request, recoveryCodes []datap
|
|||
if err != nil {
|
||||
return util.NewValidationError(fmt.Sprintf("unable to decode JSON body: %v", err))
|
||||
}
|
||||
if !admin.Filters.TOTPConfig.Enabled && admin.Filters.RequireTwoFactor {
|
||||
return util.NewValidationError("two-factor authentication must be enabled")
|
||||
}
|
||||
if admin.Filters.TOTPConfig.Enabled {
|
||||
if admin.CountUnusedRecoveryCodes() < 5 && admin.Filters.TOTPConfig.Enabled {
|
||||
admin.Filters.RecoveryCodes = recoveryCodes
|
||||
|
|
|
@ -830,6 +830,7 @@ func handleResetPassword(r *http.Request, code, newPassword, confirmPassword str
|
|||
return &admin, &user, util.NewValidationError("unable to associate the confirmation code with an existing admin")
|
||||
}
|
||||
admin.Password = newPassword
|
||||
admin.Filters.RequirePasswordChange = false
|
||||
err = dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, ipAddr, admin.Role)
|
||||
if err != nil {
|
||||
return &admin, &user, util.NewGenericError(fmt.Sprintf("unable to set the new password: %v", err))
|
||||
|
|
|
@ -3501,6 +3501,194 @@ func TestTwoFactorRequirementsGroupLevel(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAdminMustChangePasswordRequirement(t *testing.T) {
|
||||
admin := getTestAdmin()
|
||||
admin.Username = altAdminUsername
|
||||
admin.Password = altAdminPassword
|
||||
admin.Filters.RequirePasswordChange = true
|
||||
admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
token, _, err := httpdtest.GetToken(altAdminUsername, altAdminPassword)
|
||||
assert.NoError(t, err)
|
||||
httpdtest.SetJWTToken(token)
|
||||
|
||||
_, _, err = httpdtest.GetUsers(0, 0, http.StatusForbidden)
|
||||
assert.NoError(t, err)
|
||||
_, _, err = httpdtest.GetStatus(http.StatusForbidden)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.ChangeAdminPassword(altAdminPassword, defaultTokenAuthPass, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
httpdtest.SetJWTToken("")
|
||||
|
||||
admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, admin.Filters.RequirePasswordChange)
|
||||
|
||||
// get a new token
|
||||
token, _, err = httpdtest.GetToken(altAdminUsername, defaultTokenAuthPass)
|
||||
assert.NoError(t, err)
|
||||
httpdtest.SetJWTToken(token)
|
||||
|
||||
_, _, err = httpdtest.GetUsers(0, 0, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
desc := xid.New().String()
|
||||
admin.Filters.RequirePasswordChange = true
|
||||
admin.Filters.RequireTwoFactor = true
|
||||
admin.Description = desc
|
||||
_, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
|
||||
if assert.Error(t, err) {
|
||||
assert.ErrorContains(t, err, "require password change mismatch")
|
||||
}
|
||||
admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, admin.Filters.RequirePasswordChange)
|
||||
assert.False(t, admin.Filters.RequireTwoFactor)
|
||||
assert.Equal(t, desc, admin.Description)
|
||||
|
||||
httpdtest.SetJWTToken("")
|
||||
|
||||
admin.Filters.RequirePasswordChange = true
|
||||
_, _, err = httpdtest.UpdateAdmin(admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
// test the same for the WebAdmin
|
||||
webToken, err := getJWTWebTokenFromTestServer(altAdminUsername, defaultTokenAuthPass)
|
||||
assert.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, webUsersPath, nil)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = webUsersPath
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr := executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
|
||||
csrfToken, err := getCSRFToken(httpBaseURL + webLoginPath)
|
||||
assert.NoError(t, err)
|
||||
form := make(url.Values)
|
||||
form.Set(csrfFormToken, csrfToken)
|
||||
form.Set("current_password", defaultTokenAuthPass)
|
||||
form.Set("new_password1", altAdminPassword)
|
||||
form.Set("new_password2", altAdminPassword)
|
||||
req, err = http.NewRequest(http.MethodPost, webChangeAdminPwdPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusFound, rr)
|
||||
assert.Equal(t, webLoginPath, rr.Header().Get("Location"))
|
||||
|
||||
admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, admin.Filters.RequirePasswordChange)
|
||||
|
||||
webToken, err = getJWTWebTokenFromTestServer(altAdminUsername, altAdminPassword)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodGet, webUsersPath, nil)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = webUsersPath
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
|
||||
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestAdminTwoFactorRequirements(t *testing.T) {
|
||||
admin := getTestAdmin()
|
||||
admin.Username = altAdminUsername
|
||||
admin.Password = altAdminPassword
|
||||
admin.Filters.RequireTwoFactor = true
|
||||
admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
token, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
|
||||
assert.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, serverStatusPath, nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr := executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
assert.Contains(t, rr.Body.String(), "Two-factor authentication requirements not met")
|
||||
|
||||
webToken, err := getJWTWebTokenFromTestServer(altAdminUsername, altAdminPassword)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodGet, webFoldersPath, nil)
|
||||
assert.NoError(t, err)
|
||||
req.RequestURI = webFoldersPath
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
assert.Contains(t, rr.Body.String(), util.I18nError2FARequiredGeneric)
|
||||
// add TOTP config
|
||||
configName, key, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], altAdminUsername)
|
||||
assert.NoError(t, err)
|
||||
adminTOTPConfig := dataprovider.AdminTOTPConfig{
|
||||
Enabled: true,
|
||||
ConfigName: configName,
|
||||
Secret: kms.NewPlainSecret(key.Secret()),
|
||||
}
|
||||
asJSON, err := json.Marshal(adminTOTPConfig)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodPost, adminTOTPSavePath, bytes.NewBuffer(asJSON))
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, admin.Filters.TOTPConfig.Enabled)
|
||||
|
||||
passcode, err := generateTOTPPasscode(key.Secret())
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, tokenPath), nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("X-SFTPGO-OTP", passcode)
|
||||
req.SetBasicAuth(altAdminUsername, altAdminPassword)
|
||||
resp, err := httpclient.GetHTTPClient().Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
responseHolder := make(map[string]any)
|
||||
err = render.DecodeJSON(resp.Body, &responseHolder)
|
||||
assert.NoError(t, err)
|
||||
token = responseHolder["access_token"].(string)
|
||||
assert.NotEmpty(t, token)
|
||||
err = resp.Body.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, serverStatusPath), nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
resp, err = httpclient.GetHTTPClient().Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
err = resp.Body.Close()
|
||||
assert.NoError(t, err)
|
||||
// try to disable 2FA
|
||||
disableReq := map[string]any{
|
||||
"enabled": false,
|
||||
}
|
||||
asJSON, err = json.Marshal(disableReq)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("%v%v", httpBaseURL, adminTOTPSavePath), bytes.NewBuffer(asJSON))
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
resp, err = httpclient.GetHTTPClient().Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
bodyResp, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, string(bodyResp), "two-factor authentication must be enabled")
|
||||
err = resp.Body.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestLoginUserAPITOTP(t *testing.T) {
|
||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
@ -19835,6 +20023,20 @@ func TestAdminUpdateSelfMock(t *testing.T) {
|
|||
assert.Contains(t, rr.Body.String(), util.I18nErrorAdminSelfDisable)
|
||||
|
||||
form.Set("status", "1")
|
||||
form.Set("require_two_factor", "1")
|
||||
form.Set("require_password_change", "1")
|
||||
req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, defaultTokenAuthUser), bytes.NewBuffer([]byte(form.Encode())))
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
setJWTCookieForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusSeeOther, rr)
|
||||
|
||||
admin, _, err = httpdtest.GetAdminByUsername(defaultTokenAuthUser, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, admin.Filters.RequirePasswordChange)
|
||||
assert.False(t, admin.Filters.RequireTwoFactor)
|
||||
|
||||
form.Set("role", "my role")
|
||||
req, _ = http.NewRequest(http.MethodPost, path.Join(webAdminPath, defaultTokenAuthUser), bytes.NewBuffer([]byte(form.Encode())))
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
|
@ -24375,6 +24577,7 @@ func TestAdminForgotPassword(t *testing.T) {
|
|||
a := getTestAdmin()
|
||||
a.Username = altAdminUsername
|
||||
a.Password = altAdminPassword
|
||||
a.Filters.RequirePasswordChange = true
|
||||
admin, _, err := httpdtest.AddAdmin(a, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
@ -24514,6 +24717,10 @@ func TestAdminForgotPassword(t *testing.T) {
|
|||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
|
||||
admin, _, err = httpdtest.GetAdminByUsername(admin.Username, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, admin.Filters.RequirePasswordChange)
|
||||
|
||||
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -1554,6 +1554,14 @@ func TestJWTTokenValidation(t *testing.T) {
|
|||
fn.ServeHTTP(rr, req.WithContext(ctx))
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
|
||||
fn = server.checkAuthRequirements(r)
|
||||
rr = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest(http.MethodPost, webGroupsPath, nil)
|
||||
req.RequestURI = webGroupsPath
|
||||
ctx = jwtauth.NewContext(req.Context(), token, errTest)
|
||||
fn.ServeHTTP(rr, req.WithContext(ctx))
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest(http.MethodPost, userSharesPath, nil)
|
||||
req.RequestURI = userSharesPath
|
||||
|
|
|
@ -223,7 +223,11 @@ func (s *httpdServer) checkAuthRequirements(next http.Handler) http.Handler {
|
|||
_, claims, err := jwtauth.FromContext(r.Context())
|
||||
if err != nil {
|
||||
if isWebRequest(r) {
|
||||
s.renderClientBadRequestPage(w, r, err)
|
||||
if isWebClientRequest(r) {
|
||||
s.renderClientBadRequestPage(w, r, err)
|
||||
} else {
|
||||
s.renderBadRequestPage(w, r, err)
|
||||
}
|
||||
} else {
|
||||
sendAPIResponse(w, r, err, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
}
|
||||
|
@ -234,16 +238,23 @@ func (s *httpdServer) checkAuthRequirements(next http.Handler) http.Handler {
|
|||
if tokenClaims.MustSetTwoFactorAuth || tokenClaims.MustChangePassword {
|
||||
var err error
|
||||
if tokenClaims.MustSetTwoFactorAuth {
|
||||
protocols := strings.Join(tokenClaims.RequiredTwoFactorProtocols, ", ")
|
||||
err = util.NewI18nError(
|
||||
util.NewGenericError(
|
||||
fmt.Sprintf("Two-factor authentication requirements not met, please configure two-factor authentication for the following protocols: %v",
|
||||
protocols)),
|
||||
util.I18nError2FARequired,
|
||||
util.I18nErrorArgs(map[string]any{
|
||||
"val": protocols,
|
||||
}),
|
||||
)
|
||||
if len(tokenClaims.RequiredTwoFactorProtocols) > 0 {
|
||||
protocols := strings.Join(tokenClaims.RequiredTwoFactorProtocols, ", ")
|
||||
err = util.NewI18nError(
|
||||
util.NewGenericError(
|
||||
fmt.Sprintf("Two-factor authentication requirements not met, please configure two-factor authentication for the following protocols: %v",
|
||||
protocols)),
|
||||
util.I18nError2FARequired,
|
||||
util.I18nErrorArgs(map[string]any{
|
||||
"val": protocols,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
err = util.NewI18nError(
|
||||
util.NewGenericError("Two-factor authentication requirements not met, please configure two-factor authentication"),
|
||||
util.I18nError2FARequiredGeneric,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
err = util.NewI18nError(
|
||||
util.NewGenericError("Password change required. Please set a new password to continue to use your account"),
|
||||
|
@ -251,7 +262,11 @@ func (s *httpdServer) checkAuthRequirements(next http.Handler) http.Handler {
|
|||
)
|
||||
}
|
||||
if isWebRequest(r) {
|
||||
s.renderClientForbiddenPage(w, r, err)
|
||||
if isWebClientRequest(r) {
|
||||
s.renderClientForbiddenPage(w, r, err)
|
||||
} else {
|
||||
s.renderForbiddenPage(w, r, err)
|
||||
}
|
||||
} else {
|
||||
sendAPIResponse(w, r, err, "", http.StatusForbidden)
|
||||
}
|
||||
|
|
|
@ -794,6 +794,8 @@ func (s *httpdServer) loginAdmin(
|
|||
Role: admin.Role,
|
||||
Signature: admin.GetSignature(),
|
||||
HideUserPageSections: admin.Filters.Preferences.HideUserPageSections,
|
||||
MustSetTwoFactorAuth: admin.Filters.RequireTwoFactor && !admin.Filters.TOTPConfig.Enabled,
|
||||
MustChangePassword: admin.Filters.RequirePasswordChange,
|
||||
}
|
||||
|
||||
audience := tokenAudienceWebAdmin
|
||||
|
@ -982,10 +984,12 @@ func (s *httpdServer) getToken(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func (s *httpdServer) generateAndSendToken(w http.ResponseWriter, r *http.Request, admin dataprovider.Admin, ip string) {
|
||||
c := jwtTokenClaims{
|
||||
Username: admin.Username,
|
||||
Permissions: admin.Permissions,
|
||||
Role: admin.Role,
|
||||
Signature: admin.GetSignature(),
|
||||
Username: admin.Username,
|
||||
Permissions: admin.Permissions,
|
||||
Role: admin.Role,
|
||||
Signature: admin.GetSignature(),
|
||||
MustSetTwoFactorAuth: admin.Filters.RequireTwoFactor && !admin.Filters.TOTPConfig.Enabled,
|
||||
MustChangePassword: admin.Filters.RequirePasswordChange,
|
||||
}
|
||||
|
||||
resp, err := c.createTokenResponse(s.tokenAuth, tokenAudienceAPI, ip)
|
||||
|
@ -1318,7 +1322,7 @@ func (s *httpdServer) initializeRouter() {
|
|||
|
||||
router.With(forbidAPIKeyAuthentication).Get(logoutPath, s.logout)
|
||||
router.With(forbidAPIKeyAuthentication).Get(adminProfilePath, getAdminProfile)
|
||||
router.With(forbidAPIKeyAuthentication).Put(adminProfilePath, updateAdminProfile)
|
||||
router.With(forbidAPIKeyAuthentication, s.checkAuthRequirements).Put(adminProfilePath, updateAdminProfile)
|
||||
router.With(forbidAPIKeyAuthentication).Put(adminPwdPath, changeAdminPassword)
|
||||
// admin TOTP APIs
|
||||
router.With(forbidAPIKeyAuthentication).Get(adminTOTPConfigsPath, getTOTPConfigs)
|
||||
|
@ -1328,62 +1332,6 @@ func (s *httpdServer) initializeRouter() {
|
|||
router.With(forbidAPIKeyAuthentication).Get(admin2FARecoveryCodesPath, getRecoveryCodes)
|
||||
router.With(forbidAPIKeyAuthentication).Post(admin2FARecoveryCodesPath, generateRecoveryCodes)
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewServerStatus)).
|
||||
Get(serverStatusPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
render.JSON(w, r, getServicesStatus())
|
||||
})
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections)).Get(activeConnectionsPath, getActiveConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminCloseConnections)).
|
||||
Delete(activeConnectionsPath+"/{connectionID}", handleCloseConnection)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/users/scans", getUsersQuotaScans)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/users/{username}/scan", startUserQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/folders/scans", getFoldersQuotaScans)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/folders/{name}/scan", startFolderQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath, getUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers)).Post(userPath, addUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath+"/{username}", getUserByUsername) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}", updateUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers)).Delete(userPath+"/{username}", deleteUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}/2fa/disable", disableUser2FA)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Get(folderPath, getFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Get(folderPath+"/{name}", getFolderByName) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(folderPath, addFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Put(folderPath+"/{name}", updateFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Delete(folderPath+"/{name}", deleteFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Get(groupPath, getGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Get(groupPath+"/{name}", getGroupByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(groupPath, addGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Put(groupPath+"/{name}", updateGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Delete(groupPath+"/{name}", deleteGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(dumpDataPath, dumpData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(loadDataPath, loadData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(loadDataPath, loadDataFromRequest)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/usage",
|
||||
updateUserQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/transfer-usage",
|
||||
updateUserTransferQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/folders/{name}/usage",
|
||||
updateFolderQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts, getDefenderHosts)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts+"/{id}", getDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Delete(defenderHosts+"/{id}", deleteDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath, getAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(adminPath, addAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath+"/{username}", getAdminByUsername)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}", updateAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Delete(adminPath+"/{username}", deleteAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}/2fa/disable", disableAdmin2FA)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
|
||||
startRetentionCheck)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(fsEventsPath, searchFsEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(providerEventsPath, searchProviderEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(logEventsPath, searchLogEvents)
|
||||
router.With(forbidAPIKeyAuthentication, s.checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
||||
Get(apiKeysPath, getAPIKeys)
|
||||
router.With(forbidAPIKeyAuthentication, s.checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
||||
|
@ -1394,27 +1342,88 @@ func (s *httpdServer) initializeRouter() {
|
|||
Put(apiKeysPath+"/{id}", updateAPIKey)
|
||||
router.With(forbidAPIKeyAuthentication, s.checkPerm(dataprovider.PermAdminManageAPIKeys)).
|
||||
Delete(apiKeysPath+"/{id}", deleteAPIKey)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventActionsPath, getEventActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventActionsPath+"/{name}", getEventActionByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventActionsPath, addEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Put(eventActionsPath+"/{name}", updateEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Delete(eventActionsPath+"/{name}", deleteEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventRulesPath, getEventRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventRulesPath+"/{name}", getEventRuleByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventRulesPath, addEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Put(eventRulesPath+"/{name}", updateEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Delete(eventRulesPath+"/{name}", deleteEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventRulesPath+"/run/{name}", runOnDemandRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Get(rolesPath, getRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(rolesPath, addRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Get(rolesPath+"/{name}", getRoleByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Put(rolesPath+"/{name}", updateRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Delete(rolesPath+"/{name}", deleteRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), compressor.Handler).Get(ipListsPath+"/{type}", getIPListEntries) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(ipListsPath+"/{type}", addIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Get(ipListsPath+"/{type}/{ipornet}", getIPListEntry) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Put(ipListsPath+"/{type}/{ipornet}", updateIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Delete(ipListsPath+"/{type}/{ipornet}", deleteIPListEntry)
|
||||
|
||||
router.Group(func(router chi.Router) {
|
||||
router.Use(s.checkAuthRequirements)
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewServerStatus)).
|
||||
Get(serverStatusPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
render.JSON(w, r, getServicesStatus())
|
||||
})
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections)).Get(activeConnectionsPath, getActiveConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminCloseConnections)).
|
||||
Delete(activeConnectionsPath+"/{connectionID}", handleCloseConnection)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/users/scans", getUsersQuotaScans)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/users/{username}/scan", startUserQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Get(quotasBasePath+"/folders/scans", getFoldersQuotaScans)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans)).Post(quotasBasePath+"/folders/{name}/scan", startFolderQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath, getUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers)).Post(userPath, addUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath+"/{username}", getUserByUsername) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}", updateUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers)).Delete(userPath+"/{username}", deleteUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(userPath+"/{username}/2fa/disable", disableUser2FA)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Get(folderPath, getFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Get(folderPath+"/{name}", getFolderByName) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(folderPath, addFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Put(folderPath+"/{name}", updateFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Delete(folderPath+"/{name}", deleteFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Get(groupPath, getGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Get(groupPath+"/{name}", getGroupByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(groupPath, addGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Put(groupPath+"/{name}", updateGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Delete(groupPath+"/{name}", deleteGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(dumpDataPath, dumpData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(loadDataPath, loadData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(loadDataPath, loadDataFromRequest)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/usage",
|
||||
updateUserQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/users/{username}/transfer-usage",
|
||||
updateUserTransferQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Put(quotasBasePath+"/folders/{name}/usage",
|
||||
updateFolderQuotaUsage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts, getDefenderHosts)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(defenderHosts+"/{id}", getDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Delete(defenderHosts+"/{id}", deleteDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath, getAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(adminPath, addAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath+"/{username}", getAdminByUsername)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}", updateAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Delete(adminPath+"/{username}", deleteAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Put(adminPath+"/{username}/2fa/disable", disableAdmin2FA)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
|
||||
startRetentionCheck)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(fsEventsPath, searchFsEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(providerEventsPath, searchProviderEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler).
|
||||
Get(logEventsPath, searchLogEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventActionsPath, getEventActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventActionsPath+"/{name}", getEventActionByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventActionsPath, addEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Put(eventActionsPath+"/{name}", updateEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Delete(eventActionsPath+"/{name}", deleteEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventRulesPath, getEventRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Get(eventRulesPath+"/{name}", getEventRuleByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventRulesPath, addEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Put(eventRulesPath+"/{name}", updateEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Delete(eventRulesPath+"/{name}", deleteEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(eventRulesPath+"/run/{name}", runOnDemandRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Get(rolesPath, getRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(rolesPath, addRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Get(rolesPath+"/{name}", getRoleByName)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Put(rolesPath+"/{name}", updateRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Delete(rolesPath+"/{name}", deleteRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), compressor.Handler).Get(ipListsPath+"/{type}", getIPListEntries) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(ipListsPath+"/{type}", addIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Get(ipListsPath+"/{type}/{ipornet}", getIPListEntry) //nolint:goconst
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Put(ipListsPath+"/{type}/{ipornet}", updateIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Delete(ipListsPath+"/{type}/{ipornet}", deleteIPListEntry)
|
||||
})
|
||||
})
|
||||
|
||||
s.router.Get(userTokenPath, s.getUserToken)
|
||||
|
@ -1675,8 +1684,9 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
router.Use(jwtAuthenticatorWebAdmin)
|
||||
|
||||
router.Get(webLogoutPath, s.handleWebAdminLogout)
|
||||
router.With(s.refreshCookie, s.requireBuiltinLogin).Get(webAdminProfilePath, s.handleWebAdminProfile)
|
||||
router.With(s.requireBuiltinLogin).Post(webAdminProfilePath, s.handleWebAdminProfilePost)
|
||||
router.With(s.refreshCookie, s.checkAuthRequirements, s.requireBuiltinLogin).Get(
|
||||
webAdminProfilePath, s.handleWebAdminProfile)
|
||||
router.With(s.checkAuthRequirements, s.requireBuiltinLogin).Post(webAdminProfilePath, s.handleWebAdminProfilePost)
|
||||
router.With(s.refreshCookie, s.requireBuiltinLogin).Get(webChangeAdminPwdPath, s.handleWebAdminChangePwd)
|
||||
router.With(s.requireBuiltinLogin).Post(webChangeAdminPwdPath, s.handleWebAdminChangePwdPost)
|
||||
|
||||
|
@ -1689,153 +1699,157 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
getRecoveryCodes)
|
||||
router.With(verifyCSRFHeader, s.requireBuiltinLogin).Post(webAdminRecoveryCodesPath, generateRecoveryCodes)
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
|
||||
Get(webUsersPath, s.handleGetWebUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), compressor.Handler, s.refreshCookie).
|
||||
Get(webUsersPath+jsonAPISuffix, getAllUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers), s.refreshCookie).
|
||||
Get(webUserPath, s.handleWebAddUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers), s.refreshCookie).
|
||||
Get(webUserPath+"/{username}", s.handleWebUpdateUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers)).Post(webUserPath, s.handleWebAddUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Post(webUserPath+"/{username}",
|
||||
s.handleWebUpdateUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupsPath, s.handleWebGetGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), compressor.Handler, s.refreshCookie).
|
||||
Get(webGroupsPath+jsonAPISuffix, getAllGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupPath, s.handleWebAddGroupGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(webGroupPath, s.handleWebAddGroupPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupPath+"/{name}", s.handleWebUpdateGroupGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(webGroupPath+"/{name}",
|
||||
s.handleWebUpdateGroupPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), verifyCSRFHeader).
|
||||
Delete(webGroupPath+"/{name}", deleteGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie).
|
||||
Get(webConnectionsPath, s.handleWebGetConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie).
|
||||
Get(webConnectionsPath+jsonAPISuffix, getActiveConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFoldersPath, s.handleWebGetFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), compressor.Handler, s.refreshCookie).
|
||||
Get(webFoldersPath+jsonAPISuffix, getAllFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFolderPath, s.handleWebAddFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(webFolderPath, s.handleWebAddFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewServerStatus), s.refreshCookie).
|
||||
Get(webStatusPath, s.handleWebGetStatus)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminsPath, s.handleGetWebAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminsPath+jsonAPISuffix, getAllAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminPath, s.handleWebAddAdminGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminPath+"/{username}", s.handleWebUpdateAdminGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath, s.handleWebAddAdminPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath+"/{username}",
|
||||
s.handleWebUpdateAdminPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader).
|
||||
Delete(webAdminPath+"/{username}", deleteAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader).
|
||||
Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFolderPath+"/{name}", s.handleWebUpdateFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(webFolderPath+"/{name}",
|
||||
s.handleWebUpdateFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), verifyCSRFHeader).
|
||||
Delete(webFolderPath+"/{name}", deleteFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
|
||||
Post(webScanVFolderPath+"/{name}", startFolderQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader).
|
||||
Delete(webUserPath+"/{username}", deleteUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
|
||||
Post(webQuotaScanPath+"/{username}", startUserQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, s.handleWebMaintenance)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webBackupPath, dumpData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webRestorePath, s.handleWebRestore)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
|
||||
Get(webTemplateUser, s.handleWebTemplateUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateUser, s.handleWebTemplateUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
|
||||
Get(webTemplateFolder, s.handleWebTemplateFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateFolder, s.handleWebTemplateFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderPath, s.handleWebDefenderPage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderHostsPath, getDefenderHosts)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Delete(webDefenderHostsPath+"/{id}",
|
||||
deleteDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminEventActionsPath+jsonAPISuffix, getAllActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionsPath, s.handleWebGetEventActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionPath, s.handleWebAddEventActionGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventActionPath,
|
||||
s.handleWebAddEventActionPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionPath+"/{name}", s.handleWebUpdateEventActionGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventActionPath+"/{name}",
|
||||
s.handleWebUpdateEventActionPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Delete(webAdminEventActionPath+"/{name}", deleteEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminEventRulesPath+jsonAPISuffix, getAllRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulesPath, s.handleWebGetEventRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulePath, s.handleWebAddEventRuleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventRulePath,
|
||||
s.handleWebAddEventRulePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulePath+"/{name}", s.handleWebUpdateEventRuleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventRulePath+"/{name}",
|
||||
s.handleWebUpdateEventRulePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Delete(webAdminEventRulePath+"/{name}", deleteEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Post(webAdminEventRulePath+"/run/{name}", runOnDemandRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolesPath, s.handleWebGetRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminRolesPath+jsonAPISuffix, getAllRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolePath, s.handleWebAddRoleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath, s.handleWebAddRolePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolePath+"/{name}", s.handleWebUpdateRoleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath+"/{name}",
|
||||
s.handleWebUpdateRolePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), verifyCSRFHeader).
|
||||
Delete(webAdminRolePath+"/{name}", deleteRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), s.refreshCookie).Get(webEventsPath,
|
||||
s.handleWebGetEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsFsSearchPath, searchFsEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsProviderSearchPath, searchProviderEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsLogSearchPath, searchLogEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Get(webIPListsPath, s.handleWebIPListsPage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), compressor.Handler, s.refreshCookie).
|
||||
Get(webIPListsPath+"/{type}", getIPListEntries)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), s.refreshCookie).Get(webIPListPath+"/{type}",
|
||||
s.handleWebAddIPListEntryGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(webIPListPath+"/{type}",
|
||||
s.handleWebAddIPListEntryPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), s.refreshCookie).Get(webIPListPath+"/{type}/{ipornet}",
|
||||
s.handleWebUpdateIPListEntryGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(webIPListPath+"/{type}/{ipornet}",
|
||||
s.handleWebUpdateIPListEntryPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), verifyCSRFHeader).
|
||||
Delete(webIPListPath+"/{type}/{ipornet}", deleteIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).Get(webConfigsPath, s.handleWebConfigs)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webConfigsPath, s.handleWebConfigsPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), verifyCSRFHeader, s.refreshCookie).
|
||||
Post(webConfigsPath+"/smtp/test", testSMTPConfig)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), verifyCSRFHeader, s.refreshCookie).
|
||||
Post(webOAuth2TokenPath, handleSMTPOAuth2TokenRequestPost)
|
||||
router.Group(func(router chi.Router) {
|
||||
router.Use(s.checkAuthRequirements)
|
||||
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), s.refreshCookie).
|
||||
Get(webUsersPath, s.handleGetWebUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewUsers), compressor.Handler, s.refreshCookie).
|
||||
Get(webUsersPath+jsonAPISuffix, getAllUsers)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers), s.refreshCookie).
|
||||
Get(webUserPath, s.handleWebAddUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers), s.refreshCookie).
|
||||
Get(webUserPath+"/{username}", s.handleWebUpdateUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminAddUsers)).Post(webUserPath, s.handleWebAddUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminChangeUsers)).Post(webUserPath+"/{username}",
|
||||
s.handleWebUpdateUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupsPath, s.handleWebGetGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), compressor.Handler, s.refreshCookie).
|
||||
Get(webGroupsPath+jsonAPISuffix, getAllGroups)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupPath, s.handleWebAddGroupGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(webGroupPath, s.handleWebAddGroupPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), s.refreshCookie).
|
||||
Get(webGroupPath+"/{name}", s.handleWebUpdateGroupGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups)).Post(webGroupPath+"/{name}",
|
||||
s.handleWebUpdateGroupPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageGroups), verifyCSRFHeader).
|
||||
Delete(webGroupPath+"/{name}", deleteGroup)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie).
|
||||
Get(webConnectionsPath, s.handleWebGetConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie).
|
||||
Get(webConnectionsPath+jsonAPISuffix, getActiveConnections)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFoldersPath, s.handleWebGetFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), compressor.Handler, s.refreshCookie).
|
||||
Get(webFoldersPath+jsonAPISuffix, getAllFolders)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFolderPath, s.handleWebAddFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(webFolderPath, s.handleWebAddFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewServerStatus), s.refreshCookie).
|
||||
Get(webStatusPath, s.handleWebGetStatus)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminsPath, s.handleGetWebAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminsPath+jsonAPISuffix, getAllAdmins)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminPath, s.handleWebAddAdminGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), s.refreshCookie).
|
||||
Get(webAdminPath+"/{username}", s.handleWebUpdateAdminGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath, s.handleWebAddAdminPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Post(webAdminPath+"/{username}",
|
||||
s.handleWebUpdateAdminPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader).
|
||||
Delete(webAdminPath+"/{username}", deleteAdmin)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader).
|
||||
Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
|
||||
Get(webFolderPath+"/{name}", s.handleWebUpdateFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(webFolderPath+"/{name}",
|
||||
s.handleWebUpdateFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), verifyCSRFHeader).
|
||||
Delete(webFolderPath+"/{name}", deleteFolder)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
|
||||
Post(webScanVFolderPath+"/{name}", startFolderQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader).
|
||||
Delete(webUserPath+"/{username}", deleteUser)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
|
||||
Post(webQuotaScanPath+"/{username}", startUserQuotaScan)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, s.handleWebMaintenance)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webBackupPath, dumpData)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webRestorePath, s.handleWebRestore)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
|
||||
Get(webTemplateUser, s.handleWebTemplateUserGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateUser, s.handleWebTemplateUserPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).
|
||||
Get(webTemplateFolder, s.handleWebTemplateFolderGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webTemplateFolder, s.handleWebTemplateFolderPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderPath, s.handleWebDefenderPage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewDefender)).Get(webDefenderHostsPath, getDefenderHosts)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageDefender)).Delete(webDefenderHostsPath+"/{id}",
|
||||
deleteDefenderHostByID)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminEventActionsPath+jsonAPISuffix, getAllActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionsPath, s.handleWebGetEventActions)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionPath, s.handleWebAddEventActionGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventActionPath,
|
||||
s.handleWebAddEventActionPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventActionPath+"/{name}", s.handleWebUpdateEventActionGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventActionPath+"/{name}",
|
||||
s.handleWebUpdateEventActionPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Delete(webAdminEventActionPath+"/{name}", deleteEventAction)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminEventRulesPath+jsonAPISuffix, getAllRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulesPath, s.handleWebGetEventRules)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulePath, s.handleWebAddEventRuleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventRulePath,
|
||||
s.handleWebAddEventRulePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), s.refreshCookie).
|
||||
Get(webAdminEventRulePath+"/{name}", s.handleWebUpdateEventRuleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules)).Post(webAdminEventRulePath+"/{name}",
|
||||
s.handleWebUpdateEventRulePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Delete(webAdminEventRulePath+"/{name}", deleteEventRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageEventRules), verifyCSRFHeader).
|
||||
Post(webAdminEventRulePath+"/run/{name}", runOnDemandRule)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolesPath, s.handleWebGetRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), compressor.Handler, s.refreshCookie).
|
||||
Get(webAdminRolesPath+jsonAPISuffix, getAllRoles)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolePath, s.handleWebAddRoleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath, s.handleWebAddRolePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), s.refreshCookie).
|
||||
Get(webAdminRolePath+"/{name}", s.handleWebUpdateRoleGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles)).Post(webAdminRolePath+"/{name}",
|
||||
s.handleWebUpdateRolePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), verifyCSRFHeader).
|
||||
Delete(webAdminRolePath+"/{name}", deleteRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), s.refreshCookie).Get(webEventsPath,
|
||||
s.handleWebGetEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsFsSearchPath, searchFsEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsProviderSearchPath, searchProviderEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsLogSearchPath, searchLogEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Get(webIPListsPath, s.handleWebIPListsPage)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), compressor.Handler, s.refreshCookie).
|
||||
Get(webIPListsPath+"/{type}", getIPListEntries)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), s.refreshCookie).Get(webIPListPath+"/{type}",
|
||||
s.handleWebAddIPListEntryGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(webIPListPath+"/{type}",
|
||||
s.handleWebAddIPListEntryPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), s.refreshCookie).Get(webIPListPath+"/{type}/{ipornet}",
|
||||
s.handleWebUpdateIPListEntryGet)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists)).Post(webIPListPath+"/{type}/{ipornet}",
|
||||
s.handleWebUpdateIPListEntryPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageIPLists), verifyCSRFHeader).
|
||||
Delete(webIPListPath+"/{type}/{ipornet}", deleteIPListEntry)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), s.refreshCookie).Get(webConfigsPath, s.handleWebConfigs)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Post(webConfigsPath, s.handleWebConfigsPost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), verifyCSRFHeader, s.refreshCookie).
|
||||
Post(webConfigsPath+"/smtp/test", testSMTPConfig)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageSystem), verifyCSRFHeader, s.refreshCookie).
|
||||
Post(webOAuth2TokenPath, handleSMTPOAuth2TokenRequestPost)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,12 +210,13 @@ type changePasswordPage struct {
|
|||
|
||||
type mfaPage struct {
|
||||
basePage
|
||||
TOTPConfigs []string
|
||||
TOTPConfig dataprovider.AdminTOTPConfig
|
||||
GenerateTOTPURL string
|
||||
ValidateTOTPURL string
|
||||
SaveTOTPURL string
|
||||
RecCodesURL string
|
||||
TOTPConfigs []string
|
||||
TOTPConfig dataprovider.AdminTOTPConfig
|
||||
GenerateTOTPURL string
|
||||
ValidateTOTPURL string
|
||||
SaveTOTPURL string
|
||||
RecCodesURL string
|
||||
RequireTwoFactor bool
|
||||
}
|
||||
|
||||
type maintenancePage struct {
|
||||
|
@ -786,6 +787,7 @@ func (s *httpdServer) renderMFAPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
data.TOTPConfig = admin.Filters.TOTPConfig
|
||||
data.RequireTwoFactor = admin.Filters.RequireTwoFactor
|
||||
renderAdminTemplate(w, templateMFA, data)
|
||||
}
|
||||
|
||||
|
@ -1741,6 +1743,8 @@ func getAdminFromPostFields(r *http.Request) (dataprovider.Admin, error) {
|
|||
admin.Role = strings.TrimSpace(r.Form.Get("role"))
|
||||
admin.Filters.AllowList = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
|
||||
admin.Filters.AllowAPIKeyAuth = r.Form.Get("allow_api_key_auth") != ""
|
||||
admin.Filters.RequireTwoFactor = r.Form.Get("require_two_factor") != ""
|
||||
admin.Filters.RequirePasswordChange = r.Form.Get("require_password_change") != ""
|
||||
admin.AdditionalInfo = r.Form.Get("additional_info")
|
||||
admin.Description = r.Form.Get("description")
|
||||
admin.Filters.Preferences.HideUserPageSections = getAdminHiddenUserPageSections(r)
|
||||
|
@ -3016,6 +3020,8 @@ func (s *httpdServer) handleWebUpdateAdminPost(w http.ResponseWriter, r *http.Re
|
|||
), false)
|
||||
return
|
||||
}
|
||||
updatedAdmin.Filters.RequirePasswordChange = admin.Filters.RequirePasswordChange
|
||||
updatedAdmin.Filters.RequireTwoFactor = admin.Filters.RequireTwoFactor
|
||||
}
|
||||
err = dataprovider.UpdateAdmin(&updatedAdmin, claims.Username, ipAddr, claims.Role)
|
||||
if err != nil {
|
||||
|
|
|
@ -1970,6 +1970,12 @@ func compareAdminFilters(expected, actual dataprovider.AdminFilters) error {
|
|||
if expected.Preferences.DefaultUsersExpiration != actual.Preferences.DefaultUsersExpiration {
|
||||
return errors.New("default users expiration mismatch")
|
||||
}
|
||||
if expected.RequirePasswordChange != actual.RequirePasswordChange {
|
||||
return errors.New("require password change mismatch")
|
||||
}
|
||||
if expected.RequireTwoFactor != actual.RequireTwoFactor {
|
||||
return errors.New("require two factor mismatch")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ const (
|
|||
I18nErrorShareExpired = "share.expired"
|
||||
I18nErrorLoginFromIPDenied = "login.ip_not_allowed"
|
||||
I18nError2FARequired = "login.two_factor_required"
|
||||
I18nError2FARequiredGeneric = "login.two_factor_required_generic"
|
||||
I18nErrorNoOIDCFeature = "general.no_oidc_feature"
|
||||
I18nErrorNoPermissions = "general.no_permissions"
|
||||
I18nErrorShareBrowsePaths = "share.browsable_multiple_paths"
|
||||
|
|
|
@ -6003,6 +6003,10 @@ components:
|
|||
allow_api_key_auth:
|
||||
type: boolean
|
||||
description: 'API key auth allows to impersonate this administrator with an API key'
|
||||
require_two_factor:
|
||||
type: boolean
|
||||
require_password_change:
|
||||
type: boolean
|
||||
totp_config:
|
||||
$ref: '#/components/schemas/AdminTOTPConfig'
|
||||
recovery_codes:
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
"reset_ok_login_error": "The password reset completed successfully but an unexpected error occurred while signing in",
|
||||
"ip_not_allowed": "Login is not allowed from this IP address",
|
||||
"two_factor_required": "Set up two-factor authentication, it is required for the following protocols: {{val}}",
|
||||
"two_factor_required_generic": "Set up two-factor authentication, it is mandatory for your account",
|
||||
"link": "Go to {{link}}"
|
||||
},
|
||||
"theme": {
|
||||
|
@ -386,6 +387,7 @@
|
|||
"msg_enabled": "Two-factor authentication is enabled",
|
||||
"msg_disabled": "Secure Your Account",
|
||||
"msg_info": "Two-factor authentication adds an extra layer of security to your account. To log in you'll need to provide an additional authentication code.",
|
||||
"require": "Two-factor authentication is required for this account",
|
||||
"require_for": "Require 2FA for",
|
||||
"generate": "Generate new secret key",
|
||||
"recovery_codes": "Recovery codes",
|
||||
|
@ -727,7 +729,8 @@
|
|||
"user_page_pref_help": "You can hide some sections from the user page. These are not security settings and are not enforced server side in any way. They are only intended to simplify the add/update user page",
|
||||
"hide_sections": "Hide sections",
|
||||
"default_users_expiration": "Default users expiration",
|
||||
"default_users_expiration_help": "Default expiration for new users as number of days"
|
||||
"default_users_expiration_help": "Default expiration for new users as number of days",
|
||||
"require_pwd_change_help": "A password change is required at the next login"
|
||||
},
|
||||
"connections": {
|
||||
"view_manage": "View and manage connections",
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
"reset_ok_login_error": "La reimpostazione della password è stata completata correttamente ma si è verificato un errore imprevisto durante l'accesso",
|
||||
"ip_not_allowed": "L'accesso non è consentito da questo indirizzo IP",
|
||||
"two_factor_required": "Configura l'autenticazione a due fattori, è obbligatoria per i seguenti protocolli: {{val}}",
|
||||
"two_factor_required_generic": "Configura l'autenticazione a due fattori, è obbligatoria per il tuo account",
|
||||
"link": "Vai a {{link}}"
|
||||
},
|
||||
"theme": {
|
||||
|
@ -386,6 +387,7 @@
|
|||
"msg_enabled": "L'autenticazione a due fattori è abilitata",
|
||||
"msg_disabled": "Metti il tuo account in sicurezza",
|
||||
"msg_info": "L'autenticazione a due fattori aggiunge un ulteriore livello di sicurezza al tuo account. Per accedere dovrai fornire un ulteriore codice di autenticazione.",
|
||||
"require": "L'autenticazione a due fattori è obbligatoria per questo account",
|
||||
"require_for": "Richiedi 2FA per",
|
||||
"generate": "Genera una nuova chiave segreta",
|
||||
"recovery_codes": "Codici di ripristino",
|
||||
|
@ -727,7 +729,8 @@
|
|||
"user_page_pref_help": "Puoi nascondere alcune sezioni dalla pagina utente. Queste non sono impostazioni di sicurezza e non vengono verificate lato server. Hanno il solo scopo di semplificare la pagina di creazione/modifica utenti",
|
||||
"hide_sections": "Nascondi sezioni",
|
||||
"default_users_expiration": "Scadenza predefinita utenti",
|
||||
"default_users_expiration_help": "Scadenza predefinita per i nuovi utenti espressa in numero di giorni"
|
||||
"default_users_expiration_help": "Scadenza predefinita per i nuovi utenti espressa in numero di giorni",
|
||||
"require_pwd_change_help": "Il cambio password è obbligatorio al prossimo accesso"
|
||||
},
|
||||
"connections": {
|
||||
"view_manage": "Visualizza e gestisci connessioni attive",
|
||||
|
|
|
@ -42,6 +42,18 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row align-items-center mt-10 {{if eq .LoggedUser.Username .Admin.Username}}d-none{{end}}">
|
||||
<label data-i18n="user.require_pwd_change" class="col-md-3 col-form-label" for="idRequirePasswordChange">Require password change</label>
|
||||
<div class="col-md-9">
|
||||
<div class="form-check form-switch form-check-custom form-check-solid">
|
||||
<input class="form-check-input" type="checkbox" id="idRequirePasswordChange" name="require_password_change" {{if .Admin.Filters.RequirePasswordChange}}checked="checked"{{end}}/>
|
||||
<label data-i18n="admin.require_pwd_change_help" class="form-check-label fw-semibold text-gray-800" for="idRequirePasswordChange">
|
||||
A password change is required at the next login
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mt-10">
|
||||
<label for="idStatus" data-i18n="general.status" class="col-md-3 col-form-label">Status</label>
|
||||
<div class="col-md-9">
|
||||
|
@ -245,6 +257,18 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row align-items-center mt-10 {{if eq .LoggedUser.Username .Admin.Username}}d-none{{end}}">
|
||||
<label data-i18n="title.two_factor_auth_short" class="col-md-3 col-form-label" for="idRequire2FA">2FA</label>
|
||||
<div class="col-md-9">
|
||||
<div class="form-check form-switch form-check-custom form-check-solid">
|
||||
<input class="form-check-input" type="checkbox" id="idRequire2FA" name="require_two_factor" {{if .Admin.Filters.RequireTwoFactor}}checked{{end}}/>
|
||||
<label data-i18n="2fa.require" class="form-check-label fw-semibold text-gray-800" for="idRequire2FA">
|
||||
Two-factor authentication is required
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mt-10">
|
||||
<label for="idAdditionalInfo" data-i18n="general.additional_info" class="col-md-3 col-form-label">Additional info</label>
|
||||
<div class="col-md-9">
|
||||
|
|
|
@ -344,6 +344,16 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
}
|
||||
|
||||
function disableConfig() {
|
||||
//{{- if .RequireTwoFactor}}
|
||||
ModalAlert.fire({
|
||||
text: $.t('2fa.require'),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
//{{- else}}
|
||||
ModalAlert.fire({
|
||||
text: $.t('2fa.disable_question'),
|
||||
icon: "warning",
|
||||
|
@ -358,6 +368,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
doSaveConfig(document.querySelector('#disable_btn'), null, false, true);
|
||||
}
|
||||
});
|
||||
//{{- end}}
|
||||
}
|
||||
|
||||
function validatePasscode() {
|
||||
|
|
Loading…
Reference in a new issue