oscerd commented on code in PR #27332:
URL: https://github.com/apache/camel/pull/27332#discussion_r4177661116
##########
components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolConsumer.java:
##########
@@ -54,6 +58,55 @@ protected void doStart() throws Exception {
register();
}
+ /**
+ * Wraps the tool route's processor with the configured {@link
AuthorizationPolicy}, if any, so the call is
+ * authorized before the route runs. The policy is applied via {@code
beforeWrap} then {@code wrap} (as the
+ * {@code .policy()} DSL does through {@code PolicyReifier}); the wrapped
processor is started and stopped with this
+ * consumer. Because {@code getProcessor()} is the route's <em>outer</em>
processor, the guard runs in front of the
+ * route (before its unit of work, tracing and error handling): a denied
call is logged and returned to the model as
Review Comment:
Deliberate — the guard wraps the route's outer processor (so a deny produces
no span/metric and `onException` can't see it), and I've corrected the docs to
say exactly that: the `applyAuthorizationPolicy` javadoc and the "Authorizing
tool calls" adoc now say the guard runs *in front of* the route (logged at
`WARN`, relayed as `AuthorizationDenied`, no route span), and I fixed the PR
description's "inside the route / visible to tracing" line. Running the check
truly inside the route — so a deny emits a span/metric/event and fires
`onException` — needs model-level policy injection (a `PolicyDefinition` on the
route) rather than a processor wrap, which is a larger change; I'll open a
follow-up for that observability. For now the deny is fail-closed, typed, and
relayed to the model, which is the security behaviour the issue needs.
_Claude Code on behalf of oscerd_
##########
components/camel-ai/camel-ai-tool/src/main/docs/ai-tool-component.adoc:
##########
@@ -353,6 +353,74 @@ YAML::
----
====
+== Authorizing tool calls
+
+A tool call is a security boundary: an AI model decides, from its own output,
which `ai-tool` route to invoke. Set
+an `authorizationPolicy` — a reference to an
`org.apache.camel.spi.AuthorizationPolicy` bean — to authorize every
+call before the route runs. Set it on the *component* to guard every tool
route by construction, or on a single
+endpoint to override.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+// one policy guarding every ai-tool route
+AiToolComponent ai = context.getComponent("ai-tool", AiToolComponent.class);
+ai.getConfiguration().setAuthorizationPolicy(myAuthorizationPolicy);
+
+from("ai-tool:transferFunds?tags=banking&description=Transfer funds")
+ .to("bean:ledger");
+
+// ...or override on a single endpoint
+from("ai-tool:transferFunds?tags=banking&description=Transfer
funds&authorizationPolicy=#myAuthorizationPolicy")
+ .to("bean:ledger");
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: ai-tool:transferFunds
+ parameters:
+ tags: banking
+ description: "Transfer funds"
+ authorizationPolicy: "#myAuthorizationPolicy"
+ steps:
+ - to: bean:ledger
+----
+====
+
+The guard runs in front of the route: it wraps the route's outer processor, so
it executes before the route's unit
+of work, tracing and error handling. A denied call
(`CamelAuthorizationException`) is returned to the model as a
+short refusal it can relay — not as a tool result and not as a stack trace —
regardless of the tool-execution error
+strategy; the denial is logged at `WARN`, but it does not produce a route span
or metric.
+
+The policy authorizes on *trustworthy* input only:
+
+* the *tool name* comes from the route (the tool's id), never from model
output;
+* the *caller identity* comes from an exchange *property* (or a validated
token) set before the agent ran — for
+ example by `camel-spiffe` or `camel-keycloak`. Authorize on properties or
validated tokens only, *never* on
+ message headers: on a tool route the headers carry the model-controlled tool
arguments (and, on the
+ `langchain4j-agent` path, the caller's inbound HTTP headers), so a policy
such as OPA with the default
Review Comment:
Agreed — #27332 and #27264 (CAMEL-24832) both touch this section and
`AiToolExecutor` / the adapters. #27264 gives the tool route a detached copy
and propagates the caller context for openai and spring-ai; this PR's adoc
already carries an "until CAMEL-24832" bullet for exactly that. Whichever
merges first, I'll rebase the second onto it and reconcile this section — the
overlap is additive (the context-propagation text from #27264 plus the
authorization text here). I won't let them drift.
_Claude Code on behalf of oscerd_
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]