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

coheigea pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.6.x-fixes by this push:
     new a5fb1ce25a7 Make SimpleAuthorizingInterceptor signature keys 
whitespace-insensitive (#3623)
a5fb1ce25a7 is described below

commit a5fb1ce25a7c0a95f115b8d8f64efaf7275bf8af
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..9501951effb 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 {
+        @javax.annotation.security.RolesAllowed("admin")
+        public void transfer(String account, long amount) {
+
+        }
+
+        @javax.annotation.security.RolesAllowed("user")
+        public void transfer(String account) {
+
+        }
+    }
+
     private static final class IncompleteSecurityContext implements 
SecurityContext {
 
         public Principal getUserPrincipal() {

Reply via email to