Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
*/
public final class ShiroSecurityConstants {

public static final String SHIRO_SECURITY_TOKEN = "SHIRO_SECURITY_TOKEN";
public static final String SHIRO_SECURITY_USERNAME = "SHIRO_SECURITY_USERNAME";
public static final String SHIRO_SECURITY_PASSWORD = "SHIRO_SECURITY_PASSWORD";
public static final String SHIRO_SECURITY_TOKEN = "CamelShiroSecurityToken";
public static final String SHIRO_SECURITY_USERNAME = "CamelShiroSecurityUsername";
public static final String SHIRO_SECURITY_PASSWORD = "CamelShiroSecurityPassword";

private ShiroSecurityConstants() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,70 @@ As a consequence, the generated Endpoint DSL header accessors on
`resultClassType()` -> `arangoDbResultClassType()`.


=== camel-shiro - potential breaking change

The three Exchange header constants in `ShiroSecurityConstants` that drive
Shiro authentication used header values outside the `Camel` namespace
(`SHIRO_SECURITY_TOKEN`, `SHIRO_SECURITY_USERNAME`, `SHIRO_SECURITY_PASSWORD`)
and were therefore not filtered by the default `HeaderFilterStrategy`. They
have been renamed to follow the Camel naming convention. The Java field names
are unchanged; only the header string values have changed:

[options="header"]
|===
| Constant | Previous value | New value
| `ShiroSecurityConstants.SHIRO_SECURITY_TOKEN` | `SHIRO_SECURITY_TOKEN` | `CamelShiroSecurityToken`
| `ShiroSecurityConstants.SHIRO_SECURITY_USERNAME` | `SHIRO_SECURITY_USERNAME` | `CamelShiroSecurityUsername`
| `ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD` | `SHIRO_SECURITY_PASSWORD` | `CamelShiroSecurityPassword`
|===

These headers carry credentials and a serialized authentication token, so
filtering them at transport boundaries by default is particularly important.

Routes that reference the constants symbolically (for example
`setHeader(ShiroSecurityConstants.SHIRO_SECURITY_USERNAME, ...)`) continue to
work without changes. Routes that set the header by its literal string value
(for example `setHeader("SHIRO_SECURITY_USERNAME", ...)`) must be updated to
use the new value (`setHeader("CamelShiroSecurityUsername", ...)`).

Because the three header values are now in the `Camel*` namespace, transports
that filter Camel-internal headers by default (JMS, CXF, HTTP, etc.) will
strip the serialized Shiro authentication token before publishing. This is
the intended behavior for untrusted producers. Trusted Shiro-over-transport
routes that previously relied on the token surviving the boundary must opt
those three headers back in via a custom `HeaderFilterStrategy`, for example:

[source,java]
----
public class ShiroFriendlyJmsHeaderFilterStrategy extends JmsHeaderFilterStrategy {
@Override
public boolean applyFilterToCamelHeaders(String name, Object value, Exchange ex) {
if (isShiroSecurityHeader(name)) {
return false;
}
return super.applyFilterToCamelHeaders(name, value, ex);
}

@Override
public boolean applyFilterToExternalHeaders(String name, Object value, Exchange ex) {
if (isShiroSecurityHeader(name)) {
return false;
}
return super.applyFilterToExternalHeaders(name, value, ex);
}

private static boolean isShiroSecurityHeader(String name) {
return ShiroSecurityConstants.SHIRO_SECURITY_TOKEN.equalsIgnoreCase(name)
|| ShiroSecurityConstants.SHIRO_SECURITY_USERNAME.equalsIgnoreCase(name)
|| ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD.equalsIgnoreCase(name);
}
}

jmsComponent.setHeaderFilterStrategy(new ShiroFriendlyJmsHeaderFilterStrategy());
----

A worked example is in `ShiroOverJmsTest` in the `camel-itest` module.

== Upgrading from 4.14.5 to 4.14.6

=== camel-platform-http-main
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import java.util.HashMap;
import java.util.Map;

import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.component.jms.JmsHeaderFilterStrategy;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.component.shiro.security.ShiroSecurityConstants;
import org.apache.camel.component.shiro.security.ShiroSecurityPolicy;
Expand Down Expand Up @@ -61,9 +63,42 @@ protected void bindToRegistry(Registry registry) {

amq.setCamelContext(context);

// After CAMEL-23592 the Shiro security headers live in the CamelShiroSecurity* namespace,
// which the default JmsHeaderFilterStrategy filters at the transport boundary. For a
// trusted Shiro-over-JMS route, the operator must opt those three headers back in.
amq.setHeaderFilterStrategy(new ShiroFriendlyJmsHeaderFilterStrategy());

registry.bind("jms", amq);
}

/**
* Allows the three Shiro security headers (token, username, password) to cross the JMS transport boundary while
* still filtering every other Camel-internal header.
*/
private static final class ShiroFriendlyJmsHeaderFilterStrategy extends JmsHeaderFilterStrategy {
@Override
public boolean applyFilterToCamelHeaders(String headerName, Object headerValue, Exchange exchange) {
if (isShiroSecurityHeader(headerName)) {
return false;
}
return super.applyFilterToCamelHeaders(headerName, headerValue, exchange);
}

@Override
public boolean applyFilterToExternalHeaders(String headerName, Object headerValue, Exchange exchange) {
if (isShiroSecurityHeader(headerName)) {
return false;
}
return super.applyFilterToExternalHeaders(headerName, headerValue, exchange);
}

private static boolean isShiroSecurityHeader(String headerName) {
return ShiroSecurityConstants.SHIRO_SECURITY_TOKEN.equalsIgnoreCase(headerName)
|| ShiroSecurityConstants.SHIRO_SECURITY_USERNAME.equalsIgnoreCase(headerName)
|| ShiroSecurityConstants.SHIRO_SECURITY_PASSWORD.equalsIgnoreCase(headerName);
}
}

@Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
Expand Down
Loading