You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2026/10/23 12:46:09 UTC
(cxf) 01/01: Make SimpleAuthorizingInterceptor signature keys whitespace-insensitive
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch coheigea/SimpleAuthorizingInterceptor
in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 15bcfc8ee01723f247ee3a7c4a50e55066c867d2
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Oct 23 13:45:38 2026 +0100
Make SimpleAuthorizingInterceptor signature keys whitespace-insensitive
---
.../security/SimpleAuthorizingInterceptor.java | 52 +++++++++++++++++-
.../security/SimpleAuthorizingInterceptorTest.java | 62 ++++++++++++++++++++++
2 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptor.java b/core/src/main/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptor.java
index 2fd195245f8..5b73d24be81 100644
--- a/core/src/main/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptor.java
@@ -24,11 +24,19 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.security.SecurityContext;
public class SimpleAuthorizingInterceptor extends AbstractAuthorizingInInterceptor {
+ private static final Logger LOG = LogUtils.getL7dLogger(SimpleAuthorizingInterceptor.class);
+ private static final Set<String> PRIMITIVE_TYPES = Set.of(
+ "boolean", "byte", "char", "short", "int", "long", "float", "double");
+ private static final Pattern WHITESPACE = Pattern.compile("\\s+");
protected Map<String, List<String>> methodRolesMap = new HashMap<>();
protected Map<String, List<String>> userRolesMap = Collections.emptyMap();
@@ -80,7 +88,11 @@ public class SimpleAuthorizingInterceptor extends AbstractAuthorizingInIntercept
@Override
protected List<String> getExpectedRoles(Method method) {
- List<String> roles = methodRolesMap.get(createMethodSig(method));
+ String sig = createMethodSig(method);
+ List<String> roles = methodRolesMap.get(sig);
+ if (roles == null) {
+ roles = methodRolesMap.get(normalizeMethodSig(sig));
+ }
if (roles == null) {
roles = methodRolesMap.get(method.getName());
}
@@ -92,7 +104,11 @@ public class SimpleAuthorizingInterceptor extends AbstractAuthorizingInIntercept
public void setMethodRolesMap(Map<String, String> rolesMap) {
- methodRolesMap.putAll(parseRolesMap(rolesMap));
+ for (Map.Entry<String, List<String>> entry : parseRolesMap(rolesMap).entrySet()) {
+ String key = normalizeMethodSig(entry.getKey());
+ warnOnUnmatchableSig(key);
+ methodRolesMap.put(key, entry.getValue());
+ }
}
public void setUserRolesMap(Map<String, String> rolesMap) {
@@ -107,6 +123,38 @@ public class SimpleAuthorizingInterceptor extends AbstractAuthorizingInIntercept
this.checkConfiguredRolesOnly = checkConfiguredRolesOnly;
}
+ /**
+ * Brings a method signature key into the format produced by {@link #createMethodSig(Method)},
+ * e.g. "void op(java.lang.String, long)", so that whitespace differences such as
+ * "op(java.lang.String,long)" do not prevent a configured role rule from matching.
+ * Keys that are not signatures are returned as is.
+ */
+ private static String normalizeMethodSig(String key) {
+ int open = key.indexOf('(');
+ int close = key.lastIndexOf(')');
+ if (open == -1 || close < open) {
+ return key;
+ }
+ String prefix = WHITESPACE.matcher(key.substring(0, open).trim()).replaceAll(" ");
+ String params = WHITESPACE.matcher(key.substring(open + 1, close)).replaceAll("");
+ return prefix + '(' + params.replace(",", ", ") + ')';
+ }
+
+ private static void warnOnUnmatchableSig(String key) {
+ int open = key.indexOf('(');
+ if (open == -1 || open + 1 >= key.length() - 1) {
+ return;
+ }
+ for (String param : key.substring(open + 1, key.length() - 1).split(", ")) {
+ // Class.getName() yields "java.lang.String", "long" or "[I", never "String" or "int[]"
+ if (param.endsWith("[]")
+ || param.indexOf('.') == -1 && !param.startsWith("[") && !PRIMITIVE_TYPES.contains(param)) {
+ LOG.warning("Method roles key \"" + key + "\" has the parameter type \"" + param
+ + "\" which is not a Class.getName() type name, the key will not match any method");
+ }
+ }
+ }
+
private static Map<String, List<String>> parseRolesMap(Map<String, String> rolesMap) {
Map<String, List<String>> map = new HashMap<>();
for (Map.Entry<String, String> entry : rolesMap.entrySet()) {
diff --git a/core/src/test/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptorTest.java b/core/src/test/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptorTest.java
index a5341736e24..a4252adeef3 100644
--- a/core/src/test/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptorTest.java
+++ b/core/src/test/java/org/apache/cxf/interceptor/security/SimpleAuthorizingInterceptorTest.java
@@ -201,6 +201,56 @@ public class SimpleAuthorizingInterceptorTest {
assertEquals(Collections.singletonList("admin"), in.getExpectedRoles(transfer));
}
+ @Test
+ public void testMethodSignatureRolesIgnoreWhitespace() throws Exception {
+ Method transfer = TestService.class.getMethod("transfer", String.class, long.class);
+ for (String key : new String[] {"void transfer(java.lang.String,long)",
+ " void transfer( java.lang.String , long ) ", "void transfer(java.lang.String,\tlong)"}) {
+ SimpleAuthorizingInterceptor in = new SimpleAuthorizingInterceptor();
+ in.setMethodRolesMap(Collections.singletonMap(key, "admin"));
+ assertEquals(key, Collections.singletonList("admin"), in.getExpectedRoles(transfer));
+ }
+ }
+
+ @Test
+ public void testMethodSignatureRolesAddedDirectly() throws Exception {
+ Method transfer = TestService.class.getMethod("transfer", String.class, long.class);
+ SimpleAuthorizingInterceptor in = new SimpleAuthorizingInterceptor();
+ in.methodRolesMap.put(in.createMethodSig(transfer), Collections.singletonList("admin"));
+ assertEquals(Collections.singletonList("admin"), in.getExpectedRoles(transfer));
+ }
+
+ @Test
+ public void testCustomMethodSigAddedDirectly() throws Exception {
+ Method transfer = TestService.class.getMethod("transfer", String.class, long.class);
+ SimpleAuthorizingInterceptor in = new SimpleAuthorizingInterceptor() {
+ @Override
+ protected String createMethodSig(Method m) {
+ return super.createMethodSig(m).replace(", ", ",");
+ }
+ };
+ in.methodRolesMap.put(in.createMethodSig(transfer), Collections.singletonList("admin"));
+ assertEquals(Collections.singletonList("admin"), in.getExpectedRoles(transfer));
+ }
+
+ @Test
+ public void testMethodSignatureRolesNoParameters() throws Exception {
+ Method echo = TestService.class.getMethod("echo");
+ SimpleAuthorizingInterceptor in = new SimpleAuthorizingInterceptor();
+ in.setMethodRolesMap(Collections.singletonMap("void echo( )", "admin"));
+ assertEquals(Collections.singletonList("admin"), in.getExpectedRoles(echo));
+ }
+
+ @Test
+ public void testSecureAnnotationsSignatureRoles() throws Exception {
+ SecureAnnotationsInterceptor in = new SecureAnnotationsInterceptor();
+ in.setSecuredObject(new AnnotatedService());
+ assertEquals(Collections.singletonList("admin"),
+ in.getExpectedRoles(AnnotatedService.class.getMethod("transfer", String.class, long.class)));
+ assertEquals(Collections.singletonList("user"),
+ in.getExpectedRoles(AnnotatedService.class.getMethod("transfer", String.class)));
+ }
+
private static final class TestService {
@SuppressWarnings("unused")
public void echo() {
@@ -213,6 +263,18 @@ public class SimpleAuthorizingInterceptorTest {
}
}
+ public static class AnnotatedService {
+ @jakarta.annotation.security.RolesAllowed("admin")
+ public void transfer(String account, long amount) {
+
+ }
+
+ @jakarta.annotation.security.RolesAllowed("user")
+ public void transfer(String account) {
+
+ }
+ }
+
private static final class IncompleteSecurityContext implements SecurityContext {
public Principal getUserPrincipal() {