# LDAP group を keycloak に読み込む方法
## 参考
- https://extensions.xwiki.org/xwiki/bin/view/Extension/OpenID%20Connect/OpenID%20Connect%20Authenticator/OpenID%20Authentication%20with%20Keycloak/Keycloak%20Group-Mapping/
- https://www.janua.fr/mapping-ldap-group-and-roles-to-redhat-sso-keycloak/
## 環境
keycloak: v21.1.1
## 手順
1. Keycloak で LDAP の設定を済ませる
1. User federation から `Add new provider` をクリックし、LDAP を選択
2. 設定値を入力する
2. 作成した LDAP の User federation の詳細画面にいく
3. Mappers のタブを開く
4. `Add Mapper` をクリック
5. Name を適当に入力し、Mapper type に `group-ldap-mapper` を選択
6. 設定値を入力する
- `LDAP Groups DN`: LDAP グループが所属するディレクトリの DN。
- `Group Name LDAP Attribute`: グループ object の RDN に使用されている attribute。
- `Group Object Classes`: グループ object の object class。複数ある場合は comma 区切り。
- `Membership LDAP Attribute`: グループ object の attribute のうち、所属メンバーを表すもの。
- `Membership Attribute Type`: `Membership LDAP Attribute` で指定した attribute の値の type。DN の場合は user object の DN が値として入っている想定。
- `User Groups Retrieve Strategy`: どのようにグループに所属しているユーザ情報を取得するか。LOAD_GROUPS_BY_MEMBER_ATTRIBUTE であれば、group object の Membership LDAP Attribute で指定した attribute 値を使って user を取得する。
7. 右上の Action から `Sync LDAP groups to keycloak` を選択
## example
以下の内容で LDAP サーバデータがあるとする。
```
# example.org
dn: dc=example,dc=org
objectClass: dcObject
objectClass: organization
dc: example
o: example
# users, example.org
dn: ou=users,dc=example,dc=org
objectClass: organizationalUnit
ou: users
# customuser, users, example.org
dn: cn=customuser,ou=users,dc=example,dc=org
cn: User1
cn: customuser
sn: Bar1
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:: Y3VzdG9tcGFzc3dvcmQ=
uid: customuser
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/customuser
# customuser2, users, example.org
dn: cn=customuser2,ou=users,dc=example,dc=org
cn: User2
cn: customuser2
sn: Bar2
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
userPassword:: Y3VzdG9tcGFzc3dvcmQ=
uid: customuser2
uidNumber: 2000
gidNumber: 2000
homeDirectory: /home/customuser2
# groups, example.org
dn: ou=groups,dc=example,dc=org
ou: groups
objectClass: organizationalUnit
# parentGroup, groups, example.org
dn: cn=parentGroup,ou=groups,dc=example,dc=org
cn: parentGroup
objectClass: groupOfNames
member: cn=customuser,ou=users,dc=example,dc=org
member: cn=childGroup,ou=groups,dc=example,dc=org
# childGroup, groups, example.org
dn: cn=childGroup,ou=groups,dc=example,dc=org
cn: childGroup
objectClass: groupOfNames
member: cn=customuser2,ou=users,dc=example,dc=org
```
以下の内容で新たに group-ldap-mapper を登録する。
```
LDAP Groups DN: ou=groups,dc=example,dc=org
Group Name LDAP Attribute: cn
Group Object Classes: groupOfNames
Preserve Group Inheritance: On
Membership LDAP Attribute: member
Membership Attribute Type: DN
User Groups Retrieve Strategy: LOAD_GROUPS_BY_MEMBER_ATTRIBUTE
```
以下のように Keycloak group が登録される。
- parentGroup
- Child groups: childGroup
- Members: user1
- childGroup
- Members: user2
# Keycloak から group 情報を読み込む/LDAP との sync を発火する方法
## 前提
- [Keycloak Admin REST API](https://www.keycloak.org/docs-api/21.1.1/rest-api/index.html) を使う。
- 使うためには、master realm の Clients にある admin-cli に対する OIDC access-token の取得が必要となる。
```
# token_endpoint 確認
$ curl http://localhost:8080/realms/master/.well-known/openid-configuration | python3 -m json.tool
# access_token 取得
$ curl --insecure -X POST {確認した token_endpoint} --user admin-cli: -H 'content-type: application/x-www-form-urlencoded' -d 'username={管理者ユーザ名}&password={管理者パスワード}&grant_type=password' | python3 -m json.tool
$ access_token={取得した access_token}
```
- js でリクエストする場合は[ライブラリ](https://github.com/keycloak/keycloak/tree/main/js/libs/keycloak-admin-client)がある
## group 構造取得
```
$ curl -k -X GET http://localhost:8080/admin/realms/{realm 名}/groups -H "Content-Type: application/json" -H "Authorization: Bearer $access_token" | python3 -m json.tool
# レスポンス例
[
{
"id": "c54d5280-0f59-4cd3-920d-803b785f9f2e",
"name": "parentGroup",
"path": "/parentGroup",
"subGroups": [
{
"id": "e90232f7-cffc-493d-a061-b08f710608f9",
"name": "childGroup",
"path": "/parentGroup/childGroup",
"subGroups": []
}
]
}
]
```
## member 情報取得
1 group 単位でリクエストする必要がある。
```
$ curl -k -X GET http://localhost:8080/admin/realms/{realm 名}/groups/{group id}/members -H "Content-Type: application/json" -H "Authorization: Bearer $access_token" | python3 -m json.tool
# レスポンス例
[
{
"id": "b683abaa-5ac0-45b0-93d0-1fe7814f48fd",
"createdTimestamp": 1683912761540,
"username": "user1",
"enabled": true,
"totp": false,
"emailVerified": false,
"lastName": "Bar1",
"federationLink": "cc03a070-5adc-413a-9ec1-e315bdf229ac",
"attributes": {
"LDAP_ENTRY_DN": [
"cn=customuser,ou=users,dc=example,dc=org"
],
"LDAP_ID": [
"customuser"
]
},
"disableableCredentialTypes": [],
"requiredActions": [
"UPDATE_PASSWORD"
],
"notBefore": 0
}
]
```
## LDAP group との sync を発火する方法
```
# 各 ID は詳細画面の URL、または GET /{realm}/components を叩くことで確認できる
$ curl -k -X POST "http://localhost:8080/admin/realms/{realm 名}/user-storage/{LDAP user federation の ID}/mappers/{group mapper の ID}/sync?direction=fedToKeycloak" -H "Content-Type: application/json" -H "Authorization: Bearer $access_token"
```
### 注意
- sync API は現在 [legacy module に含まれている](https://github.com/keycloak/keycloak/blob/main/docs/documentation/upgrading/topics/keycloak/changes-19_0_0.adoc#changes-in-the-module-structure)。
- API documentation には説明がなく、JS のライブラリにもリクエストするための実装は用意されていない。
- [storage の移行](https://www.keycloak.org/2022/07/storage-map.html)の過程で legacy となったようだが、リリースノートなどを読んでいるとまだ過渡期な雰囲気を感じるので、新しい storage 用に再実装されるのか[質問中](https://github.com/keycloak/keycloak/discussions/20338)