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;
+ }
+
+ });
+ }
+
+}