61
Red Hat Single Sign-On 7.4 サーバー開発者ガイド Red Hat Single Sign-On 7.4 向け Last Updated: 2021-04-01

Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Red Hat Single Sign-On 7.4

サーバー開発者ガイド

Red Hat Single Sign-On 7.4 向け

Last Updated: 2021-04-01

Page 2: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト
Page 3: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Red Hat Single Sign-On 7.4 サーバー開発者ガイド

Red Hat Single Sign-On 7.4 向け

Enter your first name here. Enter your surname here.Enter your organisation's name here. Enter your organisational division here.Enter your email address here.

Page 4: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

法律上の通知法律上の通知

Copyright © 2021 | You need to change the HOLDER entity in the en-US/Server_Developer_Guide.ent file |.

The text of and illustrations in this document are licensed by Red Hat under a Creative CommonsAttribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA isavailable athttp://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you mustprovide the URL for the original version.

Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert,Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.

Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift,Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United Statesand other countries.

Linux ® is the registered trademark of Linus Torvalds in the United States and other countries.

Java ® is a registered trademark of Oracle and/or its affiliates.

XFS ® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United Statesand/or other countries.

MySQL ® is a registered trademark of MySQL AB in the United States, the European Union andother countries.

Node.js ® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by theofficial Joyent Node.js open source or commercial project.

The OpenStack ® Word Mark and OpenStack logo are either registered trademarks/service marksor trademarks/service marks of the OpenStack Foundation, in the United States and othercountries and are used with the OpenStack Foundation's permission. We are not affiliated with,endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.

All other trademarks are the property of their respective owners.

概要概要

本ガイドは、開発者が Red Hat Single Sign-On 7.4 をカスタマイズするための情報で構成されています。

Page 5: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

目次目次

第第1章章 はじめにはじめに

第第2章章 MANAGEMENT REST API2.1. CURL の使用例

第第3章章 テーマテーマ3.1. テーマのタイプ3.2. 設定画面3.3. デフォルトテーマ3.4. 写真の作成

3.4.1. テーマのプロパティー3.4.2. スタイルシート3.4.3. スクリプト3.4.4. イメージ3.4.5. メッセージ3.4.6. 国際化3.4.7. HTML テンプレート3.4.8. 電子メール

3.5. テーマのデプロイ3.6. テーマセレクター3.7. テーマリソース3.8. ロケールセレクター

第第4章章 カスタムユーザー属性カスタムユーザー属性4.1. 登録ページ4.2. アカウント管理コンソール

第第5章章 ID ブローカーブローカー API5.1. 外部 IDP トークンの取得5.2. クライアント開始アカウントリンク

5.2.1. 外部トークンの更新

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)6.1. SPI の実装

6.1.1. 管理コンソールで SPI 実装から情報を表示6.2. プロバイダー実装の登録

6.2.1. Red Hat Single Sign-On Deployer の使用6.2.2. モジュールを使用したプロバイダーの登録6.2.3. プロバイダーの無効化

6.3. JAVA EE の活用6.4. JAVASCRIPT プロバイダー

6.4.1. オーセンティケーター6.4.2. デプロイするスクリプトで JAR を作成します。6.4.3. スクリプト JAR のデプロイ6.4.4. Red Hat Single Sign-On 管理コンソールを使用したスクリプトのアップロード

6.5. 利用可能な SPI

第第7章章 ユーザーストレージユーザーストレージ SPI7.1. プロバイダーインターフェース7.2. プロバイダー機能インターフェース7.3. モデルインターフェース

7.3.1. ストレージ ID7.4. パッケージおよびデプロイメント

4

55

66677899

10101011

1212131313

151515

16161618

1919212121222223242425262627

282830313132

目次目次

1

Page 6: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7.5. シンプルな読み取り専用、ルックアップの例7.5.1. プロバイダークラス

7.5.1.1. UserLookupProvider 実装7.5.1.2. CredentialInputValidator 実装7.5.1.3. CredentialInputUpdater 実装

7.5.2. プロバイダーファクトリーの実装7.5.2.1. 初期化7.5.2.2. Create メソッド

7.5.3. パッケージおよびデプロイメント7.5.4. 管理コンソールでのプロバイダーの有効化

7.6. 設定技術7.6.1. 設定例7.6.2. 管理コンソールでのプロバイダーの設定

7.7. ユーザーの追加/削除およびクエリー機能インターフェースの追加/削除7.7.1. UserRegistrationProvider の実装7.7.2. UserQueryProvider の実装

7.8. 外部ストレージの拡張7.8.1. 拡張された例

7.9. 実装ストラテジーのインポート7.9.1. ImportedUserValidation インターフェース7.9.2. ImportSynchronization インターフェース

7.10. ユーザーキャッシュ7.10.1. ユーザーキャッシュの管理7.10.2. OnUserCache Callback インターフェース7.10.3. キャッシュポリシー

7.11. JAVA EE の活用7.12. REST 管理 API7.13. 以前のユーザーフェデレーション SPI からの移行

7.13.1. インポートとインポートなし7.13.2. UserFederationProvider と UserStorageProvider7.13.3. UserFederationProviderFactory と UserStorageProviderFactory7.13.4. 新規モデルへのアップグレード

第第8章章 VAULT SPI8.1. VAULT プロバイダー8.2. VAULT からの値の使用

323233343535363737373838404040424445454747484849505051

5353545555

565656

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

2

Page 7: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

目次目次

3

Page 8: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第1章 はじめに例の一部のリストでは、1 行に表示されるものは、利用可能なページ幅内に収まらない場合があります。これらの行は破損しています。行の最後に「\」と「\」は、ページに適合するために分割され、次の行がインデントされたことを意味します。したがって、以下のようになります。

Let's pretend to have an extremely \long line that \does not fitThis one is short

これは、以下の通りです。

Let's pretend to have an extremely long line that does not fitThis one is short

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

4

Page 9: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第2章 MANAGEMENT REST APIRed Hat Single Sign-On には、管理コンソールが提供するすべての機能を備えた完全な管理 REST APIが同梱されています。

API を呼び出すには、適切なパーミッションでアクセストークンを取得する必要があります。必要なパーミッションは、『 サーバー管理ガイド』を参照してください。

Red Hat Single Sign-On でアプリケーションへの認証を有効にすることにより、トークンを取得することができます。『 Securing Applications and Services Guide』を参照してください。直接アクセス許可を使用して、アクセストークンを取得することもできます。

詳しい情報は API ドキュメント を参照してください。

2.1. CURL の使用例

ユーザー名 admin およびパスワード password を使用して、master レルムのユーザーのアクセストークンを取得します。

注記注記

デフォルトでは、このトークンの有効期限は 1 分単位です。

結果は JSON ドキュメントになります。API を呼び出すには、access_token プロパティーの値を抽出する必要があります。API へのリクエストの Authorization ヘッダーに値を追加して、API を呼び出すことができます。

以下の例は、マスターレルムの詳細を取得する方法を示しています。

curl \ -d "client_id=admin-cli" \ -d "username=admin" \ -d "password=password" \ -d "grant_type=password" \ "http://localhost:8080/auth/realms/master/protocol/openid-connect/token"

curl \ -H "Authorization: bearer eyJhbGciOiJSUz..." \ "http://localhost:8080/auth/admin/realms/master"

第第2章章 MANAGEMENT REST API

5

Page 10: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第3章 テーマRed Hat Single Sign-On は、Web ページと電子メールに対してテーマサポートを提供します。これにより、エンドユーザー向けページの外観や操作感をカスタマイズすることができ、アプリケーションと統合できます。

図図3.1 sunrise のサンプルテーマを含むログインページのサンプルテーマを含むログインページ

3.1. テーマのタイプ

テーマは、1 つ以上のタイプを提供して、Red Hat Single Sign-On のさまざまな側面をカスタマイズすることができます。利用可能なタイプは以下のとおりです。

Account - アカウント管理

Admin - 管理コンソール

Email - メール

Login - ログインフォーム

Welcome - Welcome ページ

3.2. 設定画面

Welcome を除くすべてのテーマタイプは、管理コンソール管理コンソール を介して設定されます。レルムに使用されるテーマを変更するには、管理コンソール管理コンソール を開き、左上隅のドロップダウンボックスからレルムを選択します。Realm Settings で、Themes をクリックします。

注記注記

master 管理コンソールのテーマを設定するには、master レルムの管理コンソールテーマを設定する必要があります。管理コンソールへの変更を表示するには、ページを更新します。

welcome テーマを変更するには、standalone.xml、standalone-ha.xml、または domain.xml を編集する必要があります。

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

6

Page 11: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

welcomeTheme を theme 要素に追加します。以下に例を示します。

サーバーが実行している場合は、ウェルカムテーマへの変更を有効にするには、サーバーを再起動する必要があります。

3.3. デフォルトテーマ

Red Hat Single Sign-On は、サーバーのルート themes ディレクトリーにデフォルトのテーマがバンドルされています。アップグレードを簡素化するには、バンドルされたテーマを直接編集しないでください。代わりに、バンドルされたテーマのいずれかを拡張する専用のテーマを作成します。

3.4. 写真の作成

テーマは以下で構成されます。

HTML テンプレート(Freemarker テンプレート)

イメージ

メッセージバンドル

スタイルシート

スクリプト

テーマのプロパティー

1 つのページをすべて置き換える予定がなければ、別のテーマを拡張する必要があります。Red HatSingle Sign-On テーマを拡張する可能性が高くなりますが、ページの外観や操作感を大幅に変更する場合は、ベースのテーマを拡張することを検討することもできます。ベーステーマは主に HTML テンプレートとメッセージバンドルで構成されますが、Red Hat Single Sign-On テーマには主にイメージとスタイルシートが含まれます。

テーマを拡張する際には、個別のリソース(テンプレート、スタイルシートなど)を上書きできます。新規リリースへのアップグレード時にカスタムテンプレートを更新する必要がある可能性があることに注意してください。

テーマを作成する際に、Red Hat Single Sign-On を再起動せずに themes ディレクトリーから直接テーマリソースを編集できるため、キャッシングを無効にするとよいでしょう。これを行うには、standalone.xml を編集します。theme の場合は、staticMaxAge を -1 に設定し、cacheTemplates および cacheThemes を false に設定します。

<theme> ... <welcomeTheme>custom-theme</welcomeTheme> ...</theme>

<theme> <staticMaxAge>-1</staticMaxAge> <cacheThemes>false</cacheThemes> <cacheTemplates>false</cacheTemplates> ...</theme>

第第3章章 テーマテーマ

7

Page 12: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

パフォーマンスに大きく影響するため、本番環境でキャッシュを再度有効にするのを忘れないようにしてください。

themes ディレクトリーに新しいディレクトリーを作成して新たなテーマを作成するには、以下のようにします。ディレクトリーの名前は、テーマの名前になります。たとえば、mytheme というテーマを作成するには、themes/mytheme ディレクトリーを作成します。

theme ディレクトリーには、テーマが提供するタイプごとにディレクトリーを作成します。たとえば、ログインタイプを mytheme テーマに追加するには、themes/mytheme/login ディレクトリーを作成します。

それぞれのタイプに対して、テーマの設定を可能にする theme.properties ファイルを作成します。たとえば、先ほど作成したテーマ themes/mytheme/login を構成して基本テーマを拡張し、いくつかの共通リソースをインポートするには、次の内容でファイル themes/mytheme/login/theme.propertiesを作成します。

parent=baseimport=common/keycloak

これで、ログインタイプのサポートのあるテーマが作成されました。管理コンソールが開いていることを確認するには、管理コンソールを開きます。レルムを選択し、Themes をクリックします。Login Theme については mytheme を選択し、Save をを クリックします。次に、レルムのログインページを開きます。

これは、アプリケーションからログインするか、Account Management コンソール (/realms/{realm name}/account) を開いて実行できます。

親テーマの変更の効果を確認するには、parent=keycloak in theme.properties に設定し、ログインページを更新します。

3.4.1. テーマのプロパティー

テーマプロパティーは、テーマディレクトリーの <THEME TYPE>/theme.properties ファイルで設定されます。

parent - 拡張する親テーマ

import - 別のテーマからのリソースのインポート

styles - 追加するスタイルのスペース区切りリスト

locales - サポートされるロケールのコンマ区切りリスト

特定の要素タイプに使用する css クラスを変更するために使用できるプロパティーのリストがあります。これらのプロパティーの一覧は、keycloak テーマ (themes/keycloak/<THEME TYPE>/theme.properties) の対応するタイプの theme.properties ファイルを参照してください。

また、独自のカスタムプロパティーを追加して、カスタムテンプレートから使用することができます。

これを行う場合は、以下の形式を使用してシステムプロパティーまたは環境変数を置き換えることができます。

${some.system.property} - システムプロパティーの場合。

${env.ENV_VAR} - 環境変数の場合。

システムプロパティーまたは環境変数が ${foo:defaultValue} で見つからない場合は、デフォルト値を

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

8

Page 13: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

システムプロパティーまたは環境変数が ${foo:defaultValue} で見つからない場合は、デフォルト値を指定することもできます。

注記注記

デフォルト値が指定されておらず、対応するシステムプロパティーまたは環境変数がない場合は、何も置き換えられ、テンプレートの形式で終了します。

以下は可能なものの例です。

3.4.2. スタイルシート

テーマには、1 つ以上のスタイルシートを含めることができます。スタイルシートを追加するには、テーマの <THEME TYPE>/resources/css ディレクトリーにファイルを作成します。次に、それを theme.properties の styles プロパティーに追加します。

たとえば、styles.css を mytheme に追加するには、以下の内容を含む themes/mytheme/login/resources/css/styles.css を作成します。

次に、themes/mytheme/login/theme.properties を編集し、以下を追加します。

styles=css/styles.css

変更を確認するには、レルムのログインページを開きます。適用されたスタイルがカスタムスタイルシートの唯一のスタイルであることが分かります。親テーマのスタイルを含めるには、そのテーマからスタイルを読み込む必要もあります。これは、themes/mytheme/login/theme.properties を編集し、styles を以下のように変更します。

styles=node_modules/patternfly/dist/css/patternfly.css node_modules/patternfly/dist/css/patternfly-additions.css lib/zocial/zocial.css css/login.css css/styles.css

注記注記

親スタイルシートからスタイルを上書きするには、スタイルシートを最後にリストする必要があります。

3.4.3. スクリプト

テーマには 1 つ以上のスクリプトを持たせることができ、スクリプトの <THEME TYPE>/resources/jsディレクトリーにファイルを作成します。次に、theme.properties の scripts プロパティーに追加します。

たとえば、script.js を mytheme に追加するには、以下の内容で

javaVersion=${java.version}

unixHome=${env.HOME:Unix home not found}windowsHome=${env.HOMEPATH:Windows home not found}

.login-pf body { background: DimGrey none;}

第第3章章 テーマテーマ

9

Page 14: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

たとえば、script.js を mytheme に追加するには、以下の内容で themes/mytheme/login/resources/js/script.js を作成します。

次に、themes/mytheme/login/theme.properties を編集し、以下を追加します。

scripts=js/script.js

3.4.4. イメージ

テーマがイメージを使用できるようにするには、テーマの <THEME TYPE>/resources/img ディレクトリーにイメージを追加します。スタイルシート内から、または HTML テンプレートで直接使用することができます。

たとえば、イメージを mytheme に追加するには、イメージを themes/mytheme/login/resources/img/image.jpg にイメージをコピーします。

以下を使用して、カスタムスタイルシート内からこのイメージを使用できます。

または、HTML テンプレートで直接使用するには、以下をカスタム HTML テンプレートに追加します。

3.4.5. メッセージ

テンプレートのテキストは、メッセージバンドルから読み込まれます。別のテーマを拡張するテーマは、親のメッセージバンドルからすべてのメッセージを継承し、<THEME TYPE>/messages/messages_en.properties をテーマに追加して個別のメッセージを上書きできます。

たとえば、ログインフォーム上の Username を、mytheme の Your Username に置き換えるには、以下の内容で themes/mytheme/login/messages/messages_en.properties ファイルを作成します。

usernameOrEmail=Your Username

メッセージの使用時に {0} や {1} などのメッセージ値内では引数に置き換えられます。たとえば、Log in to {0} の {0} はレルム名に置き換えられます。

3.4.6. 国際化

Red Hat Single Sign-On は国際化をサポートします。レルムの国際化を有効にするには『 サーバー管理ガイド』を参照してください。本セクションでは、独自の言語を追加する方法を説明します。

新しい言語を追加するには、テーマのディレクトリーに <THEME TYPE>/messages/messages_<LOCALE>.properties ファイルを作成します。次に、これを <THEME TYPE>/theme.properties の locales プロパティーに追加します。言語をユーザーが利用できるように

alert('Hello');

body { background-image: url('../img/image.jpg'); background-size: cover;}

<img src="${url.resourcesPath}/img/image.jpg">

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

10

Page 15: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

するには、レルムの login、account、および email テーマがその言語をサポートしていなければならないため、これらのテーマタイプに言語を追加する必要があります。

たとえば、mytheme テーマにノルウェー語の翻訳を追加するには、以下の内容で themes/mytheme/login/messages/messages_no.properties ファイルを作成します。

usernameOrEmail=Brukernavnpassword=Passord

翻訳を提供しないメッセージはすべてデフォルトの英語翻訳を使用します。

次に、themes/mytheme/login/theme.properties を編集し、以下を追加します。

locales=en,no

テーマタイプ account および email にも同様の作業を行う必要があります。そのためには、themes/mytheme/account/messages/messages_no.properties および themes/mytheme/email/messages/messages_no.properties を作成します。これらのファイルを空のままにすると、英語のメッセージが使用されます。次に、themes/mytheme/login/theme.propertiesを themes/mytheme/account/theme.properties およに themes/mytheme/email/theme.properties にコピーします。

最後に、言語セレクターの翻訳を追加する必要があります。これは、メッセージを英語の翻訳に追加することで行います。これには、以下を themes/mytheme/account/messages/messages_en.properties および themes/mytheme/login/messages/messages_en.properties に追加します。

locale_no=Norsk

デフォルトでは、メッセージプロパティーファイルは ISO-8859-1 を使用してエンコードする必要があります。特別なヘッダーを使用してエンコーディングを指定することもできます。たとえば、UTF-8 エンコーディングを使用するには、次のコマンドを実行します。

# encoding: UTF-8usernameOrEmail=....

現在のロケールの選択方法は、Locale Selector を参照してください。

3.4.7. HTML テンプレート

Red Hat Single Sign-On は、HTML を生成するために Freemarker テンプレート を使用します。<THEME TYPE>/<TEMPLATE>.ftl を作成して、独自のテーマで個別のテンプレートを上書きできます。使用されるテンプレートの一覧は、themes/base/<THEME TYPE> を参照してください。

カスタムテンプレートを作成する場合には、ベーステーマから自分のテーマにテンプレートをコピーし、必要な変更を適用することが推奨されます。Red Hat Single Sign-On の新規バージョンにアップグレードする際には、該当する場合に変更を元のテンプレートに適用するようにカスタムテンプレートを更新する必要がある場合があります。

たとえば、mytheme テーマのカスタムログインフォームを作成するには、themes/base/login/login.ftlを themes/mytheme/login にコピーし、エディターで開きます。最初の行の後 (<#import …>) に、以下のように <h1>HELLO WORLD!</h1> を追加します。

第第3章章 テーマテーマ

11

Page 16: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

テンプレートの編集方法の詳細は、FreeMarker Manual を参照してください。

3.4.8. 電子メール

パスワードリカバリーメールなどのメールの件名および内容を編集するには、テーマの email タイプにメッセージバンドルをを追加します。メールごとに 3 つのメッセージがあります。このサブジェクトの1 つ。1 つはプレーンテキストの本文用に、もう 1 つは html 本文用です。

利用可能なすべてのメールを確認するには、themes/base/email/messages/messages_en.propertiesを確認します。

たとえば、mytheme テーマのパスワードリカバリーメールを変更するには、以下の内容で themes/mytheme/email/messages/messages_en.properties を作成します。

passwordResetSubject=My password recoverypasswordResetBody=Reset password link: {0}passwordResetBodyHtml=<a href="{0}">Reset password</a>

3.5. テーマのデプロイ

テーマは、テーマディレクトリーを テーマテーマ にコピーして Red Hat Single Sign-On にデプロイするか、アーカイブとしてデプロイできます。開発中にテーマを themes のディレクトリーにコピーできますが、実稼働環境では アーカイブアーカイブ の使用を検討してください。アーカイブアーカイブ により、特にクラスタリングなどの Red Hat Single Sign-On のインスタンスが複数ある場合に、テーマのバージョン化されたコピーが簡単になります。

テーマをアーカイブとしてデプロイするには、自分専用のリソースが含まれる JAR アーカイブを作成する必要があります。また、META-INF/keycloak-themes.json ファイルをアーカイブで利用可能なテーマを一覧表示するアーカイブに追加して、各テーマが提供するタイプを追加する必要もあります。

たとえば、mytheme テーマでは、以下の内容を含む mytheme.jar を作成します。

META-INF/keycloak-themes.json

theme/mytheme/login/theme.properties

theme/mytheme/login/login.ftl

theme/mytheme/login/resources/css/styles.css

theme/mytheme/login/resources/img/image.png

theme/mytheme/login/messages/messages_en.properties

theme/mytheme/email/messages/messages_en.properties

この場合、META-INF/keycloak-themes.json の内容は以下のようになります。

<#import "template.ftl" as layout><h1>HELLO WORLD!</h1>...

{ "themes": [{ "name" : "mytheme",

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

12

Page 17: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

1 つのアーカイブには複数のテーマを含めることができ、各テーマは 1 つ以上のタイプをサポートします。

アーカイブを Red Hat Single Sign-On にデプロイするには、単に Red Hat Single Sign-On の standalone/deployments/ ディレクトリーにドロップし、自動的に読み込まれます。

3.6. テーマセレクター

デフォルトでは、レルムに設定されたテーマが使用されます。また、クライアントを除き、ログインテーマを上書きできます。この動作は、Dash Selector SPI を使用して変更できます。

これは、ユーザーエージェントヘッダーなどで、デスクトップやモバイルデバイスの異なるテーマを選択するために使用できます。

カスタムテーマセレクターを作成するには、ThemeSelectorProviderFactory および ThemeSelectorProvider を実装する必要があります。

カスタムプロバイダーの作成およびデプロイ方法 についての詳細は、サービスプロバイダーインターフェース の手順に従います。

3.7. テーマリソース

Red Hat Single Sign-On でカスタムプロバイダーを実装する場合、テンプレート、リソース、およびメッセージバンドルを追加する必要があることがあります。

追加のテーマリソースを読み込む最も簡単な方法は、theme-resources/resources の theme-resources/templates リソースに JAR を作成し、 theme-resources/messages のメッセージバンドルで Red Hat Single Sign-On の standalone/deployments/ ディレクトリーに置くことです。

実行可能なテンプレートとリソースをより柔軟に読み込むには、ResourceSPI で実行可能なテンプレートおよびリソースをロードする必要があります。ThemeResourceProviderFactory および ThemeResourceProvider を実装することで、テンプレートおよびリソースを読み込む方法を正確に決定できます。

カスタムプロバイダーの作成およびデプロイ方法 についての詳細は、サービスプロバイダーインターフェース の手順に従います。

3.8. ロケールセレクター

デフォルトでは、LocaleSelectorProvider インターフェースを実装する DefaultLocaleSelectorProvider を使用してロケールが選択されます。国際化が無効になっている場合は、英語がデフォルトの言語です。国際化を有効にすると、ロケールは以下の優先度で解決されます。

1. kc_locale クエリーパラメーター

2. KEYCLOAK_LOCALEcookie 値

3. ユーザーインスタンスが利用できる場合は、ユーザーの推奨ロケール

4. ui_locales クエリーパラメーター

"types": [ "login", "email" ] }]}

第第3章章 テーマテーマ

13

Page 18: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

5. Accept-Language 要求ヘッダー

6. レルムのデフォルト言語

この動作は、LocaleSelectorProvider および LocaleSelectorProviderFactory を実装して、LocaleSelectorSPIを介して変更できます。

LocaleSelectorProvider インターフェースには単一のメソッド resolveLocale があり、RealmModelおよび Null が可能な UserModel を指定したロケールを返す必要があります。実際のリクエストは KeycloakSession#getContext メソッドから利用できます。

カスタム実装は、デフォルトの動作の一部を再利用できるように DefaultLocaleSelectorProvider を拡張できます。たとえば、Accept-Language リクエストヘッダーを無視する場合、カスタム実装はデフォルトのプロバイダーを拡張し、その getAcceptLanguageHeaderLocale をオーバーライドしてNull 値を返すことができます。その結果、ロケールの選択がレルムのデフォルト言語にフォールバックされます。

カスタムプロバイダーの作成およびデプロイ方法 についての詳細は、サービスプロバイダーインターフェース の手順に従います。

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

14

Page 19: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第4章 カスタムユーザー属性カスタムテーマを使用して、登録ページおよびアカウント管理コンソールにカスタムユーザー属性を追加できます。本章では、カスタムテーマに属性を追加する方法について説明しますが、カスタムのテーマの作成方法についての テーマ に関する章を参照する必要があります。

4.1. 登録ページ

登録ページでカスタム属性を入力できるようにするには、テンプレートの themes/base/login/register.ftl をカスタムテーマのログインタイプにコピーします。エディターでコピーを開きます。

登録ページに行番号を追加する例は、以下のスニペットをフォームに追加します。

入力 html 要素の名前が user.attributes. で開始することを確認します。上記の例では、属性は、名前 mobile で Keycloak によって保存されます。

変更を確認するには、レルムがログインテーマにカスタムテーマを使用していることを確認し、登録ページを開きます。

4.2. アカウント管理コンソール

アカウント管理コンソールでユーザープロファイルページでカスタム属性を管理できるようにするには、テンプレートの themes/base/account/account.ftl をカスタムテーマのアカウントタイプにコピーします。エディターでコピーを開きます。

アカウントページにメールアドレスを追加する例は、以下のスニペットをフォームに追加します。

入力 html 要素の名前が user.attributes. で開始することを確認します。

変更を確認するには、レルムがアカウントテーマのカスタムテーマを使用していることを確認し、アカウント管理コンソールでユーザープロファイルページを開きます。

<div class="form-group"> <div class="${properties.kcLabelWrapperClass!}"> <label for="user.attributes.mobile" class="${properties.kcLabelClass!}">Mobile number</label> </div>

<div class="${properties.kcInputWrapperClass!}"> <input type="text" class="${properties.kcInputClass!}" id="user.attributes.mobile" name="user.attributes.mobile" value="${(register.formData['user.attributes.mobile']!'')}"/> </div></div>

<div class="form-group"> <div class="col-sm-2 col-md-2"> <label for="user.attributes.mobile" class="control-label">Mobile number</label> </div>

<div class="col-sm-10 col-md-10"> <input type="text" class="form-control" id="user.attributes.mobile" name="user.attributes.mobile" value="${(account.attributes.mobile!'')}"/> </div></div>

第第4章章 カスタムユーザー属性カスタムユーザー属性

15

Page 20: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第5章 ID ブローカー APIRed Hat Single Sign-On は、ログインのために親 IDP に認証を委譲できます。典型的な例は、Postfixや Google などのソーシャルプロバイダーを介してユーザーがログインできるようにする必要がある場合です。Red Hat Single Sign-On では、既存のアカウントをブローカーの IDP にリンクすることもできます。本セクションでは、アプリケーションがアイデンティティーブローカーに関連するように使用できる一部の API について説明します。

5.1. 外部 IDP トークンの取得

Red Hat Single Sign-On では、外部 IDP を使用して、認証プロセスからのトークンおよび応答を保存することができます。これには、IDP の設定ページで Store Token 設定オプションを使用できます。

アプリケーションコードは、これらのトークンおよび応答を取得して、追加のユーザー情報をプルしたり、外部 IDP でリクエストを安全に呼び出すことができます。たとえば、アプリケーションは Googleトークンを使用して他の Google サービスおよび REST API で呼び出す場合があります。特定のアイデンティティープロバイダーのトークンを取得するには、以下のようにリクエストを送信する必要があります。

GET /auth/realms/{realm}/broker/{provider_alias}/token HTTP/1.1Host: localhost:8080Authorization: Bearer <KEYCLOAK ACCESS TOKEN>

アプリケーションが Red Hat Single Sign-On で認証され、アクセストークンを受信した。このアクセストークンには、ブローカーブローカー のクライアントレベルのロール read-token を設定する必要があります。つまり、ユーザーはこのロールのロールマッピングを持ち、クライアントアプリケーションがそのスコープ内でロールを持つ必要があります。この場合、Red Hat Single Sign-On で保護されたサービスにアクセスしている場合は、ユーザー認証中に Red Hat Single Sign-On が発行するアクセストークンを送信する必要があります。ブローカー設定ページでは、Stored Tokens Readable スイッチを有効にして、新たにインポートしたユーザーにこのロールを自動的に割り当てることができます。

これらの外部トークンは、プロバイダー経由で再度ログインするか、またはクライアント開始されたAPI を使用して再確立することができます。

5.2. クライアント開始アカウントリンク

アプリケーションによっては、Facebook などのソーシャルプロバイダーと統合したいものもありますが、このようなソーシャルプロバイダー経由でログインオプションを提供したくないものもあります。Red Hat Single Sign-On は、アプリケーションが既存のユーザーアカウントを特定の外部 IDP にリンクするために使用できるブラウザーベースの API を提供します。これは、クライアントが起動するアカウントリンクと呼ばれます。アカウントリンクは、OIDC アプリケーションによってのみ開始できます。

アプリケーションがユーザーのブラウザーを Red Hat Single Sign-On サーバーの URL に転送する方法は、ユーザーのアカウントを特定の外部プロバイダー(つまり Facebook) にリンクするように要求することです。サーバーは外部プロバイダーでログインを開始します。ブラウザーは外部プロバイダーにログインし、サーバーにリダイレクトされます。サーバーはリンクを確立し、確認のためにアプリケーションにリダイレクトします。

このプロトコルを開始する前に、クライアントアプリケーションによって満たさなければならない事前条件があります。

管理コンソールでユーザーのレルムに対して必要なアイデンティティープロバイダーを設定し、有効にする必要があります。

ユーザーアカウントは、OIDC プロトコルを使用して既存ユーザーとしてログインしている必

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

16

Page 21: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

ユーザーアカウントは、OIDC プロトコルを使用して既存ユーザーとしてログインしている必要があります。

ユーザーに account.manage-account または account.manage-account-links ロールマッピングが必要です。

アプリケーションは、アクセストークン内でそれらのロールのスコープが付与される必要があります。

リダイレクト URL を生成するには、そのアプリケーション内に情報が必要になるため、アプリケーションがアクセストークンにアクセスできる必要があります。

ログインを開始するには、アプリケーションは URL をファブリックし、ユーザーのブラウザーをこのURL にリダイレクトする必要があります。URL は以下の例のようになります。

/{auth-server-root}/auth/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}

以下は、各パスおよびクエリーパラメーターの説明です。

provider

これは、管理コンソールの Identity Provider セクションで定義した外部 IDP のプロバイダーエイリアスです。

client_id

これは、アプリケーションの OIDC クライアント ID です。管理コンソールでアプリケーションをクライアントとして登録する場合、このクライアント ID を指定する必要がありました。

redirect_uri

これは、アカウントリンクの設定後にリダイレクトするアプリケーションコールバック URL です。有効なクライアントのリダイレクト URI パターンである必要があります。つまり、管理コンソールでクライアントを登録する際に定義した有効な URL パターンのいずれかと一致する必要があります。

nonce

これは、アプリケーションが生成する必要のあるランダムな文字列です。

hash

これは Base64 URL でエンコードされたハッシュです。このハッシュは、Base64 URL で nonce + token.getSessionState() + token.getIssuedFor() + provider の SHA_256 ハッシュをエンコードすることで生成されます。トークン変数は OIDC アクセストークンから取得されます。基本的に、ランダムの nonce、ユーザーセッション ID、クライアント ID、およびアクセスするアイデンティティープロバイダーのエイリアスをハッシュ化します。

以下は、アカウントリンクを確立する URL を生成する Java Servlet コードの例です。

KeycloakSecurityContext session = (KeycloakSecurityContext) httpServletRequest.getAttribute(KeycloakSecurityContext.class.getName()); AccessToken token = session.getToken(); String clientId = token.getIssuedFor(); String nonce = UUID.randomUUID().toString(); MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); }

第第5章章 ID ブローカーブローカー API

17

Page 22: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

このハッシュが含まれている理由これにより、認証サーバーは、クライアントアプリケーションが要求を開始したことを認識し、特定のプロバイダーにリンクするユーザーアカウントにランダムに要求されないようにします。認証サーバーは、まずログイン時に設定された SSO クッキーを確認して、ユーザーがログインしているかどうかを確認します。現在のログインに基づいてハッシュを再生成し、アプリケーションによって送信されたハッシュと一致するようにします。

アカウントのリンク後、認証サーバーは redirect_uri にリダイレクトします。リンク要求の処理に問題がある場合は、認証サーバーが redirect_uri にリダイレクトされない場合があります。ブラウザーは、アプリケーションにリダイレクトする代わりに、エラーページで終了する場合があります。エラー条件があり、認証サーバーがクライアントアプリケーションにリダイレクトするのに十分な安全でない場合には、追加の error クエリーパラメーターが redirect_uri に追加されます。

警告警告

この API はアプリケーションが要求を開始したことを保証しますが、この操作に対する CSRF 攻撃を完全に防止することはできません。アプリケーションは引き続きCSRF 攻撃ターゲットに対して保護を行います。

5.2.1. 外部トークンの更新

プロバイダーにログインして生成した外部トークンを使用している場合は(例: Facebook またはGitHub トークン)、API のリンク API を再初期化してこのトークンを更新できます。

String input = nonce + token.getSessionState() + clientId + provider; byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8)); String hash = Base64Url.encode(check); request.getSession().setAttribute("hash", hash); String redirectUri = ...; String accountLinkUrl = KeycloakUriBuilder.fromUri(authServerRootUrl) .path("/auth/realms/{realm}/broker/{provider}/link") .queryParam("nonce", nonce) .queryParam("hash", hash) .queryParam("client_id", clientId) .queryParam("redirect_uri", redirectUri).build(realm, provider).toString();

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

18

Page 23: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第6章 SPI (サービスプロバイダーインターフェース)Red Hat Single Sign-On は、カスタムコードなしにほとんどのユースケースに対応するように設計されていますが、カスタマイズが可能です。Red Hat Single Sign-On には、独自のプロバイダーを実装することのできる数多くの SPI (サービスプロバイダーインターフェース) があります。

6.1. SPI の実装

SPI を実装するには、ProviderFactory インターフェースおよび Provider インターフェースを実装する必要があります。サービス設定ファイルも作成する必要があります。

たとえば、Theme Selector SPI を実装するには、ThemeSelectorProviderFactory およびThemeSelectorProvider を実装し、さらに META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory ファイルも指定する必要があります。

ThemeSelectorProviderFactory の例:

注記注記

Keycloak は、プロバイダーファクトリーのインスタンスを 1 つ作成し、複数のリクエストの状態を保管できるようにします。プロバイダーインスタンスは、各リクエストのファクトリーで create を呼び出すことで作成されるので、これらは軽量オブジェクトである必要があります。

package org.acme.provider;

import ...

public class MyThemeSelectorProviderFactory implements ThemeSelectorProviderFactory {

@Override public ThemeSelectorProvider create(KeycloakSession session) { return new MyThemeSelectorProvider(session); }

@Override public void init(Config.Scope config) { }

@Override public void postInit(KeycloakSessionFactory factory) { }

@Override public void close() { }

@Override public String getId() { return "myThemeSelector"; }}

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)

19

Page 24: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

ThemeSelectorProvider の例:

サービス設定ファイルの例 (META-INF/services/org.keycloak.theme.ThemeSelectorProviderFactory):

org.acme.provider.MyThemeSelectorProviderFactory

standalone.xml、standalone-ha.xml、または domain.xml を使用してプロバイダーを設定できます。

たとえば、以下を standalone.xml に追加します。

その後、init メソッド ProviderFactory で設定を取得できます。

プロバイダーは必要に応じて他のプロバイダーを検索することもできます。以下に例を示します。

package org.acme.provider;

import ...

public class MyThemeSelectorProvider implements ThemeSelectorProvider {

public MyThemeSelectorProvider(KeycloakSession session) { }

@Override public String getThemeName(Theme.Type type) { return "my-theme"; }

@Override public void close() { }}

<spi name="themeSelector"> <provider name="myThemeSelector" enabled="true"> <properties> <property name="theme" value="my-theme"/> </properties> </provider></spi>

public void init(Config.Scope config) { String themeName = config.get("theme");}

public class MyThemeSelectorProvider implements ThemeSelectorProvider {

private KeycloakSession session;

public MyThemeSelectorProvider(KeycloakSession session) { this.session = session; }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

20

Page 25: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

6.1.1. 管理コンソールで SPI 実装から情報を表示

プロバイダーに関する追加情報を Red Hat Single Sign-On 管理者に表示すると便利な場合があります。プロバイダーのビルド時間情報(現在インストールされているカスタムプロバイダーのバージョンなど)、プロバイダーの現在の設定(例: リモートシステムの URL)、または一部の運用情報(プロバイダーが対話するリモートシステムの応答時間)を表示できます。Red Hat Single Sign-On 管理コンソールは、Server Info ページを提供し、このような情報を表示します。

プロバイダーからの情報を表示するには、ProviderFactory に org.keycloak.provider.ServerInfoAwareProviderFactory インターフェースを実装するだけで十分です。

前の例の MyThemeSelectorProviderFactory の実装例:

6.2. プロバイダー実装の登録

プロバイダーの実装を登録する方法は 2 つあります。ほとんどの場合、Red Hat Single Sign-On デプロイヤーアプローチを使用することが推奨されます。これは、多くの依存関係を自動的に処理するためです。また、ホットデプロイメントと再デプロイメントもサポートします。

別の方法として、モジュールとしてデプロイできます。

カスタム SPI を作成する場合には、モジュールとしてデプロイする必要があります。そうでない場合には、Red Hat Single Sign-On デプロイヤーを使用することが推奨されます。

6.2.1. Red Hat Single Sign-On Deployer の使用

プロバイダー jar を Red Hat Single Sign-On の standalone/deployments/ ディレクトリーにコピーする場合は、プロバイダーが自動的にデプロイされます。ホットデプロイメントも機能します。さらに、プロバイダーの jar は、jboss-deployment-structure.xml ファイルなどの機能を使用できるように、

@Override public String getThemeName(Theme.Type type) { return session.getContext().getRealm().getLoginTheme(); }}

package org.acme.provider;

import ...

public class MyThemeSelectorProviderFactory implements ThemeSelectorProviderFactory, ServerInfoAwareProviderFactory { ...

@Override public Map<String, String> getOperationalInfo() { Map<String, String> ret = new LinkedHashMap<>(); ret.put("theme-name", "my-theme"); return ret; }}

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)

21

Page 26: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

JBoss EAP 環境にデプロイされた他のコンポーネントと同じように機能します。このファイルでは、他のコンポーネントの依存関係を設定でき、サードパーティーの jar およびモジュールを読み込むことができます。

プロバイダー jar は、EAR や WAR などの他の deployable ユニットに含めることもできます。これらのライブラリーを EAR の lib/ ディレクトリーに配置できるため、EAR をデプロイすると、サードパーティーの jar を使用するのが非常に簡単になります。

6.2.2. モジュールを使用したプロバイダーの登録

モジュールを使用してプロバイダーを登録するには、最初にモジュールを作成します。これを実行するには、jboss-cli スクリプトを使用するか、手動で KEYCLOAK_HOME/modules 内にディレクトリーを作成し、jar と module.xml を追加します。たとえば、jboss-cli スクリプトを使用してイベントリスナーの sysout サンプルプロバイダーを追加するには、以下を実行します。

KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.acme.provider --resources=target/provider.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"

または、手動でKEYCLOAK_HOME/modules/org/acme/provider/main ディレクトリーを作成して、それが起動するようにします。次に provider.jar をこのディレクトリーにコピーし、以下の内容で module.xml を作成します。

モジュールを作成したら、このモジュールを Red Hat Single Sign-On に登録する必要があります。これは、standalone.xml、standalone-ha.xml、または domain.xml の keycloak-server サブシステムセクションを編集してプロバイダーに追加します。

6.2.3. プロバイダーの無効化

プロバイダーを無効にするには、standalone.xml、standalone-ha.xml、または domain.xml でプロバイダーの enabled 属性を false に設定します。たとえば、Infinispan ユーザーキャッシュプロバイダーを無効にするには、以下を追加します。

<?xml version="1.0" encoding="UTF-8"?><module xmlns="urn:jboss:module:1.3" name="org.acme.provider"> <resources> <resource-root path="provider.jar"/> </resources> <dependencies> <module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-server-spi"/> </dependencies></module>

<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1"> <web-context>auth</web-context> <providers> <provider>module:org.keycloak.examples.event-sysout</provider> </providers> ...

<spi name="userCache"> <provider name="infinispan" enabled="false"/></spi>

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

22

Page 27: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

6.3. JAVA EE の活用

META-INF/services ファイルを適切に設定してプロバイダーを参照するように、サービスプロバイダーを Java EE コンポーネント内にパッケージ化できます。たとえば、プロバイダーがサードパーティーのライブラリーを使用する必要がある場合は、プロバイダーを ear にパッケージ化して、サードパーティーライブラリーを ear の lib/ ディレクトリーに保存します。また、プロバイダー jars は EJB、WARS、および EAR が JBoss EAP 環境で使用できる jboss-deployment-structure.xml ファイルを利用できます。このファイルの詳細は、JBoss EAP のドキュメントを参照してください。これにより、他の詳細なブランディングアクション間で外部の依存関係をプルできます。

ProviderFactory 実装はプレーンな Java オブジェクトである必要があります。ただし、現在、プロバイダークラスを Stateful EJB として実装することもサポートしています。これは、以下の方法で行います。

@Local アノテーションを定義し、そこにプロバイダークラスを指定する必要があります。これを実行しない場合、EJB はプロバイダーインスタンスを正しくプロキシーせず、プロバイダーが機能しません。

@Remove アノテーションをプロバイダーの close() メソッドに配置する必要があります。指定しないと、ステートフル Bean はクリーンアップされず、最終的にエラーメッセージが表示されることがあります。

ProviderFactory の実装は、プレーンな Java オブジェクトである必要があります。ファクトリークラ

@Stateful@Local(EjbExampleUserStorageProvider.class)public class EjbExampleUserStorageProvider implements UserStorageProvider, UserLookupProvider, UserRegistrationProvider, UserQueryProvider, CredentialInputUpdater, CredentialInputValidator, OnUserCache{ @PersistenceContext protected EntityManager em;

protected ComponentModel model; protected KeycloakSession session;

public void setModel(ComponentModel model) { this.model = model; }

public void setSession(KeycloakSession session) { this.session = session; }

@Remove @Override public void close() { }...}

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)

23

Page 28: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

ProviderFactory の実装は、プレーンな Java オブジェクトである必要があります。ファクトリークラスは create() メソッドで Stateful EJB の JNDI ルックアップを実行します。

6.4. JAVASCRIPT プロバイダー

Red Hat Single Sign-On には、管理者が特定の機能のカスタマイズを可能にするために、ランタイム時にスクリプトを実行する機能があります。

オーセンティケーター

JavaScript ポリシー

OpenID Connect Protocol Mapper

6.4.1. オーセンティケーター

認証スクリプトは、Authenticator#action(AuthenticationFlowContext) から呼び出される Authenticator#authenticate(AuthenticationFlowContext) action(..) から呼び出される authenticate(..) 関数を少なくとも 1 つ提供する必要があります。

カスタム Authenticator は、最低でも authenticate(..) 関数を提供する必要があります。コード内で javax.script.Bindings スクリプトを使用できます。

script

スクリプトメタデータにアクセスするための ScriptModel

realm

RealmModel

user

現行の UserModel

session

アクティブな KeycloakSession

authenticationSession

現在の AuthenticationSessionModel

public class EjbExampleUserStorageProviderFactory implements UserStorageProviderFactory<EjbExampleUserStorageProvider> {

@Override public EjbExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) { try { InitialContext ctx = new InitialContext(); EjbExampleUserStorageProvider provider = (EjbExampleUserStorageProvider)ctx.lookup( "java:global/user-storage-jpa-example/" + EjbExampleUserStorageProvider.class.getSimpleName()); provider.setModel(model); provider.setSession(session); return provider; } catch (Exception e) { throw new RuntimeException(e); } }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

24

Page 29: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

httpRequest

the current org.jboss.resteasy.spi.HttpRequest

LOG

ScriptBasedAuthenticator にスコープ指定された org.jboss.logging.Logger

注記注記

authenticate(context) action(context) 関数に渡される context 引数から追加のコンテキスト情報を抽出できます。

6.4.2. デプロイするスクリプトで JAR を作成します。

注記注記

JAR ファイルは、.jar 拡張子を持つ通常の ZIP ファイルです。

Red Hat Single Sign-On でスクリプトを使用できるようにするには、サーバーにデプロイする必要があります。そのため、以下の構造で JAR ファイルを作成します。

META-INF/keycloak-scripts.json

my-script-authenticator.jsmy-script-policy.jsmy-script-mapper.js

META-INF/keycloak-scripts.json は、デプロイするスクリプトに関するメタデータ情報を提供するファイル記述子です。これは、以下の構造を持つ JSON ファイルです。

AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");

function authenticate(context) {

LOG.info(script.name + " --> trace auth for: " + user.username);

if ( user.username === "tester" && user.getAttribute("someAttribute") && user.getAttribute("someAttribute").contains("someValue")) {

context.failure(AuthenticationFlowError.INVALID_USER); return; }

context.success();}

{ "authenticators": [ { "name": "My Authenticator", "fileName": "my-script-authenticator.js", "description": "My Authenticator from a JS file"

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)

25

Page 30: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

このファイルは、デプロイする異なるタイプのスクリプトプロバイダーを参照する必要があります。

authenticatorsOpenID Connect スクリプトオーセンティケーターの場合。同じ JAR ファイルに 1 つ以上のオーセンティケーターを指定することができます。

policiesRed Hat Single Sign-On の Authorization Services を使用する場合の JavaScript ポリシー。同じ JAR ファイルに 1 つまたは複数のポリシーを指定できます。

mappersOpenID Connect Script Protocol Mapper 用。同じ JAR ファイルに 1 つまたは複数のマッパーを指定できます。

JAR ファイルのスクリプトファイルごとに、スクリプトファイルを特定のプロバイダータイプにマッピングする META-INF/keycloak-scripts.json に対応するエントリーが必要です。各エントリーに以下のプロパティーを指定する必要があります。

nameRed Hat Single Sign-On 管理コンソールからスクリプトを表示するために使用される平易な名前。指定しないと、代わりにスクリプトファイルの名前が使用されます。

descriptionスクリプトファイルの意図をより効果的に説明する任意のテキスト

fileNameスクリプトファイルの名前。このプロパティーは 必須必須 であり、JAR 内のファイルにマップする必要があります。

6.4.3. スクリプト JAR のデプロイ

記述子とデプロイするスクリプトが含まれる JAR ファイルを追加したら、JAR を Red Hat Single Sign-On の standalone/deployments/ ディレクトリーにコピーする必要があります。

6.4.4. Red Hat Single Sign-On 管理コンソールを使用したスクリプトのアップロード

注記注記

} ], "policies": [ { "name": "My Policy", "fileName": "my-script-policy.js", "description": "My Policy from a JS file" } ], "mappers": [ { "name": "My Mapper", "fileName": "my-script-mapper.js", "description": "My Mapper from a JS file" } ]}

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

26

Page 31: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

注記注記

管理コンソールでスクリプトをアップロードする機能は非推奨となり、Red Hat SingleSign-On の今後のバージョンで削除されます。

管理者は、サーバーにスクリプトをアップロードできません。この動作は、悪意のあるスクリプトが誤って実行された場合に、システムに潜在的な悪影響を防ぎます。管理者は、起動時にスクリプトを実行すると攻撃を防ぐため、JAR ファイルを使用してスクリプトを直接サーバーに直接デプロイする必要があります。

スクリプトをアップロードする機能を明示的に有効にできます。これは、できるだけ早くすべてのスクリプトを直接サーバーにデプロイするため、優れた注意と計画と共に使用する必要があります。

upload_scripts 機能を有効にする方法の詳細。プロファイル を確認してください。

6.5. 利用可能な SPI

ランタイム時に利用可能なすべての SPI の一覧を表示するには、「管理コンソール」セクションの説明に従って、管理コンソール の Server Info ページを参照してください。

第第6章章 SPI (サービスプロバイダーインターフェースサービスプロバイダーインターフェース)

27

Page 32: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第7章 ユーザーストレージ SPIユーザーストレージ SPI を使用して拡張機能を Red Hat Single Sign-On に書き込み、外部のユーザーデータベースおよび認証情報ストアに接続することができます。ビルトイン LDAP およびActiveDirectory サポートは、この SPI の実装を実行中です。初期状態の Red Hat Single Sign-On は、ローカルデータベースを使用してユーザーを作成、更新、および検証します。ただし、組織には既存の外部プロプライエタリーユーザーデータベースがあり、Red Hat Single Sign-On のデータモデルに移行できません。このような場合、アプリケーション開発者は、ユーザーストレージ SPI の実装を記述して、外部ユーザーストアと、Red Hat Single Sign-On が使用する内部ユーザーオブジェクトモデルをブリッジすることができます。

Red Hat Single Sign-On ランタイムがユーザーを検索する必要がある場合(ユーザーのログイン時など)、ユーザーを特定するために多くの手順を実施します。最初に、ユーザーがユーザーキャッシュに含まれているかどうかを確認します。ユーザーが見つかった場合はそのインメモリー表現を使用します。次に、Red Hat Single Sign-On のローカルデータベース内でユーザーを検索します。ユーザーが見つからなかった場合、ランタイムが検索しているユーザーを返すまで、User Storage SPI プロバイダーの実装をループしてユーザークエリーを実行します。プロバイダーはユーザーの外部ユーザーストアをクエリーし、ユーザーの外部データ表現を Red Hat Single Sign-On のユーザーのメタモデルにマッピングします。

ユーザーストレージ SPI プロバイダーの実装は、複雑な基準クエリーの実行、ユーザーの CRUD 操作の実行、認証情報の検証および管理、または多数のユーザーの一括更新を一度に実行できます。外部ストアの機能により異なります。

ユーザーストレージ SPI プロバイダーの実装は(多くの場合)Java EE コンポーネントと同様にパッケージ化され、デプロイされます。これらはデフォルトでは有効になっていませんが、管理コンソールの User Federation タブでレルムごとに有効および設定する必要があります。

7.1. プロバイダーインターフェース

User Storage SPI の実装をビルドする場合、プロバイダークラスとプロバイダーファクトリーを定義する必要があります。プロバイダークラスインスタンスは、プロバイダーファクトリーによってトランザクションごとに作成されます。プロバイダークラスは、ユーザー検索やその他のユーザー操作の負荷が非常に大きいものをすべて実行します。org.keycloak.storage.UserStorageProvider インターフェースを実装する必要があります。

package org.keycloak.storage;

public interface UserStorageProvider extends Provider {

/** * Callback when a realm is removed. Implement this if, for example, you want to do some * cleanup in your user storage when a realm is removed * * @param realm */ default void preRemove(RealmModel realm) {

}

/** * Callback when a group is removed. Allows you to do things like remove a user * group mapping in your external store if appropriate

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

28

Page 33: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

UserStorageProvider インターフェースはほんのわずかであると思われるかもしれません。本章では、プロバイダークラスがユーザーの統合をサポートするように実装できる他の混在インターフェースが存在することを前提としています。

UserStorageProvider インスタンスは、トランザクションごとに 1 度作成されます。トランザクションが完了すると、UserStorageProvider.close() メソッドが呼び出され、インスタンスはガベージコレクションされます。インスタンスはプロバイダーファクトリーによって作成されます。プロバイダーファクトリーは org.keycloak.storage.UserStorageProviderFactory インターフェースを実装します。

* * @param realm * @param group */ default void preRemove(RealmModel realm, GroupModel group) {

}

/** * Callback when a role is removed. Allows you to do things like remove a user * role mapping in your external store if appropriate

* @param realm * @param role */ default void preRemove(RealmModel realm, RoleModel role) {

}

}

package org.keycloak.storage;

/** * @author <a href="mailto:[email protected]">Bill Burke</a> * @version $Revision: 1 $ */public interface UserStorageProviderFactory<T extends UserStorageProvider> extends ComponentFactory<T, UserStorageProvider> {

/** * This is the name of the provider and will be shown in the admin console as an option. * * @return */ @Override String getId();

/** * called per Keycloak transaction. * * @param session * @param model * @return */

第第7章章 ユーザーストレージユーザーストレージ SPI

29

Page 34: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

プロバイダーファクトリークラスは、UserStorageProviderFactory を実装する際に、具体的なプロバイダークラスをテンプレートパラメーターとして指定する必要があります。これはランタイムがインストロスペクトして機能をスキャンします(実装する他のインターフェース)。たとえば、プロバイダークラスの名前が FileProvider の場合、ファクトリークラスは以下のようになります。

getId() メソッドは、User Storage プロバイダーの名前を返します。この ID は、特定のレルムのプロバイダーを有効にする際に、管理コンソールのユーザーフェデレーションページに表示されます。

create() メソッドは、プロバイダークラスのインスタンスを割り当てます。org.keycloak.models.KeycloakSession パラメーターを取ります。このオブジェクトは、他の情報やメタデータを検索し、ランタイム内の他のコンポーネントへのアクセスを提供するために使用できます。ComponentModel パラメーターは、プロバイダーが特定のレルム内でどのように有効および設定されたかを表します。有効化されたプロバイダーのインスタンス ID と、管理コンソールで有効化した場合に指定した可能性のある設定が含まれます。

UserStorageProviderFactory には他の機能があり、この後、本章で取り上げます。

7.2. プロバイダー機能インターフェース

UserStorageProvider インターフェースを詳しく調べると、ユーザーを検索または管理するための方法を定義しないことが分かります。これらの方法は、外部ユーザーストアが提供および実行可能な機能の範囲に応じて、実際に他の 機能インターフェース機能インターフェース で定義されます。たとえば、一部の外部ストアは読み取り専用であり、単純なクエリーと認証情報の検証のみが可能です。可能な機能の 機能インター機能インターフェースフェース を実装することのみが必要になります。これらのインターフェースを実装することができます。

SPI 説明説明

org.keycloak.storage.user.UserLookupProvider

このインターフェースは、外部ストアからユーザーでログインできる場合に必要です。大半 (またはすべて) プロバイダーはこのインターフェースを実装します。

org.keycloak.storage.user.UserQueryProvider

1 つ以上のユーザーを見つけるために使用される複雑なクエリーを定義します。管理コンソールからユーザーを表示および管理する場合には、このインターフェースを実装する必要があります。

org.keycloak.storage.user.UserRegistrationProvider

プロバイダーがユーザーの追加と削除をサポートする場合は、このインターフェースを実装します。

T create(KeycloakSession session, ComponentModel model);...}

public class FileProviderFactory implements UserStorageProviderFactory<FileProvider> {

public String getId() { return "file-provider"; }

public FileProvider create(KeycloakSession session, ComponentModel model) { ... }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

30

Page 35: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

org.keycloak.storage.user.UserBulkUpdateProvider

プロバイダーが一連のユーザーの一括更新をサポートする場合は、このインターフェースを実装します。

org.keycloak.credential.CredentialInputValidator

プロバイダーが 1 つ以上の異なるクレデンシャルタイプを検証できる場合にこのインターフェースを実装します(例: プロバイダーがパスワードを検証できる場合など)。

org.keycloak.credential.CredentialInputUpdater

プロバイダーが 1 つ以上の異なる認証情報タイプの更新をサポートする場合は、このインターフェースを実装します。

SPI 説明説明

7.3. モデルインターフェース

機能機能 インターフェースインターフェース で定義されるメソッドのほとんどは、ユーザーの表現を返すか、または渡されます。これらの表現は、org.keycloak.models.UserModel インターフェースで定義されます。アプリケーション開発者は、このインターフェースを実装する必要があります。外部ユーザーストアと、RedHat Single Sign-On が使用するユーザーメタモデルのマッピングを提供します。

UserModel 実装は、ユーザー名、名前、電子メール、ロール、グループマッピング、その他の任意の属性などのユーザーについてのメタデータの読み取りおよび更新へのアクセスを提供します。

org.keycloak.models パッケージには、Red Hat Single Sign-On メタモデルの他の部分を表す他のモデルクラスがあります (RealmModel、RoleModel、GroupModel、および ClientModel)。

7.3.1. ストレージ ID

UserModel の重要なメソッドの 1 つとして、getId() メソッドがあります。UserModel の開発者は、ユーザー ID 形式を認識する必要があります。形式は以下のとおりです。

package org.keycloak.models;

public interface UserModel extends RoleMapperModel { String getId();

String getUsername(); void setUsername(String username);

String getFirstName(); void setFirstName(String firstName);

String getLastName(); void setLastName(String lastName);

String getEmail(); void setEmail(String email);...}

第第7章章 ユーザーストレージユーザーストレージ SPI

31

Page 36: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

"f:" + component id + ":" + external id

多くの場合、Red Hat Single Sign-On ランタイムはユーザー ID でユーザーを検索する必要があります。ユーザー ID には十分な情報が含まれているため、ランタイムは、システム内のすべての UserStorageProvider を紹介して、ユーザーを見つける必要はありません。

コンポーネント ID は、ComponentModel.getId() から返される ID です。ComponentModel はプロバイダークラスの作成時にパラメーターとして渡され、そこから取得できるようにします。外部 ID は、プロバイダークラスが外部ストアでユーザーを検索する必要がある情報です。多くの場合、ユーザー名または uid になります。たとえば、以下のようになります。

f:332a234e31234:wburke

ランタイムが ID でルックアップが実行されると、ID はコンポーネント ID を取得するために解析されます。コンポーネント ID は、ユーザーの読み込みに最初に使用された UserStorageProvider を見つけるために使用されます。そのプロバイダーは id を渡します。プロバイダーは、再度 id を解析し、外部ユーザーストレージでユーザーを見つけるのに使用します。

7.4. パッケージおよびデプロイメント

ユーザーストレージプロバイダーは JAR にパッケージ化され、JBoss EAP アプリケーションサーバーに何かをデプロイするのと同じ方法で Red Hat Single Sign-On ランタイムにデプロイまたはデプロイ解除されます。JAR を直接サーバーの standalone/deployments/ ディレクトリーにコピーするか、または JBoss CLI を使用してデプロイメントを実行できます。

Red Hat Single Sign-On がプロバイダーを認識できるようにするには、ファイルを JAR (META-INF/services/org.keycloak.storage.UserStorageProviderFactory) に追加する必要があります。このファイルには、UserStorageProviderFactory 実装の完全修飾クラス名の行区切りリストが含まれている必要があります。

org.keycloak.examples.federation.properties.ClasspathPropertiesStorageFactoryorg.keycloak.examples.federation.properties.FilePropertiesStorageFactory

Red Hat Single Sign-On は、これらのプロバイダー JAR のホットデプロイメントをサポートします。本章では、Java EE コンポーネントとしてパッケージ化できる、後ほど説明します。

7.5. シンプルな読み取り専用、ルックアップの例

User Storage SPI の実装の基本を示すために、簡単な例を見てみましょう。本章では、簡単なプロパティーファイルでユーザーを検索する簡単な UserStorageProvider の実装を説明します。プロパティーファイルにはユーザー名とパスワードの定義が含まれ、クラスパスの特定の場所にハードコーディングされます。プロバイダーは ID およびユーザー名でユーザーを検索し、パスワードを検証することもできます。このプロバイダーから生成されたユーザーは読み取り専用になります。

7.5.1. プロバイダークラス

最初に説明するのは UserStorageProvider クラスです。

public class PropertyFileUserStorageProvider implements UserStorageProvider, UserLookupProvider, CredentialInputValidator, CredentialInputUpdater

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

32

Page 37: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

プロバイダークラス PropertyFileUserStorageProvider は多くのインターフェースを実装します。SPIのベース要件であるように UserStorageProvider を実装します。このプロバイダーが保存したユーザーでログインできるため、UserLookupProvider インターフェースを実装します。ログイン画面で入力されたパスワードを検証する必要があるため、CredentialInputValidator インターフェースを実装します。プロパティーファイルは読み取り専用です。ユーザーがパスワードを更新しようとするとエラー条件を投稿するため、CredentialInputUpdater を実装します。

このプロバイダークラスのコンストラクターは KeycloakSession、ComponentModel、およびプロパティーファイルへの参照を保存します。これらすべては後ほど使用します。また、ロードされたユーザーのマップがあることにも注意してください。ユーザーを見つけると、同じトランザクション内で再度作成されないように、このマップに保存されます。これには多くのプロバイダーに従う必要があります(JPA と統合するプロバイダー)。また、プロバイダークラスインスタンスがトランザクションごとに 1 回作成され、トランザクションの完了後に閉じられることに注意してください。

7.5.1.1. UserLookupProvider 実装実装

{...}

protected KeycloakSession session; protected Properties properties; protected ComponentModel model; // map of loaded users in this transaction protected Map<String, UserModel> loadedUsers = new HashMap<>();

public PropertyFileUserStorageProvider(KeycloakSession session, ComponentModel model, Properties properties) { this.session = session; this.model = model; this.properties = properties; }

@Override public UserModel getUserByUsername(String username, RealmModel realm) { UserModel adapter = loadedUsers.get(username); if (adapter == null) { String password = properties.getProperty(username); if (password != null) { adapter = createAdapter(realm, username); loadedUsers.put(username, adapter); } } return adapter; }

protected UserModel createAdapter(RealmModel realm, String username) { return new AbstractUserAdapter(session, realm, model) { @Override public String getUsername() { return username; } }; }

第第7章章 ユーザーストレージユーザーストレージ SPI

33

Page 38: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

getUserByUsername() メソッドは、ユーザーがログインすると Red Hat Single Sign-On ログインページにより呼び出されます。実装では最初に loadedUsers マップを確認し、ユーザーがこのトランザクション内にすでに読み込まれているかどうかを確認します。読み込まれていない場合は、ユーザー名のプロパティーファイルを調べます。UserModel の実装を作成する場合は、将来のの参照のために loadedUsers に保存し、このインスタンスを返します。

createAdapter() メソッドはヘルパークラス org.keycloak.storage.adapter.AbstractUserAdapter を使用します。これにより、UserModel のベース実装が提供されます。ユーザー ID を外部 ID として使用して、必要なストレージ ID 形式に基づいてユーザー ID が自動的に生成されます。

"f:" + component id + ":" + username

AbstractUserAdapter のすべての get メソッドは、Null または空のコレクションを返します。ただし、ロールとグループマッピングを返すメソッドは、全ユーザーに対してレルムに設定されたデフォルトのロールとグループを返します。AbstractUserAdapter のすべてのセットメソッドは、org.keycloak.storage.ReadOnlyException を発生させます。そのため、管理コンソールでユーザーを変更しようとすると、エラーが発生します。

getUserById() メソッドは、org.keycloak.storage.StorageId ヘルパークラスを使用して id パラメーターを解析します。StorageId.getExternalId() メソッドが呼び出され、id パラメーターに埋め込まれたユーザー名を取得します。その後、メソッドは getUserByUsername() に委譲します。

メールは保存されないため、getUserByEmail() メソッドは null を返します。

7.5.1.2. CredentialInputValidator 実装実装

次に、CredentialInputValidator のメソッド実装を確認します。

@Override public UserModel getUserById(String id, RealmModel realm) { StorageId storageId = new StorageId(id); String username = storageId.getExternalId(); return getUserByUsername(username, realm); }

@Override public UserModel getUserByEmail(String email, RealmModel realm) { return null; }

@Override public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { String password = properties.getProperty(user.getUsername()); return credentialType.equals(CredentialModel.PASSWORD) && password != null; }

@Override public boolean supportsCredentialType(String credentialType) { return credentialType.equals(CredentialModel.PASSWORD); }

@Override public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { if (!supportsCredentialType(input.getType())) return false;

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

34

Page 39: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

isConfiguredFor() メソッドはランタイムによって呼び出され、特定の認証情報タイプがユーザーに設定されているかどうかを判断します。この方法は、パスワードがユーザーに設定されていることを確認します。

supportsCredentialType() メソッドは、特定の認証情報タイプに対して検証がサポートされるかどうかを返します。認証情報のタイプが password であるかを確認します。

isValid() メソッドはパスワードを検証します。CredentialInput パラメーターは、すべての認証情報タイプの抽象インターフェースにすぎません。認証情報タイプもサポートし、UserCredentialModel のインスタンスであることを確認します。ユーザーがログインページでログインすると、UserCredentialModel のインスタンスに入力されたパスワード入力のプレーンテキストが追加されます。isValid() メソッドは、この値をプロパティーファイルに保存されているプレーンテキストのパスワードと照合します。戻り値 true はパスワードが有効であることを意味します。

7.5.1.3. CredentialInputUpdater 実装実装

前述したように、CredentialInputUpdater インターフェースを実装する唯一の理由は、ユーザーパスワードの変更を禁止することです。これを実行しないと、ランタイムで Red Hat Single Sign-On のローカルストレージでパスワードを上書きできるためです。本章の後半でこれについてさらに説明します。

updateCredential() メソッドは、認証情報タイプがパスワードであるかどうかを確認します。存在する場合は、ReadOnlyExceptionが発生します。

7.5.2. プロバイダーファクトリーの実装

これで、プロバイダークラスが完了し、プロバイダーファクトリークラスに注意が加えられました。

String password = properties.getProperty(user.getUsername()); if (password == null) return false; return password.equals(input.getChallengeResponse()); }

@Override public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { if (input.getType().equals(CredentialModel.PASSWORD)) throw new ReadOnlyException("user is read only for this update");

return false; }

@Override public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {

}

@Override public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) { return Collections.EMPTY_SET; }

public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {

public static final String PROVIDER_NAME = "readonly-property-file";

第第7章章 ユーザーストレージユーザーストレージ SPI

35

Page 40: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

最初にUserStorageProviderFactory クラスを実装する場合は、具体的なプロバイダークラス実装をテンプレートパラメーターとして渡す必要があります。ここで、以前に定義したプロバイダークラス(PropertyFileUserStorageProvider) を指定します。

警告警告

template パラメーターを指定しない場合、プロバイダーは機能しません。ランタイムはクラスのイントロスペクションを実行し、プロバイダーが実装する 機能イン機能インターフェースターフェース を判別します。

getId() メソッドはランタイムのファクトリーを識別し、レルムのユーザーストレージプロバイダーを有効にする場合も管理コンソールに表示される文字列になります。

7.5.2.1. 初期化初期化

UserStorageProviderFactory インターフェースには、実装できる任意の init() メソッドがあります。Red Hat Single Sign-On の起動時に、各プロバイダーファクトリーのインスタンスは 1 つだけ作成されます。また、システムの起動時に、これらの各ファクトリーのインスタンスでも init() メソッドが呼び出されます。同様に実装できる postInit() メソッドも実装できます。各ファクトリーの init() メソッドが呼び出されると、postInit() メソッドが呼び出されます。

@Override public String getId() { return PROVIDER_NAME; }

private static final Logger logger = Logger.getLogger(PropertyFileUserStorageProviderFactory.class); protected Properties properties = new Properties();

@Override public void init(Config.Scope config) { InputStream is = getClass().getClassLoader().getResourceAsStream("/users.properties");

if (is == null) { logger.warn("Could not find users.properties in classpath"); } else { try { properties.load(is); } catch (IOException ex) { logger.error("Failed to load users.properties file", ex); } } }

@Override public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) { return new PropertyFileUserStorageProvider(session, model, properties); }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

36

Page 41: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

init() メソッド実装では、クラスパスからユーザー宣言が含まれるプロパティーファイルを見つけます。次に properties フィールドを、そこに保存されたユーザー名とパスワードの組み合わせでロードします。

Config.Scope パラメーターは、standalone.xml、standalone-ha.xml、または domain.xml 内で設定できるファクトリー設定です。

たとえば、以下を standalone.xml に追加した場合は以下のようになります。

ハードコーディングするのではなく、ユーザープロパティーファイルのクラスパスを指定できます。次に、PropertyFileUserStorageProviderFactory.init() で設定を取得できます。

7.5.2.2. Create メソッドメソッド

プロバイダーファクトリー作成における最後の手順は create() メソッドです。

PropertyFileUserStorageProvider クラスを割り当てるだけです。この作成メソッドは、トランザクションごとに 1 回呼び出されます。

7.5.3. パッケージおよびデプロイメント

プロバイダー実装のクラスファイルは jar に配置される必要があります。また、META-INF/services/org.keycloak.storage.UserStorageProviderFactory ファイルでプロバイダーファクトリークラスを宣言する必要があります。

org.keycloak.examples.federation.properties.FilePropertiesStorageFactory

Jar を作成したら、通常の JBoss EAP を使用してデプロイできます。つまり、jar を standalone/deployments/ ディレクトリーにコピーするか、または JBoss CLI を使用してデプロイできます。

7.5.4. 管理コンソールでのプロバイダーの有効化

<spi name="storage"> <provider name="readonly-property-file" enabled="true"> <properties> <property name="path" value="/other-users.properties"/> </properties> </provider></spi>

public void init(Config.Scope config) { String path = config.get("path"); InputStream is = getClass().getClassLoader().getResourceAsStream(path);

...}

@Override public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) { return new PropertyFileUserStorageProvider(session, model, properties); }

第第7章章 ユーザーストレージユーザーストレージ SPI

37

Page 42: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

管理コンソールの User Federation ページで、レルムごとにユーザーストレージプロバイダーを有効にします。

readonly-property-file リストから作成したプロバイダーを選択します。これにより、プロバイダーの設定ページに移動します。設定すべきものはないため、Save をクリックします。

メインの User Federation ページに戻ると、プロバイダーが一覧表示されます。

これで、users.properties ファイルで宣言されたユーザーでログインできるようになります。このユーザーは、ログイン後にのみアカウントページを表示できます。

7.6. 設定技術

PropertyFileUserStorageProvider の例は少々工夫されています。これは、プロバイダーの jar に組み込まれるプロパティーファイルにハードコーディングされますが、これは特に有用なものではありません。プロバイダーインスタンスごとにこのファイルの場所を設定可能な場合があります。つまり、このプロバイダーを複数の異なるレルムで複数回再利用し、完全に異なるユーザープロパティーファイルを示すことがあります。また、管理コンソール UI 内でこの設定を実行することもできます。

UserStorageProviderFactory には、プロバイダー設定を実装できる追加のメソッドがあります。プロバイダーごとに設定する変数を記述し、管理コンソールが汎用入力ページを自動的にレンダリングしてこの設定を収集します。実装時には、コールバックメソッドは、プロバイダーの初回作成時、および更新時の設定を検証する前に設定を検証します。UserStorageProviderFactoryは、org.keycloak.component.ComponentFactory インターフェースからこれらのメソッドを継承します。

ComponentFactory.getConfigProperties() メソッドは、org.keycloak.provider.ProviderConfigProperty インスタンスの一覧を返します。これらのインスタンスは、プロバイダーの各設定変数のレンダリングおよび保存に必要なメタデータを宣言します。

7.6.1. 設定例

PropertyFileUserStorageProviderFactory の例を拡張して、プロバイダーインスタンスをディスク上の特定のファイルを指定できるようにします。

List<ProviderConfigProperty> getConfigProperties();

default void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel model) throws ComponentValidationException {

}

default void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {

}

default void onUpdate(KeycloakSession session, RealmModel realm, ComponentModel model) {

}

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

38

Page 43: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

PropertyFileUserStorageProviderFactory

ProviderConfigurationBuilder クラスは、設定プロパティーのリストを作成するためのヘルパークラスです。ここでは、String タイプである path という名前の変数を指定します。このプロバイダーの管理コンソール設定ページでは、この設定変数は Path としてラベル付けされ、デフォルト値は ${jboss.server.config.dir}/example-users.properties です。この設定オプションのツールチップにカーソルを合わせると、ヘルプテキスト (File path to properties file (プロパティーファイルへのファプロパティーファイルへのファイルパスイルパス)) が表示されます。

次に、このファイルがディスクに存在することを確認します。有効なユーザープロパティーファイルを指定しない限り、レルムでこのプロバイダーのインスタンスを有効にしないでください。これを実行するには、validateConfiguration() メソッドを実装します。

validateConfiguration() メソッドでは、ComponentModel から設定変数を取得し、そのファイルがディスクに存在するかどうかを確認します。org.keycloak.common.util.EnvUtil.replace() メソッドを使用することに注意してください。このメソッドでは、${} が含まれる文字列がシステムプロパティーの値に置き換えられます。${jboss.server.config.dir} 文字列はサーバーの configuration/ ディレクトリーに対応し、この例では役に立ちます。

次に、古い init() メソッドを削除します。これは、ユーザープロパティーファイルがプロバイダーインスタンスごとに一意になるためです。このロジックは create() メソッドに移動します。

public class PropertyFileUserStorageProviderFactory implements UserStorageProviderFactory<PropertyFileUserStorageProvider> {

protected static final List<ProviderConfigProperty> configMetadata;

static { configMetadata = ProviderConfigurationBuilder.create() .property().name("path") .type(ProviderConfigProperty.STRING_TYPE) .label("Path") .defaultValue("${jboss.server.config.dir}/example-users.properties") .helpText("File path to properties file") .add().build(); }

@Override public List<ProviderConfigProperty> getConfigProperties() { return configMetadata; }

@Override public void validateConfiguration(KeycloakSession session, RealmModel realm, ComponentModel config) throws ComponentValidationException { String fp = config.getConfig().getFirst("path"); if (fp == null) throw new ComponentValidationException("user property file does not exist"); fp = EnvUtil.replace(fp); File file = new File(fp); if (!file.exists()) { throw new ComponentValidationException("user property file does not exist"); } }

第第7章章 ユーザーストレージユーザーストレージ SPI

39

Page 44: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

このロジックは、すべてのトランザクションがユーザープロパティーファイル全体をディスクから読み取るため、非効率ですが、この設定変数のフックの単純な方法になります。

7.6.2. 管理コンソールでのプロバイダーの設定

設定が有効になったため、管理コンソールでプロバイダーを設定したときに path 変数を設定できます。

7.7. ユーザーの追加/削除およびクエリー機能インターフェースの追加/削除

この例で実行しなかったことの 1 つは、ユーザーの追加やパスワードの変更を可能にすることです。この例で定義されたユーザーは、管理コンソールでもクエリー可能または表示できません。これらの拡張機能を追加するには、example プロバイダーが UserQueryProvider インターフェースおよび UserRegistrationProvider インターフェースを実装する必要があります。

7.7.1. UserRegistrationProvider の実装

この特定のストアからユーザーの追加と削除を実装するには、最初にプロパティーファイルをディスクに保存できるようにする必要があります。

PropertyFileUserStorageProvider

次に、addUser() メソッドおよび removeUser() メソッドの実装が簡単になります。

PropertyFileUserStorageProvider

@Override public PropertyFileUserStorageProvider create(KeycloakSession session, ComponentModel model) { String path = model.getConfig().getFirst("path");

Properties props = new Properties(); try { InputStream is = new FileInputStream(path); props.load(is); is.close(); } catch (IOException e) { throw new RuntimeException(e); }

return new PropertyFileUserStorageProvider(session, model, props); }

public void save() { String path = model.getConfig().getFirst("path"); path = EnvUtil.replace(path); try { FileOutputStream fos = new FileOutputStream(path); properties.store(fos, ""); fos.close(); } catch (IOException e) { throw new RuntimeException(e); } }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

40

Page 45: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

ユーザーを追加する場合は、プロパティーマップのパスワード値を UNSET_PASSWORD に設定することに注意してください。これは、プロパティー値のプロパティーに null 値を含めることができないためです。また、これを反映するように CredentialInputValidator メソッドを変更する必要もあります。

プロバイダーがUserRegistrationProvider インターフェースを実装している場合は、addUser() メソッドが呼び出されます。プロバイダーにユーザーの追加をオフにする設定スイッチがある場合は、このメソッドから null を返すとこのプロバイダーが飛ばされ、次のプロバイダーを呼び出します。

PropertyFileUserStorageProvider

プロパティーファイルを保存できるようになりました。そのため、パスワードの更新を許可することもできます。

PropertyFileUserStorageProvider

public static final String UNSET_PASSWORD="#$!-UNSET-PASSWORD";

@Override public UserModel addUser(RealmModel realm, String username) { synchronized (properties) { properties.setProperty(username, UNSET_PASSWORD); save(); } return createAdapter(realm, username); }

@Override public boolean removeUser(RealmModel realm, UserModel user) { synchronized (properties) { if (properties.remove(user.getUsername()) == null) return false; save(); return true; } }

@Override public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { if (!supportsCredentialType(input.getType()) || !(input instanceof UserCredentialModel)) return false;

UserCredentialModel cred = (UserCredentialModel)input; String password = properties.getProperty(user.getUsername()); if (password == null || UNSET_PASSWORD.equals(password)) return false; return password.equals(cred.getValue()); }

@Override public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { if (!(input instanceof UserCredentialModel)) return false; if (!input.getType().equals(CredentialModel.PASSWORD)) return false; UserCredentialModel cred = (UserCredentialModel)input; synchronized (properties) { properties.setProperty(user.getUsername(), cred.getValue()); save();

第第7章章 ユーザーストレージユーザーストレージ SPI

41

Page 46: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

パスワードの無効化も実装できるようになりました。

PropertyFileUserStorageProvider

これらの方法を実装すると、管理コンソールでユーザーのパスワードを変更および無効にできます。

7.7.2. UserQueryProvider の実装

UserQueryProvider を実装しないと、管理コンソールは example プロバイダーによって読み込まれたユーザーを表示および管理できません。このインターフェースの実装を見てみましょう。

PropertyFileUserStorageProvider

} return true; }

@Override public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { if (!credentialType.equals(CredentialModel.PASSWORD)) return; synchronized (properties) { properties.setProperty(user.getUsername(), UNSET_PASSWORD); save(); }

}

private static final Set<String> disableableTypes = new HashSet<>();

static { disableableTypes.add(CredentialModel.PASSWORD); }

@Override public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {

return disableableTypes; }

@Override public int getUsersCount(RealmModel realm) { return properties.size(); }

@Override public List<UserModel> getUsers(RealmModel realm) { return getUsers(realm, 0, Integer.MAX_VALUE); }

@Override public List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults) { List<UserModel> users = new LinkedList<>(); int i = 0; for (Object obj : properties.keySet()) { if (i++ < firstResult) continue;

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

42

Page 47: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

getUsers() メソッドはプロパティーファイルのキーセットを繰り返し処理し、getUserByUsername()にユーザーを読み込みます。firstResult パラメーターおよび maxResults パラメーターに基づいてこの呼び出しにインデックス化していることに留意してください。外部ストアがページネーションをサポートしない場合は、同様のロジックを行う必要があります。

PropertyFileUserStorageProvider

searchForUser() の最初の宣言は String パラメーターを取ります。これは、ユーザー名とメール属性の検索に使用する文字列で、ユーザーを見つける必要があります。この文字列は部分文字列にできます。そのため、検索時に String.contains() メソッドを使用します。

PropertyFileUserStorageProvider

String username = (String)obj; UserModel user = getUserByUsername(username, realm); users.add(user); if (users.size() >= maxResults) break; } return users; }

@Override public List<UserModel> searchForUser(String search, RealmModel realm) { return searchForUser(search, realm, 0, Integer.MAX_VALUE); }

@Override public List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { List<UserModel> users = new LinkedList<>(); int i = 0; for (Object obj : properties.keySet()) { String username = (String)obj; if (!username.contains(search)) continue; if (i++ < firstResult) continue; UserModel user = getUserByUsername(username, realm); users.add(user); if (users.size() >= maxResults) break; } return users; }

@Override public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm) { return searchForUser(params, realm, 0, Integer.MAX_VALUE); }

@Override public List<UserModel> searchForUser(Map<String, String> params, RealmModel realm, int firstResult, int maxResults) { // only support searching by username String usernameSearchString = params.get("username"); if (usernameSearchString == null) return Collections.EMPTY_LIST; return searchForUser(usernameSearchString, realm, firstResult, maxResults); }

第第7章章 ユーザーストレージユーザーストレージ SPI

43

Page 48: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Map パラメーターを取る searchForUser() メソッドは、姓名、ユーザー名、およびメールに基づいてユーザーを検索できます。ユーザー名のみを保存するため、ユーザー名を基に検索します。これについては、searchForUser() を委譲します。

PropertyFileUserStorageProvider

グループや属性は保存されないため、他のメソッドは空のリストを返します。

7.8. 外部ストレージの拡張

PropertyFileUserStorageProvider 例は実際に制限されています。プロパティーファイルに保存されたユーザーでログインできますが、他の多くの操作を行うことはできません。このプロバイダーによってロードされたユーザーに特定のアプリケーションへの完全アクセスに特別なロールまたはグループマッピングが必要な場合は、これらのユーザーに追加のロールマッピングを追加する方法はありません。email、first、および last などの重要な属性を変更したり追加したりすることはできません。

このような状況では、Red Hat Single Sign-On では、追加情報を Red Hat Single Sign-On のデータベースに保存して、外部ストアを拡張できます。これは フェデレーション (連合型)ユーザーストレージと呼ばれ、org.keycloak.storage.federated.UserFederatedStorageProvider 内でカプセル化されます。

UserFederatedStorageProvider

UserFederatedStorageProvider インスタンスは、KeycloakSession.userFederatedStorage() メソッドで利用できます。これには、属性、グループおよびロールマッピング、異なるクレデンシャルタイプ、および必要なアクションを保存するためのさまざまなメソッドがあります。外部ストアの

@Override public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { return Collections.EMPTY_LIST; }

@Override public List<UserModel> getGroupMembers(RealmModel realm, GroupModel group) { return Collections.EMPTY_LIST; }

@Override public List<UserModel> searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { return Collections.EMPTY_LIST; }

package org.keycloak.storage.federated;

public interface UserFederatedStorageProvider extends Provider {

Set<GroupModel> getGroups(RealmModel realm, String userId); void joinGroup(RealmModel realm, String userId, GroupModel group); void leaveGroup(RealmModel realm, String userId, GroupModel group); List<String> getMembership(RealmModel realm, GroupModel group, int firstResult, int max);

...

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

44

Page 49: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Datamodel が完全な Red Hat Single Sign-On 機能セットをサポートしない場合、このサービスはギャップを埋めることができます。

Red Hat Single Sign-On にはヘルパークラス org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage が同梱されています。このクラスは、ユーザー名の get/set を除くすべての UserModel メソッドが、ユーザーがフェデレーションされたストレージに委譲します。外部ストレージ表現に委譲するために上書きする必要のあるメソッドを上書きします。このクラスの Javadoc を読み取ることが強く推奨されます。これは、上書きする可能性のあるサイズの小さいため、このクラスの Java を理解することが強く推奨されます。具体的には、グループメンバーシップとロールマッピングです。

7.8.1. 拡張された例

PropertyFileUserStorageProvider の例では、AbstractUserAdapterFederatedStorage を使用するためにプロバイダーに簡単な変更が必要になります。

PropertyFileUserStorageProvider

代わりに、AbstractUserAdapterFederatedStorage の匿名クラス実装を定義します。setUsername()メソッドはプロパティーファイルを変更し、これを保存します。

7.9. 実装ストラテジーのインポート

ユーザーストレージプロバイダーを実装する場合、別のストラテジーを取ることができます。ユーザーフェデレーションストレージを使用する代わりに、Red Hat Single Sign-On の組み込みユーザーデータベースでローカルにユーザーを作成し、外部ストアからこのローカルコピーに属性をコピーすることができます。この方法には、多くの利点があります。

Red Hat Single Sign-On は基本的に外部ストアの永続ユーザーキャッシュになります。ユーザーをインポートしたら、外部ストアに到達できなくなります。そのため、ユーザーは外部ストアから読み込まれます。

公式のユーザーストアとして Red Hat Single Sign-On に移動し、古い外部ストアを非推奨にした場合に、アプリケーションを移行して Red Hat Single Sign-On を使用できます。すべてのアプリケーションが移行されたら、インポートされたユーザーのリンクを解除し、古いレガシー外部ストアをリタイアします。

protected UserModel createAdapter(RealmModel realm, String username) { return new AbstractUserAdapterFederatedStorage(session, realm, model) { @Override public String getUsername() { return username; }

@Override public void setUsername(String username) { String pw = (String)properties.remove(username); if (pw != null) { properties.put(username, pw); save(); } } }; }

第第7章章 ユーザーストレージユーザーストレージ SPI

45

Page 50: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

インポートストラテジーの使用には明確な欠点があります。

最初にユーザーを検索する場合は、Red Hat Single Sign-On データベースへの複数の更新が必要になります。これは、負荷がかかるとパフォーマンスが大幅に低下する可能性があり、RedHat Single Sign-On データベースに多くの制約があります。ユーザーフェデレーションストレージアプローチでは、必要に応じて追加のデータのみが保存され、外部ストアの機能によっては使用できない場合があります。

インポートのアプローチでは、ローカルの Red Hat Single Sign-On ストレージと外部ストレージを同期させる必要があります。User Storage SPI には、同期をサポートするように実装できるケイパビリティーインターフェースがありますが、これはすぐに、悪影響を与える可能性があります。

インポートストラテジーを実装するには、ユーザーがローカルにインポートされたかどうか最初にチェックします。ユーザーがローカルユーザーを返す場合は、ローカルユーザーをローカルに作成し、外部ストアからデータをインポートします。また、ほとんどの変更が自動的に同期されるように、ローカルユーザーをプロキシーすることもできます。

これは少し複雑になりますが、PropertyFileUserStorageProvider を拡張してこのアプローチを取ることができます。最初に createAdapter() メソッドを修正します。

PropertyFileUserStorageProvider

この方法では、KeycloakSession.userLocalStorage() メソッドを呼び出して、ローカルの Red HatSingle Sign-On ユーザーストレージへの参照を取得します。ユーザーがローカルに保存されているかどうかを確認し、ローカルに追加するかどうかを確認します。ローカルユーザーの id を設定しないでください。Red Hat Single Sign-On が id を自動的に生成します。また、UserModel.setFederationLink()を呼び出して、プロバイダーの ComponentModel に ID を渡すことにも注意してください。これにより、プロバイダーとインポートされたユーザー間のリンクが設定されます。

注記注記

ユーザーストレージプロバイダーが削除されると、インポートしたユーザーも削除されます。これは、UserModel.setFederationLink() を呼び出す目的の 1 つです。

また、ローカルユーザーがリンクしている場合は、CredentialInputValidator インターフェースおよび

protected UserModel createAdapter(RealmModel realm, String username) { UserModel local = session.userLocalStorage().getUserByUsername(username, realm); if (local == null) { local = session.userLocalStorage().addUser(realm, username); local.setFederationLink(model.getId()); } return new UserModelDelegate(local) { @Override public void setUsername(String username) { String pw = (String)properties.remove(username); if (pw != null) { properties.put(username, pw); save(); } super.setUsername(username); } }; }

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

46

Page 51: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

CredentialInputUpdater インターフェースから実装するメソッドのために、ストレージプロバイダーが依然として委譲される点に留意してください。検証または更新から false を返すと、Red Hat SingleSign-On はローカルストレージを使用して検証または更新できるかどうかを確認できます。

また、org.keycloak.models.utils.UserModelDelegate クラスを使用してローカルユーザーをプロキシー処理している点に注意してください。このクラスは UserModel の実装です。すべてのメソッドは、それがインスタンス化された UserModel に委譲するだけです。プロパティーファイルと自動的に同期するため、この委譲クラスの setUsername() メソッドが上書きされます。プロバイダーの場合、これを使用して、ローカル UserModel 上の他のメソッドを 傍受傍受 して、外部ストアと同期できます。たとえば、メソッドを取得すると、ローカルストアが同期されていることを確認できます。設定したメソッドは、外部ストアをローカルストアと同期させます。getId() メソッドは、ユーザーをローカルで作成したときに自動生成された id を常に返す必要がある点に留意してください。他のインポートの例のように、フェデレーションされた ID が返されないでください。

注記注記

プロバイダーが UserRegistrationProvider インターフェースを実装している場合、removeUser() メソッドはローカルストレージからユーザーを削除する必要はありません。ランタイムはこの操作を自動的に実行します。また、ローカルストレージから削除される前に removeUser() が呼び出されることに注意してください。

7.9.1. ImportedUserValidation インターフェース

本章で前述したことを念頭に置いても、ユーザーのクエリーの仕組みについて説明しています。ローカルストレージが最初にクエリーされ、ユーザーが見つかるとクエリーは終了します。これは、上記の実装で問題になります。ローカル UserModel をプロキシーして、ユーザー名の同期を維持します。UserStorage SPI には、リンクされたローカルユーザーがローカルデータベースから読み込まれるたびにコールバックがあります。

リンクされたローカルユーザーが読み込まれるたびに、ユーザーストレージプロバイダークラスがこのインターフェースを実装している場合は、validate() メソッドが呼び出されます。ここでは、パラメーターとして渡されるローカルユーザーをプロキシーして返すことができます。その新しい UserModelが使用されます。また、オプションでチェックを実行して、ユーザーが外部ストアに存在するかどうかを確認することもできます。validate() が Null を返すと、ローカルユーザーがこのデータベースから削除されます。

7.9.2. ImportSynchronization インターフェース

インポートストラテジーを使用すると、ローカルユーザーのコピーで外部ストレージと同期しなくなることが確認できます。たとえば、ユーザーが外部ストアから削除されています。ユーザーストレージSPI には、これに対応するために実装できる追加のインターフェース(org.keycloak.storage.user.ImportSynchronization) があります。

package org.keycloak.storage.user;public interface ImportedUserValidation { /** * If this method returns null, then the user in local storage will be removed * * @param realm * @param user * @return null if user no longer valid */ UserModel validate(RealmModel realm, UserModel user);}

第第7章章 ユーザーストレージユーザーストレージ SPI

47

Page 52: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

このインターフェースはプロバイダーファクトリーによって実装されます。このインターフェースがプロバイダーファクトリーによって実装されると、プロバイダーの管理コンソール管理ページに追加のオプションが表示されます。ボタンをクリックすると、同期を手動で強制できます。これにより、ImportSynchronization.sync() メソッドが呼び出されます。また、同期を自動的にスケジュールできるその他の設定オプションも表示されます。自動同期は syncSince() メソッドを呼び出します。

7.10. ユーザーキャッシュ

ユーザーオブジェクトが ID、ユーザー名、またはメールクエリーによってロードされる場合、これはキャッシュされます。ユーザーオブジェクトがキャッシュされている場合、これは UserModel インターフェース全体を繰り返し処理し、この情報をローカルのインメモリーのみのキャッシュにプルします。クラスターでは、このキャッシュはローカルですが、無効化キャッシュになります。ユーザーオブジェクトが変更されると、エビクトされます。このエビクションイベントはクラスター全体に伝播され、他のノードのユーザーキャッシュも無効にされます。

7.10.1. ユーザーキャッシュの管理

KeycloakSession.userCache() を呼び出してユーザーキャッシュにアクセスできます。

package org.keycloak.storage.user;

public interface ImportSynchronization { SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model); SynchronizationResult syncSince(Date lastSync, KeycloakSessionFactory sessionFactory, String realmId, UserStorageProviderModel model);}

/** * All these methods effect an entire cluster of Keycloak instances. * * @author <a href="mailto:[email protected]">Bill Burke</a> * @version $Revision: 1 $ */public interface UserCache extends UserProvider { /** * Evict user from cache. * * @param user */ void evict(RealmModel realm, UserModel user);

/** * Evict users of a specific realm * * @param realm */ void evict(RealmModel realm);

/** * Clear cache entirely. * */ void clear();}

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

48

Page 53: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

特定のユーザー、特定のレルムに含まれるユーザー、またはキャッシュ全体をエビクトする方法があります。

7.10.2. OnUserCache Callback インターフェース

プロバイダーの実装に固有の追加情報をキャッシュしたい場合があります。ユーザーストレージ SPI には、ユーザーがキャッシュされるたびにのコールバック (org.keycloak.models.cache.OnUserCache)が設定されます。

プロバイダークラスはこのコールバックが必要な場合は、このインターフェースを実装する必要があります。UserModel パラメーターは、プロバイダーによって返される UserModel インスタンスです。CachedUserModel は、拡張された UserModel インターフェースです。これは、ローカルストレージでローカルにキャッシュされるインスタンスです。

CachedUserModel インターフェースを使用すると、ユーザーをキャッシュから強制的に削除 (エビクト) し、プロバイダーの UserModel インスタンスを取得できます。getCachedWith() メソッドは、ユーザーに関連する追加情報をキャッシュできるマップを返します。たとえば、認証情報は

public interface OnUserCache { void onCache(RealmModel realm, CachedUserModel user, UserModel delegate);}

public interface CachedUserModel extends UserModel {

/** * Invalidates the cache for this user and returns a delegate that represents the actual data provider * * @return */ UserModel getDelegateForUpdate();

boolean isMarkedForEviction();

/** * Invalidate the cache for this model * */ void invalidate();

/** * When was the model was loaded from database. * * @return */ long getCacheTimestamp();

/** * Returns a map that contains custom things that are cached along with this model. You can write to this map. * * @return */ ConcurrentHashMap getCachedWith();}

第第7章章 ユーザーストレージユーザーストレージ SPI

49

Page 54: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

UserModel インターフェースの一部ではありません。認証情報をメモリーにキャッシュする必要がある場合は OnUserCache を実装し、getCachedWith() メソッドを使用してユーザーの認証情報をキャッシュします。

7.10.3. キャッシュポリシー

ユーザーストレージプロバイダーの管理コンソール管理ページで、一意のキャッシュポリシーを指定できます。

7.11. JAVA EE の活用

META-INF/services ファイルを適切に設定してプロバイダーを参照するようにユーザーストレージプロバイダーを Java EE コンポーネントにパッケージ化できます。たとえば、プロバイダーがサードパーティーライブラリーを使用する必要がある場合は、EAR 内でプロバイダーをパッケージ化し、これらのサードパーティーライブラリーを EAR の lib/ ディレクトリーに保存できます。また、プロバイダーJAR は EJB、WARS、および EAR が JBoss EAP 環境で使用できる jboss-deployment-structure.xmlファイルを利用できます。このファイルの詳細は、JBoss EAP のドキュメントを参照してください。これにより、他の粒度の細かいアクション間で外部の依存関係をプルできます。

プロバイダーの実装は、プレーンな java オブジェクトである必要があります。ただし、現時点では、UserStorageProvider クラスを Stateful EJB として実装することもできます。これは、JPA を使用してリレーショナルストアに接続する場合に特に便利です。これは、以下の方法で行います。

@Stateful@Local(EjbExampleUserStorageProvider.class)public class EjbExampleUserStorageProvider implements UserStorageProvider, UserLookupProvider, UserRegistrationProvider, UserQueryProvider, CredentialInputUpdater, CredentialInputValidator, OnUserCache{ @PersistenceContext protected EntityManager em;

protected ComponentModel model; protected KeycloakSession session;

public void setModel(ComponentModel model) { this.model = model; }

public void setSession(KeycloakSession session) { this.session = session; }

@Remove @Override public void close() { }...}

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

50

Page 55: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

@Local アノテーションを定義し、そこにプロバイダークラスを指定する必要があります。これを実行しない場合、EJB はユーザーを正しくプロキシーせず、プロバイダーが機能しなくなります。

@Remove アノテーションをプロバイダーの close() メソッドに配置する必要があります。そうでない場合は、ステートフル Bean はクリーンアップされず、最終的にエラーメッセージが表示される可能性があります。

UserStorageProvider の実装は、プレーンな Java オブジェクトである必要があります。ファクトリークラスは create() メソッドで Stateful EJB の JNDI ルックアップを実行します。

また、この例では、JPA デプロイメントをプロバイダーと同じ JAR で定義していることを前提としています。これは、persistence.xml ファイルと JPA @Entity クラスを意味します。

警告警告

JPA を使用する場合、追加のデータソースは XA データソースである必要があります。Red Hat Single Sign-On データソースは XA データソースではありません。同じトランザクションで 2 つ以上の非 XA データソースと対話する場合、サーバーはエラーメッセージを返します。単一のトランザクションで許可される非 XA リソースは 1 つだけです。XA データソースのデプロイに関する詳細は、JBoss EAP のマニュアルを参照してください。

CDI はサポートされていません。

7.12. REST 管理 API

管理者 REST API を使用して、ユーザーストレージプロバイダーのデプロイメントを作成、削除、および更新できます。User Storage SPI は汎用コンポーネントインターフェース上に構築されるため、その汎用 API を使用してプロバイダーを管理します。

REST Component API は、レルムの管理リソースの下にあります。

public class EjbExampleUserStorageProviderFactory implements UserStorageProviderFactory<EjbExampleUserStorageProvider> {

@Override public EjbExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) { try { InitialContext ctx = new InitialContext(); EjbExampleUserStorageProvider provider = (EjbExampleUserStorageProvider)ctx.lookup( "java:global/user-storage-jpa-example/" + EjbExampleUserStorageProvider.class.getSimpleName()); provider.setModel(model); provider.setSession(session); return provider; } catch (Exception e) { throw new RuntimeException(e); } }

第第7章章 ユーザーストレージユーザーストレージ SPI

51

Page 56: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

/admin/realms/{realm-name}/components

Java クライアントとのこの REST API との対話のみを示します。この API から curl からこの実行方法を抽出できます。

ユーザーストレージプロバイダーを作成するには、プロバイダー ID、文字列 org.keycloak.storage.UserStorageProvider のプロバイダータイプ、および設定を指定する必要があります。

public interface ComponentsResource { @GET @Produces(MediaType.APPLICATION_JSON) public List<ComponentRepresentation> query();

@GET @Produces(MediaType.APPLICATION_JSON) public List<ComponentRepresentation> query(@QueryParam("parent") String parent);

@GET @Produces(MediaType.APPLICATION_JSON) public List<ComponentRepresentation> query(@QueryParam("parent") String parent, @QueryParam("type") String type);

@GET @Produces(MediaType.APPLICATION_JSON) public List<ComponentRepresentation> query(@QueryParam("parent") String parent, @QueryParam("type") String type, @QueryParam("name") String name);

@POST @Consumes(MediaType.APPLICATION_JSON) Response add(ComponentRepresentation rep);

@Path("{id}") ComponentResource component(@PathParam("id") String id);}

public interface ComponentResource { @GET public ComponentRepresentation toRepresentation();

@PUT @Consumes(MediaType.APPLICATION_JSON) public void update(ComponentRepresentation rep);

@DELETE public void remove();}

import org.keycloak.admin.client.Keycloak;import org.keycloak.representations.idm.RealmRepresentation;...

Keycloak keycloak = Keycloak.getInstance( "http://localhost:8080/auth",

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

52

Page 57: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

7.13. 以前のユーザーフェデレーション SPI からの移行

注記注記

本章では、以前の(および削除済みの)ユーザーフェデレーション SPI を使用してプロバイダーを実装している場合にのみ該当します。

Keycloak バージョン 2.4.0 以前では、ユーザーフェデレーション SPI がありました。この以前の SPI も利用できますが、Red Hat Single Sign-On バージョン 7.0 も利用可能です。この以前のユーザーフェデレーション SPI は、Keycloak バージョン 2.5.0 および Red Hat Single Sign-On バージョン 7.1 から削除されました。ただし、この前の SPI でプロバイダーを作成した場合は、本章ではそのプロバイダーをポートに使用できるストラテジーについて説明します。

7.13.1. インポートとインポートなし

以前のユーザーフェデレーション SPI では、Red Hat Single Sign-On のデータベースにユーザーのローカルコピーを作成し、外部ストアからローカルコピーに情報をインポートする必要がありました。ただし、これは不要になりました。以前のプロバイダーをそのままに移植できますが、インポート以外のストラテジーがより適しているかどうかを考慮する必要があります。

インポートストラテジーの利点:

"master", "admin", "password", "admin-cli");RealmResource realmResource = keycloak.realm("master");RealmRepresentation realm = realmResource.toRepresentation();

ComponentRepresentation component = new ComponentRepresentation();component.setName("home");component.setProviderId("readonly-property-file");component.setProviderType("org.keycloak.storage.UserStorageProvider");component.setParentId(realm.getId());component.setConfig(new MultivaluedHashMap());component.getConfig().putSingle("path", "~/users.properties");

realmResource.components().add(component);

// retrieve a component

List<ComponentRepresentation> components = realmResource.components().query(realm.getId(), "org.keycloak.storage.UserStorageProvider", "home");component = components.get(0);

// Update a component

component.getConfig().putSingle("path", "~/my-users.properties");realmResource.components().component(component.getId()).update(component);

// Remove a component

realmREsource.components().component(component.getId()).remove();

第第7章章 ユーザーストレージユーザーストレージ SPI

53

Page 58: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Red Hat Single Sign-On は基本的に外部ストアの永続ユーザーキャッシュになります。ユーザーをインポートしたら、外部ストアに到達しないため、そこから読み込むことができます。

正式なユーザーストアとして Red Hat Single Sign-On に移動し、以前の外部ストアを非推奨にした場合に、アプリケーションを移行して Red Hat Single Sign-On を使用できます。すべてのアプリケーションが移行されたら、インポートされたユーザーのリンクを解除し、以前のレガシー外部ストアをリタイアします。

インポートストラテジーの使用には明確な欠点があります。

最初にユーザーを検索する場合は、Red Hat Single Sign-On データベースへの複数の更新が必要になります。これは、負荷がかかるとパフォーマンスが大幅に低下する可能性があり、RedHat Single Sign-On データベースに多くの制約があります。ユーザーフェデレーションストレージアプローチでは、必要に応じて追加のデータのみが保存され、外部ストアの機能によっては使用できない可能性があります。

インポートのアプローチでは、ローカルの Red Hat Single Sign-On ストレージと外部ストレージを同期させる必要があります。User Storage SPI には、同期をサポートするように実装できるケイパビリティーインターフェースがありますが、これはすぐに、悪影響を与える可能性があります。

7.13.2. UserFederationProvider と UserStorageProvider

最初に、UserFederationProvider が完全なインターフェースであったことに留意してください。このインターフェースにすべてのメソッドを実装している。ただし、UserStorageProvider は、このインターフェースを、必要に応じて実装する複数の機能インターフェースに分割します。

UserFederationProvider.getUserByUsername() および getUserByEmail() には、新しい SPI に完全に同等のものが含まれます。この 2 つの間の違いは、インポート方法です。インポートストラテジーを続行する場合は、KeycloakSession.userStorage().addUser() を呼び出してユーザーをローカルに作成しなくなりました。代わりに KeycloakSession.userLocalStorage().addUser() を呼び出します。userStorage() メソッドがなくなりました。

UserFederationProvider.validateAndProxy() メソッドは任意の機能インターフェース ImportedUserValidation に移動しました。以前のプロバイダーをそのまま移植する場合は、このインターフェースを実装する必要があります。また、以前の SPI では、ローカルユーザーがキャッシュにある場合でも、ユーザーはアクセスするたびにこの方法が呼び出されていました。後続の SPI では、この方法はローカルユーザーがローカルストレージからロードされた場合にのみ呼び出されます。ローカルユーザーがキャッシュされていると、ImportedUserValidation.validate() メソッドは呼び出されません。

UserFederationProvider.isValid() メソッドは、後続の SPI に存在しなくなりました。

UserFederationProvider のメソッド synchronizeRegistrations()、registerUser()、および removeUser() が UserRegistrationProvider 機能インターフェースに移動しました。プロバイダーがユーザーの作成および削除をサポートしない場合は、この新しいインターフェースを実装する必要はありません。以前のプロバイダーが新規ユーザー登録のサポートを切り替える場合は、これが新しい SPIでサポートされ、プロバイダーがユーザーの追加をサポートしていない場合は UserRegistrationProvider.addUser() から Null を返します。

認証情報を中心とする以前の UserFederationProvider メソッドは、CredentialInputValidator インターフェースおよび CredentialInputUpdater インターフェースにカプセル化されるようになりました。また、認証情報の検証または更新をサポートしているかどうかに応じて実装することもできます。認証情報管理は、以前は UserModel メソッドに存在していました。これらは CredentialInputValidator インターフェースおよび CredentialInputUpdater インターフェースに移動されています。CredentialInputUpdater インターフェースを実装していない場合は、Red Hat Single

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

54

Page 59: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

Sign-On ストレージでプロバイダーが提供する認証情報をローカルで上書きできます。そのため、認証情報を読み取り専用にするには、CredentialInputUpdater.updateCredential() メソッドを実装し、ReadOnlyException を返します。

searchByAttributes()、getGroupMembers()などの UserFederationProvider クエリーメソッドは、任意のインターフェース UserQueryProvider でカプセル化されるようになりました。このインターフェースを実装しない場合、ユーザーは管理コンソールで表示できません。ただし、ログインは可能です。

7.13.3. UserFederationProviderFactory と UserStorageProviderFactory

以前の SPI の同期メソッドは、任意の ImportSynchronization インターフェース内でカプセル化されるようになりました。同期ロジックを実装している場合、新しい UserStorageProviderFactory が ImportSynchronization インターフェースを実装します。

7.13.4. 新規モデルへのアップグレード

User Storage SPI インスタンスは、異なるリレーショナルテーブルセットに保存されます。Red HatSingle Sign-On は、移行スクリプトを自動的に実行します。以前のユーザーフェデレーションプロバイダーがレルムにデプロイされると、データの id を含む、後のストレージモデルにそのまま変換されます。この移行は、以前のユーザーフェデレーションプロバイダーと同じプロバイダー ID(「 ldap」、Kerberos)でユーザーストレージプロバイダーが存在する場合にのみ発生します。

そのため、これを理解するには、さまざまなアプローチがあります。

1. 以前の Red Hat Single Sign-On デプロイメントで以前のプロバイダーを削除できます。これにより、インポートしたすべてのユーザーのローカルリンクされたコピーが削除されます。次に、Red Hat Single Sign-On をアップグレードすると、レルムの新しいプロバイダーをデプロイして設定します。

2. 2 つ目のオプションは、UserStorageProviderFactory.getId() という同じプロバイダー ID を持つように新規プロバイダーを作成します。このプロバイダーは、Red Hat Single Sign-On の新規インストールの standalone/deployments/ ディレクトリーにあることを確認してください。サーバーを起動し、組み込みの移行スクリプトを以前のデータモデルから後続データモデルに変換します。この場合、以前にリンクされたインポートしたユーザーはすべて有効で、同じものになります。

インポートストラテジーを修復し、ユーザーストレージプロバイダーを書き換えた場合、Red HatSingle Sign-On をアップグレードする前に以前のプロバイダーを削除することが推奨されます。これにより、インポートしたユーザーのリンク済みローカルインポートコピーが削除されます。

第第7章章 ユーザーストレージユーザーストレージ SPI

55

Page 60: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第8章 VAULT SPI

8.1. VAULT プロバイダー

org.keycloak.vault パッケージの Vault SPI を使用して、Red Hat Single Sign-On のカスタム拡張を作成し、任意の Vault 実装に接続することができます。

組み込みの files-plaintext プロバイダーは、この SPI の実装例です。一般的には、以下のルールが適用されます。

シークレットがレルム全体でリークしないようにするには、レルムが取得できるシークレットを分離または制限できます。このような場合、プロバイダーはレルム名をプレフィックスしてシークレットを検索する際にレルム名を考慮する必要があります。たとえば、式 ${vault.key}は、レルム A またはレルム B で使用されるかに応じて、一般的に異なるエントリー名に評価されます。レルムを区別するには、レルムを、KeycloakSession パラメーターから利用できる VaultProviderFactory.create() メソッドから、作成された VaultProvider インスタンスに渡す必要があります。

Vault プロバイダーは、指定したシークレット名に VaultRawSecret 返す単一のメソッド obtainSecret を実装します。そのクラスは、シークレットの表現を byte[] または ByteBufferのいずれかに保持し、必要に応じて 2 つの間で変換することが期待されます。このバッファーは、以下の説明にあるように、使用後に破棄されることに注意してください。

カスタムプロバイダーをパッケージ 化およびデプロイする方法の詳細は、「サービス プロバイダーのインターフェース」の章を参照してください。

8.2. VAULT からの値の使用

vault には機密データが含まれ、Red Hat Single Sign-On は適切にシークレットを処理します。シークレットにアクセスする場合、シークレットは vault から取得され、必要な時間のみ JVM メモリーに保持されます。その後、JVM メモリーからそのコンテンツの破棄をすべて試行します。これは、以下のように try-with-resources ステートメント内でのみ Vault シークレットを使用して実行されます。

この例では、シークレットにアクセスするためのエントリーポイントとして KeycloakSession.vault()を使用します。VaultProvider.obtainSecret メソッドを直接使用することも可能になります。ただし、vault() メソッドは、オリジナルの解釈されていない値 (vault().getRawSecret() メソッド経由) を取得するのに加え、(通常はバイト配列である) 未編集のシークレットを文字アレイ(vault().getCharSecret() 経由) または String (via vault().getStringSecret() 経由) として解釈できるという利点があります。

String オブジェクトは変更できないため、ランダムなガベージでコンテンツを破棄できないことに注意してください。デフォルトの VaultStringSecret の実装では、String の内面化を防ぐための対策が取られていますが、String オブジェクトに格納されているシークレットは、少なくとも次の GC ラウンドまで存続します。そのため、プレーンなバイト配列、文字配列、およびバッファーを使用することが推奨されます。

char[] c; try (VaultCharSecret cSecret = session.vault().getCharSecret(SECRET_NAME)) { // ... use cSecret c = cSecret.getAsArray().orElse(null); // if c != null, it now contains password }

// if c != null, it now contains garbage

Red Hat Single Sign-On 7.4 サーバー開発者ガイドサーバー開発者ガイド

56

Page 61: Red Hat Single Sign-On 7.4 サーバー開発者ガイド...HTML テンプレート (Freemarker テンプレート) イメージ メッセージバンドル スタイルシート スクリプト

第第8章章 VAULT SPI

57