Skip to content

CAMEL-23453: camel-keycloak - add federated identity linking operations#25091

Open
oscerd wants to merge 1 commit into
apache:mainfrom
oscerd:feature/CAMEL-23453-keycloak-federated-identity
Open

CAMEL-23453: camel-keycloak - add federated identity linking operations#25091
oscerd wants to merge 1 commit into
apache:mainfrom
oscerd:feature/CAMEL-23453-keycloak-federated-identity

Conversation

@oscerd

@oscerd oscerd commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

CAMEL-23453. The component exposed user CRUD but no federated-identity (IdP) link management. Those links are what connect a Keycloak user to an external identity-provider account (Google, GitHub, a SAML provider), so they matter for SSO scenarios and for provisioning users with pre-linked accounts during a migration.

Changes

Three new operations, implemented against the admin client's UserResource and following the existing producer pattern (header validation, then the admin-client call, then the response on the out-message):

Operation Admin client call Required headers
addFederatedIdentity addFederatedIdentity(alias, rep) realm, userId, CamelKeycloakIdentityProvider, CamelKeycloakFederatedUserId
removeFederatedIdentity removeFederatedIdentity(alias) realm, userId, CamelKeycloakIdentityProvider
getFederatedIdentities getFederatedIdentity() realm, userId

New header constants, as proposed on the issue: CamelKeycloakIdentityProvider, CamelKeycloakFederatedUserId, CamelKeycloakFederatedUsername.

getFederatedIdentities sets the body to the List<FederatedIdentityRepresentation>.

Two behaviours worth calling out for review:

  • Username fallback. CamelKeycloakFederatedUsername is optional; when it is absent the external user id is used as the username rather than creating the link with a blank one, since Keycloak surfaces that field in the admin console. A test pins this — happy to switch it to leaving the field null if you'd prefer.
  • Status checking. addFederatedIdentity returns a JAX-RS Response; the producer checks it and fails with the status code on a non-2xx (e.g. a 409 when the link already exists) instead of reporting success. The response is closed via try-with-resources.

Testing

KeycloakProducerFederatedIdentityTest9 tests, all passing — using the same mocked-admin-client approach as the existing KeycloakProducerTest:

  • add: the captured FederatedIdentityRepresentation carries the right provider/userId/username
  • add: username falls back to the external user id when not supplied
  • add: a non-2xx status fails the exchange and reports the code
  • remove: verifies removeFederatedIdentity is called with the alias
  • list: the body is the list of links
  • validation: missing realm, userId, identity provider, and federated user id each produce the documented error

Full reactor mvn clean install -DskipTests from root: success, with catalog and endpoint-DSL metadata regenerated.

Docs: a Federated Identity Operations section in keycloak-component.adoc with the header contract and Java/YAML examples.

Main-only (4.22.0), additive — no backport.

Claude Code on behalf of Andrea Cosentino (@oscerd).

🤖 Generated with Claude Code

The component exposed user CRUD but not federated-identity (IdP) link
management, which is needed for SSO scenarios where a Keycloak user is linked
to an external identity-provider account, and for provisioning users with
pre-linked accounts during a migration.

Add three operations backed by the admin client's UserResource:

- addFederatedIdentity - link a user to an identity provider account
- removeFederatedIdentity - unlink a user from an identity provider
- getFederatedIdentities - list all identity provider links for a user

New headers: CamelKeycloakIdentityProvider, CamelKeycloakFederatedUserId and
CamelKeycloakFederatedUsername. The username is optional: when it is not
supplied the external user id is used, so the link is never created with a
blank username (Keycloak surfaces that field in the admin console).

addFederatedIdentity checks the returned status and fails with the status code
when the link could not be created, rather than reporting success.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
@oscerd oscerd added the enhancement New feature or request label Jul 24, 2026
@oscerd
oscerd requested review from davsclaus and gnodet July 24, 2026 10:49
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-keycloak
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 11 tested, 27 compile-only — current: 9 all tested

Maveniverse Scalpel detected 38 affected modules (current approach: 9).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 11 modules (3 direct + 8 downstream), skip tests for 27 (generated code, meta-modules)

Modules Scalpel would test (11)
  • camel-catalog
  • camel-endpointdsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-keycloak
  • camel-launcher-container
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (27)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (38 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Keycloak
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, clean PR — the implementation follows the existing producer pattern well, the documentation is thorough with both Java and YAML examples, and the test coverage is solid with 9 tests. CI checks all pass.

A few suggestions below, mostly around test conventions. Nothing blocking.

This is a rules-and-conventions review. It does not replace specialized review tools such as CodeRabbit or Sourcery, or static analysis tools such as SonarCloud.

This review was generated by an AI agent (Claude Code on behalf of @davsclaus) and may contain inaccuracies. Please verify all suggestions before applying.

* Unit tests for the federated identity (identity provider link) operations.
*/
public class KeycloakProducerFederatedIdentityTest extends CamelTestSupport {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per project test conventions, new test classes should be package-private (no public modifier). Same applies to all @Test methods in this file. The existing KeycloakProducerTest uses public (pre-existing style), but new files should follow the current convention.

Suggested change
class KeycloakProducerFederatedIdentityTest extends CamelTestSupport {

assertTrue(e.getCause().getMessage().contains("409"),
"The failing status should be reported — was: " + e.getCause().getMessage());
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This try-catch pattern silently passes if the exception is never thrown — a broken status check would go unnoticed. The same pattern exists in KeycloakProducerTest, but for new code assertThrows is more robust:

Exception e = assertThrows(Exception.class, () ->
    template.sendBodyAndHeaders("direct:addFederatedIdentity", null,
            headers(KeycloakConstants.IDENTITY_PROVIDER, "google",
                    KeycloakConstants.FEDERATED_USER_ID, "google-123")));
assertThat(e.getCause().getMessage()).contains("409");

Same concern applies to testMissingIdentityProvider, testMissingFederatedUserId, testMissingUserId, and testMissingRealmName below.

verify(userResource).addFederatedIdentity(eq("google"), captor.capture());

FederatedIdentityRepresentation sent = captor.getValue();
assertEquals("google", sent.getIdentityProvider());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: project guidelines prefer AssertJ assertions (assertThat(...)) over JUnit assertions for new test code. The existing KeycloakProducerTest uses JUnit style, but new files should prefer the updated convention. For example:

assertThat(sent.getIdentityProvider()).isEqualTo("google");
assertThat(sent.getUserId()).isEqualTo("google-123");
assertThat(sent.getUserName()).isEqualTo("jane@example.com");

@oscerd
oscerd requested a review from davsclaus July 24, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants