/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.acls.domain; import static org.assertj.core.api.Assertions.*; import org.junit.*; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.acls.model.Acl; import org.springframework.security.acls.model.MutableAcl; import org.springframework.security.acls.model.NotFoundException; import org.springframework.security.acls.model.ObjectIdentity; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; /** * Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl} security * checks. * * @author Andrei Stefan */ public class AclImplementationSecurityCheckTests { private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject"; // ~ Methods // ======================================================================================================== @Before public void setUp() throws Exception { SecurityContextHolder.clearContext(); } @After public void tearDown() throws Exception { SecurityContextHolder.clearContext(); } @Test public void testSecurityCheckNoACEs() throws Exception { Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL", "ROLE_AUDITING", "ROLE_OWNERSHIP"); auth.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(auth); ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl( new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority( "ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL")); Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger()); aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL); aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING); aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP); // Create another authorization strategy AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl( new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority( "ROLE_TWO"), new SimpleGrantedAuthority("ROLE_THREE")); Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2, new ConsoleAuditLogger()); // Check access in case the principal has no authorization rights try { aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } try { aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } try { aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } } @Test public void testSecurityCheckWithMultipleACEs() throws Exception { // Create a simple authentication with ROLE_GENERAL Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL"); auth.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(auth); ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100)); // Authorization strategy will require a different role for each access AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl( new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority( "ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL")); // Let's give the principal the ADMINISTRATION permission, without // granting access MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger()); aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false); // The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_GENERAL); // The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the // principal doesn't have these authorities, // nor granting access try { aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING); fail("It should have thrown AccessDeniedException"); } catch (AccessDeniedException expected) { } try { aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP); fail("It should have thrown AccessDeniedException"); } catch (AccessDeniedException expected) { } // Add granting access to this principal aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true); // and try again for CHANGE_AUDITING - the first ACE's granting flag // (false) will deny this access try { aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING); fail("It should have thrown AccessDeniedException"); } catch (AccessDeniedException expected) { } // Create another ACL and give the principal the ADMINISTRATION // permission, with granting access MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger()); aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true); // The CHANGE_AUDITING test should pass as there is one ACE with // granting access aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING); // Add a deny ACE and test again for CHANGE_AUDITING aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false); try { aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING); } catch (AccessDeniedException notExpected) { fail("It shouldn't have thrown AccessDeniedException"); } // Create an ACL with no ACE MutableAcl aclNoACE = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger()); try { aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } // and still grant access for CHANGE_GENERAL try { aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL); } catch (NotFoundException expected) { fail("It shouldn't have thrown NotFoundException"); } } @Test public void testSecurityCheckWithInheritableACEs() throws Exception { // Create a simple authentication with ROLE_GENERAL Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL"); auth.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(auth); ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100); // Authorization strategy will require a different role for each access AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl( new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority( "ROLE_TWO"), new SimpleGrantedAuthority("ROLE_GENERAL")); // Let's give the principal an ADMINISTRATION permission, with granting // access MutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger()); parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true); MutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy, new ConsoleAuditLogger()); // Check against the 'child' acl, which doesn't offer any authorization // rights on CHANGE_OWNERSHIP try { aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP); fail("It should have thrown NotFoundException"); } catch (NotFoundException expected) { } // Link the child with its parent and test again against the // CHANGE_OWNERSHIP right childAcl.setParent(parentAcl); childAcl.setEntriesInheriting(true); try { aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP); } catch (NotFoundException expected) { fail("It shouldn't have thrown NotFoundException"); } // Create a root parent and link it to the middle parent MutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger()); parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger()); rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true); parentAcl.setEntriesInheriting(true); parentAcl.setParent(rootParentAcl); childAcl.setParent(parentAcl); try { aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP); } catch (NotFoundException expected) { fail("It shouldn't have thrown NotFoundException"); } } @Test public void testSecurityCheckPrincipalOwner() throws Exception { Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_ONE"); auth.setAuthenticated(true); SecurityContextHolder.getContext().setAuthentication(auth); ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100); AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl( new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority( "ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL")); Acl acl = new AclImpl(identity, 1, aclAuthorizationStrategy, new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null, null, false, new PrincipalSid(auth)); try { aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL); } catch (AccessDeniedException notExpected) { fail("It shouldn't have thrown AccessDeniedException"); } try { aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING); fail("It shouldn't have thrown AccessDeniedException"); } catch (NotFoundException expected) { } try { aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP); } catch (AccessDeniedException notExpected) { fail("It shouldn't have thrown AccessDeniedException"); } } }