2.7. 非推奨の機能と削除された機能
2.7.1. トラストストアの非推奨化
spi-truststore-file-*
オプションとトラストストア関連のオプション https-trust-store-*
が非推奨になりました。そのため、トラストストアマテリアルの新しいデフォルトの場所である conf/truststores
を使用するか、truststore-paths
オプションを使用して目的のパスを指定してください。詳細は、送信要求用の信頼済み証明書の設定 を参照してください。
spi-truststore-file-hostname-verification-policy
プロパティーの代わりに tls-hostname-verifier
プロパティーを使用する必要があります。
変更の副次的な影響として、トラストストアプロバイダーが常に何らかの証明書で設定されるようになりました (少なくとも、デフォルトの Java 信頼済み証明書が存在します)。この新しい動作は、Red Hat build of Keycloak の他の部分に影響を与える可能性があります。
たとえば、attestation conveyance が Direct 検証に設定されている場合は、webauthn 登録が失敗する可能性があります。以前は、トラストストアプロバイダーが設定されていないと、受信証明書が検証されませんでした。しかし、現在ではこの検証は常に実行されます。この登録は invalid cert path
エラーで失敗します。ドングルによって送信された証明書チェーンが Red Hat build of Keycloak によって信頼されていないためです。アテステーションを正しく実行するには、オーセンティケーターの認証局がトラストストアプロバイダーに存在している必要があります。
2.7.2. --proxy
オプションの非推奨化
--proxy
オプションは非推奨となり、今後のリリースで削除される予定です。次の表に、非推奨のオプションとサポートされているオプションの対応関係を示します。
非推奨の使用法 | 新しい使用法 |
---|---|
|
|
|
|
|
|
|
|
|
|
セキュリティーを強化するため、--proxy-headers
オプションでは、forwarded
値と xforwarded
値の両方を同時に選択することはできません (以前の --proxy edge
および --proxy reencrypt
の場合と同様)。
プロキシーヘッダーオプションを使用する場合は、リバースプロキシーによって Forwarded
ヘッダーまたは X-Forwarded-*
ヘッダーが適切に設定および上書きされることを確認してください。これらのヘッダーを設定するには、リバースプロキシーのドキュメントを参照してください。設定を誤ると、Red Hat build of Keycloak がセキュリティー上の脆弱性にさらされることになります。
Operator を使用するときにプロキシーヘッダーを設定することもできます。
apiVersion: k8s.keycloak.org/v2alpha1 kind: Keycloak metadata: name: example-kc spec: ... proxy: headers: forwarded|xforwarded
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: example-kc
spec:
...
proxy:
headers: forwarded|xforwarded
proxy.headers
フィールドが指定されていない場合、Operator はデフォルトで proxy=passthrough
を暗黙的に設定して、以前の動作にフォールバックします。これにより、サーバーログに非推奨の警告が記録されます。このフォールバックは今後のリリースで削除される予定です。
2.7.3. オフラインセッションのプリロードの非推奨化
Red Hat build of Keycloak のデフォルトの動作では、オンデマンドでオフラインセッションをロードします。起動時にオフラインセッションをプリロードするという従来の動作は、非推奨になりました。起動時にプリロードすると、セッション数の増加に応じて適切にスケールすることができず、Red Hat build of Keycloak のメモリー使用量が増加するためです。古い動作は今後のリリースで削除される予定です。
非推奨でまだ削除されていない古い動作を再度有効にするには、次に示すように機能フラグと SPI オプションを使用します。
bin/kc.[sh|bat] start --features-enabled offline-session-preloading --spi-user-sessions-infinispan-preload-offline-sessions-from-database=true
bin/kc.[sh|bat] start --features-enabled offline-session-preloading --spi-user-sessions-infinispan-preload-offline-sessions-from-database=true
UserSessionProvider
の API で、メソッド getOfflineUserSessionByBrokerSessionId (RealmModel レルム、String brokerSessionId)
が非推奨になりました。このメソッドの代わりに、getOfflineUserSessionByBrokerUserIdStream (RealmModel、String brokerUserId)
を使用して、ユーザーのセッションを取得してから、必要に応じてブローカーセッション ID でそれらをフィルタリングします。
2.7.4. データプロバイダーおよびモデルのメソッドの非推奨化
-
RealmModel#getTopLevelGroupsStream()
およびオーバーロードされたメソッドの非推奨化
2.7.5. Cookie の非推奨化と削除
Red Hat build of Keycloak での Cookie 処理のリファクタリングの一環として、Cookie の設定方法にいくつか変更が加えられています。
- 要求がセキュアなコンテキストを介して行われる場合は、すべての Cookie にセキュア属性が設定されるようになりました。
-
WELCOME_STATE_CHECKER
Cookie がSameSite=Strict
に設定されるようになりました。
カスタムエクステンションの場合は、いくつかの変更が必要になることがあります。
-
LocaleSelectorProvider.KEYCLOAK_LOCALE
は、Cookie が CookieProvider を通じて管理されるようになったため、非推奨になりました。 -
HttpResponse.setWriteCookiesOnTransactionComplete
が削除されました。 -
HttpCookie
が非推奨になりました。代わりにNewCookie.Builder
を使用してください。 -
ServerCookie
が非推奨になりました。代わりにNewCookie.Builder
を使用してください。
2.7.6. SAML 暗号化の非推奨モードの削除
バージョン 21 で導入された SAML 暗号化の互換モードが削除されました。システムプロパティー keycloak.saml.deprecated.encryption
が、サーバーによって管理されなくなりました。暗号化に古い署名鍵をまだ使用しているクライアントは、新しい IDP 設定メタデータから署名鍵を更新する必要があります。
2.7.7. モデルモジュールの名前変更
マップストアの削除後に、次のモジュールの名前が変更になりました。
-
org.keycloak:keycloak-model-legacy-private
をorg.keycloak:keycloak-model-storage-private
に変更 -
org.keycloak:keycloak-model-legacy-services
をorg.keycloak:keycloak-model-storage-services
に変更
org.keycloak:keycloak-model-legacy
モジュールが非推奨になりました。次のリリースで org.keycloak:keycloak-model-storage
モジュールに置き換えられて削除される予定です。
2.7.8. RegistrationProfile フォームアクションの削除
フォームアクション RegistrationProfile
(認証フローの UI では Profile Validation
として表示されます) がコードベースから削除され、すべての認証フローからも削除されました。これはデフォルトですべてのレルムの組み込み登録フローに含まれていました。ユーザー属性の検証と、そのユーザーのすべての属性を含むユーザーの作成は、RegistrationUserCreation
フォームアクションによって処理されるため、RegistrationProfile
は不要になります。独自のプロバイダーで RegistrationProfile
クラスを使用していない限り、通常、この変更に関するさらなる対処は不要です。
2.7.9. 管理ユーザー API を通じてユーザーを更新する際のユーザー属性の部分的な更新がサポート対象外に
管理ユーザー API を介してユーザー属性を更新する場合は、username
、email
、firstName
、lastName
などのルート属性を含むユーザー属性を更新するときに、部分的な更新ができません。
2.7.10. 非推奨の auto-build
CLI オプションの削除
auto-build
CLI オプションは、長い間、非推奨とマークされていました。このリリースで完全に削除され、サポートされなくなりました。
start
コマンドを実行すると、設定に基づいてサーバーが自動的に構築されます。この動作を防ぐには、--optimized
フラグを設定します。
2.7.11. イベントの詳細の長さを短縮するオプションの削除
このリリース以降、Keycloak は EventEntity
詳細列の長い値をサポートします。そのため、イベントの詳細の長さを短縮するためのオプション --spi-events-store-jpa-max-detail-length
および --spi-events-store-jpa-max-field-length
がサポートされなくなりました。
2.7.12. 翻訳からの名前空間の削除
すべての翻訳を admin-ui の 1 つのファイルに移動しました。独自の翻訳を作成したり、admin-ui を拡張したりした場合は、この新しい形式に移行する必要があります。また、データベースに "オーバーライド" がある場合は、キーから名前空間を削除する必要があります。一部のキーは名前空間を除いて同じです。これが最もわかりやすいのは help です。このような場合には、キーの末尾に Help
が付いています。
必要に応じて、次のノードスクリプトを使用して移行に役立てることができます。このスクリプトは、すべての単一ファイルを取得して新しいファイルに格納し、さらにマッピングの一部を処理します。
import { readFileSync, writeFileSync, appendFileSync } from "node:fs"; const ns = [ "common", "common-help", "dashboard", "clients", "clients-help", "client-scopes", "client-scopes-help", "groups", "realm", "roles", "users", "users-help", "sessions", "events", "realm-settings", "realm-settings-help", "authentication", "authentication-help", "user-federation", "user-federation-help", "identity-providers", "identity-providers-help", "dynamic", ]; const map = new Map(); const dup = []; ns.forEach((n) => { const rawData = readFileSync(n + ".json"); const translation = JSON.parse(rawData); Object.entries(translation).map((e) => { const name = e[0]; const value = e[1]; if (map.has(name) && map.get(name) !== value) { if (n.includes("help")) { map.set(name + "Help", value); } else { map.set(name, value); dup.push({ name: name, value: map.get(name), dup: { ns: n, value: value }, }); } } else { map.set(name, value); } }); }); writeFileSync( "translation.json", JSON.stringify(Object.fromEntries(map.entries()), undefined, 2), ); const mapping = [ ["common:clientScope", "clientScopeType"], ["identity-providers:createSuccess", "createIdentityProviderSuccess"], ["identity-providers:createError", "createIdentityProviderError"], ["clients:createError", "createClientError"], ["clients:createSuccess", "createClientSuccess"], ["user-federation:createSuccess", "createUserProviderSuccess"], ["user-federation:createError", "createUserProviderError"], ["authentication-help:name", "flowNameHelp"], ["authentication-help:description", "flowDescriptionHelp"], ["clientScopes:noRoles", "noRoles-clientScope"], ["clientScopes:noRolesInstructions", "noRolesInstructions-clientScope"], ["users:noRoles", "noRoles-user"], ["users:noRolesInstructions", "noRolesInstructions-user"], ["clients:noRoles", "noRoles-client"], ["clients:noRolesInstructions", "noRolesInstructions-client"], ["groups:noRoles", "noRoles-group"], ["groups:noRolesInstructions", "noRolesInstructions-group"], ["roles:noRoles", "noRoles-roles"], ["roles:noRolesInstructions", "noRolesInstructions-roles"], ["realm:realmName:", "realmNameField"], ["client-scopes:searchFor", "searchForClientScope"], ["roles:searchFor", "searchForRoles"], ["authentication:title", "titleAuthentication"], ["events:title", "titleEvents"], ["roles:title", "titleRoles"], ["users:title", "titleUsers"], ["sessions:title", "titleSessions"], ["client-scopes:deleteConfirm", "deleteConfirmClientScopes"], ["users:deleteConfirm", "deleteConfirmUsers"], ["groups:deleteConfirm_one", "deleteConfirmGroup_one"], ["groups:deleteConfirm_other", "deleteConfirmGroup_other"], ["identity-providers:deleteConfirm", "deleteConfirmIdentityProvider"], ["realm-settings:deleteConfirm", "deleteConfirmRealmSetting"], ["roles:whoWillAppearLinkText", "whoWillAppearLinkTextRoles"], ["users:whoWillAppearLinkText", "whoWillAppearLinkTextUsers"], ["roles:whoWillAppearPopoverText", "whoWillAppearPopoverTextRoles"], ["users:whoWillAppearPopoverText", "whoWillAppearPopoverTextUsers"], ["client-scopes:deletedSuccess", "deletedSuccessClientScope"], ["identity-providers:deletedSuccess", "deletedSuccessIdentityProvider"], ["realm-settings:deleteSuccess", "deletedSuccessRealmSetting"], ["client-scopes:deleteError", "deletedErrorClientScope"], ["identity-providers:deleteError", "deletedErrorIdentityProvider"], ["realm-settings:deleteError", "deletedErrorRealmSetting"], ["realm-settings:saveSuccess", "realmSaveSuccess"], ["user-federation:saveSuccess", "userProviderSaveSuccess"], ["realm-settings:saveError", "realmSaveError"], ["user-federation:saveError", "userProviderSaveError"], ["realm-settings:validateName", "validateAttributeName"], ["identity-providers:disableConfirm", "disableConfirmIdentityProvider"], ["realm-settings:disableConfirm", "disableConfirmRealm"], ["client-scopes:updateSuccess", "updateSuccessClientScope"], ["client-scopes:updateError", "updateErrorClientScope"], ["identity-providers:updateSuccess", "updateSuccessIdentityProvider"], ["identity-providers:updateError", "updateErrorIdentityProvider"], ["user-federation:orderChangeSuccess", "orderChangeSuccessUserFed"], ["user-federation:orderChangeError", "orderChangeErrorUserFed"], ["authentication-help:alias", "authenticationAliasHelp"], ["authentication-help:flowType", "authenticationFlowTypeHelp"], ["authentication:createFlow", "authenticationCreateFlowHelp"], ["client-scopes-help:rolesScope", "clientScopesRolesScope"], ["client-scopes-help:name", "scopeNameHelp"], ["client-scopes-help:description", "scopeDescriptionHelp"], ["client-scopes-help:type", "scopeTypeHelp"], ["clients-help:description", "clientDescriptionHelp"], ["clients-help:clientType", "clientsClientTypeHelp"], ["clients-help:scopes", "clientsClientScopesHelp"], ["common:clientScope", "clientScopeTypes"], ["dashboard:realmName", "realmNameTitle"], ["common:description", "description"], ]; mapping.forEach((m) => { const key = m[0].split(":"); try { const data = readFileSync(key[0] + ".json"); const translation = JSON.parse(data); const value = translation[key[1]]; if (value) { appendFileSync( "translation.json", '"' + m[1] + '": ' + JSON.stringify(value) + ',\n', ); } } catch (error) { console.error("skipping namespace key: " + key); } });
import { readFileSync, writeFileSync, appendFileSync } from "node:fs";
const ns = [
"common",
"common-help",
"dashboard",
"clients",
"clients-help",
"client-scopes",
"client-scopes-help",
"groups",
"realm",
"roles",
"users",
"users-help",
"sessions",
"events",
"realm-settings",
"realm-settings-help",
"authentication",
"authentication-help",
"user-federation",
"user-federation-help",
"identity-providers",
"identity-providers-help",
"dynamic",
];
const map = new Map();
const dup = [];
ns.forEach((n) => {
const rawData = readFileSync(n + ".json");
const translation = JSON.parse(rawData);
Object.entries(translation).map((e) => {
const name = e[0];
const value = e[1];
if (map.has(name) && map.get(name) !== value) {
if (n.includes("help")) {
map.set(name + "Help", value);
} else {
map.set(name, value);
dup.push({
name: name,
value: map.get(name),
dup: { ns: n, value: value },
});
}
} else {
map.set(name, value);
}
});
});
writeFileSync(
"translation.json",
JSON.stringify(Object.fromEntries(map.entries()), undefined, 2),
);
const mapping = [
["common:clientScope", "clientScopeType"],
["identity-providers:createSuccess", "createIdentityProviderSuccess"],
["identity-providers:createError", "createIdentityProviderError"],
["clients:createError", "createClientError"],
["clients:createSuccess", "createClientSuccess"],
["user-federation:createSuccess", "createUserProviderSuccess"],
["user-federation:createError", "createUserProviderError"],
["authentication-help:name", "flowNameHelp"],
["authentication-help:description", "flowDescriptionHelp"],
["clientScopes:noRoles", "noRoles-clientScope"],
["clientScopes:noRolesInstructions", "noRolesInstructions-clientScope"],
["users:noRoles", "noRoles-user"],
["users:noRolesInstructions", "noRolesInstructions-user"],
["clients:noRoles", "noRoles-client"],
["clients:noRolesInstructions", "noRolesInstructions-client"],
["groups:noRoles", "noRoles-group"],
["groups:noRolesInstructions", "noRolesInstructions-group"],
["roles:noRoles", "noRoles-roles"],
["roles:noRolesInstructions", "noRolesInstructions-roles"],
["realm:realmName:", "realmNameField"],
["client-scopes:searchFor", "searchForClientScope"],
["roles:searchFor", "searchForRoles"],
["authentication:title", "titleAuthentication"],
["events:title", "titleEvents"],
["roles:title", "titleRoles"],
["users:title", "titleUsers"],
["sessions:title", "titleSessions"],
["client-scopes:deleteConfirm", "deleteConfirmClientScopes"],
["users:deleteConfirm", "deleteConfirmUsers"],
["groups:deleteConfirm_one", "deleteConfirmGroup_one"],
["groups:deleteConfirm_other", "deleteConfirmGroup_other"],
["identity-providers:deleteConfirm", "deleteConfirmIdentityProvider"],
["realm-settings:deleteConfirm", "deleteConfirmRealmSetting"],
["roles:whoWillAppearLinkText", "whoWillAppearLinkTextRoles"],
["users:whoWillAppearLinkText", "whoWillAppearLinkTextUsers"],
["roles:whoWillAppearPopoverText", "whoWillAppearPopoverTextRoles"],
["users:whoWillAppearPopoverText", "whoWillAppearPopoverTextUsers"],
["client-scopes:deletedSuccess", "deletedSuccessClientScope"],
["identity-providers:deletedSuccess", "deletedSuccessIdentityProvider"],
["realm-settings:deleteSuccess", "deletedSuccessRealmSetting"],
["client-scopes:deleteError", "deletedErrorClientScope"],
["identity-providers:deleteError", "deletedErrorIdentityProvider"],
["realm-settings:deleteError", "deletedErrorRealmSetting"],
["realm-settings:saveSuccess", "realmSaveSuccess"],
["user-federation:saveSuccess", "userProviderSaveSuccess"],
["realm-settings:saveError", "realmSaveError"],
["user-federation:saveError", "userProviderSaveError"],
["realm-settings:validateName", "validateAttributeName"],
["identity-providers:disableConfirm", "disableConfirmIdentityProvider"],
["realm-settings:disableConfirm", "disableConfirmRealm"],
["client-scopes:updateSuccess", "updateSuccessClientScope"],
["client-scopes:updateError", "updateErrorClientScope"],
["identity-providers:updateSuccess", "updateSuccessIdentityProvider"],
["identity-providers:updateError", "updateErrorIdentityProvider"],
["user-federation:orderChangeSuccess", "orderChangeSuccessUserFed"],
["user-federation:orderChangeError", "orderChangeErrorUserFed"],
["authentication-help:alias", "authenticationAliasHelp"],
["authentication-help:flowType", "authenticationFlowTypeHelp"],
["authentication:createFlow", "authenticationCreateFlowHelp"],
["client-scopes-help:rolesScope", "clientScopesRolesScope"],
["client-scopes-help:name", "scopeNameHelp"],
["client-scopes-help:description", "scopeDescriptionHelp"],
["client-scopes-help:type", "scopeTypeHelp"],
["clients-help:description", "clientDescriptionHelp"],
["clients-help:clientType", "clientsClientTypeHelp"],
["clients-help:scopes", "clientsClientScopesHelp"],
["common:clientScope", "clientScopeTypes"],
["dashboard:realmName", "realmNameTitle"],
["common:description", "description"],
];
mapping.forEach((m) => {
const key = m[0].split(":");
try {
const data = readFileSync(key[0] + ".json");
const translation = JSON.parse(data);
const value = translation[key[1]];
if (value) {
appendFileSync(
"translation.json",
'"' + m[1] + '": ' + JSON.stringify(value) + ',\n',
);
}
} catch (error) {
console.error("skipping namespace key: " + key);
}
});
これを public/locale/<language>
フォルダー内の transform.mjs
というファイルに保存し、次のコマンドで実行します。
node ./transform.mjs
node ./transform.mjs
これでは完全な変換を実行できない可能性がありますが、それに非常に近いものになります。