From a577d8b3cd27f95a29996ad0eb39d02fa7eb99f1 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Fri, 23 Feb 2024 18:24:07 +0100 Subject: [PATCH] WebAdmin: allow to disable 2FA Before it was only possible using REST API Signed-off-by: Nicola Murino --- go.mod | 30 +++--- go.sum | 60 +++++------ internal/dataprovider/admin.go | 3 +- internal/httpd/api_admin.go | 4 + internal/httpd/api_user.go | 4 + internal/httpd/httpd_test.go | 44 ++++++-- internal/httpd/server.go | 8 +- openapi/openapi.yaml | 2 + static/locales/en/translation.json | 5 +- static/locales/it/translation.json | 5 +- templates/webadmin/admins.html | 97 ++++++++++++++--- templates/webadmin/eventactions.html | 4 +- templates/webadmin/eventrules.html | 6 +- templates/webadmin/folders.html | 6 +- templates/webadmin/groups.html | 4 +- templates/webadmin/roles.html | 4 +- templates/webadmin/users.html | 155 +++++++++++++++++++-------- 17 files changed, 312 insertions(+), 129 deletions(-) diff --git a/go.mod b/go.mod index c4a0123e..4bf61dc8 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,14 @@ require ( 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.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/config v1.27.3 + 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/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/aws/aws-sdk-go-v2/feature/s3/manager v1.16.5 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.51.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.28.0 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 @@ -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.166.0 + google.golang.org/api v0.167.0 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/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/aws-sdk-go-v2/service/sso v1.20.0 // 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/beorn7/perks 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // 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/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -162,11 +162,11 @@ require ( 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/net/http/otelhttp v0.48.0 // indirect - go.opentelemetry.io/otel v1.23.1 // indirect - go.opentelemetry.io/otel/metric v1.23.1 // indirect - go.opentelemetry.io/otel/trace v1.23.1 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/metric v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.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/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index 407b52fe..fe4528ed 100644 --- a/go.sum +++ b/go.sum @@ -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/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/config v1.27.3 h1:0PRdb/q5a77HVYj+2rvPiCObfMfl/pWhwa5cs3cnl3c= +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.3 h1:dDM5wrgwOL5gTZ0Gv/bvewPldjBcJywoaO5ClERrOGE= +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/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/feature/s3/manager v1.16.5 h1:IEv6homMJMnedG/2VWfNuV34ouXUmK8E7y4rAl59Fhs= +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/go.mod h1:rH61DT6FDdikhPghymripNUCsf+uVF4Cnk4c4DBKH64= 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/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/aws-sdk-go-v2/service/marketplacemetering v1.21.0 h1:NEWfVJgrDwEXrKDNrrX+2LtWhKVLYpJ2d7/gr1N1B54= +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.51.0 h1:rNVsCe3bqTAhG+qjnHJKgYKdHEsqqo/GMK3gEYY8W6g= +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.28.0 h1:Xf3s55N9cqKvFK6D70zCXvXXN4ZovTCy7glL+gUhLEc= +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.20.0 h1:6YL8G91QZ52KlPrLkEgEez5kejIVwChVCgND3qgY5j0= +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.23.0 h1:+DqIa5Ll7W311QLUvGFDdVit9uC4G0VioDdw08cXcow= +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.28.0 h1:F7tQr61zYnTaeY50Rn4jwfVQbtcqJuBRwN/nGGNwzb0= +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/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= 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.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= 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.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +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/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 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/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/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= -go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= -go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= -go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +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/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= -go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +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/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= 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/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-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 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-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.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24= -google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= +google.golang.org/api v0.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE= +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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= diff --git a/internal/dataprovider/admin.go b/internal/dataprovider/admin.go index dac80ea4..366882d2 100644 --- a/internal/dataprovider/admin.go +++ b/internal/dataprovider/admin.go @@ -58,6 +58,7 @@ const ( PermAdminManageEventRules = "manage_event_rules" PermAdminManageRoles = "manage_roles" PermAdminManageIPLists = "manage_ip_lists" + PermAdminDisableMFA = "disable_mfa" ) const ( @@ -75,7 +76,7 @@ var ( PermAdminCloseConnections, PermAdminViewServerStatus, PermAdminManageAdmins, PermAdminManageRoles, PermAdminManageEventRules, PermAdminManageAPIKeys, PermAdminQuotaScans, PermAdminManageSystem, PermAdminManageDefender, PermAdminViewDefender, PermAdminManageIPLists, PermAdminRetentionChecks, - PermAdminViewEvents} + PermAdminViewEvents, PermAdminDisableMFA} forbiddenPermsForRoleAdmins = []string{PermAdminAny, PermAdminManageAdmins, PermAdminManageSystem, PermAdminManageEventRules, PermAdminManageIPLists, PermAdminManageRoles} ) diff --git a/internal/httpd/api_admin.go b/internal/httpd/api_admin.go index feb2a4d8..b232362c 100644 --- a/internal/httpd/api_admin.go +++ b/internal/httpd/api_admin.go @@ -100,6 +100,10 @@ func disableAdmin2FA(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) 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.Filters.RequireTwoFactor { err := util.NewValidationError("two-factor authentication must be enabled") diff --git a/internal/httpd/api_user.go b/internal/httpd/api_user.go index 750f52e2..551aed11 100644 --- a/internal/httpd/api_user.go +++ b/internal/httpd/api_user.go @@ -134,6 +134,10 @@ func disableUser2FA(w http.ResponseWriter, r *http.Request) { sendAPIResponse(w, r, err, "", getRespStatus(err)) 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.TOTPConfig = dataprovider.UserTOTPConfig{ Enabled: false, diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go index 333ca00f..b2fc9a50 100644 --- a/internal/httpd/httpd_test.go +++ b/internal/httpd/httpd_test.go @@ -6812,14 +6812,6 @@ func TestNamingRules(t *testing.T) { rr = executeRequest(req) checkResponseCode(t, http.StatusBadRequest, rr) 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 form = make(url.Values) form.Set("username", admin.Username) @@ -9086,6 +9078,18 @@ func TestAdminTwoFactorLogin(t *testing.T) { admin.Password = altAdminPassword admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated) 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 configName, key, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], admin.Username) assert.NoError(t, err) @@ -9369,12 +9373,27 @@ func TestAdminTwoFactorLogin(t *testing.T) { assert.NoError(t, err) // 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) assert.NoError(t, err) setBearerForReq(req, altToken) rr = executeRequest(req) 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.Set("recovery_code", recoveryCode) form.Set(csrfFormToken, csrfToken) @@ -9399,6 +9418,8 @@ func TestAdminTwoFactorLogin(t *testing.T) { _, err = httpdtest.RemoveAdmin(admin, http.StatusOK) 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()))) assert.NoError(t, err) @@ -10107,6 +10128,13 @@ func TestWebUserTwoFactorLogin(t *testing.T) { rr = executeRequest(req) 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.Set("recovery_code", recoveryCode) form.Set("passcode", passcode) diff --git a/internal/httpd/server.go b/internal/httpd/server.go index 4fddeb19..6dcb0195 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -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.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.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+"/{name}", getFolderByName) //nolint:goconst 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)).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.PermAdminDisableMFA)).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) @@ -1752,6 +1752,8 @@ func (s *httpdServer) setupWebAdminRoutes() { s.handleWebUpdateAdminPost) router.With(s.checkPerm(dataprovider.PermAdminManageAdmins), verifyCSRFHeader). Delete(webAdminPath+"/{username}", deleteAdmin) + router.With(s.checkPerm(dataprovider.PermAdminDisableMFA), verifyCSRFHeader). + Put(webAdminPath+"/{username}/2fa/disable", disableAdmin2FA) router.With(s.checkPerm(dataprovider.PermAdminCloseConnections), verifyCSRFHeader). Delete(webConnectionsPath+"/{connectionID}", handleCloseConnection) router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie). @@ -1764,6 +1766,8 @@ func (s *httpdServer) setupWebAdminRoutes() { Post(webScanVFolderPath+"/{name}", startFolderQuotaScan) router.With(s.checkPerm(dataprovider.PermAdminDeleteUsers), verifyCSRFHeader). Delete(webUserPath+"/{username}", deleteUser) + router.With(s.checkPerm(dataprovider.PermAdminDisableMFA), verifyCSRFHeader). + Put(webUserPath+"/{username}/2fa/disable", disableUser2FA) router.With(s.checkPerm(dataprovider.PermAdminQuotaScans), verifyCSRFHeader). Post(webQuotaScanPath+"/{username}", startUserQuotaScan) router.With(s.checkPerm(dataprovider.PermAdminManageSystem)).Get(webMaintenancePath, s.handleWebMaintenance) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index bce6e97a..0c2234d7 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -4948,6 +4948,7 @@ components: - manage_event_rules - manage_roles - manage_ip_lists + - disable_mfa description: | Admin permissions: * `*` - all permissions are granted @@ -4971,6 +4972,7 @@ components: * `manage_event_rules` - manage event actions and rules 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 + * `disable_mfa` - allow to disable two-factor authentication for users and admins FsProviders: type: integer enum: diff --git a/static/locales/en/translation.json b/static/locales/en/translation.json index 6c2db3a5..9293fd1a 100644 --- a/static/locales/en/translation.json +++ b/static/locales/en/translation.json @@ -132,6 +132,7 @@ "add": "Add", "enable": "Enable", "disable": "Disable", + "disable_confirm_btn": "Yes, disable", "close": "Close", "search": "Search", "configuration": "Configuration", @@ -403,6 +404,8 @@ "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_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?", "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", @@ -410,7 +413,7 @@ "recovery_codes_get_err": "Unable to obtain recovery codes", "auth_code_invalid": "Failed to validate the provided authentication code", "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", "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}}", diff --git a/static/locales/it/translation.json b/static/locales/it/translation.json index 81dd676a..1b6ef580 100644 --- a/static/locales/it/translation.json +++ b/static/locales/it/translation.json @@ -132,6 +132,7 @@ "add": "Aggiungi", "enable": "Abilita", "disable": "Disabilita", + "disable_confirm_btn": "Si, disabilita", "close": "Chiudi", "search": "Cerca", "configuration": "Configurazione", @@ -403,6 +404,8 @@ "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_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?", "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", @@ -410,7 +413,7 @@ "recovery_codes_get_err": "Impossibile ottenere i codici di ripristino", "auth_code_invalid": "Impossibile convalidare il codice di autenticazione fornito", "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", "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}}", diff --git a/templates/webadmin/admins.html b/templates/webadmin/admins.html index 1ab40e49..0e9e02c0 100644 --- a/templates/webadmin/admins.html +++ b/templates/webadmin/admins.html @@ -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 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, defaultContent: false, render: function(data, type, row) { if (type === 'display') { - if (data){ + if (data && data.enabled){ return $.t('general.active'); } 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', render: function (data, type, row) { if (type === 'display') { - //{{- if .LoggedUser.HasPermission "manage_admins"}} - return ` - `; + `; + return actions; + } } 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"]'); deleteButtons.forEach(d => { let el = $(d); diff --git a/templates/webadmin/eventactions.html b/templates/webadmin/eventactions.html index ab1faf36..0b8dd7b0 100644 --- a/templates/webadmin/eventactions.html +++ b/templates/webadmin/eventactions.html @@ -230,11 +230,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). numActions++; actions+=`` + `; numActions++; actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; diff --git a/templates/webadmin/eventrules.html b/templates/webadmin/eventrules.html index 10df0b3e..4dcaf544 100644 --- a/templates/webadmin/eventrules.html +++ b/templates/webadmin/eventrules.html @@ -286,17 +286,17 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). numActions++; actions+=`` + `; numActions++; if (row.trigger === 6){ actions+=`` + `; numActions++; } actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; diff --git a/templates/webadmin/folders.html b/templates/webadmin/folders.html index b5e597d8..3a10d975 100644 --- a/templates/webadmin/folders.html +++ b/templates/webadmin/folders.html @@ -287,19 +287,19 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). numActions++; actions+=`` + `; //{{- end}} //{{- if .LoggedUser.HasPermission "manage_system"}} numActions++; actions+=`` + `; //{{- end}} //{{- if .LoggedUser.HasPermission "manage_folders"}} numActions++; actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; diff --git a/templates/webadmin/groups.html b/templates/webadmin/groups.html index 8e8919ac..049eec8e 100644 --- a/templates/webadmin/groups.html +++ b/templates/webadmin/groups.html @@ -228,11 +228,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). numActions++; actions+=`` + `; numActions++; actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; diff --git a/templates/webadmin/roles.html b/templates/webadmin/roles.html index 5f4e4760..6a726796 100644 --- a/templates/webadmin/roles.html +++ b/templates/webadmin/roles.html @@ -229,11 +229,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). numActions++; actions+=`` + `; numActions++; actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; diff --git a/templates/webadmin/users.html b/templates/webadmin/users.html index 25cbf020..8800369b 100644 --- a/templates/webadmin/users.html +++ b/templates/webadmin/users.html @@ -188,44 +188,85 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). }); } - 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" + 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 = '{{.UserURL}}' + "/" + 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" + } + }); }); + } + }); + } + + 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 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, defaultContent: "", render: function(data, type, row) { - if (type === 'display') { - if (data){ - return escapeHTML(data.join(', ')); - } - return "" + let protocols = ""; + if (data && data.enabled){ + protocols = data.protocols.join(', '); } - 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++; actions+=`` + `; //{{- end}} //{{- if .LoggedUser.HasPermission "manage_system"}} numActions++; actions+=`` + `; //{{- end}} //{{- if .LoggedUser.HasPermission "quota_scans"}} numActions++; actions+=`` + `; + //{{- end}} + //{{- if .LoggedUser.HasPermission "disable_mfa"}} + if (row.filters.totp_config && row.filters.totp_config.enabled){ + numActions++; + actions+=``; + } //{{- end}} //{{- if .LoggedUser.HasPermission "del_users"}} numActions++; actions+=`` + `; //{{- end}} if (numActions > 0){ actions+=``; @@ -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"]'); deleteButtons.forEach(d => { let el = $(d);