midPoint の開発/検証環境を作る

# はじめに

[midPoint by OpenStandia Advent Calendar 2024](https://qiita.com/advent-calendar/2024/midpoint-by-openstandia) の4日目は、midPointの開発/検証環境を構築します。[2日目](https://qiita.com/wadahiro/items/7b68b087e6beab1374fd)・[3日目](https://qiita.com/wadahiro/items/df964d32e28f3e3427d8)では、Evolveum社が用意するデモ環境を利用していましたが、色々と制約がありますので、本格的に触ってみたい場合は専用の環境を構築するとよいでしょう。

midPointはインストール用に、ZIPまたはTAR.GZ形式で配布されていますが、コンテナイメージも配布されています。今回はコンテナイメージを使用し、Docker Composeを使って構築します。これには以下の理由があります。

* midPoint 4.4 からNative RepositoryというPostgreSQL特化の機能が追加され、開発・検証用途のmidPoint単体環境（h2データベース組み込み版）では機能的に不十分となってきている（例えば、シミュレーション機能はNative Repository専用でありPostgreSQLを使わないと動かないなど）
* midPoint 4.9 からh2データベースのサポートは削除された

よって2024年12月時点では、Docker Composeを使って環境を構築することをおすすめします。

:::note info
本記事ではDocker Composeが利用可能な環境を前提としています。利用されているOS環境に応じて、事前にセットアップを行ってください。詳細は、以下の公式ドキュメントなどを参照してください。

https://docs.docker.com/compose/install/
:::

# Docker Compose定義

（おそらくmidPoint 4.9のh2データベースのサポート削除に伴い）公式ドキュメントにDocker Composeの定義例が記載されています。

https://docs.evolveum.com/midpoint/install/containers/docker/

本記事でも、この定義を利用して構築していきます。この定義にはmidPoint本体だけでなく、PostgreSQLとそのデータベースを初期化するための仕掛けも含まれています。

:::note info
実際のmidPoint構築プロジェクトでも、Docker Composeを使って開発環境を準備するとよいでしょう。midPoint本体だけでなく、OpenLDAPやActive Directory（Samba4で代替）など、連携先システムも可能な限りDocker Composeで一括構築できるようにしておきます。そうすることで、プロジェクトメンバーのローカル環境でmidPointの設定カスタマイズ作業やテストを容易に実施できるようになります。この方法だとプロジェクトメンバーが増えてもスケールするため、非常におすすめです。これはOSSならではのメリットかと思います。
:::

以下の内容の`docker-compose.yml`を用意します。

<details><summary>docker-compose.yml</summary>

```yaml
version: "3.3"

services:
  midpoint_data:
    image: postgres:16-alpine
    environment:
     - POSTGRES_PASSWORD=db.secret.pw.007
     - POSTGRES_USER=midpoint
     - POSTGRES_INITDB_ARGS=--lc-collate=en_US.utf8 --lc-ctype=en_US.utf8
    networks:
     - net
    volumes:
     - midpoint_data:/var/lib/postgresql/data

  data_init:
    image: evolveum/midpoint:${MP_VER:-latest}-alpine
    command: >
      bash -c "
      cd /opt/midpoint ;
      bin/midpoint.sh init-native ;
      echo ' - - - - - - ' ;
      bin/ninja.sh -B info >/dev/null 2>/tmp/ninja.log ;
      grep -q \"ERROR\" /tmp/ninja.log && (
      bin/ninja.sh run-sql --create --mode REPOSITORY  ;
      bin/ninja.sh run-sql --create --mode AUDIT
      ) ||
      echo -e '\\n Repository init is not needed...' ;
      "
    depends_on:
     - midpoint_data
    environment:
     - MP_SET_midpoint_repository_jdbcUsername=midpoint
     - MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
     - MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
     - MP_SET_midpoint_repository_database=postgresql
     - MP_INIT_CFG=/opt/midpoint/var
    networks:
     - net
    volumes:
     - midpoint_home:/opt/midpoint/var

  midpoint_server:
    image: evolveum/midpoint:${MP_VER:-latest}-alpine
    container_name: midpoint_server
    hostname: midpoint-container
    depends_on:
      data_init:
        condition: service_completed_successfully
      midpoint_data:
        condition: service_started
    command: [ "/opt/midpoint/bin/midpoint.sh", "container" ]
    ports:
      - 8080:8080
    environment:
     - MP_SET_midpoint_repository_jdbcUsername=midpoint
     - MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
     - MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
     - MP_SET_midpoint_repository_database=postgresql
     - MP_SET_midpoint_administrator_initialPassword=Test5ecr3t
     - MP_UNSET_midpoint_repository_hibernateHbm2ddl=1
     - MP_NO_ENV_COMPAT=1
    networks:
     - net
    volumes:
     - midpoint_home:/opt/midpoint/var

networks:
  net:
    driver: bridge

volumes:
  midpoint_data:
  midpoint_home:
```
</details>

上記のYAMLを見ると分かりますが、midPointコンテナのイメージタグを環境変数`MP_VER`で切り替え可能になっています。未設定の場合は`latest`タグが使われます。このコンテナイメージはDocker Hubにアップロードされている公式イメージになります。使用可能なタグは以下で確認できます。

https://hub.docker.com/r/evolveum/midpoint/tags



# 環境の起動

今回は2024年12月時点の最新のLTSバージョンで構築するため、環境変数`MP_VER`に`4.8.5`を設定の上、`docker compose up -d`を実行します（Docker Composeでは`.env`ファイルに環境変数を設定しておくと便利です）。

```bash
echo MP_VER=4.8.5 > .env
docker compose up -d
```

しばらく待つと環境が構築され、http://localhost:8080/midpoint にアクセスすると、midPointのログイン画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/33936817-cb5e-3cda-3093-88719f073b55.png)

この環境は、`administrator` / `Test5ecr3t` でシステム管理者としてログインできるようにセットアップされています[^1]。ログインできることを確認します。

[^1]:YAMLの中で、環境変数`MP_SET_midpoint_administrator_initialPassword`で`administrator`のパスワードを設定しています。未設定の場合、ランダムパスワードが生成され起動時のログに出力されます。特にセキュリティの考慮を必要としないローカルの開発/検証環境では、この環境変数を設定して固定化しておくと便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/baaf73fe-9631-2f7c-b9e1-0e8dd09d4642.png)


# バージョンの確認

画面左メニューの「このシステムについて」をクリックすると、midPointの詳細情報を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/4fc20c5b-aafb-0217-f7f7-c32f6d7ba2a6.png)

基本情報に記載のバージョンが、想定通り指定したバージョン「4.8.5」になっていることを確認しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/507021ac-ee54-820b-6478-8b0a3909cfe2.png)


# おすすめの最低限の設定

日本国内でmidPointを使う場合の最低限の設定として、**PolyString Normalization Configuration** を変更することをおすすめします。

https://docs.evolveum.com/midpoint/reference/support-4.8/schema/polystring-normalization/

midPointではいくつかの属性は **PolyString** というmidPoint独自のデータ型が使われています。例えばユーザーオブジェクトの場合、`name`、`fullName`、`givenName`、`familyName`、`nickName`などはPolyString型です。一方、`emailAddress`、`telephoneNumber`、`personalNumber`、`costCenter`などは単純なString型です。

PolyString型は、内部にオリジナルの値とNormalize（正規化）した値を持ちます。そうすることで、検索時には正規化した値でも検索可能になっています。

:::note info
midPointは欧州の開発者中心で開発されていることもあり、この正規化を大事にしています。例えば、ユーザーの名前（`name`属性）が、`semancik`、`Semancik`、`semančík`のどれであっても正規化により同一ユーザーとして扱えるようにしたいという話があります。詳しくは、The Bookの「[PolyString and Protected String](https://docs.evolveum.com/book/practical-identity-management-with-midpoint.html#_polystring_and_protected_string)」を参照してください。
:::

しかしながら、デフォルトの設定だと日本語環境では非常に使いづらいものになっています。例えば、`givenName`、`familyName`にはマルチバイト文字の日本語名称を入力すると、正規化後の文字列は空文字（""）となってしまいます。結果、ユーザーの検索で正しい結果を取得できません。日本語環境では、このデフォルトの正規化が動かないように設定するとよいでしょう。

## 正規化の設定

画面左メニューの「リポジトリ・オブジェクト ＞ すべてのオブジェクト」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/c58ff758-1853-6185-239b-c639e9b365c0.png)

「タイプ」に「システム設定」が選択されていることを確認します。一覧に表示されている「SystemConfiguration」をクリックして、Raw編集画面を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/cd83b4de-7d63-341e-f35d-60e90f62466a.png)

:::note info
「リポジトリ・オブジェクト ＞ すべてのオブジェクト」では、midPointで管理しているすべてのオブジェクトをRaw編集モードで参照・更新が可能です。Raw編集モードでは、管理オブジェクトをXML/JSON/YAML形式で表示し、編集することができます。midPointの多くのオブジェクトは専用UIで表示・更新が可能ですが、一部はUIが対応しておらず、Raw編集モードを利用する必要があります。
:::

`<internals>〜</internals>`の設定箇所を探し、以下のように`<polyStringNormalizer>〜</polyStringNormalizer>`の設定を挿入し、「保存」ボタンをクリックして保存します。


```xml
    <internals>
        <!--
            日本語環境向けにPolyString Normalization設定を変更する
        -->
        <polyStringNormalizer>
            <className>PassThroughPolyStringNormalizer</className>
            <nfkd>false</nfkd>
        </polyStringNormalizer>

        ...
    </internals>
```

この設定により、PolyString型に入力した値は正規化されず、そのまま保存される動きに変わります。

## 再インデックスの実行

正規化設定を変更した後は、再インデックスの実行をしておくことをおすすめします。もし、すでにマルチバイト文字を含む何らかのデータをmidPointに保存してしまった場合は、再インデックスを実行することで新しい設定で再正規化が行われ、正しく扱えるようになります（再インデックスにより、PolyString型のデータがオリジナルの値を元に再正規化され、midPointのデータベースに再格納されます）。

画面左メニューの「このシステムについて」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/4fc20c5b-aafb-0217-f7f7-c32f6d7ba2a6.png)

システム情報の画面にて、最下部にある「リポジトリ・オブジェクトの再インデックス」ボタンをクリックして、再インデックスを実行します。

:::note warn
このボタンをクリックすると、確認画面なしに即座に再インデックスが実行されます。再インデックスは、midPointの管理するデータ量が多いと完了までに時間がかかります。本番環境では安易にクリックしないよう注意してください。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/7a341bbb-5e36-45be-80d5-7f733c6e4544.png)

実行開始すると、画面上部にバックグラウンドタスクを開始した旨が表示されます。ここの「タスク表示」をクリックすると、バックグラウンドタスクの詳細を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/2d40e7d0-bc81-f72f-b1fb-16c10c5ac470.png)

以下のように終了となれば、すべてのオブジェクトの再インデックスが完了しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/e7a8d6b0-c81f-a75f-2d3a-1b3b0c8752cd.png)


## midPointの初期構築時に設定を反映する

例えば、`docker compose down -v`で構築した環境をデータも含めて破棄し、その後また再構築した場合、都度正規化の設定を行うのは不便です。また、自身のユースケースに合わせた設定を今後作っていく際にも、その設定を手動で反映させていくのは非効率です。

midPointには、起動時に特定ディレクトリにあるXMLファイルを自動インポートする機能として「[Post-Initial Import](https://docs.evolveum.com/midpoint/reference/support-4.8/deployment/post-initial-import/)」があります。これを使って正規化の設定変更をするようにしておきます。

### SystemConfigurationのXMLファイルの配置

SystemConfigurationのデフォルト設定は、以下のGitHubリポジトリから取得できます（midPoint 4.8.5の場合）。

https://github.com/Evolveum/midpoint/blob/v4.8.5/repo/system-init/src/main/resources/initial-objects/system-configuration/000-system-configuration.xml

このXMLファイルを取得し、以下のディレクトリ構造で配置します。

```bash
.
├── .env
├── docker-compose.yml
└── post-initial-objects
    └── 000-system-configuration.xml
```

そして`000-system-configuration.xml`には、先程と同様に`<polyStringNormalizer>〜</polyStringNormalizer>`の設定を挿入しておきます。

### docker-compose.ymlの修正

`docker-compose.yml`を以下のように編集します。

```diff_yaml
  midpoint_server:
    image: evolveum/midpoint:${MP_VER:-latest}-alpine
    container_name: midpoint_server
    hostname: midpoint-container
    depends_on:
      data_init:
        condition: service_completed_successfully
      midpoint_data:
        condition: service_started
    command: [ "/opt/midpoint/bin/midpoint.sh", "container" ]
    ports:
      - 8080:8080
    environment:
     - MP_SET_midpoint_repository_jdbcUsername=midpoint
     - MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
     - MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
     - MP_SET_midpoint_repository_database=postgresql
     - MP_SET_midpoint_administrator_initialPassword=Test5ecr3t
     - MP_UNSET_midpoint_repository_hibernateHbm2ddl=1
     - MP_NO_ENV_COMPAT=1
+    - MP_ENTRY_POINT=/opt/entry-point
    networks:
     - net
    volumes:
     - midpoint_home:/opt/midpoint/var
+    - ./post-initial-objects:/opt/entry-point/post-initial-objects:ro
```

### 起動と確認

これでDocker Composeを起動すると、以下のように`Starting post-initial import of file 000-system-configuration.xml`というメッセージが`midpoint_server`のログに出力されます（`docker compose logs -f midpoint_server`で確認できます）。

```
midpoint_server        | 2024-12-02 03:04:24,201 [] [main] INFO (com.evolveum.midpoint.init.PostInitialDataImport): Starting initial object import (if necessary).
midpoint_server        | 2024-12-02 03:04:24,202 [] [main] INFO (com.evolveum.midpoint.init.PostInitialDataImport): Directory /opt/midpoint/var/ exists. Using it.
midpoint_server        | 2024-12-02 03:04:24,202 [] [main] INFO (com.evolveum.midpoint.init.PostInitialDataImport): Directory /opt/midpoint/var/post-initial-objects exists. Using it.
midpoint_server        | 2024-12-02 03:04:24,213 [] [main] INFO (com.evolveum.midpoint.init.PostInitialDataImport): Starting post-initial import of file 000-system-configuration.xml.
midpoint_server        | 2024-12-02 03:04:24,657 [MODEL] [main] INFO (com.evolveum.midpoint.model.impl.importer.ObjectImporter): Imported object systemConfiguration:00000000-0000-0000-0000-000000000001(SystemConfiguration)
midpoint_server        | 2024-12-02 03:04:24,658 [] [main] INFO (com.evolveum.midpoint.init.PostInitialDataImport): Post-initial object import finished (1 objects imported, 0 scripts executed)
```

起動後、midPointに`administrator`でログインし、SystemConfigurationに`<polyStringNormalizer>〜</polyStringNormalizer>`の設定が反映されていれば問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/cc63cd29-514f-0a36-27b7-38a655a7624c.png)

# その他のよく使う設定

その他に、よく設定しておくものをいくつか紹介しておきます。

## タイムゾーンの設定

ログの時刻や画面の日時表示がデフォルトだとUTC表示になっているため、必要に応じてタイムゾーンを変更します。midpointコンテナの環境変数に`TZ`を設定すればよいです。例えば`Asia/Tokyo`と設定する場合、以下のように`docker-compose.yml`を修正します。

```diff_yaml
  midpoint_server:
    image: evolveum/midpoint:${MP_VER:-latest}-alpine
    container_name: midpoint_server
    hostname: midpoint-container
    depends_on:
      data_init:
        condition: service_completed_successfully
      midpoint_data:
        condition: service_started
    command: [ "/opt/midpoint/bin/midpoint.sh", "container" ]
    ports:
      - 8080:8080
    environment:
     - MP_SET_midpoint_repository_jdbcUsername=midpoint
     - MP_SET_midpoint_repository_jdbcPassword=db.secret.pw.007
     - MP_SET_midpoint_repository_jdbcUrl=jdbc:postgresql://midpoint_data:5432/midpoint
     - MP_SET_midpoint_repository_database=postgresql
     - MP_SET_midpoint_administrator_initialPassword=Test5ecr3t
     - MP_UNSET_midpoint_repository_hibernateHbm2ddl=1
     - MP_NO_ENV_COMPAT=1
     - MP_ENTRY_POINT=/opt/entry-point
+    - TZ=Asia/Tokyo
    networks:
     - net
    volumes:
     - midpoint_home:/opt/midpoint/var
     - ./post-initial-objects:/opt/entry-point/post-initial-objects:ro
```

## ワークフロー承認後のタスク実行の並行制御設定

midPointではワークフローが承認されると、バックグラウンドタスクとして処理が実行されます。そのタスクが複数、同一オブジェクトに対して並行に処理が動くとコンフリクトが発生する可能性があります。同一オブジェクトに対するワークフロー承認後の処理は、直列で行うように設定しておくとこの問題を回避できます。

Post-Init Importで設定する場合は、`000-system-configuration.xml`に以下の`<workflowConfiguration>〜</workflowConfiguration>`を設定します。

```xml
    <workflowConfiguration>
        <executionTasks>
            <serialization>
                <enabled>true</enabled>
                <scope>object</scope>
            </serialization>
        </executionTasks>
    </workflowConfiguration>
```

## 同一オブジェクトに対する並行処理でコンフリクト検知時の対策設定

ワークフロー承認後の処理に限らず、あるオブジェクトに対して並行処理が行われるケースはmidPointでは想定されます。下記ドキュメントに記載の「Conflict Resolution」の設定を行うことで、コンフリクトを検知した際に、エラーで終了させず再計算アクションを実行して自動復旧させることができます（ただし、「再計算」であるべき姿に修正されるようにmidPointを設定しておく必要があります）。

https://docs.evolveum.com/midpoint/reference/support-4.8/concepts/clockwork/conflict-resolution-howto/

Post-Init Importで設定する場合は、`000-system-configuration.xml`に以下の`<defaultObjectPolicyConfiguration>〜</defaultObjectPolicyConfiguration>`を設定します。

```xml
    <defaultObjectPolicyConfiguration>
        <conflictResolution>
            <action>recompute</action>
        </conflictResolution>
    </defaultObjectPolicyConfiguration>
```

## ワークフロー承認時の実行制御設定

アクセス要求で複数のロールを同時に要求した場合に、片方のロールは承認を必要としないとなった場合でも、デフォルトでは承認が必要なロール側の承認が終わったタイミングでまとめて処理されます。要件にもよりますが、承認が必要ないものは独立して先に処理を進めるように変更したい場合は、`defaultExecuteAfterAllApprovals`の設定を`false`にします。

Post-Init Importで設定する場合は、`000-system-configuration.xml`に以下の`<roleManagement>〜</roleManagement>`を設定します。

```xml
    <roleManagement>
        <defaultExecuteAfterAllApprovals>false</defaultExecuteAfterAllApprovals>
    </roleManagement>
```


# まとめ

4日目は、midPointの開発/検証環境の構築について解説しました。Docker Composeを使うと簡単に環境を準備することができるのでおすすめです。

また、日本語環境向けに必要となる最低限の設定として、**PolyString Normalization Configuration** についても紹介しました。日本国内でmidPointを使おうとすると、後々嵌まるポイントになりがちなので忘れないように注意してください。

「Post-Initial Import」は、プロジェクト固有の設定を自動で適用するのに非常に便利です。実際のプロジェクトでは、GitでXMLファイル群を管理しておくとよいでしょう。開発者は`git clone`してすぐに環境を準備することができるようになります。

その他によく使う設定をいくつか紹介しました。要件に応じて利用していただければと思います。

明日以降は、この開発/検証環境を利用して源泉取り込みやプロビジョニングの設定を追加していく予定です。お楽しみに！