You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by ka...@apache.org on 2010/08/03 06:12:36 UTC

svn commit: r981734 - in /incubator/shiro/trunk/core/src: main/java/org/apache/shiro/authz/ main/java/org/apache/shiro/authz/annotation/ main/java/org/apache/shiro/authz/aop/ main/java/org/apache/shiro/mgt/ main/java/org/apache/shiro/realm/ main/java/o...

Author: kaosko
Date: Tue Aug  3 04:12:36 2010
New Revision: 981734

URL: http://svn.apache.org/viewvc?rev=981734&view=rev
Log:
Complete - issue SHIRO-175: Improve Set of permission and role checks 
https://issues.apache.org/jira/browse/SHIRO-175
- added Logical enum and implemented optional Logical.OR annotation parameter for RequiresRoles and RequiresPermissions annotations
- added Subject.checkRoles(String... roleIdentifiers) etc. (there are several pass-through operations underneath) for completeness

Added:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java
Modified:
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/Authorizer.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java
    incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java
    incubator/shiro/trunk/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/Authorizer.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/Authorizer.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/Authorizer.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/Authorizer.java Tue Aug  3 04:12:36 2010
@@ -258,5 +258,21 @@ public interface Authorizer {
      */
     void checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) throws AuthorizationException;
 
+    /**
+     * Same as {@link #checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers) 
+     * checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers)} but doesn't require a collection 
+     * as an argument.
+     * Asserts the corresponding Subject/user has all of the specified roles by returning quietly if they do or
+     * throwing an {@link AuthorizationException} if they do not.
+     *
+     * @param subjectPrincipal the application-specific subject/user identifier.
+     * @param roleIdentifiers  the application-specific role identifiers to check (usually role ids or role names).
+     * @throws AuthorizationException
+     *          if the user does not have all of the specified roles.
+     *          
+     *  @since 1.1.0
+     */
+    void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException;
+    
 }
 

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java Tue Aug  3 04:12:36 2010
@@ -66,7 +66,6 @@ public class ModularRealmAuthorizer impl
      *
      * @param realms the realms to consult during an authorization check.
      */
-    @SuppressWarnings({"UnusedDeclaration"})
     public ModularRealmAuthorizer(Collection<Realm> realms) {
         setRealms(realms);
     }
@@ -421,9 +420,16 @@ public class ModularRealmAuthorizer impl
     }
 
     /**
-     * Calls {@link #checkRole(org.apache.shiro.subject.PrincipalCollection , String) checkRole} for each role specified.
+     * Calls {@link #checkRoles(PrincipalCollection principals, String... roles) checkRoles(PrincipalCollection principals, String... roles) }.
      */
     public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
+	if (roles != null && !roles.isEmpty()) checkRoles(principals, (String[])roles.toArray() );
+    }
+
+    /**
+     * Calls {@link #checkRole(org.apache.shiro.subject.PrincipalCollection , String) checkRole} for each role specified.
+     */
+    public void checkRoles(PrincipalCollection principals, String... roles) throws AuthorizationException {
         assertRealmsConfigured();
         if (roles != null) {
             for (String role : roles) {

Added: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java?rev=981734&view=auto
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java (added)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java Tue Aug  3 04:12:36 2010
@@ -0,0 +1,11 @@
+package org.apache.shiro.authz.annotation;
+
+/**
+ * An enum for specifying a logical operation that can be used for 
+ * interpreting authorization annotations 
+ *
+ * @since 1.1.0
+ */
+public enum Logical {
+    AND, OR
+}

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java Tue Aug  3 04:12:36 2010
@@ -33,7 +33,7 @@ import java.lang.annotation.Target;
  *
  * <p>For example, this declaration:
  * <p/>
- * <code>&#64;RequiresPermissions( "file:read,write:aFile.txt" )<br/>
+ * <code>&#64;RequiresPermissions( {"file:read", "write:aFile.txt"} )<br/>
  * void someMethod();</code>
  * <p/>
  * indicates the current user must be able to both <tt>read</tt> and <tt>write</tt>
@@ -52,6 +52,12 @@ public @interface RequiresPermissions {
      * to determine if the user is allowed to invoke the code protected by this annotation.
      */
     String[] value();
+    
+    /**
+     * The logical operation for the permission checks in case multiple roles are specified. AND is the default
+     * @since 1.1.0
+     */
+    Logical logical() default Logical.AND; 
 
 }
 

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java Tue Aug  3 04:12:36 2010
@@ -59,5 +59,10 @@ public @interface RequiresRoles {
      * invocation to be allowed.
      */
     String[] value();
-
+    
+    /**
+     * The logical operation for the permission check in case multiple roles are specified. AND is the default
+     * @since 1.1.0
+     */
+    Logical logical() default Logical.AND; 
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java Tue Aug  3 04:12:36 2010
@@ -19,14 +19,12 @@
 package org.apache.shiro.authz.aop;
 
 import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.annotation.Logical;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.apache.shiro.authz.annotation.RequiresRoles;
 import org.apache.shiro.subject.Subject;
-import org.apache.shiro.util.PermissionUtils;
 
 import java.lang.annotation.Annotation;
-import java.util.Arrays;
-import java.util.Set;
-
 
 /**
  * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotation is
@@ -68,10 +66,25 @@ public class PermissionAnnotationHandler
     public void assertAuthorized(Annotation a) throws AuthorizationException {
         if (!(a instanceof RequiresPermissions)) return;
 
+        RequiresPermissions rpAnnotation = (RequiresPermissions) a;
         String[] perms = getAnnotationValue(a);
         Subject subject = getSubject();
 
-        if (perms.length == 1) subject.checkPermission(perms[0]);
-        else subject.checkPermissions(perms);
+        if (perms.length == 1) {
+            subject.checkPermission(perms[0]);
+            return;
+        }
+        if (Logical.AND.equals(rpAnnotation.logical())) {
+            getSubject().checkPermissions(perms);
+            return;
+        }
+        if (Logical.OR.equals(rpAnnotation.logical())) {
+            // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
+            boolean hasAtLeastOnePermission = false;
+            for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
+            // Cause the exception if none of the role match, note that the exception message will be a bit misleading
+            if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);
+            
+        }
     }
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java Tue Aug  3 04:12:36 2010
@@ -19,6 +19,7 @@
 package org.apache.shiro.authz.aop;
 
 import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.annotation.Logical;
 import org.apache.shiro.authz.annotation.RequiresRoles;
 
 import java.lang.annotation.Annotation;
@@ -55,8 +56,21 @@ public class RoleAnnotationHandler exten
         RequiresRoles rrAnnotation = (RequiresRoles) a;
         String[] roles = rrAnnotation.value();
 
-        if (roles.length == 1) getSubject().checkRole(roles[0]);
-        else getSubject().checkRoles(Arrays.asList(roles));
+        if (roles.length == 1) {
+            getSubject().checkRole(roles[0]);
+            return;
+        }
+        if (Logical.AND.equals(rrAnnotation.logical())) {
+            getSubject().checkRoles(Arrays.asList(roles));
+            return;
+        }
+        if (Logical.OR.equals(rrAnnotation.logical())) {
+            // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
+            boolean hasAtLeastOneRole = false;
+            for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true;
+            // Cause the exception if none of the role match, note that the exception message will be a bit misleading
+            if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]);
+        }
     }
 
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java Tue Aug  3 04:12:36 2010
@@ -168,4 +168,8 @@ public abstract class AuthorizingSecurit
     public void checkRoles(PrincipalCollection principals, Collection<String> roles) throws AuthorizationException {
         this.authorizer.checkRoles(principals, roles);
     }
+    
+    public void checkRoles(PrincipalCollection principals, String... roles) throws AuthorizationException {
+        this.authorizer.checkRoles(principals, roles);
+    }    
 }

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java Tue Aug  3 04:12:36 2010
@@ -628,7 +628,11 @@ public abstract class AuthorizingRealm e
         AuthorizationInfo info = getAuthorizationInfo(principal);
         checkRoles(roles, info);
     }
-
+    
+    public void checkRoles(PrincipalCollection principal, String... roles) throws AuthorizationException {
+	checkRoles(principal, Arrays.asList(roles));
+    }
+    
     protected void checkRoles(Collection<String> roles, AuthorizationInfo info) {
         if (roles != null && !roles.isEmpty()) {
             for (String roleName : roles) {

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/Subject.java Tue Aug  3 04:12:36 2010
@@ -300,6 +300,19 @@ public interface Subject {
      *          if this Subject does not have all of the specified roles.
      */
     void checkRoles(Collection<String> roleIdentifiers) throws AuthorizationException;
+    
+    /**
+     * Same as {@link #checkRoles(Collection<String> roleIdentifiers) checkRoles(Collection<String> roleIdentifiers)} but
+     * doesn't require a collection as a an argument.
+     * Asserts this Subject has all of the specified roles by returning quietly if they do or throwing an
+     * {@link org.apache.shiro.authz.AuthorizationException} if they do not.
+     * 
+     * @param roleIdentifiers roleIdentifiers the application-specific role identifiers to check (usually role ids or role names).
+     * @throws AuthorizationException org.apache.shiro.authz.AuthorizationException
+     *          if this Subject does not have all of the specified roles.
+     * @since 1.1.0
+     */
+    void checkRoles(String... roleIdentifiers) throws AuthorizationException;
 
     /**
      * Performs a login attempt for this Subject/user.  If unsuccessful,

Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java Tue Aug  3 04:12:36 2010
@@ -233,6 +233,9 @@ public class DelegatingSubject implement
         assertAuthzCheckPossible();
         securityManager.checkRole(getPrincipals(), role);
     }
+    
+    public void checkRoles(String... roleIdentifiers) throws AuthorizationException {
+    }
 
     public void checkRoles(Collection<String> roles) throws AuthorizationException {
         assertAuthzCheckPossible();
@@ -456,5 +459,4 @@ public class DelegatingSubject implement
 
         return popped;
     }
-
 }

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java Tue Aug  3 04:12:36 2010
@@ -19,6 +19,7 @@
 package org.apache.shiro.authz.aop;
 
 import org.apache.shiro.authz.UnauthenticatedException;
+import org.apache.shiro.authz.annotation.Logical;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.apache.shiro.test.SecurityManagerTestSupport;
 import org.junit.Test;
@@ -44,6 +45,10 @@ public class PermissionAnnotationHandler
             public Class<? extends Annotation> annotationType() {
                 return RequiresPermissions.class;
             }
+
+	    public Logical logical() {
+		return Logical.AND;
+	    }
         };
 
         handler.assertAuthorized(requiresPermissionAnnotation);
@@ -63,6 +68,10 @@ public class PermissionAnnotationHandler
             public Class<? extends Annotation> annotationType() {
                 return RequiresPermissions.class;
             }
+            
+	    public Logical logical() {
+		return Logical.AND;
+	    }
         };
 
         handler.assertAuthorized(requiresPermissionAnnotation);

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java Tue Aug  3 04:12:36 2010
@@ -18,8 +18,14 @@
  */
 package org.apache.shiro.authz.aop;
 
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
 import org.apache.shiro.authz.UnauthenticatedException;
+import org.apache.shiro.authz.annotation.Logical;
 import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.apache.shiro.subject.Subject;
 import org.apache.shiro.test.SecurityManagerTestSupport;
 import org.junit.Test;
 
@@ -29,6 +35,7 @@ import java.lang.annotation.Annotation;
  * Test cases for the {@link RoleAnnotationHandler} class.
  */
 public class RoleAnnotationHandlerTest extends SecurityManagerTestSupport {
+    private Subject subject;
 
     //Added to satisfy SHIRO-146
 
@@ -44,6 +51,9 @@ public class RoleAnnotationHandlerTest e
             public Class<? extends Annotation> annotationType() {
                 return RequiresRoles.class;
             }
+	    public Logical logical() {
+		return Logical.AND;
+	    }
         };
 
         handler.assertAuthorized(requiresRolesAnnotation);
@@ -63,8 +73,39 @@ public class RoleAnnotationHandlerTest e
             public Class<? extends Annotation> annotationType() {
                 return RequiresRoles.class;
             }
+	    public Logical logical() {
+		return Logical.AND;
+	    }
+        };
+
+        handler.assertAuthorized(requiresRolesAnnotation);
+    }
+    
+    @Test
+    public void testOneOfTheRolesRequired() throws Throwable {
+	subject = createMock(Subject.class);
+	expect(subject.hasRole("blah")).andReturn(true);
+	expect(subject.hasRole("blah2")).andReturn(false);
+        replay(subject);
+	RoleAnnotationHandler handler = new RoleAnnotationHandler() {
+            @Override
+	    protected Subject getSubject() {
+        	return subject;
+            }
         };
 
+        Annotation requiresRolesAnnotation = new RequiresRoles() {
+            public String[] value() {
+                return new String[]{"blah", "blah2"};
+            }
+
+            public Class<? extends Annotation> annotationType() {
+                return RequiresRoles.class;
+            }
+	    public Logical logical() {
+		return Logical.OR;
+	    }
+        };
         handler.assertAuthorized(requiresRolesAnnotation);
     }
 }

Modified: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java?rev=981734&r1=981733&r2=981734&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java (original)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java Tue Aug  3 04:12:36 2010
@@ -249,6 +249,7 @@ public class AuthorizingRealmTest {
             principals.add(USER_ID + USERNAME);
             return new SimpleAuthenticationInfo(principals, PASSWORD, getName());
         }
+
     }
 
 }
\ No newline at end of file