初版: 2018/8/1
著者: 茂木 昂士, 株式会社日立製作所
はじめに
Keycloak(https://www.keycloak.org/)はオープンソースのアイデンティティ・アクセス管理ソフトウェアです。コミュニティベースで開発が行われているため誰でもKeycloakの開発に貢献することができますが、開発方法やコミュニティのルールが分からないとなかなか手を出せないと思います。そこで今回は、私が行ったパッチ開発(#5163)の内容を通して、得られた知見などを共有していきたいと思います。
なおこれらの情報は、KeycloakのリポジトリにあるREADME.mdやHackingOnKeycloak.md, HOW-TO-RUN.mdといった内容をもとにしています。
この記事はOSSセキュリティ技術の会 第三回勉強会 で発表した内容に加筆したものです。
目次
開発した内容
KeycloakにはIdentity Brokeringという機能があり、外部のOpenID Connect Providerで認証した結果をKeycloakで利用することができます。KeycloakにはGoogleやFacebookといった一般的なプロバイダーに接続するための設定も準備されています。
ユーザの認証処理は画面も含めて自前で行いたいという要件があったため、Identity Brokeringを利用して独自の認証アプリケーションにリダイレクトする設定にしました。しかしこの認証アプリケーションでは、クライアントからOpenID Connectの仕様外のパラメータを送信してもらう必要があったのですが、KeycloakのIdentity Brokeringでは独自に指定したいQueryParameterが外部Identity Provider(=認証画面)に転送されませんでした。
これを解決するため、Identity Provider SPI(Identity Brokeringを拡張するためのインターフェース)を使って、パラメータを転送するIdentity Providerを独自に実装しました。 しかし、OpenID Connect仕様外のパラメータを利用するユースケースはほかにも考えられると感じ、一般的な機能としてコミュニティにコントリビューションすることにしました。
機能の提案、バグの報告
Keycloakでは新機能の提案やバグの報告に下記のツールを利用しています。
- Developer Maling List (https://lists.jboss.org/mailman/listinfo/keycloak-dev)
- JIRA (https://issues.jboss.org/projects/KEYCLOAK/issues)
- GitHub (https://github.com/keycloak/keycloak)
バグなどを見つけた場合、まずはのJIRAに詳細を記載したチケットを作成し、そのチケットのIDをタイトルに入れたPull Requestを作成します。
ここからは、実際にパッチ開発をした知見をもとにして、パッチ開発環境の準備や、コード変更の方法、テスト実行の方法などを紹介していきます。
実装のための準備
まずはKeycloakのRepositoryをForkし、そのRepositoryをクローンしてきます。
$ git clone https://github.com/<username>/keycloak
このクローンしてきたディレクトリで準備をしていきます。
ビルド方法
ソースコードを持ってきたらまずはビルドを行います。ビルドのためにはJDKやMavenが必要になるので、それぞれセットアップを行ってください。
ソースコードのビルドは下記のコマンドで行います。
$ mvn install -DskipTests=true
$ cd distribution
$ mvn install
テストは環境によって失敗することがあるので、ビルド時はテストを飛ばし修正した後に関連するテストだけを走らせるのが良いと思います。
上記コマンドを実行すると、distribution/server-dist/target
以下にビルド物(.tar.gz, .zip)が出力されます。これらを展開し、bin/standalone.sh
を実行することでKeycloakを実行することもできます。
IDEへのインポート
次にIDEで開発を行うための準備を行っていきます。今回はIDEとしてEclipse (Oxygen) を利用しました。Keycloakリポジトリの直下にあるpom.xml
ファイル(keycloak-parent
というプロジェクトになる)を指定してMavenプロジェクトをEclipseにインポートします。
プロジェクトが大量に(300以上)あるため、インポートには多少の時間がかかります。
IDE(Eclipse) でのデバッグ方法
開発中のKeycloakを起動するためには、README.md
に記載してある通りmvn -f testsuite/utils/pom.xml exec:java -Pkeycloak-server
を実行します。Keycloakサーバ起動後はlocalhost:8081/auth/
からKeycloakのGUIにアクセスできます。
上記の通りコマンドラインからMavenで実行することは可能ですが、せっかくEclipseを利用しているのでそちらでデバッグを行えるようにします。上記コマンドで指定しているpom.xml
を見ると、keycloak-testsuite-utils
プロジェクトのorg.keycloak.testsuite.KeycloakServer
というクラスを実行しているので、このクラスを指定してデバッグを実行します。設定は下図のようになります。
これでEclipseを使ってKeycloakのデバッグができるようになります。
さらにデータベースの設定を行います。デフォルトの設定ではH2DBのInMemoryDBが利用されますが、このままでは起動しなおすたびに設定が消えてしまいます。何回も設定しなおすのは面倒なのでファイルに保存する設定にします。起動時の引数として-Dkeycloak.connectionsJpa.url=jdbc:h2:./keycloak;AUTO_SERVER=TRUE
を指定します。Eclipseでの設定方法は下図の通りです。
ここまででデバッグも含めた開発の準備ができたので、コードの修正を行っていきます。
コードの修正
ここからは、「Identity Brokering機能でカスタムパラメータを外部のIdenity Providerに転送する」という機能を実装した内容をもとに、Keycloakの簡単なアーキテクチャ説明を行っていきます。
修正の内容
KeycloakのAuthorization Endpointにアクセスがあった際のパラメータ値は、仕様外のパラメータも含めてAuthorizationSessionというモデルに保存されていました。なので、外部Identity ProviderへのRedirectURLを作成する部分に、AuthorizationSessionから仕様外のパラメータを取得してURLに追加する修正を行いました。
また、すべてのパラメータを転送するのではなく、Identity Brokeringの設定で指定されたパラメータのみを転送するようにしました。
Identity Provider関連ファイルの場所
認証、認可といった基本的な機能や、Identity Brokeringの機能は keycloak-services
というプロジェクトに実装されています。このプロジェクトにはkeycloak-spi
, keycloak-spi-private
で定義されているSPIを実装しています。SPIを利用してプラグインを開発する場合と同様に、Provider
, ProviderFactory
の二つが対になって実装されています。
OpenID Connect Identity Providerはorg.keycloak.broker.oidc.OIDCIdentityProvider
というクラスに実装されています。外部Identity ProviderへのRedirect URLを作成しているのは、このクラスが継承しているorg.keycloak.broker.oidc.AbstractOauth2IdentityProvider
のcreateAuthorizationUrl
というメソッドだったので、このメソッドに修正を行いました。
GUIの変更
外部の特定のパラメータだけを転送するようにするため、Identity Providerの設定項目に転送するパラメータを設定する項目を追加しました。
KeycloakのGUIは AngularJS(1.6)で作られています。 keycloak-themes
というプロジェクトの下にtemplate
やJavascriptのコードが入っています。Idenity Providerの設定項目は、Formの内容をすべてAPIに送信する作りになっていたため、realm-identity-provider-oidc.html
というテンプレートにFormとTooltipを追加するだけで実装できました。
2018/07 現在、keycloak-themes
以下にkeycloak-preview
というフォルダが作られていました。KeycloakのAdmin ConsoleのGUIを新しく作っているらしく、軽く眺めてみたところAngular 5で作っている模様でした。レスポンシブ対応などのPull Requestを目にしたこともあるので期待して待ちたいと思います。
DBスキーマの修正
Identity Providerの設定項目を増やしたので、DBスキーマの変更が必要だと思っていました。しかし、設定項目はIDENTITY_PROVIDER_CONFIG
というテーブルにKey-Valueの形で登録されていたので、スキーマの変更は必要ありませんでした。コード上でもMap<String, String>
で管理されています。設定項目は変更が多いことを考えると、スキーマ変更の必要なく対応できるこの作りは合理的だと思いました。
今回はスキーマの変更が必要ありませんでしたが、もしDB関連の変更が必要な場合はkeycloak-model-jpa
プロジェクトにモデルがまとまっているので、それらを変更します。
Testの実装
KeycloakではArquiilianというテストフレームワークを利用してインテグレーションテストを行っています。Identity Brokeringのテストは、integration-arquiilian-tests-base
(testsuite\integration-arquillian\tests\base
)というプロジェクトに含まれています。
外部Identity Providerのテストをどのようにやるかというと、図のようにconsumer
とprovider
という二つのRealmを利用することで対応していました。
既存のテストを見てみると、クエリパラメータをチェックしているテストがあったので、それを流用してテストを記載しました。
実装したテストを実行するためには、下記のコマンドを実行します。
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test
これで基本機能のテストが実行されますが、一つ一つのテストに時間がかかり、テスト数もあるのですべてのテストが終了するまでにかなりの時間がかかります。特定のテストを実行するためには、-Dtest
でクラス名を指定します。
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test -Dtest=org.keycloak.testsuite.broker.KcOidcBrokerParameterForwardTest
また、-Dtest
では正規表現を使ったテストの指定も可能です。例えば下記コマンドでは、Identity Broker関連のテストのみを実行することができます。
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test -Dtest=org.keycloak.testsuite.broker.Kc*
今回のBaseテストは基本的な機能のテストのみでしたが、アダプターのテストやUIテスト等様々なテストが実装されています。詳細については、keycloak-testsuite
プロジェクトにあるHOW-TO-RUN.mdを参照してください。
ここまでで必要なコードの修正が終わりました。いよいよ修正をGitHubに投稿します。
GitHubへの投稿
修正したコードをGitHubにPushしてPull Requestを出すと、TravisCIでインテグレーションテストが実行されます。すべてのテストが完了するまで20分程度かかります。全Testがパスすればレビューの対象になります。
コードレビューはしっかりと見てもらえました(assertTrue/assertFalseよりassertThatのほうが見やすい、など)。最初はユニットテストも書いたのですが、インテグレーションテストですでに機能がカバーできていたため、「インテグレーションテストとユニットテストで内容が重複してるとメンテナンスコストが上がる」とコメントをもらい、不要との判断になりました。
インテグレーションテストがしっかりしているので、カバーできているならユニットテストは必要ないというポリシーのようです。
変更は最終的には1コミットにまとめてと言われたので、git rebase -i
でコミットをまとめて、git push -f
でPushしました。
マージされるまでに時間がかかりましたが、無事マージされました。https://github.com/keycloak/keycloak/pull/5163
まとめ
Keycloakはコミュニティの活動が活発で、新しい機能が次々と開発されていて、コミュニティ外部からの改善も積極的に取り入れています。
日本からもどんどんパッチを出して、よいOSSにしていきましょう。