From 4d1e1e0bcb4cbc29880e53029264c0fa2b064ad3 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Wed, 4 Sep 2024 13:23:54 +0200 Subject: [PATCH] Show details for error messages where they were missing (#32534) Closes #32533 Signed-off-by: Alexander Schwartz Signed-off-by: Jon Koops Co-authored-by: Jon Koops --- .../admin/messages/messages_es.properties | 24 ++++---- .../admin/messages/messages_pl.properties | 24 ++++---- .../admin/messages/messages_zh_CN.properties | 26 ++++---- .../admin/messages/messages_en.properties | 35 +++++------ .../src/client-scopes/ClientScopesSection.tsx | 2 +- .../authorization/AuthorizationExport.tsx | 2 +- .../src/identity-providers/add/AddMapper.tsx | 4 +- .../src/realm-settings/ClientProfileForm.tsx | 4 +- .../admin-ui/src/realm-settings/LoginTab.tsx | 2 +- .../src/realm-settings/NewClientPolicy.tsx | 10 ++-- .../src/realm-settings/PoliciesTab.tsx | 2 +- .../src/realm-settings/ProfilesTab.tsx | 2 +- .../localization/RealmOverrides.tsx | 6 +- .../admin-ui/src/sessions/RevocationModal.tsx | 6 +- .../org/keycloak/email/EmailException.java | 17 ++++-- .../models/utils/KeycloakModelUtils.java | 4 +- .../email/DefaultEmailSenderProvider.java | 14 ++++- .../services/error/KeycloakErrorHandler.java | 59 +++++++++++-------- .../resources/admin/UserResource.java | 4 +- .../admin/authentication/FlowTest.java | 4 +- .../error/UncaughtErrorPageTest.java | 22 +++---- 21 files changed, 150 insertions(+), 123 deletions(-) diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties index b09d8ca3ef..2a0ba2699d 100644 --- a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties +++ b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_es.properties @@ -187,7 +187,7 @@ searchByName=Buscar por nombre executorTypeSwitchHelpText=Texto de ayuda del interruptor de tipo de ejecutor attributeConsumingServiceNameHelp=Nombre del perfil de Servicio de Consumo de Atributos para anunciar en los metadatos del SP. overrideActionTokens=Sobreescribir tokens de acción -deleteGrantsError=Error al eliminar concesiones. +deleteGrantsError=Error al eliminar concesiones: {{error}} defaultGroupAdded_other=Se añadieron {{count}} grupos a los grupos predeterminados used.SPECIFIC_CLIENTS=Clientes específicos freeMemory=Memoria libre @@ -420,7 +420,7 @@ displayOnConsentScreen=Mostrar en la pantalla de consentimiento noClientPolicies=Sin políticas de cliente defaultAdminInitiatedActionLifespanHelp=Tiempo máximo antes de que un permiso de acción enviado a un usuario por un administrador caduque. Se recomienda que este valor sea largo para permitir a los administradores enviar correos electrónicos a usuarios que actualmente están desconectados. El tiempo de espera predeterminado se puede anular inmediatamente antes de emitir el token. syncUsersSuccess=Sincronización de usuarios finalizada con éxito. -updatedCredentialMoveError=La configuración de credenciales de usuario no se ha guardado +updatedCredentialMoveError=La configuración de credenciales de usuario no se ha guardado: {{error}} searchForRoles=Buscar rol por nombre refresh=Actualizar roleDeletedSuccess=El rol ha sido eliminado @@ -563,7 +563,7 @@ jsonType.label=Tipo JSON de reclamación fullScopeAllowed=Se permite el alcance completo syncModes.inherit=Heredar masterSamlProcessingUrlHelp=Si está configurada, esta URL se utilizará para cada enlace tanto para el Consumidor de Aserciones del SP como para los Servicios de Cierre de Sesión Únicos. Esto se puede anular individualmente para cada enlace y servicio en la Configuración de Punto Final SAML de Grano Fino. -addedGroupMembershipError=Error al agregar la membresía del grupo +addedGroupMembershipError=Error al agregar la membresía del grupo: {{error}} authenticatorAttachment.platform=Plataforma configSaveSuccess=Configuración guardada con éxito regenerate=Regenerar @@ -737,7 +737,7 @@ eventTypes.EXECUTE_ACTIONS_ERROR.name=Error al ejecutar acciones. path=Ruta. overwritten=Sobrescrito. mapperNameHelp=Nombre del mapeador. -deleteProviderError=Error al eliminar el proveedor. +deleteProviderError=Error al eliminar el proveedor: {{error}} supportedLocalesHelp=Los idiomas admitidos para este reino. El usuario elige uno de estos idiomas en la pantalla de inicio de sesión. comparisonHelp=Especifica el método de comparación usado para evaluar las clases o declaraciones de contexto solicitadas. El valor predeterminado es "Exacto". generatedIdTokenIsDisabled=El token ID generado está desactivado cuando no se selecciona ningún usuario. @@ -861,7 +861,7 @@ back=Atrás. deleteScopeConfirm=Si eliminas este alcance de autorización, algunas permisiones se verán afectadas. updateOtpSuccess=Política OTP actualizada correctamente. title=Autenticación. -deleteAttributeError=Atributo no eliminado. +deleteAttributeError=Atributo no eliminado: {{error}} enableClientSignatureRequiredExplain=Si habilitas "Firma del cliente requerida", el adaptador de este cliente se actualizará. Es posible que necesites descargar un nuevo adaptador para este cliente. Debes generar o importar claves para este cliente; de lo contrario, la autenticación no funcionará. policiesConfigTypes.formView=Vista de formulario. residentKey.No=No. @@ -1103,13 +1103,13 @@ archiveFormat=Formato de archivo. requestObjectEncryptionHelp=Algoritmo JWE, que el cliente necesita usar al enviar el objeto de solicitud OIDC especificado por los parámetros 'request' o 'request_uri'. Si se establece en 'cualquiera', la encriptación es opcional y se permite cualquier algoritmo. importSuccess=Nuevo certificado importado. attributeConsumingServiceName=Nombre del Servicio Consumidor de Atributos. -invalidJsonError=No se puede guardar el perfil de usuario, la información proporcionada no es un JSON válido. +invalidJsonError=No se puede guardar el perfil de usuario, la información proporcionada no es un JSON válido: {{error}} promptHelp=Especifica si el Servidor de Autorización solicita al Usuario Final una nueva autenticación y consentimiento. deleteBtn=Eliminar. defaultLocale=Idioma predeterminado. addLdapWizardDescription=Texto necesario aquí. aggregate.attrs.label=Agregar valores de atributo. -removedGroupMembershipError=Error al eliminar la membresía del grupo. +removedGroupMembershipError=Error al eliminar la membresía del grupo: {{error}} allowPasswordAuthenticationHelp=Habilitar/deshabilitar la posibilidad de autenticación de nombre de usuario/contraseña contra la base de datos Kerberos. deleteExecutorSuccess=¡Éxito! El ejecutor fue eliminado. eventTypes.SEND_RESET_PASSWORD_ERROR.name=Error al enviar restablecer contraseña. @@ -1199,7 +1199,7 @@ fineGrainOpenIdConnectConfigurationHelp=Esta sección se utiliza para configurar searchForUserDescription=Este reino puede tener un proveedor federado. Ver todos los usuarios puede hacer que el sistema se vuelva lento, pero se puede hacer buscando "*". Por favor, busca un usuario arriba. expirationHelp=Establece la expiración para eventos. Los eventos caducados se eliminan periódicamente de la base de datos. webAuthnPolicySignatureAlgorithmsHelp=Qué algoritmos de firma se deben usar para la Asertación de Autenticación. -setToNowError=¡Error\! No se pudo establecer 'No antes' en la fecha y hora actuales. +setToNowError=¡Error\! No se pudo establecer 'No antes' en la fecha y hora actuales: {{error}} eventTypes.UNREGISTER_NODE_ERROR.description=Error al anular el registro del nodo clientScopeTypes.optional=Opcional nameIdFormat=Formato de ID de nombre @@ -1761,7 +1761,7 @@ setPasswordFor=Establecer contraseña para {{username}} eventTypes.CODE_TO_TOKEN.name=Código a token updateUserLocale=Actualizar idioma del usuario whoWillAppearPopoverTextUsers=Los grupos son jerárquicos. Cuando seleccionas Membresía Directa, solo ves el grupo secundario al que se unió el usuario. Los grupos ancestrales no están incluidos. -mapperCreateError=Error al crear el mapeador. +mapperCreateError=Error al crear el mapeador: {{error}} resetBtn=Reiniciar mode=Modo kc.realm.name=Reino @@ -1900,7 +1900,7 @@ targetOptions.local=LOCAL addMessageBundleError=Error al crear el paquete de mensajes, {{error}} pkceMethodHelp=Método PKCE para usar encryption=Cifrado -addExecutorError=Ejecutor no creado +addExecutorError=Ejecutor no creado: {{error}} scopePermissions.clients.manage-description=Políticas que deciden si un administrador puede administrar este cliente vendor=Proveedor roleRemoveAssociatedText=Esta acción eliminará {{rol}} de {{nombreRol}}. Todos los roles asociados a {{rol}} también se eliminarán. @@ -2128,7 +2128,7 @@ includeInAccessToken.label=Agregar al token de acceso samlKeysExportSuccess=Claves exportadas con éxito usersInRole=Usuarios en rol policyProvider.group=Define condiciones para tus permisos donde se permite un conjunto de uno o más grupos (y sus jerarquías) para acceder a un objeto. -updatedUserProfileError=La configuración del perfil de usuario no se ha guardado +updatedUserProfileError=La configuración del perfil de usuario no se ha guardado: {{error}} emptyPermissions=Sin permisos deletePermission=¿Eliminar permiso permanentemente? selectUser=Seleccionar un usuario cuya identidad se usará para consultar permisos desde el servidor. @@ -2656,7 +2656,7 @@ emptyAuthorizationInstructions=Si deseas crear alcances de autorización, haz cl subjectHelp=Una expresión regular para validar el DN de sujeto en el Certificado del Cliente. Usa "(.*?)(?\:$)" para coincidir con todo tipo de expresiones. updatePolicySuccess=Política actualizada con éxito eventTypes.CUSTOM_REQUIRED_ACTION.name=Acción requerida personalizada -updateExecutorError=Executor no actualizado +updateExecutorError=Executor no actualizado: {{error}} clientIdHelpHelp=ID de cliente del cliente al que se asignarán los mapeos de roles LDAP. Aplicable solo si 'Usar Mapeo de Roles de Reino' es falso. createdAt=Creado en moveGroupEmpty=No hay subgrupos diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties index 434e5f200a..4d3b74b623 100644 --- a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties +++ b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_pl.properties @@ -187,7 +187,7 @@ searchByName=Szukaj według nazwy executorTypeSwitchHelpText=Tekst pomocy przełącznika typu wykonawcy attributeConsumingServiceNameHelp=Nazwa profilu usługi konsumującej atrybuty do reklamowania w metadanych SP. overrideActionTokens=Nadpisz tokeny akcji -deleteGrantsError=Błąd usuwania grantów. +deleteGrantsError=Błąd usuwania grantów: {{error}} defaultGroupAdded_other=Dodano {{count}} grup do domyślnych grup used.SPECIFIC_CLIENTS=Specyficzni klienci freeMemory=Wolna pamięć @@ -420,7 +420,7 @@ displayOnConsentScreen=Wyświetl na ekranie zgody noClientPolicies=Brak polityk klienta defaultAdminInitiatedActionLifespanHelp=Maksymalny czas przed wygaśnięciem pozwolenia na działanie wysłanego do użytkownika przez administratora. Zaleca się, aby wartość ta była długa, aby umożliwić administratorom wysyłanie e-maili do użytkowników, którzy są obecnie offline. Domyślny limit czasu można zastąpić bezpośrednio przed wydaniem tokenu. syncUsersSuccess=Synchronizacja użytkowników zakończona pomyślnie. -updatedCredentialMoveError=Konfiguracja poświadczeń użytkownika nie została zapisana +updatedCredentialMoveError=Konfiguracja poświadczeń użytkownika nie została zapisana: {{error}} searchForRoles=Wyszukaj rolę według nazwy refresh=Odśwież roleDeletedSuccess=Rola została usunięta @@ -563,7 +563,7 @@ jsonType.label=Typ JSON roszczenia fullScopeAllowed=Pełny zakres dozwolony syncModes.inherit=Dziedzicz masterSamlProcessingUrlHelp=Jeśli skonfigurowane, ten URL zostanie użyty dla każdego wiązania zarówno do Usługi Konsumenckiej Zapewnienia SP, jak i Usług Jednokrotnego Wylogowania. Może to być indywidualnie zmienione dla każdego wiązania i usługi w Konfiguracji Punktów Końcowych SAML o Drobnoziarnistym Ustawieniu. -addedGroupMembershipError=Błąd dodania członkostwa w grupie +addedGroupMembershipError=Błąd dodania członkostwa w grupie: {{error}} authenticatorAttachment.platform=Platforma configSaveSuccess=Pomyślnie zapisano konfigurację wykonania regenerate=Wygeneruj ponownie @@ -737,7 +737,7 @@ eventTypes.EXECUTE_ACTIONS_ERROR.name=Błąd wykonania akcji path=Ścieżka overwritten=Nadpisany mapperNameHelp=Nazwa mapera -deleteProviderError=Błąd usuwania dostawcy +deleteProviderError=Błąd usuwania dostawcy: {{error}} supportedLocalesHelp=Lokalizacje obsługiwane przez tę domenę. Użytkownik wybiera jedną z tych lokalizacji na ekranie logowania. comparisonHelp=Określa metodę porównania używaną do oceny żądanych klas kontekstu lub oświadczeń. Domyślnie jest to "Dokładne". generatedIdTokenIsDisabled=Wygenerowany token ID jest wyłączony, gdy nie wybrano użytkownika @@ -860,7 +860,7 @@ back=Wstecz deleteScopeConfirm=Jeśli usuniesz ten zakres autoryzacji, niektóre uprawnienia zostaną dotknięte. updateOtpSuccess=Pomyślnie zaktualizowano politykę OTP title=Autentykacja -deleteAttributeError=Atrybut nie został usunięty +deleteAttributeError=Atrybut nie został usunięty: {{error}} enableClientSignatureRequiredExplain=Jeśli włączysz "Wymagany podpis klienta", adapter tego klienta zostanie zaktualizowany. Może być konieczne pobranie nowego adaptera dla tego klienta. Musisz wygenerować lub zaimportować klucze dla tego klienta, inaczej autentykacja nie zadziała. policiesConfigTypes.formView=Widok formularza residentKey.No=Nie @@ -1102,13 +1102,13 @@ archiveFormat=Format archiwum requestObjectEncryptionHelp=Algorytm JWE, który klient musi użyć podczas wysyłania obiektu żądania OIDC określonego przez parametry 'request' lub 'request_uri'. Jeśli ustawione na 'any', szyfrowanie jest opcjonalne, a dozwolony jest dowolny algorytm. importSuccess=Nowy certyfikat został zaimportowany attributeConsumingServiceName=Nazwa usługi konsumującej atrybuty -invalidJsonError=Nie można zapisać profilu użytkownika, dostarczone informacje nie są prawidłowym JSON-em. +invalidJsonError=Nie można zapisać profilu użytkownika, dostarczone informacje nie są prawidłowym JSON-em: {{error}} promptHelp=Określa, czy Serwer Autoryzacji ma prosić Koniec-Użytkownika o ponowne uwierzytelnienie i wyrażenie zgody. deleteBtn=Usuń defaultLocale=Domyślne ustawienia regionalne addLdapWizardDescription=Tekst potrzebny tutaj aggregate.attrs.label=Zagreguj wartości atrybutów -removedGroupMembershipError=Błąd usuwania przynależności do grupy +removedGroupMembershipError=Błąd usuwania przynależności do grupy: {{error}} allowPasswordAuthenticationHelp=Włącz/wyłącz możliwość uwierzytelniania za pomocą nazwy użytkownika/hasła w bazie danych Kerberos deleteExecutorSuccess=Sukces! Wykonawca został usunięty. eventTypes.SEND_RESET_PASSWORD_ERROR.name=Błąd wysyłania resetowania hasła @@ -1198,7 +1198,7 @@ fineGrainOpenIdConnectConfigurationHelp=Ta sekcja służy do konfiguracji zaawan searchForUserDescription=Ten obszar może mieć dostawcę federacyjnego. Wyświetlanie wszystkich użytkowników może spowodować spowolnienie systemu, ale można to zrobić, wyszukując "*". Proszę szukać użytkownika powyżej. expirationHelp=Ustawia wygaśnięcie zdarzeń. Wygasłe zdarzenia są regularnie usuwane z bazy danych. webAuthnPolicySignatureAlgorithmsHelp=Jakie algorytmy podpisu powinny być używane do tworzenia twierdzeń uwierzytelniających. -setToNowError=Błąd! Nie można ustawić "notBefore" na bieżącą datę i godzinę. +setToNowError=Błąd! Nie można ustawić "notBefore" na bieżącą datę i godzinę: {{error}} eventTypes.UNREGISTER_NODE_ERROR.description=Błąd wyrejestrowywania węzła clientScopeTypes.optional=Opcjonalne nameIdFormat=Format nazwy ID @@ -1760,7 +1760,7 @@ setPasswordFor=Ustaw hasło dla {{username}} eventTypes.CODE_TO_TOKEN.name=Kod na token updateUserLocale=Zaktualizuj ustawienia regionalne użytkownika whoWillAppearPopoverTextUsers=Grupy są hierarchiczne. Po wybraniu Bezpośredniego Członkostwa widoczna jest tylko grupa potomna, do której dołączył użytkownik. Grupy przodków nie są uwzględnione. -mapperCreateError=Błąd tworzenia mapera. +mapperCreateError=Błąd tworzenia mapera: {{error}} resetBtn=Resetuj mode=Tryb kc.realm.name=Obszar @@ -1899,7 +1899,7 @@ targetOptions.local=LOKALNY addMessageBundleError=Błąd podczas tworzenia pakietu wiadomości: {{error}} pkceMethodHelp=Metoda PKCE do użycia encryption=Szyfrowanie -addExecutorError=Nie utworzono wykonawcy +addExecutorError=Nie utworzono wykonawcy: {{error}} scopePermissions.clients.manage-description=Polityki decydujące, czy administrator może zarządzać tym klientem vendor=Dostawca roleRemoveAssociatedText=Ta akcja usunie {{role}} z {{roleName}}. Wszystkie powiązane role z {{role}} zostaną również usunięte. @@ -2127,7 +2127,7 @@ includeInAccessToken.label=Dodaj do tokena dostępu samlKeysExportSuccess=Klucze zostały pomyślnie wyeksportowane usersInRole=Użytkownicy w roli policyProvider.group=Zdefiniuj warunki uprawnień, w których zbiór jednej lub więcej grup (i ich hierarchie) ma dostęp do obiektu. -updatedUserProfileError=Konfiguracja profilu użytkownika nie została zapisana +updatedUserProfileError=Konfiguracja profilu użytkownika nie została zapisana: {{error}} emptyPermissions=Brak uprawnień deletePermission=Usuń uprawnienia na stałe? selectUser=Wybierz użytkownika, którego tożsamość zostanie użyta do zapytania o uprawnienia z serwera. @@ -2655,7 +2655,7 @@ emptyAuthorizationInstructions=Jeśli chcesz utworzyć zakresy autoryzacji, pros subjectHelp=Wyrażenie regularne do walidacji Distinguished Name (DN) w certyfikacie klienta. Użyj "(.*?)(?\:$)" aby dopasować wszystkie rodzaje wyrażeń. updatePolicySuccess=Zaktualizowano politykę pomyślnie eventTypes.CUSTOM_REQUIRED_ACTION.name=Custom required action -updateExecutorError=Executor nie został zaktualizowany +updateExecutorError=Executor nie został zaktualizowany: {{error}} clientIdHelpHelp=Identyfikator klienta, do którego zostaną mapowane odwzorowania ról LDAP. Dotyczy tylko sytuacji, gdy opcja 'Use Realm Roles Mapping' jest ustawiona na false. createdAt=Utworzono o moveGroupEmpty=Brak podgrup diff --git a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties index a4b5f64690..82988ef71e 100644 --- a/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties +++ b/js/apps/admin-ui/maven-resources-community/theme/keycloak.v2/admin/messages/messages_zh_CN.properties @@ -179,7 +179,7 @@ searchByName=按名称搜索 executorTypeSwitchHelpText=执行器类型切换帮助文本 attributeConsumingServiceNameHelp=要在 SP 元数据中通告的属性消费服务配置文件的名称。 overrideActionTokens=覆盖操作令牌 -deleteGrantsError=删除授权时出错。 +deleteGrantsError=删除授权时出错。 {{error}} defaultGroupAdded_other={{count}} 个新群组已被添加到默认群组 used.SPECIFIC_CLIENTS=特定客户 freeMemory=空余内存 @@ -410,7 +410,7 @@ userInfoUrl=用户信息网址 displayOnConsentScreen=在许可上显示 noClientPolicies=无客户端策略 syncUsersSuccess=用户同步成功完成。 -updatedCredentialMoveError=尚未保存用户凭据设置 +updatedCredentialMoveError=尚未保存用户凭据设置 {{error}} shortVerificationUriTooltip=如果设置,此值将在设备授权流程中作为 verification_uri 返回。此 uri 需要重定向到 {server-root}/realms/{realm}/device searchForRoles=按名称搜索角色 refresh=刷新 @@ -554,7 +554,7 @@ jsonType.label=声明的 JSON 类型 fullScopeAllowed=允许全范围 syncModes.inherit=继承 masterSamlProcessingUrlHelp=如果配置,则此 URL 将用于每个绑定到 SP 的断言消费者和单点注销服务。这可以在 Fine Grain SAML 端点配置中为每个绑定和服务单独覆写。 -addedGroupMembershipError=添加群组成员身份时出错 +addedGroupMembershipError=添加群组成员身份时出错 {{error}} authenticatorAttachment.platform=平台 configSaveSuccess=成功保存配置 regenerate=重新生成 @@ -724,7 +724,7 @@ createClientPolicyError=无法创建策略,因为:{{error}} eventTypes.EXECUTE_ACTIONS_ERROR.name=执行动作错误 path=路经 overwritten=已被覆写 -deleteProviderError=删除密钥供应商时出错 +deleteProviderError=删除密钥供应商时出错 {{error}} supportedLocalesHelp=该领域支持的语言环境。用户在登录屏幕上选择这些语言环境之一。 comparisonHelp=指定用于评估请求的上下文类或语句的比较方法。默认为"Exact"。 generatedIdTokenIsDisabled=未选择用户时禁用生成的 ID 令牌 @@ -845,7 +845,7 @@ back=返回 deleteScopeConfirm=如果删除此授权范围,部分权限将受到影响。 updateOtpSuccess=OTP 策略成功更新 title=身份验证 -deleteAttributeError=属性未删除 +deleteAttributeError=属性未删除 {{error}} enableClientSignatureRequiredExplain=如果启用"需要客户端签名",此客户端的适配器将被更新。您可能需要为此客户端下载一个新的适配器。您需要为此客户端生成或导入密钥,否则身份验证不管用。 policiesConfigTypes.formView=表单视图 residentKey.No=否 @@ -1084,13 +1084,13 @@ archiveFormat=存档格式 requestObjectEncryptionHelp=JWE算法,客户端发送'request'或'request_uri'参数指定的OIDC请求对象时需要使用的JWE算法。如果设置为'任何',加密是可选的,允许任何算法。 importSuccess=导入新证书 attributeConsumingServiceName=属性消费服务名称 -invalidJsonError=无法保存用户配置文件,提供的信息不是有效的 JSON。 +invalidJsonError=无法保存用户配置文件,提供的信息不是有效的 JSON。 {{error}} promptHelp=指定授权服务器是否提示最终用户重新验证和授权。 deleteBtn=删除 defaultLocale=默认语言环境 addLdapWizardDescription=此处需要文本 aggregate.attrs.label=聚合属性值 -removedGroupMembershipError=移除群组成员身份时出错 +removedGroupMembershipError=移除群组成员身份时出错 {{error}} allowPasswordAuthenticationHelp=启用/禁用针对 Kerberos 数据库的用户名/密码身份验证的可能性 deleteExecutorSuccess=成功!执行器被删除。 eventTypes.SEND_RESET_PASSWORD_ERROR.name=发送重置密码错误 @@ -1179,7 +1179,7 @@ fineGrainOpenIdConnectConfigurationHelp=此部分用于配置此客户端与 Ope searchForUserDescription=此领域可能具有联合提供程序。查看所有用户可能会导致系统变慢,但可以通过搜索来完成。请通过上面的搜索框搜索用户。 expirationHelp=设置事件的到期时间。过期的事件会定期从数据库中清除。 webAuthnPolicySignatureAlgorithmsHelp=身份验证断言应该使用什么签名算法。 -setToNowError=错误!无法设置为当前日期和时间。 +setToNowError=错误!无法设置为当前日期和时间。 {{error}} eventTypes.UNREGISTER_NODE_ERROR.description=注销节点错误 clientScopeTypes.optional=非必需 nameIdFormat=姓名ID格式 @@ -1731,7 +1731,7 @@ setPasswordFor=设置{{username}}的密码 eventTypes.CODE_TO_TOKEN.name=用于交换令牌的代码 updateUserLocale=更新用户区域设置 whoWillAppearPopoverTextUsers=群组是分层的。选择“直接群组成员资格”时,只会看到用户直接加入的子组,而不包括父级群组。 -mapperCreateError=创建映射器时出错。 +mapperCreateError=创建映射器时出错。 {{error}} resetBtn=重置 mode=模式 kc.realm.name=领域 @@ -1862,7 +1862,7 @@ targetOptions.local=本地 addMessageBundleError=创建消息包时出错,{{error}} pkceMethodHelp=使用的 PKCE 方法 encryption=加密 -addExecutorError=未创建执行器 +addExecutorError=未创建执行器 {{error}} scopePermissions.clients.manage-description=决定管理员是否可以管理此客户端的策略 vendor=供应商 roleRemoveAssociatedText=此操作将从{{roleName}}中移除{{role}}。{{role}}的所有关联角色也将被移除。 @@ -2085,8 +2085,8 @@ includeInAccessToken.label=添加到访问令牌 samlKeysExportSuccess=成功导出密钥 usersInRole=角色中的用户 policyProvider.group=为您的权限定义条件,允许一组一个或多个组(及其层次结构)访问一个对象。 -updatedUserProfileError=用户资料的配置尚未保存 -密码策略.lowerCase=密码字符串中要求的小写字母的个数。 +updatedUserProfileError=用户资料的配置尚未保存 {{error}} +passwordPoliciesHelp.lowerCase=密码字符串中要求的小写字母的个数。 emptyPermissions=无权限 deletePermission=永久删除权限? selectUser=选择一个用户,其身份将用于从服务器查询权限。 @@ -2605,7 +2605,7 @@ emptyAuthorizationInstructions=如果要创建授权范围,请点击下方按 subjectHelp=用于验证客户端证书中的主题 DN 的正则表达式。使用 "(.*?)(?\:$)" 匹配所有类型的表达式。 updatePolicySuccess=成功更新策略 eventTypes.CUSTOM_REQUIRED_ACTION.name=自定义所需操作 -updateExecutorError=执行器未更新 +updateExecutorError=执行器未更新 {{error}} clientIdHelpHelp=LDAP 角色映射将映射到的客户端的客户端ID。仅当'使用领域角色映射'为假时适用。 createdAt=创建时间 moveGroupEmpty=无子级群组 diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 962de5dedd..0b1101ed17 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -197,7 +197,7 @@ searchByName=Search by name executorTypeSwitchHelpText=Executor Type Switch Help Text attributeConsumingServiceNameHelp=Name of the Attribute Consuming Service profile to advertise in the SP metadata. overrideActionTokens=Override Action Tokens -deleteGrantsError=Error deleting grants. +deleteGrantsError=Error deleting grants: {{error}} defaultGroupAdded_other=Added {{count}} groups to the default groups used.SPECIFIC_CLIENTS=Specific clients freeMemory=Free memory @@ -286,7 +286,7 @@ minuteHelp=Defines the minute when the policy MUST be granted. You can also prov updateCibaSuccess=CIBA policy successfully updated newRoleNameHelp=The new role name. The new name format corresponds to where in the access token the role will be mapped to. So, a new name of 'myapp.newname' will map the role to that position in the access token. A new name of 'newname' will map the role to the realm roles in the token. mapperTypeFullNameLdapMapper=full-name-ldap-mapper -searchUserByAttributeMissingKeyError=Specify a attribute key +searchUserByAttributeMissingKeyError=Specify an attribute key eventTypes.INVALID_SIGNATURE.name=Invalid signature topLevelFlowTypeHelp=What kind of top level flow is it? Type 'client' is used for authentication of clients (applications) when generic is for users and everything else authDetailsHelp=Export and download all resource settings for this resource server. @@ -435,7 +435,7 @@ displayOnConsentScreen=Display on consent screen noClientPolicies=No client policies defaultAdminInitiatedActionLifespanHelp=Maximum time before an action permit sent to a user by administrator is expired. This value is recommended to be long to allow administrators to send e-mails for users that are currently offline. The default timeout can be overridden immediately before issuing the token. syncUsersSuccess=Sync of users finished successfully. -updatedCredentialMoveError=User Credential configuration hasn't been saved +updatedCredentialMoveError=User Credential configuration hasn't been saved: {{error}} searchForRoles=Search role by name refresh=Refresh roleDeletedSuccess=The role has been deleted @@ -578,7 +578,7 @@ jsonType.label=Claim JSON Type fullScopeAllowed=Full scope allowed syncModes.inherit=Inherit masterSamlProcessingUrlHelp=If configured, this URL will be used for every binding to both the SP's Assertion Consumer and Single Logout Services. This can be individually overridden for each binding and service in the Fine Grain SAML Endpoint Configuration. -addedGroupMembershipError=Error adding group membership +addedGroupMembershipError=Error adding group membership: {{error}} authenticatorAttachment.platform=Platform configSaveSuccess=Successfully saved the config regenerate=Regenerate @@ -764,7 +764,7 @@ overwritten=Overwritten mapperNameHelp=Name of the mapper keyProviderMapperNameHelp=Display name of provider when linked in admin console. providerIdHelp=Provider ID -deleteProviderError=Error deleting the provider +deleteProviderError=Error deleting the provider: {{error}} supportedLocalesHelp=The locales to support for this realm. The user chooses one of these locales on the login screen. comparisonHelp=Specifies the comparison method used to evaluate the requested context classes or statements. The default is "Exact". generatedIdTokenIsDisabled=Generated id token is disabled when no user is selected @@ -886,7 +886,7 @@ back=Back deleteScopeConfirm=If you delete this authorization scope, some permissions will be affected. updateOtpSuccess=OTP policy successfully updated title=Authentication -deleteAttributeError=Attribute not deleted +deleteAttributeError=Attribute not deleted: {{error}} enableClientSignatureRequiredExplain=If you enable "Client signature required", the adapter of this client will be updated. You may need to download a new adapter for this client. You need to generate or import keys for this client otherwise the authentication will not work. policiesConfigTypes.formView=Form view residentKey.No=No @@ -1079,6 +1079,7 @@ clientScope=Client scope inheritedFrom=Inherited from clientScopeSearch.name=Name deleteConditionSuccess=The condition has been deleted +deleteConditionError=Failed to delete condition\: {{error}} clientProfile=Client profile details syncAllUsers=Sync all users allowedClockSkewHelp=Clock skew in seconds that is tolerated when validating identity provider tokens. Default value is zero. @@ -1145,15 +1146,15 @@ archiveFormat=Archive format requestObjectEncryptionHelp=JWE algorithm, which client needs to use when sending OIDC request object specified by 'request' or 'request_uri' parameters. If set to 'any', encryption is optional and any algorithm is allowed. importSuccess=New certificate imported attributeConsumingServiceName=Attribute Consuming Service Name -invalidJsonError=Unable to save user profile, the provided information is not valid JSON. -invalidJsonClientProfilesError=Unable to save client profiles, the provided information is not valid JSON. -invalidJsonClientPoliciesError=Unable to save client policies, the provided information is not valid JSON. +invalidJsonError=Unable to save user profile, the provided information is not valid JSON: {{error}} +invalidJsonClientProfilesError=Unable to save client profiles, the provided information is not valid JSON: {{error}} +invalidJsonClientPoliciesError=Unable to save client policies, the provided information is not valid JSON: {{error}} promptHelp=Specifies whether the Authorization Server prompts the End-User for re-authentication and consent. deleteBtn=Delete defaultLocale=Default locale addLdapWizardDescription=Text needed here aggregate.attrs.label=Aggregate attribute values -removedGroupMembershipError=Error removing group membership +removedGroupMembershipError=Error removing group membership: {{error}} allowPasswordAuthenticationHelp=Enable/disable possibility of username/password authentication against Kerberos database deleteExecutorSuccess=Success\! The executor was deleted. eventTypes.SEND_RESET_PASSWORD_ERROR.name=Send reset password error @@ -1244,7 +1245,7 @@ fineGrainOpenIdConnectConfigurationHelp=This section is used to configure advanc searchForUserDescription=This realm may have a federated provider. Viewing all users may cause the system to slow down, but it can be done by searching for "*". Please search for a user above. expirationHelp=Sets the expiration for events. Expired events are periodically deleted from the database. webAuthnPolicySignatureAlgorithmsHelp=What signature algorithms should be used for Authentication Assertion. -setToNowError=Error\! Failed to set notBefore to current date and time. +setToNowError=Error\! Failed to set notBefore to current date and time: {{error}} eventTypes.UNREGISTER_NODE_ERROR.description=Unregister node error clientScopeTypes.optional=Optional nameIdFormat=Name ID format @@ -1810,7 +1811,7 @@ setPasswordFor=Set password for {{username}} eventTypes.CODE_TO_TOKEN.name=Code to token updateUserLocale=Update User Locale whoWillAppearPopoverTextUsers=Groups are hierarchical. When you select Direct Membership, you see only the child group that the user joined. Ancestor groups are not included. -mapperCreateError=Error creating mapper. +mapperCreateError=Error creating mapper: {{error}} resetBtn=Reset mode=Mode kc.realm.name=Realm @@ -1949,7 +1950,7 @@ targetOptions.local=LOCAL addTranslationError=Error creating translation, {{error}} pkceMethodHelp=PKCE Method to use encryption=Encryption -addExecutorError=Executor not created +addExecutorError=Executor not created: {{error}} scopePermissions.clients.manage-description=Policies that decide if an administrator can manage this client vendor=Vendor roleRemoveAssociatedText=This action will remove {{role}} from {{roleName}}. All the associated roles of {{role}} will also be removed. @@ -2179,7 +2180,7 @@ includeInAccessToken.label=Add to access token samlKeysExportSuccess=Successfully exported keys usersInRole=Users in role policyProvider.group=Define conditions for your permissions where a set of one or more groups (and their hierarchies) is permitted to access an object. -updatedUserProfileError=User Profile configuration hasn't been saved +updatedUserProfileError=User Profile configuration hasn't been saved: {{error}} emptyPermissions=No permissions deletePermission=Permanently delete permission? selectUser=Select a user whose identity is going to be used to query permissions from the server. @@ -2708,7 +2709,7 @@ emptyAuthorizationInstructions=If you want to create authorization scopes, pleas subjectHelp=A regular expression for validating Subject DN in the Client Certificate. Use "(.*?)(?\:$)" to match all kind of expressions. updatePolicySuccess=Successfully updated the policy eventTypes.CUSTOM_REQUIRED_ACTION.name=Custom required action -updateExecutorError=Executor not updated +updateExecutorError=Executor not updated: {{error}} clientIdHelpHelp=Client ID of client to which LDAP role mappings will be mapped. Applicable only if 'Use Realm Roles Mapping' is false. createdAt=Created at moveGroupEmpty=No sub groups @@ -2877,7 +2878,7 @@ addProvider_other=Add {{provider}} providers resetAction=Reset action cibaExpiresIn=Expires In dynamicScopeFormatHelp=This is the regular expression that the system will use to extract the scope name and variable. -updateTranslationError=Error updating translation. +updateTranslationError=Error updating translation: {{error}} resetPasswordConfirmText=Are you sure you want to reset the password for the user {{username}}? create=Create noAvailableIdentityProviders=No available identity providers. @@ -3128,7 +3129,7 @@ addTranslationsDialogRowsTable=Add a translations dialog rows table addTranslationDialogHelperText=The translation based on the default language is required. noLanguagesSearchResultsInstructions=Click on the search bar above to search for languages addTranslationDialogOkBtn=Ok -translationError=Please add translations before saving +translationError=Please add translations before saving: {{error}} fetchRoles=Fetch Roles fetchRolesHelp=By default, only the roles available from the token sent with the authorization requests are used to check if the user is granted with a role. If this setting is enabled, the policy will ignore roles from the token and check any role associated with the user instead. emptyAdminEvents=No admin events diff --git a/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx b/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx index fb69de4a81..629743a33a 100644 --- a/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx +++ b/js/apps/admin-ui/src/client-scopes/ClientScopesSection.tsx @@ -180,7 +180,7 @@ export default function ClientScopesSection() { addError("deleteErrorClientScope", error); } } else { - addError(t("notAllowedToDeleteAllClientScopes"), "error"); + addAlert(t("notAllowedToDeleteAllClientScopes"), AlertVariant.danger); } }, }); diff --git a/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx b/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx index 61de0c30be..e6f359fd72 100644 --- a/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx +++ b/js/apps/admin-ui/src/clients/authorization/AuthorizationExport.tsx @@ -93,7 +93,7 @@ export const AuthorizationExport = () => { await navigator.clipboard.writeText(code!); addAlert(t("copied"), AlertVariant.success); } catch (error) { - addError(t("copyError"), error); + addError("copyError", error); } }} > diff --git a/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx b/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx index d37dcfd131..fcf8ed15b6 100644 --- a/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx +++ b/js/apps/admin-ui/src/identity-providers/add/AddMapper.tsx @@ -85,7 +85,7 @@ export default function AddMapper() { ); addAlert(t("mapperSaveSuccess"), AlertVariant.success); } catch (error) { - addError(t("mapperSaveError"), error); + addError("mapperSaveError", error); } } else { try { @@ -104,7 +104,7 @@ export default function AddMapper() { }), ); } catch (error) { - addError(t("mapperCreateError"), error); + addError("mapperCreateError", error); } } }; diff --git a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx index 6accd20289..d67160d06b 100644 --- a/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx +++ b/js/apps/admin-ui/src/realm-settings/ClientProfileForm.tsx @@ -172,7 +172,7 @@ export default function ClientProfileForm() { addAlert(t("deleteExecutorSuccess"), AlertVariant.success); navigate(toClientProfile({ realm, profileName })); } catch (error) { - addError(t("deleteExecutorError"), error); + addError("deleteExecutorError", error); } } else { try { @@ -180,7 +180,7 @@ export default function ClientProfileForm() { addAlert(t("deleteClientSuccess"), AlertVariant.success); navigate(toClientPolicies({ realm, tab: "profiles" })); } catch (error) { - addError(t("deleteClientError"), error); + addError("deleteClientError", error); } } }, diff --git a/js/apps/admin-ui/src/realm-settings/LoginTab.tsx b/js/apps/admin-ui/src/realm-settings/LoginTab.tsx index a3f0b1582d..72ab8e26ed 100644 --- a/js/apps/admin-ui/src/realm-settings/LoginTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/LoginTab.tsx @@ -42,7 +42,7 @@ export const RealmSettingsLoginTab = ({ addAlert(t("enableSwitchSuccess", { switch: t(name) })); refresh(); } catch (error) { - addError(t("enableSwitchError"), error); + addError("enableSwitchError", error); } }; diff --git a/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx b/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx index c0238febb8..0fa658e0ab 100644 --- a/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx +++ b/js/apps/admin-ui/src/realm-settings/NewClientPolicy.tsx @@ -237,7 +237,7 @@ export default function NewClientPolicy() { }), ); } catch (error) { - addError(t("deleteClientPolicyError"), error); + addError("deleteClientPolicyError", error); } }, }); @@ -262,7 +262,7 @@ export default function NewClientPolicy() { toEditClientPolicy({ realm, policyName: formValues.name! }), ); } catch (error) { - addError(t("deleteConditionError"), error); + addError("deleteConditionError", error); } } else { const updatedPolicies = policies?.filter( @@ -281,7 +281,7 @@ export default function NewClientPolicy() { }), ); } catch (error) { - addError(t("deleteClientError"), error); + addError("deleteClientError", error); } } }, @@ -306,7 +306,7 @@ export default function NewClientPolicy() { form.setValue("profiles", currentPolicy?.profiles || []); navigate(toEditClientPolicy({ realm, policyName: formValues.name! })); } catch (error) { - addError(t("deleteClientPolicyProfileError"), error); + addError("deleteClientPolicyProfileError", error); } } else { const updatedPolicies = policies?.filter( @@ -325,7 +325,7 @@ export default function NewClientPolicy() { }), ); } catch (error) { - addError(t("deleteClientError"), error); + addError("deleteClientError", error); } } }, diff --git a/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx b/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx index c15509ee3a..9ade05167e 100644 --- a/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/PoliciesTab.tsx @@ -175,7 +175,7 @@ export const PoliciesTab = () => { addAlert(t("deleteClientPolicySuccess"), AlertVariant.success); refresh(); } catch (error) { - addError(t("deleteClientPolicyError"), error); + addError("deleteClientPolicyError", error); } }, }); diff --git a/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx b/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx index d3edeb374f..3b3256a2e0 100644 --- a/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx +++ b/js/apps/admin-ui/src/realm-settings/ProfilesTab.tsx @@ -112,7 +112,7 @@ export default function ProfilesTab() { addAlert(t("deleteClientSuccess"), AlertVariant.success); setKey(key + 1); } catch (error) { - addError(t("deleteClientError"), error); + addError("deleteClientError", error); } }, }); diff --git a/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx b/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx index eb6827bd85..88f5a92f33 100644 --- a/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx +++ b/js/apps/admin-ui/src/realm-settings/localization/RealmOverrides.tsx @@ -225,7 +225,7 @@ export const RealmOverrides = ({ addAlert(t("addTranslationSuccess"), AlertVariant.success); } catch (error) { - addError(t("addTranslationError"), error); + addError("addTranslationError", error); } }; @@ -325,8 +325,8 @@ export const RealmOverrides = ({ addAlert(t("updateTranslationSuccess"), AlertVariant.success); setTableRows(newRows); - } catch { - addAlert(t("updateTranslationError"), AlertVariant.danger); + } catch (error) { + addError("updateTranslationError", error); } setEditStates((prevEditStates) => ({ diff --git a/js/apps/admin-ui/src/sessions/RevocationModal.tsx b/js/apps/admin-ui/src/sessions/RevocationModal.tsx index b527e3e384..f3e69e30db 100644 --- a/js/apps/admin-ui/src/sessions/RevocationModal.tsx +++ b/js/apps/admin-ui/src/sessions/RevocationModal.tsx @@ -28,7 +28,7 @@ export const RevocationModal = ({ const { adminClient } = useAdminClient(); const { t } = useTranslation(); - const { addAlert } = useAlerts(); + const { addAlert, addError } = useAlerts(); const { realm: realmName, realmRepresentation: realm, refresh } = useRealm(); const { register, handleSubmit } = useForm(); @@ -74,7 +74,7 @@ export const RevocationModal = ({ addAlert(t("notBeforeSuccess"), AlertVariant.success); } catch (error) { - addAlert(t("setToNowError", { error }), AlertVariant.danger); + addError("setToNowError", error); } }; @@ -90,7 +90,7 @@ export const RevocationModal = ({ addAlert(t("notBeforeClearedSuccess"), AlertVariant.success); refresh(); } catch (error) { - addAlert(t("notBeforeError", { error }), AlertVariant.danger); + addError("notBeforeError", error); } }; diff --git a/server-spi-private/src/main/java/org/keycloak/email/EmailException.java b/server-spi-private/src/main/java/org/keycloak/email/EmailException.java index f9e2d903d1..ee306b24bb 100644 --- a/server-spi-private/src/main/java/org/keycloak/email/EmailException.java +++ b/server-spi-private/src/main/java/org/keycloak/email/EmailException.java @@ -22,14 +22,21 @@ package org.keycloak.email; */ public class EmailException extends Exception { - public EmailException(Throwable cause) { - super(cause); - } - + /** + * Record an exception around email generation and sending + * + * @param message Shown to users in the admin console + */ public EmailException(String message) { super(message); } - + + /** + * Record an exception around email generation and sending + * + * @param message Shown to users in the admin console + * @param cause Additional information to be logged + */ public EmailException(String message, Throwable cause) { super(message, cause); } diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index 8e320087a4..ab5a93f282 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -84,6 +84,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.keycloak.utils.StreamsUtil.closing; + /** * Set of helper methods, which are useful in various model implementations. * @@ -922,7 +924,7 @@ public final class KeycloakModelUtils { Stream browserFlowOverridingClients = realm.searchClientByAuthenticationFlowBindingOverrides(Collections.singletonMap("browser", model.getId()), 0, 1); Stream directGrantFlowOverridingClients = realm.searchClientByAuthenticationFlowBindingOverrides(Collections.singletonMap("direct_grant", model.getId()), 0, 1); - boolean usedByClient = Stream.concat(browserFlowOverridingClients, directGrantFlowOverridingClients) + boolean usedByClient = closing(Stream.concat(browserFlowOverridingClients, directGrantFlowOverridingClients)) .limit(1) .findAny() .isPresent(); diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java index 9b6f20b68b..2e74a6b523 100644 --- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java +++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java @@ -63,7 +63,11 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { @Override public void send(Map config, UserModel user, String subject, String textBody, String htmlBody) throws EmailException { - send(config, retrieveEmailAddress(user), subject, textBody, htmlBody); + String address = retrieveEmailAddress(user); + if (address == null) { + throw new EmailException("No email address configured for the user"); + } + send(config, address, subject, textBody, htmlBody); } @Override @@ -107,6 +111,10 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { props.setProperty("mail.smtp.connectiontimeout", "10000"); String from = config.get("from"); + if (from == null) { + throw new EmailException("No sender address configured in the realm settings for emails"); + } + String fromDisplayName = config.get("fromDisplayName"); String replyTo = config.get("replyTo"); String replyToDisplayName = config.get("replyToDisplayName"); @@ -156,9 +164,11 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { transport.connect(); } transport.sendMessage(msg, new InternetAddress[]{new InternetAddress(address)}); + } catch (EmailException e) { + throw e; } catch (Exception e) { ServicesLogger.LOGGER.failedToSendEmail(e); - throw new EmailException(e); + throw new EmailException("Error when attempting to send the email to the server. More information is available in the server log.", e); } finally { if (transport != null) { try { diff --git a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java index 655d4ff0e0..a6629d27f9 100644 --- a/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java +++ b/services/src/main/java/org/keycloak/services/error/KeycloakErrorHandler.java @@ -75,12 +75,13 @@ public class KeycloakErrorHandler implements ExceptionMapper { KeycloakTransaction tx = session.getTransactionManager(); tx.setRollbackOnly(); - int statusCode = getStatusCode(throwable); + Response.Status responseStatus = getResponseStatus(throwable); + boolean isServerError = responseStatus.getFamily().equals(Response.Status.Family.SERVER_ERROR); - if (statusCode >= 500 && statusCode <= 599) { + if (isServerError) { logger.error(UNCAUGHT_SERVER_ERROR_TEXT, throwable); } else { - logger.debugv(throwable, ERROR_RESPONSE_TEXT, statusCode); + logger.debugv(throwable, ERROR_RESPONSE_TEXT, responseStatus); } HttpHeaders headers = session.getContext().getRequestHeaders(); @@ -89,9 +90,15 @@ public class KeycloakErrorHandler implements ExceptionMapper { OAuth2ErrorRepresentation error = new OAuth2ErrorRepresentation(); error.setError(getErrorCode(throwable)); - error.setErrorDescription("For more on this error consult the server log at the debug level."); + if (throwable instanceof ModelDuplicateException || throwable instanceof ModelValidationException) { + error.setErrorDescription(throwable.getMessage()); + } else if (throwable instanceof JsonProcessingException || throwable.getCause() instanceof JsonProcessingException) { + error.setErrorDescription("Cannot parse the JSON"); + } else if (isServerError) { + error.setErrorDescription("For more on this error consult the server log."); + } - return Response.status(statusCode) + return Response.status(responseStatus) .header(HttpHeaders.CONTENT_TYPE, jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE.toString()) .entity(error) .build(); @@ -105,36 +112,36 @@ public class KeycloakErrorHandler implements ExceptionMapper { Locale locale = session.getContext().resolveLocale(null); FreeMarkerProvider freeMarker = session.getProvider(FreeMarkerProvider.class); - Map attributes = initAttributes(session, realm, theme, locale, statusCode); + Map attributes = initAttributes(session, realm, theme, locale, responseStatus); String templateName = "error.ftl"; String content = freeMarker.processTemplate(attributes, templateName, theme); - return Response.status(statusCode).type(MediaType.TEXT_HTML_UTF_8_TYPE).entity(content).build(); + return Response.status(responseStatus).type(MediaType.TEXT_HTML_UTF_8_TYPE).entity(content).build(); } catch (Throwable t) { logger.error("Failed to create error page", t); return Response.serverError().build(); } } - private static int getStatusCode(Throwable throwable) { - int status = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); - if (throwable instanceof WebApplicationException) { - WebApplicationException ex = (WebApplicationException) throwable; - status = ex.getResponse().getStatus(); - } - if (throwable instanceof JsonProcessingException - || throwable instanceof ModelValidationException) { - status = Response.Status.BAD_REQUEST.getStatusCode(); - } - if (throwable instanceof ModelIllegalStateException) { - status = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(); - } - if (throwable instanceof ModelDuplicateException) { - status = Response.Status.CONFLICT.getStatusCode(); + private static Response.Status getResponseStatus(Throwable throwable) { + if (throwable instanceof WebApplicationException ex) { + return Response.Status.fromStatusCode(ex.getResponse().getStatus()); } - return status; + if (throwable instanceof JsonProcessingException || throwable instanceof ModelValidationException) { + return Response.Status.BAD_REQUEST; + } + + if (throwable instanceof ModelIllegalStateException) { + return Response.Status.INTERNAL_SERVER_ERROR; + } + + if (throwable instanceof ModelDuplicateException) { + return Response.Status.CONFLICT; + } + + return Response.Status.INTERNAL_SERVER_ERROR; } private static String getErrorCode(Throwable throwable) { @@ -172,18 +179,18 @@ public class KeycloakErrorHandler implements ExceptionMapper { return realm; } - private static Map initAttributes(KeycloakSession session, RealmModel realm, Theme theme, Locale locale, int statusCode) throws IOException { + private static Map initAttributes(KeycloakSession session, RealmModel realm, Theme theme, Locale locale, Response.Status responseStatus) throws IOException { Map attributes = new HashMap<>(); Properties messagesBundle = theme.getMessages(locale); - attributes.put("statusCode", statusCode); + attributes.put("statusCode", responseStatus.getStatusCode()); attributes.put("realm", realm); attributes.put("url", new UrlBean(realm, theme, session.getContext().getUri().getBaseUri(), null)); attributes.put("locale", new LocaleBean(realm, locale, session.getContext().getUri().getRequestUriBuilder(), messagesBundle)); - String errorKey = statusCode == 404 ? Messages.PAGE_NOT_FOUND : Messages.INTERNAL_SERVER_ERROR; + String errorKey = responseStatus == Response.Status.NOT_FOUND ? Messages.PAGE_NOT_FOUND : Messages.INTERNAL_SERVER_ERROR; String errorMessage = messagesBundle.getProperty(errorKey); attributes.put("message", new MessageBean(errorMessage, MessageType.ERROR)); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java index c28c181ecd..8cc6f0f0a2 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java @@ -44,12 +44,10 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.GroupModel; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelException; import org.keycloak.models.ModelIllegalStateException; -import org.keycloak.models.OrganizationModel; import org.keycloak.models.RealmModel; import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserCredentialModel; @@ -924,7 +922,7 @@ public class UserResource { return Response.noContent().build(); } catch (EmailException e) { ServicesLogger.LOGGER.failedToSendActionsEmail(e); - throw ErrorResponse.error("Failed to send execute actions email", Status.INTERNAL_SERVER_ERROR); + throw ErrorResponse.error("Failed to send execute actions email: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java index 913605af3b..07992bc684 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java @@ -278,10 +278,10 @@ public class FlowTest extends AbstractAuthenticationTest { Runnable assertRemoveFail = () -> { try { authMgmtResource.deleteFlow(flowId); - Assert.fail("Not expected to delete flow that in used."); + Assert.fail("Not expected to delete flow that is in use."); } catch (WebApplicationException e) { OAuth2ErrorRepresentation error = e.getResponse().readEntity(OAuth2ErrorRepresentation.class); - Assert.assertEquals("For more on this error consult the server log at the debug level.", error.getErrorDescription()); + Assert.assertEquals("For more on this error consult the server log.", error.getErrorDescription()); } }; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java index ae21990e1c..62ea5402a4 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/error/UncaughtErrorPageTest.java @@ -93,12 +93,13 @@ public class UncaughtErrorPageTest extends AbstractKeycloakTest { post.setEntity(new StringEntity("{ invalid : invalid }")); post.setHeader("Content-Type", "application/json"); - CloseableHttpResponse response = client.execute(post); - assertEquals(400, response.getStatusLine().getStatusCode()); + try (CloseableHttpResponse response = client.execute(post)) { + assertEquals(400, response.getStatusLine().getStatusCode()); - OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); - assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); - assertNotNull(error.getErrorDescription()); + OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); + assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); + assertNotNull("found error with " + error.getError() + "/" + error.getErrorDescription(), error.getErrorDescription()); + } } } @@ -113,12 +114,13 @@ public class UncaughtErrorPageTest extends AbstractKeycloakTest { post.setHeader("Authorization", "bearer " + accessToken); post.setHeader("Content-Type", "application/json"); - CloseableHttpResponse response = client.execute(post); - assertEquals(400, response.getStatusLine().getStatusCode()); + try (CloseableHttpResponse response = client.execute(post)) { + assertEquals(400, response.getStatusLine().getStatusCode()); - OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); - assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); - assertNotNull(error.getErrorDescription()); + OAuth2ErrorRepresentation error = JsonSerialization.readValue(response.getEntity().getContent(), OAuth2ErrorRepresentation.class); + assertEquals(OAuthErrorException.INVALID_REQUEST, error.getError()); + assertNotNull("found error with " + error.getError() + "/" + error.getErrorDescription(), error.getErrorDescription()); + } } }