adorsys / adorsys/keycloak-config-cli
Organization reconcile re-creates existing organizations when realm has >10 organizations (409/400) — pagination of existing-org lookup (incomplete #1493/#1497)
- Dominant language
- Java
- Stars
- 1.2k
- Forks
- 200
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
When a realm has **more than 10 organizations**, re-importing/updating organizations (e.g. to add members to organizations created in a previous import) makes keycloak-config-cli **try to re-create organizations that already exist**, failing the whole organization import.
The organization reconcile only looks at the **first page (10)** of existing organizations, so any organization beyond that page is treated as non-existent and a `POST .../organizations` is issued for it. This looks like the same root cause as the pagination fix in #1493 / #1497, but that path in `OrganizationImportService` is still affected in **6.5.1**.
Depending on whether the organization in the import file carries a full definition or only `{alias, members}`, the failing call returns:
- `Create method returned status Conflict (Code: 409)` → `{"errorMessage":"A organization with the same name already exists."}`
- `Create method returned status Bad Request (Code: 400)` → `{"errorMessage":"Name can not be null"}` (when the org entry only has `alias` + `members`)
## Environment
- keycloak-config-cli: **6.5.1** (image `adorsys/keycloak-config-cli:6.5.1-26`, bundled `keycloak-admin-client-26.0.4`)
- Keycloak server: **26.4.7** (`start-dev --features=organization`)
- `IMPORT_VARSUBSTITUTION_ENABLED=true`, default managed settings
## Minimal reproduction
Works with **≤10** organizations; fails with **>10**. Below uses 12.
**`01.json`** — create 12 org shells (no members) + 12 users:
```json
{
"realm": "repro", "enabled": true, "organizationsEnabled": true,
"users": [ { "username": "user0", "enabled": true, "email": "user0@t.test", "emailVerified": true }, "... user1 .. user11 ..." ],
"organizations": [ { "alias": "org0", "name": "org0", "enabled": true, "domains": [], "members": [] }, "... org1 .. org11 ..." ]
}
```
**`02.json`** — same realm, now each org with its member:
```json
{
"realm": "repro", "enabled": true, "organizationsEnabled": true,
"organizations": [ { "alias": "org0", "name": "org0", "enabled": true, "domains": [], "members": [ { "username": "user0" } ] }, "... org1 .. org11 ..." ]
}
```
Steps:
1. Import `01.json` → 12 orgs + 12 users created OK.
2. Import `02.json` (add members) → **fails**.
## Observed
```
WARN d.a.k.c.s.OrganizationImportService : Failed to import organizations for realm 'repro'.
Error: Create method returned status Conflict (Code: 409); expected status: Created (201)
```
Captured request/response (HTTP proxy between config-cli and Keycloak). Note config-cli issues a **POST** (create) for `org8`, which already exists:
```
POST /admin/realms/repro/organizations -> 409
REQ Content-Type: application/json
REQ BODY: {"id":null,"name":"org8","alias":"org8","enabled":true,...,
"members":[{"username":"user8",...}],"identityProviders":null}
RESP BODY: {"errorMessage":"A organization with the same name already exists."}
```
`org8` (not `org0`) is the first to fail because Keycloak's default-ordered first page of 10 is
`org0, org1, org10, org11, org2, org3, org4, org5, org6, org7` — so `org8`/`org9` fall outside it and are
seen as missing. The import aborts on the first conflict.
With **≤10** organizations the exact same files import correctly (members added, no spurious POST).
## Expected
Existing organizations beyond the first page should be recognized, and config-cli should **update** them
(and reconcile members) instead of attempting to create duplicates.
## Likely root cause / suggested fix
The lookup of existing organizations used to decide create-vs-update appears to fetch only Keycloak's
default page (`max=10`) instead of paginating through all of them — same class of issue as #1493 / #1497,
but in the reconcile path of `OrganizationImportService` / `OrganizationRepository`.
Suggested fix: when listing existing organizations for reconciliation, paginate through all results
(loop `first`/`max`, or pass `max=-1` / a large `max`), mirroring the fix applied for #1493. A quick grep
for the organizations `list()`/`search()` call in `OrganizationRepository` (the counterpart of the already
non-paginated `getMembers().getAll()`) should surface the spot.
## Secondary observation (separate, but related to org-import reliability)
The `*-26.5.4` / `*-26.5.5` images (and `latest-26.5.x`) are built **without** `OrganizationImportService`
on the classpath:
```
DEBUG d.a.k.config.service.RealmImportService : OrganizationImportService not available on classpath.
```
With those images, organizations present in the import are **silently skipped** — no orgs created, no
`WARN`/`ERROR`, exit code 0. A user only finds out by inspecting the realm afterwards. It would help to log
a `WARN` (not `DEBUG`) when the import declares organizations but the service is absent, so the skip is not
silent.
Happy to provide the full generator script / a self-contained repro repo if useful.
Contributor guide
Assessment
This issue has not been assessed yet.