midPoint by OpenStandia Advent Calendar 2024 の21日目は、プロビジョニングからはちょっと離れて、midPointに拡張属性を追加する方法を紹介します。
IDM/IGAでプロジェクト固有の拡張属性を追加するケースは必ずといっていいほどあります。特に、海外で開発されているプロダクトの場合は、日本だと必要となる基本的な属性(姓名のフリガナや英語表記)をデフォルトで持つことは基本的になく、拡張属性として追加することが多いでしょう。
多くのIDM/IGAプロダクトがサポートしているように、midPointでも拡張属性の追加が可能です。また、一般的には拡張属性はString型やInt型、Boolean型など基本的なデータ型に限定されていますが、midPointの特徴として参照型やオブジェクト型(構造化データ)にも対応している点があります。これは、他のIDM/IGAプロダクトでは中々見られないものだと思いますので、本記事でも紹介したいと思います。
公式ドキュメント
公式ドキュメントでは「Custom Schema Extension」として以下のページで書かれています。ここに、サポートされているデータ型についても記載されています。
また、サンプル設定がGitHubにもあります。
設定方法
拡張属性のスキーマをXSDファイル形式で定義する必要があります。このファイルを所定のディレクトリ(MP_HOME/schema
)に配置してmidPointを起動すると、拡張属性を認識します。
2024年12月時点で最新の非LTSバージョンである4.9では、GUIから拡張属性を設定できる機能が追加されました。以下の、4.9用のドキュメントに記載があります。
準備
4日目の記事「midPoint の開発/検証環境を作る」で用意したDocker Compose環境を今まで使ってきました。作成する拡張スキーマのファイルをmidPoint本体に読ませるために、docker-compose.yml
を少し修正します。
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
+ - ./schema:/opt/midpoint/var/schema
- ./post-initial-objects:/opt/entry-point/post-initial-objects:ro
- ./hr:/var/lib/hr
- ./addressbook:/var/lib/addressbook
拡張スキーマファイルを配置するディレクトリを作成します。
mkdir schema
schema
ディレクトリ以下に、extension-samples.xsd
ファイルを作成します。拡張子が.xsd
であればよく、ファイル名は何でも構いません。また、複数ファイルに分けて配置しても構いません。
.
├── .env
├── ad
│ ├── Dockerfile
│ ├── ad-init.sh
│ ├── entrypoint.sh
│ ├── named.conf.options
│ └── supervisord.conf
├── addressbook
│ └── users.csv
├── docker-compose.yml
├── hr
│ ├── orgs.csv
│ └── users.csv
├── post-initial-objects
│ └── ...
└── schema
└── extension-samples.xsd
拡張スキーマの設定
例として、姓名の表記を4つ増やす拡張属性を追加します。以下の内容を設定します。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsd:schema elementFormDefault="qualified"
targetNamespace="http://openstandia.jp/midpoint"
xmlns:tns="http://openstandia.jp/midpoint"
xmlns:a="http://prism.evolveum.com/xml/ns/public/annotation-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://prism.evolveum.com/xml/ns/public/types-3"/>
<xsd:import namespace="http://midpoint.evolveum.com/xml/ns/public/common/common-3" />
<xsd:complexType name="UserExtensionType">
<xsd:annotation>
<xsd:appinfo>
<a:extension ref="c:UserType"/>
<a:container>true</a:container>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="familyNameKana" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>姓(フリカナ)</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="givenNameKana" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>名(フリカナ)</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="familyNameEn" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>姓(英語表記)</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="givenNameEn" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>名(英語表記)</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="userExtension" type="tns:UserExtensionType" />
</xsd:schema>
ポイントは、<a:extension ref="c:UserType"/>
の箇所です。これで、midPointのどのオブジェクトを拡張して属性を追加するか定義しています。今回はユーザー属性として追加するため、c:UserType
を設定しています。
追加する4つの属性では、すべて<a:indexed>true</a:indexed>
として「インデックスあり」で設定しています。この場合、検索画面からなどで、この拡張属性での検索が可能になります。ただしその分、データベースのインデックス用領域を消費します。逆にfalse
にするとインデックスは作成されずインデックス用領域を消費しませんが、検索は不可になります。最初はfalse
で後でtrue
に変更することも可能ですが、その場合は4日目の記事で紹介した「リポジトリ・オブジェクトの再インデックス」を行う必要があります。
なお、今回はすべてシングルバリュー項目として定義していますが、minOccurs="0" maxOccurs="unbounded"
と設定することでマルチバリュー項目として設定することも可能です。
midPointの起動
midPointをDocker Composeで起動します。拡張スキーマファイルを作成または更新した際には、midPointを必ず再起動する必要があります。
動作確認
ユーザーの新規作成画面を開いてみると、追加した4項目が表示されることを確認できます。
試しに適当な値を入力してユーザーを作成し、RAW編集画面を開いてどのように保存されるか見てみます。
<user xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3" xmlns:icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3" xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3" xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3" xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3" xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" oid="254e4d48-9cd8-446d-8391-4704d26439c8" version="1">
<name>test</name>
<extension xmlns:gen600="http://openstandia.jp/midpoint">
<gen600:familyNameKana>ノムラ</gen600:familyNameKana>
<gen600:givenNameKana>タロウ</gen600:givenNameKana>
<gen600:familyNameEn>Nomura</gen600:familyNameEn>
<gen600:givenNameEn>Taro</gen600:givenNameEn>
</extension>
...(省略)
<givenName>太郎</givenName>
<familyName>野村</familyName>
</user>
上記のように、<extension>〜</extension>
配下に拡張属性が保存されていることが分かります。実はmidPointでは、すべてのオブジェクトがextension
属性という拡張属性を格納する領域を備えています。ユーザーだけでなくロールや組織、サービス、そしてアサインを表すオブジェクト(AssignmentType)にもextension
属性が存在し、拡張属性を追加できるようになっています。
検索画面での使用
ユーザー一覧画面を開き、検索ボックスの「詳細...」ボタンをクリックして、そこから属性一覧に追加された拡張属性を選択します。
例として「姓(フリガナ)」属性を検索項目に追加し、検索しています。
また、「基本情報」ボタン横の▼をクリックして「高度」を選択すると「midPoint Query Language」を使った高度な検索モードに変わります。
このモードでは、任意のクエリで柔軟な検索が可能になります
拡張属性を使って検索する場合は、例えば一致検索の場合はextension/givenNameKana = "タロウ"
のように書きます。先ほどRAW編集画面で確認したように、すべての拡張属性はextension
配下に保存されているため、検索クエリではextension/〜
で指定します。また、givenNameKana
のようにスキーマファイルで設定した拡張属性の項目名を指定する必要があります。
midPoint Query Languageでは、他にも部分一致・前方一致・後方一致検索だったり、and
、or
、not
といったオペレータを使ったり色々細かく記述できます。詳細は、以下の公式ドキュメントを参照してください。
https://docs.evolveum.com/midpoint/reference/support-4.8/concepts/query/midpoint-query-language/
マッピングでの使用
拡張属性はもちろん、リソース設定のマッピングやオブジェクトテンプレートのマッピングでも利用可能です。
5日目の記事「midPoint にCSVの源泉データをインポートする(ユーザー編)」で実施したHRシステムからのインポートで、CSVファイルに姓(フリガナ)、名(フリガナ)を表す列を追加した場合を例に説明します。
始めに、以下のように./hr/users.csv
に「givenNameKana」「familyNameKana」列を追加し、データの行にも追加します(とりあえず動作確認のため、1001
の行にだけフリガナを入力し、その他の行には,,
を入力しています)。
employeeNumber,givenName,familyName,email,primaryOrgId,secondaryOrgIds,givenNameKana,familyNameKana
1001,太郎,山田,taro.yamada@example.com,005,006;007,タロウ,ヤマダ
1002,花子,佐藤,hanako.sato@example.com,006,005;009,,
1003,次郎,鈴木,jiro.suzuki@example.com,007,006,,
1004,美咲,高橋,misaki.takahashi@example.com,008,009,,
1005,健一,田中,kenichi.tanaka@example.com,009,008,,
1006,陽菜,伊藤,hina.ito@example.com,005,,,
1007,翔,渡辺,sho.watanabe@example.com,006,,,
1008,さくら,小林,sakura.kobayashi@example.com,007,,,
1009,優,中村,yu.nakamura@example.com,008,005;007,,
1010,一郎,山本,ichiro.yamamoto@example.com,009,006,,
「HR Users」リソースの詳細画面を開き、「スキーマのリフレッシュ」ボタンをクリックします。そうするとCSVコネクターはCSVファイルを読み取り、ヘッダ行をみてそのスキーマが更新されたことを(列が追加されたことを)認識します。
「HR Users」リソースの詳細画面から「Account」オブジェクトタイプのインバウンドマッピングの設定画面を開き、マッピングを追加します。CSV側のスキーマが更新されたため「From resource attribute」列では「givenNameKana」「familyNameKana」がサジェスチョン一覧にも表示されるようになります。
同様に、「ターゲット」列ではmidPointに拡張属性を追加したため「extension/givenNameKana」「extension/familyNameKana」などが選択できるようになっています。
今回は単純マッピングで以下のように設定します。これで保存します。
「HR import users」インポートタスクを実行すると、midPointのユーザーに拡張属性が反映されることを確認できます。
高度な拡張属性定義
単純なデータ型ではないタイプの拡張属性定義についても紹介します。
midPointのデータ型
midPointが提供するデータ型を使用して任意の拡張属性を追加することができます。
ProtectedStringType
midPointには「ProtectedStringType」という値を暗号化して保持する特殊な型があります。特定システム向けのパスワードをユーザーに持たせたいといったケースで使用することができます。
以下のように、拡張スキーマのファイルを修正します。
<xsd:element name="givenNameEn" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>名(英語表記)</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
+ <xsd:element name="fooSystemPassword" type="t:ProtectedStringType" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:appinfo>
+ <a:indexed>false</a:indexed>
+ <a:displayName>Fooシステム用パスワード</a:displayName>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="userExtension" type="tns:UserExtensionType" />
</xsd:schema>
docker compose restart midpoint_server
コマンドでmidPointを再起動して拡張スキーマを反映させると、ユーザーの属性で以下のようにパスワード用項目が追加されていることを確認できます。
通常のパスワードもですが、midPointでは「ProtectedStringType」で保存されるデータはデフォルトでは「AES256-CBC」で暗号化されています。IDM/IGAとしてパスワードを外部システムにプロビジョニングする要件があるため、ハッシュ化方式ではなく復号可能な方式で保存しています。
なお、パスワードのプロビジョニング要件がなくハッシュ化方式が望ましい場合は、そのように設定変更することも可能です。詳細は以下のドキュメントを参照してください。
参照型(ObjectReferenceType)
midPointには別のオブジェクトへの参照を保持する「ObjectReferenceType」という型があります。これを利用して、ユーザーにその管理者ユーザーを参照情報として設定するためのmanagerRef
属性を拡張属性に追加してみます。
以下のように、拡張スキーマのファイルを修正します。
<xsd:element name="fooSystemPassword" type="t:ProtectedStringType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>false</a:indexed>
<a:displayName>Fooシステム用パスワード</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
+ <xsd:element name="managerRef" type="c:ObjectReferenceType" minOccurs="0" maxOccurs="1">
+ <xsd:annotation>
+ <xsd:appinfo>
+ <a:indexed>true</a:indexed>
+ <a:objectReferenceTargetType>c:UserType</a:objectReferenceTargetType>
+ <a:displayName>管理者</a:displayName>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="userExtension" type="tns:UserExtensionType" />
</xsd:schema>
<a:objectReferenceTargetType>c:UserType</a:objectReferenceTargetType>
は「ObjectReferenceType」型の属性を定義する際に利用可能なオプションで、これで指定したターゲットの型に限定したUIの制御をしてくれるようになります。今回は管理者ユーザーの設定なので「c:UserType」を設定しています。
docker compose restart midpoint_server
コマンドでmidPointを再起動して拡張スキーマを反映させると、ユーザーの属性で以下のように参照型の管理者用項目が追加されていることを確認できます。
「編集」ボタンをクリックするとユーザーオブジェクトに限定した選択ダイアログが開きます。
ユーザーを選択すると、拡張属性側は以下のように設定された状態になります。
オブジェクト型(構造型データ)
最後に、オブジェクト型(構造型データ)の拡張属性の追加例を紹介します。midPointが標準で提供するユーザーや組織オブジェクトのようなオブジェクト型までも拡張属性として定義することができます。
例えば、ユーザーに追加のメールアドレスを説明文付きで複数設定できるようにします。
以下のように、拡張スキーマのファイルを修正します。
<xsd:element name="managerRef" type="c:ObjectReferenceType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:objectReferenceTargetType>c:UserType</a:objectReferenceTargetType>
<a:displayName>管理者</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
+ <xsd:element name="additionalEmailAddress" type="tns:AdditionalEmailAddressType" minOccurs="0" maxOccurs="unbounded">
+ <xsd:annotation>
+ <xsd:appinfo>
+ <a:indexed>false</a:indexed>
+ <a:displayName>追加メールアドレス</a:displayName>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="userExtension" type="tns:UserExtensionType" />
+
+ <xsd:complexType name="AdditionalEmailAddressType">
+ <xsd:annotation>
+ <xsd:documentation>
+ Additional email address
+ </xsd:documentation>
+ <xsd:appinfo>
+ <a:indexed>false</a:indexed>
+ <a:operational>false</a:operational>
+ <a:container/>
+ </xsd:appinfo>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0">
+ <xsd:annotation>
+ <xsd:appinfo>
+ <a:indexed>false</a:indexed>
+ <a:displayName>メールアドレス</a:displayName>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name="intent" type="xsd:string" minOccurs="0">
+ <xsd:annotation>
+ <xsd:appinfo>
+ <a:indexed>false</a:indexed>
+ <a:displayName>用途</a:displayName>
+ </xsd:appinfo>
+ </xsd:annotation>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="id" type="xsd:long" />
+ </xsd:complexType>
+ <xsd:element name="additionalEmailAddress" type="tns:UserExtensionType" />
</xsd:schema>
docker compose restart midpoint_server
コマンドでmidPointを再起動して拡張スキーマを反映させると、ユーザーの属性で以下のように「追加メールアドレス」の項目が追加されていることを確認できます。
以下のように複数の値を設定して保存してみます。
RAW編集モードで開いてみると、以下のようにネストされた形で保存されていることが分かります。
<user xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3" xmlns:icfs="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3" xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3" xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3" xmlns:ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance-3" xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" oid="fffca5ca-a331-4b17-a96f-c71cc04ed938" version="9">
<name>1001</name>
<extension xmlns:gen40="http://openstandia.jp/midpoint">
<gen40:familyNameKana>ヤマダ</gen40:familyNameKana>
<gen40:givenNameKana>タロウ</gen40:givenNameKana>
<gen40:additionalEmailAddress id="7">
<gen40:value>yamada@example.org</gen40:value>
<gen40:intent>出向先</gen40:intent>
</gen40:additionalEmailAddress>
<gen40:additionalEmailAddress id="8">
<gen40:value>t-yamada@example.net</gen40:value>
<gen40:intent>緊急連絡先</gen40:intent>
</gen40:additionalEmailAddress>
</extension>
...
オブジェクト型の拡張属性は非常に強力な機能ですが、インデックス設定が効かないという点に注意してください。つまり、この拡張属性の値を条件にして検索することはできません。
運用時の考慮点
一度追加した拡張属性の定義を削除する場合は注意が必要です。既にその項目にデータを格納している場合、スキーマ定義から削除後にそのデータにアクセスすると、スキーマエラーとなってしまいます。また、たとえデータから削除したとしても、監査ログで過去の履歴を参照する際にはやはりスキーマエラーとなるため、基本的には一度設定してデータが格納されてしまったものについては、スキーマ定義から削除しないほうが安全でしょう。そのような不要になった拡張属性は、画面からは非表示にする設定を入れておくとよいです。データが空の場合はRAWデータで表示しても、単にXMLタグなしの状態のため、もう使われなくなった拡張属性項目が存在しても、特に気にならないはずです。
その他、非常に多くの項目を定義すると(数百個など)、一画面で表示した際に画面表示や編集操作にパフォーマンスの影響があるかもしれません。midPointでは画面の表示項目も設定でカスタマイズ可能なため、1画面にすべての属性は表示しないといった工夫が必要になるかもしれません。
まとめ
21日目では、midPointに拡張属性を追加する「Custom Schema Extension」機能を紹介しました。midPointの特徴として、単純な基本型だけでなく、midPointが提供するデータ型や任意のオブジェクト型(構造化データ)に対応している点も紹介しました。設定可能な数も特に制限はないため、大量の拡張属性を必要とする複雑な業務要件にも対応が可能ではありますが、画面操作時にパフォーマンスの問題を引き起こす恐れがあるため、その点はご注意ください。