From 0e3ee46988effcedf596b8bc9ab79845309e86f6 Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 25 Nov 2015 16:08:22 +0000 Subject: [PATCH] CCM NG: SecuredIterator: Iterator for CcmObjects which checks permissions for the objects before returning them. git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@3748 8810af33-2d31-482b-a856-94f89814c4df --- .../libreccm/security/SecuredIterator.java | 86 +++++ .../security/SecuredIteratorTest.java | 360 ++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java create mode 100644 ccm-core/src/test/java/org/libreccm/security/SecuredIteratorTest.java diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java b/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java new file mode 100644 index 000000000..0dfbaf96d --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.security; + +import com.arsdigita.util.UncheckedWrapperException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.cdi.utils.CdiLookupException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; + +import java.util.Iterator; + +/** + * + * @author Jens Pelzetter + * @param + */ +public class SecuredIterator implements Iterator { + + private static final Logger LOGGER = LogManager.getLogger(SecuredIterator.class); + + private final Iterator iterator; + + private final Class clazz; + + private final String requiredPrivilege; + + public SecuredIterator(final Iterator iterator, + final Class clazz, + final String requiredPrivilege) { + this.iterator = iterator; + this.clazz = clazz; + this.requiredPrivilege = requiredPrivilege; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public E next() { + final CdiUtil cdiUtil = new CdiUtil(); + final PermissionChecker permissionChecker ; + try { + permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException(ex); + } + + final E object = iterator.next(); + if (permissionChecker.isPermitted(requiredPrivilege, object)) { + return object; + } else { + try { + final E placeholder = clazz.newInstance(); + placeholder.setDisplayName("Access denied"); + + return placeholder; + } catch (InstantiationException | IllegalAccessException ex) { + LOGGER.error("Failed to create placeholder object. Returing null.", ex); + return null; + } + } + } + +} diff --git a/ccm-core/src/test/java/org/libreccm/security/SecuredIteratorTest.java b/ccm-core/src/test/java/org/libreccm/security/SecuredIteratorTest.java new file mode 100644 index 000000000..c74325c40 --- /dev/null +++ b/ccm-core/src/test/java/org/libreccm/security/SecuredIteratorTest.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2015 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.security; + +import com.arsdigita.kernel.KernelConfig; +import com.arsdigita.kernel.security.SecurityConfig; +import com.arsdigita.runtime.AbstractConfig; +import com.arsdigita.util.UncheckedWrapperException; +import com.arsdigita.util.parameter.AbstractParameterContext; +import com.arsdigita.web.CCMApplicationContextListener; +import com.arsdigita.xml.XML; +import com.arsdigita.xml.formatters.DateTimeFormatter; + +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.subject.Subject; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.junit.InSequence; +import org.jboss.arquillian.persistence.CreateSchema; +import org.jboss.arquillian.persistence.PersistenceTest; +import org.jboss.arquillian.persistence.UsingDataSet; +import org.jboss.arquillian.transaction.api.annotation.TransactionMode; +import org.jboss.arquillian.transaction.api.annotation.Transactional; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.resolver.api.maven.Maven; +import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.libreccm.categorization.Categorization; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.core.CcmObjectRepository; +import org.libreccm.jpa.EntityManagerProducer; +import org.libreccm.jpa.utils.MimeTypeConverter; +import org.libreccm.l10n.LocalizedString; +import org.libreccm.tests.categories.IntegrationTest; +import org.libreccm.testutils.EqualsVerifier; +import org.libreccm.web.CcmApplication; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; + +import javax.inject.Inject; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +/** + * + * @author Jens Pelzetter + */ +@Category(IntegrationTest.class) +@RunWith(Arquillian.class) +@PersistenceTest +@Transactional(TransactionMode.COMMIT) +@CreateSchema({"create_ccm_core_schema.sql"}) +public class SecuredIteratorTest { + + private static final String ACCESS_DENIED = "Access denied"; + + @Inject + private Subject subject; + + @Inject + private Shiro shiro; + + @Inject + private CcmObjectRepository objectRepository; + + private List list; + + public SecuredIteratorTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + final CcmObject object1 = objectRepository.findById(-20001L); + final CcmObject object2 = objectRepository.findById(-20002L); + final CcmObject object3 = objectRepository.findById(-20003L); + + list = new ArrayList<>(); + list.add(object1); + list.add(object2); + list.add(object3); + } + + @After + public void tearDown() { + } + + @Deployment + public static WebArchive createDeployment() { + final PomEquippedResolveStage pom = Maven + .resolver() + .loadPomFromFile("pom.xml"); + final PomEquippedResolveStage dependencies = pom. + importCompileAndRuntimeDependencies(); + final File[] libs = dependencies.resolve().withTransitivity().asFile(); + + for (File lib : libs) { + System.err.printf("Adding file '%s' to test archive...%n", + lib.getName()); + } + + return ShrinkWrap + .create(WebArchive.class, + "LibreCCM-org.libreccm.security.SecuredIteratorTest.war") + .addPackage(User.class.getPackage()) + .addPackage(CcmObject.class.getPackage()) + .addPackage(Categorization.class.getPackage()) + .addPackage(LocalizedString.class.getPackage()) + .addPackage(CcmApplication.class.getPackage()) + .addPackage(EntityManagerProducer.class.getPackage()) + .addPackage(MimeTypeConverter.class.getPackage()) + .addPackage(EqualsVerifier.class.getPackage()) + .addPackage(IntegrationTest.class.getPackage()) + .addPackage(KernelConfig.class.getPackage()) + .addPackage(SecurityConfig.class.getPackage()) + .addPackage(AbstractConfig.class.getPackage()) + .addPackage(AbstractParameterContext.class.getPackage()) + .addPackage(UncheckedWrapperException.class.getPackage()) + .addPackage(CCMApplicationContextListener.class.getPackage()) + .addPackage(XML.class.getPackage()) + .addPackage(DateTimeFormatter.class.getPackage()) + .addPackage(CdiUtil.class.getPackage()) + .addAsLibraries(libs) + .addAsResource("test-persistence.xml", + "META-INF/persistence.xml") + .addAsResource("com/arsdigita/kernel/" + + "KernelConfig_parameter.properties", + "com/arsdigita/kernel/" + + "KernelConfig_parameter.properties") + .addAsResource("com/arsdigita/kernel/security/" + + "SecurityConfig_parameter.properties", + "com/arsdigita/kernel/security/" + + "SecurityConfig_parameter.properties") + .addAsWebInfResource( + "configs/org/libreccm/security/UserManagerTest/" + + "registry.properties", + "conf/registry/registry.properties") + .addAsResource( + "configs/org/libreccm/security/UserManagerTest/ccm-core.config", + "ccm-core.config") + .addAsResource( + "configs/org/libreccm/security/ShiroTest/shiro.ini", + "shiro.ini") + .addAsResource( + "configs/org/libreccm/security/ShiroTest/log4j2.xml", + "log4j2.xml") + .addAsWebInfResource( + "configs/org/libreccm/security/ShiroTest/" + + "kernel.properties", + "conf/registry/ccm-core/kernel.properties") + .addAsWebInfResource( + "configs/org/libreccm//security/ShiroTest/" + + "security.properties", + "conf/registry/ccm-core/security.properties") + .addAsWebInfResource("test-web.xml", "web.xml") + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); + } + + @Test + @UsingDataSet("datasets/org/libreccm/security/ShiroTest/data.yml") + @InSequence(100) + public void checkSecuredIteratorJdoe() { + final SecuredIterator iterator1 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege1"); + final SecuredIterator iterator2 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege2"); + final SecuredIterator iterator3 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege3"); + + final UsernamePasswordToken token = new UsernamePasswordToken("jdoe", + "foo123"); + token.setRememberMe(true); + subject.login(token); + + final List list1 = new ArrayList<>(); + while (iterator1.hasNext()) { + list1.add(iterator1.next()); + } + assertThat(list1.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list1.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list1.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + final List list2 = new ArrayList<>(); + while (iterator2.hasNext()) { + list2.add(iterator2.next()); + } + assertThat(list2.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list2.get(1).getDisplayName(), is(equalTo("object2"))); + assertThat(list2.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + final List list3 = new ArrayList<>(); + while (iterator3.hasNext()) { + list3.add(iterator3.next()); + } + assertThat(list3.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list3.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list3.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + } + + @Test + @UsingDataSet("datasets/org/libreccm/security/ShiroTest/data.yml") + @InSequence(200) + public void checkSecuredIteratorMmuster() { + final SecuredIterator iterator1 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege1"); + final SecuredIterator iterator2 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege2"); + final SecuredIterator iterator3 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege3"); + + final UsernamePasswordToken token = new UsernamePasswordToken("mmuster", + "foo123"); + token.setRememberMe(true); + subject.login(token); + + final List list1 = new ArrayList<>(); + while (iterator1.hasNext()) { + list1.add(iterator1.next()); + } + assertThat(list1.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list1.get(1).getDisplayName(), is(equalTo("object2"))); + assertThat(list1.get(2).getDisplayName(), is(equalTo("object3"))); + + final List list2 = new ArrayList<>(); + while (iterator2.hasNext()) { + list2.add(iterator2.next()); + } + assertThat(list2.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list2.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list2.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + final List list3 = new ArrayList<>(); + while (iterator3.hasNext()) { + list3.add(iterator3.next()); + } + assertThat(list3.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list3.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list3.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + } + + @Test + @UsingDataSet("datasets/org/libreccm/security/ShiroTest/data.yml") + @InSequence(300) + public void checkSecuredIteratorPublicUser() { + final SecuredIterator iterator1 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege1"); + final SecuredIterator iterator2 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege2"); + final SecuredIterator iterator3 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege3"); + + final List list1 = new ArrayList<>(); + while (iterator1.hasNext()) { + list1.add(iterator1.next()); + } + assertThat(list1.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list1.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list1.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + final List list2 = new ArrayList<>(); + while (iterator2.hasNext()) { + list2.add(iterator2.next()); + } + assertThat(list2.get(0).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list2.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list2.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + + final List list3 = new ArrayList<>(); + while (iterator3.hasNext()) { + list3.add(iterator3.next()); + } + assertThat(list3.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list3.get(1).getDisplayName(), is(equalTo(ACCESS_DENIED))); + assertThat(list3.get(2).getDisplayName(), is(equalTo(ACCESS_DENIED))); + } + + @Test + @UsingDataSet("datasets/org/libreccm/security/ShiroTest/data.yml") + @InSequence(400) + public void checkSecuredIteratorSystemUser() { + final SecuredIterator iterator1 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege1"); + final SecuredIterator iterator2 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege2"); + final SecuredIterator iterator3 = new SecuredIterator<>( + list.iterator(), CcmObject.class, "privilege3"); + + shiro.getSystemUser().execute(new Callable() { + + @Override + public Boolean call() { + final List list1 = new ArrayList<>(); + while (iterator1.hasNext()) { + list1.add(iterator1.next()); + } + assertThat(list1.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list1.get(1).getDisplayName(), is(equalTo("object2"))); + assertThat(list1.get(2).getDisplayName(), is(equalTo("object3"))); + + final List list2 = new ArrayList<>(); + while (iterator2.hasNext()) { + list2.add(iterator2.next()); + } + assertThat(list2.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list2.get(1).getDisplayName(), is(equalTo("object2"))); + assertThat(list2.get(2).getDisplayName(), is(equalTo("object3"))); + + final List list3 = new ArrayList<>(); + while (iterator3.hasNext()) { + list3.add(iterator3.next()); + } + assertThat(list3.get(0).getDisplayName(), is(equalTo("object1"))); + assertThat(list3.get(1).getDisplayName(), is(equalTo("object2"))); + assertThat(list3.get(2).getDisplayName(), is(equalTo("object3"))); + + return false; + } + + }); + } + +}