Uploaded image for project: 'DeltaSpike'
  1. DeltaSpike
  2. DELTASPIKE-1014

SecuredAnnotationAuthorizer overwrites method-level annotation metadata with class-level annotation metadata

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Minor
    • Resolution: Fixed
    • 1.5.1
    • 1.5.2
    • Security-Module
    • None
    • Weld 2.2.15.Final

    Description

      Short Overview of What I'm trying to Do

      I'm trying to make a CDI-based equivalent of javax.annotation.security.RolesAllowed that uses my custom ROLE enum.

      @Target({TYPE, METHOD, FIELD})
      @Retention(RUNTIME)
      @Inherited
      @Stereotype
      @Secured(MyRoleAccessDecisionVoter.class)
      public @interface MyRolesAllowed {
      
          ROLE[] value();
      
      }
      
      @RequestScoped
      public class MyRoleAccessDecisionVoter extends AbstractAccessDecisionVoter {
          @Inject
          private Principal principal;
      
          @Override
          protected void checkPermission(AccessDecisionVoterContext voterContext, Set<SecurityViolation> violations) {
              // get the roles from the annotation
              ROLE[] rolesAllowed = voterContext.getMetaDataFor(MyRolesAllowed.class.getName(), MyRolesAllowed.class).value();
              // BUG ABOVE!  it'll have class-level annotation instead of the method-level annotation
          }
      }
              
      @MyRolesAllowed({ADMIN, ROOT, USER})
      @Stateless
      public class TestBean {
          @MyRolesAllowed({ADMIN, ROOT})
          public List<String> getWhatever() {
              return ImmutableList.of();
          }
      }
      

      My Thoughts

      It looks like org.apache.deltaspike.security.impl.authorization.SecuredAnnotationAuthorizer is where the bug is.

      It parses both method- and class-level annotations in extractMetadata(), in that order (method first, then class).

      Then that data gets passed to DefaultAccessDecisionVoterContext.addMetaData(), which puts it in a HashMap.

      Because the order is method-first, that entry in the map gets overwritten by the class-level info.

      Workaround

      Use AccessDecisionVoterContext.getSource().getMethod().getAnnotationsByType() and use the annotation's value, if any.

              InvocationContext context = voterContext.getSource();
              // get the metadata from the method, only required if using DS <= 1.5.1
              MyRolesAllowed[] methodLevelAnnotations = context.getMethod().getAnnotationsByType(MyRolesAllowed.class);
              if(methodLevelAnnotations.length != 0) {
                  rolesAllowed = methodLevelAnnotations[0].value();
              } else {
                  // get the roles from the class-level annotation, if available
                  rolesAllowed = voterContext.getMetaDataFor(MyRolesAllowed.class.getName(), MyRolesAllowed.class).value();
              }
      

      Attachments

        Activity

          People

            gpetracek Gerhard Petracek
            the_alchemist The Alchemist
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: