diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml
index f85e185ecd..baca5175e1 100644
--- a/cmd/defaults.yaml
+++ b/cmd/defaults.yaml
@@ -710,7 +710,8 @@ DefaultInstance:
ErrorMsgPopup: false # ZITADEL_DEFAULTINSTANCE_LABELPOLICY_ERRORMSGPOPUP
DisableWatermark: false # ZITADEL_DEFAULTINSTANCE_LABELPOLICY_DISABLEWATERMARK
LockoutPolicy:
- MaxAttempts: 0 # ZITADEL_DEFAULTINSTANCE_LOCKOUTPOLICY_MAXATTEMPTS
+ MaxPasswordAttempts: 0 # ZITADEL_DEFAULTINSTANCE_LOCKOUTPOLICY_MAXPASSWORDATTEMPTS
+ MaxOTPAttempts: 0 # ZITADEL_DEFAULTINSTANCE_LOCKOUTPOLICY_MAXOTPATTEMPTS
ShouldShowLockoutFailure: true # ZITADEL_DEFAULTINSTANCE_LOCKOUTPOLICY_SHOULDSHOWLOCKOUTFAILURE
EmailTemplate: CjwhZG9jdHlwZSBodG1sPgo8aHRtbCB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwiIHhtbG5zOm89InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9mZmljZSI+CjxoZWFkPgogIDx0aXRsZT4KCiAgPC90aXRsZT4KICA8IS0tW2lmICFtc29dPjwhLS0+CiAgPG1ldGEgaHR0cC1lcXVpdj0iWC1VQS1Db21wYXRpYmxlIiBjb250ZW50PSJJRT1lZGdlIj4KICA8IS0tPCFbZW5kaWZdLS0+CiAgPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9VVRGLTgiPgogIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSI+CiAgPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KICAgICNvdXRsb29rIGEgeyBwYWRkaW5nOjA7IH0KICAgIGJvZHkgeyBtYXJnaW46MDtwYWRkaW5nOjA7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0OjEwMCU7LW1zLXRleHQtc2l6ZS1hZGp1c3Q6MTAwJTsgfQogICAgdGFibGUsIHRkIHsgYm9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO21zby10YWJsZS1sc3BhY2U6MHB0O21zby10YWJsZS1yc3BhY2U6MHB0OyB9CiAgICBpbWcgeyBib3JkZXI6MDtoZWlnaHQ6YXV0bztsaW5lLWhlaWdodDoxMDAlOyBvdXRsaW5lOm5vbmU7dGV4dC1kZWNvcmF0aW9uOm5vbmU7LW1zLWludGVycG9sYXRpb24tbW9kZTpiaWN1YmljOyB9CiAgICBwIHsgZGlzcGxheTpibG9jazttYXJnaW46MTNweCAwOyB9CiAgPC9zdHlsZT4KICA8IS0tW2lmIG1zb10+CiAgPHhtbD4KICAgIDxvOk9mZmljZURvY3VtZW50U2V0dGluZ3M+CiAgICAgIDxvOkFsbG93UE5HLz4KICAgICAgPG86UGl4ZWxzUGVySW5jaD45NjwvbzpQaXhlbHNQZXJJbmNoPgogICAgPC9vOk9mZmljZURvY3VtZW50U2V0dGluZ3M+CiAgPC94bWw+CiAgPCFbZW5kaWZdLS0+CiAgPCEtLVtpZiBsdGUgbXNvIDExXT4KICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgogICAgLm1qLW91dGxvb2stZ3JvdXAtZml4IHsgd2lkdGg6MTAwJSAhaW1wb3J0YW50OyB9CiAgPC9zdHlsZT4KICA8IVtlbmRpZl0tLT4KCgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CiAgICBAbWVkaWEgb25seSBzY3JlZW4gYW5kIChtaW4td2lkdGg6NDgwcHgpIHsKICAgICAgLm1qLWNvbHVtbi1wZXItMTAwIHsgd2lkdGg6MTAwJSAhaW1wb3J0YW50OyBtYXgtd2lkdGg6IDEwMCU7IH0KICAgICAgLm1qLWNvbHVtbi1wZXItNjAgeyB3aWR0aDo2MCUgIWltcG9ydGFudDsgbWF4LXdpZHRoOiA2MCU7IH0KICAgIH0KICA8L3N0eWxlPgoKCiAgPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCgoKICAgIEBtZWRpYSBvbmx5IHNjcmVlbiBhbmQgKG1heC13aWR0aDo0ODBweCkgewogICAgICB0YWJsZS5tai1mdWxsLXdpZHRoLW1vYmlsZSB7IHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7IH0KICAgICAgdGQubWotZnVsbC13aWR0aC1tb2JpbGUgeyB3aWR0aDogYXV0byAhaW1wb3J0YW50OyB9CiAgICB9CgogIDwvc3R5bGU+CiAgPHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc2hhZG93IGEgewogICAgYm94LXNoYWRvdzogMHB4IDNweCAxcHggLTJweCByZ2JhKDAsIDAsIDAsIDAuMiksIDBweCAycHggMnB4IDBweCByZ2JhKDAsIDAsIDAsIDAuMTQpLCAwcHggMXB4IDVweCAwcHggcmdiYSgwLCAwLCAwLCAwLjEyKTsKICB9PC9zdHlsZT4KCiAge3tpZiAuRm9udFVSTH19CiAgPHN0eWxlPgogICAgQGZvbnQtZmFjZSB7CiAgICAgIGZvbnQtZmFtaWx5OiAne3suRm9udEZhY2VGYW1pbHl9fSc7CiAgICAgIGZvbnQtc3R5bGU6IG5vcm1hbDsKICAgICAgZm9udC1kaXNwbGF5OiBzd2FwOwogICAgICBzcmM6IHVybCh7ey5Gb250VVJMfX0pOwogICAgfQogIDwvc3R5bGU+CiAge3tlbmR9fQoKPC9oZWFkPgo8Ym9keSBzdHlsZT0id29yZC1zcGFjaW5nOm5vcm1hbDsiPgoKCjxkaXYKICAgICAgICBzdHlsZT0iIgo+CgogIDx0YWJsZQogICAgICAgICAgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIHJvbGU9InByZXNlbnRhdGlvbiIgc3R5bGU9ImJhY2tncm91bmQ6e3suQmFja2dyb3VuZENvbG9yfX07YmFja2dyb3VuZC1jb2xvcjp7ey5CYWNrZ3JvdW5kQ29sb3J9fTt3aWR0aDoxMDAlO2JvcmRlci1yYWRpdXM6MTZweDsiCiAgPgogICAgPHRib2R5PgogICAgPHRyPgogICAgICA8dGQ+CgoKICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48dGFibGUgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIGNsYXNzPSIiIHN0eWxlPSJ3aWR0aDo4MDBweDsiIHdpZHRoPSI4MDAiID48dHI+PHRkIHN0eWxlPSJsaW5lLWhlaWdodDowcHg7Zm9udC1zaXplOjBweDttc28tbGluZS1oZWlnaHQtcnVsZTpleGFjdGx5OyI+PCFbZW5kaWZdLS0+CgoKICAgICAgICA8ZGl2ICBzdHlsZT0ibWFyZ2luOjBweCBhdXRvO2JvcmRlci1yYWRpdXM6MTZweDttYXgtd2lkdGg6ODAwcHg7Ij4KCiAgICAgICAgICA8dGFibGUKICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIHJvbGU9InByZXNlbnRhdGlvbiIgc3R5bGU9IndpZHRoOjEwMCU7Ym9yZGVyLXJhZGl1czoxNnB4OyIKICAgICAgICAgID4KICAgICAgICAgICAgPHRib2R5PgogICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgPHRkCiAgICAgICAgICAgICAgICAgICAgICBzdHlsZT0iZGlyZWN0aW9uOmx0cjtmb250LXNpemU6MHB4O3BhZGRpbmc6MjBweCAwO3BhZGRpbmctbGVmdDowO3RleHQtYWxpZ246Y2VudGVyOyIKICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48dGFibGUgcm9sZT0icHJlc2VudGF0aW9uIiBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCI+PHRyPjx0ZCBjbGFzcz0iIiB3aWR0aD0iODAwcHgiID48IVtlbmRpZl0tLT4KCiAgICAgICAgICAgICAgICA8dGFibGUKICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIHJvbGU9InByZXNlbnRhdGlvbiIgc3R5bGU9IndpZHRoOjEwMCU7IgogICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICA8dGQ+CgoKICAgICAgICAgICAgICAgICAgICAgIDwhLS1baWYgbXNvIHwgSUVdPjx0YWJsZSBhbGlnbj0iY2VudGVyIiBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgY2xhc3M9IiIgc3R5bGU9IndpZHRoOjgwMHB4OyIgd2lkdGg9IjgwMCIgPjx0cj48dGQgc3R5bGU9ImxpbmUtaGVpZ2h0OjBweDtmb250LXNpemU6MHB4O21zby1saW5lLWhlaWdodC1ydWxlOmV4YWN0bHk7Ij48IVtlbmRpZl0tLT4KCgogICAgICAgICAgICAgICAgICAgICAgPGRpdiAgc3R5bGU9Im1hcmdpbjowcHggYXV0bzttYXgtd2lkdGg6ODAwcHg7Ij4KCiAgICAgICAgICAgICAgICAgICAgICAgIDx0YWJsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduPSJjZW50ZXIiIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIwIiByb2xlPSJwcmVzZW50YXRpb24iIHN0eWxlPSJ3aWR0aDoxMDAlOyIKICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgIDx0Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGU9ImRpcmVjdGlvbjpsdHI7Zm9udC1zaXplOjBweDtwYWRkaW5nOjA7dGV4dC1hbGlnbjpjZW50ZXI7IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48dGFibGUgcm9sZT0icHJlc2VudGF0aW9uIiBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCI+PHRyPjx0ZCBjbGFzcz0iIiBzdHlsZT0id2lkdGg6ODAwcHg7IiA+PCFbZW5kaWZdLS0+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9Im1qLWNvbHVtbi1wZXItMTAwIG1qLW91dGxvb2stZ3JvdXAtZml4IiBzdHlsZT0iZm9udC1zaXplOjA7bGluZS1oZWlnaHQ6MDt0ZXh0LWFsaWduOmxlZnQ7ZGlzcGxheTppbmxpbmUtYmxvY2s7d2lkdGg6MTAwJTtkaXJlY3Rpb246bHRyOyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwhLS1baWYgbXNvIHwgSUVdPjx0YWJsZSBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiA+PHRyPjx0ZCBzdHlsZT0idmVydGljYWwtYWxpZ246dG9wO3dpZHRoOjgwMHB4OyIgPjwhW2VuZGlmXS0tPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz0ibWotY29sdW1uLXBlci0xMDAgbWotb3V0bG9vay1ncm91cC1maXgiIHN0eWxlPSJmb250LXNpemU6MHB4O3RleHQtYWxpZ246bGVmdDtkaXJlY3Rpb246bHRyO2Rpc3BsYXk6aW5saW5lLWJsb2NrO3ZlcnRpY2FsLWFsaWduOnRvcDt3aWR0aDoxMDAlOyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIwIiByb2xlPSJwcmVzZW50YXRpb24iIHdpZHRoPSIxMDAlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQgIHN0eWxlPSJ2ZXJ0aWNhbC1hbGlnbjp0b3A7cGFkZGluZzowOyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7e2lmIC5Mb2dvVVJMfX0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0YWJsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiBzdHlsZT0iIiB3aWR0aD0iMTAwJSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRib2R5PgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgc3R5bGU9ImZvbnQtc2l6ZTowcHg7cGFkZGluZzo1MHB4IDAgMzBweCAwO3dvcmQtYnJlYWs6YnJlYWstd29yZDsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0YWJsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiBzdHlsZT0iYm9yZGVyLWNvbGxhcHNlOmNvbGxhcHNlO2JvcmRlci1zcGFjaW5nOjBweDsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCAgc3R5bGU9IndpZHRoOjE4MHB4OyI+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGltZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQ9ImF1dG8iIHNyYz0ie3suTG9nb1VSTH19IiBzdHlsZT0iYm9yZGVyOjA7Ym9yZGVyLXJhZGl1czo4cHg7ZGlzcGxheTpibG9jaztvdXRsaW5lOm5vbmU7dGV4dC1kZWNvcmF0aW9uOm5vbmU7aGVpZ2h0OmF1dG87d2lkdGg6MTAwJTtmb250LXNpemU6MTNweDsiIHdpZHRoPSIxODAiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge3tlbmR9fQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPCEtLVtpZiBtc28gfCBJRV0+PC90ZD48L3RyPjwvdGFibGU+PCFbZW5kaWZdLS0+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPCEtLVtpZiBtc28gfCBJRV0+PC90ZD48L3RyPjwvdGFibGU+PCFbZW5kaWZdLS0+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KCiAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KCgogICAgICAgICAgICAgICAgICAgICAgPCEtLVtpZiBtc28gfCBJRV0+PC90ZD48L3RyPjwvdGFibGU+PCFbZW5kaWZdLS0+CgoKICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgPC90YWJsZT4KCiAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48L3RkPjwvdHI+PHRyPjx0ZCBjbGFzcz0iIiB3aWR0aD0iODAwcHgiID48IVtlbmRpZl0tLT4KCiAgICAgICAgICAgICAgICA8dGFibGUKICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIHJvbGU9InByZXNlbnRhdGlvbiIgc3R5bGU9IndpZHRoOjEwMCU7IgogICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICA8dGQ+CgoKICAgICAgICAgICAgICAgICAgICAgIDwhLS1baWYgbXNvIHwgSUVdPjx0YWJsZSBhbGlnbj0iY2VudGVyIiBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgY2xhc3M9IiIgc3R5bGU9IndpZHRoOjgwMHB4OyIgd2lkdGg9IjgwMCIgPjx0cj48dGQgc3R5bGU9ImxpbmUtaGVpZ2h0OjBweDtmb250LXNpemU6MHB4O21zby1saW5lLWhlaWdodC1ydWxlOmV4YWN0bHk7Ij48IVtlbmRpZl0tLT4KCgogICAgICAgICAgICAgICAgICAgICAgPGRpdiAgc3R5bGU9Im1hcmdpbjowcHggYXV0bzttYXgtd2lkdGg6ODAwcHg7Ij4KCiAgICAgICAgICAgICAgICAgICAgICAgIDx0YWJsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduPSJjZW50ZXIiIGJvcmRlcj0iMCIgY2VsbHBhZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIwIiByb2xlPSJwcmVzZW50YXRpb24iIHN0eWxlPSJ3aWR0aDoxMDAlOyIKICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgIDx0Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGU9ImRpcmVjdGlvbjpsdHI7Zm9udC1zaXplOjBweDtwYWRkaW5nOjA7dGV4dC1hbGlnbjpjZW50ZXI7IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48dGFibGUgcm9sZT0icHJlc2VudGF0aW9uIiBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCI+PHRyPjx0ZCBjbGFzcz0iIiBzdHlsZT0idmVydGljYWwtYWxpZ246dG9wO3dpZHRoOjQ4MHB4OyIgPjwhW2VuZGlmXS0tPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJtai1jb2x1bW4tcGVyLTYwIG1qLW91dGxvb2stZ3JvdXAtZml4IiBzdHlsZT0iZm9udC1zaXplOjBweDt0ZXh0LWFsaWduOmxlZnQ7ZGlyZWN0aW9uOmx0cjtkaXNwbGF5OmlubGluZS1ibG9jazt2ZXJ0aWNhbC1hbGlnbjp0b3A7d2lkdGg6MTAwJTsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiB3aWR0aD0iMTAwJSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZCAgc3R5bGU9InZlcnRpY2FsLWFsaWduOnRvcDtwYWRkaW5nOjA7Ij4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiBzdHlsZT0iIiB3aWR0aD0iMTAwJSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGJvZHk+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGlnbj0iY2VudGVyIiBzdHlsZT0iZm9udC1zaXplOjBweDtwYWRkaW5nOjEwcHggMjVweDt3b3JkLWJyZWFrOmJyZWFrLXdvcmQ7IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlPSJmb250LWZhbWlseTp7ey5Gb250RmFtaWx5fX07Zm9udC1zaXplOjI0cHg7Zm9udC13ZWlnaHQ6NTAwO2xpbmUtaGVpZ2h0OjE7dGV4dC1hbGlnbjpjZW50ZXI7Y29sb3I6e3suRm9udENvbG9yfX07IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID57ey5HcmVldGluZ319PC9kaXY+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduPSJjZW50ZXIiIHN0eWxlPSJmb250LXNpemU6MHB4O3BhZGRpbmc6MTBweCAyNXB4O3dvcmQtYnJlYWs6YnJlYWstd29yZDsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGU9ImZvbnQtZmFtaWx5Ont7LkZvbnRGYW1pbHl9fTtmb250LXNpemU6MTZweDtmb250LXdlaWdodDpsaWdodDtsaW5lLWhlaWdodDoxLjU7dGV4dC1hbGlnbjpjZW50ZXI7Y29sb3I6e3suRm9udENvbG9yfX07IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID57ey5UZXh0fX08L2Rpdj4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgoKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0ZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduPSJjZW50ZXIiIHZlcnRpY2FsLWFsaWduPSJtaWRkbGUiIGNsYXNzPSJzaGFkb3ciIHN0eWxlPSJmb250LXNpemU6MHB4O3BhZGRpbmc6MTBweCAyNXB4O3dvcmQtYnJlYWs6YnJlYWstd29yZDsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3JkZXI9IjAiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0iMCIgcm9sZT0icHJlc2VudGF0aW9uIiBzdHlsZT0iYm9yZGVyLWNvbGxhcHNlOnNlcGFyYXRlO2xpbmUtaGVpZ2h0OjEwMCU7IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgYmdjb2xvcj0ie3suUHJpbWFyeUNvbG9yfX0iIHJvbGU9InByZXNlbnRhdGlvbiIgc3R5bGU9ImJvcmRlcjpub25lO2JvcmRlci1yYWRpdXM6NnB4O2N1cnNvcjphdXRvO21zby1wYWRkaW5nLWFsdDoxMHB4IDI1cHg7YmFja2dyb3VuZDp7ey5QcmltYXJ5Q29sb3J9fTsiIHZhbGlnbj0ibWlkZGxlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhyZWY9Int7LlVSTH19IiByZWw9Im5vb3BlbmVyIG5vcmVmZXJyZXIgbm90cmFjayIgc3R5bGU9ImRpc3BsYXk6aW5saW5lLWJsb2NrO2JhY2tncm91bmQ6e3suUHJpbWFyeUNvbG9yfX07Y29sb3I6I2ZmZmZmZjtmb250LWZhbWlseTp7ey5Gb250RmFtaWx5fX07Zm9udC1zaXplOjE0cHg7Zm9udC13ZWlnaHQ6NTAwO2xpbmUtaGVpZ2h0OjEyMCU7bWFyZ2luOjA7dGV4dC1kZWNvcmF0aW9uOm5vbmU7dGV4dC10cmFuc2Zvcm06bm9uZTtwYWRkaW5nOjEwcHggMjVweDttc28tcGFkZGluZy1hbHQ6MHB4O2JvcmRlci1yYWRpdXM6NnB4OyIgdGFyZ2V0PSJfYmxhbmsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge3suQnV0dG9uVGV4dH19CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90ZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7e2lmIC5JbmNsdWRlRm9vdGVyfX0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgc3R5bGU9ImZvbnQtc2l6ZTowcHg7cGFkZGluZzoxMHB4IDI1cHg7cGFkZGluZy10b3A6MjBweDtwYWRkaW5nLXJpZ2h0OjIwcHg7cGFkZGluZy1ib3R0b206MjBweDtwYWRkaW5nLWxlZnQ6MjBweDt3b3JkLWJyZWFrOmJyZWFrLXdvcmQ7IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZT0iYm9yZGVyLXRvcDpzb2xpZCAycHggI2RiZGJkYjtmb250LXNpemU6MXB4O21hcmdpbjowcHggYXV0bzt3aWR0aDoxMDAlOyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9wPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48dGFibGUgYWxpZ249ImNlbnRlciIgYm9yZGVyPSIwIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIHN0eWxlPSJib3JkZXItdG9wOnNvbGlkIDJweCAjZGJkYmRiO2ZvbnQtc2l6ZToxcHg7bWFyZ2luOjBweCBhdXRvO3dpZHRoOjQ0MHB4OyIgcm9sZT0icHJlc2VudGF0aW9uIiB3aWR0aD0iNDQwcHgiID48dHI+PHRkIHN0eWxlPSJoZWlnaHQ6MDtsaW5lLWhlaWdodDowOyI+ICZuYnNwOwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+PC90cj48L3RhYmxlPjwhW2VuZGlmXS0tPgoKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRkCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ249ImNlbnRlciIgc3R5bGU9ImZvbnQtc2l6ZTowcHg7cGFkZGluZzoxNnB4O3dvcmQtYnJlYWs6YnJlYWstd29yZDsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGU9ImZvbnQtZmFtaWx5Ont7LkZvbnRGYW1pbHl9fTtmb250LXNpemU6MTNweDtsaW5lLWhlaWdodDoxO3RleHQtYWxpZ246Y2VudGVyO2NvbG9yOnt7LkZvbnRDb2xvcn19OyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+e3suRm9vdGVyVGV4dH19PC9kaXY+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RkPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7ZW5kfX0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90YWJsZT4KCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90Ym9keT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+CgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48L3RkPjwvdHI+PC90YWJsZT48IVtlbmRpZl0tLT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgPC90cj4KICAgICAgICAgICAgICAgICAgICAgICAgICA8L3Rib2R5PgogICAgICAgICAgICAgICAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PgoKCiAgICAgICAgICAgICAgICAgICAgICA8IS0tW2lmIG1zbyB8IElFXT48L3RkPjwvdHI+PC90YWJsZT48IVtlbmRpZl0tLT4KCgogICAgICAgICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICAgICAgICAgIDwhLS1baWYgbXNvIHwgSUVdPjwvdGQ+PC90cj48L3RhYmxlPjwhW2VuZGlmXS0tPgogICAgICAgICAgICAgIDwvdGQ+CiAgICAgICAgICAgIDwvdHI+CiAgICAgICAgICAgIDwvdGJvZHk+CiAgICAgICAgICA8L3RhYmxlPgoKICAgICAgICA8L2Rpdj4KCgogICAgICAgIDwhLS1baWYgbXNvIHwgSUVdPjwvdGQ+PC90cj48L3RhYmxlPjwhW2VuZGlmXS0tPgoKCiAgICAgIDwvdGQ+CiAgICA8L3RyPgogICAgPC90Ym9keT4KICA8L3RhYmxlPgoKPC9kaXY+Cgo8L2JvZHk+CjwvaHRtbD4K # ZITADEL_DEFAULTINSTANCE_EMAILTEMPLATE
# Sets the default values for lifetime and expiration for OIDC in each newly created instance
diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html
index 9a515d1890..6591d4613b 100644
--- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html
+++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.html
@@ -17,17 +17,49 @@
-
- {{ 'POLICY.DATA.MAXATTEMPTS' | translate }}
+ {{ 'POLICY.DATA.MAXPASSWORDATTEMPTS' | translate }}
+
+
+
+
+
+
+ remove
+
+ {{ lockoutData.maxOtpAttempts }}
+
+ add
+
+
+
+
+ {{ 'POLICY.DATA.MAXOTPATTEMPTS' | translate }}
diff --git a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts
index ad4ff29b4f..e56857770d 100644
--- a/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts
+++ b/console/src/app/modules/policies/password-lockout-policy/password-lockout-policy.component.ts
@@ -91,24 +91,36 @@ export class PasswordLockoutPolicyComponent implements OnInit {
}
}
- public incrementMaxAttempts(): void {
+ public incrementPasswordMaxAttempts(): void {
if (this.lockoutData?.maxPasswordAttempts !== undefined) {
this.lockoutData.maxPasswordAttempts++;
}
}
- public decrementMaxAttempts(): void {
+ public decrementPasswordMaxAttempts(): void {
if (this.lockoutData?.maxPasswordAttempts && this.lockoutData?.maxPasswordAttempts > 0) {
this.lockoutData.maxPasswordAttempts--;
}
}
+ public incrementOTPMaxAttempts(): void {
+ if (this.lockoutData?.maxOtpAttempts !== undefined) {
+ this.lockoutData.maxOtpAttempts++;
+ }
+ }
+
+ public decrementOTPMaxAttempts(): void {
+ if (this.lockoutData?.maxOtpAttempts && this.lockoutData?.maxOtpAttempts > 0) {
+ this.lockoutData.maxOtpAttempts--;
+ }
+ }
+
public savePolicy(): void {
let promise: Promise
;
if (this.lockoutData) {
if (this.service instanceof AdminService) {
promise = this.service
- .updateLockoutPolicy(this.lockoutData.maxPasswordAttempts)
+ .updateLockoutPolicy(this.lockoutData.maxPasswordAttempts, this.lockoutData.maxOtpAttempts)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
@@ -119,7 +131,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
} else {
if ((this.lockoutData as LockoutPolicy.AsObject).isDefault) {
promise = (this.service as ManagementService)
- .addCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts)
+ .addCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts, this.lockoutData.maxOtpAttempts)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
@@ -129,7 +141,7 @@ export class PasswordLockoutPolicyComponent implements OnInit {
});
} else {
promise = (this.service as ManagementService)
- .updateCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts)
+ .updateCustomLockoutPolicy(this.lockoutData.maxPasswordAttempts, this.lockoutData.maxOtpAttempts)
.then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
this.fetchData();
diff --git a/console/src/app/services/admin.service.ts b/console/src/app/services/admin.service.ts
index 17c8f1b323..7149317ab4 100644
--- a/console/src/app/services/admin.service.ts
+++ b/console/src/app/services/admin.service.ts
@@ -957,9 +957,13 @@ export class AdminService {
return this.grpcService.admin.getLockoutPolicy(req, null).then((resp) => resp.toObject());
}
- public updateLockoutPolicy(maxAttempts: number): Promise {
+ public updateLockoutPolicy(
+ maxPasswordAttempts: number,
+ maxOTPAttempts: number,
+ ): Promise {
const req = new UpdateLockoutPolicyRequest();
- req.setMaxPasswordAttempts(maxAttempts);
+ req.setMaxPasswordAttempts(maxPasswordAttempts);
+ req.setMaxOtpAttempts(maxOTPAttempts);
return this.grpcService.admin.updateLockoutPolicy(req, null).then((resp) => resp.toObject());
}
diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts
index ae975a228d..0e3c0b3062 100644
--- a/console/src/app/services/mgmt.service.ts
+++ b/console/src/app/services/mgmt.service.ts
@@ -1587,9 +1587,13 @@ export class ManagementService {
return this.grpcService.mgmt.getLockoutPolicy(req, null).then((resp) => resp.toObject());
}
- public addCustomLockoutPolicy(maxAttempts: number): Promise {
+ public addCustomLockoutPolicy(
+ maxPasswordAttempts: number,
+ maxOTPAttempts: number,
+ ): Promise {
const req = new AddCustomLockoutPolicyRequest();
- req.setMaxPasswordAttempts(maxAttempts);
+ req.setMaxPasswordAttempts(maxPasswordAttempts);
+ req.setMaxOtpAttempts(maxOTPAttempts);
return this.grpcService.mgmt.addCustomLockoutPolicy(req, null).then((resp) => resp.toObject());
}
@@ -1599,9 +1603,13 @@ export class ManagementService {
return this.grpcService.mgmt.resetLockoutPolicyToDefault(req, null).then((resp) => resp.toObject());
}
- public updateCustomLockoutPolicy(maxAttempts: number): Promise {
+ public updateCustomLockoutPolicy(
+ maxPasswordAttempts: number,
+ maxOTPAttempts: number,
+ ): Promise {
const req = new UpdateCustomLockoutPolicyRequest();
- req.setMaxPasswordAttempts(maxAttempts);
+ req.setMaxPasswordAttempts(maxPasswordAttempts);
+ req.setMaxOtpAttempts(maxOTPAttempts);
return this.grpcService.mgmt.updateCustomLockoutPolicy(req, null).then((resp) => resp.toObject());
}
diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json
index 2ddcec25c4..6add8de104 100644
--- a/console/src/assets/i18n/bg.json
+++ b/console/src/assets/i18n/bg.json
@@ -1653,7 +1653,8 @@
"HASLOWERCASE": "има малки букви",
"HASUPPERCASE": "има главни букви",
"SHOWLOCKOUTFAILURES": "показва грешки при блокиране",
- "MAXATTEMPTS": "Максимален брой опити за парола",
+ "MAXPASSWORDATTEMPTS": "Максимален брой опити за парола",
+ "MAXOTPATTEMPTS": "Максимален брой опити за OTP",
"EXPIREWARNDAYS": "Предупреждение за изтичане след ден",
"MAXAGEDAYS": "Максимална възраст в дни",
"USERLOGINMUSTBEDOMAIN": "Добавяне на домейн на организация като суфикс към имената за вход",
diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json
index c893c5d8c3..c2c875c78c 100644
--- a/console/src/assets/i18n/cs.json
+++ b/console/src/assets/i18n/cs.json
@@ -1660,7 +1660,8 @@
"HASLOWERCASE": "obsahuje malá písmena",
"HASUPPERCASE": "obsahuje velká písmena",
"SHOWLOCKOUTFAILURES": "zobrazit neúspěšné pokusy o uzamčení",
- "MAXATTEMPTS": "Maximální počet pokusů o heslo",
+ "MAXPASSWORDATTEMPTS": "Maximální počet pokusů o heslo",
+ "MAXOTPATTEMPTS": "Maximální počet pokusů o OTP",
"EXPIREWARNDAYS": "Upozornění na expiraci po dni",
"MAXAGEDAYS": "Maximální stáří v dnech",
"USERLOGINMUSTBEDOMAIN": "Přidat doménu organizace jako příponu k přihlašovacím jménům",
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json
index e0f403b07b..f4d1cf8e6c 100644
--- a/console/src/assets/i18n/de.json
+++ b/console/src/assets/i18n/de.json
@@ -1659,7 +1659,8 @@
"HASLOWERCASE": "erfordert Kleinbuchstaben",
"HASUPPERCASE": "erfordert Grossbuchstaben",
"SHOWLOCKOUTFAILURES": "Zeige Anzahl Anmeldeversuche",
- "MAXATTEMPTS": "Maximale Anzahl an Versuchen",
+ "MAXPASSWORDATTEMPTS": "Maximale Anzahl an Passwort Versuchen",
+ "MAXOTPATTEMPTS": "Maximale Anzahl an OTP Versuchen",
"EXPIREWARNDAYS": "Ablauf Warnung nach Tagen",
"MAXAGEDAYS": "Maximale Gültigkeit in Tagen",
"USERLOGINMUSTBEDOMAIN": "Organisationsdomain dem Loginname hinzufügen",
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json
index 72cee6c0f3..0427a4af5b 100644
--- a/console/src/assets/i18n/en.json
+++ b/console/src/assets/i18n/en.json
@@ -1660,7 +1660,8 @@
"HASLOWERCASE": "must include a lowercase letter",
"HASUPPERCASE": "must include an uppercase letter",
"SHOWLOCKOUTFAILURES": "show lockout failures",
- "MAXATTEMPTS": "Password maximum Attempts",
+ "MAXPASSWORDATTEMPTS": "Password maximum attempts",
+ "MAXOTPATTEMPTS": "OTP maximum attempts",
"EXPIREWARNDAYS": "Expiration Warning after day",
"MAXAGEDAYS": "Max Age in days",
"USERLOGINMUSTBEDOMAIN": "Add organization domain as suffix to loginnames",
diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json
index 819c078905..e5630cd0a8 100644
--- a/console/src/assets/i18n/es.json
+++ b/console/src/assets/i18n/es.json
@@ -1661,7 +1661,8 @@
"HASLOWERCASE": "tiene minúsculas",
"HASUPPERCASE": "tiene mayúsculas",
"SHOWLOCKOUTFAILURES": "mostrar fallos de bloqueo",
- "MAXATTEMPTS": "Intentos máximos",
+ "MAXPASSWORDATTEMPTS": "Intentos máximos de contraseña",
+ "MAXOTPATTEMPTS": "Intentos máximos de OTP",
"EXPIREWARNDAYS": "Aviso de expiración después de estos días: ",
"MAXAGEDAYS": "Antigüedad máxima en días",
"USERLOGINMUSTBEDOMAIN": "Añadir el dominio de la organización como sufijo de los nombres de inicio de sesión",
diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json
index b88bed51b4..929dce791a 100644
--- a/console/src/assets/i18n/fr.json
+++ b/console/src/assets/i18n/fr.json
@@ -1659,7 +1659,8 @@
"HASLOWERCASE": "a minuscule",
"HASUPPERCASE": "a majuscule",
"SHOWLOCKOUTFAILURES": "montrer les échecs de verrouillage",
- "MAXATTEMPTS": "Mot de passe maximum Tentatives",
+ "MAXPASSWORDATTEMPTS": "Mot de passe maximum tentatives",
+ "MAXOTPATTEMPTS": "Maximal de tentatives OTP",
"EXPIREWARNDAYS": "Expiration Avertissement après le jour",
"MAXAGEDAYS": "Âge maximum en jours",
"USERLOGINMUSTBEDOMAIN": "Le nom de connexion de l'utilisateur doit contenir le nom de domaine de l'organisation",
diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json
index 5d1cc463ea..747e857157 100644
--- a/console/src/assets/i18n/it.json
+++ b/console/src/assets/i18n/it.json
@@ -1659,7 +1659,8 @@
"HASLOWERCASE": "ha la minuscola",
"HASUPPERCASE": "ha la maiuscola",
"SHOWLOCKOUTFAILURES": "mostra i fallimenti del blocco",
- "MAXATTEMPTS": "Massimo numero di tentativi di password",
+ "MAXPASSWORDATTEMPTS": "Massimo numero di tentativi di password",
+ "MAXOTPATTEMPTS": "Massimo numero di tentativi di OTP",
"EXPIREWARNDAYS": "Avviso scadenza dopo il giorno",
"MAXAGEDAYS": "Lunghezza massima in giorni",
"USERLOGINMUSTBEDOMAIN": "Nome utente deve contenere il dominio dell' organizzazione",
diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json
index 6036a62fe6..dd8130b6dd 100644
--- a/console/src/assets/i18n/ja.json
+++ b/console/src/assets/i18n/ja.json
@@ -1656,7 +1656,8 @@
"HASLOWERCASE": "小文字を含める",
"HASUPPERCASE": "大文字を含める",
"SHOWLOCKOUTFAILURES": "ロックアウトの失敗を表示する",
- "MAXATTEMPTS": "パスワードの最大試行",
+ "MAXPASSWORDATTEMPTS": "パスワードの最大試行",
+ "MAXOTPATTEMPTS": "最大OTP試行回数",
"EXPIREWARNDAYS": "有効期限の翌日以降の警告",
"MAXAGEDAYS": "最大有効期限",
"USERLOGINMUSTBEDOMAIN": "ログイン名の接尾辞として組織ドメインを追加する",
diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json
index c7e68f3dad..f12c16f09b 100644
--- a/console/src/assets/i18n/mk.json
+++ b/console/src/assets/i18n/mk.json
@@ -1661,7 +1661,8 @@
"HASLOWERCASE": "има мали букви",
"HASUPPERCASE": "има големи букви",
"SHOWLOCKOUTFAILURES": "прикажи неуспешни заклучувања",
- "MAXATTEMPTS": "Максимален број на обиди за лозинка",
+ "MAXPASSWORDATTEMPTS": "Максимален број на обиди за лозинка",
+ "MAXOTPATTEMPTS": "Максимални обиди за OTP",
"EXPIREWARNDAYS": "Предупредување за истекување по ден",
"MAXAGEDAYS": "Максимална возраст во денови",
"USERLOGINMUSTBEDOMAIN": "Додади организациски домен како суфикс на корисничките имиња",
diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json
index 1bfe7214b2..1da10f8e57 100644
--- a/console/src/assets/i18n/nl.json
+++ b/console/src/assets/i18n/nl.json
@@ -1660,7 +1660,8 @@
"HASLOWERCASE": "heeft kleine letters",
"HASUPPERCASE": "heeft hoofdletters",
"SHOWLOCKOUTFAILURES": "toon lockout mislukkingen",
- "MAXATTEMPTS": "Maximum pogingen voor wachtwoord",
+ "MAXPASSWORDATTEMPTS": "Maximum pogingen voor wachtwoord",
+ "MAXOTPATTEMPTS": "Maximale OTP-pogingen",
"EXPIREWARNDAYS": "Vervaldatum Waarschuwing na dag",
"MAXAGEDAYS": "Maximale Leeftijd in dagen",
"USERLOGINMUSTBEDOMAIN": "Voeg organisatie domein toe als achtervoegsel aan inlognamen",
diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json
index e1b632cd3e..ef572b7beb 100644
--- a/console/src/assets/i18n/pl.json
+++ b/console/src/assets/i18n/pl.json
@@ -1659,7 +1659,8 @@
"HASLOWERCASE": "zawiera małe litery",
"HASUPPERCASE": "zawiera duże litery",
"SHOWLOCKOUTFAILURES": "pokaż blokady nieudanych prób",
- "MAXATTEMPTS": "Maksymalna liczba prób wprowadzenia hasła",
+ "MAXPASSWORDATTEMPTS": "Maksymalna liczba prób wprowadzenia hasła",
+ "MAXOTPATTEMPTS": "Maksymalna liczba prób OTP",
"EXPIREWARNDAYS": "Ostrzeżenie o wygaśnięciu po dniu",
"MAXAGEDAYS": "Maksymalny wiek w dniach",
"USERLOGINMUSTBEDOMAIN": "Dodaj domenę organizacji jako przyrostek do nazw logowania",
diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json
index 4cbcfe4e16..9fdb634ab6 100644
--- a/console/src/assets/i18n/pt.json
+++ b/console/src/assets/i18n/pt.json
@@ -1661,7 +1661,8 @@
"HASLOWERCASE": "tem letra minúscula",
"HASUPPERCASE": "tem letra maiúscula",
"SHOWLOCKOUTFAILURES": "mostrar falhas de bloqueio",
- "MAXATTEMPTS": "Máximo de tentativas de senha",
+ "MAXPASSWORDATTEMPTS": "Máximo de tentativas de senha",
+ "MAXOTPATTEMPTS": "Máximo de tentativas de OTP",
"EXPIREWARNDAYS": "Aviso de expiração após dias",
"MAXAGEDAYS": "Idade máxima em dias",
"USERLOGINMUSTBEDOMAIN": "Adicionar domínio da organização como sufixo aos nomes de login",
diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json
index 2f2c63e0e5..ff38f488d4 100644
--- a/console/src/assets/i18n/ru.json
+++ b/console/src/assets/i18n/ru.json
@@ -1729,7 +1729,8 @@
"HASLOWERCASE": "Содержит нижний регистр",
"HASUPPERCASE": "Содержит верхний регистр",
"SHOWLOCKOUTFAILURES": "Показать ошибки блокировки",
- "MAXATTEMPTS": "Максимальное количество попыток пароля",
+ "MAXPASSWORDATTEMPTS": "Максимальное количество попыток пароля",
+ "MAXOTPATTEMPTS": "Максимальное количество попыток OTP",
"EXPIREWARNDAYS": "Предупреждение об истечении срока действия после дня",
"MAXAGEDAYS": "Максимальный возраст в днях",
"USERLOGINMUSTBEDOMAIN": "Добавить домен организации в качестве суффикса к именам логина",
diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json
index d4a8c7ead4..57d5ba2490 100644
--- a/console/src/assets/i18n/zh.json
+++ b/console/src/assets/i18n/zh.json
@@ -1658,7 +1658,8 @@
"HASLOWERCASE": "包含小写字母",
"HASUPPERCASE": "包含大写字母",
"SHOWLOCKOUTFAILURES": "显示锁定失败",
- "MAXATTEMPTS": "密码最大尝试次数",
+ "MAXPASSWORDATTEMPTS": "密码最大尝试次数",
+ "MAXOTPATTEMPTS": "最多尝试 OTP 次数",
"EXPIREWARNDAYS": "密码过期警告",
"MAXAGEDAYS": "Max Age in days",
"USERLOGINMUSTBEDOMAIN": "用户名必须包含组织域名",
diff --git a/console/yarn.lock b/console/yarn.lock
index 7b6584a842..de6a28e268 100644
--- a/console/yarn.lock
+++ b/console/yarn.lock
@@ -2103,18 +2103,6 @@
protobufjs "^7.2.4"
yargs "^17.7.2"
-"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
- version "9.3.0"
- resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
- integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
-
-"@hapi/topo@^5.1.0":
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
- integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
- dependencies:
- "@hapi/hoek" "^9.0.0"
-
"@humanwhocodes/config-array@^0.11.11":
version "0.11.11"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844"
@@ -3193,23 +3181,6 @@
"@angular-devkit/schematics" "16.2.2"
jsonc-parser "3.2.0"
-"@sideway/address@^4.1.5":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
- integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
- dependencies:
- "@hapi/hoek" "^9.0.0"
-
-"@sideway/formula@^3.0.1":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
- integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
-
-"@sideway/pinpoint@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
- integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
-
"@sigstore/protobuf-specs@^0.1.0":
version "0.1.0"
resolved "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz"
@@ -4246,30 +4217,11 @@ big.js@^5.2.2:
resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-bin-build@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/bin-build/-/bin-build-3.0.0.tgz#c5780a25a8a9f966d8244217e6c1f5082a143861"
- integrity sha512-jcUOof71/TNAI2uM5uoUaDq2ePcVBQ3R/qhxAz1rX7UfvduAL/RXD3jXzvn8cVcDJdGVkiR1shal3OH0ImpuhA==
- dependencies:
- decompress "^4.0.0"
- download "^6.2.2"
- execa "^0.7.0"
- p-map-series "^1.0.0"
- tempfile "^2.0.0"
-
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-bl@^1.0.0:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7"
- integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==
- dependencies:
- readable-stream "^2.3.5"
- safe-buffer "^5.1.1"
-
bl@^4.0.3, bl@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz"
@@ -4391,35 +4343,12 @@ browserstack@^1.5.1:
dependencies:
https-proxy-agent "^2.2.1"
-buffer-alloc-unsafe@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
- integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
-
-buffer-alloc@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
- integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
- dependencies:
- buffer-alloc-unsafe "^1.1.0"
- buffer-fill "^1.0.0"
-
-buffer-crc32@~0.2.3:
- version "0.2.13"
- resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
- integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
-
-buffer-fill@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
- integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
-
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-buffer@^5.2.1, buffer@^5.5.0:
+buffer@^5.5.0:
version "5.7.1"
resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
@@ -4527,16 +4456,6 @@ caseless@~0.12.0:
resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz"
integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
-caw@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95"
- integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==
- dependencies:
- get-proxy "^2.0.0"
- isurl "^1.0.0-alpha5"
- tunnel-agent "^0.6.0"
- url-to-options "^1.0.1"
-
chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz"
@@ -4741,7 +4660,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.11.0, commander@^2.20.0, commander@^2.8.1:
+commander@^2.11.0, commander@^2.20.0:
version "2.20.3"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -4776,14 +4695,6 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-config-chain@^1.1.11:
- version "1.1.13"
- resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
- integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
- dependencies:
- ini "^1.3.4"
- proto-list "~1.2.1"
-
connect-history-api-fallback@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz"
@@ -4804,7 +4715,7 @@ console-control-strings@^1.1.0:
resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
-content-disposition@0.5.4, content-disposition@^0.5.2:
+content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
@@ -4903,15 +4814,6 @@ critters@0.0.20:
postcss "^8.4.23"
pretty-bytes "^5.3.0"
-cross-spawn@^5.0.1:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
- integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==
- dependencies:
- lru-cache "^4.0.1"
- shebang-command "^1.2.0"
- which "^1.2.9"
-
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@@ -5050,66 +4952,6 @@ decimal.js@^10.2.1:
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
-decompress-response@^3.2.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
- integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==
- dependencies:
- mimic-response "^1.0.0"
-
-decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
- integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==
- dependencies:
- file-type "^5.2.0"
- is-stream "^1.1.0"
- tar-stream "^1.5.2"
-
-decompress-tarbz2@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b"
- integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==
- dependencies:
- decompress-tar "^4.1.0"
- file-type "^6.1.0"
- is-stream "^1.1.0"
- seek-bzip "^1.0.5"
- unbzip2-stream "^1.0.9"
-
-decompress-targz@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee"
- integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==
- dependencies:
- decompress-tar "^4.1.1"
- file-type "^5.2.0"
- is-stream "^1.1.0"
-
-decompress-unzip@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69"
- integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==
- dependencies:
- file-type "^3.8.0"
- get-stream "^2.2.0"
- pify "^2.3.0"
- yauzl "^2.4.2"
-
-decompress@^4.0.0:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118"
- integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==
- dependencies:
- decompress-tar "^4.0.0"
- decompress-tarbz2 "^4.0.0"
- decompress-targz "^4.0.0"
- decompress-unzip "^4.0.1"
- graceful-fs "^4.1.10"
- make-dir "^1.0.0"
- pify "^2.3.0"
- strip-dirs "^2.0.0"
-
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
@@ -5275,28 +5117,6 @@ dotenv@~10.0.0:
resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
-download@^6.2.2:
- version "6.2.5"
- resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714"
- integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA==
- dependencies:
- caw "^2.0.0"
- content-disposition "^0.5.2"
- decompress "^4.0.0"
- ext-name "^5.0.0"
- file-type "5.2.0"
- filenamify "^2.0.0"
- get-stream "^3.0.0"
- got "^7.0.0"
- make-dir "^1.0.0"
- p-event "^1.0.0"
- pify "^3.0.0"
-
-duplexer3@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e"
- integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==
-
duplexer@^0.1.1:
version "0.1.2"
resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
@@ -5369,7 +5189,7 @@ encoding@^0.1.13:
dependencies:
iconv-lite "^0.6.2"
-end-of-stream@^1.0.0, end-of-stream@^1.4.1:
+end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@@ -5687,19 +5507,6 @@ events@^3.2.0:
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
-execa@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
- integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==
- dependencies:
- cross-spawn "^5.0.1"
- get-stream "^3.0.0"
- is-stream "^1.1.0"
- npm-run-path "^2.0.0"
- p-finally "^1.0.0"
- signal-exit "^3.0.0"
- strip-eof "^1.0.0"
-
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz"
@@ -5757,21 +5564,6 @@ express@^4.17.3:
utils-merge "1.0.1"
vary "~1.1.2"
-ext-list@^2.0.0:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
- integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==
- dependencies:
- mime-db "^1.28.0"
-
-ext-name@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6"
- integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==
- dependencies:
- ext-list "^2.0.0"
- sort-keys-length "^1.0.0"
-
extend@^3.0.0, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
@@ -5863,13 +5655,6 @@ faye-websocket@^0.11.3:
dependencies:
websocket-driver ">=0.5.1"
-fd-slicer@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
- integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
- dependencies:
- pend "~1.2.0"
-
figures@3.2.0, figures@^3.0.0:
version "3.2.0"
resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz"
@@ -5889,21 +5674,6 @@ file-saver@^2.0.5:
resolved "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
-file-type@5.2.0, file-type@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6"
- integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==
-
-file-type@^3.8.0:
- version "3.9.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9"
- integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==
-
-file-type@^6.1.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919"
- integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==
-
filelist@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz"
@@ -5911,20 +5681,6 @@ filelist@^1.0.4:
dependencies:
minimatch "^5.0.1"
-filename-reserved-regex@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"
- integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==
-
-filenamify@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9"
- integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==
- dependencies:
- filename-reserved-regex "^2.0.0"
- strip-outer "^1.0.0"
- trim-repeated "^1.0.0"
-
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
@@ -6179,26 +5935,6 @@ get-package-type@^0.1.0:
resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz"
integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
-get-proxy@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93"
- integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==
- dependencies:
- npm-conf "^1.1.0"
-
-get-stream@^2.2.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de"
- integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==
- dependencies:
- object-assign "^4.0.1"
- pinkie-promise "^2.0.0"
-
-get-stream@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
- integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==
-
get-stream@^6.0.0:
version "6.0.1"
resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
@@ -6336,27 +6072,7 @@ google-protobuf@^3.21.2:
resolved "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz"
integrity sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==
-got@^7.0.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
- integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==
- dependencies:
- decompress-response "^3.2.0"
- duplexer3 "^0.1.4"
- get-stream "^3.0.0"
- is-plain-obj "^1.1.0"
- is-retry-allowed "^1.0.0"
- is-stream "^1.0.0"
- isurl "^1.0.0-alpha5"
- lowercase-keys "^1.0.0"
- p-cancelable "^0.3.0"
- p-timeout "^1.1.1"
- safe-buffer "^5.0.1"
- timed-out "^4.0.0"
- url-parse-lax "^1.0.0"
- url-to-options "^1.0.1"
-
-graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -6418,23 +6134,11 @@ has-proto@^1.0.1:
resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz"
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
-has-symbol-support-x@^1.4.1:
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455"
- integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==
-
has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
-has-to-string-tag-x@^1.2.0:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
- integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==
- dependencies:
- has-symbol-support-x "^1.4.1"
-
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
@@ -6811,11 +6515,6 @@ is-docker@^2.0.0, is-docker@^2.1.1:
resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz"
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
-is-extglob@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
- integrity sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==
-
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
@@ -6826,13 +6525,6 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-is-glob@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
- integrity sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==
- dependencies:
- is-extglob "^1.0.0"
-
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
@@ -6845,33 +6537,16 @@ is-interactive@^1.0.0:
resolved "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz"
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
-is-invalid-path@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/is-invalid-path/-/is-invalid-path-0.1.0.tgz#307a855b3cf1a938b44ea70d2c61106053714f34"
- integrity sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==
- dependencies:
- is-glob "^2.0.0"
-
is-lambda@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz"
integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==
-is-natural-number@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8"
- integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==
-
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-is-object@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf"
- integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==
-
is-path-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz"
@@ -6896,11 +6571,6 @@ is-path-inside@^3.0.3:
resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
-is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
- integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
-
is-plain-obj@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz"
@@ -6923,16 +6593,6 @@ is-potential-custom-element-name@^1.0.1:
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
-is-retry-allowed@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
- integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
-
-is-stream@^1.0.0, is-stream@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
- integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
-
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
@@ -6948,13 +6608,6 @@ is-unicode-supported@^0.1.0:
resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-is-valid-path@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-valid-path/-/is-valid-path-0.1.1.tgz#110f9ff74c37f663e1ec7915eb451f2db93ac9df"
- integrity sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==
- dependencies:
- is-invalid-path "^0.1.0"
-
is-what@^3.14.1:
version "3.14.1"
resolved "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz"
@@ -7041,14 +6694,6 @@ istanbul-reports@^3.0.2:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
-isurl@^1.0.0-alpha5:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
- integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==
- dependencies:
- has-to-string-tag-x "^1.2.0"
- is-object "^1.0.1"
-
jackspeak@^2.0.3:
version "2.2.0"
resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.0.tgz"
@@ -7113,17 +6758,6 @@ jiti@^1.18.2:
resolved "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz"
integrity sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==
-joi@^17.4.0:
- version "17.12.2"
- resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.2.tgz#283a664dabb80c7e52943c557aab82faea09f521"
- integrity sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==
- dependencies:
- "@hapi/hoek" "^9.3.0"
- "@hapi/topo" "^5.1.0"
- "@sideway/address" "^4.1.5"
- "@sideway/formula" "^3.0.1"
- "@sideway/pinpoint" "^2.0.0"
-
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
@@ -7508,19 +7142,6 @@ long@^5.0.0:
resolved "https://registry.npmjs.org/long/-/long-5.2.3.tgz"
integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==
-lowercase-keys@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
- integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
-
-lru-cache@^4.0.1:
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
- integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
- dependencies:
- pseudomap "^1.0.2"
- yallist "^2.1.2"
-
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz"
@@ -7552,13 +7173,6 @@ magic-string@0.30.1:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
-make-dir@^1.0.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c"
- integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==
- dependencies:
- pify "^3.0.0"
-
make-dir@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz"
@@ -7667,7 +7281,7 @@ micromatch@^4.0.2, micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
-mime-db@1.52.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0:
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
@@ -7694,11 +7308,6 @@ mimic-fn@^2.1.0:
resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-mimic-response@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
- integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
-
mini-css-extract-plugin@2.7.6:
version "2.7.6"
resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz"
@@ -7933,11 +7542,6 @@ node-addon-api@^3.0.0, node-addon-api@^3.2.1:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
-node-downloader-helper@^2.1.6:
- version "2.1.9"
- resolved "https://registry.yarnpkg.com/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz#a59ee7276b2bf708bbac2cc5872ad28fc7cd1b0e"
- integrity sha512-FSvAol2Z8UP191sZtsUZwHIN0eGoGue3uEXGdWIH5228e9KH1YHXT7fN8Oa33UGf+FbqGTQg3sJfrRGzmVCaJA==
-
node-forge@^1:
version "1.3.1"
resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz"
@@ -7964,18 +7568,6 @@ node-gyp@^9.0.0:
tar "^6.1.2"
which "^2.0.2"
-node-jq@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/node-jq/-/node-jq-4.3.1.tgz#c2a082210745fd1c54df5965b9ba5d046fce9d68"
- integrity sha512-5iU9L/7j8ZNHwhxDRJXgyza6JnEKqdkNcJ9+ul5HZnhConhg/v9JdvA9agJ8XA+qBgGr1MK/MeHDrdK1tL2QAA==
- dependencies:
- bin-build "^3.0.0"
- is-valid-path "^0.1.1"
- joi "^17.4.0"
- node-downloader-helper "^2.1.6"
- strip-final-newline "^2.0.0"
- tempfile "^3.0.0"
-
node-releases@^2.0.12:
version "2.0.13"
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz"
@@ -8030,14 +7622,6 @@ npm-bundled@^3.0.0:
dependencies:
npm-normalize-package-bin "^3.0.0"
-npm-conf@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
- integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
- dependencies:
- config-chain "^1.1.11"
- pify "^3.0.0"
-
npm-install-checks@^6.0.0:
version "6.1.1"
resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz"
@@ -8090,13 +7674,6 @@ npm-registry-fetch@^14.0.0:
npm-package-arg "^10.0.0"
proc-log "^3.0.0"
-npm-run-path@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
- integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==
- dependencies:
- path-key "^2.0.0"
-
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz"
@@ -8284,18 +7861,6 @@ os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz"
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
-p-cancelable@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
- integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==
-
-p-event@^1.0.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085"
- integrity sha512-hV1zbA7gwqPVFcapfeATaNjQ3J0NuzorHPyG8GPL9g/Y/TplWVBVoCKCXL6Ej2zscrCEv195QNWJXuBH6XZuzA==
- dependencies:
- p-timeout "^1.1.1"
-
p-filter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-3.0.0.tgz#ce50e03b24b23930e11679ab8694bd09a2d7ed35"
@@ -8303,11 +7868,6 @@ p-filter@^3.0.0:
dependencies:
p-map "^5.1.0"
-p-finally@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
- integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
-
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
@@ -8350,13 +7910,6 @@ p-locate@^6.0.0:
dependencies:
p-limit "^4.0.0"
-p-map-series@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca"
- integrity sha512-4k9LlvY6Bo/1FcIdV33wqZQES0Py+iKISU9Uc8p8AjWoZPnFKMpVIVD3s0EYn4jzLh1I+WeUZkJ0Yoa4Qfw3Kg==
- dependencies:
- p-reduce "^1.0.0"
-
p-map@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz"
@@ -8371,11 +7924,6 @@ p-map@^5.1.0:
dependencies:
aggregate-error "^4.0.0"
-p-reduce@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
- integrity sha512-3Tx1T3oM1xO/Y8Gj0sWyE78EIJZ+t+aEmXUdvQgvGmSMri7aPTHoovbXEreWKkL5j21Er60XAWLTzKbAKYOujQ==
-
p-retry@^4.5.0:
version "4.6.2"
resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz"
@@ -8384,13 +7932,6 @@ p-retry@^4.5.0:
"@types/retry" "0.12.0"
retry "^0.13.1"
-p-timeout@^1.1.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386"
- integrity sha512-gb0ryzr+K2qFqFv6qi3khoeqMZF/+ajxQipEF6NteZVnvz9tzdsfAVj3lYtn1gAXvH5lfLwfxEII799gt/mRIA==
- dependencies:
- p-finally "^1.0.0"
-
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
@@ -8500,11 +8041,6 @@ path-is-inside@^1.0.1:
resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz"
integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==
-path-key@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
- integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
-
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
@@ -8533,11 +8069,6 @@ path-type@^4.0.0:
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-pend@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
- integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
-
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
@@ -8553,16 +8084,11 @@ picomatch@2.3.1, picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-pify@^2.0.0, pify@^2.3.0:
+pify@^2.0.0:
version "2.3.0"
resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
-pify@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
- integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==
-
pify@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz"
@@ -8685,11 +8211,6 @@ prelude-ls@^1.2.1:
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-prepend-http@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
- integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==
-
prettier-plugin-organize-imports@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz#77967f69d335e9c8e6e5d224074609309c62845e"
@@ -8733,11 +8254,6 @@ promise-retry@^2.0.1:
err-code "^2.0.2"
retry "^0.12.0"
-proto-list@~1.2.1:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
- integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==
-
protobufjs@^7.0.0, protobufjs@^7.2.4:
version "7.2.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.5.tgz#45d5c57387a6d29a17aab6846dcc283f9b8e7f2d"
@@ -8795,11 +8311,6 @@ prr@~1.0.1:
resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz"
integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==
-pseudomap@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
- integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==
-
psl@^1.1.28, psl@^1.1.33:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
@@ -8926,7 +8437,7 @@ read-pkg@^7.1.0:
parse-json "^5.2.0"
type-fest "^2.0.0"
-readable-stream@^2.0.1, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@~2.3.6:
+readable-stream@^2.0.1, readable-stream@~2.3.6:
version "2.3.8"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz"
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
@@ -9164,7 +8675,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -9242,13 +8753,6 @@ schema-utils@^4.0.0:
ajv-formats "^2.1.1"
ajv-keywords "^5.1.0"
-seek-bzip@^1.0.5:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
- integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==
- dependencies:
- commander "^2.8.1"
-
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz"
@@ -9385,13 +8889,6 @@ shallow-clone@^3.0.0:
dependencies:
kind-of "^6.0.2"
-shebang-command@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
- integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==
- dependencies:
- shebang-regex "^1.0.0"
-
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz"
@@ -9399,11 +8896,6 @@ shebang-command@^2.0.0:
dependencies:
shebang-regex "^3.0.0"
-shebang-regex@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
- integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==
-
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz"
@@ -9423,7 +8915,7 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
+signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
version "3.0.7"
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
@@ -9510,20 +9002,6 @@ socks@^2.6.2:
ip "^2.0.0"
smart-buffer "^4.2.0"
-sort-keys-length@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
- integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==
- dependencies:
- sort-keys "^1.0.0"
-
-sort-keys@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
- integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==
- dependencies:
- is-plain-obj "^1.0.0"
-
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
@@ -9738,18 +9216,6 @@ strip-bom@^3.0.0:
resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
-strip-dirs@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5"
- integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==
- dependencies:
- is-natural-number "^4.0.1"
-
-strip-eof@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
- integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==
-
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
@@ -9760,13 +9226,6 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.1:
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-strip-outer@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631"
- integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==
- dependencies:
- escape-string-regexp "^1.0.2"
-
strong-log-transformer@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz"
@@ -9822,19 +9281,6 @@ tapable@^2.1.1, tapable@^2.2.0:
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
-tar-stream@^1.5.2:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555"
- integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==
- dependencies:
- bl "^1.0.0"
- buffer-alloc "^1.2.0"
- end-of-stream "^1.0.0"
- fs-constants "^1.0.0"
- readable-stream "^2.3.0"
- to-buffer "^1.1.1"
- xtend "^4.0.0"
-
tar-stream@~2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz"
@@ -9858,32 +9304,6 @@ tar@^6.1.11, tar@^6.1.2:
mkdirp "^1.0.3"
yallist "^4.0.0"
-temp-dir@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
- integrity sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==
-
-temp-dir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
- integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
-
-tempfile@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265"
- integrity sha512-ZOn6nJUgvgC09+doCEF3oB+r3ag7kUvlsXEGX069QRD60p+P3uP7XG9N2/at+EyIRGSN//ZY3LyEotA1YpmjuA==
- dependencies:
- temp-dir "^1.0.0"
- uuid "^3.0.1"
-
-tempfile@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-3.0.0.tgz#5376a3492de7c54150d0cc0612c3f00e2cdaf76c"
- integrity sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==
- dependencies:
- temp-dir "^2.0.0"
- uuid "^3.3.2"
-
terser-webpack-plugin@^5.3.7:
version "5.3.8"
resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.8.tgz"
@@ -9929,7 +9349,7 @@ text-table@0.2.0, text-table@^0.2.0:
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
-through@X.X.X, through@^2.3.4, through@^2.3.6, through@^2.3.8:
+through@X.X.X, through@^2.3.4, through@^2.3.6:
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@@ -9939,11 +9359,6 @@ thunky@^1.0.2:
resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
-timed-out@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
- integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
-
tiny-inflate@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz"
@@ -9975,11 +9390,6 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
-to-buffer@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80"
- integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==
-
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
@@ -10027,13 +9437,6 @@ tree-kill@1.2.2:
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
-trim-repeated@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21"
- integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==
- dependencies:
- escape-string-regexp "^1.0.2"
-
tsconfig-paths@^4.1.2:
version "4.2.0"
resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz"
@@ -10131,14 +9534,6 @@ ua-parser-js@^0.7.30:
resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz"
integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==
-unbzip2-stream@^1.0.9:
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
- integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
- dependencies:
- buffer "^5.2.1"
- through "^2.3.8"
-
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz"
@@ -10225,13 +9620,6 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-url-parse-lax@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
- integrity sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==
- dependencies:
- prepend-http "^1.0.1"
-
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
@@ -10240,11 +9628,6 @@ url-parse@^1.5.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
-url-to-options@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
- integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==
-
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@@ -10255,7 +9638,7 @@ utils-merge@1.0.1:
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
-uuid@^3.0.1, uuid@^3.3.2:
+uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
@@ -10544,7 +9927,7 @@ which-module@^2.0.0:
resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz"
integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
-which@^1.2.1, which@^1.2.9:
+which@^1.2.1:
version "1.3.1"
resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -10647,11 +10030,6 @@ xmlchars@^2.2.0:
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
-xtend@^4.0.0:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
- integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
-
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz"
@@ -10662,11 +10040,6 @@ y18n@^5.0.5:
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-yallist@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
- integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==
-
yallist@^3.0.2:
version "3.1.1"
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
@@ -10738,14 +10111,6 @@ yargs@^16.1.1:
y18n "^5.0.5"
yargs-parser "^20.2.2"
-yauzl@^2.4.2:
- version "2.10.0"
- resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
- integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
- dependencies:
- buffer-crc32 "~0.2.3"
- fd-slicer "~1.1.0"
-
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
diff --git a/docs/docs/guides/manage/console/default-settings.mdx b/docs/docs/guides/manage/console/default-settings.mdx
index c29bb8d53d..a715f8a4db 100644
--- a/docs/docs/guides/manage/console/default-settings.mdx
+++ b/docs/docs/guides/manage/console/default-settings.mdx
@@ -20,7 +20,7 @@ When you configure your default settings, you can set the following:
- [**Login Behavior and Access**](#login-behavior-and-access): Multifactor Authentication Options and Enforcement, Define whether Passwordless authentication methods are allowed or not, Set Login Lifetimes and advanced behavour for the login interface.
- [**Identity Providers**](#identity-providers): Define IDPs which are available for all organizations
- [**Password Complexity**](#password-complexity): Requirements for Passwords ex. Symbols, Numbers, min length and more.
-- [**Lockout**](#lockout): Set the maximum attempts a user can try to enter the password. When the number is exceeded, the user gets locked out and has to be unlocked.
+- [**Lockout**](#lockout): Set the maximum attempts a user can try to enter the password or any (T)OTP method. When the number is exceeded, the user gets locked out and has to be unlocked.
- [**Domain settings**](#domain-settings): Whether users use their email or the generated username to login. Other Validation, SMTP settings
- [**Branding**](#branding): Appearance of the login interface.
- [**Message Texts**](#message-texts): Text and internationalization for emails
@@ -189,6 +189,7 @@ Define when an account should be locked.
The following settings are available:
- Maximum Password Attempts: When the user has reached the maximum password attempts the account will be locked, If this is set to 0 the lockout will not trigger.
+- Maximum OTP Attempts: When the user has reached the maximum (T)OTP attempts the account will be locked, If this is set to 0 the lockout will not trigger.
If an account is locked, the administrator has to unlock it in the ZITADEL console
diff --git a/docs/docs/guides/manage/console/organizations.mdx b/docs/docs/guides/manage/console/organizations.mdx
index 99092c304e..6763a09670 100644
--- a/docs/docs/guides/manage/console/organizations.mdx
+++ b/docs/docs/guides/manage/console/organizations.mdx
@@ -108,7 +108,7 @@ Those settings are the same as on your instance.
- [**Login Behavior and Access**](./default-settings#login-behaviour-and-access): Multifactor Authentication Options and Enforcement, Define whether Passwordless authentication methods are allowed or not, Set Login Lifetimes and advanced behavour for the login interface.
- [**Identity Providers**](./default-settings#identity-providers): Define IDPs which are available for all organizations
- [**Password Complexity**](./default-settings#password-complexity): Requirements for Passwords ex. Symbols, Numbers, min length and more.
-- [**Lockout**](./default-settings#lockout): Set the maximum attempts a user can try to enter the password. When the number is exceeded, the user gets locked out and has to be unlocked.
+- [**Lockout**](./default-settings#lockout): Set the maximum attempts a user can try to enter the password or any (T)OTP method. When the number is exceeded, the user gets locked out and has to be unlocked.
- [**Verified domains**](/docs/guides/manage/console/organizations#verify-your-domain-name): This is where you manage your organization specific domains which can be used to build usernames
- [**Domain settings**](./default-settings#domain-settings): Whether users use their email or the generated username to login. Other Validation, SMTP settings
- [**Branding**](./default-settings#branding): Appearance of the login interface.
diff --git a/docs/static/img/guides/console/lockout.png b/docs/static/img/guides/console/lockout.png
index 8f6c170d3b..8b87718fc9 100644
Binary files a/docs/static/img/guides/console/lockout.png and b/docs/static/img/guides/console/lockout.png differ
diff --git a/internal/api/grpc/admin/export.go b/internal/api/grpc/admin/export.go
index e47d1f04f7..679ae0271c 100644
--- a/internal/api/grpc/admin/export.go
+++ b/internal/api/grpc/admin/export.go
@@ -491,13 +491,14 @@ func (s *Server) getLockoutPolicy(ctx context.Context, orgID string) (_ *managem
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
- queriedLockout, err := s.query.LockoutPolicyByOrg(ctx, false, orgID, false)
+ queriedLockout, err := s.query.LockoutPolicyByOrg(ctx, false, orgID)
if err != nil {
return nil, err
}
if !queriedLockout.IsDefault {
return &management_pb.AddCustomLockoutPolicyRequest{
MaxPasswordAttempts: uint32(queriedLockout.MaxPasswordAttempts),
+ MaxOtpAttempts: uint32(queriedLockout.MaxOTPAttempts),
}, nil
}
return nil, nil
diff --git a/internal/api/grpc/admin/lockout_converter.go b/internal/api/grpc/admin/lockout_converter.go
index 555e0c2e89..eabd5104e1 100644
--- a/internal/api/grpc/admin/lockout_converter.go
+++ b/internal/api/grpc/admin/lockout_converter.go
@@ -8,5 +8,6 @@ import (
func UpdateLockoutPolicyToDomain(p *admin.UpdateLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
+ MaxOTPAttempts: uint64(p.MaxOtpAttempts),
}
}
diff --git a/internal/api/grpc/management/policy_lockout.go b/internal/api/grpc/management/policy_lockout.go
index a36ee862d8..740a34e3a2 100644
--- a/internal/api/grpc/management/policy_lockout.go
+++ b/internal/api/grpc/management/policy_lockout.go
@@ -10,7 +10,7 @@ import (
)
func (s *Server) GetLockoutPolicy(ctx context.Context, req *mgmt_pb.GetLockoutPolicyRequest) (*mgmt_pb.GetLockoutPolicyResponse, error) {
- policy, err := s.query.LockoutPolicyByOrg(ctx, true, authz.GetCtxData(ctx).OrgID, false)
+ policy, err := s.query.LockoutPolicyByOrg(ctx, true, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/management/policy_lockout_converter.go b/internal/api/grpc/management/policy_lockout_converter.go
index 57910c4aba..83f5648103 100644
--- a/internal/api/grpc/management/policy_lockout_converter.go
+++ b/internal/api/grpc/management/policy_lockout_converter.go
@@ -8,11 +8,13 @@ import (
func AddLockoutPolicyToDomain(p *mgmt.AddCustomLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
+ MaxOTPAttempts: uint64(p.MaxOtpAttempts),
}
}
func UpdateLockoutPolicyToDomain(p *mgmt.UpdateCustomLockoutPolicyRequest) *domain.LockoutPolicy {
return &domain.LockoutPolicy{
MaxPasswordAttempts: uint64(p.MaxPasswordAttempts),
+ MaxOTPAttempts: uint64(p.MaxOtpAttempts),
}
}
diff --git a/internal/api/grpc/policy/password_lockout_policy.go b/internal/api/grpc/policy/password_lockout_policy.go
index af73f1bcef..c33076234d 100644
--- a/internal/api/grpc/policy/password_lockout_policy.go
+++ b/internal/api/grpc/policy/password_lockout_policy.go
@@ -10,6 +10,7 @@ func ModelLockoutPolicyToPb(policy *query.LockoutPolicy) *policy_pb.LockoutPolic
return &policy_pb.LockoutPolicy{
IsDefault: policy.IsDefault,
MaxPasswordAttempts: policy.MaxPasswordAttempts,
+ MaxOtpAttempts: policy.MaxOTPAttempts,
Details: object.ToViewDetailsPb(
policy.Sequence,
policy.CreationDate,
diff --git a/internal/api/grpc/settings/v2/settings.go b/internal/api/grpc/settings/v2/settings.go
index c16178c370..11f41f26b6 100644
--- a/internal/api/grpc/settings/v2/settings.go
+++ b/internal/api/grpc/settings/v2/settings.go
@@ -90,7 +90,7 @@ func (s *Server) GetLegalAndSupportSettings(ctx context.Context, req *settings.G
}
func (s *Server) GetLockoutSettings(ctx context.Context, req *settings.GetLockoutSettingsRequest) (*settings.GetLockoutSettingsResponse, error) {
- current, err := s.query.LockoutPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
+ current, err := s.query.LockoutPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()))
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/settings/v2/settings_converter.go b/internal/api/grpc/settings/v2/settings_converter.go
index 1bd9837127..f00bbf0f9d 100644
--- a/internal/api/grpc/settings/v2/settings_converter.go
+++ b/internal/api/grpc/settings/v2/settings_converter.go
@@ -160,6 +160,7 @@ func legalAndSupportSettingsToPb(current *query.PrivacyPolicy) *settings.LegalAn
func lockoutSettingsToPb(current *query.LockoutPolicy) *settings.LockoutSettings {
return &settings.LockoutSettings{
MaxPasswordAttempts: current.MaxPasswordAttempts,
+ MaxOtpAttempts: current.MaxOTPAttempts,
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
}
}
diff --git a/internal/api/grpc/settings/v2/settings_converter_test.go b/internal/api/grpc/settings/v2/settings_converter_test.go
index 7fcd5d96ce..d1cde07b87 100644
--- a/internal/api/grpc/settings/v2/settings_converter_test.go
+++ b/internal/api/grpc/settings/v2/settings_converter_test.go
@@ -339,10 +339,12 @@ func Test_legalSettingsToPb(t *testing.T) {
func Test_lockoutSettingsToPb(t *testing.T) {
arg := &query.LockoutPolicy{
MaxPasswordAttempts: 22,
+ MaxOTPAttempts: 22,
IsDefault: true,
}
want := &settings.LockoutSettings{
MaxPasswordAttempts: 22,
+ MaxOtpAttempts: 22,
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
}
got := lockoutSettingsToPb(arg)
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index 82a60b641f..98912c9309 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -75,7 +75,7 @@ type loginPolicyViewProvider interface {
}
type lockoutPolicyViewProvider interface {
- LockoutPolicyByOrg(context.Context, bool, string, bool) (*query.LockoutPolicy, error)
+ LockoutPolicyByOrg(context.Context, bool, string) (*query.LockoutPolicy, error)
}
type idpProviderViewProvider interface {
@@ -366,6 +366,7 @@ func lockoutPolicyToDomain(policy *query.LockoutPolicy) *domain.LockoutPolicy {
},
Default: policy.IsDefault,
MaxPasswordAttempts: policy.MaxPasswordAttempts,
+ MaxOTPAttempts: policy.MaxOTPAttempts,
ShowLockOutFailures: policy.ShowFailures,
}
}
@@ -1281,7 +1282,7 @@ func privacyPolicyToDomain(p *query.PrivacyPolicy) *domain.PrivacyPolicy {
}
func (repo *AuthRequestRepo) getLockoutPolicy(ctx context.Context, orgID string) (*query.LockoutPolicy, error) {
- policy, err := repo.LockoutPolicyViewProvider.LockoutPolicyByOrg(ctx, false, orgID, false)
+ policy, err := repo.LockoutPolicyViewProvider.LockoutPolicyByOrg(ctx, false, orgID)
if err != nil {
return nil, err
}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
index 55bcf2e56d..99e0c78ec6 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
@@ -184,7 +184,7 @@ type mockLockoutPolicy struct {
policy *query.LockoutPolicy
}
-func (m *mockLockoutPolicy) LockoutPolicyByOrg(context.Context, bool, string, bool) (*query.LockoutPolicy, error) {
+func (m *mockLockoutPolicy) LockoutPolicyByOrg(context.Context, bool, string) (*query.LockoutPolicy, error) {
return m.policy, nil
}
diff --git a/internal/command/instance.go b/internal/command/instance.go
index 7f379d976b..f9f9121889 100644
--- a/internal/command/instance.go
+++ b/internal/command/instance.go
@@ -101,7 +101,8 @@ type InstanceSetup struct {
ThemeMode domain.LabelPolicyThemeMode
}
LockoutPolicy struct {
- MaxAttempts uint64
+ MaxPasswordAttempts uint64
+ MaxOTPAttempts uint64
ShouldShowLockoutFailure bool
}
EmailTemplate []byte
@@ -271,7 +272,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink, setup.PrivacyPolicy.SupportEmail),
prepareAddDefaultNotificationPolicy(instanceAgg, setup.NotificationPolicy.PasswordChange),
- prepareAddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure),
+ prepareAddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxPasswordAttempts, setup.LockoutPolicy.MaxOTPAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure),
prepareAddDefaultLabelPolicy(
instanceAgg,
diff --git a/internal/command/instance_converter.go b/internal/command/instance_converter.go
index cf1a10b199..1ed1cc123a 100644
--- a/internal/command/instance_converter.go
+++ b/internal/command/instance_converter.go
@@ -109,6 +109,7 @@ func writeModelToLockoutPolicy(wm *LockoutPolicyWriteModel) *domain.LockoutPolic
return &domain.LockoutPolicy{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
MaxPasswordAttempts: wm.MaxPasswordAttempts,
+ MaxOTPAttempts: wm.MaxOTPAttempts,
ShowLockOutFailures: wm.ShowLockOutFailures,
}
}
diff --git a/internal/command/instance_policy_password_lockout.go b/internal/command/instance_policy_password_lockout.go
index 2ed88b997f..59766ae38f 100644
--- a/internal/command/instance_policy_password_lockout.go
+++ b/internal/command/instance_policy_password_lockout.go
@@ -12,9 +12,15 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
-func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) (*domain.ObjectDetails, error) {
+func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, maxPasswordAttempts, maxOTPAttempts uint64, showLockoutFailure bool) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
- cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultLockoutPolicy(instanceAgg, maxAttempts, showLockoutFailure))
+ //nolint:staticcheck
+ cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultLockoutPolicy(
+ instanceAgg,
+ maxPasswordAttempts,
+ maxOTPAttempts,
+ showLockoutFailure,
+ ))
if err != nil {
return nil, err
}
@@ -35,7 +41,13 @@ func (c *Commands) ChangeDefaultLockoutPolicy(ctx context.Context, policy *domai
}
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
- changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, instanceAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
+ changedEvent, hasChanged := existingPolicy.NewChangedEvent(
+ ctx,
+ instanceAgg,
+ policy.MaxPasswordAttempts,
+ policy.MaxOTPAttempts,
+ policy.ShowLockOutFailures,
+ )
if !hasChanged {
return nil, zerrors.ThrowPreconditionFailed(nil, "INSTANCE-0psjF", "Errors.IAM.LockoutPolicy.NotChanged")
}
@@ -65,7 +77,8 @@ func (c *Commands) defaultLockoutPolicyWriteModelByID(ctx context.Context) (poli
func prepareAddDefaultLockoutPolicy(
a *instance.Aggregate,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockoutFailure bool,
) preparation.Validation {
return func() (preparation.CreateCommands, error) {
@@ -83,7 +96,7 @@ func prepareAddDefaultLockoutPolicy(
return nil, zerrors.ThrowAlreadyExists(nil, "INSTANCE-0olDf", "Errors.Instance.LockoutPolicy.AlreadyExists")
}
return []eventstore.Command{
- instance.NewLockoutPolicyAddedEvent(ctx, &a.Aggregate, maxAttempts, showLockoutFailure),
+ instance.NewLockoutPolicyAddedEvent(ctx, &a.Aggregate, maxPasswordAttempts, maxOTPAttempts, showLockoutFailure),
}, nil
}, nil
}
diff --git a/internal/command/instance_policy_password_lockout_model.go b/internal/command/instance_policy_password_lockout_model.go
index dcfa0b62ca..4d529169bb 100644
--- a/internal/command/instance_policy_password_lockout_model.go
+++ b/internal/command/instance_policy_password_lockout_model.go
@@ -54,11 +54,15 @@ func (wm *InstanceLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilde
func (wm *InstanceLockoutPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockoutFailure bool) (*instance.LockoutPolicyChangedEvent, bool) {
changes := make([]policy.LockoutPolicyChanges, 0)
- if wm.MaxPasswordAttempts != maxAttempts {
- changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
+ if wm.MaxPasswordAttempts != maxPasswordAttempts {
+ changes = append(changes, policy.ChangeMaxPasswordAttempts(maxPasswordAttempts))
+ }
+ if wm.MaxOTPAttempts != maxOTPAttempts {
+ changes = append(changes, policy.ChangeMaxOTPAttempts(maxOTPAttempts))
}
if wm.ShowLockOutFailures != showLockoutFailure {
changes = append(changes, policy.ChangeShowLockOutFailures(showLockoutFailure))
diff --git a/internal/command/instance_policy_password_lockout_test.go b/internal/command/instance_policy_password_lockout_test.go
index 02d5ab488d..866399caec 100644
--- a/internal/command/instance_policy_password_lockout_test.go
+++ b/internal/command/instance_policy_password_lockout_test.go
@@ -22,6 +22,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
type args struct {
ctx context.Context
maxPasswordAttempts uint64
+ maxOTPAttempts uint64
showLockOutFailures bool
}
type res struct {
@@ -44,6 +45,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
instance.NewLockoutPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
10,
+ 10,
true,
),
),
@@ -69,6 +71,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
instance.NewLockoutPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
10,
+ 10,
true,
),
),
@@ -77,6 +80,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
maxPasswordAttempts: 10,
+ maxOTPAttempts: 10,
showLockOutFailures: true,
},
res: res{
@@ -91,7 +95,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
- got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.maxPasswordAttempts, tt.args.showLockOutFailures)
+ got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.maxPasswordAttempts, tt.args.maxOTPAttempts, tt.args.showLockOutFailures)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -135,6 +139,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
ctx: context.Background(),
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -152,6 +157,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
instance.NewLockoutPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
10,
+ 10,
true,
),
),
@@ -162,6 +168,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
ctx: context.Background(),
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -179,12 +186,13 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
instance.NewLockoutPolicyAddedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
10,
+ 10,
true,
),
),
),
expectPush(
- newDefaultLockoutPolicyChangedEvent(context.Background(), 20, false),
+ newDefaultLockoutPolicyChangedEvent(context.Background(), 20, 20, false),
),
),
},
@@ -192,6 +200,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
ctx: context.Background(),
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 20,
+ MaxOTPAttempts: 20,
ShowLockOutFailures: false,
},
},
@@ -203,6 +212,7 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
InstanceID: "INSTANCE",
},
MaxPasswordAttempts: 20,
+ MaxOTPAttempts: 20,
ShowLockOutFailures: false,
},
},
@@ -227,11 +237,12 @@ func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
}
}
-func newDefaultLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *instance.LockoutPolicyChangedEvent {
+func newDefaultLockoutPolicyChangedEvent(ctx context.Context, maxPasswordAttempts, maxOTPAttempts uint64, showLockoutFailure bool) *instance.LockoutPolicyChangedEvent {
event, _ := instance.NewLockoutPolicyChangedEvent(ctx,
&instance.NewAggregate("INSTANCE").Aggregate,
[]policy.LockoutPolicyChanges{
- policy.ChangeMaxAttempts(maxAttempts),
+ policy.ChangeMaxPasswordAttempts(maxPasswordAttempts),
+ policy.ChangeMaxOTPAttempts(maxOTPAttempts),
policy.ChangeShowLockOutFailures(showLockoutFailure),
},
)
diff --git a/internal/command/org_policy_lockout.go b/internal/command/org_policy_lockout.go
index 47f98d9770..052fd9e239 100644
--- a/internal/command/org_policy_lockout.go
+++ b/internal/command/org_policy_lockout.go
@@ -21,7 +21,13 @@ func (c *Commands) AddLockoutPolicy(ctx context.Context, resourceOwner string, p
}
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
- pushedEvents, err := c.eventstore.Push(ctx, org.NewLockoutPolicyAddedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures))
+ pushedEvents, err := c.eventstore.Push(ctx, org.NewLockoutPolicyAddedEvent(
+ ctx,
+ orgAgg,
+ policy.MaxPasswordAttempts,
+ policy.MaxOTPAttempts,
+ policy.ShowLockOutFailures,
+ ))
if err != nil {
return nil, err
}
@@ -45,7 +51,7 @@ func (c *Commands) ChangeLockoutPolicy(ctx context.Context, resourceOwner string
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
- changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
+ changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.MaxOTPAttempts, policy.ShowLockOutFailures)
if !hasChanged {
return nil, zerrors.ThrowPreconditionFailed(nil, "ORG-0JFSr", "Errors.Org.LockoutPolicy.NotChanged")
}
@@ -106,3 +112,20 @@ func (c *Commands) orgLockoutPolicyWriteModelByID(ctx context.Context, orgID str
}
return policy, nil
}
+
+func (c *Commands) getLockoutPolicy(ctx context.Context, orgID string) (*domain.LockoutPolicy, error) {
+ orgWm, err := c.orgLockoutPolicyWriteModelByID(ctx, orgID)
+ if err != nil {
+ return nil, err
+ }
+ if orgWm.State == domain.PolicyStateActive {
+ return writeModelToLockoutPolicy(&orgWm.LockoutPolicyWriteModel), nil
+ }
+ instanceWm, err := c.defaultLockoutPolicyWriteModelByID(ctx)
+ if err != nil {
+ return nil, err
+ }
+ policy := writeModelToLockoutPolicy(&instanceWm.LockoutPolicyWriteModel)
+ policy.Default = true
+ return policy, nil
+}
diff --git a/internal/command/org_policy_lockout_model.go b/internal/command/org_policy_lockout_model.go
index abf893fece..9749183e5a 100644
--- a/internal/command/org_policy_lockout_model.go
+++ b/internal/command/org_policy_lockout_model.go
@@ -55,11 +55,15 @@ func (wm *OrgLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
func (wm *OrgLockoutPolicyWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockoutFailure bool) (*org.LockoutPolicyChangedEvent, bool) {
changes := make([]policy.LockoutPolicyChanges, 0)
- if wm.MaxPasswordAttempts != maxAttempts {
- changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
+ if wm.MaxPasswordAttempts != maxPasswordAttempts {
+ changes = append(changes, policy.ChangeMaxPasswordAttempts(maxPasswordAttempts))
+ }
+ if wm.MaxOTPAttempts != maxOTPAttempts {
+ changes = append(changes, policy.ChangeMaxOTPAttempts(maxOTPAttempts))
}
if wm.ShowLockOutFailures != showLockoutFailure {
changes = append(changes, policy.ChangeShowLockOutFailures(showLockoutFailure))
diff --git a/internal/command/org_policy_lockout_test.go b/internal/command/org_policy_lockout_test.go
index 1eda5f348c..89444d8dc2 100644
--- a/internal/command/org_policy_lockout_test.go
+++ b/internal/command/org_policy_lockout_test.go
@@ -44,6 +44,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
ctx: context.Background(),
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -61,6 +62,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
10,
+ 10,
true,
),
),
@@ -72,6 +74,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
orgID: "org1",
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -89,6 +92,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
10,
+ 10,
true,
),
),
@@ -99,6 +103,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
orgID: "org1",
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -109,6 +114,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
ResourceOwner: "org1",
},
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -163,6 +169,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
ctx: context.Background(),
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -183,6 +190,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
orgID: "org1",
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -200,6 +208,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
10,
+ 10,
true,
),
),
@@ -211,6 +220,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
orgID: "org1",
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 10,
+ MaxOTPAttempts: 10,
ShowLockOutFailures: true,
},
},
@@ -228,12 +238,13 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
10,
+ 10,
true,
),
),
),
expectPush(
- newPasswordLockoutPolicyChangedEvent(context.Background(), "org1", 5, false),
+ newPasswordLockoutPolicyChangedEvent(context.Background(), "org1", 5, 5, false),
),
),
},
@@ -242,6 +253,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
orgID: "org1",
policy: &domain.LockoutPolicy{
MaxPasswordAttempts: 5,
+ MaxOTPAttempts: 5,
ShowLockOutFailures: false,
},
},
@@ -252,6 +264,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
ResourceOwner: "org1",
},
MaxPasswordAttempts: 5,
+ MaxOTPAttempts: 5,
ShowLockOutFailures: false,
},
},
@@ -334,6 +347,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
org.NewLockoutPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
10,
+ 10,
true,
),
),
@@ -371,11 +385,12 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
}
}
-func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.LockoutPolicyChangedEvent {
+func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxPasswordAttempts, maxOTPAttempts uint64, showLockoutFailure bool) *org.LockoutPolicyChangedEvent {
event, _ := org.NewLockoutPolicyChangedEvent(ctx,
&org.NewAggregate(orgID).Aggregate,
[]policy.LockoutPolicyChanges{
- policy.ChangeMaxAttempts(maxAttempts),
+ policy.ChangeMaxPasswordAttempts(maxPasswordAttempts),
+ policy.ChangeMaxOTPAttempts(maxOTPAttempts),
policy.ChangeShowLockOutFailures(showLockoutFailure),
},
)
diff --git a/internal/command/policy_password_lockout_model.go b/internal/command/policy_password_lockout_model.go
index a931b63b65..a772cd9828 100644
--- a/internal/command/policy_password_lockout_model.go
+++ b/internal/command/policy_password_lockout_model.go
@@ -10,6 +10,7 @@ type LockoutPolicyWriteModel struct {
eventstore.WriteModel
MaxPasswordAttempts uint64
+ MaxOTPAttempts uint64
ShowLockOutFailures bool
State domain.PolicyState
}
@@ -19,12 +20,16 @@ func (wm *LockoutPolicyWriteModel) Reduce() error {
switch e := event.(type) {
case *policy.LockoutPolicyAddedEvent:
wm.MaxPasswordAttempts = e.MaxPasswordAttempts
+ wm.MaxOTPAttempts = e.MaxOTPAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures
wm.State = domain.PolicyStateActive
case *policy.LockoutPolicyChangedEvent:
if e.MaxPasswordAttempts != nil {
wm.MaxPasswordAttempts = *e.MaxPasswordAttempts
}
+ if e.MaxOTPAttempts != nil {
+ wm.MaxOTPAttempts = *e.MaxOTPAttempts
+ }
if e.ShowLockOutFailures != nil {
wm.ShowLockOutFailures = *e.ShowLockOutFailures
}
diff --git a/internal/command/user_human_otp.go b/internal/command/user_human_otp.go
index 21e0021d44..fd717c0de4 100644
--- a/internal/command/user_human_otp.go
+++ b/internal/command/user_human_otp.go
@@ -158,14 +158,37 @@ func (c *Commands) HumanCheckMFATOTP(ctx context.Context, userID, code, resource
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-3Mif9s", "Errors.User.MFA.OTP.NotReady")
}
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
- err = domain.VerifyTOTP(code, existingOTP.Secret, c.multifactors.OTP.CryptoMFA)
- if err == nil {
+ verifyErr := domain.VerifyTOTP(code, existingOTP.Secret, c.multifactors.OTP.CryptoMFA)
+
+ // recheck for additional events (failed OTP checks or locks)
+ recheckErr := c.eventstore.FilterToQueryReducer(ctx, existingOTP)
+ if recheckErr != nil {
+ return recheckErr
+ }
+ if existingOTP.UserLocked {
+ return zerrors.ThrowPreconditionFailed(nil, "COMMAND-SF3fg", "Errors.User.Locked")
+ }
+
+ // the OTP check succeeded and the user was not locked in the meantime
+ if verifyErr == nil {
_, err = c.eventstore.Push(ctx, user.NewHumanOTPCheckSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
return err
}
- _, pushErr := c.eventstore.Push(ctx, user.NewHumanOTPCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
+
+ // the OTP check failed, therefore check if the limit was reached and the user must additionally be locked
+ commands := make([]eventstore.Command, 0, 2)
+ commands = append(commands, user.NewHumanOTPCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
+ lockoutPolicy, err := c.getLockoutPolicy(ctx, resourceOwner)
+ if err != nil {
+ return err
+ }
+ if lockoutPolicy.MaxOTPAttempts > 0 && existingOTP.CheckFailedCount+1 >= lockoutPolicy.MaxOTPAttempts {
+ commands = append(commands, user.NewUserLockedEvent(ctx, userAgg))
+ }
+
+ _, pushErr := c.eventstore.Push(ctx, commands...)
logging.OnError(pushErr).Error("error create password check failed event")
- return err
+ return verifyErr
}
func (c *Commands) HumanRemoveTOTP(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
@@ -515,14 +538,37 @@ func (c *Commands) humanCheckOTP(
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-S34gh", "Errors.User.Code.NotFound")
}
userAgg := &user.NewAggregate(userID, existingOTP.ResourceOwner()).Aggregate
- err = crypto.VerifyCode(existingOTP.CodeCreationDate(), existingOTP.CodeExpiry(), existingOTP.Code(), code, c.userEncryption)
- if err == nil {
+ verifyErr := crypto.VerifyCode(existingOTP.CodeCreationDate(), existingOTP.CodeExpiry(), existingOTP.Code(), code, c.userEncryption)
+
+ // recheck for additional events (failed OTP checks or locks)
+ recheckErr := c.eventstore.FilterToQueryReducer(ctx, existingOTP)
+ if recheckErr != nil {
+ return recheckErr
+ }
+ if existingOTP.UserLocked() {
+ return zerrors.ThrowPreconditionFailed(nil, "COMMAND-S6h4R", "Errors.User.Locked")
+ }
+
+ // the OTP check succeeded and the user was not locked in the meantime
+ if verifyErr == nil {
_, err = c.eventstore.Push(ctx, checkSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
return err
}
- _, pushErr := c.eventstore.Push(ctx, checkFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
+
+ // the OTP check failed, therefore check if the limit was reached and the user must additionally be locked
+ commands := make([]eventstore.Command, 0, 2)
+ commands = append(commands, checkFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
+ lockoutPolicy, err := c.getLockoutPolicy(ctx, resourceOwner)
+ if err != nil {
+ return err
+ }
+ if lockoutPolicy.MaxOTPAttempts > 0 && existingOTP.CheckFailedCount()+1 >= lockoutPolicy.MaxOTPAttempts {
+ commands = append(commands, user.NewUserLockedEvent(ctx, userAgg))
+ }
+
+ _, pushErr := c.eventstore.Push(ctx, commands...)
logging.WithFields("userID", userID).OnError(pushErr).Error("otp failure check push failed")
- return err
+ return verifyErr
}
func (c *Commands) totpWriteModelByID(ctx context.Context, userID, resourceOwner string) (writeModel *HumanTOTPWriteModel, err error) {
diff --git a/internal/command/user_human_otp_model.go b/internal/command/user_human_otp_model.go
index 25bd4c9ef5..43abc14349 100644
--- a/internal/command/user_human_otp_model.go
+++ b/internal/command/user_human_otp_model.go
@@ -12,8 +12,10 @@ import (
type HumanTOTPWriteModel struct {
eventstore.WriteModel
- State domain.MFAState
- Secret *crypto.CryptoValue
+ State domain.MFAState
+ Secret *crypto.CryptoValue
+ CheckFailedCount uint64
+ UserLocked bool
}
func NewHumanTOTPWriteModel(userID, resourceOwner string) *HumanTOTPWriteModel {
@@ -33,6 +35,16 @@ func (wm *HumanTOTPWriteModel) Reduce() error {
wm.State = domain.MFAStateNotReady
case *user.HumanOTPVerifiedEvent:
wm.State = domain.MFAStateReady
+ wm.CheckFailedCount = 0
+ case *user.HumanOTPCheckSucceededEvent:
+ wm.CheckFailedCount = 0
+ case *user.HumanOTPCheckFailedEvent:
+ wm.CheckFailedCount++
+ case *user.UserLockedEvent:
+ wm.UserLocked = true
+ case *user.UserUnlockedEvent:
+ wm.CheckFailedCount = 0
+ wm.UserLocked = false
case *user.HumanOTPRemovedEvent:
wm.State = domain.MFAStateRemoved
case *user.UserRemovedEvent:
@@ -50,6 +62,10 @@ func (wm *HumanTOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
EventTypes(user.HumanMFAOTPAddedType,
user.HumanMFAOTPVerifiedType,
user.HumanMFAOTPRemovedType,
+ user.HumanMFAOTPCheckSucceededType,
+ user.HumanMFAOTPCheckFailedType,
+ user.UserLockedType,
+ user.UserUnlockedType,
user.UserRemovedType,
user.UserV1MFAOTPAddedType,
user.UserV1MFAOTPVerifiedType,
@@ -72,6 +88,9 @@ type OTPCodeWriteModel interface {
CodeCreationDate() time.Time
CodeExpiry() time.Duration
Code() *crypto.CryptoValue
+ CheckFailedCount() uint64
+ UserLocked() bool
+ eventstore.QueryReducer
}
type HumanOTPSMSWriteModel struct {
@@ -141,6 +160,9 @@ type HumanOTPSMSCodeWriteModel struct {
code *crypto.CryptoValue
codeCreationDate time.Time
codeExpiry time.Duration
+
+ checkFailedCount uint64
+ userLocked bool
}
func (wm *HumanOTPSMSCodeWriteModel) CodeCreationDate() time.Time {
@@ -155,6 +177,14 @@ func (wm *HumanOTPSMSCodeWriteModel) Code() *crypto.CryptoValue {
return wm.code
}
+func (wm *HumanOTPSMSCodeWriteModel) CheckFailedCount() uint64 {
+ return wm.checkFailedCount
+}
+
+func (wm *HumanOTPSMSCodeWriteModel) UserLocked() bool {
+ return wm.userLocked
+}
+
func NewHumanOTPSMSCodeWriteModel(userID, resourceOwner string) *HumanOTPSMSCodeWriteModel {
return &HumanOTPSMSCodeWriteModel{
HumanOTPSMSWriteModel: NewHumanOTPSMSWriteModel(userID, resourceOwner),
@@ -163,10 +193,20 @@ func NewHumanOTPSMSCodeWriteModel(userID, resourceOwner string) *HumanOTPSMSCode
func (wm *HumanOTPSMSCodeWriteModel) Reduce() error {
for _, event := range wm.Events {
- if e, ok := event.(*user.HumanOTPSMSCodeAddedEvent); ok {
+ switch e := event.(type) {
+ case *user.HumanOTPSMSCodeAddedEvent:
wm.code = e.Code
wm.codeCreationDate = e.CreationDate()
wm.codeExpiry = e.Expiry
+ case *user.HumanOTPSMSCheckSucceededEvent:
+ wm.checkFailedCount = 0
+ case *user.HumanOTPSMSCheckFailedEvent:
+ wm.checkFailedCount++
+ case *user.UserLockedEvent:
+ wm.userLocked = true
+ case *user.UserUnlockedEvent:
+ wm.checkFailedCount = 0
+ wm.userLocked = false
}
}
return wm.HumanOTPSMSWriteModel.Reduce()
@@ -179,6 +219,10 @@ func (wm *HumanOTPSMSCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
AggregateIDs(wm.AggregateID).
EventTypes(
user.HumanOTPSMSCodeAddedType,
+ user.HumanOTPSMSCheckSucceededType,
+ user.HumanOTPSMSCheckFailedType,
+ user.UserLockedType,
+ user.UserUnlockedType,
user.HumanPhoneVerifiedType,
user.HumanOTPSMSAddedType,
user.HumanOTPSMSRemovedType,
@@ -259,6 +303,9 @@ type HumanOTPEmailCodeWriteModel struct {
code *crypto.CryptoValue
codeCreationDate time.Time
codeExpiry time.Duration
+
+ checkFailedCount uint64
+ userLocked bool
}
func (wm *HumanOTPEmailCodeWriteModel) CodeCreationDate() time.Time {
@@ -273,6 +320,14 @@ func (wm *HumanOTPEmailCodeWriteModel) Code() *crypto.CryptoValue {
return wm.code
}
+func (wm *HumanOTPEmailCodeWriteModel) CheckFailedCount() uint64 {
+ return wm.checkFailedCount
+}
+
+func (wm *HumanOTPEmailCodeWriteModel) UserLocked() bool {
+ return wm.userLocked
+}
+
func NewHumanOTPEmailCodeWriteModel(userID, resourceOwner string) *HumanOTPEmailCodeWriteModel {
return &HumanOTPEmailCodeWriteModel{
HumanOTPEmailWriteModel: NewHumanOTPEmailWriteModel(userID, resourceOwner),
@@ -281,10 +336,20 @@ func NewHumanOTPEmailCodeWriteModel(userID, resourceOwner string) *HumanOTPEmail
func (wm *HumanOTPEmailCodeWriteModel) Reduce() error {
for _, event := range wm.Events {
- if e, ok := event.(*user.HumanOTPEmailCodeAddedEvent); ok {
+ switch e := event.(type) {
+ case *user.HumanOTPEmailCodeAddedEvent:
wm.code = e.Code
wm.codeCreationDate = e.CreationDate()
wm.codeExpiry = e.Expiry
+ case *user.HumanOTPEmailCheckSucceededEvent:
+ wm.checkFailedCount = 0
+ case *user.HumanOTPEmailCheckFailedEvent:
+ wm.checkFailedCount++
+ case *user.UserLockedEvent:
+ wm.userLocked = true
+ case *user.UserUnlockedEvent:
+ wm.checkFailedCount = 0
+ wm.userLocked = false
}
}
return wm.HumanOTPEmailWriteModel.Reduce()
@@ -297,6 +362,10 @@ func (wm *HumanOTPEmailCodeWriteModel) Query() *eventstore.SearchQueryBuilder {
AggregateIDs(wm.AggregateID).
EventTypes(
user.HumanOTPEmailCodeAddedType,
+ user.HumanOTPEmailCheckSucceededType,
+ user.HumanOTPEmailCheckFailedType,
+ user.UserLockedType,
+ user.UserUnlockedType,
user.HumanEmailVerifiedType,
user.HumanOTPEmailAddedType,
user.HumanOTPEmailRemovedType,
diff --git a/internal/command/user_human_otp_test.go b/internal/command/user_human_otp_test.go
index 490751610d..838e2357c6 100644
--- a/internal/command/user_human_otp_test.go
+++ b/internal/command/user_human_otp_test.go
@@ -1671,6 +1671,15 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
),
),
),
+ expectFilter(), // recheck
+ expectFilter(
+ eventFromEventPusher(
+ org.NewLockoutPolicyAddedEvent(ctx,
+ &org.NewAggregate("orgID").Aggregate,
+ 3, 3, true,
+ ),
+ ),
+ ),
expectPush(
user.NewHumanOTPSMSCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -1707,6 +1716,86 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
+ {
+ name: "invalid code, max attempts reached, error",
+ fields: fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanOTPSMSAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ eventFromEventPusherWithCreationDateNow(
+ user.NewHumanOTPSMSCodeAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("other-code"),
+ },
+ time.Hour,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ ),
+ ),
+ expectFilter(), // recheck
+ expectFilter(
+ eventFromEventPusher(
+ org.NewLockoutPolicyAddedEvent(ctx,
+ &org.NewAggregate("orgID").Aggregate,
+ 1, 1, true,
+ ),
+ ),
+ ),
+ expectPush(
+ user.NewHumanOTPSMSCheckFailedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ user.NewUserLockedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ ),
+ userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: ctx,
+ userID: "user1",
+ code: "code",
+ resourceOwner: "org1",
+ authRequest: &domain.AuthRequest{
+ ID: "authRequestID",
+ AgentID: "userAgentID",
+ BrowserInfo: &domain.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ },
+ res: res{
+ err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
+ },
+ },
{
name: "code ok",
fields: fields{
@@ -1739,6 +1828,7 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
),
),
),
+ expectFilter(), // recheck
expectPush(
user.NewHumanOTPSMSCheckSucceededEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -1777,6 +1867,65 @@ func TestCommandSide_HumanCheckOTPSMS(t *testing.T) {
},
},
},
+ {
+ name: "code ok, locked in the meantime",
+ fields: fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanOTPSMSAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ eventFromEventPusherWithCreationDateNow(
+ user.NewHumanOTPSMSCodeAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("code"),
+ },
+ time.Hour,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ ),
+ ),
+ expectFilter( // recheck
+ user.NewUserLockedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ ),
+ userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: ctx,
+ userID: "user1",
+ code: "code",
+ resourceOwner: "org1",
+ authRequest: &domain.AuthRequest{
+ ID: "authRequestID",
+ AgentID: "userAgentID",
+ BrowserInfo: &domain.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ },
+ res: res{
+ err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-S6h4R", "Errors.User.Locked"),
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -2616,6 +2765,15 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
),
),
),
+ expectFilter(), // recheck
+ expectFilter(
+ eventFromEventPusher(
+ org.NewLockoutPolicyAddedEvent(ctx,
+ &org.NewAggregate("orgID").Aggregate,
+ 3, 3, true,
+ ),
+ ),
+ ),
expectPush(
user.NewHumanOTPEmailCheckFailedEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -2652,6 +2810,86 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
},
},
+ {
+ name: "invalid code, max attempts reached, error",
+ fields: fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanOTPEmailAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ eventFromEventPusherWithCreationDateNow(
+ user.NewHumanOTPEmailCodeAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("other-code"),
+ },
+ time.Hour,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ ),
+ ),
+ expectFilter(), // recheck
+ expectFilter(
+ eventFromEventPusher(
+ org.NewLockoutPolicyAddedEvent(ctx,
+ &org.NewAggregate("orgID").Aggregate,
+ 1, 1, true,
+ ),
+ ),
+ ),
+ expectPush(
+ user.NewHumanOTPEmailCheckFailedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ user.NewUserLockedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ ),
+ userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: ctx,
+ userID: "user1",
+ code: "code",
+ resourceOwner: "org1",
+ authRequest: &domain.AuthRequest{
+ ID: "authRequestID",
+ AgentID: "userAgentID",
+ BrowserInfo: &domain.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ },
+ res: res{
+ err: zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"),
+ },
+ },
{
name: "code ok",
fields: fields{
@@ -2684,6 +2922,7 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
),
),
),
+ expectFilter(), // recheck
expectPush(
user.NewHumanOTPEmailCheckSucceededEvent(ctx,
&user.NewAggregate("user1", "org1").Aggregate,
@@ -2722,6 +2961,65 @@ func TestCommandSide_HumanCheckOTPEmail(t *testing.T) {
},
},
},
+ {
+ name: "code ok, locked in the meantime",
+ fields: fields{
+ eventstore: expectEventstore(
+ expectFilter(
+ eventFromEventPusher(
+ user.NewHumanOTPEmailAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ eventFromEventPusherWithCreationDateNow(
+ user.NewHumanOTPEmailCodeAddedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("code"),
+ },
+ time.Hour,
+ &user.AuthRequestInfo{
+ ID: "authRequestID",
+ UserAgentID: "userAgentID",
+ BrowserInfo: &user.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ ),
+ ),
+ ),
+ expectFilter( // recheck
+ user.NewUserLockedEvent(ctx,
+ &user.NewAggregate("user1", "org1").Aggregate,
+ ),
+ ),
+ ),
+ userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: ctx,
+ userID: "user1",
+ code: "code",
+ resourceOwner: "org1",
+ authRequest: &domain.AuthRequest{
+ ID: "authRequestID",
+ AgentID: "userAgentID",
+ BrowserInfo: &domain.BrowserInfo{
+ UserAgent: "user-agent",
+ AcceptLanguage: "en",
+ RemoteIP: net.IP{192, 0, 2, 1},
+ },
+ },
+ },
+ res: res{
+ err: zerrors.ThrowPreconditionFailed(nil, "COMMAND-S6h4R", "Errors.User.Locked"),
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/internal/command/user_human_password_test.go b/internal/command/user_human_password_test.go
index bb0fa9b26f..0c2b21b3d8 100644
--- a/internal/command/user_human_password_test.go
+++ b/internal/command/user_human_password_test.go
@@ -1643,6 +1643,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
},
lockoutPolicy: &domain.LockoutPolicy{
MaxPasswordAttempts: 1,
+ MaxOTPAttempts: 1,
},
},
res: res{
diff --git a/internal/domain/policy_password_lockout.go b/internal/domain/policy_password_lockout.go
index ddac4de427..a5c304cd69 100644
--- a/internal/domain/policy_password_lockout.go
+++ b/internal/domain/policy_password_lockout.go
@@ -9,5 +9,6 @@ type LockoutPolicy struct {
Default bool
MaxPasswordAttempts uint64
+ MaxOTPAttempts uint64
ShowLockOutFailures bool
}
diff --git a/internal/query/lockout_policy.go b/internal/query/lockout_policy.go
index 7aa650b191..64be9b0b76 100644
--- a/internal/query/lockout_policy.go
+++ b/internal/query/lockout_policy.go
@@ -27,6 +27,7 @@ type LockoutPolicy struct {
State domain.PolicyState
MaxPasswordAttempts uint64
+ MaxOTPAttempts uint64
ShowFailures bool
IsDefault bool
@@ -69,6 +70,10 @@ var (
name: projection.LockoutPolicyMaxPasswordAttemptsCol,
table: lockoutTable,
}
+ LockoutColMaxOTPAttempts = Column{
+ name: projection.LockoutPolicyMaxOTPAttemptsCol,
+ table: lockoutTable,
+ }
LockoutColIsDefault = Column{
name: projection.LockoutPolicyIsDefaultCol,
table: lockoutTable,
@@ -77,13 +82,9 @@ var (
name: projection.LockoutPolicyStateCol,
table: lockoutTable,
}
- LockoutPolicyOwnerRemoved = Column{
- name: projection.LockoutPolicyOwnerRemovedCol,
- table: lockoutTable,
- }
)
-func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (policy *LockoutPolicy, err error) {
+func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string) (policy *LockoutPolicy, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@@ -96,9 +97,6 @@ func (q *Queries) LockoutPolicyByOrg(ctx context.Context, shouldTriggerBulk bool
eq := sq.Eq{
LockoutColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
}
- if !withOwnerRemoved {
- eq[LockoutPolicyOwnerRemoved.identifier()] = false
- }
stmt, scan := prepareLockoutPolicyQuery(ctx, q.client)
query, args, err := stmt.Where(
@@ -153,6 +151,7 @@ func prepareLockoutPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Sele
LockoutColResourceOwner.identifier(),
LockoutColShowFailures.identifier(),
LockoutColMaxPasswordAttempts.identifier(),
+ LockoutColMaxOTPAttempts.identifier(),
LockoutColIsDefault.identifier(),
LockoutColState.identifier(),
).
@@ -168,6 +167,7 @@ func prepareLockoutPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Sele
&policy.ResourceOwner,
&policy.ShowFailures,
&policy.MaxPasswordAttempts,
+ &policy.MaxOTPAttempts,
&policy.IsDefault,
&policy.State,
)
diff --git a/internal/query/lockout_policy_test.go b/internal/query/lockout_policy_test.go
index 044f6c291b..2805ef8fdc 100644
--- a/internal/query/lockout_policy_test.go
+++ b/internal/query/lockout_policy_test.go
@@ -13,16 +13,17 @@ import (
)
var (
- prepareLockoutPolicyStmt = `SELECT projections.lockout_policies2.id,` +
- ` projections.lockout_policies2.sequence,` +
- ` projections.lockout_policies2.creation_date,` +
- ` projections.lockout_policies2.change_date,` +
- ` projections.lockout_policies2.resource_owner,` +
- ` projections.lockout_policies2.show_failure,` +
- ` projections.lockout_policies2.max_password_attempts,` +
- ` projections.lockout_policies2.is_default,` +
- ` projections.lockout_policies2.state` +
- ` FROM projections.lockout_policies2` +
+ prepareLockoutPolicyStmt = `SELECT projections.lockout_policies3.id,` +
+ ` projections.lockout_policies3.sequence,` +
+ ` projections.lockout_policies3.creation_date,` +
+ ` projections.lockout_policies3.change_date,` +
+ ` projections.lockout_policies3.resource_owner,` +
+ ` projections.lockout_policies3.show_failure,` +
+ ` projections.lockout_policies3.max_password_attempts,` +
+ ` projections.lockout_policies3.max_otp_attempts,` +
+ ` projections.lockout_policies3.is_default,` +
+ ` projections.lockout_policies3.state` +
+ ` FROM projections.lockout_policies3` +
` AS OF SYSTEM TIME '-1 ms'`
prepareLockoutPolicyCols = []string{
@@ -33,6 +34,7 @@ var (
"resource_owner",
"show_failure",
"max_password_attempts",
+ "max_otp_attempts",
"is_default",
"state",
}
@@ -82,6 +84,7 @@ func Test_LockoutPolicyPrepares(t *testing.T) {
"ro",
true,
20,
+ 20,
true,
domain.PolicyStateActive,
},
@@ -96,6 +99,7 @@ func Test_LockoutPolicyPrepares(t *testing.T) {
State: domain.PolicyStateActive,
ShowFailures: true,
MaxPasswordAttempts: 20,
+ MaxOTPAttempts: 20,
IsDefault: true,
},
},
diff --git a/internal/query/projection/lockout_policy.go b/internal/query/projection/lockout_policy.go
index ceb99c2aa0..4412308b89 100644
--- a/internal/query/projection/lockout_policy.go
+++ b/internal/query/projection/lockout_policy.go
@@ -14,7 +14,7 @@ import (
)
const (
- LockoutPolicyTable = "projections.lockout_policies2"
+ LockoutPolicyTable = "projections.lockout_policies3"
LockoutPolicyIDCol = "id"
LockoutPolicyCreationDateCol = "creation_date"
@@ -25,8 +25,8 @@ const (
LockoutPolicyResourceOwnerCol = "resource_owner"
LockoutPolicyInstanceIDCol = "instance_id"
LockoutPolicyMaxPasswordAttemptsCol = "max_password_attempts"
+ LockoutPolicyMaxOTPAttemptsCol = "max_otp_attempts"
LockoutPolicyShowLockOutFailuresCol = "show_failure"
- LockoutPolicyOwnerRemovedCol = "owner_removed"
)
type lockoutPolicyProjection struct{}
@@ -51,11 +51,10 @@ func (*lockoutPolicyProjection) Init() *old_handler.Check {
handler.NewColumn(LockoutPolicyResourceOwnerCol, handler.ColumnTypeText),
handler.NewColumn(LockoutPolicyInstanceIDCol, handler.ColumnTypeText),
handler.NewColumn(LockoutPolicyMaxPasswordAttemptsCol, handler.ColumnTypeInt64),
+ handler.NewColumn(LockoutPolicyMaxOTPAttemptsCol, handler.ColumnTypeInt64, handler.Default(0)),
handler.NewColumn(LockoutPolicyShowLockOutFailuresCol, handler.ColumnTypeBool),
- handler.NewColumn(LockoutPolicyOwnerRemovedCol, handler.ColumnTypeBool, handler.Default(false)),
},
handler.NewPrimaryKey(LockoutPolicyInstanceIDCol, LockoutPolicyIDCol),
- handler.WithIndex(handler.NewIndex("owner_removed", []string{LockoutPolicyOwnerRemovedCol})),
),
)
}
@@ -125,6 +124,7 @@ func (p *lockoutPolicyProjection) reduceAdded(event eventstore.Event) (*handler.
handler.NewCol(LockoutPolicyIDCol, policyEvent.Aggregate().ID),
handler.NewCol(LockoutPolicyStateCol, domain.PolicyStateActive),
handler.NewCol(LockoutPolicyMaxPasswordAttemptsCol, policyEvent.MaxPasswordAttempts),
+ handler.NewCol(LockoutPolicyMaxOTPAttemptsCol, policyEvent.MaxOTPAttempts),
handler.NewCol(LockoutPolicyShowLockOutFailuresCol, policyEvent.ShowLockOutFailures),
handler.NewCol(LockoutPolicyIsDefaultCol, isDefault),
handler.NewCol(LockoutPolicyResourceOwnerCol, policyEvent.Aggregate().ResourceOwner),
@@ -149,6 +149,9 @@ func (p *lockoutPolicyProjection) reduceChanged(event eventstore.Event) (*handle
if policyEvent.MaxPasswordAttempts != nil {
cols = append(cols, handler.NewCol(LockoutPolicyMaxPasswordAttemptsCol, *policyEvent.MaxPasswordAttempts))
}
+ if policyEvent.MaxOTPAttempts != nil {
+ cols = append(cols, handler.NewCol(LockoutPolicyMaxOTPAttemptsCol, *policyEvent.MaxOTPAttempts))
+ }
if policyEvent.ShowLockOutFailures != nil {
cols = append(cols, handler.NewCol(LockoutPolicyShowLockOutFailuresCol, *policyEvent.ShowLockOutFailures))
}
diff --git a/internal/query/projection/lockout_policy_test.go b/internal/query/projection/lockout_policy_test.go
index 781f3a48c4..f44e1f4f0b 100644
--- a/internal/query/projection/lockout_policy_test.go
+++ b/internal/query/projection/lockout_policy_test.go
@@ -30,6 +30,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
org.AggregateType,
[]byte(`{
"maxPasswordAttempts": 10,
+ "maxOTPAttempts": 10,
"showLockOutFailures": true
}`),
), org.LockoutPolicyAddedEventMapper),
@@ -41,7 +42,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "INSERT INTO projections.lockout_policies2 (creation_date, change_date, sequence, id, state, max_password_attempts, show_failure, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+ expectedStmt: "INSERT INTO projections.lockout_policies3 (creation_date, change_date, sequence, id, state, max_password_attempts, max_otp_attempts, show_failure, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@@ -49,6 +50,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
"agg-id",
domain.PolicyStateActive,
uint64(10),
+ uint64(10),
true,
false,
"ro-id",
@@ -69,6 +71,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
org.AggregateType,
[]byte(`{
"maxPasswordAttempts": 10,
+ "maxOTPAttempts": 10,
"showLockOutFailures": true
}`),
), org.LockoutPolicyChangedEventMapper),
@@ -79,11 +82,12 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "UPDATE projections.lockout_policies2 SET (change_date, sequence, max_password_attempts, show_failure) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
+ expectedStmt: "UPDATE projections.lockout_policies3 SET (change_date, sequence, max_password_attempts, max_otp_attempts, show_failure) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
uint64(10),
+ uint64(10),
true,
"agg-id",
"instance-id",
@@ -110,7 +114,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "DELETE FROM projections.lockout_policies2 WHERE (id = $1) AND (instance_id = $2)",
+ expectedStmt: "DELETE FROM projections.lockout_policies3 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
@@ -137,7 +141,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "DELETE FROM projections.lockout_policies2 WHERE (instance_id = $1)",
+ expectedStmt: "DELETE FROM projections.lockout_policies3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
@@ -156,6 +160,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
instance.AggregateType,
[]byte(`{
"maxPasswordAttempts": 10,
+ "maxOTPAttempts": 10,
"showLockOutFailures": true
}`),
), instance.LockoutPolicyAddedEventMapper),
@@ -166,7 +171,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "INSERT INTO projections.lockout_policies2 (creation_date, change_date, sequence, id, state, max_password_attempts, show_failure, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+ expectedStmt: "INSERT INTO projections.lockout_policies3 (creation_date, change_date, sequence, id, state, max_password_attempts, max_otp_attempts, show_failure, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@@ -174,6 +179,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
"agg-id",
domain.PolicyStateActive,
uint64(10),
+ uint64(10),
true,
true,
"ro-id",
@@ -194,6 +200,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
instance.AggregateType,
[]byte(`{
"maxPasswordAttempts": 10,
+ "maxOTPAttempts": 10,
"showLockOutFailures": true
}`),
), instance.LockoutPolicyChangedEventMapper),
@@ -204,11 +211,12 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "UPDATE projections.lockout_policies2 SET (change_date, sequence, max_password_attempts, show_failure) = ($1, $2, $3, $4) WHERE (id = $5) AND (instance_id = $6)",
+ expectedStmt: "UPDATE projections.lockout_policies3 SET (change_date, sequence, max_password_attempts, max_otp_attempts, show_failure) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
uint64(10),
+ uint64(10),
true,
"agg-id",
"instance-id",
@@ -235,7 +243,7 @@ func TestLockoutPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
- expectedStmt: "DELETE FROM projections.lockout_policies2 WHERE (instance_id = $1) AND (resource_owner = $2)",
+ expectedStmt: "DELETE FROM projections.lockout_policies3 WHERE (instance_id = $1) AND (resource_owner = $2)",
expectedArgs: []interface{}{
"instance-id",
"agg-id",
diff --git a/internal/repository/instance/policy_password_lockout.go b/internal/repository/instance/policy_password_lockout.go
index 671b539610..b05f992668 100644
--- a/internal/repository/instance/policy_password_lockout.go
+++ b/internal/repository/instance/policy_password_lockout.go
@@ -19,7 +19,8 @@ type LockoutPolicyAddedEvent struct {
func NewLockoutPolicyAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockoutFailure bool,
) *LockoutPolicyAddedEvent {
return &LockoutPolicyAddedEvent{
@@ -28,7 +29,8 @@ func NewLockoutPolicyAddedEvent(
ctx,
aggregate,
LockoutPolicyAddedEventType),
- maxAttempts,
+ maxPasswordAttempts,
+ maxOTPAttempts,
showLockoutFailure),
}
}
diff --git a/internal/repository/org/policy_password_lockout.go b/internal/repository/org/policy_password_lockout.go
index e95d86eb79..8ada7f2320 100644
--- a/internal/repository/org/policy_password_lockout.go
+++ b/internal/repository/org/policy_password_lockout.go
@@ -20,7 +20,8 @@ type LockoutPolicyAddedEvent struct {
func NewLockoutPolicyAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockoutFailure bool,
) *LockoutPolicyAddedEvent {
return &LockoutPolicyAddedEvent{
@@ -29,7 +30,8 @@ func NewLockoutPolicyAddedEvent(
ctx,
aggregate,
LockoutPolicyAddedEventType),
- maxAttempts,
+ maxPasswordAttempts,
+ maxOTPAttempts,
showLockoutFailure),
}
}
diff --git a/internal/repository/policy/policy_password_lockout.go b/internal/repository/policy/policy_password_lockout.go
index 8ca0737674..ec500144ef 100644
--- a/internal/repository/policy/policy_password_lockout.go
+++ b/internal/repository/policy/policy_password_lockout.go
@@ -15,6 +15,7 @@ type LockoutPolicyAddedEvent struct {
eventstore.BaseEvent `json:"-"`
MaxPasswordAttempts uint64 `json:"maxPasswordAttempts,omitempty"`
+ MaxOTPAttempts uint64 `json:"maxOTPAttempts,omitempty"`
ShowLockOutFailures bool `json:"showLockOutFailures,omitempty"`
}
@@ -28,13 +29,15 @@ func (e *LockoutPolicyAddedEvent) UniqueConstraints() []*eventstore.UniqueConstr
func NewLockoutPolicyAddedEvent(
base *eventstore.BaseEvent,
- maxAttempts uint64,
+ maxPasswordAttempts,
+ maxOTPAttempts uint64,
showLockOutFailures bool,
) *LockoutPolicyAddedEvent {
return &LockoutPolicyAddedEvent{
BaseEvent: *base,
- MaxPasswordAttempts: maxAttempts,
+ MaxPasswordAttempts: maxPasswordAttempts,
+ MaxOTPAttempts: maxOTPAttempts,
ShowLockOutFailures: showLockOutFailures,
}
}
@@ -56,6 +59,7 @@ type LockoutPolicyChangedEvent struct {
eventstore.BaseEvent `json:"-"`
MaxPasswordAttempts *uint64 `json:"maxPasswordAttempts,omitempty"`
+ MaxOTPAttempts *uint64 `json:"maxOTPAttempts,omitempty"`
ShowLockOutFailures *bool `json:"showLockOutFailures,omitempty"`
}
@@ -85,12 +89,18 @@ func NewLockoutPolicyChangedEvent(
type LockoutPolicyChanges func(*LockoutPolicyChangedEvent)
-func ChangeMaxAttempts(maxAttempts uint64) func(*LockoutPolicyChangedEvent) {
+func ChangeMaxPasswordAttempts(maxAttempts uint64) func(*LockoutPolicyChangedEvent) {
return func(e *LockoutPolicyChangedEvent) {
e.MaxPasswordAttempts = &maxAttempts
}
}
+func ChangeMaxOTPAttempts(maxAttempts uint64) func(*LockoutPolicyChangedEvent) {
+ return func(e *LockoutPolicyChangedEvent) {
+ e.MaxOTPAttempts = &maxAttempts
+ }
+}
+
func ChangeShowLockOutFailures(showLockOutFailures bool) func(*LockoutPolicyChangedEvent) {
return func(e *LockoutPolicyChangedEvent) {
e.ShowLockOutFailures = &showLockOutFailures
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index 3e42109619..5211995f23 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -6650,6 +6650,12 @@ message UpdateLockoutPolicyRequest {
example: "\"10\""
}
];
+ uint32 max_otp_attempts = 2 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "Maximum failed attempts for a single OTP type (TOTP, SMS, Email) before the account gets locked. Attempts are reset as soon as the OTP is entered correctly. If set to 0 the account will never be locked."
+ example: "\"10\""
+ }
+ ];
}
message UpdateLockoutPolicyResponse {
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index 284b070d5d..aca7ae17ca 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -10412,6 +10412,12 @@ message AddCustomLockoutPolicyRequest {
description: "When the user has reached the maximum password attempts the account will be locked, If this is set to 0 the lockout will not trigger."
}
];
+ uint32 max_otp_attempts = 2 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "Maximum failed attempts for a single OTP type (TOTP, SMS, Email) before the account gets locked. Attempts are reset as soon as the OTP is entered correctly. If set to 0 the account will never be locked."
+ example: "\"10\""
+ }
+ ];
}
message AddCustomLockoutPolicyResponse {
@@ -10424,6 +10430,12 @@ message UpdateCustomLockoutPolicyRequest {
description: "When the user has reached the maximum password attempts the account will be locked, If this is set to 0 the lockout will not trigger."
}
];
+ uint32 max_otp_attempts = 2 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "Maximum failed attempts for a single OTP type (TOTP, SMS, Email) before the account gets locked. Attempts are reset as soon as the OTP is entered correctly. If set to 0 the account will never be locked."
+ example: "\"10\""
+ }
+ ];
}
message UpdateCustomLockoutPolicyResponse {
diff --git a/proto/zitadel/policy.proto b/proto/zitadel/policy.proto
index 8fcb976d44..75d8472103 100644
--- a/proto/zitadel/policy.proto
+++ b/proto/zitadel/policy.proto
@@ -337,6 +337,12 @@ message LockoutPolicy {
example: "\"10\""
}
];
+ uint64 max_otp_attempts = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "Maximum failed attempts for a single OTP type (TOTP, SMS, Email) before the account gets locked. Attempts are reset as soon as the OTP is entered correctly. If set to 0 the account will never be locked."
+ example: "\"10\""
+ }
+ ];
bool is_default = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "defines if the organization's admin changed the policy"
diff --git a/proto/zitadel/settings/v2beta/lockout_settings.proto b/proto/zitadel/settings/v2beta/lockout_settings.proto
index 10786c9282..1ff10c65f0 100644
--- a/proto/zitadel/settings/v2beta/lockout_settings.proto
+++ b/proto/zitadel/settings/v2beta/lockout_settings.proto
@@ -20,4 +20,10 @@ message LockoutSettings {
description: "resource_owner_type returns if the settings is managed on the organization or on the instance";
}
];
+ uint64 max_otp_attempts = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "Maximum failed attempts for a single OTP type (TOTP, SMS, Email) before the account gets locked. Attempts are reset as soon as the OTP is entered correctly. If set to 0 the account will never be locked."
+ example: "\"10\""
+ }
+ ];
}