| = Keycloak Component |
| :doctitle: Keycloak |
| :shortname: keycloak |
| :artifactid: camel-keycloak |
| :description: Manage Keycloak instances via Admin API. |
| :since: 4.15 |
| :supportlevel: Preview |
| :tabs-sync-option: |
| :component-header: Only producer is supported |
| //Manually maintained attributes |
| :camel-spring-boot-name: keycloak |
| |
| *Since Camel {since}* |
| |
| *{component-header}* |
| |
| The Keycloak component supports running operations on Keycloak instance and policy enforcements. |
| |
| == Component Features |
| |
| The Keycloak component provides two main functionalities: |
| |
| 1. **Producer Operations** - Manage Keycloak instances via the Admin API (realms, users, roles, clients) |
| 2. **Security Policies** - Route-level authorization using Keycloak authentication and authorization services |
| |
| |
| == URI Format |
| |
| ------------------------- |
| keycloak://label[?options] |
| ------------------------- |
| |
| You can append query options to the URI in the following format: |
| |
| `?options=value&option2=value&...` |
| |
| |
| // component-configure options: START |
| |
| // component-configure options: END |
| |
| // component options: START |
| include::partial$component-configure-options.adoc[] |
| include::partial$component-endpoint-options.adoc[] |
| // component options: END |
| |
| // endpoint options: START |
| |
| // endpoint options: END |
| |
| == Producer Operations |
| |
| The Keycloak producer supports administrative operations on Keycloak instances via the Admin API. |
| |
| === Configuration |
| |
| To use the producer, configure the Keycloak connection details: |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Configure Keycloak component |
| KeycloakComponent keycloak = context.getComponent("keycloak", KeycloakComponent.class); |
| KeycloakConfiguration config = new KeycloakConfiguration(); |
| config.setServerUrl("http://localhost:8080"); |
| config.setRealm("master"); |
| config.setUsername("admin"); |
| config.setPassword("admin"); |
| keycloak.setConfiguration(config); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Configuration in application.yaml |
| camel: |
| component: |
| keycloak: |
| server-url: "http://localhost:8080" |
| realm: "master" |
| username: "admin" |
| password: "admin" |
| ---- |
| ==== |
| |
| === Supported Operations |
| |
| The component supports the following operations: |
| |
| * **Realm Management**: `createRealm`, `getRealm`, `updateRealm`, `deleteRealm` |
| * **User Management**: `createUser`, `getUser`, `updateUser`, `listUsers`, `searchUsers`, `deleteUser` |
| * **Role Management**: `createRole`, `getRole`, `updateRole`, `listRoles`, `deleteRole`, `assignRoleToUser`, `removeRoleFromUser`, `getUserRoles` |
| * **Group Management**: `createGroup`, `getGroup`, `updateGroup`, `listGroups`, `deleteGroup`, `addUserToGroup`, `removeUserFromGroup`, `listUserGroups` |
| * **Client Management**: `createClient`, `getClient`, `updateClient`, `listClients`, `deleteClient` |
| * **Client Role Management**: `createClientRole`, `getClientRole`, `updateClientRole`, `listClientRoles`, `deleteClientRole`, `assignClientRoleToUser`, `removeClientRoleFromUser` |
| * **Password Management**: `resetUserPassword` |
| * **Session Management**: `listUserSessions`, `logoutUser` |
| * **Client Scope Management**: `createClientScope`, `getClientScope`, `updateClientScope`, `listClientScopes`, `deleteClientScope` |
| |
| === Realm Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a new realm |
| template.sendBodyAndHeader("keycloak:admin?operation=createRealm", null, |
| KeycloakConstants.REALM_NAME, "my-new-realm"); |
| |
| // Get realm information |
| template.sendBodyAndHeader("keycloak:admin?operation=getRealm", null, |
| KeycloakConstants.REALM_NAME, "my-realm"); |
| |
| // Delete a realm |
| template.sendBodyAndHeader("keycloak:admin?operation=deleteRealm", null, |
| KeycloakConstants.REALM_NAME, "my-old-realm"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:create-realm |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-new-realm" |
| - to: |
| uri: keycloak:admin?operation=createRealm |
| - log: "Created realm: ${header.CamelKeycloakRealmName}" |
| |
| - route: |
| from: |
| uri: direct:get-realm |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - to: |
| uri: keycloak:admin?operation=getRealm |
| - log: "Realm info: ${body}" |
| |
| - route: |
| from: |
| uri: direct:delete-realm |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-old-realm" |
| - to: |
| uri: keycloak:admin?operation=deleteRealm |
| - log: "Deleted realm: ${header.CamelKeycloakRealmName}" |
| ---- |
| ==== |
| |
| === User Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a new user |
| Map<String, Object> headers = new HashMap<>(); |
| headers.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| headers.put(KeycloakConstants.USERNAME, "john.doe"); |
| headers.put(KeycloakConstants.USER_EMAIL, "john.doe@example.com"); |
| headers.put(KeycloakConstants.USER_FIRST_NAME, "John"); |
| headers.put(KeycloakConstants.USER_LAST_NAME, "Doe"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createUser", null, headers); |
| |
| // Set user password |
| Map<String, Object> passwordHeaders = new HashMap<>(); |
| passwordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| passwordHeaders.put(KeycloakConstants.USERNAME, "john.doe"); |
| passwordHeaders.put("CamelKeycloakUserPassword", "secure-password"); |
| passwordHeaders.put("CamelKeycloakUserPasswordTemporary", false); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=setUserPassword", null, passwordHeaders); |
| |
| // List all users in realm |
| template.sendBodyAndHeader("keycloak:admin?operation=listUsers", null, |
| KeycloakConstants.REALM_NAME, "my-realm"); |
| |
| // Delete a user |
| Map<String, Object> deleteHeaders = new HashMap<>(); |
| deleteHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| deleteHeaders.put(KeycloakConstants.USERNAME, "john.doe"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=deleteUser", null, deleteHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create user route |
| - route: |
| from: |
| uri: direct:create-user |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUsername |
| simple: "${body[username]}" |
| - setHeader: |
| name: CamelKeycloakUserEmail |
| simple: "${body[email]}" |
| - setHeader: |
| name: CamelKeycloakUserFirstName |
| simple: "${body[firstName]}" |
| - setHeader: |
| name: CamelKeycloakUserLastName |
| simple: "${body[lastName]}" |
| - to: |
| uri: keycloak:admin?operation=createUser |
| - log: "Created user: ${header.CamelKeycloakUsername}" |
| |
| # Set user password route |
| - route: |
| from: |
| uri: direct:set-user-password |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUsername |
| simple: "${body[username]}" |
| - setHeader: |
| name: CamelKeycloakUserPassword |
| simple: "${body[password]}" |
| - setHeader: |
| name: CamelKeycloakUserPasswordTemporary |
| constant: false |
| - to: |
| uri: keycloak:admin?operation=setUserPassword |
| - log: "Set password for user: ${header.CamelKeycloakUsername}" |
| |
| # List users route |
| - route: |
| from: |
| uri: direct:list-users |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - to: |
| uri: keycloak:admin?operation=listUsers |
| - log: "Users in realm: ${body}" |
| |
| # Delete user route |
| - route: |
| from: |
| uri: direct:delete-user |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUsername |
| simple: "${body[username]}" |
| - to: |
| uri: keycloak:admin?operation=deleteUser |
| - log: "Deleted user: ${header.CamelKeycloakUsername}" |
| ---- |
| ==== |
| |
| === Role Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a new role |
| Map<String, Object> roleHeaders = new HashMap<>(); |
| roleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| roleHeaders.put(KeycloakConstants.ROLE_NAME, "manager"); |
| roleHeaders.put(KeycloakConstants.ROLE_DESCRIPTION, "Manager role with elevated privileges"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createRole", null, roleHeaders); |
| |
| // Get role information |
| Map<String, Object> getRoleHeaders = new HashMap<>(); |
| getRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| getRoleHeaders.put(KeycloakConstants.ROLE_NAME, "manager"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=getRole", null, getRoleHeaders); |
| |
| // Assign role to user |
| Map<String, Object> assignHeaders = new HashMap<>(); |
| assignHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| assignHeaders.put(KeycloakConstants.USERNAME, "john.doe"); |
| assignHeaders.put(KeycloakConstants.ROLE_NAME, "manager"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=assignRoleToUser", null, assignHeaders); |
| |
| // Delete a role |
| Map<String, Object> deleteRoleHeaders = new HashMap<>(); |
| deleteRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| deleteRoleHeaders.put(KeycloakConstants.ROLE_NAME, "old-role"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=deleteRole", null, deleteRoleHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create role route |
| - route: |
| from: |
| uri: direct:create-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - setHeader: |
| name: CamelKeycloakRoleDescription |
| simple: "${body[description]}" |
| - to: |
| uri: keycloak:admin?operation=createRole |
| - log: "Created role: ${header.CamelKeycloakRoleName}" |
| |
| # Get role route |
| - route: |
| from: |
| uri: direct:get-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - to: |
| uri: keycloak:admin?operation=getRole |
| - log: "Role info: ${body}" |
| |
| # Assign role to user route |
| - route: |
| from: |
| uri: direct:assign-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUsername |
| simple: "${body[username]}" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - to: |
| uri: keycloak:admin?operation=assignRoleToUser |
| - log: "Assigned role ${header.CamelKeycloakRoleName} to user ${header.CamelKeycloakUsername}" |
| |
| # Delete role route |
| - route: |
| from: |
| uri: direct:delete-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - to: |
| uri: keycloak:admin?operation=deleteRole |
| - log: "Deleted role: ${header.CamelKeycloakRoleName}" |
| ---- |
| ==== |
| |
| === Client Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a new client |
| Map<String, Object> clientHeaders = new HashMap<>(); |
| clientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| clientHeaders.put("CamelKeycloakClientId", "my-service-client"); |
| clientHeaders.put("CamelKeycloakClientSecretRequired", true); |
| clientHeaders.put("CamelKeycloakClientDirectAccessGrantsEnabled", true); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createClient", null, clientHeaders); |
| |
| // Get client information |
| Map<String, Object> getClientHeaders = new HashMap<>(); |
| getClientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| getClientHeaders.put("CamelKeycloakClientId", "my-service-client"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=getClient", null, getClientHeaders); |
| |
| // Get client secret |
| Map<String, Object> secretHeaders = new HashMap<>(); |
| secretHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| secretHeaders.put("CamelKeycloakClientId", "my-service-client"); |
| |
| String clientSecret = template.requestBodyAndHeaders("keycloak:admin?operation=getClientSecret", |
| null, secretHeaders, String.class); |
| |
| // Delete a client |
| Map<String, Object> deleteClientHeaders = new HashMap<>(); |
| deleteClientHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| deleteClientHeaders.put("CamelKeycloakClientId", "old-client"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=deleteClient", null, deleteClientHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create client route |
| - route: |
| from: |
| uri: direct:create-client |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientId |
| simple: "${body[clientId]}" |
| - setHeader: |
| name: CamelKeycloakClientSecretRequired |
| constant: true |
| - setHeader: |
| name: CamelKeycloakClientDirectAccessGrantsEnabled |
| constant: true |
| - to: |
| uri: keycloak:admin?operation=createClient |
| - log: "Created client: ${header.CamelKeycloakClientId}" |
| |
| # Get client route |
| - route: |
| from: |
| uri: direct:get-client |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientId |
| simple: "${body[clientId]}" |
| - to: |
| uri: keycloak:admin?operation=getClient |
| - log: "Client info: ${body}" |
| |
| # Get client secret route |
| - route: |
| from: |
| uri: direct:get-client-secret |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientId |
| simple: "${body[clientId]}" |
| - to: |
| uri: keycloak:admin?operation=getClientSecret |
| - log: "Client secret retrieved for: ${header.CamelKeycloakClientId}" |
| |
| # Delete client route |
| - route: |
| from: |
| uri: direct:delete-client |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientId |
| simple: "${body[clientId]}" |
| - to: |
| uri: keycloak:admin?operation=deleteClient |
| - log: "Deleted client: ${header.CamelKeycloakClientId}" |
| ---- |
| ==== |
| |
| === Group Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a new group |
| Map<String, Object> groupHeaders = new HashMap<>(); |
| groupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| groupHeaders.put(KeycloakConstants.GROUP_NAME, "developers"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createGroup", null, groupHeaders); |
| |
| // List all groups |
| template.sendBodyAndHeader("keycloak:admin?operation=listGroups", null, |
| KeycloakConstants.REALM_NAME, "my-realm"); |
| |
| // Add user to group |
| Map<String, Object> addToGroupHeaders = new HashMap<>(); |
| addToGroupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| addToGroupHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| addToGroupHeaders.put(KeycloakConstants.GROUP_ID, "group-id-456"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=addUserToGroup", null, addToGroupHeaders); |
| |
| // List user's groups |
| Map<String, Object> userGroupsHeaders = new HashMap<>(); |
| userGroupsHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| userGroupsHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=listUserGroups", null, userGroupsHeaders); |
| |
| // Delete a group |
| Map<String, Object> deleteGroupHeaders = new HashMap<>(); |
| deleteGroupHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| deleteGroupHeaders.put(KeycloakConstants.GROUP_ID, "group-id-456"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=deleteGroup", null, deleteGroupHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create group route |
| - route: |
| from: |
| uri: direct:create-group |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakGroupName |
| simple: "${body[groupName]}" |
| - to: |
| uri: keycloak:admin?operation=createGroup |
| - log: "Created group: ${header.CamelKeycloakGroupName}" |
| |
| # List groups route |
| - route: |
| from: |
| uri: direct:list-groups |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - to: |
| uri: keycloak:admin?operation=listGroups |
| - log: "Groups: ${body}" |
| |
| # Add user to group route |
| - route: |
| from: |
| uri: direct:add-user-to-group |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - setHeader: |
| name: CamelKeycloakGroupId |
| simple: "${body[groupId]}" |
| - to: |
| uri: keycloak:admin?operation=addUserToGroup |
| - log: "Added user ${header.CamelKeycloakUserId} to group" |
| |
| # List user groups route |
| - route: |
| from: |
| uri: direct:list-user-groups |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - to: |
| uri: keycloak:admin?operation=listUserGroups |
| - log: "User groups: ${body}" |
| ---- |
| ==== |
| |
| === Password Management Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Reset user password |
| Map<String, Object> passwordHeaders = new HashMap<>(); |
| passwordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| passwordHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| passwordHeaders.put(KeycloakConstants.USER_PASSWORD, "newSecurePassword123!"); |
| passwordHeaders.put(KeycloakConstants.PASSWORD_TEMPORARY, false); // User won't need to change password |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=resetUserPassword", null, passwordHeaders); |
| |
| // Reset with temporary password (user must change on first login) |
| Map<String, Object> tempPasswordHeaders = new HashMap<>(); |
| tempPasswordHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| tempPasswordHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| tempPasswordHeaders.put(KeycloakConstants.USER_PASSWORD, "tempPassword123"); |
| tempPasswordHeaders.put(KeycloakConstants.PASSWORD_TEMPORARY, true); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=resetUserPassword", null, tempPasswordHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Reset user password route |
| - route: |
| from: |
| uri: direct:reset-password |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - setHeader: |
| name: CamelKeycloakUserPassword |
| simple: "${body[password]}" |
| - setHeader: |
| name: CamelKeycloakPasswordTemporary |
| simple: "${body[temporary]}" |
| - to: |
| uri: keycloak:admin?operation=resetUserPassword |
| - log: "Password reset for user ${header.CamelKeycloakUserId}" |
| |
| # Reset with temporary password |
| - route: |
| from: |
| uri: direct:reset-temp-password |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - setHeader: |
| name: CamelKeycloakUserPassword |
| simple: "${body[password]}" |
| - setHeader: |
| name: CamelKeycloakPasswordTemporary |
| constant: true |
| - to: |
| uri: keycloak:admin?operation=resetUserPassword |
| - log: "Temporary password set for user ${header.CamelKeycloakUserId}" |
| ---- |
| ==== |
| |
| === User Search Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Search users by query |
| Map<String, Object> searchHeaders = new HashMap<>(); |
| searchHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| searchHeaders.put(KeycloakConstants.SEARCH_QUERY, "john"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=searchUsers", null, searchHeaders); |
| |
| // Search with pagination |
| Map<String, Object> paginatedSearchHeaders = new HashMap<>(); |
| paginatedSearchHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| paginatedSearchHeaders.put(KeycloakConstants.SEARCH_QUERY, "doe"); |
| paginatedSearchHeaders.put(KeycloakConstants.FIRST_RESULT, 0); |
| paginatedSearchHeaders.put(KeycloakConstants.MAX_RESULTS, 10); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=searchUsers", null, paginatedSearchHeaders); |
| |
| // Get user roles |
| Map<String, Object> rolesHeaders = new HashMap<>(); |
| rolesHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| rolesHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| |
| List<RoleRepresentation> roles = template.requestBodyAndHeaders( |
| "keycloak:admin?operation=getUserRoles", null, rolesHeaders, List.class); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Search users route |
| - route: |
| from: |
| uri: direct:search-users |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakSearchQuery |
| simple: "${body[query]}" |
| - to: |
| uri: keycloak:admin?operation=searchUsers |
| - log: "Search results: ${body}" |
| |
| # Search with pagination |
| - route: |
| from: |
| uri: direct:search-users-paginated |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakSearchQuery |
| simple: "${body[query]}" |
| - setHeader: |
| name: CamelKeycloakFirstResult |
| simple: "${body[offset]}" |
| - setHeader: |
| name: CamelKeycloakMaxResults |
| simple: "${body[limit]}" |
| - to: |
| uri: keycloak:admin?operation=searchUsers |
| - log: "Found ${body.size} users" |
| |
| # Get user roles route |
| - route: |
| from: |
| uri: direct:get-user-roles |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - to: |
| uri: keycloak:admin?operation=getUserRoles |
| - log: "User roles: ${body}" |
| ---- |
| ==== |
| |
| === Client Role Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create client role |
| Map<String, Object> clientRoleHeaders = new HashMap<>(); |
| clientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| clientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123"); |
| clientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin"); |
| clientRoleHeaders.put(KeycloakConstants.ROLE_DESCRIPTION, "Service administrator role"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createClientRole", null, clientRoleHeaders); |
| |
| // List client roles |
| Map<String, Object> listClientRolesHeaders = new HashMap<>(); |
| listClientRolesHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| listClientRolesHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=listClientRoles", null, listClientRolesHeaders); |
| |
| // Assign client role to user |
| Map<String, Object> assignClientRoleHeaders = new HashMap<>(); |
| assignClientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| assignClientRoleHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| assignClientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123"); |
| assignClientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=assignClientRoleToUser", null, assignClientRoleHeaders); |
| |
| // Remove client role from user |
| Map<String, Object> removeClientRoleHeaders = new HashMap<>(); |
| removeClientRoleHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| removeClientRoleHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| removeClientRoleHeaders.put(KeycloakConstants.CLIENT_UUID, "client-uuid-123"); |
| removeClientRoleHeaders.put(KeycloakConstants.ROLE_NAME, "service-admin"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=removeClientRoleFromUser", null, removeClientRoleHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create client role route |
| - route: |
| from: |
| uri: direct:create-client-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientUuid |
| simple: "${body[clientUuid]}" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - setHeader: |
| name: CamelKeycloakRoleDescription |
| simple: "${body[description]}" |
| - to: |
| uri: keycloak:admin?operation=createClientRole |
| - log: "Created client role: ${header.CamelKeycloakRoleName}" |
| |
| # List client roles route |
| - route: |
| from: |
| uri: direct:list-client-roles |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientUuid |
| simple: "${body[clientUuid]}" |
| - to: |
| uri: keycloak:admin?operation=listClientRoles |
| - log: "Client roles: ${body}" |
| |
| # Assign client role to user |
| - route: |
| from: |
| uri: direct:assign-client-role |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - setHeader: |
| name: CamelKeycloakClientUuid |
| simple: "${body[clientUuid]}" |
| - setHeader: |
| name: CamelKeycloakRoleName |
| simple: "${body[roleName]}" |
| - to: |
| uri: keycloak:admin?operation=assignClientRoleToUser |
| - log: "Assigned client role to user" |
| ---- |
| ==== |
| |
| === Session Management Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // List user sessions |
| Map<String, Object> sessionsHeaders = new HashMap<>(); |
| sessionsHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| sessionsHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| |
| List<UserSessionRepresentation> sessions = template.requestBodyAndHeaders( |
| "keycloak:admin?operation=listUserSessions", null, sessionsHeaders, List.class); |
| |
| // Logout user (invalidate all sessions) |
| Map<String, Object> logoutHeaders = new HashMap<>(); |
| logoutHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| logoutHeaders.put(KeycloakConstants.USER_ID, "user-id-123"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=logoutUser", null, logoutHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # List user sessions route |
| - route: |
| from: |
| uri: direct:list-user-sessions |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - to: |
| uri: keycloak:admin?operation=listUserSessions |
| - log: "User sessions: ${body}" |
| |
| # Logout user route |
| - route: |
| from: |
| uri: direct:logout-user |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakUserId |
| simple: "${body[userId]}" |
| - to: |
| uri: keycloak:admin?operation=logoutUser |
| - log: "User logged out: ${header.CamelKeycloakUserId}" |
| ---- |
| ==== |
| |
| === Client Scope Operations |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create client scope |
| Map<String, Object> scopeHeaders = new HashMap<>(); |
| scopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| scopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_NAME, "custom-scope"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=createClientScope", null, scopeHeaders); |
| |
| // List client scopes |
| template.sendBodyAndHeader("keycloak:admin?operation=listClientScopes", null, |
| KeycloakConstants.REALM_NAME, "my-realm"); |
| |
| // Get client scope |
| Map<String, Object> getScopeHeaders = new HashMap<>(); |
| getScopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| getScopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_ID, "scope-id-123"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=getClientScope", null, getScopeHeaders); |
| |
| // Delete client scope |
| Map<String, Object> deleteScopeHeaders = new HashMap<>(); |
| deleteScopeHeaders.put(KeycloakConstants.REALM_NAME, "my-realm"); |
| deleteScopeHeaders.put(KeycloakConstants.CLIENT_SCOPE_ID, "scope-id-123"); |
| |
| template.sendBodyAndHeaders("keycloak:admin?operation=deleteClientScope", null, deleteScopeHeaders); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Create client scope route |
| - route: |
| from: |
| uri: direct:create-client-scope |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientScopeName |
| simple: "${body[scopeName]}" |
| - to: |
| uri: keycloak:admin?operation=createClientScope |
| - log: "Created client scope: ${header.CamelKeycloakClientScopeName}" |
| |
| # List client scopes route |
| - route: |
| from: |
| uri: direct:list-client-scopes |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - to: |
| uri: keycloak:admin?operation=listClientScopes |
| - log: "Client scopes: ${body}" |
| |
| # Get client scope route |
| - route: |
| from: |
| uri: direct:get-client-scope |
| steps: |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-realm" |
| - setHeader: |
| name: CamelKeycloakClientScopeId |
| simple: "${body[scopeId]}" |
| - to: |
| uri: keycloak:admin?operation=getClientScope |
| - log: "Client scope: ${body}" |
| ---- |
| ==== |
| |
| === Complete Producer Example |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| public class KeycloakManagementRoutes extends RouteBuilder { |
| |
| @Override |
| public void configure() throws Exception { |
| |
| // Configure Keycloak component |
| KeycloakComponent keycloak = getContext().getComponent("keycloak", KeycloakComponent.class); |
| KeycloakConfiguration config = new KeycloakConfiguration(); |
| config.setServerUrl("http://localhost:8080"); |
| config.setRealm("master"); |
| config.setUsername("admin"); |
| config.setPassword("admin"); |
| keycloak.setConfiguration(config); |
| |
| // Comprehensive user management route |
| from("direct:setup-user-environment") |
| .routeId("setup-user-environment") |
| .log("Setting up user environment...") |
| |
| // Step 1: Create realm |
| .setHeader(KeycloakConstants.REALM_NAME, constant("my-company")) |
| .to("keycloak:admin?operation=createRealm") |
| .log("Created realm: my-company") |
| |
| // Step 2: Create roles |
| .setHeader(KeycloakConstants.ROLE_NAME, constant("admin")) |
| .setHeader(KeycloakConstants.ROLE_DESCRIPTION, constant("Administrator role")) |
| .to("keycloak:admin?operation=createRole") |
| .log("Created admin role") |
| |
| .setHeader(KeycloakConstants.ROLE_NAME, constant("user")) |
| .setHeader(KeycloakConstants.ROLE_DESCRIPTION, constant("Standard user role")) |
| .to("keycloak:admin?operation=createRole") |
| .log("Created user role") |
| |
| // Step 3: Create client |
| .setHeader("CamelKeycloakClientId", constant("my-app")) |
| .setHeader("CamelKeycloakClientSecretRequired", constant(true)) |
| .setHeader("CamelKeycloakClientDirectAccessGrantsEnabled", constant(true)) |
| .to("keycloak:admin?operation=createClient") |
| .log("Created client: my-app") |
| |
| // Step 4: Create users |
| .setHeader(KeycloakConstants.USERNAME, constant("admin.user")) |
| .setHeader(KeycloakConstants.USER_EMAIL, constant("admin@company.com")) |
| .setHeader(KeycloakConstants.USER_FIRST_NAME, constant("Admin")) |
| .setHeader(KeycloakConstants.USER_LAST_NAME, constant("User")) |
| .to("keycloak:admin?operation=createUser") |
| .log("Created admin user") |
| |
| // Step 5: Set password |
| .setHeader("CamelKeycloakUserPassword", constant("admin123")) |
| .setHeader("CamelKeycloakUserPasswordTemporary", constant(false)) |
| .to("keycloak:admin?operation=setUserPassword") |
| .log("Set admin user password") |
| |
| // Step 6: Assign role |
| .setHeader(KeycloakConstants.ROLE_NAME, constant("admin")) |
| .to("keycloak:admin?operation=assignRoleToUser") |
| .log("Assigned admin role to user") |
| |
| .transform().constant("User environment setup completed successfully"); |
| |
| // User management API routes |
| from("rest:post:/users") |
| .routeId("create-user-api") |
| .log("Creating user: ${body}") |
| .setHeader(KeycloakConstants.REALM_NAME, constant("my-company")) |
| .setHeader(KeycloakConstants.USERNAME, jsonpath("$.username")) |
| .setHeader(KeycloakConstants.USER_EMAIL, jsonpath("$.email")) |
| .setHeader(KeycloakConstants.USER_FIRST_NAME, jsonpath("$.firstName")) |
| .setHeader(KeycloakConstants.USER_LAST_NAME, jsonpath("$.lastName")) |
| .to("keycloak:admin?operation=createUser") |
| .setHeader("Content-Type", constant("application/json")) |
| .transform().constant("{\"status\": \"success\", \"message\": \"User created\"}"); |
| |
| from("rest:get:/users") |
| .routeId("list-users-api") |
| .log("Listing users") |
| .setHeader(KeycloakConstants.REALM_NAME, constant("my-company")) |
| .to("keycloak:admin?operation=listUsers") |
| .setHeader("Content-Type", constant("application/json")); |
| |
| from("rest:delete:/users/{username}") |
| .routeId("delete-user-api") |
| .log("Deleting user: ${header.username}") |
| .setHeader(KeycloakConstants.REALM_NAME, constant("my-company")) |
| .setHeader(KeycloakConstants.USERNAME, header("username")) |
| .to("keycloak:admin?operation=deleteUser") |
| .setHeader("Content-Type", constant("application/json")) |
| .transform().constant("{\"status\": \"success\", \"message\": \"User deleted\"}"); |
| } |
| } |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Complete Keycloak producer configuration |
| - route: |
| id: setup-user-environment |
| from: |
| uri: direct:setup-user-environment |
| steps: |
| - log: "Setting up user environment..." |
| |
| # Step 1: Create realm |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-company" |
| - to: |
| uri: keycloak:admin?operation=createRealm |
| - log: "Created realm: my-company" |
| |
| # Step 2: Create admin role |
| - setHeader: |
| name: CamelKeycloakRoleName |
| constant: "admin" |
| - setHeader: |
| name: CamelKeycloakRoleDescription |
| constant: "Administrator role" |
| - to: |
| uri: keycloak:admin?operation=createRole |
| - log: "Created admin role" |
| |
| # Step 3: Create user role |
| - setHeader: |
| name: CamelKeycloakRoleName |
| constant: "user" |
| - setHeader: |
| name: CamelKeycloakRoleDescription |
| constant: "Standard user role" |
| - to: |
| uri: keycloak:admin?operation=createRole |
| - log: "Created user role" |
| |
| # Step 4: Create client |
| - setHeader: |
| name: CamelKeycloakClientId |
| constant: "my-app" |
| - setHeader: |
| name: CamelKeycloakClientSecretRequired |
| constant: true |
| - setHeader: |
| name: CamelKeycloakClientDirectAccessGrantsEnabled |
| constant: true |
| - to: |
| uri: keycloak:admin?operation=createClient |
| - log: "Created client: my-app" |
| |
| # Step 5: Create admin user |
| - setHeader: |
| name: CamelKeycloakUsername |
| constant: "admin.user" |
| - setHeader: |
| name: CamelKeycloakUserEmail |
| constant: "admin@company.com" |
| - setHeader: |
| name: CamelKeycloakUserFirstName |
| constant: "Admin" |
| - setHeader: |
| name: CamelKeycloakUserLastName |
| constant: "User" |
| - to: |
| uri: keycloak:admin?operation=createUser |
| - log: "Created admin user" |
| |
| # Step 6: Set password |
| - setHeader: |
| name: CamelKeycloakUserPassword |
| constant: "admin123" |
| - setHeader: |
| name: CamelKeycloakUserPasswordTemporary |
| constant: false |
| - to: |
| uri: keycloak:admin?operation=setUserPassword |
| - log: "Set admin user password" |
| |
| # Step 7: Assign role |
| - setHeader: |
| name: CamelKeycloakRoleName |
| constant: "admin" |
| - to: |
| uri: keycloak:admin?operation=assignRoleToUser |
| - log: "Assigned admin role to user" |
| |
| - transform: |
| constant: "User environment setup completed successfully" |
| |
| # REST API routes for user management |
| - rest: |
| path: /users |
| post: |
| - to: direct:create-user-api |
| |
| - rest: |
| path: /users |
| get: |
| - to: direct:list-users-api |
| |
| - rest: |
| path: /users/{username} |
| delete: |
| - to: direct:delete-user-api |
| |
| # Route implementations |
| - route: |
| id: create-user-api |
| from: |
| uri: direct:create-user-api |
| steps: |
| - log: "Creating user: ${body}" |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-company" |
| - setHeader: |
| name: CamelKeycloakUsername |
| jsonpath: "$.username" |
| - setHeader: |
| name: CamelKeycloakUserEmail |
| jsonpath: "$.email" |
| - setHeader: |
| name: CamelKeycloakUserFirstName |
| jsonpath: "$.firstName" |
| - setHeader: |
| name: CamelKeycloakUserLastName |
| jsonpath: "$.lastName" |
| - to: |
| uri: keycloak:admin?operation=createUser |
| - setHeader: |
| name: Content-Type |
| constant: "application/json" |
| - transform: |
| constant: '{"status": "success", "message": "User created"}' |
| |
| - route: |
| id: list-users-api |
| from: |
| uri: direct:list-users-api |
| steps: |
| - log: "Listing users" |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-company" |
| - to: |
| uri: keycloak:admin?operation=listUsers |
| - setHeader: |
| name: Content-Type |
| constant: "application/json" |
| |
| - route: |
| id: delete-user-api |
| from: |
| uri: direct:delete-user-api |
| steps: |
| - log: "Deleting user: ${header.username}" |
| - setHeader: |
| name: CamelKeycloakRealmName |
| constant: "my-company" |
| - setHeader: |
| name: CamelKeycloakUsername |
| header: "username" |
| - to: |
| uri: keycloak:admin?operation=deleteUser |
| - setHeader: |
| name: Content-Type |
| constant: "application/json" |
| - transform: |
| constant: '{"status": "success", "message": "User deleted"}' |
| |
| # Component configuration |
| camel: |
| component: |
| keycloak: |
| server-url: "http://localhost:8080" |
| realm: "master" |
| username: "admin" |
| password: "admin" |
| ---- |
| ==== |
| |
| == Security Policies |
| |
| The Keycloak security policy provides route-level authorization using Keycloak authentication and authorization services. |
| |
| == Features |
| |
| The Keycloak security policy supports: |
| |
| * **Role-based authorization** - Validate user roles from Keycloak tokens |
| * **Permission-based authorization** - Validate fine-grained permissions using Keycloak Authorization Services |
| * **Token validation** - Verify access tokens from Keycloak |
| * **Flexible configuration** - Support for client credentials and resource owner password flows |
| |
| == Configuration |
| |
| === Basic Setup |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(); |
| policy.setServerUrl("http://localhost:8080"); |
| policy.setRealm("my-realm"); |
| policy.setClientId("my-client"); |
| policy.setClientSecret("my-client-secret"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:start |
| steps: |
| - policy: |
| ref: keycloakPolicy |
| - to: |
| uri: mock:result |
| |
| # Bean definition in beans configuration |
| beans: |
| - name: keycloakPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-realm" |
| clientId: "my-client" |
| clientSecret: "my-client-secret" |
| ---- |
| ==== |
| |
| === Role-based Authorization |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy( |
| "http://localhost:8080", "my-realm", "my-client", "client-secret"); |
| |
| // Require specific roles |
| policy.setRequiredRoles(Arrays.asList("admin", "user")); |
| policy.setAllRolesRequired(true); // User must have ALL roles |
| |
| from("direct:admin") |
| .policy(policy) |
| .to("mock:admin-endpoint"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:admin |
| steps: |
| - policy: |
| ref: adminPolicy |
| - to: |
| uri: mock:admin-endpoint |
| |
| # Bean definition |
| beans: |
| - name: adminPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-realm" |
| clientId: "my-client" |
| clientSecret: "client-secret" |
| requiredRoles: |
| - "admin" |
| - "user" |
| allRolesRequired: true |
| ---- |
| ==== |
| |
| === Permission-based Authorization |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy( |
| "http://localhost:8080", "my-realm", "my-client", "client-secret"); |
| |
| // Require specific permissions |
| policy.setRequiredPermissions(Arrays.asList("read:documents", "write:documents")); |
| policy.setAllPermissionsRequired(false); // User needs ANY permission |
| |
| from("direct:documents") |
| .policy(policy) |
| .to("mock:documents-endpoint"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:documents |
| steps: |
| - policy: |
| ref: documentsPolicy |
| - to: |
| uri: mock:documents-endpoint |
| |
| # Bean definition |
| beans: |
| - name: documentsPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-realm" |
| clientId: "my-client" |
| clientSecret: "client-secret" |
| requiredPermissions: |
| - "read:documents" |
| - "write:documents" |
| allPermissionsRequired: false |
| ---- |
| ==== |
| |
| === Resource Owner Password Credentials |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy( |
| "http://localhost:8080", "my-realm", "my-client", "username", "password"); |
| |
| from("direct:user-flow") |
| .policy(policy) |
| .to("mock:result"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:user-flow |
| steps: |
| - policy: |
| ref: userFlowPolicy |
| - to: |
| uri: mock:result |
| |
| # Bean definition |
| beans: |
| - name: userFlowPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-realm" |
| clientId: "my-client" |
| username: "username" |
| password: "password" |
| useResourceOwnerPasswordCredentials: true |
| ---- |
| ==== |
| |
| == Usage |
| |
| === Providing Access Tokens |
| |
| The security policy expects access tokens to be provided in one of the following ways: |
| |
| 1. **Header**: `CamelKeycloakAccessToken` |
| 2. **Authorization Header**: `Authorization: Bearer <token>` |
| 3. **Exchange Property**: `CamelKeycloakAccessToken` |
| |
| [source,java] |
| ---- |
| // Using header |
| template.sendBodyAndHeader("direct:protected", "message", |
| KeycloakSecurityConstants.ACCESS_TOKEN_HEADER, accessToken); |
| |
| // Using Authorization header |
| template.sendBodyAndHeader("direct:protected", "message", |
| "Authorization", "Bearer " + accessToken); |
| ---- |
| |
| === Route Examples |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| from("direct:admin-only") |
| .policy(adminPolicy) |
| .transform().constant("Admin access granted") |
| .to("mock:admin"); |
| |
| from("direct:user-or-admin") |
| .policy(userPolicy) |
| .transform().constant("User access granted") |
| .to("mock:user"); |
| |
| from("rest:get:/api/documents") |
| .policy(documentsPolicy) |
| .to("direct:list-documents"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:admin-only |
| steps: |
| - policy: |
| ref: adminPolicy |
| - transform: |
| constant: "Admin access granted" |
| - to: |
| uri: mock:admin |
| |
| - route: |
| from: |
| uri: direct:user-or-admin |
| steps: |
| - policy: |
| ref: userPolicy |
| - transform: |
| constant: "User access granted" |
| - to: |
| uri: mock:user |
| |
| - rest: |
| get: |
| - uri: /api/documents |
| to: direct:list-documents |
| route: |
| policy: |
| ref: documentsPolicy |
| ---- |
| ==== |
| |
| == Configuration Options |
| |
| [width="100%",cols="10%,10%,80%",options="header"] |
| |=== |
| | Name | Default | Description |
| |
| | serverUrl | | Keycloak server URL (e.g., http://localhost:8080) |
| | realm | | Keycloak realm name |
| | clientId | | Keycloak client ID |
| | clientSecret | | Keycloak client secret (for client credentials flow) |
| | username | | Username (for resource owner password flow) |
| | password | | Password (for resource owner password flow) |
| | requiredRoles | [] | List of required roles |
| | requiredPermissions | [] | List of required permissions |
| | allRolesRequired | true | Whether ALL roles are required (true) or ANY role (false) |
| | allPermissionsRequired | true | Whether ALL permissions are required (true) or ANY permission (false) |
| | useResourceOwnerPasswordCredentials | false | Whether to use resource owner password flow |
| |=== |
| |
| == Security Considerations |
| |
| * Always use HTTPS in production environments |
| * Store client secrets securely (environment variables, secret management systems) |
| * Regularly rotate client secrets and user passwords |
| * Use the principle of least privilege when assigning roles and permissions |
| * Consider token expiration and refresh strategies |
| |
| == Error Handling |
| |
| The component throws `CamelAuthorizationException` when: |
| |
| * Access token is missing or invalid |
| * User doesn't have required roles |
| * User doesn't have required permissions |
| * Keycloak server is unreachable |
| * Token verification fails |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| onException(CamelAuthorizationException.class) |
| .handled(true) |
| .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403)) |
| .transform().constant("Access denied"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - onException: |
| exception: |
| - "org.apache.camel.CamelAuthorizationException" |
| handled: true |
| steps: |
| - setHeader: |
| name: "CamelHttpResponseCode" |
| constant: 403 |
| - transform: |
| constant: "Access denied" |
| ---- |
| ==== |
| |
| == Examples |
| |
| === Basic Role-based Authorization |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create Keycloak security policy |
| KeycloakSecurityPolicy keycloakPolicy = new KeycloakSecurityPolicy(); |
| keycloakPolicy.setServerUrl("http://localhost:8080"); |
| keycloakPolicy.setRealm("my-company"); |
| keycloakPolicy.setClientId("my-service"); |
| keycloakPolicy.setClientSecret("client-secret-value"); |
| |
| // Require admin role |
| keycloakPolicy.setRequiredRoles(Arrays.asList("admin")); |
| |
| // Apply to route |
| from("direct:admin-endpoint") |
| .policy(keycloakPolicy) |
| .transform().constant("Admin access granted") |
| .to("mock:admin-result"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:admin-endpoint |
| steps: |
| - policy: |
| ref: keycloakPolicy |
| - transform: |
| constant: "Admin access granted" |
| - to: |
| uri: mock:admin-result |
| |
| # Bean definition |
| beans: |
| - name: keycloakPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-company" |
| clientId: "my-service" |
| clientSecret: "client-secret-value" |
| requiredRoles: |
| - "admin" |
| ---- |
| ==== |
| |
| === Multiple Role Authorization |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Require either admin OR user role |
| KeycloakSecurityPolicy userPolicy = new KeycloakSecurityPolicy( |
| "http://localhost:8080", "my-company", "my-service", "client-secret"); |
| userPolicy.setRequiredRoles(Arrays.asList("admin", "user")); |
| userPolicy.setAllRolesRequired(false); // ANY role (OR logic) |
| |
| from("direct:user-endpoint") |
| .policy(userPolicy) |
| .to("bean:userService?method=processUser"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:user-endpoint |
| steps: |
| - policy: |
| ref: userPolicy |
| - to: |
| uri: bean:userService?method=processUser |
| |
| # Bean definition |
| beans: |
| - name: userPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "http://localhost:8080" |
| realm: "my-company" |
| clientId: "my-service" |
| clientSecret: "client-secret" |
| requiredRoles: |
| - "admin" |
| - "user" |
| allRolesRequired: false |
| ---- |
| ==== |
| |
| === REST API with Keycloak Protection |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Configure different policies for different endpoints |
| KeycloakSecurityPolicy readPolicy = new KeycloakSecurityPolicy( |
| "{{keycloak.server-url}}", "{{keycloak.realm}}", |
| "{{keycloak.client-id}}", "{{keycloak.client-secret}}"); |
| readPolicy.setRequiredRoles(Arrays.asList("reader", "writer", "admin")); |
| readPolicy.setAllRolesRequired(false); |
| |
| KeycloakSecurityPolicy writePolicy = new KeycloakSecurityPolicy( |
| "{{keycloak.server-url}}", "{{keycloak.realm}}", |
| "{{keycloak.client-id}}", "{{keycloak.client-secret}}"); |
| writePolicy.setRequiredRoles(Arrays.asList("writer", "admin")); |
| writePolicy.setAllRolesRequired(false); |
| |
| KeycloakSecurityPolicy adminPolicy = new KeycloakSecurityPolicy( |
| "{{keycloak.server-url}}", "{{keycloak.realm}}", |
| "{{keycloak.client-id}}", "{{keycloak.client-secret}}"); |
| adminPolicy.setRequiredRoles(Arrays.asList("admin")); |
| |
| // Configure REST endpoints |
| rest("/api") |
| .get("/documents") |
| .route() |
| .policy(readPolicy) |
| .to("bean:documentService?method=listDocuments") |
| .endRest() |
| .post("/documents") |
| .route() |
| .policy(writePolicy) |
| .to("bean:documentService?method=createDocument") |
| .endRest() |
| .delete("/documents/{id}") |
| .route() |
| .policy(adminPolicy) |
| .to("bean:documentService?method=deleteDocument") |
| .endRest(); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - rest: |
| path: "/api" |
| get: |
| - uri: "/documents" |
| to: bean:documentService?method=listDocuments |
| route: |
| policy: |
| ref: readPolicy |
| post: |
| - uri: "/documents" |
| to: bean:documentService?method=createDocument |
| route: |
| policy: |
| ref: writePolicy |
| delete: |
| - uri: "/documents/{id}" |
| to: bean:documentService?method=deleteDocument |
| route: |
| policy: |
| ref: adminPolicy |
| |
| # Bean definitions for policies |
| beans: |
| - name: readPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "reader" |
| - "writer" |
| - "admin" |
| allRolesRequired: false |
| |
| - name: writePolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "writer" |
| - "admin" |
| allRolesRequired: false |
| |
| - name: adminPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "admin" |
| ---- |
| ==== |
| |
| === Sending Requests with Tokens |
| |
| [source,java] |
| ---- |
| // In your client code, include the access token |
| String accessToken = "eyJhbGciOiJSUzI1NiIsInR5cC..."; // From Keycloak |
| |
| // Option 1: Using custom header |
| template.sendBodyAndHeader("direct:protected-endpoint", |
| requestBody, |
| KeycloakSecurityConstants.ACCESS_TOKEN_HEADER, |
| accessToken); |
| |
| // Option 2: Using Authorization header (standard) |
| template.sendBodyAndHeader("direct:protected-endpoint", |
| requestBody, |
| "Authorization", |
| "Bearer " + accessToken); |
| |
| // Option 3: Using exchange property |
| Exchange exchange = ExchangeBuilder.anExchange(camelContext) |
| .withBody(requestBody) |
| .withProperty(KeycloakSecurityConstants.ACCESS_TOKEN_PROPERTY, accessToken) |
| .build(); |
| template.send("direct:protected-endpoint", exchange); |
| ---- |
| |
| === Advanced Error Handling |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Global error handler for authorization failures |
| onException(CamelAuthorizationException.class) |
| .handled(true) |
| .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403)) |
| .setHeader("Content-Type", constant("application/json")) |
| .transform().constant("{\"error\": \"Access denied\", \"message\": \"Insufficient privileges\"}") |
| .log("Authorization failed: ${exception.message}"); |
| |
| // Route-specific error handling |
| from("rest:post:/secure-data") |
| .doTry() |
| .policy(keycloakPolicy) |
| .to("bean:dataProcessor") |
| .doCatch(CamelAuthorizationException.class) |
| .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403)) |
| .transform().constant("Access denied") |
| .end(); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Global error handler |
| - onException: |
| exception: |
| - "org.apache.camel.CamelAuthorizationException" |
| handled: true |
| steps: |
| - setHeader: |
| name: "CamelHttpResponseCode" |
| constant: 403 |
| - setHeader: |
| name: "Content-Type" |
| constant: "application/json" |
| - transform: |
| constant: '{"error": "Access denied", "message": "Insufficient privileges"}' |
| - log: "Authorization failed: ${exception.message}" |
| |
| # Route-specific error handling |
| - route: |
| from: |
| uri: rest:post:/secure-data |
| steps: |
| - doTry: |
| steps: |
| - policy: |
| ref: keycloakPolicy |
| - to: |
| uri: bean:dataProcessor |
| doCatch: |
| - exception: |
| - "org.apache.camel.CamelAuthorizationException" |
| steps: |
| - setHeader: |
| name: "CamelHttpResponseCode" |
| constant: 403 |
| - transform: |
| constant: "Access denied" |
| ---- |
| ==== |
| |
| === Configuration Properties |
| |
| [tabs] |
| ==== |
| Properties:: |
| + |
| [source,properties] |
| ---- |
| # application.properties |
| keycloak.server-url=http://localhost:8080 |
| keycloak.realm=my-company |
| keycloak.client-id=my-service |
| keycloak.client-secret=your-client-secret |
| ---- |
| |
| === Spring Configuration |
| |
| [source,java] |
| ---- |
| @Configuration |
| public class SecurityConfiguration { |
| |
| @Value("${keycloak.server-url}") |
| private String serverUrl; |
| |
| @Value("${keycloak.realm}") |
| private String realm; |
| |
| @Value("${keycloak.client-id}") |
| private String clientId; |
| |
| @Value("${keycloak.client-secret}") |
| private String clientSecret; |
| |
| @Bean |
| public KeycloakSecurityPolicy adminPolicy() { |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy( |
| serverUrl, realm, clientId, clientSecret); |
| policy.setRequiredRoles(Arrays.asList("admin")); |
| return policy; |
| } |
| |
| @Bean |
| public KeycloakSecurityPolicy userPolicy() { |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy( |
| serverUrl, realm, clientId, clientSecret); |
| policy.setRequiredRoles(Arrays.asList("user", "admin")); |
| policy.setAllRolesRequired(false); |
| return policy; |
| } |
| } |
| ---- |
| |
| === Complete Route Configuration |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| public class KeycloakSecurityRoutes extends RouteBuilder { |
| |
| @Override |
| public void configure() throws Exception { |
| |
| // Admin policy - requires admin role |
| KeycloakSecurityPolicy adminPolicy = new KeycloakSecurityPolicy( |
| "{{keycloak.server-url}}", "{{keycloak.realm}}", |
| "{{keycloak.client-id}}", "{{keycloak.client-secret}}"); |
| adminPolicy.setRequiredRoles(Arrays.asList("admin")); |
| |
| // User policy - requires user or admin role |
| KeycloakSecurityPolicy userPolicy = new KeycloakSecurityPolicy( |
| "{{keycloak.server-url}}", "{{keycloak.realm}}", |
| "{{keycloak.client-id}}", "{{keycloak.client-secret}}"); |
| userPolicy.setRequiredRoles(Arrays.asList("user", "admin")); |
| userPolicy.setAllRolesRequired(false); // ANY role |
| |
| // Error handling |
| onException(CamelAuthorizationException.class) |
| .handled(true) |
| .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403)) |
| .transform().constant("Forbidden"); |
| |
| // Routes |
| from("rest:get:/admin/users") |
| .policy(adminPolicy) |
| .to("bean:userService?method=getAllUsers"); |
| |
| from("rest:get:/profile") |
| .policy(userPolicy) |
| .to("bean:userService?method=getCurrentUser"); |
| } |
| } |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| # Complete route configuration with Keycloak security |
| - onException: |
| exception: |
| - "org.apache.camel.CamelAuthorizationException" |
| handled: true |
| steps: |
| - setHeader: |
| name: "CamelHttpResponseCode" |
| constant: 403 |
| - transform: |
| constant: "Forbidden" |
| |
| - route: |
| from: |
| uri: rest:get:/admin/users |
| steps: |
| - policy: |
| ref: adminPolicy |
| - to: |
| uri: bean:userService?method=getAllUsers |
| |
| - route: |
| from: |
| uri: rest:get:/profile |
| steps: |
| - policy: |
| ref: userPolicy |
| - to: |
| uri: bean:userService?method=getCurrentUser |
| |
| # Security policy beans |
| beans: |
| - name: adminPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "admin" |
| |
| - name: userPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "user" |
| - "admin" |
| allRolesRequired: false |
| ---- |
| |
| == Running Integration Tests |
| |
| The component includes integration tests that require a running Keycloak instance. These tests are disabled by default and only run when specific system properties are provided. |
| |
| The integration tests include comprehensive testing for: |
| * Role-based authorization with different role requirements |
| * Permission-based authorization using custom claims and scopes |
| * Public key verification with JWKS endpoint integration |
| * Combined roles and permissions validation |
| * Token parsing with and without public key verification |
| * Different authorization header formats (Bearer token, custom header) |
| * Token expiration and validity checks |
| * Error handling for invalid tokens and insufficient privileges |
| |
| === Starting Keycloak with Docker |
| |
| ==== 1. Start Keycloak Container |
| |
| [source,bash] |
| ---- |
| # Start Keycloak in development mode |
| docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \ |
| quay.io/keycloak/keycloak:latest start-dev |
| ---- |
| |
| ==== 2. Access Keycloak Admin Console |
| |
| Open your browser to http://localhost:8080/admin and login with: |
| - Username: `admin` |
| - Password: `admin` |
| |
| === Keycloak Configuration for Integration Tests |
| |
| ==== 3. Create Test Realm |
| |
| 1. In the Keycloak Admin Console, click **"Add realm"** |
| 2. Set realm name to: `test-realm` |
| 3. Click **"Create"** |
| |
| ==== 4. Create Test Client |
| |
| 1. In the `test-realm`, go to **Clients** → **"Create client"** |
| 2. Set the following: |
| - Client type: `OpenID Connect` |
| - Client ID: `test-client` |
| - Next → Client authentication: `ON` |
| - Authorization: `ON` (optional, for advanced features) |
| - Next → Valid redirect URIs: `*` |
| - Click **"Save"** |
| 3. Go to **Credentials** tab and copy the **Client Secret** |
| |
| ==== 5. Create Test Roles |
| |
| 1. Go to **Realm roles** → **"Create role"** |
| 2. Create the following roles: |
| - `admin-role` |
| - `user` |
| - `reader` |
| |
| ==== 6. Create Test Users |
| |
| Create three test users with the following configuration: |
| |
| **User 1: myuser** |
| 1. Go to **Users** → **"Add user"** |
| 2. Set: |
| - Username: `myuser` |
| - Email: `myuser@test.com` |
| - First name: `My` |
| - Last name: `User` |
| - Click **"Create"** |
| 3. Go to **Credentials** tab → **"Set password"** |
| - Password: `pippo123` |
| - Temporary: `OFF` |
| 4. Go to **Role mapping** tab → **"Assign role"** |
| - Assign role: `admin-role` |
| |
| **User 2: test-user** |
| 1. Create user with: |
| - Username: `test-user` |
| - Password: `user123` (temporary: OFF) |
| - Assign role: `user` |
| |
| **User 3: reader-user** |
| 1. Create user with: |
| - Username: `reader-user` |
| - Password: `reader123` (temporary: OFF) |
| - Assign role: `reader` |
| |
| === Running the Integration Tests |
| |
| ==== 7. Execute Tests with Maven |
| |
| **Run All Integration Tests:** |
| [source,bash] |
| ---- |
| # Run integration tests with required properties |
| mvn test -Dtest=KeycloakSecurityIT \ |
| -Dkeycloak.server.url=http://localhost:8080 \ |
| -Dkeycloak.realm=test-realm \ |
| -Dkeycloak.client.id=test-client \ |
| -Dkeycloak.client.secret=YOUR_CLIENT_SECRET |
| ---- |
| |
| **Run Specific Test Categories:** |
| [source,bash] |
| ---- |
| # Test only role-based authorization |
| mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithValidAdminToken,testKeycloakSecurityPolicyWithValidUserToken,testKeycloakSecurityPolicyUserCannotAccessAdminRoute \ |
| -Dkeycloak.server.url=http://localhost:8080 \ |
| -Dkeycloak.realm=test-realm \ |
| -Dkeycloak.client.id=test-client \ |
| -Dkeycloak.client.secret=YOUR_CLIENT_SECRET |
| |
| # Test only permissions-based authorization |
| mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithPermissions,testKeycloakSecurityPolicyWithScopeBasedPermissions,testKeycloakSecurityPolicyWithCombinedRolesAndPermissions \ |
| -Dkeycloak.server.url=http://localhost:8080 \ |
| -Dkeycloak.realm=test-realm \ |
| -Dkeycloak.client.id=test-client \ |
| -Dkeycloak.client.secret=YOUR_CLIENT_SECRET |
| |
| # Test only public key verification |
| mvn test -Dtest=KeycloakSecurityIT#testKeycloakSecurityPolicyWithPublicKeyVerification,testParseTokenDirectlyWithPublicKey \ |
| -Dkeycloak.server.url=http://localhost:8080 \ |
| -Dkeycloak.realm=test-realm \ |
| -Dkeycloak.client.id=test-client \ |
| -Dkeycloak.client.secret=YOUR_CLIENT_SECRET |
| ---- |
| |
| Replace `YOUR_CLIENT_SECRET` with the actual client secret from step 4. |
| |
| ==== 8. Alternative: Set Environment Variables |
| |
| [source,bash] |
| ---- |
| export KEYCLOAK_SERVER_URL=http://localhost:8080 |
| export KEYCLOAK_REALM=test-realm |
| export KEYCLOAK_CLIENT_ID=test-client |
| export KEYCLOAK_CLIENT_SECRET=YOUR_CLIENT_SECRET |
| |
| # Run tests |
| mvn test -Dtest=KeycloakSecurityIT \ |
| -Dkeycloak.server.url=$KEYCLOAK_SERVER_URL \ |
| -Dkeycloak.realm=$KEYCLOAK_REALM \ |
| -Dkeycloak.client.id=$KEYCLOAK_CLIENT_ID \ |
| -Dkeycloak.client.secret=$KEYCLOAK_CLIENT_SECRET |
| ---- |
| |
| === Troubleshooting |
| |
| **Tests are skipped**: Verify all four required properties are provided and Keycloak is running on the specified URL. |
| |
| **401 Unauthorized**: Check that: |
| - Users exist with correct passwords |
| - Users have the required roles assigned |
| - Client credentials are correct |
| |
| **Connection refused**: Ensure Keycloak is running and accessible at the specified URL. |
| |
| **Token validation errors**: Verify the realm name and client configuration match exactly. |
| |
| === Setting up Permissions in Keycloak |
| |
| For permissions-based authorization, you have several options to include permissions in tokens: |
| |
| ==== Option 1: Custom Claims Mapper |
| |
| 1. In your realm, go to **Client Scopes** → **roles** → **Mappers** → **Create mapper** |
| 2. Set the following: |
| - Mapper Type: `User Attribute` |
| - Name: `permissions-mapper` |
| - User Attribute: `permissions` |
| - Token Claim Name: `permissions` |
| - Claim JSON Type: `JSON` |
| - Add to ID token: `ON` |
| - Add to access token: `ON` |
| |
| 3. Add the `permissions` attribute to users: |
| - Go to **Users** → Select user → **Attributes** tab |
| - Add attribute: `permissions` with value like `["read:documents", "write:documents"]` |
| |
| ==== Option 2: Scope-based Permissions |
| |
| 1. Configure client scopes: |
| - Go to **Client Scopes** → **Create client scope** |
| - Scope Name: `documents` |
| - Protocol: `openid-connect` |
| |
| 2. Add scope to client: |
| - Go to **Clients** → Your client → **Client Scopes** tab |
| - Add the scope as **Default** or **Optional** |
| |
| 3. In your application code, you can then use scopes as permissions: |
| |
| [source,java] |
| ---- |
| KeycloakSecurityPolicy policy = new KeycloakSecurityPolicy(); |
| policy.setRequiredPermissions(Arrays.asList("documents", "users", "admin")); |
| policy.setAllPermissionsRequired(false); // ANY permission |
| ---- |
| |
| ==== Option 3: Authorization Services (Advanced) |
| |
| For complex permission models, enable Keycloak Authorization Services: |
| |
| 1. Go to **Clients** → Your client → **Settings** → **Authorization Enabled**: `ON` |
| 2. Configure Resources, Scopes, and Policies in the **Authorization** tab |
| 3. Enable **Authorization** on the client |
| |
| Note: Full Authorization Services integration requires additional setup and is more complex than the simple approaches above. |
| |
| === Combined Roles and Permissions Example |
| |
| [tabs] |
| ==== |
| Java:: |
| + |
| [source,java] |
| ---- |
| // Create a policy that requires BOTH roles AND permissions |
| KeycloakSecurityPolicy strictPolicy = new KeycloakSecurityPolicy(); |
| strictPolicy.setServerUrl("{{keycloak.server-url}}"); |
| strictPolicy.setRealm("{{keycloak.realm}}"); |
| strictPolicy.setClientId("{{keycloak.client-id}}"); |
| strictPolicy.setClientSecret("{{keycloak.client-secret}}"); |
| |
| // User must have admin role AND document permissions |
| strictPolicy.setRequiredRoles(Arrays.asList("admin")); |
| strictPolicy.setRequiredPermissions(Arrays.asList("read:documents", "write:documents")); |
| strictPolicy.setAllRolesRequired(true); |
| strictPolicy.setAllPermissionsRequired(false); // ANY permission |
| |
| // Create a policy that requires EITHER roles OR permissions |
| KeycloakSecurityPolicy flexiblePolicy = new KeycloakSecurityPolicy(); |
| flexiblePolicy.setServerUrl("{{keycloak.server-url}}"); |
| flexiblePolicy.setRealm("{{keycloak.realm}}"); |
| flexiblePolicy.setClientId("{{keycloak.client-id}}"); |
| flexiblePolicy.setClientSecret("{{keycloak.client-secret}}"); |
| |
| // Apply different policies to different routes |
| from("direct:admin-documents") |
| .policy(strictPolicy) |
| .to("bean:documentService?method=adminOperations"); |
| |
| from("direct:flexible-access") |
| .policy(flexiblePolicy) |
| .to("bean:documentService?method=flexibleOperations"); |
| ---- |
| |
| YAML:: |
| + |
| [source,yaml] |
| ---- |
| - route: |
| from: |
| uri: direct:admin-documents |
| steps: |
| - policy: |
| ref: strictPolicy |
| - to: |
| uri: bean:documentService?method=adminOperations |
| |
| - route: |
| from: |
| uri: direct:flexible-access |
| steps: |
| - policy: |
| ref: flexiblePolicy |
| - to: |
| uri: bean:documentService?method=flexibleOperations |
| |
| # Bean definitions |
| beans: |
| - name: strictPolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "admin" |
| requiredPermissions: |
| - "read:documents" |
| - "write:documents" |
| allRolesRequired: true |
| allPermissionsRequired: false |
| |
| - name: flexiblePolicy |
| type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy |
| properties: |
| serverUrl: "{{keycloak.server-url}}" |
| realm: "{{keycloak.realm}}" |
| clientId: "{{keycloak.client-id}}" |
| clientSecret: "{{keycloak.client-secret}}" |
| requiredRoles: |
| - "admin" |
| - "manager" |
| requiredPermissions: |
| - "read:documents" |
| - "emergency:access" |
| allRolesRequired: false |
| allPermissionsRequired: false |
| ---- |
| ==== |