SHIRO-348, Adding boolean to toggle if exceptions should be thrown or ignored.
git-svn-id: https://svn.apache.org/repos/asf/shiro/branches/exceptionCatchingModularRealmAuthorizer@1298870 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java b/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java
index 4a12024..0b99841 100644
--- a/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java
+++ b/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java
@@ -18,12 +18,15 @@
*/
package org.apache.shiro.authz;
+import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.apache.shiro.authz.permission.PermissionResolverAware;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.authz.permission.RolePermissionResolverAware;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
@@ -38,6 +41,11 @@
public class ModularRealmAuthorizer implements Authorizer, PermissionResolverAware, RolePermissionResolverAware {
/**
+ * Logger
+ */
+ private static final Logger log = LoggerFactory.getLogger( ModularRealmAuthorizer.class );
+
+ /**
* The realms to consult during any authorization check.
*/
protected Collection<Realm> realms;
@@ -55,6 +63,13 @@
protected RolePermissionResolver rolePermissionResolver;
/**
+ * A boolean which can be set to ignore Exceptions thrown from AuthorizationRealms. Useful when multiple
+ * realms are configured, and one of them throws Exceptions, and you want other realms to be consulted. <BR/>
+ * NOTE: the default value must be false to keep backwards compatibility.
+ */
+ private boolean ignoreExceptionsFromRealms = false;
+
+ /**
* Default no-argument constructor, does nothing.
*/
public ModularRealmAuthorizer() {
@@ -170,6 +185,46 @@
applyRolePermissionResolverToRealms();
}
+ /**
+ * Returns true if Exceptions thrown by AuthorizationRealms should be ignored.
+ * If the value is false (the default) the AuthorizationExceptions will propagate back to the caller.
+ *
+ * @return
+ * @since 1.3
+ */
+ public boolean isIgnoreExceptionsFromRealms()
+ {
+ return ignoreExceptionsFromRealms;
+ }
+
+ /**
+ * Sets if the Exceptions thrown from AuthorizationRealms should be ignored. Useful when multiple
+ * realms are configured, and one of them throws Exceptions, and you want other realms to be consulted. <BR/>
+ * If the value is false (the default) the AuthorizationExceptions will propagate back to the caller.
+ *
+ * @param ignoreExceptionsFromRealms true if Exceptions should be ignored from AuthorizationRealms.
+ * @since 1.3
+ */
+ public void setIgnoreExceptionsFromRealms( boolean ignoreExceptionsFromRealms )
+ {
+ this.ignoreExceptionsFromRealms = ignoreExceptionsFromRealms;
+ }
+
+ /**
+ * Checks if ShiroExceptions thrown from a Realm when checking authz should be ignored.<BR/>
+ * The default implementation just checks the value of isIgnoreExceptionsFromRealms().
+ * @param realm The realm from which the ShiroException was thrown.
+ * @param e The Exception that was thrown by the realm when checking authz.
+ */
+ protected void checkIgnoreExceptionsFromRealm( Realm realm, ShiroException e) {
+ if(isIgnoreExceptionsFromRealms()) {
+ log.trace( "Realm: [{}], caused an exception that will be ignored: {}", new Object[] {realm, e.getMessage(), e } );
+ }
+ else {
+ throw e;
+ }
+ }
+
/**
* Sets the internal {@link #getRolePermissionResolver} on any internal configured
@@ -220,8 +275,13 @@
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
- if (((Authorizer) realm).isPermitted(principals, permission)) {
- return true;
+ try {
+ if (((Authorizer) realm).isPermitted(principals, permission)) {
+ return true;
+ }
+ }
+ catch(ShiroException e) {
+ checkIgnoreExceptionsFromRealm(realm, e);
}
}
return false;
@@ -236,8 +296,13 @@
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
- if (((Authorizer) realm).isPermitted(principals, permission)) {
- return true;
+ try {
+ if (((Authorizer) realm).isPermitted(principals, permission)) {
+ return true;
+ }
+ }
+ catch(ShiroException e) {
+ checkIgnoreExceptionsFromRealm(realm, e);
}
}
return false;
@@ -371,8 +436,13 @@
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (!(realm instanceof Authorizer)) continue;
- if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
- return true;
+ try {
+ if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
+ return true;
+ }
+ }
+ catch(ShiroException e) {
+ checkIgnoreExceptionsFromRealm(realm, e);
}
}
return false;
diff --git a/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java b/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java
index edd046b..7802911 100644
--- a/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java
+++ b/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java
@@ -18,46 +18,84 @@
*/
package org.apache.shiro.authz;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
-import junit.framework.Assert;
-import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.permission.RolePermissionResolver;
+import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static junit.framework.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
public class ModularRealmAuthorizerTest
{
+ @Rule
+ public ExpectedException expected = ExpectedException.none();
+
+ private static String INVALID_PRIV = "priv:invalid";
+ private static String VALID_PRIV = "priv:valid";
+
+ private static String INVALID_ROLE = "invalid-role";
+ private static String VALID_ROLE = "valid-role";
+
+ private ModularRealmAuthorizer modRealmAuthz;
+ private Collection<Realm> realms = new ArrayList<Realm>();
+
+ private List<Permission> permissions = new ArrayList<Permission>();
+
+ private List<String> roles = new ArrayList<String>();
+
+ private PrincipalCollection principalCollection = EasyMock.createNiceMock(PrincipalCollection.class);
+
+ @Before
+ public void setUp()
+ {
+ SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(Collections.singleton(VALID_ROLE));
+ authorizationInfo.addStringPermission(VALID_PRIV);
+
+ permissions.add(new WildcardPermission(INVALID_PRIV));
+ permissions.add(new WildcardPermission(VALID_PRIV));
+
+ roles.add(INVALID_ROLE);
+ roles.add(VALID_ROLE);
+
+ realms.add(new ExceptionThrowingAuthorizingRealm());
+ realms.add(new MockAuthorizingRealm(authorizationInfo));
+
+ modRealmAuthz = new ModularRealmAuthorizer();
+ modRealmAuthz.setRealms( realms );
+ }
+
+ /**
+ * Tests setting the RolePermissionResolver is propagated to realms.
+ */
@Test
public void testSettingOfRolePermissionResolver()
{
- Collection<Realm> realms = new ArrayList<Realm>();
-
- realms.add( new MockAuthorizingRealm() );
- realms.add( new MockAuthorizingRealm() );
-
- // its null to start with
- for ( Realm realm : realms )
- {
- Assert.assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() );
- }
-
- ModularRealmAuthorizer modRealmAuthz = new ModularRealmAuthorizer();
- modRealmAuthz.setRealms( realms );
+ // make sure initializing a realm has a null rolePermissionResolver (or the next test is not valid)
+ assertNull( new MockAuthorizingRealm().getRolePermissionResolver() );
// make sure they are still null
for ( Realm realm : realms )
{
- Assert.assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() );
+ assertNull( ( (AuthorizingRealm) realm ).getRolePermissionResolver() );
}
// now set the RolePermissionResolver
@@ -74,7 +112,7 @@
for ( Realm realm : realms )
{
// check for same instance
- Assert.assertTrue( ((AuthorizingRealm)realm).getRolePermissionResolver() == rolePermissionResolver );
+ assertTrue( ( (AuthorizingRealm) realm ).getRolePermissionResolver() == rolePermissionResolver );
}
// add a new realm and make sure the RolePermissionResolver is set
@@ -91,25 +129,370 @@
// {
// Assert.assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() );
// }
-
-
}
-
- class MockAuthorizingRealm extends AuthorizingRealm
- {
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals )
- {
- return null;
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedWithString() {
+
+ try {
+ modRealmAuthz.isPermitted(principalCollection, VALID_PRIV);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertFalse( modRealmAuthz.isPermitted( principalCollection, INVALID_PRIV ) );
+ assertTrue(modRealmAuthz.isPermitted(principalCollection, VALID_PRIV));
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedWithPermission() {
+
+ try {
+ modRealmAuthz.isPermitted(principalCollection, new WildcardPermission(VALID_PRIV));
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertFalse(modRealmAuthz.isPermitted(principalCollection, new WildcardPermission(INVALID_PRIV)));
+ assertTrue(modRealmAuthz.isPermitted(principalCollection, new WildcardPermission(VALID_PRIV)));
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedWithStrings() {
+
+ try {
+ modRealmAuthz.isPermitted(principalCollection, INVALID_PRIV, VALID_PRIV);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertThat( modRealmAuthz.isPermitted( principalCollection, INVALID_PRIV, VALID_PRIV ),
+ equalTo( new boolean[]{ false, true } ) );
+ assertThat( modRealmAuthz.isPermitted( principalCollection, permissions ),
+ equalTo( new boolean[]{ false, true } ) );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedWithPermissions() {
+
+ try {
+ modRealmAuthz.isPermitted(principalCollection, permissions);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertThat(modRealmAuthz.isPermitted(principalCollection, permissions), equalTo(new boolean[]{ false, true }));
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedAllWithStrings() {
+
+ try {
+ modRealmAuthz.isPermittedAll( principalCollection, INVALID_PRIV, VALID_PRIV );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertFalse( modRealmAuthz.isPermittedAll( principalCollection, INVALID_PRIV, VALID_PRIV ) );
+ assertTrue( modRealmAuthz.isPermittedAll( principalCollection, VALID_PRIV, VALID_PRIV ) );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testIsPermittedAllWithPermissions() {
+
+ try {
+ modRealmAuthz.isPermittedAll(principalCollection, permissions);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertFalse( modRealmAuthz.isPermittedAll( principalCollection, permissions ) );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testHasRole() {
+
+ try {
+ modRealmAuthz.hasRole( principalCollection, VALID_ROLE );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertTrue(modRealmAuthz.hasRole( principalCollection, VALID_ROLE));
+ assertFalse(modRealmAuthz.hasRole( principalCollection, INVALID_ROLE ));
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testHasRoles() {
+
+ try {
+ modRealmAuthz.hasRoles( principalCollection, roles );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertThat( modRealmAuthz.hasRoles( principalCollection, roles ), equalTo( new boolean[]{ false, true } ) );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testHasAllRoles() {
+
+ try {
+ modRealmAuthz.hasAllRoles( principalCollection, roles );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ assertFalse( modRealmAuthz.hasAllRoles( principalCollection, roles ) );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckPermissionWithString() {
+
+ try {
+ modRealmAuthz.checkPermission( principalCollection, VALID_PRIV );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkPermission( principalCollection, VALID_PRIV );
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckPermissionWithStrings() {
+
+ try {
+ modRealmAuthz.checkPermission(principalCollection, new WildcardPermission(VALID_PRIV));
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkPermission(principalCollection, new WildcardPermission(VALID_PRIV));
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckPermissionsWithStrings() {
+
+ try {
+ modRealmAuthz.checkPermissions(principalCollection, VALID_PRIV );
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkPermissions(principalCollection, VALID_PRIV);
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckPermissionsWithPermissions() {
+
+ Collection<Permission> validPermissions = Collections.<Permission>singleton(new WildcardPermission(VALID_PRIV));
+ try {
+ modRealmAuthz.checkPermissions(principalCollection, validPermissions);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkPermissions(principalCollection, validPermissions);
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckRole() {
+
+ try {
+ modRealmAuthz.checkRole(principalCollection, VALID_ROLE);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkRole(principalCollection, VALID_ROLE);
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckRoles() {
+
+ try {
+ modRealmAuthz.checkRoles(principalCollection, VALID_ROLE);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkRoles(principalCollection, VALID_ROLE);
+ }
+
+ /**
+ * @since 1.3
+ */
+ @Test
+ public void testCheckRolesCollection() {
+
+ Collection<String> validRoles = Collections.singleton(VALID_ROLE);
+ try {
+ modRealmAuthz.checkRoles(principalCollection, validRoles);
+ fail("Expected AuthorizationException");
+ }
+ catch(AuthorizationException e){
+ // expected
+ }
+
+ // now with ignoring exceptions
+ modRealmAuthz.setIgnoreExceptionsFromRealms( true );
+
+ modRealmAuthz.checkRoles(principalCollection, validRoles);
+ }
+
+ class MockAuthorizingRealm extends AuthorizingRealm {
+
+ private final AuthorizationInfo authorizationInfo;
+
+ public MockAuthorizingRealm () {
+ this(null);
+ }
+
+ public MockAuthorizingRealm( AuthorizationInfo authorizationInfo ) {
+ this.authorizationInfo = authorizationInfo;
}
@Override
- protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token )
- throws AuthenticationException
- {
+ protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals ) {
+ return authorizationInfo;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) {
return null;
}
-
+ }
+
+ class ExceptionThrowingAuthorizingRealm extends AuthorizingRealm {
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals ) {
+ throw new AuthorizationException( "Thrown by a Test Realm that only throws exceptions." );
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) {
+ return null;
+ }
}
}