This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new d8e538bc9fd1 CAMEL-26275: camel-keycloak - take the operation from the 
CamelKeycloakOperation header only when allowOperationHeader is set (#29212)
d8e538bc9fd1 is described below

commit d8e538bc9fd15f3e73892d468ce20d17ab1f79b2
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Nov 5 13:02:14 2026 +0100

    CAMEL-26275: camel-keycloak - take the operation from the 
CamelKeycloakOperation header only when allowOperationHeader is set (#29212)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/catalog/components/keycloak.json  |  10 +-
 .../camel/catalog/docs/keycloak-producer.adoc      |  11 ++
 .../keycloak/KeycloakComponentConfigurer.java      |   6 +
 .../keycloak/KeycloakEndpointConfigurer.java       |   6 +
 .../keycloak/KeycloakEndpointUriFactory.java       |   3 +-
 .../apache/camel/component/keycloak/keycloak.json  |  10 +-
 .../src/main/docs/keycloak-producer.adoc           |  11 ++
 .../component/keycloak/KeycloakConfiguration.java  |  27 +++-
 .../component/keycloak/KeycloakConstants.java      |   5 +-
 .../camel/component/keycloak/KeycloakEndpoint.java |   5 +-
 .../camel/component/keycloak/KeycloakProducer.java |  15 ++-
 .../KeycloakProducerOperationHeaderTest.java       | 148 +++++++++++++++++++++
 .../java/org/apache/camel/util/SecurityUtils.java  |   1 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  11 ++
 .../dsl/KeycloakComponentBuilderFactory.java       |  23 ++++
 .../dsl/KeycloakEndpointBuilderFactory.java        |  45 ++++++-
 16 files changed, 317 insertions(+), 20 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/keycloak.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/keycloak.json
index d6844636797c..1c04241849c9 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/keycloak.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/keycloak.json
@@ -56,10 +56,11 @@
     "useTokenIntrospection": { "index": 29, "kind": "property", "displayName": 
"Use Token Introspection", "group": "common", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Enable OAuth 2.0 token 
introspection for real-time token validation. When  [...]
     "bridgeErrorHandler": { "index": 30, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the lik [...]
     "lazyStartProducer": { "index": 31, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fai [...]
-    "autowiredEnabled": { "index": 32, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
+    "autowiredEnabled": { "index": 32, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
+    "allowOperationHeader": { "index": 33, "kind": "property", "displayName": 
"Allow Operation Header", "group": "security", "label": "producer,security", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "security": "insecure:dev", 
"insecureValue": "true", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Whet [...]
   },
   "headers": {
-    "CamelKeycloakOperation": { "index": 0, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": 
"org.apache.camel.component.keycloak.KeycloakOperations", "enum": [ 
"createRealm", "deleteRealm", "getRealm", "updateRealm", "createUser", 
"deleteUser", "getUser", "updateUser", "listUsers", "searchUsers", 
"createRole", "deleteRole", "getRole", "updateRole", "listRoles", 
"assignRoleToUser", "removeRoleFromUser", "getUserRoles", "createGroup", "de 
[...]
+    "CamelKeycloakOperation": { "index": 0, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": 
"org.apache.camel.component.keycloak.KeycloakOperations", "enum": [ 
"createRealm", "deleteRealm", "getRealm", "updateRealm", "createUser", 
"deleteUser", "getUser", "updateUser", "listUsers", "searchUsers", 
"createRole", "deleteRole", "getRole", "updateRole", "listRoles", 
"assignRoleToUser", "removeRoleFromUser", "getUserRoles", "createGroup", "de 
[...]
     "CamelKeycloakRealmName": { "index": 1, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The realm name", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#REALM_NAME" },
     "CamelKeycloakUserId": { "index": 2, "kind": "header", "displayName": "", 
"group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The user ID", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#USER_ID" },
     "CamelKeycloakUsername": { "index": 3, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The username", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#USERNAME" },
@@ -130,7 +131,7 @@
   "bodyTypes": {
     "consumerOut": { "carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.EventRepresentation", "byOption": [ { 
"option": "eventType", "value": "admin-events", "carriers": [ "pojo" ], 
"javaType": "org.keycloak.representations.idm.AdminEventRepresentation" } ], 
"description": "One message per event: the EventRepresentation of a user event 
such as a login, or the AdminEventRepresentation with eventType=admin-events. 
The CamelKeycloakEventType header says event or admin-event" },
     "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"pojoRequest", "value": "true", "carriers": [ "pojo" ] } ], "description": 
"Mostly not read: the operations take their input from headers, such as 
CamelKeycloakRealmName and CamelKeycloakUserId. With pojoRequest=true the 
create and update operations take the Keycloak representation, such as a 
UserRepresentation for createUser, or a Map or JSON text of it; a body that 
does not convert fails. The bulk operations take a Li [...]
-    "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": 
"operation", "value": "createRealm", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "operation", "value": "deleteRealm", 
"carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": 
"operation", "value": "getRealm", "carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.RealmRepresentation" }, { "option": 
"operation", "value": "updateRealm", "carriers": [ "text" ], "ja [...]
+    "producerOut": { "carriers": [ "unknown" ], "dependsOn": [ 
"allowOperationHeader" ], "byOption": [ { "option": "operation", "value": 
"createRealm", "carriers": [ "text" ], "javaType": "java.lang.String" }, { 
"option": "operation", "value": "deleteRealm", "carriers": [ "text" ], 
"javaType": "java.lang.String" }, { "option": "operation", "value": "getRealm", 
"carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.RealmRepresentation" }, { "option": 
"operation", "value": " [...]
   },
   "properties": {
     "label": { "index": 0, "kind": "path", "displayName": "Label", "group": 
"common", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Logical name" },
@@ -182,6 +183,7 @@
     "schedulerProperties": { "index": 46, "kind": "parameter", "displayName": 
"Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", 
"required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": "scheduler.", 
"multiValue": true, "deprecated": false, "autowired": false, "secret": false, 
"description": "To configure additional properties when using a custom 
scheduler or any of the Quartz, Spring based scheduler. This i [...]
     "startScheduler": { "index": 47, "kind": "parameter", "displayName": 
"Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": true, 
"description": "Whether the scheduler should be auto started." },
     "timeUnit": { "index": 48, "kind": "parameter", "displayName": "Time 
Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, 
"type": "enum", "javaType": "java.util.concurrent.TimeUnit", "enum": [ 
"NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", 
"DAYS" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and 
delay options." },
-    "useFixedDelay": { "index": 49, "kind": "parameter", "displayName": "Use 
Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in 
JDK for details." }
+    "useFixedDelay": { "index": 49, "kind": "parameter", "displayName": "Use 
Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in 
JDK for details." },
+    "allowOperationHeader": { "index": 50, "kind": "parameter", "displayName": 
"Allow Operation Header", "group": "security", "label": "producer,security", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "security": "insecure:dev", 
"insecureValue": "true", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Whe [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
index 980b9e7bf553..91ed62987db2 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/keycloak-producer.adoc
@@ -132,6 +132,17 @@ All producer operations follow the same pattern: set the 
operation name in the U
 template.sendBodyAndHeaders("keycloak:admin?operation=<operationName>", null, 
headers);
 ----
 
+=== Choosing the operation with a header
+
+The endpoint runs its `operation`. The `CamelKeycloakOperation` header is 
ignored unless the endpoint sets
+`allowOperationHeader=true` (disabled by default). With that option, the 
header takes the place of the `operation` of
+the endpoint for one message, so the message chooses what the endpoint does 
with its admin client, such as
+`deleteRealm` on an endpoint configured for `getUser`. Enable it only on 
routes that set the header themselves or whose
+messages come from a trusted source. The option can also be set on the 
component, for all its endpoints.
+
+When the messages come from an untrusted producer through a consumer that does 
not filter out `Camel*` headers,
+remove these headers with `removeHeaders("CamelKeycloak*")` before the 
`keycloak` endpoint.
+
 === Representation bodies
 
 With `pojoRequest=true`, the create and update operations take the Keycloak 
representation as the body, such as a
diff --git 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakComponentConfigurer.java
 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakComponentConfigurer.java
index f8bba5645bf0..601e902e8b51 100644
--- 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakComponentConfigurer.java
+++ 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakComponentConfigurer.java
@@ -33,6 +33,8 @@ public class KeycloakComponentConfigurer extends 
PropertyConfigurerSupport imple
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": 
getOrCreateConfiguration(target).setAccessToken(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "allowoperationheader":
+        case "allowOperationHeader": 
getOrCreateConfiguration(target).setAllowOperationHeader(property(camelContext, 
boolean.class, value)); return true;
         case "authclient":
         case "authClient": 
getOrCreateConfiguration(target).setAuthClient(property(camelContext, 
java.lang.String.class, value)); return true;
         case "authipaddress":
@@ -102,6 +104,8 @@ public class KeycloakComponentConfigurer extends 
PropertyConfigurerSupport imple
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": return java.lang.String.class;
+        case "allowoperationheader":
+        case "allowOperationHeader": return boolean.class;
         case "authclient":
         case "authClient": return java.lang.String.class;
         case "authipaddress":
@@ -167,6 +171,8 @@ public class KeycloakComponentConfigurer extends 
PropertyConfigurerSupport imple
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": return 
getOrCreateConfiguration(target).getAccessToken();
+        case "allowoperationheader":
+        case "allowOperationHeader": return 
getOrCreateConfiguration(target).isAllowOperationHeader();
         case "authclient":
         case "authClient": return 
getOrCreateConfiguration(target).getAuthClient();
         case "authipaddress":
diff --git 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointConfigurer.java
 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointConfigurer.java
index f5c493b3a0ce..d1414a736635 100644
--- 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointConfigurer.java
+++ 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointConfigurer.java
@@ -26,6 +26,8 @@ public class KeycloakEndpointConfigurer extends 
PropertyConfigurerSupport implem
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": 
target.getConfiguration().setAccessToken(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "allowoperationheader":
+        case "allowOperationHeader": 
target.getConfiguration().setAllowOperationHeader(property(camelContext, 
boolean.class, value)); return true;
         case "authclient":
         case "authClient": 
target.getConfiguration().setAuthClient(property(camelContext, 
java.lang.String.class, value)); return true;
         case "authipaddress":
@@ -125,6 +127,8 @@ public class KeycloakEndpointConfigurer extends 
PropertyConfigurerSupport implem
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": return java.lang.String.class;
+        case "allowoperationheader":
+        case "allowOperationHeader": return boolean.class;
         case "authclient":
         case "authClient": return java.lang.String.class;
         case "authipaddress":
@@ -220,6 +224,8 @@ public class KeycloakEndpointConfigurer extends 
PropertyConfigurerSupport implem
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "accesstoken":
         case "accessToken": return target.getConfiguration().getAccessToken();
+        case "allowoperationheader":
+        case "allowOperationHeader": return 
target.getConfiguration().isAllowOperationHeader();
         case "authclient":
         case "authClient": return target.getConfiguration().getAuthClient();
         case "authipaddress":
diff --git 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointUriFactory.java
 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointUriFactory.java
index c3cca8be9a8e..9d52a0813745 100644
--- 
a/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointUriFactory.java
+++ 
b/components/camel-keycloak/src/generated/java/org/apache/camel/component/keycloak/KeycloakEndpointUriFactory.java
@@ -24,8 +24,9 @@ public class KeycloakEndpointUriFactory extends 
org.apache.camel.support.compone
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(50);
+        Set<String> props = new HashSet<>(51);
         props.add("accessToken");
+        props.add("allowOperationHeader");
         props.add("authClient");
         props.add("authIpAddress");
         props.add("authRealm");
diff --git 
a/components/camel-keycloak/src/generated/resources/META-INF/org/apache/camel/component/keycloak/keycloak.json
 
b/components/camel-keycloak/src/generated/resources/META-INF/org/apache/camel/component/keycloak/keycloak.json
index d6844636797c..1c04241849c9 100644
--- 
a/components/camel-keycloak/src/generated/resources/META-INF/org/apache/camel/component/keycloak/keycloak.json
+++ 
b/components/camel-keycloak/src/generated/resources/META-INF/org/apache/camel/component/keycloak/keycloak.json
@@ -56,10 +56,11 @@
     "useTokenIntrospection": { "index": 29, "kind": "property", "displayName": 
"Use Token Introspection", "group": "common", "label": "", "required": false, 
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Enable OAuth 2.0 token 
introspection for real-time token validation. When  [...]
     "bridgeErrorHandler": { "index": 30, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the lik [...]
     "lazyStartProducer": { "index": 31, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fai [...]
-    "autowiredEnabled": { "index": 32, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
+    "autowiredEnabled": { "index": 32, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching  [...]
+    "allowOperationHeader": { "index": 33, "kind": "property", "displayName": 
"Allow Operation Header", "group": "security", "label": "producer,security", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "security": "insecure:dev", 
"insecureValue": "true", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Whet [...]
   },
   "headers": {
-    "CamelKeycloakOperation": { "index": 0, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": 
"org.apache.camel.component.keycloak.KeycloakOperations", "enum": [ 
"createRealm", "deleteRealm", "getRealm", "updateRealm", "createUser", 
"deleteUser", "getUser", "updateUser", "listUsers", "searchUsers", 
"createRole", "deleteRole", "getRole", "updateRole", "listRoles", 
"assignRoleToUser", "removeRoleFromUser", "getUserRoles", "createGroup", "de 
[...]
+    "CamelKeycloakOperation": { "index": 0, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": 
"org.apache.camel.component.keycloak.KeycloakOperations", "enum": [ 
"createRealm", "deleteRealm", "getRealm", "updateRealm", "createUser", 
"deleteUser", "getUser", "updateUser", "listUsers", "searchUsers", 
"createRole", "deleteRole", "getRole", "updateRole", "listRoles", 
"assignRoleToUser", "removeRoleFromUser", "getUserRoles", "createGroup", "de 
[...]
     "CamelKeycloakRealmName": { "index": 1, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The realm name", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#REALM_NAME" },
     "CamelKeycloakUserId": { "index": 2, "kind": "header", "displayName": "", 
"group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The user ID", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#USER_ID" },
     "CamelKeycloakUsername": { "index": 3, "kind": "header", "displayName": 
"", "group": "common", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The username", "constantName": 
"org.apache.camel.component.keycloak.KeycloakConstants#USERNAME" },
@@ -130,7 +131,7 @@
   "bodyTypes": {
     "consumerOut": { "carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.EventRepresentation", "byOption": [ { 
"option": "eventType", "value": "admin-events", "carriers": [ "pojo" ], 
"javaType": "org.keycloak.representations.idm.AdminEventRepresentation" } ], 
"description": "One message per event: the EventRepresentation of a user event 
such as a login, or the AdminEventRepresentation with eventType=admin-events. 
The CamelKeycloakEventType header says event or admin-event" },
     "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"pojoRequest", "value": "true", "carriers": [ "pojo" ] } ], "description": 
"Mostly not read: the operations take their input from headers, such as 
CamelKeycloakRealmName and CamelKeycloakUserId. With pojoRequest=true the 
create and update operations take the Keycloak representation, such as a 
UserRepresentation for createUser, or a Map or JSON text of it; a body that 
does not convert fails. The bulk operations take a Li [...]
-    "producerOut": { "carriers": [ "unknown" ], "byOption": [ { "option": 
"operation", "value": "createRealm", "carriers": [ "text" ], "javaType": 
"java.lang.String" }, { "option": "operation", "value": "deleteRealm", 
"carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": 
"operation", "value": "getRealm", "carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.RealmRepresentation" }, { "option": 
"operation", "value": "updateRealm", "carriers": [ "text" ], "ja [...]
+    "producerOut": { "carriers": [ "unknown" ], "dependsOn": [ 
"allowOperationHeader" ], "byOption": [ { "option": "operation", "value": 
"createRealm", "carriers": [ "text" ], "javaType": "java.lang.String" }, { 
"option": "operation", "value": "deleteRealm", "carriers": [ "text" ], 
"javaType": "java.lang.String" }, { "option": "operation", "value": "getRealm", 
"carriers": [ "pojo" ], "javaType": 
"org.keycloak.representations.idm.RealmRepresentation" }, { "option": 
"operation", "value": " [...]
   },
   "properties": {
     "label": { "index": 0, "kind": "path", "displayName": "Label", "group": 
"common", "label": "", "required": true, "type": "string", "javaType": 
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Logical name" },
@@ -182,6 +183,7 @@
     "schedulerProperties": { "index": 46, "kind": "parameter", "displayName": 
"Scheduler Properties", "group": "scheduler", "label": "consumer,scheduler", 
"required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": "scheduler.", 
"multiValue": true, "deprecated": false, "autowired": false, "secret": false, 
"description": "To configure additional properties when using a custom 
scheduler or any of the Quartz, Spring based scheduler. This i [...]
     "startScheduler": { "index": 47, "kind": "parameter", "displayName": 
"Start Scheduler", "group": "scheduler", "label": "consumer,scheduler", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": true, 
"description": "Whether the scheduler should be auto started." },
     "timeUnit": { "index": 48, "kind": "parameter", "displayName": "Time 
Unit", "group": "scheduler", "label": "consumer,scheduler", "required": false, 
"type": "enum", "javaType": "java.util.concurrent.TimeUnit", "enum": [ 
"NANOSECONDS", "MICROSECONDS", "MILLISECONDS", "SECONDS", "MINUTES", "HOURS", 
"DAYS" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "MILLISECONDS", "description": "Time unit for initialDelay and 
delay options." },
-    "useFixedDelay": { "index": 49, "kind": "parameter", "displayName": "Use 
Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in 
JDK for details." }
+    "useFixedDelay": { "index": 49, "kind": "parameter", "displayName": "Use 
Fixed Delay", "group": "scheduler", "label": "consumer,scheduler", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Controls if fixed delay or fixed rate is used. See ScheduledExecutorService in 
JDK for details." },
+    "allowOperationHeader": { "index": 50, "kind": "parameter", "displayName": 
"Allow Operation Header", "group": "security", "label": "producer,security", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "security": "insecure:dev", 
"insecureValue": "true", "defaultValue": false, "configurationClass": 
"org.apache.camel.component.keycloak.KeycloakConfiguration", 
"configurationField": "configuration", "description": "Whe [...]
   }
 }
diff --git a/components/camel-keycloak/src/main/docs/keycloak-producer.adoc 
b/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
index 980b9e7bf553..91ed62987db2 100644
--- a/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
+++ b/components/camel-keycloak/src/main/docs/keycloak-producer.adoc
@@ -132,6 +132,17 @@ All producer operations follow the same pattern: set the 
operation name in the U
 template.sendBodyAndHeaders("keycloak:admin?operation=<operationName>", null, 
headers);
 ----
 
+=== Choosing the operation with a header
+
+The endpoint runs its `operation`. The `CamelKeycloakOperation` header is 
ignored unless the endpoint sets
+`allowOperationHeader=true` (disabled by default). With that option, the 
header takes the place of the `operation` of
+the endpoint for one message, so the message chooses what the endpoint does 
with its admin client, such as
+`deleteRealm` on an endpoint configured for `getUser`. Enable it only on 
routes that set the header themselves or whose
+messages come from a trusted source. The option can also be set on the 
component, for all its endpoints.
+
+When the messages come from an untrusted producer through a consumer that does 
not filter out `Camel*` headers,
+remove these headers with `removeHeaders("CamelKeycloak*")` before the 
`keycloak` endpoint.
+
 === Representation bodies
 
 With `pojoRequest=true`, the create and update operations take the Keycloak 
representation as the body, such as a
diff --git 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConfiguration.java
 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConfiguration.java
index db6d5bbd1a8e..34bc98400c8e 100644
--- 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConfiguration.java
+++ 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConfiguration.java
@@ -65,6 +65,14 @@ public class KeycloakConfiguration implements Cloneable {
     @UriParam(description = "The operation to perform")
     private KeycloakOperations operation;
 
+    @UriParam(label = "producer,security", defaultValue = "false", security = 
"insecure:dev", insecureValue = "true",
+              description = "Whether the CamelKeycloakOperation header may 
override the configured operation."
+                            + " Disabled by default: the operation decides 
what the endpoint does with its admin client,"
+                            + " from reading a user to deleting a realm, so a 
message that can set the header can run any"
+                            + " operation that the credentials of the endpoint 
allow. Enable it only on routes whose input"
+                            + " is trusted.")
+    private boolean allowOperationHeader;
+
     @UriParam(description = "If we want to use a POJO request as body or not")
     private boolean pojoRequest;
 
@@ -248,13 +256,28 @@ public class KeycloakConfiguration implements Cloneable {
     /**
      * The operation to perform.
      *
-     * You can configure a default operation on the component level, or the 
operation as part of the endpoint, or via a
-     * message header with the key CamelKeycloakOperation.
+     * You can configure a default operation on the component level, or the 
operation as part of the endpoint. With
+     * allowOperationHeader enabled, a message header with the key 
CamelKeycloakOperation overrides it.
      */
     public void setOperation(KeycloakOperations operation) {
         this.operation = operation;
     }
 
+    public boolean isAllowOperationHeader() {
+        return allowOperationHeader;
+    }
+
+    /**
+     * Whether the {@code CamelKeycloakOperation} header may override the 
configured operation.
+     * <p/>
+     * Disabled by default: the operation decides what the endpoint does with 
its admin client, from reading a user to
+     * deleting a realm, so a message that can set the header can run any 
operation that the credentials of the endpoint
+     * allow. Enable it only on routes whose input is trusted.
+     */
+    public void setAllowOperationHeader(boolean allowOperationHeader) {
+        this.allowOperationHeader = allowOperationHeader;
+    }
+
     public boolean isPojoRequest() {
         return pojoRequest;
     }
diff --git 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConstants.java
 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConstants.java
index 1100ed1cc1ee..12949d97252e 100644
--- 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConstants.java
+++ 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakConstants.java
@@ -20,7 +20,10 @@ import org.apache.camel.spi.Metadata;
 
 public final class KeycloakConstants {
 
-    @Metadata(description = "The operation to perform", javaType = 
"org.apache.camel.component.keycloak.KeycloakOperations")
+    @Metadata(description = "The operation to perform, in place of the 
operation of the endpoint. Ignored unless the"
+                            + " endpoint sets allowOperationHeader=true, 
because the operation decides what the endpoint"
+                            + " does with its admin client, such as deleting a 
realm.",
+              javaType = 
"org.apache.camel.component.keycloak.KeycloakOperations")
     public static final String OPERATION = "CamelKeycloakOperation";
 
     @Metadata(description = "The realm name", javaType = "String")
diff --git 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakEndpoint.java
 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakEndpoint.java
index 0cc36f10b725..0e27849eb918 100644
--- 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakEndpoint.java
+++ 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakEndpoint.java
@@ -57,7 +57,7 @@ import org.keycloak.admin.client.KeycloakBuilder;
                                                 + " for the others."
                                                 + " introspectToken, 
revokeAccessToken and revokeRefreshToken take the token"
                                                 + " as text when the 
CamelKeycloakToken header is not set"),
-           producerOut = @BodyType(value = Carrier.UNKNOWN,
+           producerOut = @BodyType(value = Carrier.UNKNOWN, dependsOn = 
"allowOperationHeader",
                                    byOption = {
                                            @BodyType.When(option = 
"operation", value = "createRealm", carriers = Carrier.TEXT,
                                                           javaType = 
"java.lang.String"),
@@ -297,7 +297,8 @@ import org.keycloak.admin.client.KeycloakBuilder;
                                            @BodyType.When(option = 
"operation", value = "getFederatedIdentities",
                                                           carriers = 
Carrier.LIST,
                                                           javaType = 
"java.util.List<org.keycloak.representations.idm.FederatedIdentityRepresentation>")
 },
-                                   description = "Depends on the operation, 
which the CamelKeycloakOperation header can set:"
+                                   description = "Depends on the operation, 
which the CamelKeycloakOperation header can set"
+                                                 + " with 
allowOperationHeader=true:"
                                                  + " get gives the Keycloak 
representation, such as a UserRepresentation, and"
                                                  + " list and search a List of 
them. Most create operations, deleteUser and"
                                                  + " some organization 
operations give the closed JAX-RS Response, whose"
diff --git 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
index 9a9c5f5cea29..a54cb4e17b97 100644
--- 
a/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
+++ 
b/components/camel-keycloak/src/main/java/org/apache/camel/component/keycloak/KeycloakProducer.java
@@ -100,7 +100,9 @@ public class KeycloakProducer extends DefaultProducer {
     public void process(Exchange exchange) throws Exception {
         KeycloakOperations operation = determineOperation(exchange);
         if (operation == null) {
-            throw new IllegalArgumentException("Operation must be provided");
+            throw new IllegalArgumentException(
+                    "Operation must be provided: set the operation option, or 
allowOperationHeader=true to take it from"
+                                               + " the CamelKeycloakOperation 
header");
         }
 
         switch (operation) {
@@ -422,11 +424,14 @@ public class KeycloakProducer extends DefaultProducer {
     }
 
     private KeycloakOperations determineOperation(Exchange exchange) {
-        KeycloakOperations operation = 
exchange.getIn().getHeader(KeycloakConstants.OPERATION, 
KeycloakOperations.class);
-        if (operation == null) {
-            operation = getConfiguration().getOperation();
+        KeycloakOperations configured = getConfiguration().getOperation();
+        if (!getConfiguration().isAllowOperationHeader()) {
+            // the operation decides what the admin client of the endpoint 
does, so the message only picks it when the
+            // route allows it
+            return configured;
         }
-        return operation;
+        KeycloakOperations operation = 
exchange.getIn().getHeader(KeycloakConstants.OPERATION, 
KeycloakOperations.class);
+        return operation != null ? operation : configured;
     }
 
     protected KeycloakConfiguration getConfiguration() {
diff --git 
a/components/camel-keycloak/src/test/java/org/apache/camel/component/keycloak/KeycloakProducerOperationHeaderTest.java
 
b/components/camel-keycloak/src/test/java/org/apache/camel/component/keycloak/KeycloakProducerOperationHeaderTest.java
new file mode 100644
index 000000000000..2b461ddd89b3
--- /dev/null
+++ 
b/components/camel-keycloak/src/test/java/org/apache/camel/component/keycloak/KeycloakProducerOperationHeaderTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.keycloak;
+
+import java.util.Map;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Covers when the CamelKeycloakOperation header may choose the operation of 
the producer: only when the endpoint sets
+ * allowOperationHeader=true, as the operation decides what the endpoint does 
with its admin client.
+ */
+class KeycloakProducerOperationHeaderTest extends CamelTestSupport {
+
+    private static final String REALM = "my-realm";
+    private static final String USER_ID = "user-1";
+    private static final String GET_USER = 
"keycloak:admin?keycloakClient=#keycloakClient&operation=getUser";
+
+    @BindToRegistry("keycloakClient")
+    private final Keycloak keycloakClient = mock(Keycloak.class);
+
+    private final RealmResource realmResource = mock(RealmResource.class);
+    private final UsersResource usersResource = mock(UsersResource.class);
+    private final UserResource userResource = mock(UserResource.class);
+    private final UserRepresentation user = new UserRepresentation();
+
+    @BeforeEach
+    void stubTheAdminClient() {
+        when(keycloakClient.realm(REALM)).thenReturn(realmResource);
+        when(realmResource.users()).thenReturn(usersResource);
+        when(usersResource.get(USER_ID)).thenReturn(userResource);
+        when(userResource.toRepresentation()).thenReturn(user);
+    }
+
+    @Test
+    void theOperationHeaderIsIgnoredByDefault() {
+        Exchange out = template.request(GET_USER, e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.USER_ID, USER_ID);
+            e.getIn().setHeader(KeycloakConstants.OPERATION, 
KeycloakOperations.deleteRealm);
+        });
+
+        // the endpoint stays a getUser endpoint: the user is read, and the 
realm is not deleted
+        assertNull(out.getException());
+        assertSame(user, out.getMessage().getBody());
+        verify(realmResource, never()).remove();
+    }
+
+    @Test
+    void theOperationHeaderAsTextIsIgnoredByDefault() {
+        Exchange out = template.request(GET_USER, e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.USER_ID, USER_ID);
+            e.getIn().setHeader(KeycloakConstants.OPERATION, "deleteRealm");
+        });
+
+        assertNull(out.getException());
+        assertSame(user, out.getMessage().getBody());
+        verify(realmResource, never()).remove();
+    }
+
+    @Test
+    void anEndpointWithoutAnOperationDoesNotTakeItFromTheHeaderByDefault() {
+        Exchange out = 
template.request("keycloak:admin?keycloakClient=#keycloakClient", e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.OPERATION, 
KeycloakOperations.deleteRealm);
+        });
+
+        IllegalArgumentException error = 
assertInstanceOf(IllegalArgumentException.class, out.getException());
+        assertTrue(error.getMessage().contains("allowOperationHeader=true"), 
error.getMessage());
+        verify(realmResource, never()).remove();
+    }
+
+    @Test
+    void theOperationHeaderChoosesTheOperationWhenTheEndpointAllowsIt() {
+        Exchange out = template.request(GET_USER + 
"&allowOperationHeader=true", e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.OPERATION, "deleteRealm");
+        });
+
+        assertNull(out.getException());
+        assertEquals("Realm deleted successfully", out.getMessage().getBody());
+        verify(realmResource).remove();
+    }
+
+    @Test
+    void withoutTheHeaderAnEndpointThatAllowsItRunsItsOperation() {
+        Exchange out = template.request(GET_USER + 
"&allowOperationHeader=true", e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.USER_ID, USER_ID);
+        });
+
+        assertNull(out.getException());
+        assertSame(user, out.getMessage().getBody());
+        verify(realmResource, never()).remove();
+    }
+
+    @Test
+    void theOptionCanBeSetOnTheComponent() {
+        // as camel.component.keycloak.allowOperationHeader=true does
+        PropertyBindingSupport.setPropertiesOnTarget(context, 
context.getComponent("keycloak"),
+                Map.of("allowOperationHeader", "true"));
+
+        // a new endpoint takes the option from the configuration of the 
component
+        Exchange out = 
template.request("keycloak:other?keycloakClient=#keycloakClient&operation=getUser",
 e -> {
+            e.getIn().setHeader(KeycloakConstants.REALM_NAME, REALM);
+            e.getIn().setHeader(KeycloakConstants.OPERATION, 
KeycloakOperations.deleteRealm);
+        });
+
+        assertNull(out.getException());
+        assertEquals("Realm deleted successfully", out.getMessage().getBody());
+        verify(realmResource).remove();
+    }
+}
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java 
b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
index 83852df136c9..9a7e15b986c4 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
@@ -147,6 +147,7 @@ public final class SecurityUtils {
         owners.put("allowlocalwebhookurls", Set.of(
                 "component:a2a"));
         owners.put("allowoperationheader", Set.of(
+                "component:keycloak",
                 "component:spiffe"));
         owners.put("allowpredicatefrommessage", Set.of(
                 "component:dynamicroutercontrol"));
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index d544460bf3ce..4c5b28b9e0d2 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -2524,6 +2524,17 @@ marked rollback only, so the commit of the outer block 
fails (in Camel Quarkus w
 to commit, as with camel-spring (`UnexpectedRollbackException`). To roll back 
only the inner block, use
 `PROPAGATION_REQUIRES_NEW` for it.
 
+=== camel-keycloak - the CamelKeycloakOperation header needs the 
allowOperationHeader option
+
+The `CamelKeycloakOperation` header took the place of the `operation` of the 
`keycloak` endpoint, so a message could
+make the endpoint run another operation than the one it is configured with. 
The header is now ignored unless the
+endpoint sets the new `allowOperationHeader=true` option (disabled by 
default), and the endpoint runs its configured
+`operation`. A route that picks the operation with the header must set 
`allowOperationHeader=true` on the endpoint, or
+on the component (`camel.component.keycloak.allowOperationHeader=true`) for 
all its endpoints.
+
+An endpoint without an `operation`, which took the operation from the header, 
now fails with an
+`IllegalArgumentException` that asks for the operation, unless it sets 
`allowOperationHeader=true`.
+
 === camel-thrift - SSL/TLS settings of the consumer, and errors sent to the 
client
 
 * A consumer configured with `negotiationType=SSL` and the default 
`synchronous=false` now fails to start. It used to
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KeycloakComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KeycloakComponentBuilderFactory.java
index 6aff0f5efc9c..d8e5eb88152b 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KeycloakComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KeycloakComponentBuilderFactory.java
@@ -606,6 +606,28 @@ public interface KeycloakComponentBuilderFactory {
             doSetProperty("autowiredEnabled", autowiredEnabled);
             return this;
         }
+    
+        
+        /**
+         * Whether the CamelKeycloakOperation header may override the 
configured
+         * operation. Disabled by default: the operation decides what the
+         * endpoint does with its admin client, from reading a user to deleting
+         * a realm, so a message that can set the header can run any operation
+         * that the credentials of the endpoint allow. Enable it only on routes
+         * whose input is trusted.
+         * 
+         * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
+         * 
+         * Default: false
+         * Group: security
+         * 
+         * @param allowOperationHeader the value to set
+         * @return the dsl builder
+         */
+        default KeycloakComponentBuilder allowOperationHeader(boolean 
allowOperationHeader) {
+            doSetProperty("allowOperationHeader", allowOperationHeader);
+            return this;
+        }
     }
 
     class KeycloakComponentBuilderImpl
@@ -660,6 +682,7 @@ public interface KeycloakComponentBuilderFactory {
             case "bridgeErrorHandler": ((KeycloakComponent) 
component).setBridgeErrorHandler((boolean) value); return true;
             case "lazyStartProducer": ((KeycloakComponent) 
component).setLazyStartProducer((boolean) value); return true;
             case "autowiredEnabled": ((KeycloakComponent) 
component).setAutowiredEnabled((boolean) value); return true;
+            case "allowOperationHeader": 
getOrCreateConfiguration((KeycloakComponent) 
component).setAllowOperationHeader((boolean) value); return true;
             default: return false;
             }
         }
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KeycloakEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KeycloakEndpointBuilderFactory.java
index c066d31f71ef..861f2e986225 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KeycloakEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KeycloakEndpointBuilderFactory.java
@@ -1822,6 +1822,46 @@ public interface KeycloakEndpointBuilderFactory {
             doSetProperty("useTokenIntrospection", useTokenIntrospection);
             return this;
         }
+        /**
+         * Whether the CamelKeycloakOperation header may override the 
configured
+         * operation. Disabled by default: the operation decides what the
+         * endpoint does with its admin client, from reading a user to deleting
+         * a realm, so a message that can set the header can run any operation
+         * that the credentials of the endpoint allow. Enable it only on routes
+         * whose input is trusted.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: false
+         * Group: security
+         * 
+         * @param allowOperationHeader the value to set
+         * @return the dsl builder
+         */
+        default KeycloakEndpointProducerBuilder allowOperationHeader(boolean 
allowOperationHeader) {
+            doSetProperty("allowOperationHeader", allowOperationHeader);
+            return this;
+        }
+        /**
+         * Whether the CamelKeycloakOperation header may override the 
configured
+         * operation. Disabled by default: the operation decides what the
+         * endpoint does with its admin client, from reading a user to deleting
+         * a realm, so a message that can set the header can run any operation
+         * that the credentials of the endpoint allow. Enable it only on routes
+         * whose input is trusted.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: false
+         * Group: security
+         * 
+         * @param allowOperationHeader the value to set
+         * @return the dsl builder
+         */
+        default KeycloakEndpointProducerBuilder allowOperationHeader(String 
allowOperationHeader) {
+            doSetProperty("allowOperationHeader", allowOperationHeader);
+            return this;
+        }
     }
 
     /**
@@ -2530,7 +2570,10 @@ public interface KeycloakEndpointBuilderFactory {
         public static final KeycloakHeaderNameBuilder INSTANCE = new 
KeycloakHeaderNameBuilder();
 
         /**
-         * The operation to perform.
+         * The operation to perform, in place of the operation of the endpoint.
+         * Ignored unless the endpoint sets allowOperationHeader=true, because
+         * the operation decides what the endpoint does with its admin client,
+         * such as deleting a realm.
          * 
          * The option is a: {@code
          * org.apache.camel.component.keycloak.KeycloakOperations} type.

Reply via email to