From 016676e1dc21f031eb3819179f7c2827ad30ca3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Thu, 26 Jun 2025 19:17:45 +0300 Subject: [PATCH] chore(oidc): graduate webkey to stable (#10122) # Which Problems Are Solved Stabilize the usage of webkeys. # How the Problems Are Solved - Remove all legacy signing key code from the OIDC API - Remove the webkey feature flag from proto - Remove the webkey feature flag from console - Cleanup documentation # Additional Changes - Resolved some canonical header linter errors in OIDC - Use the constant for `projections.lock` in the saml package. # Additional Context - Closes #10029 - After #10105 - After #10061 --- cmd/start/start.go | 1 - .../components/features/features.component.ts | 1 - console/src/assets/i18n/bg.json | 2 - console/src/assets/i18n/cs.json | 2 - console/src/assets/i18n/de.json | 2 - console/src/assets/i18n/en.json | 2 - console/src/assets/i18n/es.json | 2 - console/src/assets/i18n/fr.json | 2 - console/src/assets/i18n/hu.json | 2 - console/src/assets/i18n/id.json | 2 - console/src/assets/i18n/it.json | 2 - console/src/assets/i18n/ja.json | 2 - console/src/assets/i18n/ko.json | 2 - console/src/assets/i18n/mk.json | 2 - console/src/assets/i18n/nl.json | 2 - console/src/assets/i18n/pl.json | 2 - console/src/assets/i18n/pt.json | 2 - console/src/assets/i18n/ro.json | 2 - console/src/assets/i18n/ru.json | 2 - console/src/assets/i18n/sv.json | 2 - console/src/assets/i18n/zh.json | 2 - .../guides/integrate/login/oidc/webkeys.md | 7 - internal/api/grpc/feature/v2/converter.go | 2 - .../api/grpc/feature/v2/converter_test.go | 10 - internal/api/grpc/feature/v2beta/converter.go | 2 - .../api/grpc/feature/v2beta/converter_test.go | 10 - .../webkey_integration_test.go | 62 +-- internal/api/grpc/webkey/v2beta/webkey.go | 21 - internal/api/oidc/access_token.go | 2 +- internal/api/oidc/auth_request_converter.go | 9 +- .../api/oidc/integration_test/keys_test.go | 41 +- .../oidc/integration_test/userinfo_test.go | 10 +- internal/api/oidc/key.go | 210 +------- internal/api/oidc/op.go | 12 +- internal/api/oidc/server.go | 2 +- internal/api/oidc/server_test.go | 89 ---- internal/api/oidc/token.go | 21 +- internal/api/saml/certificate.go | 3 +- .../eventstore/token_verifier.go | 29 +- internal/command/instance_features.go | 23 - internal/command/instance_features_model.go | 5 - internal/command/key_pair.go | 25 - internal/crypto/rsa.go | 8 - internal/feature/feature.go | 4 +- internal/feature/key_enumer.go | 65 +-- .../handlers/back_channel_logout.go | 18 +- .../handlers/mock/commands.mock.go | 113 ++--- .../handlers/mock/queries.mock.go | 137 +++--- .../notification/handlers/mock/queue.mock.go | 11 +- internal/notification/handlers/queries.go | 2 - internal/query/instance_features.go | 1 - internal/query/instance_features_model.go | 3 - internal/query/key.go | 317 ------------ internal/query/key_test.go | 453 ------------------ .../query/projection/instance_features.go | 4 - .../feature/feature_v2/eventstore.go | 1 - .../repository/feature/feature_v2/feature.go | 1 - proto/zitadel/feature/v2/instance.proto | 22 +- proto/zitadel/feature/v2beta/instance.proto | 22 +- 59 files changed, 203 insertions(+), 1614 deletions(-) delete mode 100644 internal/query/key_test.go diff --git a/cmd/start/start.go b/cmd/start/start.go index 8820480f0c..3c3b5cb3e0 100644 --- a/cmd/start/start.go +++ b/cmd/start/start.go @@ -550,7 +550,6 @@ func startAPIs( keys.OIDC, keys.OIDCKey, eventstore, - dbClient, userAgentInterceptor, instanceInterceptor.Handler, limitingAccessInterceptor, diff --git a/console/src/app/components/features/features.component.ts b/console/src/app/components/features/features.component.ts index 70e038bae8..ace2788fcf 100644 --- a/console/src/app/components/features/features.component.ts +++ b/console/src/app/components/features/features.component.ts @@ -38,7 +38,6 @@ const FEATURE_KEYS = [ 'oidcTriggerIntrospectionProjections', 'permissionCheckV2', 'userSchema', - 'webKey', ] as const; export type ToggleState = { source: Source; enabled: boolean }; diff --git a/console/src/assets/i18n/bg.json b/console/src/assets/i18n/bg.json index 7d594e8318..50c0d66027 100644 --- a/console/src/assets/i18n/bg.json +++ b/console/src/assets/i18n/bg.json @@ -1641,8 +1641,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се използва за уведомяване на клиентите за прекратяване на сесията при OpenID доставчика.", "PERMISSIONCHECKV2": "Проверка на разрешения V2", "PERMISSIONCHECKV2_DESCRIPTION": "Ако флагът е активиран, ще можете да използвате новия API и неговите функции.", - "WEBKEY": "Уеб ключ", - "WEBKEY_DESCRIPTION": "Ако флагът е активиран, ще можете да използвате новия API и неговите функции.", "STATES": { "INHERITED": "Наследено", "ENABLED": "Активирано", diff --git a/console/src/assets/i18n/cs.json b/console/src/assets/i18n/cs.json index 2ee5d9d0c5..5b4547ccb4 100644 --- a/console/src/assets/i18n/cs.json +++ b/console/src/assets/i18n/cs.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 a může být použit k informování klientů o ukončení relace u poskytovatele OpenID.", "PERMISSIONCHECKV2": "Kontrola oprávnění V2", "PERMISSIONCHECKV2_DESCRIPTION": "Pokud je příznak povolen, budete moci používat nový API a jeho funkce.", - "WEBKEY": "Webový klíč", - "WEBKEY_DESCRIPTION": "Pokud je příznak povolen, budete moci používat nový API a jeho funkce.", "STATES": { "INHERITED": "Děděno", "ENABLED": "Povoleno", diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index b8f8363d13..8fec6498ec 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Der Back-Channel-Logout implementiert OpenID Connect Back-Channel Logout 1.0 und kann verwendet werden, um Clients über die Beendigung der Sitzung beim OpenID-Provider zu benachrichtigen.", "PERMISSIONCHECKV2": "Berechtigungsprüfung V2", "PERMISSIONCHECKV2_DESCRIPTION": "Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.", - "WEBKEY": "Web-Schlüssel", - "WEBKEY_DESCRIPTION": "Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.", "STATES": { "INHERITED": "Erben", "ENABLED": "Aktiviert", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index fe152acb81..95fd55bfef 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -1645,8 +1645,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.", "PERMISSIONCHECKV2": "Permission Check V2", "PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", - "WEBKEY": "Web Key", - "WEBKEY_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", "STATES": { "INHERITED": "Inherit", "ENABLED": "Enabled", diff --git a/console/src/assets/i18n/es.json b/console/src/assets/i18n/es.json index fff111fd1d..359aa4a0b5 100644 --- a/console/src/assets/i18n/es.json +++ b/console/src/assets/i18n/es.json @@ -1643,8 +1643,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "El Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 y se puede usar para notificar a los clientes sobre la terminación de la sesión en el proveedor de OpenID.", "PERMISSIONCHECKV2": "Verificación de permisos V2", "PERMISSIONCHECKV2_DESCRIPTION": "Si la bandera está habilitada, podrá usar la nueva API y sus funciones.", - "WEBKEY": "Clave web", - "WEBKEY_DESCRIPTION": "Si la bandera está habilitada, podrá usar la nueva API y sus funciones.", "STATES": { "INHERITED": "Heredado", "ENABLED": "Habilitado", diff --git a/console/src/assets/i18n/fr.json b/console/src/assets/i18n/fr.json index fc5cf69602..0864a2f8c0 100644 --- a/console/src/assets/i18n/fr.json +++ b/console/src/assets/i18n/fr.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Le Back-Channel Logout implémente OpenID Connect Back-Channel Logout 1.0 et peut être utilisé pour notifier les clients de la fin de session chez le fournisseur OpenID.", "PERMISSIONCHECKV2": "Vérification des permissions V2", "PERMISSIONCHECKV2_DESCRIPTION": "Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.", - "WEBKEY": "Clé web", - "WEBKEY_DESCRIPTION": "Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.", "STATES": { "INHERITED": "Hérité", "ENABLED": "Activé", diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json index d7dd32b15a..a87122dc52 100644 --- a/console/src/assets/i18n/hu.json +++ b/console/src/assets/i18n/hu.json @@ -1640,8 +1640,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "A Back-Channel Logout megvalósítja az OpenID Connect Back-Channel Logout 1.0-t, és használható az ügyfelek értesítésére a munkamenet befejezéséről az OpenID szolgáltatónál.", "PERMISSIONCHECKV2": "Engedély ellenőrzés V2", "PERMISSIONCHECKV2_DESCRIPTION": "Ha a zászló engedélyezve van, használhatja az új API-t és annak funkcióit.", - "WEBKEY": "Webkulcs", - "WEBKEY_DESCRIPTION": "Ha a zászló engedélyezve van, használhatja az új API-t és annak funkcióit.", "STATES": { "INHERITED": "Örököl", "ENABLED": "Engedélyezve", diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json index 3dcd7b36b7..3f245d03c5 100644 --- a/console/src/assets/i18n/id.json +++ b/console/src/assets/i18n/id.json @@ -1513,8 +1513,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.", "PERMISSIONCHECKV2": "Permission Check V2", "PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", - "WEBKEY": "Web Key", - "WEBKEY_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", "STATES": { "INHERITED": "Mewarisi", "ENABLED": "Diaktifkan", "DISABLED": "Dengan disabilitas" }, "INHERITED_DESCRIPTION": "Ini menetapkan nilai ke nilai default sistem.", "INHERITEDINDICATOR_DESCRIPTION": { diff --git a/console/src/assets/i18n/it.json b/console/src/assets/i18n/it.json index da1df2ba38..e127281433 100644 --- a/console/src/assets/i18n/it.json +++ b/console/src/assets/i18n/it.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Il Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 e può essere utilizzato per notificare ai client la terminazione della sessione presso il provider OpenID.", "PERMISSIONCHECKV2": "Controllo permessi V2", "PERMISSIONCHECKV2_DESCRIPTION": "Se il flag è abilitato, potrai utilizzare la nuova API e le sue funzionalità.", - "WEBKEY": "Chiave Web", - "WEBKEY_DESCRIPTION": "Se il flag è abilitato, potrai utilizzare la nuova API e le sue funzionalità.", "STATES": { "INHERITED": "Predefinito", "ENABLED": "Abilitato", diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 1828a8ada8..250561e938 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "バックチャネルログアウトは OpenID Connect バックチャネルログアウト 1.0 を実装し、OpenID プロバイダーでのセッション終了についてクライアントに通知するために使用できます。", "PERMISSIONCHECKV2": "権限チェック V2", "PERMISSIONCHECKV2_DESCRIPTION": "フラグが有効になっている場合、新しい API とその機能を使用できます。", - "WEBKEY": "ウェブキー", - "WEBKEY_DESCRIPTION": "フラグが有効になっている場合、新しい API とその機能を使用できます。", "STATES": { "INHERITED": "継承", "ENABLED": "有効", diff --git a/console/src/assets/i18n/ko.json b/console/src/assets/i18n/ko.json index af5fa65972..716375941d 100644 --- a/console/src/assets/i18n/ko.json +++ b/console/src/assets/i18n/ko.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "백채널 로그아웃은 OpenID Connect 백채널 로그아웃 1.0을 구현하며, OpenID 제공자에서 세션 종료에 대해 클라이언트에게 알리는 데 사용할 수 있습니다.", "PERMISSIONCHECKV2": "권한 확인 V2", "PERMISSIONCHECKV2_DESCRIPTION": "플래그가 활성화되면 새로운 API와 그 기능을 사용할 수 있습니다.", - "WEBKEY": "웹 키", - "WEBKEY_DESCRIPTION": "플래그가 활성화되면 새로운 API와 그 기능을 사용할 수 있습니다.", "STATES": { "INHERITED": "상속", "ENABLED": "활성화됨", diff --git a/console/src/assets/i18n/mk.json b/console/src/assets/i18n/mk.json index 1e85e06928..39836f5dfc 100644 --- a/console/src/assets/i18n/mk.json +++ b/console/src/assets/i18n/mk.json @@ -1643,8 +1643,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се користи за известување на клиентите за завршување на сесијата кај OpenID провајдерот.", "PERMISSIONCHECKV2": "Проверка на дозволи V2", "PERMISSIONCHECKV2_DESCRIPTION": "Ако знамето е овозможено, ќе можете да ја користите новата API и нејзините функции.", - "WEBKEY": "Веб клуч", - "WEBKEY_DESCRIPTION": "Ако знамето е овозможено, ќе можете да ја користите новата API и нејзините функции.", "STATES": { "INHERITED": "Наследи", "ENABLED": "Овозможено", diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json index c3de881784..c49867aa3e 100644 --- a/console/src/assets/i18n/nl.json +++ b/console/src/assets/i18n/nl.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "De Back-Channel Logout implementeert OpenID Connect Back-Channel Logout 1.0 en kan worden gebruikt om clients te informeren over het beëindigen van de sessie bij de OpenID-provider.", "PERMISSIONCHECKV2": "Permissiecontrole V2", "PERMISSIONCHECKV2_DESCRIPTION": "Als de vlag is ingeschakeld, kunt u de nieuwe API en de bijbehorende functies gebruiken.", - "WEBKEY": "Websleutel", - "WEBKEY_DESCRIPTION": "Als de vlag is ingeschakeld, kunt u de nieuwe API en de bijbehorende functies gebruiken.", "STATES": { "INHERITED": "Overgenomen", "ENABLED": "Ingeschakeld", diff --git a/console/src/assets/i18n/pl.json b/console/src/assets/i18n/pl.json index ca5476463d..abf2e1ba8a 100644 --- a/console/src/assets/i18n/pl.json +++ b/console/src/assets/i18n/pl.json @@ -1641,8 +1641,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 i może być używany do powiadamiania klientów o zakończeniu sesji u dostawcy OpenID.", "PERMISSIONCHECKV2": "Sprawdzanie uprawnień V2", "PERMISSIONCHECKV2_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł korzystać z nowego API i jego funkcji.", - "WEBKEY": "Klucz Web", - "WEBKEY_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł korzystać z nowego API i jego funkcji.", "STATES": { "INHERITED": "Dziedziczony", "ENABLED": "Włączony", diff --git a/console/src/assets/i18n/pt.json b/console/src/assets/i18n/pt.json index 7c68a4cada..8b858bd44e 100644 --- a/console/src/assets/i18n/pt.json +++ b/console/src/assets/i18n/pt.json @@ -1643,8 +1643,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "O Logout de Back-Channel implementa o OpenID Connect Back-Channel Logout 1.0 e pode ser usado para notificar os clientes sobre a terminação da sessão no Provedor de OpenID.", "PERMISSIONCHECKV2": "Verificação de Permissão V2", "PERMISSIONCHECKV2_DESCRIPTION": "Se a bandeira estiver ativada, você poderá usar a nova API e seus recursos.", - "WEBKEY": "Chave Web", - "WEBKEY_DESCRIPTION": "Se a bandeira estiver ativada, você poderá usar a nova API e seus recursos.", "STATES": { "INHERITED": "Herdade", "ENABLED": "Habilitado", diff --git a/console/src/assets/i18n/ro.json b/console/src/assets/i18n/ro.json index 0e0802a17c..d2f51a81e0 100644 --- a/console/src/assets/i18n/ro.json +++ b/console/src/assets/i18n/ro.json @@ -1640,8 +1640,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Logout-ul Back-Channel implementează OpenID Connect Back-Channel Logout 1.0 și poate fi folosit pentru a notifica clienții despre terminarea sesiunii la Producătorul OpenID.", "PERMISSIONCHECKV2": "Verificare Permisiuni V2", "PERMISSIONCHECKV2_DESCRIPTION": "Dacă steagul este activat, veți putea folosi noua API și funcțiile sale.", - "WEBKEY": "Cheie Web", - "WEBKEY_DESCRIPTION": "Dacă steagul este activat, veți putea folosi noua API și funcțiile sale.", "STATES": { "INHERITED": "Moșteniți", "ENABLED": "Activat", diff --git a/console/src/assets/i18n/ru.json b/console/src/assets/i18n/ru.json index 8e06568a82..3070b311e7 100644 --- a/console/src/assets/i18n/ru.json +++ b/console/src/assets/i18n/ru.json @@ -1695,8 +1695,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout реализует OpenID Connect Back-Channel Logout 1.0 и может использоваться для уведомления клиентов о завершении сеанса у поставщика OpenID.", "PERMISSIONCHECKV2": "Проверка Разрешений V2", "PERMISSIONCHECKV2_DESCRIPTION": "Если флаг включен, вы сможете использовать новый API и его функции.", - "WEBKEY": "Веб-ключ", - "WEBKEY_DESCRIPTION": "Если флаг включен, вы сможете использовать новый API и его функции.", "STATES": { "INHERITED": "Наследовать", "ENABLED": "Включено", diff --git a/console/src/assets/i18n/sv.json b/console/src/assets/i18n/sv.json index 1b80021a67..8f03501054 100644 --- a/console/src/assets/i18n/sv.json +++ b/console/src/assets/i18n/sv.json @@ -1646,8 +1646,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementerar OpenID Connect Back-Channel Logout 1.0 och kan användas för att meddela klienter om sessionens avslutning hos OpenID-leverantören.", "PERMISSIONCHECKV2": "Behörighetskontroll V2", "PERMISSIONCHECKV2_DESCRIPTION": "Om flaggan är aktiverad kan du använda den nya API:n och dess funktioner.", - "WEBKEY": "Webbnyckel", - "WEBKEY_DESCRIPTION": "Om flaggan är aktiverad kan du använda den nya API:n och dess funktioner.", "STATES": { "INHERITED": "Ärv", "ENABLED": "Aktiverad", diff --git a/console/src/assets/i18n/zh.json b/console/src/assets/i18n/zh.json index 9565b61eca..0431405979 100644 --- a/console/src/assets/i18n/zh.json +++ b/console/src/assets/i18n/zh.json @@ -1642,8 +1642,6 @@ "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel 注销实现了 OpenID Connect Back-Channel Logout 1.0,可用于通知客户端在 OpenID 提供商处终止会话。", "PERMISSIONCHECKV2": "权限检查 V2", "PERMISSIONCHECKV2_DESCRIPTION": "如果启用该标志,您将能够使用新的 API 及其功能。", - "WEBKEY": "Web 密钥", - "WEBKEY_DESCRIPTION": "如果启用该标志,您将能够使用新的 API 及其功能。", "STATES": { "INHERITED": "继承", "ENABLED": "已启用", diff --git a/docs/docs/guides/integrate/login/oidc/webkeys.md b/docs/docs/guides/integrate/login/oidc/webkeys.md index 62f62a90e0..288284fefc 100644 --- a/docs/docs/guides/integrate/login/oidc/webkeys.md +++ b/docs/docs/guides/integrate/login/oidc/webkeys.md @@ -20,13 +20,6 @@ JWT access tokens, instead of [introspection](/docs/apis/openidoauth/endpoints#i ZITADEL uses public key verification when API calls are made or when the userInfo or introspection endpoints are called with a JWT access token. -:::info -Web keys are an [experimental](/docs/support/software-release-cycles-support#beta) feature. Be sure to enable the `web_key` [feature](/docs/apis/resources/feature_service_v2/feature-service-set-instance-features) before using it. - -The documentation describes the state of the feature in ZITADEL V3. -Test the feature and add improvement or bug reports directly to the [github repository](https://github.com/zitadel/zitadel) or let us know your general feedback in the [discord thread](https://discord.com/channels/927474939156643850/1329100936127320175/threads/1332344892629717075)! -::: - ### JSON Web Key ZITADEL implements the [RFC7517 - JSON Web Key (JWK)](https://www.rfc-editor.org/rfc/rfc7517) format for storage and distribution of public keys. diff --git a/internal/api/grpc/feature/v2/converter.go b/internal/api/grpc/feature/v2/converter.go index 56d3009457..1f0a3b21e7 100644 --- a/internal/api/grpc/feature/v2/converter.go +++ b/internal/api/grpc/feature/v2/converter.go @@ -58,7 +58,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com UserSchema: req.UserSchema, TokenExchange: req.OidcTokenExchange, ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), - WebKey: req.WebKey, DebugOIDCParentError: req.DebugOidcParentError, OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination, DisableUserTokenEvent: req.DisableUserTokenEvent, @@ -77,7 +76,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat UserSchema: featureSourceToFlagPb(&f.UserSchema), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), - WebKey: featureSourceToFlagPb(&f.WebKey), DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError), OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination), DisableUserTokenEvent: featureSourceToFlagPb(&f.DisableUserTokenEvent), diff --git a/internal/api/grpc/feature/v2/converter_test.go b/internal/api/grpc/feature/v2/converter_test.go index b77ed438f5..d09f1839ba 100644 --- a/internal/api/grpc/feature/v2/converter_test.go +++ b/internal/api/grpc/feature/v2/converter_test.go @@ -153,7 +153,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true), ImprovedPerformance: nil, - WebKey: gu.Ptr(true), DebugOidcParentError: gu.Ptr(true), OidcSingleV1SessionTermination: gu.Ptr(true), EnableBackChannelLogout: gu.Ptr(true), @@ -169,7 +168,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), TokenExchange: gu.Ptr(true), ImprovedPerformance: nil, - WebKey: gu.Ptr(true), DebugOIDCParentError: gu.Ptr(true), OIDCSingleV1SessionTermination: gu.Ptr(true), EnableBackChannelLogout: gu.Ptr(true), @@ -211,10 +209,6 @@ func Test_instanceFeaturesToPb(t *testing.T) { Level: feature.LevelSystem, Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, }, - WebKey: query.FeatureSource[bool]{ - Level: feature.LevelInstance, - Value: true, - }, OIDCSingleV1SessionTermination: query.FeatureSource[bool]{ Level: feature.LevelInstance, Value: true, @@ -265,10 +259,6 @@ func Test_instanceFeaturesToPb(t *testing.T) { ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, Source: feature_pb.Source_SOURCE_SYSTEM, }, - WebKey: &feature_pb.FeatureFlag{ - Enabled: true, - Source: feature_pb.Source_SOURCE_INSTANCE, - }, DebugOidcParentError: &feature_pb.FeatureFlag{ Enabled: false, Source: feature_pb.Source_SOURCE_UNSPECIFIED, diff --git a/internal/api/grpc/feature/v2beta/converter.go b/internal/api/grpc/feature/v2beta/converter.go index 406146fdbe..8927b16e29 100644 --- a/internal/api/grpc/feature/v2beta/converter.go +++ b/internal/api/grpc/feature/v2beta/converter.go @@ -38,7 +38,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm UserSchema: req.UserSchema, TokenExchange: req.OidcTokenExchange, ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), - WebKey: req.WebKey, DebugOIDCParentError: req.DebugOidcParentError, OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination, } @@ -52,7 +51,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat UserSchema: featureSourceToFlagPb(&f.UserSchema), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), - WebKey: featureSourceToFlagPb(&f.WebKey), DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError), OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination), } diff --git a/internal/api/grpc/feature/v2beta/converter_test.go b/internal/api/grpc/feature/v2beta/converter_test.go index 2395574733..5fdb5e993e 100644 --- a/internal/api/grpc/feature/v2beta/converter_test.go +++ b/internal/api/grpc/feature/v2beta/converter_test.go @@ -111,7 +111,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true), ImprovedPerformance: nil, - WebKey: gu.Ptr(true), OidcSingleV1SessionTermination: gu.Ptr(true), } want := &command.InstanceFeatures{ @@ -120,7 +119,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) { UserSchema: gu.Ptr(true), TokenExchange: gu.Ptr(true), ImprovedPerformance: nil, - WebKey: gu.Ptr(true), OIDCSingleV1SessionTermination: gu.Ptr(true), } got := instanceFeaturesToCommand(arg) @@ -154,10 +152,6 @@ func Test_instanceFeaturesToPb(t *testing.T) { Level: feature.LevelSystem, Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, }, - WebKey: query.FeatureSource[bool]{ - Level: feature.LevelInstance, - Value: true, - }, OIDCSingleV1SessionTermination: query.FeatureSource[bool]{ Level: feature.LevelInstance, Value: true, @@ -189,10 +183,6 @@ func Test_instanceFeaturesToPb(t *testing.T) { ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, Source: feature_pb.Source_SOURCE_SYSTEM, }, - WebKey: &feature_pb.FeatureFlag{ - Enabled: true, - Source: feature_pb.Source_SOURCE_INSTANCE, - }, DebugOidcParentError: &feature_pb.FeatureFlag{ Enabled: false, Source: feature_pb.Source_SOURCE_UNSPECIFIED, diff --git a/internal/api/grpc/webkey/v2beta/integration_test/webkey_integration_test.go b/internal/api/grpc/webkey/v2beta/integration_test/webkey_integration_test.go index 002669c233..0cbf629b43 100644 --- a/internal/api/grpc/webkey/v2beta/integration_test/webkey_integration_test.go +++ b/internal/api/grpc/webkey/v2beta/integration_test/webkey_integration_test.go @@ -12,11 +12,9 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta" ) @@ -33,34 +31,8 @@ func TestMain(m *testing.M) { }()) } -func TestServer_Feature_Disabled(t *testing.T) { - instance, iamCtx, _ := createInstance(t, false) - client := instance.Client.WebKeyV2Beta - - t.Run("CreateWebKey", func(t *testing.T) { - _, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{}) - assertFeatureDisabledError(t, err) - }) - t.Run("ActivateWebKey", func(t *testing.T) { - _, err := client.ActivateWebKey(iamCtx, &webkey.ActivateWebKeyRequest{ - Id: "1", - }) - assertFeatureDisabledError(t, err) - }) - t.Run("DeleteWebKey", func(t *testing.T) { - _, err := client.DeleteWebKey(iamCtx, &webkey.DeleteWebKeyRequest{ - Id: "1", - }) - assertFeatureDisabledError(t, err) - }) - t.Run("ListWebKeys", func(t *testing.T) { - _, err := client.ListWebKeys(iamCtx, &webkey.ListWebKeysRequest{}) - assertFeatureDisabledError(t, err) - }) -} - func TestServer_ListWebKeys(t *testing.T) { - instance, iamCtx, creationDate := createInstance(t, true) + instance, iamCtx, creationDate := createInstance(t) // After the feature is first enabled, we can expect 2 generated keys with the default config. checkWebKeyListState(iamCtx, t, instance, 2, "", &webkey.WebKey_Rsa{ Rsa: &webkey.RSA{ @@ -71,7 +43,7 @@ func TestServer_ListWebKeys(t *testing.T) { } func TestServer_CreateWebKey(t *testing.T) { - instance, iamCtx, creationDate := createInstance(t, true) + instance, iamCtx, creationDate := createInstance(t) client := instance.Client.WebKeyV2Beta _, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{ @@ -93,7 +65,7 @@ func TestServer_CreateWebKey(t *testing.T) { } func TestServer_ActivateWebKey(t *testing.T) { - instance, iamCtx, creationDate := createInstance(t, true) + instance, iamCtx, creationDate := createInstance(t) client := instance.Client.WebKeyV2Beta resp, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{ @@ -120,7 +92,7 @@ func TestServer_ActivateWebKey(t *testing.T) { } func TestServer_DeleteWebKey(t *testing.T) { - instance, iamCtx, creationDate := createInstance(t, true) + instance, iamCtx, creationDate := createInstance(t) client := instance.Client.WebKeyV2Beta keyIDs := make([]string, 2) @@ -197,40 +169,22 @@ func TestServer_DeleteWebKey(t *testing.T) { }, creationDate) } -func createInstance(t *testing.T, enableFeature bool) (*integration.Instance, context.Context, *timestamppb.Timestamp) { +func createInstance(t *testing.T) (*integration.Instance, context.Context, *timestamppb.Timestamp) { instance := integration.NewInstance(CTX) creationDate := timestamppb.Now() iamCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner) - if enableFeature { - _, err := instance.Client.FeatureV2.SetInstanceFeatures(iamCTX, &feature.SetInstanceFeaturesRequest{ - WebKey: proto.Bool(true), - }) - require.NoError(t, err) - } - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamCTX, time.Minute) assert.EventuallyWithT(t, func(collect *assert.CollectT) { resp, err := instance.Client.WebKeyV2Beta.ListWebKeys(iamCTX, &webkey.ListWebKeysRequest{}) - if enableFeature { - assert.NoError(collect, err) - assert.Len(collect, resp.GetWebKeys(), 2) - } else { - assert.Error(collect, err) - } + assert.NoError(collect, err) + assert.Len(collect, resp.GetWebKeys(), 2) + }, retryDuration, tick) return instance, iamCTX, creationDate } -func assertFeatureDisabledError(t *testing.T, err error) { - t.Helper() - require.Error(t, err) - s := status.Convert(err) - assert.Equal(t, codes.FailedPrecondition, s.Code()) - assert.Contains(t, s.Message(), "WEBKEY-Ohx6E") -} - func checkWebKeyListState(ctx context.Context, t *testing.T, instance *integration.Instance, nKeys int, expectActiveKeyID string, config any, creationDate *timestamppb.Timestamp) { t.Helper() diff --git a/internal/api/grpc/webkey/v2beta/webkey.go b/internal/api/grpc/webkey/v2beta/webkey.go index d45288dff2..469d6fc9a6 100644 --- a/internal/api/grpc/webkey/v2beta/webkey.go +++ b/internal/api/grpc/webkey/v2beta/webkey.go @@ -5,9 +5,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/telemetry/tracing" - "github.com/zitadel/zitadel/internal/zerrors" webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta" ) @@ -15,9 +13,6 @@ func (s *Server) CreateWebKey(ctx context.Context, req *webkey.CreateWebKeyReque ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if err = checkWebKeyFeature(ctx); err != nil { - return nil, err - } webKey, err := s.command.CreateWebKey(ctx, createWebKeyRequestToConfig(req)) if err != nil { return nil, err @@ -33,9 +28,6 @@ func (s *Server) ActivateWebKey(ctx context.Context, req *webkey.ActivateWebKeyR ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if err = checkWebKeyFeature(ctx); err != nil { - return nil, err - } details, err := s.command.ActivateWebKey(ctx, req.GetId()) if err != nil { return nil, err @@ -50,9 +42,6 @@ func (s *Server) DeleteWebKey(ctx context.Context, req *webkey.DeleteWebKeyReque ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if err = checkWebKeyFeature(ctx); err != nil { - return nil, err - } deletedAt, err := s.command.DeleteWebKey(ctx, req.GetId()) if err != nil { return nil, err @@ -71,9 +60,6 @@ func (s *Server) ListWebKeys(ctx context.Context, _ *webkey.ListWebKeysRequest) ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if err = checkWebKeyFeature(ctx); err != nil { - return nil, err - } list, err := s.query.ListWebKeys(ctx) if err != nil { return nil, err @@ -83,10 +69,3 @@ func (s *Server) ListWebKeys(ctx context.Context, _ *webkey.ListWebKeysRequest) WebKeys: webKeyDetailsListToPb(list), }, nil } - -func checkWebKeyFeature(ctx context.Context) error { - if !authz.GetFeatures(ctx).WebKey { - return zerrors.ThrowPreconditionFailed(nil, "WEBKEY-Ohx6E", "Errors.WebKey.FeatureDisabled") - } - return nil -} diff --git a/internal/api/oidc/access_token.go b/internal/api/oidc/access_token.go index 2f2880efc2..5c0b9c9f66 100644 --- a/internal/api/oidc/access_token.go +++ b/internal/api/oidc/access_token.go @@ -53,7 +53,7 @@ func (s *Server) verifyAccessToken(ctx context.Context, tkn string) (_ *accessTo tokenID, subject = split[0], split[1] } else { verifier := op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), s.accessTokenKeySet, - op.WithSupportedAccessTokenSigningAlgorithms(supportedSigningAlgs(ctx)...), + op.WithSupportedAccessTokenSigningAlgorithms(supportedSigningAlgs()...), ) claims, err := op.VerifyAccessToken[*oidc.AccessTokenClaims](ctx, tkn, verifier) if err != nil { diff --git a/internal/api/oidc/auth_request_converter.go b/internal/api/oidc/auth_request_converter.go index 2144ca8ba1..064af20de0 100644 --- a/internal/api/oidc/auth_request_converter.go +++ b/internal/api/oidc/auth_request_converter.go @@ -140,13 +140,8 @@ func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string) if !ok { return } - if agents, ok := ctxHeaders[http_utils.UserAgentHeader]; ok { - userAgent = agents[0] - } - if langs, ok := ctxHeaders[http_utils.AcceptLanguage]; ok { - acceptLang = langs[0] - } - return userAgent, acceptLang + return ctxHeaders.Get(http_utils.UserAgentHeader), + ctxHeaders.Get(http_utils.AcceptLanguage) } func IpFromContext(ctx context.Context) net.IP { diff --git a/internal/api/oidc/integration_test/keys_test.go b/internal/api/oidc/integration_test/keys_test.go index 8b66e980d0..a6223cf1ee 100644 --- a/internal/api/oidc/integration_test/keys_test.go +++ b/internal/api/oidc/integration_test/keys_test.go @@ -14,12 +14,10 @@ import ( "github.com/stretchr/testify/require" "github.com/zitadel/oidc/v3/pkg/client" "github.com/zitadel/oidc/v3/pkg/oidc" - "google.golang.org/protobuf/proto" http_util "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2" ) @@ -53,25 +51,16 @@ func TestServer_Keys(t *testing.T) { require.NoError(t, err) tests := []struct { - name string - webKeyFeature bool - wantLen int + name string + wantLen int }{ { - name: "legacy only", - webKeyFeature: false, - wantLen: 1, - }, - { - name: "webkeys with legacy", - webKeyFeature: true, - wantLen: 3, // 1 legacy + 2 created by enabling feature flag + name: "webkeys", + wantLen: 2, // 2 from instance creation. }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ensureWebKeyFeature(t, instance, tt.webKeyFeature) - assert.EventuallyWithT(t, func(ttt *assert.CollectT) { resp, err := http.Get(discovery.JwksURI) require.NoError(ttt, err) @@ -92,30 +81,10 @@ func TestServer_Keys(t *testing.T) { } cacheControl := resp.Header.Get("cache-control") - if tt.webKeyFeature { - require.Equal(ttt, "max-age=300, must-revalidate", cacheControl) - return - } - require.Equal(ttt, "no-store", cacheControl) + require.Equal(ttt, "max-age=300, must-revalidate", cacheControl) }, time.Minute, time.Second/10) }) } } - -func ensureWebKeyFeature(t *testing.T, instance *integration.Instance, set bool) { - ctxIam := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner) - - _, err := instance.Client.FeatureV2.SetInstanceFeatures(ctxIam, &feature.SetInstanceFeaturesRequest{ - WebKey: proto.Bool(set), - }) - require.NoError(t, err) - - t.Cleanup(func() { - _, err := instance.Client.FeatureV2.SetInstanceFeatures(ctxIam, &feature.SetInstanceFeaturesRequest{ - WebKey: proto.Bool(false), - }) - require.NoError(t, err) - }) -} diff --git a/internal/api/oidc/integration_test/userinfo_test.go b/internal/api/oidc/integration_test/userinfo_test.go index bf201b242e..b3bc836343 100644 --- a/internal/api/oidc/integration_test/userinfo_test.go +++ b/internal/api/oidc/integration_test/userinfo_test.go @@ -35,21 +35,14 @@ func TestServer_UserInfo(t *testing.T) { tests := []struct { name string trigger bool - webKey bool }{ { name: "trigger enabled", trigger: true, }, - - // This is the only functional test we need to cover web keys. - // - By creating tokens the signer is tested - // - When obtaining the tokens, the RP verifies the ID Token using the key set from the jwks endpoint. - // - By calling userinfo with the access token as JWT, the Token Verifier with the public key cache is tested. { - name: "web keys", + name: "trigger disabled", trigger: false, - webKey: true, }, } @@ -57,7 +50,6 @@ func TestServer_UserInfo(t *testing.T) { t.Run(tt.name, func(t *testing.T) { _, err := Instance.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{ OidcTriggerIntrospectionProjections: &tt.trigger, - WebKey: &tt.webKey, }) require.NoError(t, err) testServer_UserInfo(t) diff --git a/internal/api/oidc/key.go b/internal/api/oidc/key.go index 852bbc7db8..61f874664f 100644 --- a/internal/api/oidc/key.go +++ b/internal/api/oidc/key.go @@ -10,18 +10,12 @@ import ( "github.com/go-jose/go-jose/v4" "github.com/jonboulle/clockwork" - "github.com/muhlemmer/gu" - "github.com/shopspring/decimal" - "github.com/zitadel/logging" "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_util "github.com/zitadel/zitadel/internal/api/http" "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" - "github.com/zitadel/zitadel/internal/repository/instance" - "github.com/zitadel/zitadel/internal/repository/keypair" "github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -36,11 +30,8 @@ var supportedWebKeyAlgs = []string{ string(jose.ES512), } -func supportedSigningAlgs(ctx context.Context) []string { - if authz.GetFeatures(ctx).WebKey { - return supportedWebKeyAlgs - } - return []string{string(jose.RS256)} +func supportedSigningAlgs() []string { + return supportedWebKeyAlgs } type cachedPublicKey struct { @@ -211,15 +202,6 @@ func withKeyExpiryCheck(check bool) keySetOption { } } -func jsonWebkey(key query.PublicKey) *jose.JSONWebKey { - return &jose.JSONWebKey{ - KeyID: key.ID(), - Algorithm: key.Algorithm(), - Use: key.Use().String(), - Key: key.Key(), - } -} - // keySetMap is a mapping of key IDs to public key data. type keySetMap map[string][]byte @@ -250,7 +232,6 @@ func (k keySetMap) VerifySignature(ctx context.Context, jws *jose.JSONWebSignatu } const ( - locksTable = "projections.locks" signingKey = "signing_key" oidcUser = "OIDC" @@ -279,203 +260,36 @@ func (s *SigningKey) ID() string { return s.id } -// PublicKey wraps the query.PublicKey to implement the op.Key interface -type PublicKey struct { - key query.PublicKey -} - -func (s *PublicKey) Algorithm() jose.SignatureAlgorithm { - return jose.SignatureAlgorithm(s.key.Algorithm()) -} - -func (s *PublicKey) Use() string { - return s.key.Use().String() -} - -func (s *PublicKey) Key() interface{} { - return s.key.Key() -} - -func (s *PublicKey) ID() string { - return s.key.ID() -} - // KeySet implements the op.Storage interface func (o *OPStorage) KeySet(ctx context.Context) (keys []op.Key, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - err = retry(func() error { - publicKeys, err := o.query.ActivePublicKeys(ctx, time.Now()) - if err != nil { - return err - } - keys = make([]op.Key, len(publicKeys.Keys)) - for i, key := range publicKeys.Keys { - keys[i] = &PublicKey{key} - } - return nil - }) - return keys, err + panic(o.panicErr("KeySet")) } // SignatureAlgorithms implements the op.Storage interface func (o *OPStorage) SignatureAlgorithms(ctx context.Context) ([]jose.SignatureAlgorithm, error) { - key, err := o.SigningKey(ctx) - if err != nil { - logging.WithError(err).Warn("unable to fetch signing key") - return nil, err - } - return []jose.SignatureAlgorithm{key.SignatureAlgorithm()}, nil + panic(o.panicErr("SignatureAlgorithms")) } // SigningKey implements the op.Storage interface func (o *OPStorage) SigningKey(ctx context.Context) (key op.SigningKey, err error) { - err = retry(func() error { - key, err = o.getSigningKey(ctx) - if err != nil { - return err - } - if key == nil { - return zerrors.ThrowNotFound(nil, "OIDC-ve4Qu", "Errors.Internal") - } - return nil - }) - return key, err -} - -func (o *OPStorage) getSigningKey(ctx context.Context) (op.SigningKey, error) { - keys, err := o.query.ActivePrivateSigningKey(ctx, time.Now().Add(gracefulPeriod)) - if err != nil { - return nil, err - } - if len(keys.Keys) > 0 { - return PrivateKeyToSigningKey(SelectSigningKey(keys.Keys), o.encAlg) - } - var position decimal.Decimal - if keys.State != nil { - position = keys.State.Position - } - return nil, o.refreshSigningKey(ctx, position) -} - -func (o *OPStorage) refreshSigningKey(ctx context.Context, position decimal.Decimal) error { - ok, err := o.ensureIsLatestKey(ctx, position) - if err != nil || !ok { - return zerrors.ThrowInternal(err, "OIDC-ASfh3", "cannot ensure that projection is up to date") - } - err = o.lockAndGenerateSigningKeyPair(ctx) - if err != nil { - return zerrors.ThrowInternal(err, "OIDC-ADh31", "could not create signing key") - } - return zerrors.ThrowInternal(nil, "OIDC-Df1bh", "") -} - -func (o *OPStorage) ensureIsLatestKey(ctx context.Context, position decimal.Decimal) (bool, error) { - maxSequence, err := o.getMaxKeyPosition(ctx) - if err != nil { - return false, fmt.Errorf("error retrieving new events: %w", err) - } - return position.GreaterThanOrEqual(maxSequence), nil -} - -func PrivateKeyToSigningKey(key query.PrivateKey, algorithm crypto.EncryptionAlgorithm) (_ op.SigningKey, err error) { - keyData, err := crypto.Decrypt(key.Key(), algorithm) - if err != nil { - return nil, err - } - privateKey, err := crypto.BytesToPrivateKey(keyData) - if err != nil { - return nil, err - } - return &SigningKey{ - algorithm: jose.SignatureAlgorithm(key.Algorithm()), - key: privateKey, - id: key.ID(), - }, nil -} - -func (o *OPStorage) lockAndGenerateSigningKeyPair(ctx context.Context) error { - logging.Info("lock and generate signing key pair") - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - errs := o.locker.Lock(ctx, lockDuration, authz.GetInstance(ctx).InstanceID()) - err, ok := <-errs - if err != nil || !ok { - if zerrors.IsErrorAlreadyExists(err) { - return nil - } - logging.OnError(err).Debug("initial lock failed") - return err - } - - return o.command.GenerateSigningKeyPair(setOIDCCtx(ctx), "RS256") -} - -func (o *OPStorage) getMaxKeyPosition(ctx context.Context) (decimal.Decimal, error) { - return o.eventstore.LatestPosition(ctx, - eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition). - ResourceOwner(authz.GetInstance(ctx).InstanceID()). - AwaitOpenTransactions(). - AddQuery(). - AggregateTypes( - keypair.AggregateType, - instance.AggregateType, - ). - EventTypes( - keypair.AddedEventType, - instance.InstanceRemovedEventType, - ). - Builder(), - ) -} - -func SelectSigningKey(keys []query.PrivateKey) query.PrivateKey { - return keys[len(keys)-1] -} - -func setOIDCCtx(ctx context.Context) context.Context { - return authz.SetCtxData(ctx, authz.CtxData{UserID: oidcUser, OrgID: authz.GetInstance(ctx).InstanceID()}) -} - -func retry(retryable func() error) (err error) { - for i := 0; i < retryCount; i++ { - err = retryable() - if err == nil { - return nil - } - time.Sleep(retryBackoff) - } - return err + panic(o.panicErr("SigningKey")) } func (s *Server) Keys(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if !authz.GetFeatures(ctx).WebKey { - return s.LegacyServer.Keys(ctx, r) - } - keyset, err := s.query.GetWebKeySet(ctx) if err != nil { return nil, err } - // Return legacy keys, so we do not invalidate all tokens - // once the feature flag is enabled. - legacyKeys, err := s.query.ActivePublicKeys(ctx, time.Now()) - logging.OnError(err).Error("oidc server: active public keys (legacy)") - appendPublicKeysToWebKeySet(keyset, legacyKeys) - resp := op.NewResponse(keyset) if s.jwksCacheControlMaxAge != 0 { resp.Header.Set(http_util.CacheControl, fmt.Sprintf("max-age=%d, must-revalidate", int(s.jwksCacheControlMaxAge/time.Second)), ) } - return resp, nil } @@ -497,20 +311,10 @@ func appendPublicKeysToWebKeySet(keyset *jose.JSONWebKeySet, pubkeys *query.Publ func queryKeyFunc(q *query.Queries) func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) { return func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) { - if authz.GetFeatures(ctx).WebKey { - webKey, err := q.GetPublicWebKeyByID(ctx, keyID) - if err == nil { - return webKey, nil, nil - } - if !zerrors.IsNotFound(err) { - return nil, nil, err - } - } - - pubKey, err := q.GetPublicKeyByID(ctx, keyID) + webKey, err := q.GetPublicWebKeyByID(ctx, keyID) if err != nil { return nil, nil, err } - return jsonWebkey(pubKey), gu.Ptr(pubKey.Expiry()), nil + return webKey, nil, nil } } diff --git a/internal/api/oidc/op.go b/internal/api/oidc/op.go index d7171b957b..6f59ce3525 100644 --- a/internal/api/oidc/op.go +++ b/internal/api/oidc/op.go @@ -18,10 +18,8 @@ import ( "github.com/zitadel/zitadel/internal/cache" "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain/federatedlogout" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/handler/crdb" "github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/telemetry/metrics" "github.com/zitadel/zitadel/internal/zerrors" @@ -75,7 +73,6 @@ type OPStorage struct { defaultRefreshTokenIdleExpiration time.Duration defaultRefreshTokenExpiration time.Duration encAlg crypto.EncryptionAlgorithm - locker crdb.Locker assetAPIPrefix func(ctx context.Context) string contextToIssuer func(context.Context) string federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout] @@ -91,14 +88,14 @@ type Provider struct { // IDTokenHintVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context. func (o *Provider) IDTokenHintVerifier(ctx context.Context) *op.IDTokenHintVerifier { return op.NewIDTokenHintVerifier(op.IssuerFromContext(ctx), o.idTokenHintKeySet, op.WithSupportedIDTokenHintSigningAlgorithms( - supportedSigningAlgs(ctx)..., + supportedSigningAlgs()..., )) } // AccessTokenVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context. func (o *Provider) AccessTokenVerifier(ctx context.Context) *op.AccessTokenVerifier { return op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), o.accessTokenKeySet, op.WithSupportedAccessTokenSigningAlgorithms( - supportedSigningAlgs(ctx)..., + supportedSigningAlgs()..., )) } @@ -113,7 +110,6 @@ func NewServer( encryptionAlg crypto.EncryptionAlgorithm, cryptoKey []byte, es *eventstore.Eventstore, - projections *database.DB, userAgentCookie, instanceHandler func(http.Handler) http.Handler, accessHandler *middleware.AccessInterceptor, fallbackLogger *slog.Logger, @@ -124,7 +120,7 @@ func NewServer( if err != nil { return nil, zerrors.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w") } - storage := newStorage(config, command, query, repo, encryptionAlg, es, projections, ContextToIssuer, federatedLogoutCache) + storage := newStorage(config, command, query, repo, encryptionAlg, es, ContextToIssuer, federatedLogoutCache) keyCache := newPublicKeyCache(ctx, config.PublicKeyCacheMaxAge, queryKeyFunc(query)) accessTokenKeySet := newOidcKeySet(keyCache, withKeyExpiryCheck(true)) idTokenHintKeySet := newOidcKeySet(keyCache) @@ -236,7 +232,6 @@ func newStorage( repo repository.Repository, encAlg crypto.EncryptionAlgorithm, es *eventstore.Eventstore, - db *database.DB, contextToIssuer func(context.Context) string, federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout], ) *OPStorage { @@ -253,7 +248,6 @@ func newStorage( defaultRefreshTokenIdleExpiration: config.DefaultRefreshTokenIdleExpiration, defaultRefreshTokenExpiration: config.DefaultRefreshTokenExpiration, encAlg: encAlg, - locker: crdb.NewLocker(db.DB, locksTable, signingKey), assetAPIPrefix: assets.AssetAPI(), contextToIssuer: contextToIssuer, federateLogoutCache: federateLogoutCache, diff --git a/internal/api/oidc/server.go b/internal/api/oidc/server.go index 1a0854e2a6..df7127443f 100644 --- a/internal/api/oidc/server.go +++ b/internal/api/oidc/server.go @@ -188,7 +188,7 @@ func (s *Server) createDiscoveryConfig(ctx context.Context, supportedUILocales o }, GrantTypesSupported: op.GrantTypes(s.Provider()), SubjectTypesSupported: op.SubjectTypes(s.Provider()), - IDTokenSigningAlgValuesSupported: supportedSigningAlgs(ctx), + IDTokenSigningAlgValuesSupported: supportedSigningAlgs(), RequestObjectSigningAlgValuesSupported: op.RequestObjectSigAlgorithms(s.Provider()), TokenEndpointAuthMethodsSupported: op.AuthMethodsTokenEndpoint(s.Provider()), TokenEndpointAuthSigningAlgValuesSupported: op.TokenSigAlgorithms(s.Provider()), diff --git a/internal/api/oidc/server_test.go b/internal/api/oidc/server_test.go index 76d073151a..9bf22fd210 100644 --- a/internal/api/oidc/server_test.go +++ b/internal/api/oidc/server_test.go @@ -8,9 +8,6 @@ import ( "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/feature" ) func TestServer_createDiscoveryConfig(t *testing.T) { @@ -63,92 +60,6 @@ func TestServer_createDiscoveryConfig(t *testing.T) { ctx: op.ContextWithIssuer(context.Background(), "https://issuer.com"), supportedUILocales: []language.Tag{language.English, language.German}, }, - &oidc.DiscoveryConfiguration{ - Issuer: "https://issuer.com", - AuthorizationEndpoint: "https://issuer.com/auth", - TokenEndpoint: "https://issuer.com/token", - IntrospectionEndpoint: "https://issuer.com/introspect", - UserinfoEndpoint: "https://issuer.com/userinfo", - RevocationEndpoint: "https://issuer.com/revoke", - EndSessionEndpoint: "https://issuer.com/logout", - DeviceAuthorizationEndpoint: "https://issuer.com/device", - CheckSessionIframe: "", - JwksURI: "https://issuer.com/keys", - RegistrationEndpoint: "", - ScopesSupported: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopePhone, oidc.ScopeAddress, oidc.ScopeOfflineAccess}, - ResponseTypesSupported: []string{string(oidc.ResponseTypeCode), string(oidc.ResponseTypeIDTokenOnly), string(oidc.ResponseTypeIDToken)}, - ResponseModesSupported: []string{string(oidc.ResponseModeQuery), string(oidc.ResponseModeFragment), string(oidc.ResponseModeFormPost)}, - GrantTypesSupported: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeBearer}, - ACRValuesSupported: nil, - SubjectTypesSupported: []string{"public"}, - IDTokenSigningAlgValuesSupported: []string{"RS256"}, - IDTokenEncryptionAlgValuesSupported: nil, - IDTokenEncryptionEncValuesSupported: nil, - UserinfoSigningAlgValuesSupported: nil, - UserinfoEncryptionAlgValuesSupported: nil, - UserinfoEncryptionEncValuesSupported: nil, - RequestObjectSigningAlgValuesSupported: []string{"RS256"}, - RequestObjectEncryptionAlgValuesSupported: nil, - RequestObjectEncryptionEncValuesSupported: nil, - TokenEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT}, - TokenEndpointAuthSigningAlgValuesSupported: []string{"RS256"}, - RevocationEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT}, - RevocationEndpointAuthSigningAlgValuesSupported: []string{"RS256"}, - IntrospectionEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodBasic, oidc.AuthMethodPrivateKeyJWT}, - IntrospectionEndpointAuthSigningAlgValuesSupported: []string{"RS256"}, - DisplayValuesSupported: nil, - ClaimTypesSupported: nil, - ClaimsSupported: []string{"sub", "aud", "exp", "iat", "iss", "auth_time", "nonce", "acr", "amr", "c_hash", "at_hash", "act", "scopes", "client_id", "azp", "preferred_username", "name", "family_name", "given_name", "locale", "email", "email_verified", "phone_number", "phone_number_verified"}, - ClaimsParameterSupported: false, - CodeChallengeMethodsSupported: []oidc.CodeChallengeMethod{"S256"}, - ServiceDocumentation: "", - ClaimsLocalesSupported: nil, - UILocalesSupported: []language.Tag{language.English, language.German}, - RequestParameterSupported: true, - RequestURIParameterSupported: false, - RequireRequestURIRegistration: false, - OPPolicyURI: "", - OPTermsOfServiceURI: "", - }, - }, - { - "web keys feature enabled", - fields{ - LegacyServer: op.NewLegacyServer( - func() *op.Provider { - //nolint:staticcheck - provider, _ := op.NewForwardedOpenIDProvider("path", - &op.Config{ - CodeMethodS256: true, - AuthMethodPost: true, - AuthMethodPrivateKeyJWT: true, - GrantTypeRefreshToken: true, - RequestObjectSupported: true, - }, - nil, - ) - return provider - }(), - op.Endpoints{ - Authorization: op.NewEndpoint("auth"), - Token: op.NewEndpoint("token"), - Introspection: op.NewEndpoint("introspect"), - Userinfo: op.NewEndpoint("userinfo"), - Revocation: op.NewEndpoint("revoke"), - EndSession: op.NewEndpoint("logout"), - JwksURI: op.NewEndpoint("keys"), - DeviceAuthorization: op.NewEndpoint("device"), - }, - ), - signingKeyAlgorithm: "RS256", - }, - args{ - ctx: authz.WithFeatures( - op.ContextWithIssuer(context.Background(), "https://issuer.com"), - feature.Features{WebKey: true}, - ), - supportedUILocales: []language.Tag{language.English, language.German}, - }, &oidc.DiscoveryConfiguration{ Issuer: "https://issuer.com", AuthorizationEndpoint: "https://issuer.com/auth", diff --git a/internal/api/oidc/token.go b/internal/api/oidc/token.go index 485f455784..2efc0fb583 100644 --- a/internal/api/oidc/token.go +++ b/internal/api/oidc/token.go @@ -12,7 +12,6 @@ import ( "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/op" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/telemetry/tracing" @@ -64,14 +63,13 @@ func (s *Server) accessTokenResponseFromSession(ctx context.Context, client op.C type SignerFunc func(ctx context.Context) (jose.Signer, jose.SignatureAlgorithm, error) func (s *Server) getSignerOnce() SignerFunc { - return GetSignerOnce(s.query.GetActiveSigningWebKey, s.Provider().Storage().SigningKey) + return GetSignerOnce(s.query.GetActiveSigningWebKey) } // GetSignerOnce returns a function which retrieves the instance's signer from the database once. // Repeated calls of the returned function return the same results. func GetSignerOnce( getActiveSigningWebKey func(ctx context.Context) (*jose.JSONWebKey, error), - getSigningKey func(ctx context.Context) (op.SigningKey, error), ) SignerFunc { var ( once sync.Once @@ -84,23 +82,12 @@ func GetSignerOnce( ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - if authz.GetFeatures(ctx).WebKey { - var webKey *jose.JSONWebKey - webKey, err = getActiveSigningWebKey(ctx) - if err != nil { - return - } - signer, signAlg, err = signerFromWebKey(webKey) - return - } - - var signingKey op.SigningKey - signingKey, err = getSigningKey(ctx) + var webKey *jose.JSONWebKey + webKey, err = getActiveSigningWebKey(ctx) if err != nil { return } - signAlg = signingKey.SignatureAlgorithm() - signer, err = op.SignerFromKey(signingKey) + signer, signAlg, err = signerFromWebKey(webKey) }) return signer, signAlg, err } diff --git a/internal/api/saml/certificate.go b/internal/api/saml/certificate.go index 14752cd5cd..e0eb31255e 100644 --- a/internal/api/saml/certificate.go +++ b/internal/api/saml/certificate.go @@ -14,13 +14,14 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/keypair" "github.com/zitadel/zitadel/internal/zerrors" ) const ( - locksTable = "projections.locks" + locksTable = projection.LocksTable signingKey = "signing_key" samlUser = "SAML" diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index b707631c22..d6c14afea3 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "fmt" - "slices" "strings" "time" @@ -329,18 +328,10 @@ type openIDKeySet struct { // VerifySignature implements the oidc.KeySet interface // providing an implementation for the keys retrieved directly from Queries func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) { - keySet := new(jose.JSONWebKeySet) - if authz.GetFeatures(ctx).WebKey { - keySet, err = o.Queries.GetWebKeySet(ctx) - if err != nil { - return nil, err - } - } - legacyKeySet, err := o.Queries.ActivePublicKeys(ctx, time.Now()) + keySet, err := o.Queries.GetWebKeySet(ctx) if err != nil { - return nil, fmt.Errorf("error fetching keys: %w", err) + return nil, err } - appendPublicKeysToWebKeySet(keySet, legacyKeySet) keyID, alg := oidc.GetKeyIDAndAlg(jws) key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...) if err != nil { @@ -348,19 +339,3 @@ func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig } return jws.Verify(&key) } - -func appendPublicKeysToWebKeySet(keyset *jose.JSONWebKeySet, pubkeys *query.PublicKeys) { - if pubkeys == nil || len(pubkeys.Keys) == 0 { - return - } - keyset.Keys = slices.Grow(keyset.Keys, len(pubkeys.Keys)) - - for _, key := range pubkeys.Keys { - keyset.Keys = append(keyset.Keys, jose.JSONWebKey{ - Key: key.Key(), - KeyID: key.ID(), - Algorithm: key.Algorithm(), - Use: key.Use().String(), - }) - } -} diff --git a/internal/command/instance_features.go b/internal/command/instance_features.go index 4d35d5a318..21de5653a9 100644 --- a/internal/command/instance_features.go +++ b/internal/command/instance_features.go @@ -3,11 +3,8 @@ package command import ( "context" - "github.com/muhlemmer/gu" - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/feature" @@ -21,7 +18,6 @@ type InstanceFeatures struct { UserSchema *bool TokenExchange *bool ImprovedPerformance []feature.ImprovedPerformanceType - WebKey *bool DebugOIDCParentError *bool OIDCSingleV1SessionTermination *bool DisableUserTokenEvent *bool @@ -38,7 +34,6 @@ func (m *InstanceFeatures) isEmpty() bool { m.TokenExchange == nil && // nil check to allow unset improvements m.ImprovedPerformance == nil && - m.WebKey == nil && m.DebugOIDCParentError == nil && m.OIDCSingleV1SessionTermination == nil && m.DisableUserTokenEvent == nil && @@ -55,9 +50,6 @@ func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures) if err := c.eventstore.FilterToQueryReducer(ctx, wm); err != nil { return nil, err } - if err := c.setupWebKeyFeature(ctx, wm, f); err != nil { - return nil, err - } commands := wm.setCommands(ctx, f) if len(commands) == 0 { return writeModelToObjectDetails(wm.WriteModel), nil @@ -78,21 +70,6 @@ func prepareSetFeatures(instanceID string, f *InstanceFeatures) preparation.Vali } } -// setupWebKeyFeature generates the initial web keys for the instance, -// if the feature is enabled in the request and the feature wasn't enabled already in the writeModel. -// [Commands.GenerateInitialWebKeys] checks if keys already exist and does nothing if that's the case. -// The default config of a RSA key with 2048 and the SHA256 hasher is assumed. -// Users can customize this after using the webkey/v3 API. -func (c *Commands) setupWebKeyFeature(ctx context.Context, wm *InstanceFeaturesWriteModel, f *InstanceFeatures) error { - if !gu.Value(f.WebKey) || gu.Value(wm.WebKey) { - return nil - } - return c.GenerateInitialWebKeys(ctx, &crypto.WebKeyRSAConfig{ - Bits: crypto.RSABits2048, - Hasher: crypto.RSAHasherSHA256, - }) -} - func (c *Commands) ResetInstanceFeatures(ctx context.Context) (*domain.ObjectDetails, error) { instanceID := authz.GetInstance(ctx).InstanceID() wm := NewInstanceFeaturesWriteModel(instanceID) diff --git a/internal/command/instance_features_model.go b/internal/command/instance_features_model.go index 399013aded..8ca2865eae 100644 --- a/internal/command/instance_features_model.go +++ b/internal/command/instance_features_model.go @@ -71,7 +71,6 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder { feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceImprovedPerformanceEventType, - feature_v2.InstanceWebKeyEventType, feature_v2.InstanceDebugOIDCParentErrorEventType, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, feature_v2.InstanceDisableUserTokenEvent, @@ -106,9 +105,6 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an case feature.KeyImprovedPerformance: v := value.([]feature.ImprovedPerformanceType) features.ImprovedPerformance = v - case feature.KeyWebKey: - v := value.(bool) - features.WebKey = &v case feature.KeyDebugOIDCParentError: v := value.(bool) features.DebugOIDCParentError = &v @@ -140,7 +136,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.InstanceTokenExchangeEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType) cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType) - cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.WebKey, f.WebKey, feature_v2.InstanceWebKeyEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DebugOIDCParentError, f.DebugOIDCParentError, feature_v2.InstanceDebugOIDCParentErrorEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DisableUserTokenEvent, f.DisableUserTokenEvent, feature_v2.InstanceDisableUserTokenEvent) diff --git a/internal/command/key_pair.go b/internal/command/key_pair.go index 90eaf7e3da..76193431d6 100644 --- a/internal/command/key_pair.go +++ b/internal/command/key_pair.go @@ -13,31 +13,6 @@ import ( "github.com/zitadel/zitadel/internal/repository/keypair" ) -func (c *Commands) GenerateSigningKeyPair(ctx context.Context, algorithm string) error { - privateCrypto, publicCrypto, err := crypto.GenerateEncryptedKeyPair(c.keySize, c.keyAlgorithm) - if err != nil { - return err - } - keyID, err := c.idGenerator.Next() - if err != nil { - return err - } - - privateKeyExp := time.Now().UTC().Add(c.privateKeyLifetime) - publicKeyExp := time.Now().UTC().Add(c.publicKeyLifetime) - - keyPairWriteModel := NewKeyPairWriteModel(keyID, authz.GetInstance(ctx).InstanceID()) - keyAgg := KeyPairAggregateFromWriteModel(&keyPairWriteModel.WriteModel) - _, err = c.eventstore.Push(ctx, keypair.NewAddedEvent( - ctx, - keyAgg, - crypto.KeyUsageSigning, - algorithm, - privateCrypto, publicCrypto, - privateKeyExp, publicKeyExp)) - return err -} - func (c *Commands) GenerateSAMLCACertificate(ctx context.Context, algorithm string) error { now := time.Now().UTC() after := now.Add(c.certificateLifetime) diff --git a/internal/crypto/rsa.go b/internal/crypto/rsa.go index 198610d8aa..3fd9a77569 100644 --- a/internal/crypto/rsa.go +++ b/internal/crypto/rsa.go @@ -21,14 +21,6 @@ func GenerateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) { return privkey, &privkey.PublicKey, nil } -func GenerateEncryptedKeyPair(bits int, alg EncryptionAlgorithm) (*CryptoValue, *CryptoValue, error) { - privateKey, publicKey, err := GenerateKeyPair(bits) - if err != nil { - return nil, nil, err - } - return EncryptKeys(privateKey, publicKey, alg) -} - type CertificateInformations struct { SerialNumber *big.Int Organisation []string diff --git a/internal/feature/feature.go b/internal/feature/feature.go index 2e32b6b122..107b06edf1 100644 --- a/internal/feature/feature.go +++ b/internal/feature/feature.go @@ -9,7 +9,7 @@ import ( type Key int const ( - // Reserved: 3, 6 + // Reserved: 3, 6, 8 KeyUnspecified Key = 0 KeyLoginDefaultOrg Key = 1 @@ -17,7 +17,6 @@ const ( KeyUserSchema Key = 4 KeyTokenExchange Key = 5 KeyImprovedPerformance Key = 7 - KeyWebKey Key = 8 KeyDebugOIDCParentError Key = 9 KeyOIDCSingleV1SessionTermination Key = 10 KeyDisableUserTokenEvent Key = 11 @@ -46,7 +45,6 @@ type Features struct { UserSchema bool `json:"user_schema,omitempty"` TokenExchange bool `json:"token_exchange,omitempty"` ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"` - WebKey bool `json:"web_key,omitempty"` DebugOIDCParentError bool `json:"debug_oidc_parent_error,omitempty"` OIDCSingleV1SessionTermination bool `json:"oidc_single_v1_session_termination,omitempty"` DisableUserTokenEvent bool `json:"disable_user_token_event,omitempty"` diff --git a/internal/feature/key_enumer.go b/internal/feature/key_enumer.go index e06199120a..1b4fb9a3ad 100644 --- a/internal/feature/key_enumer.go +++ b/internal/feature/key_enumer.go @@ -12,14 +12,17 @@ const ( _KeyLowerName_0 = "unspecifiedlogin_default_orgtrigger_introspection_projections" _KeyName_1 = "user_schematoken_exchange" _KeyLowerName_1 = "user_schematoken_exchange" - _KeyName_2 = "improved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" - _KeyLowerName_2 = "improved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" + _KeyName_2 = "improved_performance" + _KeyLowerName_2 = "improved_performance" + _KeyName_3 = "debug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" + _KeyLowerName_3 = "debug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" ) var ( _KeyIndex_0 = [...]uint8{0, 11, 28, 61} _KeyIndex_1 = [...]uint8{0, 11, 25} - _KeyIndex_2 = [...]uint8{0, 20, 27, 50, 84, 108, 134, 142, 161, 184} + _KeyIndex_2 = [...]uint8{0, 20} + _KeyIndex_3 = [...]uint8{0, 23, 57, 81, 107, 115, 134, 157} ) func (i Key) String() string { @@ -29,9 +32,11 @@ func (i Key) String() string { case 4 <= i && i <= 5: i -= 4 return _KeyName_1[_KeyIndex_1[i]:_KeyIndex_1[i+1]] - case 7 <= i && i <= 15: - i -= 7 - return _KeyName_2[_KeyIndex_2[i]:_KeyIndex_2[i+1]] + case i == 7: + return _KeyName_2 + case 9 <= i && i <= 15: + i -= 9 + return _KeyName_3[_KeyIndex_3[i]:_KeyIndex_3[i+1]] default: return fmt.Sprintf("Key(%d)", i) } @@ -47,7 +52,6 @@ func _KeyNoOp() { _ = x[KeyUserSchema-(4)] _ = x[KeyTokenExchange-(5)] _ = x[KeyImprovedPerformance-(7)] - _ = x[KeyWebKey-(8)] _ = x[KeyDebugOIDCParentError-(9)] _ = x[KeyOIDCSingleV1SessionTermination-(10)] _ = x[KeyDisableUserTokenEvent-(11)] @@ -57,7 +61,7 @@ func _KeyNoOp() { _ = x[KeyConsoleUseV2UserApi-(15)] } -var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi} +var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi} var _KeyNameToValueMap = map[string]Key{ _KeyName_0[0:11]: KeyUnspecified, @@ -72,22 +76,20 @@ var _KeyNameToValueMap = map[string]Key{ _KeyLowerName_1[11:25]: KeyTokenExchange, _KeyName_2[0:20]: KeyImprovedPerformance, _KeyLowerName_2[0:20]: KeyImprovedPerformance, - _KeyName_2[20:27]: KeyWebKey, - _KeyLowerName_2[20:27]: KeyWebKey, - _KeyName_2[27:50]: KeyDebugOIDCParentError, - _KeyLowerName_2[27:50]: KeyDebugOIDCParentError, - _KeyName_2[50:84]: KeyOIDCSingleV1SessionTermination, - _KeyLowerName_2[50:84]: KeyOIDCSingleV1SessionTermination, - _KeyName_2[84:108]: KeyDisableUserTokenEvent, - _KeyLowerName_2[84:108]: KeyDisableUserTokenEvent, - _KeyName_2[108:134]: KeyEnableBackChannelLogout, - _KeyLowerName_2[108:134]: KeyEnableBackChannelLogout, - _KeyName_2[134:142]: KeyLoginV2, - _KeyLowerName_2[134:142]: KeyLoginV2, - _KeyName_2[142:161]: KeyPermissionCheckV2, - _KeyLowerName_2[142:161]: KeyPermissionCheckV2, - _KeyName_2[161:184]: KeyConsoleUseV2UserApi, - _KeyLowerName_2[161:184]: KeyConsoleUseV2UserApi, + _KeyName_3[0:23]: KeyDebugOIDCParentError, + _KeyLowerName_3[0:23]: KeyDebugOIDCParentError, + _KeyName_3[23:57]: KeyOIDCSingleV1SessionTermination, + _KeyLowerName_3[23:57]: KeyOIDCSingleV1SessionTermination, + _KeyName_3[57:81]: KeyDisableUserTokenEvent, + _KeyLowerName_3[57:81]: KeyDisableUserTokenEvent, + _KeyName_3[81:107]: KeyEnableBackChannelLogout, + _KeyLowerName_3[81:107]: KeyEnableBackChannelLogout, + _KeyName_3[107:115]: KeyLoginV2, + _KeyLowerName_3[107:115]: KeyLoginV2, + _KeyName_3[115:134]: KeyPermissionCheckV2, + _KeyLowerName_3[115:134]: KeyPermissionCheckV2, + _KeyName_3[134:157]: KeyConsoleUseV2UserApi, + _KeyLowerName_3[134:157]: KeyConsoleUseV2UserApi, } var _KeyNames = []string{ @@ -97,14 +99,13 @@ var _KeyNames = []string{ _KeyName_1[0:11], _KeyName_1[11:25], _KeyName_2[0:20], - _KeyName_2[20:27], - _KeyName_2[27:50], - _KeyName_2[50:84], - _KeyName_2[84:108], - _KeyName_2[108:134], - _KeyName_2[134:142], - _KeyName_2[142:161], - _KeyName_2[161:184], + _KeyName_3[0:23], + _KeyName_3[23:57], + _KeyName_3[57:81], + _KeyName_3[81:107], + _KeyName_3[107:115], + _KeyName_3[115:134], + _KeyName_3[134:157], } // KeyString retrieves an enum value from the enum constants string name. diff --git a/internal/notification/handlers/back_channel_logout.go b/internal/notification/handlers/back_channel_logout.go index f1a99146ca..983915ac28 100644 --- a/internal/notification/handlers/back_channel_logout.go +++ b/internal/notification/handlers/back_channel_logout.go @@ -7,10 +7,8 @@ import ( "sync" "time" - "github.com/zitadel/logging" "github.com/zitadel/oidc/v3/pkg/crypto" "github.com/zitadel/oidc/v3/pkg/oidc" - "github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/zitadel/internal/api/authz" http_utils "github.com/zitadel/zitadel/internal/api/http" @@ -149,7 +147,7 @@ func (u *backChannelLogoutNotifier) terminateSession(ctx context.Context, id str return err } - getSigner := zoidc.GetSignerOnce(u.queries.GetActiveSigningWebKey, u.signingKey) + getSigner := zoidc.GetSignerOnce(u.queries.GetActiveSigningWebKey) var wg sync.WaitGroup wg.Add(len(sessions.sessions)) @@ -172,20 +170,6 @@ func (u *backChannelLogoutNotifier) terminateSession(ctx context.Context, id str return errors.Join(errs...) } -func (u *backChannelLogoutNotifier) signingKey(ctx context.Context) (op.SigningKey, error) { - keys, err := u.queries.ActivePrivateSigningKey(ctx, time.Now()) - if err != nil { - return nil, err - } - if len(keys.Keys) == 0 { - logging.WithFields("instanceID", authz.GetInstance(ctx).InstanceID()). - Info("There's no active signing key and automatic rotation is not supported for back channel logout." + - "Please enable the webkey management feature on your instance") - return nil, zerrors.ThrowPreconditionFailed(nil, "HANDL-DF3nf", "no active signing key") - } - return zoidc.PrivateKeyToSigningKey(zoidc.SelectSigningKey(keys.Keys), u.keyEncryptionAlg) -} - func (u *backChannelLogoutNotifier) sendLogoutToken(ctx context.Context, oidcSession *backChannelLogoutOIDCSessions, e eventstore.Event, getSigner zoidc.SignerFunc) error { token, err := u.logoutToken(ctx, oidcSession, getSigner) if err != nil { diff --git a/internal/notification/handlers/mock/commands.mock.go b/internal/notification/handlers/mock/commands.mock.go index 7d41c30f30..ec327de8e8 100644 --- a/internal/notification/handlers/mock/commands.mock.go +++ b/internal/notification/handlers/mock/commands.mock.go @@ -23,6 +23,7 @@ import ( type MockCommands struct { ctrl *gomock.Controller recorder *MockCommandsMockRecorder + isgomock struct{} } // MockCommandsMockRecorder is the mock recorder for MockCommands. @@ -43,197 +44,197 @@ func (m *MockCommands) EXPECT() *MockCommandsMockRecorder { } // HumanEmailVerificationCodeSent mocks base method. -func (m *MockCommands) HumanEmailVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", ctx, orgID, userID) ret0, _ := ret[0].(error) return ret0 } // HumanEmailVerificationCodeSent indicates an expected call of HumanEmailVerificationCodeSent. -func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(ctx, orgID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), ctx, orgID, userID) } // HumanInitCodeSent mocks base method. -func (m *MockCommands) HumanInitCodeSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) HumanInitCodeSent(ctx context.Context, orgID, userID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanInitCodeSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "HumanInitCodeSent", ctx, orgID, userID) ret0, _ := ret[0].(error) return ret0 } // HumanInitCodeSent indicates an expected call of HumanInitCodeSent. -func (mr *MockCommandsMockRecorder) HumanInitCodeSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanInitCodeSent(ctx, orgID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), ctx, orgID, userID) } // HumanOTPEmailCodeSent mocks base method. -func (m *MockCommands) HumanOTPEmailCodeSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) HumanOTPEmailCodeSent(ctx context.Context, userID, resourceOwner string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", ctx, userID, resourceOwner) ret0, _ := ret[0].(error) return ret0 } // HumanOTPEmailCodeSent indicates an expected call of HumanOTPEmailCodeSent. -func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(ctx, userID, resourceOwner any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), ctx, userID, resourceOwner) } // HumanOTPSMSCodeSent mocks base method. -func (m *MockCommands) HumanOTPSMSCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { +func (m *MockCommands) HumanOTPSMSCodeSent(ctx context.Context, userID, resourceOwner string, generatorInfo *senders.CodeGeneratorInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", ctx, userID, resourceOwner, generatorInfo) ret0, _ := ret[0].(error) return ret0 } // HumanOTPSMSCodeSent indicates an expected call of HumanOTPSMSCodeSent. -func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(ctx, userID, resourceOwner, generatorInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), ctx, userID, resourceOwner, generatorInfo) } // HumanPasswordlessInitCodeSent mocks base method. -func (m *MockCommands) HumanPasswordlessInitCodeSent(arg0 context.Context, arg1, arg2, arg3 string) error { +func (m *MockCommands) HumanPasswordlessInitCodeSent(ctx context.Context, userID, resourceOwner, codeID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", ctx, userID, resourceOwner, codeID) ret0, _ := ret[0].(error) return ret0 } // HumanPasswordlessInitCodeSent indicates an expected call of HumanPasswordlessInitCodeSent. -func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(ctx, userID, resourceOwner, codeID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), ctx, userID, resourceOwner, codeID) } // HumanPhoneVerificationCodeSent mocks base method. -func (m *MockCommands) HumanPhoneVerificationCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { +func (m *MockCommands) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string, generatorInfo *senders.CodeGeneratorInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", ctx, orgID, userID, generatorInfo) ret0, _ := ret[0].(error) return ret0 } // HumanPhoneVerificationCodeSent indicates an expected call of HumanPhoneVerificationCodeSent. -func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(ctx, orgID, userID, generatorInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), ctx, orgID, userID, generatorInfo) } // InviteCodeSent mocks base method. -func (m *MockCommands) InviteCodeSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) InviteCodeSent(ctx context.Context, orgID, userID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InviteCodeSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "InviteCodeSent", ctx, orgID, userID) ret0, _ := ret[0].(error) return ret0 } // InviteCodeSent indicates an expected call of InviteCodeSent. -func (mr *MockCommandsMockRecorder) InviteCodeSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) InviteCodeSent(ctx, orgID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteCodeSent", reflect.TypeOf((*MockCommands)(nil).InviteCodeSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteCodeSent", reflect.TypeOf((*MockCommands)(nil).InviteCodeSent), ctx, orgID, userID) } // MilestonePushed mocks base method. -func (m *MockCommands) MilestonePushed(arg0 context.Context, arg1 string, arg2 milestone.Type, arg3 []string) error { +func (m *MockCommands) MilestonePushed(ctx context.Context, instanceID string, msType milestone.Type, endpoints []string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MilestonePushed", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "MilestonePushed", ctx, instanceID, msType, endpoints) ret0, _ := ret[0].(error) return ret0 } // MilestonePushed indicates an expected call of MilestonePushed. -func (mr *MockCommandsMockRecorder) MilestonePushed(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) MilestonePushed(ctx, instanceID, msType, endpoints any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), ctx, instanceID, msType, endpoints) } // OTPEmailSent mocks base method. -func (m *MockCommands) OTPEmailSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) OTPEmailSent(ctx context.Context, sessionID, resourceOwner string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OTPEmailSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "OTPEmailSent", ctx, sessionID, resourceOwner) ret0, _ := ret[0].(error) return ret0 } // OTPEmailSent indicates an expected call of OTPEmailSent. -func (mr *MockCommandsMockRecorder) OTPEmailSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) OTPEmailSent(ctx, sessionID, resourceOwner any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), ctx, sessionID, resourceOwner) } // OTPSMSSent mocks base method. -func (m *MockCommands) OTPSMSSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { +func (m *MockCommands) OTPSMSSent(ctx context.Context, sessionID, resourceOwner string, generatorInfo *senders.CodeGeneratorInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "OTPSMSSent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "OTPSMSSent", ctx, sessionID, resourceOwner, generatorInfo) ret0, _ := ret[0].(error) return ret0 } // OTPSMSSent indicates an expected call of OTPSMSSent. -func (mr *MockCommandsMockRecorder) OTPSMSSent(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) OTPSMSSent(ctx, sessionID, resourceOwner, generatorInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), ctx, sessionID, resourceOwner, generatorInfo) } // PasswordChangeSent mocks base method. -func (m *MockCommands) PasswordChangeSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) PasswordChangeSent(ctx context.Context, orgID, userID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasswordChangeSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "PasswordChangeSent", ctx, orgID, userID) ret0, _ := ret[0].(error) return ret0 } // PasswordChangeSent indicates an expected call of PasswordChangeSent. -func (mr *MockCommandsMockRecorder) PasswordChangeSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) PasswordChangeSent(ctx, orgID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), ctx, orgID, userID) } // PasswordCodeSent mocks base method. -func (m *MockCommands) PasswordCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { +func (m *MockCommands) PasswordCodeSent(ctx context.Context, orgID, userID string, generatorInfo *senders.CodeGeneratorInfo) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PasswordCodeSent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "PasswordCodeSent", ctx, orgID, userID, generatorInfo) ret0, _ := ret[0].(error) return ret0 } // PasswordCodeSent indicates an expected call of PasswordCodeSent. -func (mr *MockCommandsMockRecorder) PasswordCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) PasswordCodeSent(ctx, orgID, userID, generatorInfo any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), ctx, orgID, userID, generatorInfo) } // UsageNotificationSent mocks base method. -func (m *MockCommands) UsageNotificationSent(arg0 context.Context, arg1 *quota.NotificationDueEvent) error { +func (m *MockCommands) UsageNotificationSent(ctx context.Context, dueEvent *quota.NotificationDueEvent) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UsageNotificationSent", arg0, arg1) + ret := m.ctrl.Call(m, "UsageNotificationSent", ctx, dueEvent) ret0, _ := ret[0].(error) return ret0 } // UsageNotificationSent indicates an expected call of UsageNotificationSent. -func (mr *MockCommandsMockRecorder) UsageNotificationSent(arg0, arg1 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) UsageNotificationSent(ctx, dueEvent any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), ctx, dueEvent) } // UserDomainClaimedSent mocks base method. -func (m *MockCommands) UserDomainClaimedSent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockCommands) UserDomainClaimedSent(ctx context.Context, orgID, userID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UserDomainClaimedSent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "UserDomainClaimedSent", ctx, orgID, userID) ret0, _ := ret[0].(error) return ret0 } // UserDomainClaimedSent indicates an expected call of UserDomainClaimedSent. -func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(ctx, orgID, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), ctx, orgID, userID) } diff --git a/internal/notification/handlers/mock/queries.mock.go b/internal/notification/handlers/mock/queries.mock.go index 670d3f3896..2cf53d1b2a 100644 --- a/internal/notification/handlers/mock/queries.mock.go +++ b/internal/notification/handlers/mock/queries.mock.go @@ -12,7 +12,6 @@ package mock import ( context "context" reflect "reflect" - time "time" jose "github.com/go-jose/go-jose/v4" authz "github.com/zitadel/zitadel/internal/api/authz" @@ -26,6 +25,7 @@ import ( type MockQueries struct { ctrl *gomock.Controller recorder *MockQueriesMockRecorder + isgomock struct{} } // MockQueriesMockRecorder is the mock recorder for MockQueries. @@ -60,240 +60,225 @@ func (mr *MockQueriesMockRecorder) ActiveInstances() *gomock.Call { } // ActiveLabelPolicyByOrg mocks base method. -func (m *MockQueries) ActiveLabelPolicyByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.LabelPolicy, error) { +func (m *MockQueries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.LabelPolicy, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ActiveLabelPolicyByOrg", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ActiveLabelPolicyByOrg", ctx, orgID, withOwnerRemoved) ret0, _ := ret[0].(*query.LabelPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // ActiveLabelPolicyByOrg indicates an expected call of ActiveLabelPolicyByOrg. -func (mr *MockQueriesMockRecorder) ActiveLabelPolicyByOrg(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) ActiveLabelPolicyByOrg(ctx, orgID, withOwnerRemoved any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveLabelPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).ActiveLabelPolicyByOrg), arg0, arg1, arg2) -} - -// ActivePrivateSigningKey mocks base method. -func (m *MockQueries) ActivePrivateSigningKey(arg0 context.Context, arg1 time.Time) (*query.PrivateKeys, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ActivePrivateSigningKey", arg0, arg1) - ret0, _ := ret[0].(*query.PrivateKeys) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ActivePrivateSigningKey indicates an expected call of ActivePrivateSigningKey. -func (mr *MockQueriesMockRecorder) ActivePrivateSigningKey(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivePrivateSigningKey", reflect.TypeOf((*MockQueries)(nil).ActivePrivateSigningKey), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveLabelPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).ActiveLabelPolicyByOrg), ctx, orgID, withOwnerRemoved) } // CustomTextListByTemplate mocks base method. -func (m *MockQueries) CustomTextListByTemplate(arg0 context.Context, arg1, arg2 string, arg3 bool) (*query.CustomTexts, error) { +func (m *MockQueries) CustomTextListByTemplate(ctx context.Context, aggregateID, template string, withOwnerRemoved bool) (*query.CustomTexts, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CustomTextListByTemplate", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "CustomTextListByTemplate", ctx, aggregateID, template, withOwnerRemoved) ret0, _ := ret[0].(*query.CustomTexts) ret1, _ := ret[1].(error) return ret0, ret1 } // CustomTextListByTemplate indicates an expected call of CustomTextListByTemplate. -func (mr *MockQueriesMockRecorder) CustomTextListByTemplate(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) CustomTextListByTemplate(ctx, aggregateID, template, withOwnerRemoved any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomTextListByTemplate", reflect.TypeOf((*MockQueries)(nil).CustomTextListByTemplate), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomTextListByTemplate", reflect.TypeOf((*MockQueries)(nil).CustomTextListByTemplate), ctx, aggregateID, template, withOwnerRemoved) } // GetActiveSigningWebKey mocks base method. -func (m *MockQueries) GetActiveSigningWebKey(arg0 context.Context) (*jose.JSONWebKey, error) { +func (m *MockQueries) GetActiveSigningWebKey(ctx context.Context) (*jose.JSONWebKey, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetActiveSigningWebKey", arg0) + ret := m.ctrl.Call(m, "GetActiveSigningWebKey", ctx) ret0, _ := ret[0].(*jose.JSONWebKey) ret1, _ := ret[1].(error) return ret0, ret1 } // GetActiveSigningWebKey indicates an expected call of GetActiveSigningWebKey. -func (mr *MockQueriesMockRecorder) GetActiveSigningWebKey(arg0 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) GetActiveSigningWebKey(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveSigningWebKey", reflect.TypeOf((*MockQueries)(nil).GetActiveSigningWebKey), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveSigningWebKey", reflect.TypeOf((*MockQueries)(nil).GetActiveSigningWebKey), ctx) } // GetDefaultLanguage mocks base method. -func (m *MockQueries) GetDefaultLanguage(arg0 context.Context) language.Tag { +func (m *MockQueries) GetDefaultLanguage(ctx context.Context) language.Tag { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDefaultLanguage", arg0) + ret := m.ctrl.Call(m, "GetDefaultLanguage", ctx) ret0, _ := ret[0].(language.Tag) return ret0 } // GetDefaultLanguage indicates an expected call of GetDefaultLanguage. -func (mr *MockQueriesMockRecorder) GetDefaultLanguage(arg0 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) GetDefaultLanguage(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), ctx) } // GetInstanceRestrictions mocks base method. -func (m *MockQueries) GetInstanceRestrictions(arg0 context.Context) (query.Restrictions, error) { +func (m *MockQueries) GetInstanceRestrictions(ctx context.Context) (query.Restrictions, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetInstanceRestrictions", arg0) + ret := m.ctrl.Call(m, "GetInstanceRestrictions", ctx) ret0, _ := ret[0].(query.Restrictions) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInstanceRestrictions indicates an expected call of GetInstanceRestrictions. -func (mr *MockQueriesMockRecorder) GetInstanceRestrictions(arg0 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) GetInstanceRestrictions(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceRestrictions", reflect.TypeOf((*MockQueries)(nil).GetInstanceRestrictions), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceRestrictions", reflect.TypeOf((*MockQueries)(nil).GetInstanceRestrictions), ctx) } // GetNotifyUserByID mocks base method. -func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 string) (*query.NotifyUser, error) { +func (m *MockQueries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string) (*query.NotifyUser, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNotifyUserByID", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "GetNotifyUserByID", ctx, shouldTriggered, userID) ret0, _ := ret[0].(*query.NotifyUser) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNotifyUserByID indicates an expected call of GetNotifyUserByID. -func (mr *MockQueriesMockRecorder) GetNotifyUserByID(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) GetNotifyUserByID(ctx, shouldTriggered, userID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotifyUserByID", reflect.TypeOf((*MockQueries)(nil).GetNotifyUserByID), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotifyUserByID", reflect.TypeOf((*MockQueries)(nil).GetNotifyUserByID), ctx, shouldTriggered, userID) } // InstanceByID mocks base method. -func (m *MockQueries) InstanceByID(arg0 context.Context, arg1 string) (authz.Instance, error) { +func (m *MockQueries) InstanceByID(ctx context.Context, id string) (authz.Instance, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "InstanceByID", arg0, arg1) + ret := m.ctrl.Call(m, "InstanceByID", ctx, id) ret0, _ := ret[0].(authz.Instance) ret1, _ := ret[1].(error) return ret0, ret1 } // InstanceByID indicates an expected call of InstanceByID. -func (mr *MockQueriesMockRecorder) InstanceByID(arg0, arg1 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) InstanceByID(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceByID", reflect.TypeOf((*MockQueries)(nil).InstanceByID), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceByID", reflect.TypeOf((*MockQueries)(nil).InstanceByID), ctx, id) } // MailTemplateByOrg mocks base method. -func (m *MockQueries) MailTemplateByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.MailTemplate, error) { +func (m *MockQueries) MailTemplateByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.MailTemplate, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "MailTemplateByOrg", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "MailTemplateByOrg", ctx, orgID, withOwnerRemoved) ret0, _ := ret[0].(*query.MailTemplate) ret1, _ := ret[1].(error) return ret0, ret1 } // MailTemplateByOrg indicates an expected call of MailTemplateByOrg. -func (mr *MockQueriesMockRecorder) MailTemplateByOrg(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) MailTemplateByOrg(ctx, orgID, withOwnerRemoved any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MailTemplateByOrg", reflect.TypeOf((*MockQueries)(nil).MailTemplateByOrg), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MailTemplateByOrg", reflect.TypeOf((*MockQueries)(nil).MailTemplateByOrg), ctx, orgID, withOwnerRemoved) } // NotificationPolicyByOrg mocks base method. -func (m *MockQueries) NotificationPolicyByOrg(arg0 context.Context, arg1 bool, arg2 string, arg3 bool) (*query.NotificationPolicy, error) { +func (m *MockQueries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (*query.NotificationPolicy, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NotificationPolicyByOrg", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "NotificationPolicyByOrg", ctx, shouldTriggerBulk, orgID, withOwnerRemoved) ret0, _ := ret[0].(*query.NotificationPolicy) ret1, _ := ret[1].(error) return ret0, ret1 } // NotificationPolicyByOrg indicates an expected call of NotificationPolicyByOrg. -func (mr *MockQueriesMockRecorder) NotificationPolicyByOrg(arg0, arg1, arg2, arg3 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) NotificationPolicyByOrg(ctx, shouldTriggerBulk, orgID, withOwnerRemoved any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).NotificationPolicyByOrg), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).NotificationPolicyByOrg), ctx, shouldTriggerBulk, orgID, withOwnerRemoved) } // NotificationProviderByIDAndType mocks base method. -func (m *MockQueries) NotificationProviderByIDAndType(arg0 context.Context, arg1 string, arg2 domain.NotificationProviderType) (*query.DebugNotificationProvider, error) { +func (m *MockQueries) NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (*query.DebugNotificationProvider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NotificationProviderByIDAndType", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "NotificationProviderByIDAndType", ctx, aggID, providerType) ret0, _ := ret[0].(*query.DebugNotificationProvider) ret1, _ := ret[1].(error) return ret0, ret1 } // NotificationProviderByIDAndType indicates an expected call of NotificationProviderByIDAndType. -func (mr *MockQueriesMockRecorder) NotificationProviderByIDAndType(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) NotificationProviderByIDAndType(ctx, aggID, providerType any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationProviderByIDAndType", reflect.TypeOf((*MockQueries)(nil).NotificationProviderByIDAndType), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationProviderByIDAndType", reflect.TypeOf((*MockQueries)(nil).NotificationProviderByIDAndType), ctx, aggID, providerType) } // SMSProviderConfigActive mocks base method. -func (m *MockQueries) SMSProviderConfigActive(arg0 context.Context, arg1 string) (*query.SMSConfig, error) { +func (m *MockQueries) SMSProviderConfigActive(ctx context.Context, resourceOwner string) (*query.SMSConfig, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SMSProviderConfigActive", arg0, arg1) + ret := m.ctrl.Call(m, "SMSProviderConfigActive", ctx, resourceOwner) ret0, _ := ret[0].(*query.SMSConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // SMSProviderConfigActive indicates an expected call of SMSProviderConfigActive. -func (mr *MockQueriesMockRecorder) SMSProviderConfigActive(arg0, arg1 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) SMSProviderConfigActive(ctx, resourceOwner any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfigActive", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfigActive), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfigActive", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfigActive), ctx, resourceOwner) } // SMTPConfigActive mocks base method. -func (m *MockQueries) SMTPConfigActive(arg0 context.Context, arg1 string) (*query.SMTPConfig, error) { +func (m *MockQueries) SMTPConfigActive(ctx context.Context, resourceOwner string) (*query.SMTPConfig, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SMTPConfigActive", arg0, arg1) + ret := m.ctrl.Call(m, "SMTPConfigActive", ctx, resourceOwner) ret0, _ := ret[0].(*query.SMTPConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // SMTPConfigActive indicates an expected call of SMTPConfigActive. -func (mr *MockQueriesMockRecorder) SMTPConfigActive(arg0, arg1 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) SMTPConfigActive(ctx, resourceOwner any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigActive", reflect.TypeOf((*MockQueries)(nil).SMTPConfigActive), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigActive", reflect.TypeOf((*MockQueries)(nil).SMTPConfigActive), ctx, resourceOwner) } // SearchInstanceDomains mocks base method. -func (m *MockQueries) SearchInstanceDomains(arg0 context.Context, arg1 *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error) { +func (m *MockQueries) SearchInstanceDomains(ctx context.Context, queries *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SearchInstanceDomains", arg0, arg1) + ret := m.ctrl.Call(m, "SearchInstanceDomains", ctx, queries) ret0, _ := ret[0].(*query.InstanceDomains) ret1, _ := ret[1].(error) return ret0, ret1 } // SearchInstanceDomains indicates an expected call of SearchInstanceDomains. -func (mr *MockQueriesMockRecorder) SearchInstanceDomains(arg0, arg1 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) SearchInstanceDomains(ctx, queries any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchInstanceDomains", reflect.TypeOf((*MockQueries)(nil).SearchInstanceDomains), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchInstanceDomains", reflect.TypeOf((*MockQueries)(nil).SearchInstanceDomains), ctx, queries) } // SearchMilestones mocks base method. -func (m *MockQueries) SearchMilestones(arg0 context.Context, arg1 []string, arg2 *query.MilestonesSearchQueries) (*query.Milestones, error) { +func (m *MockQueries) SearchMilestones(ctx context.Context, instanceIDs []string, queries *query.MilestonesSearchQueries) (*query.Milestones, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SearchMilestones", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "SearchMilestones", ctx, instanceIDs, queries) ret0, _ := ret[0].(*query.Milestones) ret1, _ := ret[1].(error) return ret0, ret1 } // SearchMilestones indicates an expected call of SearchMilestones. -func (mr *MockQueriesMockRecorder) SearchMilestones(arg0, arg1, arg2 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) SearchMilestones(ctx, instanceIDs, queries any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMilestones", reflect.TypeOf((*MockQueries)(nil).SearchMilestones), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMilestones", reflect.TypeOf((*MockQueries)(nil).SearchMilestones), ctx, instanceIDs, queries) } // SessionByID mocks base method. -func (m *MockQueries) SessionByID(arg0 context.Context, arg1 bool, arg2, arg3 string, arg4 domain.PermissionCheck) (*query.Session, error) { +func (m *MockQueries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string, check domain.PermissionCheck) (*query.Session, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SessionByID", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "SessionByID", ctx, shouldTriggerBulk, id, sessionToken, check) ret0, _ := ret[0].(*query.Session) ret1, _ := ret[1].(error) return ret0, ret1 } // SessionByID indicates an expected call of SessionByID. -func (mr *MockQueriesMockRecorder) SessionByID(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { +func (mr *MockQueriesMockRecorder) SessionByID(ctx, shouldTriggerBulk, id, sessionToken, check any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SessionByID", reflect.TypeOf((*MockQueries)(nil).SessionByID), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SessionByID", reflect.TypeOf((*MockQueries)(nil).SessionByID), ctx, shouldTriggerBulk, id, sessionToken, check) } diff --git a/internal/notification/handlers/mock/queue.mock.go b/internal/notification/handlers/mock/queue.mock.go index e1387595db..e9cf3efed1 100644 --- a/internal/notification/handlers/mock/queue.mock.go +++ b/internal/notification/handlers/mock/queue.mock.go @@ -22,6 +22,7 @@ import ( type MockQueue struct { ctrl *gomock.Controller recorder *MockQueueMockRecorder + isgomock struct{} } // MockQueueMockRecorder is the mock recorder for MockQueue. @@ -42,10 +43,10 @@ func (m *MockQueue) EXPECT() *MockQueueMockRecorder { } // Insert mocks base method. -func (m *MockQueue) Insert(arg0 context.Context, arg1 river.JobArgs, arg2 ...queue.InsertOpt) error { +func (m *MockQueue) Insert(ctx context.Context, args river.JobArgs, opts ...queue.InsertOpt) error { m.ctrl.T.Helper() - varargs := []any{arg0, arg1} - for _, a := range arg2 { + varargs := []any{ctx, args} + for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Insert", varargs...) @@ -54,8 +55,8 @@ func (m *MockQueue) Insert(arg0 context.Context, arg1 river.JobArgs, arg2 ...que } // Insert indicates an expected call of Insert. -func (mr *MockQueueMockRecorder) Insert(arg0, arg1 any, arg2 ...any) *gomock.Call { +func (mr *MockQueueMockRecorder) Insert(ctx, args any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{arg0, arg1}, arg2...) + varargs := append([]any{ctx, args}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockQueue)(nil).Insert), varargs...) } diff --git a/internal/notification/handlers/queries.go b/internal/notification/handlers/queries.go index a3d68e4797..d9ff1b4201 100644 --- a/internal/notification/handlers/queries.go +++ b/internal/notification/handlers/queries.go @@ -2,7 +2,6 @@ package handlers import ( "context" - "time" "github.com/go-jose/go-jose/v4" "golang.org/x/text/language" @@ -30,7 +29,6 @@ type Queries interface { GetInstanceRestrictions(ctx context.Context) (restrictions query.Restrictions, err error) InstanceByID(ctx context.Context, id string) (instance authz.Instance, err error) GetActiveSigningWebKey(ctx context.Context) (*jose.JSONWebKey, error) - ActivePrivateSigningKey(ctx context.Context, t time.Time) (keys *query.PrivateKeys, err error) ActiveInstances() []string } diff --git a/internal/query/instance_features.go b/internal/query/instance_features.go index 501cfc4e9c..9e0081a542 100644 --- a/internal/query/instance_features.go +++ b/internal/query/instance_features.go @@ -14,7 +14,6 @@ type InstanceFeatures struct { UserSchema FeatureSource[bool] TokenExchange FeatureSource[bool] ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType] - WebKey FeatureSource[bool] DebugOIDCParentError FeatureSource[bool] OIDCSingleV1SessionTermination FeatureSource[bool] DisableUserTokenEvent FeatureSource[bool] diff --git a/internal/query/instance_features_model.go b/internal/query/instance_features_model.go index 7130044fbf..a30009e9ee 100644 --- a/internal/query/instance_features_model.go +++ b/internal/query/instance_features_model.go @@ -67,7 +67,6 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder { feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceImprovedPerformanceEventType, - feature_v2.InstanceWebKeyEventType, feature_v2.InstanceDebugOIDCParentErrorEventType, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, feature_v2.InstanceDisableUserTokenEvent, @@ -121,8 +120,6 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_ features.TokenExchange.set(level, event.Value) case feature.KeyImprovedPerformance: features.ImprovedPerformance.set(level, event.Value) - case feature.KeyWebKey: - features.WebKey.set(level, event.Value) case feature.KeyDebugOIDCParentError: features.DebugOIDCParentError.set(level, event.Value) case feature.KeyOIDCSingleV1SessionTermination: diff --git a/internal/query/key.go b/internal/query/key.go index 4831d88654..e7b81bb951 100644 --- a/internal/query/key.go +++ b/internal/query/key.go @@ -1,20 +1,10 @@ package query import ( - "context" - "crypto/rsa" - "database/sql" "time" - sq "github.com/Masterminds/squirrel" - - "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/query/projection" - "github.com/zitadel/zitadel/internal/repository/keypair" - "github.com/zitadel/zitadel/internal/telemetry/tracing" - "github.com/zitadel/zitadel/internal/zerrors" ) type Key interface { @@ -36,11 +26,6 @@ type PublicKey interface { Key() interface{} } -type PrivateKeys struct { - SearchResponse - Keys []PrivateKey -} - type PublicKeys struct { SearchResponse Keys []PublicKey @@ -72,34 +57,6 @@ func (k *key) Sequence() uint64 { return k.sequence } -type privateKey struct { - key - expiry time.Time - privateKey *crypto.CryptoValue -} - -func (k *privateKey) Expiry() time.Time { - return k.expiry -} - -func (k *privateKey) Key() *crypto.CryptoValue { - return k.privateKey -} - -type rsaPublicKey struct { - key - expiry time.Time - publicKey *rsa.PublicKey -} - -func (r *rsaPublicKey) Expiry() time.Time { - return r.expiry -} - -func (r *rsaPublicKey) Key() interface{} { - return r.publicKey -} - var ( keyTable = table{ name: projection.KeyProjectionTable, @@ -157,277 +114,3 @@ var ( table: keyPrivateTable, } ) - -var ( - keyPublicTable = table{ - name: projection.KeyPublicTable, - instanceIDCol: projection.KeyPrivateColumnInstanceID, - } - KeyPublicColID = Column{ - name: projection.KeyPublicColumnID, - table: keyPublicTable, - } - KeyPublicColExpiry = Column{ - name: projection.KeyPublicColumnExpiry, - table: keyPublicTable, - } - KeyPublicColKey = Column{ - name: projection.KeyPublicColumnKey, - table: keyPublicTable, - } -) - -func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (keys *PublicKeys, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - query, scan := preparePublicKeysQuery() - if t.IsZero() { - t = time.Now() - } - stmt, args, err := query.Where( - sq.And{ - sq.Eq{KeyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()}, - sq.Gt{KeyPublicColExpiry.identifier(): t}, - }).ToSql() - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-SDFfg", "Errors.Query.SQLStatement") - } - - err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { - keys, err = scan(rows) - return err - }, stmt, args...) - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-Sghn4", "Errors.Internal") - } - - keys.State, err = q.latestState(ctx, keyTable) - if !zerrors.IsNotFound(err) { - return keys, err - } - return keys, nil -} - -func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (keys *PrivateKeys, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - stmt, scan := preparePrivateKeysQuery() - if t.IsZero() { - t = time.Now() - } - query, args, err := stmt.Where( - sq.And{ - sq.Eq{ - KeyColUse.identifier(): crypto.KeyUsageSigning, - KeyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(), - }, - sq.Gt{KeyPrivateColExpiry.identifier(): t}, - }).OrderBy(KeyPrivateColExpiry.identifier()).ToSql() - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-SDff2", "Errors.Query.SQLStatement") - } - - err = q.client.QueryContext(ctx, func(rows *sql.Rows) error { - keys, err = scan(rows) - return err - }, query, args...) - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-WRFG4", "Errors.Internal") - } - keys.State, err = q.latestState(ctx, keyTable) - if !zerrors.IsNotFound(err) { - return keys, err - } - return keys, nil -} - -func preparePublicKeysQuery() (sq.SelectBuilder, func(*sql.Rows) (*PublicKeys, error)) { - return sq.Select( - KeyColID.identifier(), - KeyColCreationDate.identifier(), - KeyColChangeDate.identifier(), - KeyColSequence.identifier(), - KeyColResourceOwner.identifier(), - KeyColAlgorithm.identifier(), - KeyColUse.identifier(), - KeyPublicColExpiry.identifier(), - KeyPublicColKey.identifier(), - countColumn.identifier(), - ).From(keyTable.identifier()). - LeftJoin(join(KeyPublicColID, KeyColID)). - PlaceholderFormat(sq.Dollar), - func(rows *sql.Rows) (*PublicKeys, error) { - keys := make([]PublicKey, 0) - var count uint64 - for rows.Next() { - k := new(rsaPublicKey) - var keyValue []byte - err := rows.Scan( - &k.id, - &k.creationDate, - &k.changeDate, - &k.sequence, - &k.resourceOwner, - &k.algorithm, - &k.use, - &k.expiry, - &keyValue, - &count, - ) - if err != nil { - return nil, err - } - k.publicKey, err = crypto.BytesToPublicKey(keyValue) - if err != nil { - return nil, err - } - keys = append(keys, k) - } - - if err := rows.Close(); err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-rKd6k", "Errors.Query.CloseRows") - } - - return &PublicKeys{ - Keys: keys, - SearchResponse: SearchResponse{ - Count: count, - }, - }, nil - } -} - -func preparePrivateKeysQuery() (sq.SelectBuilder, func(*sql.Rows) (*PrivateKeys, error)) { - return sq.Select( - KeyColID.identifier(), - KeyColCreationDate.identifier(), - KeyColChangeDate.identifier(), - KeyColSequence.identifier(), - KeyColResourceOwner.identifier(), - KeyColAlgorithm.identifier(), - KeyColUse.identifier(), - KeyPrivateColExpiry.identifier(), - KeyPrivateColKey.identifier(), - countColumn.identifier(), - ).From(keyTable.identifier()). - LeftJoin(join(KeyPrivateColID, KeyColID)). - PlaceholderFormat(sq.Dollar), - func(rows *sql.Rows) (*PrivateKeys, error) { - keys := make([]PrivateKey, 0) - var count uint64 - for rows.Next() { - k := new(privateKey) - err := rows.Scan( - &k.id, - &k.creationDate, - &k.changeDate, - &k.sequence, - &k.resourceOwner, - &k.algorithm, - &k.use, - &k.expiry, - &k.privateKey, - &count, - ) - if err != nil { - return nil, err - } - keys = append(keys, k) - } - - if err := rows.Close(); err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-rKd6k", "Errors.Query.CloseRows") - } - - return &PrivateKeys{ - Keys: keys, - SearchResponse: SearchResponse{ - Count: count, - }, - }, nil - } -} - -type PublicKeyReadModel struct { - eventstore.ReadModel - - Algorithm string - Key *crypto.CryptoValue - Expiry time.Time - Usage crypto.KeyUsage -} - -func NewPublicKeyReadModel(keyID, resourceOwner string) *PublicKeyReadModel { - return &PublicKeyReadModel{ - ReadModel: eventstore.ReadModel{ - AggregateID: keyID, - ResourceOwner: resourceOwner, - }, - } -} - -func (wm *PublicKeyReadModel) AppendEvents(events ...eventstore.Event) { - wm.ReadModel.AppendEvents(events...) -} - -func (wm *PublicKeyReadModel) Reduce() error { - for _, event := range wm.Events { - switch e := event.(type) { - case *keypair.AddedEvent: - wm.Algorithm = e.Algorithm - wm.Key = e.PublicKey.Key - wm.Expiry = e.PublicKey.Expiry - wm.Usage = e.Usage - default: - } - } - return wm.ReadModel.Reduce() -} - -func (wm *PublicKeyReadModel) Query() *eventstore.SearchQueryBuilder { - return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). - AwaitOpenTransactions(). - ResourceOwner(wm.ResourceOwner). - AddQuery(). - AggregateTypes(keypair.AggregateType). - AggregateIDs(wm.AggregateID). - EventTypes(keypair.AddedEventType). - Builder() -} - -func (q *Queries) GetPublicKeyByID(ctx context.Context, keyID string) (_ PublicKey, err error) { - ctx, span := tracing.NewSpan(ctx) - defer func() { span.EndWithError(err) }() - - model := NewPublicKeyReadModel(keyID, authz.GetInstance(ctx).InstanceID()) - if err := q.eventstore.FilterToQueryReducer(ctx, model); err != nil { - return nil, err - } - if model.Algorithm == "" || model.Key == nil { - return nil, zerrors.ThrowNotFound(err, "QUERY-Ahf7x", "Errors.Key.NotFound") - } - keyValue, err := crypto.Decrypt(model.Key, q.keyEncryptionAlgorithm) - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-Ie4oh", "Errors.Internal") - } - publicKey, err := crypto.BytesToPublicKey(keyValue) - if err != nil { - return nil, zerrors.ThrowInternal(err, "QUERY-Kai2Z", "Errors.Internal") - } - - return &rsaPublicKey{ - key: key{ - id: model.AggregateID, - creationDate: model.CreationDate, - changeDate: model.ChangeDate, - sequence: model.ProcessedSequence, - resourceOwner: model.ResourceOwner, - algorithm: model.Algorithm, - use: model.Usage, - }, - expiry: model.Expiry, - publicKey: publicKey, - }, nil -} diff --git a/internal/query/key_test.go b/internal/query/key_test.go deleted file mode 100644 index 7bc029fd7f..0000000000 --- a/internal/query/key_test.go +++ /dev/null @@ -1,453 +0,0 @@ -package query - -import ( - "context" - "crypto/rsa" - "database/sql" - "database/sql/driver" - "errors" - "fmt" - "io" - "math/big" - "regexp" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - - "github.com/zitadel/zitadel/internal/api/authz" - "github.com/zitadel/zitadel/internal/crypto" - "github.com/zitadel/zitadel/internal/eventstore" - key_repo "github.com/zitadel/zitadel/internal/repository/keypair" - "github.com/zitadel/zitadel/internal/zerrors" -) - -var ( - preparePublicKeysStmt = `SELECT projections.keys4.id,` + - ` projections.keys4.creation_date,` + - ` projections.keys4.change_date,` + - ` projections.keys4.sequence,` + - ` projections.keys4.resource_owner,` + - ` projections.keys4.algorithm,` + - ` projections.keys4.use,` + - ` projections.keys4_public.expiry,` + - ` projections.keys4_public.key,` + - ` COUNT(*) OVER ()` + - ` FROM projections.keys4` + - ` LEFT JOIN projections.keys4_public ON projections.keys4.id = projections.keys4_public.id AND projections.keys4.instance_id = projections.keys4_public.instance_id` - preparePublicKeysCols = []string{ - "id", - "creation_date", - "change_date", - "sequence", - "resource_owner", - "algorithm", - "use", - "expiry", - "key", - "count", - } - - preparePrivateKeysStmt = `SELECT projections.keys4.id,` + - ` projections.keys4.creation_date,` + - ` projections.keys4.change_date,` + - ` projections.keys4.sequence,` + - ` projections.keys4.resource_owner,` + - ` projections.keys4.algorithm,` + - ` projections.keys4.use,` + - ` projections.keys4_private.expiry,` + - ` projections.keys4_private.key,` + - ` COUNT(*) OVER ()` + - ` FROM projections.keys4` + - ` LEFT JOIN projections.keys4_private ON projections.keys4.id = projections.keys4_private.id AND projections.keys4.instance_id = projections.keys4_private.instance_id` -) - -func Test_KeyPrepares(t *testing.T) { - type want struct { - sqlExpectations sqlExpectation - err checkErr - } - tests := []struct { - name string - prepare interface{} - want want - object interface{} - }{ - { - name: "preparePublicKeysQuery no result", - prepare: preparePublicKeysQuery, - want: want{ - sqlExpectations: mockQueries( - regexp.QuoteMeta(preparePublicKeysStmt), - nil, - nil, - ), - err: func(err error) (error, bool) { - if !zerrors.IsNotFound(err) { - return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false - } - return nil, true - }, - }, - object: &PublicKeys{Keys: []PublicKey{}}, - }, - { - name: "preparePublicKeysQuery found", - prepare: preparePublicKeysQuery, - want: want{ - sqlExpectations: mockQueries( - regexp.QuoteMeta(preparePublicKeysStmt), - preparePublicKeysCols, - [][]driver.Value{ - { - "key-id", - testNow, - testNow, - uint64(20211109), - "ro", - "RS256", - 0, - testNow, - []byte("-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsvX9P58JFxEs5C+L+H7W\nduFSWL5EPzber7C2m94klrSV6q0bAcrYQnGwFOlveThsY200hRbadKaKjHD7qIKH\nDEe0IY2PSRht33Jye52AwhkRw+M3xuQH/7R8LydnsNFk2KHpr5X2SBv42e37LjkE\nslKSaMRgJW+v0KZ30piY8QsdFRKKaVg5/Ajt1YToM1YVsdHXJ3vmXFMtypLdxwUD\ndIaLEX6pFUkU75KSuEQ/E2luT61Q3ta9kOWm9+0zvi7OMcbdekJT7mzcVnh93R1c\n13ZhQCLbh9A7si8jKFtaMWevjayrvqQABEcTN9N4Hoxcyg6l4neZtRDk75OMYcqm\nDQIDAQAB\n-----END RSA PUBLIC KEY-----\n"), - }, - }, - ), - }, - object: &PublicKeys{ - SearchResponse: SearchResponse{ - Count: 1, - }, - Keys: []PublicKey{ - &rsaPublicKey{ - key: key{ - id: "key-id", - creationDate: testNow, - changeDate: testNow, - sequence: 20211109, - resourceOwner: "ro", - algorithm: "RS256", - use: crypto.KeyUsageSigning, - }, - expiry: testNow, - publicKey: &rsa.PublicKey{ - E: 65537, - N: fromBase16("b2f5fd3f9f0917112ce42f8bf87ed676e15258be443f36deafb0b69bde2496b495eaad1b01cad84271b014e96f79386c636d348516da74a68a8c70fba882870c47b4218d8f49186ddf72727b9d80c21911c3e337c6e407ffb47c2f2767b0d164d8a1e9af95f6481bf8d9edfb2e3904b2529268c460256fafd0a677d29898f10b1d15128a695839fc08edd584e8335615b1d1d7277be65c532dca92ddc7050374868b117ea9154914ef9292b8443f13696e4fad50ded6bd90e5a6f7ed33be2ece31c6dd7a4253ee6cdc56787ddd1d5cd776614022db87d03bb22f23285b5a3167af8dacabbea40004471337d3781e8c5cca0ea5e27799b510e4ef938c61caa60d"), - }, - }, - }, - }, - }, - { - name: "preparePublicKeysQuery sql err", - prepare: preparePublicKeysQuery, - want: want{ - sqlExpectations: mockQueryErr( - regexp.QuoteMeta(preparePublicKeysStmt), - sql.ErrConnDone, - ), - err: func(err error) (error, bool) { - if !errors.Is(err, sql.ErrConnDone) { - return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false - } - return nil, true - }, - }, - object: (*PublicKeys)(nil), - }, - { - name: "preparePrivateKeysQuery no result", - prepare: preparePrivateKeysQuery, - want: want{ - sqlExpectations: mockQueries( - regexp.QuoteMeta(preparePrivateKeysStmt), - nil, - nil, - ), - err: func(err error) (error, bool) { - if !zerrors.IsNotFound(err) { - return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false - } - return nil, true - }, - }, - object: &PrivateKeys{Keys: []PrivateKey{}}, - }, - { - name: "preparePrivateKeysQuery found", - prepare: preparePrivateKeysQuery, - want: want{ - sqlExpectations: mockQueries( - regexp.QuoteMeta(preparePrivateKeysStmt), - preparePublicKeysCols, - [][]driver.Value{ - { - "key-id", - testNow, - testNow, - uint64(20211109), - "ro", - "RS256", - 0, - testNow, - []byte(`{"Algorithm": "enc", "Crypted": "cHJpdmF0ZUtleQ==", "CryptoType": 0, "KeyID": "id"}`), - }, - }, - ), - }, - object: &PrivateKeys{ - SearchResponse: SearchResponse{ - Count: 1, - }, - Keys: []PrivateKey{ - &privateKey{ - key: key{ - id: "key-id", - creationDate: testNow, - changeDate: testNow, - sequence: 20211109, - resourceOwner: "ro", - algorithm: "RS256", - use: crypto.KeyUsageSigning, - }, - expiry: testNow, - privateKey: &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("privateKey"), - }, - }, - }, - }, - }, - { - name: "preparePrivateKeysQuery sql err", - prepare: preparePrivateKeysQuery, - want: want{ - sqlExpectations: mockQueryErr( - regexp.QuoteMeta(preparePrivateKeysStmt), - sql.ErrConnDone, - ), - err: func(err error) (error, bool) { - if !errors.Is(err, sql.ErrConnDone) { - return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false - } - return nil, true - }, - }, - object: (*PrivateKeys)(nil), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err) - }) - } -} - -func fromBase16(base16 string) *big.Int { - i, ok := new(big.Int).SetString(base16, 16) - if !ok { - panic("bad number: " + base16) - } - return i -} - -const pubKey = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs38btwb3c7r0tMaQpGvB -mY+mPwMU/LpfuPoC0k2t4RsKp0fv40SMl50CRrHgk395wch8PMPYbl3+8TtYAJuy -rFALIj3Ff1UcKIk0hOH5DDsfh7/q2wFuncTmS6bifYo8CfSq2vDGnM7nZnEvxY/M -fSydZdcmIqlkUpfQmtzExw9+tSe5Dxq6gn5JtlGgLgZGt69r5iMMrTEGhhVAXzNu -MZbmlCoBru+rC8ITlTX/0V1ZcsSbL8tYWhthyu9x6yjo1bH85wiVI4gs0MhU8f2a -+kjL/KGZbR14Ua2eo6tonBZLC5DHWM2TkYXgRCDPufjcgmzN0Lm91E4P8KvBcvly -6QIDAQAB ------END PUBLIC KEY----- -` - -func TestQueries_GetPublicKeyByID(t *testing.T) { - now := time.Now() - future := now.Add(time.Hour) - - tests := []struct { - name string - eventstore func(*testing.T) *eventstore.Eventstore - encryption func(*testing.T) *crypto.MockEncryptionAlgorithm - want *rsaPublicKey - wantErr error - }{ - { - name: "filter error", - eventstore: expectEventstore( - expectFilterError(io.ErrClosedPipe), - ), - wantErr: io.ErrClosedPipe, - }, - { - name: "not found error", - eventstore: expectEventstore( - expectFilter(), - ), - wantErr: zerrors.ThrowNotFound(nil, "QUERY-Ahf7x", "Errors.Key.NotFound"), - }, - { - name: "decrypt error", - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher(key_repo.NewAddedEvent(context.Background(), - &eventstore.Aggregate{ - ID: "keyID", - Type: key_repo.AggregateType, - ResourceOwner: "instanceID", - InstanceID: "instanceID", - Version: key_repo.AggregateVersion, - }, - crypto.KeyUsageSigning, "alg", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("private"), - }, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("public"), - }, - future, - future, - )), - ), - ), - encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm { - encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)) - expect := encryption.EXPECT() - expect.Algorithm().Return("alg") - expect.DecryptionKeyIDs().Return([]string{}) - return encryption - }, - wantErr: zerrors.ThrowInternal(nil, "QUERY-Ie4oh", "Errors.Internal"), - }, - { - name: "parse error", - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher(key_repo.NewAddedEvent(context.Background(), - &eventstore.Aggregate{ - ID: "keyID", - Type: key_repo.AggregateType, - ResourceOwner: "instanceID", - InstanceID: "instanceID", - Version: key_repo.AggregateVersion, - }, - crypto.KeyUsageSigning, "alg", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("private"), - }, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("public"), - }, - future, - future, - )), - ), - ), - encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm { - encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)) - expect := encryption.EXPECT() - expect.Algorithm().Return("alg") - expect.DecryptionKeyIDs().Return([]string{"keyID"}) - expect.Decrypt([]byte("public"), "keyID").Return([]byte("foo"), nil) - return encryption - }, - wantErr: zerrors.ThrowInternal(nil, "QUERY-Kai2Z", "Errors.Internal"), - }, - { - name: "success", - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher(key_repo.NewAddedEvent(context.Background(), - &eventstore.Aggregate{ - ID: "keyID", - Type: key_repo.AggregateType, - ResourceOwner: "instanceID", - InstanceID: "instanceID", - Version: key_repo.AggregateVersion, - }, - crypto.KeyUsageSigning, "alg", - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("private"), - }, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "alg", - KeyID: "keyID", - Crypted: []byte("public"), - }, - future, - future, - )), - ), - ), - encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm { - encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)) - expect := encryption.EXPECT() - expect.Algorithm().Return("alg") - expect.DecryptionKeyIDs().Return([]string{"keyID"}) - expect.Decrypt([]byte("public"), "keyID").Return([]byte(pubKey), nil) - return encryption - }, - want: &rsaPublicKey{ - key: key{ - id: "keyID", - resourceOwner: "instanceID", - algorithm: "alg", - use: crypto.KeyUsageSigning, - }, - expiry: future, - publicKey: func() *rsa.PublicKey { - publicKey, err := crypto.BytesToPublicKey([]byte(pubKey)) - if err != nil { - panic(err) - } - return publicKey - }(), - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - q := &Queries{ - eventstore: tt.eventstore(t), - } - if tt.encryption != nil { - q.keyEncryptionAlgorithm = tt.encryption(t) - } - ctx := authz.NewMockContext("instanceID", "orgID", "loginClient") - key, err := q.GetPublicKeyByID(ctx, "keyID") - if tt.wantErr != nil { - require.ErrorIs(t, err, tt.wantErr) - return - } - require.NoError(t, err) - require.NotNil(t, key) - - got := key.(*rsaPublicKey) - assert.WithinDuration(t, tt.want.expiry, got.expiry, time.Second) - tt.want.expiry = time.Time{} - got.expiry = time.Time{} - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/internal/query/projection/instance_features.go b/internal/query/projection/instance_features.go index 443353c2e5..3c33ff6fdf 100644 --- a/internal/query/projection/instance_features.go +++ b/internal/query/projection/instance_features.go @@ -80,10 +80,6 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer { Event: feature_v2.InstanceImprovedPerformanceEventType, Reduce: reduceInstanceSetFeature[[]feature.ImprovedPerformanceType], }, - { - Event: feature_v2.InstanceWebKeyEventType, - Reduce: reduceInstanceSetFeature[bool], - }, { Event: feature_v2.InstanceDebugOIDCParentErrorEventType, Reduce: reduceInstanceSetFeature[bool], diff --git a/internal/repository/feature/feature_v2/eventstore.go b/internal/repository/feature/feature_v2/eventstore.go index 62fa568fca..25d0f270f6 100644 --- a/internal/repository/feature/feature_v2/eventstore.go +++ b/internal/repository/feature/feature_v2/eventstore.go @@ -24,7 +24,6 @@ func init() { eventstore.RegisterFilterEventMapper(AggregateType, InstanceUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]]) - eventstore.RegisterFilterEventMapper(AggregateType, InstanceWebKeyEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceDebugOIDCParentErrorEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceDisableUserTokenEvent, eventstore.GenericEventMapper[SetEvent[bool]]) diff --git a/internal/repository/feature/feature_v2/feature.go b/internal/repository/feature/feature_v2/feature.go index f75fae618b..a87042d72a 100644 --- a/internal/repository/feature/feature_v2/feature.go +++ b/internal/repository/feature/feature_v2/feature.go @@ -29,7 +29,6 @@ var ( InstanceUserSchemaEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyUserSchema) InstanceTokenExchangeEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyTokenExchange) InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance) - InstanceWebKeyEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyWebKey) InstanceDebugOIDCParentErrorEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDebugOIDCParentError) InstanceOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyOIDCSingleV1SessionTermination) InstanceDisableUserTokenEvent = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDisableUserTokenEvent) diff --git a/proto/zitadel/feature/v2/instance.proto b/proto/zitadel/feature/v2/instance.proto index 0455befb46..efbe5e3cdf 100644 --- a/proto/zitadel/feature/v2/instance.proto +++ b/proto/zitadel/feature/v2/instance.proto @@ -11,8 +11,8 @@ import "zitadel/feature/v2/feature.proto"; option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2;feature"; message SetInstanceFeaturesRequest{ - reserved 3, 6; - reserved "oidc_legacy_introspection", "actions"; + reserved 3, 6, 8; + reserved "oidc_legacy_introspection", "actions", "web_key"; optional bool login_default_org = 1 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; @@ -49,13 +49,6 @@ message SetInstanceFeaturesRequest{ } ]; - optional bool web_key = 8 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "true"; - description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated."; - } - ]; - optional bool debug_oidc_parent_error = 9 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; @@ -125,8 +118,8 @@ message GetInstanceFeaturesRequest { } message GetInstanceFeaturesResponse { - reserved 4, 7; - reserved "oidc_legacy_introspection", "actions"; + reserved 4, 7, 9; + reserved "oidc_legacy_introspection", "actions", "web_key"; zitadel.object.v2.Details details = 1; FeatureFlag login_default_org = 2 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { @@ -163,13 +156,6 @@ message GetInstanceFeaturesResponse { } ]; - FeatureFlag web_key = 9 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "true"; - description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated."; - } - ]; - FeatureFlag debug_oidc_parent_error = 10 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; diff --git a/proto/zitadel/feature/v2beta/instance.proto b/proto/zitadel/feature/v2beta/instance.proto index 8028305fe4..7968668e50 100644 --- a/proto/zitadel/feature/v2beta/instance.proto +++ b/proto/zitadel/feature/v2beta/instance.proto @@ -11,8 +11,8 @@ import "zitadel/feature/v2beta/feature.proto"; option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta;feature"; message SetInstanceFeaturesRequest{ - reserved 3, 6; - reserved "oidc_legacy_introspection", "actions"; + reserved 3, 6, 8; + reserved "oidc_legacy_introspection", "actions", "web_key"; optional bool login_default_org = 1 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; @@ -49,13 +49,6 @@ message SetInstanceFeaturesRequest{ } ]; - optional bool web_key = 8 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "true"; - description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated."; - } - ]; - optional bool debug_oidc_parent_error = 9 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true"; @@ -91,8 +84,8 @@ message GetInstanceFeaturesRequest { } message GetInstanceFeaturesResponse { - reserved 4, 7; - reserved "oidc_legacy_introspection", "actions"; + reserved 4, 7, 9; + reserved "oidc_legacy_introspection", "actions", "web_key"; zitadel.object.v2beta.Details details = 1; FeatureFlag login_default_org = 2 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { @@ -129,13 +122,6 @@ message GetInstanceFeaturesResponse { } ]; - FeatureFlag web_key = 9 [ - (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { - example: "true"; - description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated."; - } - ]; - FeatureFlag debug_oidc_parent_error = 10 [ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { example: "true";