WebAdmin: allow to disable 2FA

Before it was only possible using REST API

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-02-23 18:24:07 +01:00
parent 76ffa107dd
commit a577d8b3cd
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
17 changed files with 312 additions and 129 deletions

30
go.mod
View file

@ -10,14 +10,14 @@ require (
github.com/alexedwards/argon2id v1.0.0 github.com/alexedwards/argon2id v1.0.0
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
github.com/aws/aws-sdk-go-v2 v1.25.1 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/config v1.27.3
github.com/aws/aws-sdk-go-v2/credentials v1.17.2 github.com/aws/aws-sdk-go-v2/credentials v1.17.3
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.1 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/feature/s3/manager v1.16.5
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.20.3 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.50.3 github.com/aws/aws-sdk-go-v2/service/s3 v1.51.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.27.3 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.0
github.com/aws/aws-sdk-go-v2/service/sts v1.27.2 github.com/aws/aws-sdk-go-v2/service/sts v1.28.0
github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/cockroachdb/cockroach-go/v2 v2.3.6 github.com/cockroachdb/cockroach-go/v2 v2.3.6
github.com/coreos/go-oidc/v3 v3.9.0 github.com/coreos/go-oidc/v3 v3.9.0
@ -74,7 +74,7 @@ require (
golang.org/x/sys v0.17.0 golang.org/x/sys v0.17.0
golang.org/x/term v0.17.0 golang.org/x/term v0.17.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
google.golang.org/api v0.166.0 google.golang.org/api v0.167.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )
@ -94,8 +94,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.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/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/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/sso v1.20.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.2 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.0 // indirect
github.com/aws/smithy-go v1.20.1 // indirect github.com/aws/smithy-go v1.20.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect
@ -146,7 +146,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.47.0 // indirect github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
@ -162,11 +162,11 @@ require (
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
go.opentelemetry.io/otel v1.23.1 // indirect go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.23.1 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.23.1 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.15.0 // indirect golang.org/x/mod v0.15.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/sync v0.6.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect

60
go.sum
View file

@ -37,14 +37,14 @@ github.com/aws/aws-sdk-go-v2 v1.25.1 h1:P7hU6A5qEdmajGwvae/zDkOq+ULLC9tQBTwqqiwF
github.com/aws/aws-sdk-go-v2 v1.25.1/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo= 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 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/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.3 h1:0PRdb/q5a77HVYj+2rvPiCObfMfl/pWhwa5cs3cnl3c=
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/config v1.27.3/go.mod h1:WeRAr9ENap9NAegbfNsLqGQd8ERz5ypdIUx4j0/ZgKI=
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.3 h1:dDM5wrgwOL5gTZ0Gv/bvewPldjBcJywoaO5ClERrOGE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.2/go.mod h1:7Zo+D6q4auSIo3p4EItuTKTk7J+RqjASISZqLvmUgpc= github.com/aws/aws-sdk-go-v2/credentials v1.17.3/go.mod h1:G96Nuaw9qJS+s3OnK8RW8VEKEOjXi8H5Jk4lC/ZyZbw=
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 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/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.5 h1:IEv6homMJMnedG/2VWfNuV34ouXUmK8E7y4rAl59Fhs=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.4/go.mod h1:q3SxgP2WD9YRLCybtyse8EgO3vKKWVmxlTmBNeRXPyk= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.5/go.mod h1:a+wq9mSuG13iSkVMR1O8VApmAISm1ca+E2RQpcB3flw=
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 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/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 h1:RAnaIrbxPtlXNVI/OIlh1sidTQ3e1qM6LRjs7N0bE0I=
@ -61,18 +61,18 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.1 h1:cVP8mng1R
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/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 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/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.21.0 h1:NEWfVJgrDwEXrKDNrrX+2LtWhKVLYpJ2d7/gr1N1B54=
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/marketplacemetering v1.21.0/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.51.0 h1:rNVsCe3bqTAhG+qjnHJKgYKdHEsqqo/GMK3gEYY8W6g=
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/s3 v1.51.0/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.28.0 h1:Xf3s55N9cqKvFK6D70zCXvXXN4ZovTCy7glL+gUhLEc=
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/secretsmanager v1.28.0/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.20.0 h1:6YL8G91QZ52KlPrLkEgEez5kejIVwChVCgND3qgY5j0=
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/sso v1.20.0/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.23.0 h1:+DqIa5Ll7W311QLUvGFDdVit9uC4G0VioDdw08cXcow=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.22.2/go.mod h1:lZB123q0SVQ3dfIbEOcGzhQHrwVBcHVReNS9tm20oU4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.0/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.28.0 h1:F7tQr61zYnTaeY50Rn4jwfVQbtcqJuBRwN/nGGNwzb0=
github.com/aws/aws-sdk-go-v2/service/sts v1.27.2/go.mod h1:ozhhG9/NB5c9jcmhGq6tX9dpp21LYdmRWRQVppASim4= github.com/aws/aws-sdk-go-v2/service/sts v1.28.0/go.mod h1:ozhhG9/NB5c9jcmhGq6tX9dpp21LYdmRWRQVppASim4=
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw= 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/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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -333,8 +333,8 @@ github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlk
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@ -419,14 +419,14 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA=
go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@ -434,8 +434,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus=
gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
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=
@ -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-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.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24= google.golang.org/api v0.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE=
google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= google.golang.org/api v0.167.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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=

View file

@ -58,6 +58,7 @@ const (
PermAdminManageEventRules = "manage_event_rules" PermAdminManageEventRules = "manage_event_rules"
PermAdminManageRoles = "manage_roles" PermAdminManageRoles = "manage_roles"
PermAdminManageIPLists = "manage_ip_lists" PermAdminManageIPLists = "manage_ip_lists"
PermAdminDisableMFA = "disable_mfa"
) )
const ( const (
@ -75,7 +76,7 @@ var (
PermAdminCloseConnections, PermAdminViewServerStatus, PermAdminManageAdmins, PermAdminManageRoles, PermAdminCloseConnections, PermAdminViewServerStatus, PermAdminManageAdmins, PermAdminManageRoles,
PermAdminManageEventRules, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem, PermAdminManageEventRules, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem,
PermAdminManageDefender, PermAdminViewDefender, PermAdminManageIPLists, PermAdminRetentionChecks, PermAdminManageDefender, PermAdminViewDefender, PermAdminManageIPLists, PermAdminRetentionChecks,
PermAdminViewEvents} PermAdminViewEvents, PermAdminDisableMFA}
forbiddenPermsForRoleAdmins = []string{PermAdminAny, PermAdminManageAdmins, PermAdminManageSystem, forbiddenPermsForRoleAdmins = []string{PermAdminAny, PermAdminManageAdmins, PermAdminManageSystem,
PermAdminManageEventRules, PermAdminManageIPLists, PermAdminManageRoles} PermAdminManageEventRules, PermAdminManageIPLists, PermAdminManageRoles}
) )

View file

@ -100,6 +100,10 @@ func disableAdmin2FA(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, err, "", getRespStatus(err)) sendAPIResponse(w, r, err, "", getRespStatus(err))
return return
} }
if !admin.Filters.TOTPConfig.Enabled {
sendAPIResponse(w, r, nil, "two-factor authentication is not enabled", http.StatusBadRequest)
return
}
if admin.Username == claims.Username { if admin.Username == claims.Username {
if admin.Filters.RequireTwoFactor { if admin.Filters.RequireTwoFactor {
err := util.NewValidationError("two-factor authentication must be enabled") err := util.NewValidationError("two-factor authentication must be enabled")

View file

@ -134,6 +134,10 @@ func disableUser2FA(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, err, "", getRespStatus(err)) sendAPIResponse(w, r, err, "", getRespStatus(err))
return return
} }
if !user.Filters.TOTPConfig.Enabled {
sendAPIResponse(w, r, nil, "two-factor authentication is not enabled", http.StatusBadRequest)
return
}
user.Filters.RecoveryCodes = nil user.Filters.RecoveryCodes = nil
user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{ user.Filters.TOTPConfig = dataprovider.UserTOTPConfig{
Enabled: false, Enabled: false,

View file

@ -6812,14 +6812,6 @@ func TestNamingRules(t *testing.T) {
rr = executeRequest(req) rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr) checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed") assert.Contains(t, rr.Body.String(), "the following characters are allowed")
req, err = http.NewRequest(http.MethodPut, adminPath+"/"+admin.Username+"/2fa/disable", nil)
assert.NoError(t, err)
setBearerForReq(req, adminAPIToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed")
// test admin reset password // test admin reset password
form = make(url.Values) form = make(url.Values)
form.Set("username", admin.Username) form.Set("username", admin.Username)
@ -9086,6 +9078,18 @@ func TestAdminTwoFactorLogin(t *testing.T) {
admin.Password = altAdminPassword admin.Password = altAdminPassword
admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated) admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
assert.NoError(t, err) assert.NoError(t, err)
admin1 := getTestAdmin()
admin1.Username = altAdminUsername + "1"
admin1.Password = altAdminPassword
var permissions []string
for _, p := range admin1.GetValidPerms() {
if p != dataprovider.PermAdminAny && p != dataprovider.PermAdminDisableMFA {
permissions = append(permissions, p)
}
}
admin1.Permissions = permissions
admin1, _, err = httpdtest.AddAdmin(admin1, http.StatusCreated)
assert.NoError(t, err)
// enable two factor authentication // enable two factor authentication
configName, key, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], admin.Username) configName, key, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], admin.Username)
assert.NoError(t, err) assert.NoError(t, err)
@ -9369,12 +9373,27 @@ func TestAdminTwoFactorLogin(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// disable TOTP // disable TOTP
altToken1, err := getJWTAPITokenFromTestServer(admin1.Username, altAdminPassword)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
assert.NoError(t, err)
setBearerForReq(req, altToken1)
rr = executeRequest(req)
checkResponseCode(t, http.StatusForbidden, rr)
req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil) req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
assert.NoError(t, err) assert.NoError(t, err)
setBearerForReq(req, altToken) setBearerForReq(req, altToken)
rr = executeRequest(req) rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr) checkResponseCode(t, http.StatusOK, rr)
req, err = http.NewRequest(http.MethodPut, adminPath+"/"+altAdminUsername+"/2fa/disable", nil)
assert.NoError(t, err)
setBearerForReq(req, altToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "two-factor authentication is not enabled")
form = make(url.Values) form = make(url.Values)
form.Set("recovery_code", recoveryCode) form.Set("recovery_code", recoveryCode)
form.Set(csrfFormToken, csrfToken) form.Set(csrfFormToken, csrfToken)
@ -9399,6 +9418,8 @@ func TestAdminTwoFactorLogin(t *testing.T) {
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK) _, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
assert.NoError(t, err) assert.NoError(t, err)
_, err = httpdtest.RemoveAdmin(admin1, http.StatusOK)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode()))) req, err = http.NewRequest(http.MethodPost, webAdminTwoFactorRecoveryPath, bytes.NewBuffer([]byte(form.Encode())))
assert.NoError(t, err) assert.NoError(t, err)
@ -10107,6 +10128,13 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
rr = executeRequest(req) rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr) checkResponseCode(t, http.StatusOK, rr)
req, err = http.NewRequest(http.MethodPut, userPath+"/"+user.Username+"/2fa/disable", nil)
assert.NoError(t, err)
setBearerForReq(req, adminToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "two-factor authentication is not enabled")
form = make(url.Values) form = make(url.Values)
form.Set("recovery_code", recoveryCode) form.Set("recovery_code", recoveryCode)
form.Set("passcode", passcode) form.Set("passcode", passcode)

View file

@ -1364,7 +1364,7 @@ func (s *httpdServer) initializeRouter() {
router.With(s.checkPerm(dataprovider.PermAdminViewUsers)).Get(userPath+"/{username}", getUserByUsername) //nolint:goconst 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.PermAdminChangeUsers)).Put(userPath+"/{username}", updateUser)
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers)).Delete(userPath+"/{username}", deleteUser) 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.PermAdminDisableMFA)).Put(userPath+"/{username}/2fa/disable", disableUser2FA) //nolint:goconst
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Get(folderPath, getFolders) 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)).Get(folderPath+"/{name}", getFolderByName) //nolint:goconst
router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(folderPath, addFolder) router.With(s.checkPerm(dataprovider.PermAdminManageFolders)).Post(folderPath, addFolder)
@ -1392,7 +1392,7 @@ func (s *httpdServer) initializeRouter() {
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Get(adminPath+"/{username}", getAdminByUsername) 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)).Put(adminPath+"/{username}", updateAdmin)
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins)).Delete(adminPath+"/{username}", deleteAdmin) 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.PermAdminDisableMFA)).Put(adminPath+"/{username}/2fa/disable", disableAdmin2FA)
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks) router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Get(retentionChecksPath, getRetentionChecks)
router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check", router.With(s.checkPerm(dataprovider.PermAdminRetentionChecks)).Post(retentionBasePath+"/{username}/check",
startRetentionCheck) startRetentionCheck)
@ -1752,6 +1752,8 @@ func (s *httpdServer) setupWebAdminRoutes() {
s.handleWebUpdateAdminPost) s.handleWebUpdateAdminPost)
router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader). router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader).
Delete(webAdminPath+"/{username}", deleteAdmin) Delete(webAdminPath+"/{username}", deleteAdmin)
router.With(s.checkPerm(dataprovider.PermAdminDisableMFA), verifyCSRFHeader).
Put(webAdminPath+"/{username}/2fa/disable", disableAdmin2FA)
router.With(s.checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader). router.With(s.checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader).
Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection) Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection)
router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie). router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie).
@ -1764,6 +1766,8 @@ func (s *httpdServer) setupWebAdminRoutes() {
Post(webScanVFolderPath+"/{name}", startFolderQuotaScan) Post(webScanVFolderPath+"/{name}", startFolderQuotaScan)
router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader). router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader).
Delete(webUserPath+"/{username}", deleteUser) Delete(webUserPath+"/{username}", deleteUser)
router.With(s.checkPerm(dataprovider.PermAdminDisableMFA), verifyCSRFHeader).
Put(webUserPath+"/{username}/2fa/disable", disableUser2FA)
router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader). router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader).
Post(webQuotaScanPath+"/{username}", startUserQuotaScan) Post(webQuotaScanPath+"/{username}", startUserQuotaScan)
router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, s.handleWebMaintenance) router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, s.handleWebMaintenance)

View file

@ -4948,6 +4948,7 @@ components:
- manage_event_rules - manage_event_rules
- manage_roles - manage_roles
- manage_ip_lists - manage_ip_lists
- disable_mfa
description: | description: |
Admin permissions: Admin permissions:
* `*` - all permissions are granted * `*` - all permissions are granted
@ -4971,6 +4972,7 @@ components:
* `manage_event_rules` - manage event actions and rules is allowed * `manage_event_rules` - manage event actions and rules is allowed
* `manage_roles` - manage roles is allowed * `manage_roles` - manage roles is allowed
* `manage_ip_lists` - manage global and ratelimter allow lists and defender block and safe lists is allowed * `manage_ip_lists` - manage global and ratelimter allow lists and defender block and safe lists is allowed
* `disable_mfa` - allow to disable two-factor authentication for users and admins
FsProviders: FsProviders:
type: integer type: integer
enum: enum:

View file

@ -132,6 +132,7 @@
"add": "Add", "add": "Add",
"enable": "Enable", "enable": "Enable",
"disable": "Disable", "disable": "Disable",
"disable_confirm_btn": "Yes, disable",
"close": "Close", "close": "Close",
"search": "Search", "search": "Search",
"configuration": "Configuration", "configuration": "Configuration",
@ -403,6 +404,8 @@
"setup_title": "Set up two-factor authentication", "setup_title": "Set up two-factor authentication",
"setup_msg": "Use your preferred Authenticator App (e.g. Microsoft Authenticator, Google Authenticator, Authy, 1Password etc. ) to scan the QR code. It will generate an authentication code for you to enter below.", "setup_msg": "Use your preferred Authenticator App (e.g. Microsoft Authenticator, Google Authenticator, Authy, 1Password etc. ) to scan the QR code. It will generate an authentication code for you to enter below.",
"setup_help": "If you have trouble using the QR code, select manual entry on your app, and enter the code:", "setup_help": "If you have trouble using the QR code, select manual entry on your app, and enter the code:",
"disable_msg": "Disable 2FA",
"disable_confirm": "Do you want to disable two-factor authentication for the selected user? This action is generally only required if the user has lost access to the second authentication factor",
"disable_question": "Do you want to disable two-factor authentication?", "disable_question": "Do you want to disable two-factor authentication?",
"generate_question": "Do you want to generate a new secret key and invalidate the previous one? Any registered Authenticator App will stop working", "generate_question": "Do you want to generate a new secret key and invalidate the previous one? Any registered Authenticator App will stop working",
"disabled": "Two-factor authentication is disabled", "disabled": "Two-factor authentication is disabled",
@ -410,7 +413,7 @@
"recovery_codes_get_err": "Unable to obtain recovery codes", "recovery_codes_get_err": "Unable to obtain recovery codes",
"auth_code_invalid": "Failed to validate the provided authentication code", "auth_code_invalid": "Failed to validate the provided authentication code",
"auth_secret_gen_err": "Failed to generate authentication secret", "auth_secret_gen_err": "Failed to generate authentication secret",
"save_err": "Failed to save configuration", "save_err": "Failed to save two-factor authentication configuration",
"auth_code_required": "The authentication code is required", "auth_code_required": "The authentication code is required",
"no_protocol": "Please select at least a protocol", "no_protocol": "Please select at least a protocol",
"required_protocols": "Unable to disable two-factor authentication. The security policy configured for your account requires two-factor authentication for the following protocols: {{val}}", "required_protocols": "Unable to disable two-factor authentication. The security policy configured for your account requires two-factor authentication for the following protocols: {{val}}",

View file

@ -132,6 +132,7 @@
"add": "Aggiungi", "add": "Aggiungi",
"enable": "Abilita", "enable": "Abilita",
"disable": "Disabilita", "disable": "Disabilita",
"disable_confirm_btn": "Si, disabilita",
"close": "Chiudi", "close": "Chiudi",
"search": "Cerca", "search": "Cerca",
"configuration": "Configurazione", "configuration": "Configurazione",
@ -403,6 +404,8 @@
"setup_title": "Configura l'autenticazione a due fattori", "setup_title": "Configura l'autenticazione a due fattori",
"setup_msg": "Utilizza la tua app di autenticazione preferita (ad esempio Microsoft Authenticator, Google Authenticator, Authy, 1Password ecc.) per scansionare il codice QR. Verrà generato un codice di autenticazione da inserire di seguito.", "setup_msg": "Utilizza la tua app di autenticazione preferita (ad esempio Microsoft Authenticator, Google Authenticator, Authy, 1Password ecc.) per scansionare il codice QR. Verrà generato un codice di autenticazione da inserire di seguito.",
"setup_help": "Se hai problemi con l'utilizzo del codice QR, seleziona l'inserimento manuale sulla tua app e inserisci il codice:", "setup_help": "Se hai problemi con l'utilizzo del codice QR, seleziona l'inserimento manuale sulla tua app e inserisci il codice:",
"disable_msg": "Disabilita 2FA",
"disable_confirm": "Vuoi disabilitare l'autenticazione a due fattori per l'utente selezionato? Questa azione è generalmente necessaria solo se l'utente ha perso l'accesso al secondo fattore di autenticazione",
"disable_question": "Vuoi disattivare l'autenticazione a due fattori?", "disable_question": "Vuoi disattivare l'autenticazione a due fattori?",
"generate_question": "Vuoi generare un nuova chiave segreta e invalidare quella precedente? Qualsiasi app di autenticazione registrata smetterà di funzionare", "generate_question": "Vuoi generare un nuova chiave segreta e invalidare quella precedente? Qualsiasi app di autenticazione registrata smetterà di funzionare",
"disabled": "L'autenticazione a due fattori è disabilitata", "disabled": "L'autenticazione a due fattori è disabilitata",
@ -410,7 +413,7 @@
"recovery_codes_get_err": "Impossibile ottenere i codici di ripristino", "recovery_codes_get_err": "Impossibile ottenere i codici di ripristino",
"auth_code_invalid": "Impossibile convalidare il codice di autenticazione fornito", "auth_code_invalid": "Impossibile convalidare il codice di autenticazione fornito",
"auth_secret_gen_err": "Impossibile generare il segreto di autenticazione", "auth_secret_gen_err": "Impossibile generare il segreto di autenticazione",
"save_err": "Impossibile salvare la configurazione", "save_err": "Impossibile salvare la configurazione dell'autenticazione a due fattori",
"auth_code_required": "Il codice di autenticazione è obbligatorio", "auth_code_required": "Il codice di autenticazione è obbligatorio",
"no_protocol": "Seleziona almeno un protocollo", "no_protocol": "Seleziona almeno un protocollo",
"required_protocols": "Impossibile disabilitare l'autenticazione a due fattori. La politica di sicurezza configurata per il tuo account richiede l'autenticazione a due fattori per i seguenti protocolli: {{val}}", "required_protocols": "Impossibile disabilitare l'autenticazione a due fattori. La politica di sicurezza configurata per il tuo account richiede l'autenticazione a due fattori per i seguenti protocolli: {{val}}",

View file

@ -175,6 +175,47 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
}); });
} }
function disableSeconFactorAction(username) {
ModalAlert.fire({
text: $.t('2fa.disable_confirm'),
icon: "warning",
confirmButtonText: $.t('general.disable_confirm_btn'),
cancelButtonText: $.t('general.cancel'),
customClass: {
confirmButton: "btn btn-danger",
cancelButton: 'btn btn-secondary'
}
}).then((result) => {
if (result.isConfirmed){
$('#loading_message').text("");
KTApp.showPageLoading();
let path = '{{.AdminURL}}' + "/" + encodeURIComponent(username)+"/2fa/disable";
axios.put(path, null, {
timeout: 15000,
headers: {
'X-CSRF-TOKEN': '{{.CSRFToken}}'
},
validateStatus: function (status) {
return status == 200;
}
}).then(function(response){
location.reload();
}).catch(function(error){
KTApp.hidePageLoading();
ModalAlert.fire({
text: $.t('2fa.save_err'),
icon: "warning",
confirmButtonText: $.t('general.ok'),
customClass: {
confirmButton: "btn btn-primary"
}
});
});
}
});
}
var datatable = function(){ var datatable = function(){
var dt; var dt;
@ -273,17 +314,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
} }
}, },
{ {
data: "filters.totp_config.enabled", data: "filters.totp_config",
visible: false, visible: false,
defaultContent: false, defaultContent: false,
render: function(data, type, row) { render: function(data, type, row) {
if (type === 'display') { if (type === 'display') {
if (data){ if (data && data.enabled){
return $.t('general.active'); return $.t('general.active');
} }
return $.t('general.inactive') return $.t('general.inactive')
} }
return data; return data && data.enabled;
} }
}, },
{ {
@ -307,20 +348,39 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
className: 'text-end', className: 'text-end',
render: function (data, type, row) { render: function (data, type, row) {
if (type === 'display') { if (type === 'display') {
//{{- if .LoggedUser.HasPermission "manage_admins"}} let numActions = 0;
return `<button class="btn btn-light btn-active-light-primary btn-flex btn-center btn-sm rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end"> let username = "{{.LoggedUser.Username}}";
let actions = `<button class="btn btn-light btn-active-light-primary btn-flex btn-center btn-sm rotate" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end">
<span data-i18n="general.actions" class="fs-6">Actions</span> <span data-i18n="general.actions" class="fs-6">Actions</span>
<i class="ki-duotone ki-down fs-5 ms-1 rotate-180"></i> <i class="ki-duotone ki-down fs-5 ms-1 rotate-180"></i>
</button> </button>
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-700 menu-state-bg-light-primary fw-semibold fs-6 w-200px py-4" data-kt-menu="true"> <div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-700 menu-state-bg-light-primary fw-semibold fs-6 w-200px py-4" data-kt-menu="true">`;
<div class="menu-item px-3"> //{{- if .LoggedUser.HasPermission "manage_admins"}}
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> numActions++;
</div> actions+=`<div class="menu-item px-3">
<div class="menu-item px-3"> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> </div>`;
</div>
</div>`;
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "disable_mfa"}}
if (row.filters.totp_config && row.filters.totp_config.enabled && username != row.username){
numActions++;
actions+=`<div class="menu-item px-3">
<a data-i18n="2fa.disable_msg" href="#" class="menu-link text-danger px-3" data-table-action="disable_2fa_row">Disable 2FA</a>
</div>`;
}
//{{- end}}
//{{- if .LoggedUser.HasPermission "manage_admins"}}
if (username != row.username){
numActions++;
actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>`;
}
//{{- end}}
if (numActions > 0){
actions+=`</div>`;
return actions;
}
} }
return ""; return "";
} }
@ -406,6 +466,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
}); });
}); });
const diable2FAButtons = document.querySelectorAll('[data-table-action="disable_2fa_row"]');
diable2FAButtons.forEach(d => {
let el = $(d);
el.off("click");
el.on("click", function(e){
e.preventDefault();
let rowData = dt.row(e.target.closest('tr')).data();
disableSeconFactorAction(rowData['username']);
});
});
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]'); const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
deleteButtons.forEach(d => { deleteButtons.forEach(d => {
let el = $(d); let el = $(d);

View file

@ -230,11 +230,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;

View file

@ -286,17 +286,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
numActions++; numActions++;
if (row.trigger === 6){ if (row.trigger === 6){
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="rules.run" href="#" class="menu-link px-3" data-table-action="run_row">Run</a> <a data-i18n="rules.run" href="#" class="menu-link px-3" data-table-action="run_row">Run</a>
</div>` </div>`;
numActions++; numActions++;
} }
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;

View file

@ -287,19 +287,19 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "manage_system"}} //{{- if .LoggedUser.HasPermission "manage_system"}}
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a> <a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a>
</div>` </div>`;
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "manage_folders"}} //{{- if .LoggedUser.HasPermission "manage_folders"}}
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;

View file

@ -228,11 +228,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;

View file

@ -229,11 +229,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;

View file

@ -188,44 +188,85 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
}); });
} }
function quotaScanAction(username) { function disableSeconFactorAction(username) {
$('#loading_message').text(""); ModalAlert.fire({
KTApp.showPageLoading(); text: $.t('2fa.disable_confirm'),
let path = '{{.QuotaScanURL}}' + "/" + encodeURIComponent(username); icon: "warning",
axios.post(path, null, { confirmButtonText: $.t('general.disable_confirm_btn'),
timeout: 15000, cancelButtonText: $.t('general.cancel'),
headers: { customClass: {
'X-CSRF-TOKEN': '{{.CSRFToken}}' confirmButton: "btn btn-danger",
}, cancelButton: 'btn btn-secondary'
validateStatus: function (status) { }
return status == 202; }).then((result) => {
} if (result.isConfirmed){
}).then(function (response) { $('#loading_message').text("");
KTApp.hidePageLoading(); KTApp.showPageLoading();
showToast(1, 'general.quota_scan_started'); let path = '{{.UserURL}}' + "/" + encodeURIComponent(username)+"/2fa/disable";
}).catch(function (error) {
KTApp.hidePageLoading(); axios.put(path, null, {
let errorMessage; timeout: 15000,
if (error && error.response) { headers: {
switch (error.response.status) { 'X-CSRF-TOKEN': '{{.CSRFToken}}'
case 409: },
errorMessage = "general.quota_scan_conflit"; validateStatus: function (status) {
break; return status == 200;
}
}
if (!errorMessage) {
errorMessage = "general.quota_scan_error";
}
ModalAlert.fire({
text: $.t(errorMessage),
icon: "warning",
confirmButtonText: $.t('general.ok'),
customClass: {
confirmButton: "btn btn-primary"
} }
}).then(function(response){
location.reload();
}).catch(function(error){
KTApp.hidePageLoading();
ModalAlert.fire({
text: $.t('2fa.save_err'),
icon: "warning",
confirmButtonText: $.t('general.ok'),
customClass: {
confirmButton: "btn btn-primary"
}
});
}); });
}
});
}
function quotaScanAction(username) {
$('#loading_message').text("");
KTApp.showPageLoading();
let path = '{{.QuotaScanURL}}' + "/" + encodeURIComponent(username);
axios.post(path, null, {
timeout: 15000,
headers: {
'X-CSRF-TOKEN': '{{.CSRFToken}}'
},
validateStatus: function (status) {
return status == 202;
}
}).then(function (response) {
KTApp.hidePageLoading();
showToast(1, 'general.quota_scan_started');
}).catch(function (error) {
KTApp.hidePageLoading();
let errorMessage;
if (error && error.response) {
switch (error.response.status) {
case 409:
errorMessage = "general.quota_scan_conflit";
break;
}
}
if (!errorMessage) {
errorMessage = "general.quota_scan_error";
}
ModalAlert.fire({
text: $.t(errorMessage),
icon: "warning",
confirmButtonText: $.t('general.ok'),
customClass: {
confirmButton: "btn btn-primary"
}
}); });
} });
}
var datatable = function(){ var datatable = function(){
var dt; var dt;
@ -359,17 +400,20 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
} }
}, },
{ {
data: "filters.two_factor_protocols", data: "filters.totp_config",
visible: false, visible: false,
defaultContent: "", defaultContent: "",
render: function(data, type, row) { render: function(data, type, row) {
if (type === 'display') { let protocols = "";
if (data){ if (data && data.enabled){
return escapeHTML(data.join(', ')); protocols = data.protocols.join(', ');
}
return ""
} }
return data; if (type === 'display') {
if (protocols){
return escapeHTML(protocols);
}
}
return protocols;
} }
}, },
{ {
@ -486,25 +530,33 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a> <a data-i18n="general.edit" href="#" class="menu-link px-3" data-table-action="edit_row">Edit</a>
</div>` </div>`;
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "manage_system"}} //{{- if .LoggedUser.HasPermission "manage_system"}}
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a> <a data-i18n="general.template" href="#" class="menu-link px-3" data-table-action="template_row">Template</a>
</div>` </div>`;
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "quota_scans"}} //{{- if .LoggedUser.HasPermission "quota_scans"}}
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.quota_scan" href="#" class="menu-link px-3" data-table-action="quota_scan_row">Quota scan</a> <a data-i18n="general.quota_scan" href="#" class="menu-link px-3" data-table-action="quota_scan_row">Quota scan</a>
</div>` </div>`;
//{{- end}}
//{{- if .LoggedUser.HasPermission "disable_mfa"}}
if (row.filters.totp_config && row.filters.totp_config.enabled){
numActions++;
actions+=`<div class="menu-item px-3">
<a data-i18n="2fa.disable_msg" href="#" class="menu-link text-danger px-3" data-table-action="disable_2fa_row">Disable 2FA</a>
</div>`;
}
//{{- end}} //{{- end}}
//{{- if .LoggedUser.HasPermission "del_users"}} //{{- if .LoggedUser.HasPermission "del_users"}}
numActions++; numActions++;
actions+=`<div class="menu-item px-3"> actions+=`<div class="menu-item px-3">
<a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a> <a data-i18n="general.delete" href="#" class="menu-link text-danger px-3" data-table-action="delete_row">Delete</a>
</div>` </div>`;
//{{- end}} //{{- end}}
if (numActions > 0){ if (numActions > 0){
actions+=`</div>`; actions+=`</div>`;
@ -620,6 +672,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
}); });
}); });
const diable2FAButtons = document.querySelectorAll('[data-table-action="disable_2fa_row"]');
diable2FAButtons.forEach(d => {
let el = $(d);
el.off("click");
el.on("click", function(e){
e.preventDefault();
let rowData = dt.row(e.target.closest('tr')).data();
disableSeconFactorAction(rowData['username']);
});
});
const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]'); const deleteButtons = document.querySelectorAll('[data-table-action="delete_row"]');
deleteButtons.forEach(d => { deleteButtons.forEach(d => {
let el = $(d); let el = $(d);