This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/4.1.x-fixes by this push:
new e7039cc751d Make SimpleAuthorizingInterceptor signature keys
whitespace-insensitive (#3623)
e7039cc751d is described below
commit e7039cc751d7f127c914772e56311507a7d1309d
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Sun Oct 25 10:49:48 2026 +0000
Make SimpleAuthorizingInterceptor signature keys whitespace-insensitive
(#3623)
(cherry picked from commit 5a6d72e138ea46cdad13705573c7a3547d45ec05)
---
.../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() {