ffang commented on PR #3687:
URL: https://github.com/apache/cxf/pull/3687#issuecomment-6686919942

   Thanks @coheigea , failing closed is the right call. I checked that an empty 
rule really does deny every user, anonymous ones included.
   
     One case now breaks a valid configuration, though: an annotation type that 
isn't public. An application's own annotation is often declared 
package-private, for example:
   
     ```java
     package com.example.app;
   
     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.TYPE, ElementType.METHOD })
     @interface AppRoles {
         String[] value();
     }
   
     public class AppService {
         @AppRoles("admin")
         public void update() {
         }
     }
     ```
   
     `AppRoles` passes the new `setAnnotationClassName` check, because it is an 
annotation with a `String[]` `value()`. But `getRoles` calls `value()` from 
`org.apache.cxf.interceptor.security`, and that call throws:
   
     ```
     java.lang.IllegalAccessException: class 
org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor
     cannot access a member of interface com.example.app.AppRoles
     ```
   
     So `update()` is now denied to everyone, including `admin`. 
   
     <details>
     <summary>Test reproducing it (it has to be in a different package from the 
interceptor)</summary>
   
     ```java
     package org.apache.cxf.interceptor.security.app;
   
     import java.lang.annotation.ElementType;
     import java.lang.annotation.Retention;
     import java.lang.annotation.RetentionPolicy;
     import java.lang.annotation.Target;
     import java.security.Principal;
   
     import org.apache.cxf.interceptor.security.AccessDeniedException;
     import org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor;
     import org.apache.cxf.message.ExchangeImpl;
     import org.apache.cxf.message.Message;
     import org.apache.cxf.message.MessageImpl;
     import org.apache.cxf.security.SecurityContext;
   
     import org.junit.Test;
   
     import static org.junit.Assert.assertThrows;
   
     public class PackagePrivateAnnotationTest {
   
         @Retention(RetentionPolicy.RUNTIME)
         @Target({ElementType.TYPE, ElementType.METHOD })
         @interface AppRoles {
             String[] value();
         }
   
         public static class AppService {
             @AppRoles("admin")
             public void update() {
             }
         }
   
         @Test
         public void testPackagePrivateAnnotation() throws Exception {
             SecureAnnotationsInterceptor in = new 
SecureAnnotationsInterceptor();
             in.setAnnotationClassName(AppRoles.class.getName());
             in.setSecuredObject(new AppService());
   
             in.handleMessage(createMessage("admin"));
             assertThrows(AccessDeniedException.class, () -> 
in.handleMessage(createMessage("user")));
         }
   
         private static Message createMessage(String role) throws Exception {
             Message message = new MessageImpl();
             message.setExchange(new ExchangeImpl());
             message.put("org.apache.cxf.resource.method", 
AppService.class.getMethod("update"));
             message.put(SecurityContext.class, new SecurityContext() {
                 @Override
                 public Principal getUserPrincipal() {
                     return () -> "bob";
                 }
   
           message.put("org.apache.cxf.resource.method", 
AppService.class.getMethod("update"));
           message.put(SecurityContext.class, new SecurityContext() {
               @Override
               public Principal getUserPrincipal() {
                   return () -> "bob";
               }
   
               @Override
               public boolean isUserInRole(String r) {
                   return role.equals(r);
               }
           });
           return message;
       }
   }
   ```
   
   </details>
   
   Results:
   - **This PR:** fails on the first `handleMessage`, because `admin` is denied 
(`AccessDeniedException: Unauthorized`).
   - **main:** fails on the `assertThrows`, because `user` is allowed.
   - **With the change below:** passes.
   
   ```java
   Object roles = 
ReflectionUtil.setAccessible(ann.annotationType().getMethod("value")).invoke(ann);
   ```
   
   This still fails closed. If access can't be granted, for example when the 
annotation is in a module that doesn't open its package, `setAccessible` 
throws. The existing `catch (Exception ex)` then denies the method.
   
   Best Regards
   Freeman


-- 
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]

Reply via email to