Keycloak 開発入門 

初版: 2018/8/1

著者: 茂木 昂士, 株式会社日立製作所

# はじめに

Keycloak([https://www.keycloak.org/](https://www.keycloak.org/))はオープンソースのアイデンティティ・アクセス管理ソフトウェアです。コミュニティベースで開発が行われているため誰でもKeycloakの開発に貢献することができますが、開発方法やコミュニティのルールが分からないとなかなか手を出せないと思います。そこで今回は、私が行ったパッチ開発([#5163](https://github.com/keycloak/keycloak/pull/5163))の内容を通して、得られた知見などを共有していきたいと思います。

なおこれらの情報は、Keycloakのリポジトリにある[README.md](https://github.com/keycloak/keycloak/blob/master/README.md)や[HackingOnKeycloak.md](https://github.com/keycloak/keycloak/blob/master/misc/HackingOnKeycloak.md), [HOW-TO-RUN.md](https://github.com/keycloak/keycloak/blob/master/testsuite/integration-arquillian/HOW-TO-RUN.md)といった内容をもとにしています。

この記事は[OSSセキュリティ技術の会　第三回勉強会 ](https://secureoss-sig.connpass.com/event/90917/)で発表した内容に加筆したものです。


# 目次

- [開発した内容](#開発した内容)
- [機能の提案、バグの報告](#機能の提案バグの報告)
- [実装のための準備](#実装のための準備)
    - [ビルド方法](#ビルド方法)
    - [IDEへのインポート](#ideへのインポート)
    - [IDE(Eclipse) でのデバッグ方法](#ideeclipse-でのデバッグ方法)
- [コードの修正](#コードの修正)
    - [修正の内容](#修正の内容)
    - [Identity Provider関連ファイルの場所](#identity-provider関連ファイルの場所)
    - [GUIの変更](#guiの変更)
    - [DBスキーマの修正](#dbスキーマの修正)
    - [Testの実装](#testの実装)
- [GitHubへの投稿](#githubへの投稿)
- [まとめ](#まとめ)

# 開発した内容

KeycloakにはIdentity Brokeringという機能があり、外部のOpenID Connect Providerで認証した結果をKeycloakで利用することができます。KeycloakにはGoogleやFacebookといった一般的なプロバイダーに接続するための設定も準備されています。

![brokering.png](https://qiita-image-store.s3.amazonaws.com/0/273221/a53d1a96-565d-da55-3326-41eb36066650.png)

ユーザの認証処理は画面も含めて自前で行いたいという要件があったため、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](https://lists.jboss.org/mailman/listinfo/keycloak-dev))
- JIRA ([https://issues.jboss.org/projects/KEYCLOAK/issues](https://issues.jboss.org/projects/KEYCLOAK/issues))
- GitHub ([https://github.com/keycloak/keycloak](https://github.com/keycloak/keycloak/pulls))

バグなどを見つけた場合、まずはのJIRAに詳細を記載したチケットを作成し、そのチケットのIDをタイトルに入れたPull Requestを作成します。

ここからは、実際にパッチ開発をした知見をもとにして、パッチ開発環境の準備や、コード変更の方法、テスト実行の方法などを紹介していきます。

# 実装のための準備

まずはKeycloakのRepositoryをForkし、そのRepositoryをクローンしてきます。

```console
$ git clone https://github.com/<username>/keycloak
```

このクローンしてきたディレクトリで準備をしていきます。

## ビルド方法

ソースコードを持ってきたらまずはビルドを行います。ビルドのためにはJDKやMavenが必要になるので、それぞれセットアップを行ってください。

ソースコードのビルドは下記のコマンドで行います。

```console
$ 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`というクラスを実行しているので、このクラスを指定してデバッグを実行します。設定は下図のようになります。

![debug_config.png](https://qiita-image-store.s3.amazonaws.com/0/273221/273991a6-bdf1-bb54-097d-764b81de7106.png)

これでEclipseを使ってKeycloakのデバッグができるようになります。

さらにデータベースの設定を行います。デフォルトの設定ではH2DBのInMemoryDBが利用されますが、このままでは起動しなおすたびに設定が消えてしまいます。何回も設定しなおすのは面倒なのでファイルに保存する設定にします。起動時の引数として`-Dkeycloak.connectionsJpa.url=jdbc:h2:./keycloak;AUTO_SERVER=TRUE`を指定します。Eclipseでの設定方法は下図の通りです。

![persist_settings.png](https://qiita-image-store.s3.amazonaws.com/0/273221/a5a9b089-4c49-9af3-fe04-2ac772f8c3ed.png)


ここまででデバッグも含めた開発の準備ができたので、コードの修正を行っていきます。

# コードの修正

ここからは、「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の設定項目に転送するパラメータを設定する項目を追加しました。

![label.png](https://qiita-image-store.s3.amazonaws.com/0/273221/73e4175a-3a01-f069-bfe4-db29a67ae1c6.png)

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](http://arquillian.org/)というテストフレームワークを利用してインテグレーションテストを行っています。Identity Brokeringのテストは、`integration-arquiilian-tests-base`(`testsuite\integration-arquillian\tests\base`)というプロジェクトに含まれています。

外部Identity Providerのテストをどのようにやるかというと、図のように`consumer`と`provider`という二つのRealmを利用することで対応していました。

![provider-consumer.png](https://qiita-image-store.s3.amazonaws.com/0/273221/260dcb56-2317-0275-bbc3-d0b32d772ae4.png)


既存のテストを見てみると、クエリパラメータをチェックしているテストがあったので、それを流用してテストを記載しました。

実装したテストを実行するためには、下記のコマンドを実行します。

```console
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test
```

これで基本機能のテストが実行されますが、一つ一つのテストに時間がかかり、テスト数もあるのですべてのテストが終了するまでにかなりの時間がかかります。特定のテストを実行するためには、`-Dtest`でクラス名を指定します。

```console
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test -Dtest=org.keycloak.testsuite.broker.KcOidcBrokerParameterForwardTest
```

また、`-Dtest`では正規表現を使ったテストの指定も可能です。例えば下記コマンドでは、Identity Broker関連のテストのみを実行することができます。

```console
$ mvn -f testsuite/integration-arquillian/tests/base/pom.xml test -Dtest=org.keycloak.testsuite.broker.Kc*
```

今回のBaseテストは基本的な機能のテストのみでしたが、アダプターのテストやUIテスト等様々なテストが実装されています。詳細については、`keycloak-testsuite`プロジェクトにある[HOW-TO-RUN.md](https://github.com/keycloak/keycloak/blob/master/testsuite/integration-arquillian/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](https://github.com/keycloak/keycloak/pull/5163)

# まとめ

Keycloakはコミュニティの活動が活発で、新しい機能が次々と開発されていて、コミュニティ外部からの改善も積極的に取り入れています。

日本からもどんどんパッチを出して、よいOSSにしていきましょう。