Add typed keycloak_client_registration_policy_* resources (closes #715)#1580
Open
xrl wants to merge 6 commits into
Open
Add typed keycloak_client_registration_policy_* resources (closes #715)#1580xrl wants to merge 6 commits into
xrl wants to merge 6 commits into
Conversation
Add ClientRegistrationPolicy struct + CRUD methods on *KeycloakClient, plus a SubType field on the package-private component struct (omitempty, so it's a no-op for keystore/LDAP/IdP-mapper components that don't use it). Smoke-tested against Keycloak 26.5: create + read + update + read + delete on a trusted-hosts policy under master/anonymous round-trips cleanly, including the SubType field. This is the shared layer that the typed keycloak_client_registration_policy_* resources will sit on top of. Signed-off-by: Xavier Lange <xrlange@gmail.com>
Add one Terraform resource per Keycloak built-in Client Registration Policy
provider, on top of the shared CRUD primitives:
keycloak_client_registration_policy_trusted_hosts (provider_id: trusted-hosts)
keycloak_client_registration_policy_max_clients (provider_id: max-clients)
keycloak_client_registration_policy_allowed_client_scopes (provider_id: allowed-client-templates)
keycloak_client_registration_policy_allowed_protocol_mappers (provider_id: allowed-protocol-mappers)
keycloak_client_registration_policy_web_origins (provider_id: registration-web-origins)
keycloak_client_registration_policy_consent_required (provider_id: consent-required)
keycloak_client_registration_policy_full_scope_disallowed (provider_id: scope)
keycloak_client_registration_policy_client_disabled (provider_id: client-disabled)
Resources expose typed scalars (TypeBool, TypeInt) and TypeSet for
multivalued-string config fields - Keycloak's components API does not
preserve list order on round-trip, so TypeSet avoids spurious diffs.
The 3 config-less policy types share a Configless() factory in the
helpers file; each becomes a 6-line wrapper.
The shared importer parses {realmId}/{componentId} and asserts the
component's providerId matches the resource type, so importing a
trusted-hosts component into a max_clients resource fails with a clear
error instead of corrupting state.
Acceptance tests cover trusted_hosts (TypeSet round-trip), max_clients
(TypeInt round-trip), and the cross-resource import-mismatch guard.
All pass against Keycloak 26.5.5.
Signed-off-by: Xavier Lange <xrlange@gmail.com>
…urces
Per-resource basic tests round-trip every field through Terraform's
ImportStateVerify, exercising the typed schemas (TypeBool, TypeInt,
TypeSet) against a live Keycloak.
Additions:
* Shared crpExists / crpDestroy / crpImportId helpers in
resource_keycloak_client_registration_policy_helpers_test.go remove
per-resource boilerplate.
* The 3 config-less resources share a runConfiglessBasic table-driven
helper that exercises sub_type = "authenticated" along the way.
* trusted_hosts gets an _update test that mutates every field
(including the TypeSet content) and verifies the apply succeeds.
* A fullstack test declares all 8 resources in a single config (4
anonymous, 4 authenticated) to confirm they coexist cleanly.
11 tests, ~7s end-to-end against Keycloak 26.5.5.
Signed-off-by: Xavier Lange <xrlange@gmail.com>
Per-resource _update tests for every config-bearing resource: - trusted_hosts (already had one) - max_clients (rename + change int) - allowed_client_scopes (rename + flip bool + shrink set) - allowed_protocol_mappers (rename + shrink set) - web_origins (rename + shrink set) The 3 config-less resources get rename-only update tests via a shared runConfiglessUpdate helper. 18 tests, ~9s against Keycloak 26.5.5. Signed-off-by: Xavier Lange <xrlange@gmail.com>
Signed-off-by: Xavier Lange <xrlange@gmail.com>
The registration-web-origins client registration policy was added in Keycloak commit be6a3814 (Oct 2025, shipped in 26.5.0). Acceptance tests on the upstream test matrix's older entries (26.0.x through 26.4.x) fail with "Invalid provider type or no such provider" when attempting to create a keycloak_client_registration_policy_web_origins resource. The simplest fix is to drop the resource from this PR. The remaining 7 resources are present in every Keycloak version in the matrix. The web_origins resource can be reintroduced in a follow-up once the project's minimum supported Keycloak version is bumped to 26.5+, or with a Keycloak-version-aware skip in the test. Signed-off-by: Xavier Lange <xrlange@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds first-class Terraform resources for managing Keycloak Client Registration Policy components (typed schema per built-in provider), backed by a small shared CRUD/import layer over the existing /components endpoint plumbing.
Changes:
- Introduces typed resources for several built-in client registration policy providers (trusted hosts, max clients, allowed scopes, allowed protocol mappers, and three config-less policies).
- Adds shared schema/import/delete helpers (including an importer guard to prevent cross-resource imports).
- Extends the Keycloak component model to support
subTypeand adds a Keycloak client API for CRUD on client registration policy components, with accompanying acceptance tests.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| provider/resource_keycloak_client_registration_policy_trusted_hosts.go | Implements the trusted-hosts client registration policy resource with typed config fields. |
| provider/resource_keycloak_client_registration_policy_trusted_hosts_test.go | Acceptance tests for trusted-hosts policy (basic/update/import mismatch). |
| provider/resource_keycloak_client_registration_policy_max_clients.go | Implements the max-clients policy resource with typed max_clients field. |
| provider/resource_keycloak_client_registration_policy_max_clients_test.go | Acceptance tests for max-clients policy (basic/update). |
| provider/resource_keycloak_client_registration_policy_helpers_test.go | Shared acceptance-test helpers for existence/destroy/import ID generation. |
| provider/resource_keycloak_client_registration_policy_full_scope_disallowed.go | Adds config-less wrapper resource for providerId scope. |
| provider/resource_keycloak_client_registration_policy_consent_required.go | Adds config-less wrapper resource for providerId consent-required. |
| provider/resource_keycloak_client_registration_policy_client_disabled.go | Adds config-less wrapper resource for providerId client-disabled. |
| provider/resource_keycloak_client_registration_policy_configless_test.go | Shared acceptance tests for the three config-less policy resources. |
| provider/resource_keycloak_client_registration_policy_allowed_protocol_mappers.go | Implements allowed-protocol-mappers policy resource with a typed set field. |
| provider/resource_keycloak_client_registration_policy_allowed_protocol_mappers_test.go | Acceptance tests for allowed-protocol-mappers policy (basic/update). |
| provider/resource_keycloak_client_registration_policy_allowed_client_scopes.go | Implements allowed-client-scopes policy resource (legacy providerId, modern config keys). |
| provider/resource_keycloak_client_registration_policy_allowed_client_scopes_test.go | Acceptance tests for allowed-client-scopes policy (basic/update). |
| provider/resource_keycloak_client_registration_policies_fullstack_test.go | Full-stack acceptance test asserting multiple policy resources can coexist. |
| provider/provider.go | Registers the new policy resources in the provider resource map. |
| provider/generic_client_registration_policy_helpers.go | Shared schema/importer/CRUD helpers for all client registration policy resources. |
| keycloak/component.go | Adds SubType to the internal component model for serialization. |
| keycloak/client_registration_policy.go | Adds Keycloak client model + CRUD methods for client registration policy components. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+49
to
+55
| "keycloak_client_registration_policy_trusted_hosts": resourceKeycloakClientRegistrationPolicyTrustedHosts(), | ||
| "keycloak_client_registration_policy_max_clients": resourceKeycloakClientRegistrationPolicyMaxClients(), | ||
| "keycloak_client_registration_policy_allowed_client_scopes": resourceKeycloakClientRegistrationPolicyAllowedClientScopes(), | ||
| "keycloak_client_registration_policy_allowed_protocol_mappers": resourceKeycloakClientRegistrationPolicyAllowedProtocolMappers(), | ||
| "keycloak_client_registration_policy_consent_required": resourceKeycloakClientRegistrationPolicyConsentRequired(), | ||
| "keycloak_client_registration_policy_full_scope_disallowed": resourceKeycloakClientRegistrationPolicyFullScopeDisallowed(), | ||
| "keycloak_client_registration_policy_client_disabled": resourceKeycloakClientRegistrationPolicyClientDisabled(), |
Comment on lines
+11
to
+14
| // TestAccKeycloakClientRegistrationPolicies_fullStack declares all 7 typed | ||
| // policy resources in a single config (4 anonymous, 3 authenticated) and | ||
| // verifies that apply + destroy succeed with no resource interfering with | ||
| // any other. |
This was referenced Jun 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds one typed Terraform resource per built-in Keycloak Client Registration
Policy provider, replacing the need for users to manage these via a generic
keycloak_generic_component-style escape hatch. Closes #715.Eight resources, one per built-in policy factory in
org.keycloak.services.clientregistration.policy.impl:keycloak_client_registration_policy_trusted_hoststrusted-hostskeycloak_client_registration_policy_max_clientsmax-clientskeycloak_client_registration_policy_allowed_client_scopesallowed-client-templates(legacy provider id; config keys use modernscopesnaming)keycloak_client_registration_policy_allowed_protocol_mappersallowed-protocol-mapperskeycloak_client_registration_policy_web_originsregistration-web-originskeycloak_client_registration_policy_consent_requiredconsent-requiredkeycloak_client_registration_policy_full_scope_disallowedscope(resource named after the admin UI label)keycloak_client_registration_policy_client_disabledclient-disabledDesign notes
config = map(string)escape hatch. Each resourceexposes the policy's config keys as typed schema fields:
trusted_hosts = ["127.0.0.1", "localhost"], notconfig = { "trusted-hosts" = jsonencode(["127.0.0.1", "localhost"]) }.Real plan diffs, schema validation at plan time, per-resource docs.
keycloak_realm_keystore_*— one typed resource perComponent subtype, sharing a small CRUD layer over the existing
components endpoint plumbing in
*KeycloakClient.TypeSet— Keycloak's components APIdoes not preserve list order on round-trip, so
TypeListwould producespurious diffs. Affects
trusted_hosts,web_origins,allowed_client_scopes,allowed_protocol_mapper_types.keycloak/component.go: add aSubType stringjson:"subType,omitempty"field on the package-privatecomponentstruct. Required because Client Registration Policies use the ComponentsubTypefield (anonymous/authenticated); existing keystore / LDAP / IdP-mapper components don't, andomitempty` makesthis a no-op for them.
Configless()factory inprovider/generic_client_registration_policy_helpers.go. They arebyte-for-byte identical except for one provider_id constant; ~250 lines
of mechanical copy-paste avoided.
importer parses
{realmId}/{componentId}and asserts the importedcomponent's providerId matches the resource type, so importing a
trusted-hostscomponent into amax_clientsresource fails with aclear error instead of silently corrupting state.
Test plan
18 acceptance tests, all passing against Keycloak 26.5.5 (~9s wall):
_basicfor each of the 8 resources (Create + ImportStateVerifyround-trip, exercising TypeBool/TypeInt/TypeSet schemas)
_updatefor each of the 8 resources (mutates every mutable field)_importMismatch— verifies the provider_id guard fires witha readable error
_fullStack— declares all 8 resources in a single config (4anonymous + 4 authenticated) to confirm they coexist cleanly
subType=authenticatedis exercised by all 3 config-less basictests + the fullstack test
Out of scope
keycloak_client_registration_policyfor custom SPI policies.Can be added separately; nothing in this PR forecloses it.
keycloak_generic_componentresource.prefer; let me know the preferred format/location.