diff --git a/ccm-core/src/main/java/org/libreccm/security/AuthorizationInterceptor.java b/ccm-core/src/main/java/org/libreccm/security/AuthorizationInterceptor.java
index 8a553e6d1..1d21b3b39 100644
--- a/ccm-core/src/main/java/org/libreccm/security/AuthorizationInterceptor.java
+++ b/ccm-core/src/main/java/org/libreccm/security/AuthorizationInterceptor.java
@@ -74,6 +74,8 @@ public class AuthorizationInterceptor {
* @throws Exception If any exception occurs.
*/
@AroundInvoke
+ // throws Exception necessary because InvocationContext#proceed has also throws Exception
+ @SuppressWarnings("PMD.SignatureDeclareThrowsException")
public Object intercept(final InvocationContext context) throws Exception {
LOGGER.debug("Intercepting method invocation");
@@ -123,6 +125,7 @@ public class AuthorizationInterceptor {
* @param parameter The parameter to check.
* @param annotations All annotations of the parameter.
*/
+ @SuppressWarnings("PMD.UseVarargs")
private void checkParameterPermission(final Object parameter,
final Annotation[] annotations) {
if (parameter instanceof CcmObject
@@ -131,7 +134,7 @@ public class AuthorizationInterceptor {
final CcmObject object = (CcmObject) parameter;
String requiredPrivilege = null;
- for (Annotation annotation : annotations) {
+ for (final Annotation annotation : annotations) {
if (annotation instanceof RequiresPrivilege) {
requiredPrivilege = ((RequiresPrivilege) annotation).value();
break;
diff --git a/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java
index 0f0e9b984..8c99bf1f7 100644
--- a/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java
+++ b/ccm-core/src/main/java/org/libreccm/security/CcmShiroRealm.java
@@ -39,16 +39,19 @@ import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.CDI;
/**
- * Implementation of the Shiro's {@link AuthorizingRealm} to provide Shiro with
- * the users, groups, roles and permissions stored in CCM's database.
+ * Implementation of Shiro's {@link AuthorizingRealm} to provide Shiro with the
+ * users, groups, roles and permissions stored in CCM's database.
*
* @author Jens Pelzetter
*/
+@SuppressWarnings({"PMD.CyclomaticComplexity",
+ "PMD.ModifiedCyclomaticComplexity",
+ "PMD.StdCyclomaticComplexity"})
public class CcmShiroRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
- final PrincipalCollection principals) {
+ final PrincipalCollection principals) {
// Get the pricipal (object identifing the user).
final Object principal = principals.getPrimaryPrincipal();
@@ -56,9 +59,9 @@ public class CcmShiroRealm extends AuthorizingRealm {
// This realm expects the principal to be a string.
if (!(principal instanceof String)) {
throw new AuthenticationException(String.format(
- "Can' process principal of "
- + "type \"%s\".",
- principal.getClass().getName()));
+ "Can' process principal of "
+ + "type \"%s\".",
+ principal.getClass().getName()));
}
// Convert the pricipal to a string.
final String userIdentifier = (String) principal;
@@ -70,30 +73,31 @@ public class CcmShiroRealm extends AuthorizingRealm {
final RoleRepository roleRepository;
final BeanManager beanManager = CDI.current().getBeanManager();
final Set> beans = beanManager.
- getBeans(RoleRepository.class);
+ getBeans(RoleRepository.class);
final Iterator> iterator = beans.iterator();
if (iterator.hasNext()) {
@SuppressWarnings("unchecked")
- final Bean bean = (Bean) iterator.
- next();
+ final Bean bean
+ = (Bean) iterator.
+ next();
final CreationalContext ctx = beanManager.
- createCreationalContext(bean);
+ createCreationalContext(bean);
roleRepository = (RoleRepository) beanManager.getReference(
- bean, RoleRepository.class, ctx);
+ bean, RoleRepository.class, ctx);
} else {
throw new AuthenticationException(
- "Failed to retrieve RoleRepository");
+ "Failed to retrieve RoleRepository");
}
-
+
final List roles = roleRepository.findAll();
-
+
final SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- for(final Role role : roles) {
+ for (final Role role : roles) {
info.addRole(role.getName());
}
info.addStringPermission("*");
-
+
return info;
}
@@ -110,7 +114,7 @@ public class CcmShiroRealm extends AuthorizingRealm {
// Add the permissions assigned to the role to the AuthorizatonInfo.
for (final Permission permission : roleMembership.getRole()
- .getPermissions()) {
+ .getPermissions()) {
info.addStringPermission(permissionToString(permission));
}
}
@@ -119,13 +123,13 @@ public class CcmShiroRealm extends AuthorizingRealm {
for (final GroupMembership membership : user.getGroupMemberships()) {
// Get the roles assigned to the group
for (final RoleMembership roleMembership : membership.getGroup()
- .getRoleMemberships()) {
+ .getRoleMemberships()) {
// Add the role to the AuthorizationInfo
info.addRole(roleMembership.getRole().getName());
// Add the permissions assigned to the role to the
// AuthorizationInfo
for (final Permission permission : roleMembership.getRole()
- .getPermissions()) {
+ .getPermissions()) {
info.addStringPermission(permissionToString(permission));
}
}
@@ -136,8 +140,8 @@ public class CcmShiroRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
- final AuthenticationToken token)
- throws AuthenticationException {
+ final AuthenticationToken token)
+ throws AuthenticationException {
// Get the pricipal identifing the user
final Object principal = token.getPrincipal();
@@ -145,9 +149,9 @@ public class CcmShiroRealm extends AuthorizingRealm {
// This realm expects the pricipal to be a string
if (!(principal instanceof String)) {
throw new AuthenticationException(String.format(
- "Can' process authentication token with a principal of "
- + "type \"%s\".",
- principal.getClass().getName()));
+ "Can' process authentication token with a principal of "
+ + "type \"%s\".",
+ principal.getClass().getName()));
}
// Convert the pricipal to a string.
@@ -168,9 +172,11 @@ public class CcmShiroRealm extends AuthorizingRealm {
* address of the user.
*
* @param userIdentifier The identifier of the user.
+ *
* @return The User identified by the provided {@code userIdentifier}.
+ *
* @throws AuthenticationException if no user for the provided identifier
- * could be retrieved.
+ * could be retrieved.
*/
private User findUser(final String userIdentifier) {
// For some reason we can't use the the CdiUtil class here, therefore
@@ -178,20 +184,20 @@ public class CcmShiroRealm extends AuthorizingRealm {
final UserRepository userRepository;
final BeanManager beanManager = CDI.current().getBeanManager();
final Set> beans = beanManager.getBeans(
- UserRepository.class);
+ UserRepository.class);
final Iterator> iterator = beans.iterator();
if (iterator.hasNext()) {
@SuppressWarnings("unchecked")
final Bean bean = (Bean) iterator
- .next();
+ .next();
final CreationalContext ctx = beanManager
- .createCreationalContext(bean);
+ .createCreationalContext(bean);
userRepository = (UserRepository) beanManager.getReference(
- bean, UserRepository.class, ctx);
+ bean, UserRepository.class, ctx);
} else {
throw new AuthenticationException(
- "Failed to retrieve UserRepository.");
+ "Failed to retrieve UserRepository.");
}
// Depending of the configuration of CCM use the appropriate method
@@ -207,9 +213,9 @@ public class CcmShiroRealm extends AuthorizingRealm {
// If no matching user is found throw an AuthenticationException
if (user == null) {
throw new AuthenticationException(String.format(
- "No user identified by principal \"%s\" was found. Primary user "
- + "identifier is \"%s\".",
- userIdentifier, config.getPrimaryUserIdentifier()));
+ "No user identified by principal \"%s\" was found. Primary user "
+ + "identifier is \"%s\".",
+ userIdentifier, config.getPrimaryUserIdentifier()));
}
return user;
@@ -220,6 +226,7 @@ public class CcmShiroRealm extends AuthorizingRealm {
* used by Shiro.
*
* @param permission The permission to convert.
+ *
* @return A Shiro permission string.
*/
private String permissionToString(final Permission permission) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/Group.java b/ccm-core/src/main/java/org/libreccm/security/Group.java
index f2590e383..3ba918b05 100644
--- a/ccm-core/src/main/java/org/libreccm/security/Group.java
+++ b/ccm-core/src/main/java/org/libreccm/security/Group.java
@@ -36,7 +36,7 @@ import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
/**
- * A group is bascially a collection of users.
+ * A group is basically a collection of users.
*
* Group extends the {@link Party} class. Therefore {@link Role}s can be
* assigned to a group. When a {@link Role} is assigned to a group each member
@@ -113,6 +113,14 @@ public class Group extends Party implements Serializable {
return obj instanceof Group;
}
+ @Override
+ @SuppressWarnings("PMD.UselessOverridingMethod")
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+
+
@Override
public String toString(final String data) {
return super.toString(String.format(", members = { %s }%s",
diff --git a/ccm-core/src/main/java/org/libreccm/security/InheritsPermissions.java b/ccm-core/src/main/java/org/libreccm/security/InheritsPermissions.java
index abab64f3c..023a55968 100644
--- a/ccm-core/src/main/java/org/libreccm/security/InheritsPermissions.java
+++ b/ccm-core/src/main/java/org/libreccm/security/InheritsPermissions.java
@@ -21,7 +21,13 @@ package org.libreccm.security;
import org.libreccm.core.CcmObject;
/**
- *
+ * Subclasses of {@link CcmObject} can implement this interface to inherit
+ * the permissions of their parent object. This annotation is processed by the
+ * {@link PermissionChecker}.
+ *
+ * @see PermissionChecker#checkPermission(java.lang.String, org.libreccm.core.CcmObject)
+ * @see PermissionChecker#isPermitted(java.lang.String, org.libreccm.core.CcmObject)
+ *
* @author Jens Pelzetter
*/
public interface InheritsPermissions {
diff --git a/ccm-core/src/main/java/org/libreccm/security/Permission.java b/ccm-core/src/main/java/org/libreccm/security/Permission.java
index f32a7f79b..4e6d7729f 100644
--- a/ccm-core/src/main/java/org/libreccm/security/Permission.java
+++ b/ccm-core/src/main/java/org/libreccm/security/Permission.java
@@ -43,7 +43,7 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
- * A permission grants a privilege on an object or systemwide to {@link Role}.
+ * A permission grants a privilege on an object or system wide to {@link Role}.
*
* @author Jens Pelzetter
*/
diff --git a/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java b/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java
index cd892004f..153cca7b0 100644
--- a/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java
+++ b/ccm-core/src/main/java/org/libreccm/security/PermissionManager.java
@@ -37,8 +37,11 @@ import javax.enterprise.context.RequestScoped;
@RequestScoped
public class PermissionManager {
+ @SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_OBJECT = "object";
+ @SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_GRANTEE = "grantee";
+ @SuppressWarnings("PMD.LongVariable")
private static final String QUERY_PARAM_PRIVILEGE = "privilege";
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredCollection.java b/ccm-core/src/main/java/org/libreccm/security/SecuredCollection.java
index 3e7ca6546..b5e837224 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredCollection.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredCollection.java
@@ -20,8 +20,6 @@ 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;
@@ -30,24 +28,59 @@ import java.util.Collection;
import java.util.Iterator;
/**
+ * A decorator for collections which checks if the current user is permitted to
+ * access the objects in the collection before returning them.
+ *
+ * This implementation of the {@link Collection} interface decorates a given
+ * collection. The methods which manipulate the collection are simply calling
+ * the methods of decorated collection. The methods which retrieve objects from
+ * the collection are first calling the method of the decorated collection, and
+ * check if the current subject is permitted to access the object. If the
+ * current subject is permitted to access the object the object is returned.
+ * Otherwise the object is replaced with a virtual object were the
+ * {@link CcmObject#displayName} property is set to {@code Access Denied}.
+ * Methods which return arrays or collections of objects from the decorated
+ * collection check each object in the array or collection and replace the
+ * objects which the current subject is not permitted to access with a
+ * Access denied object.
*
* @author Jens Pelzetter
*
- * @param
+ * @param Type of the objects in the collection. Must extend
+ * {@link CcmObject}.
*/
+@SuppressWarnings("PMD.TooManyMethods")
public class SecuredCollection implements Collection {
- private static final Logger LOGGER = LogManager.getLogger(
- SecuredCollection.class);
-
+ /**
+ * The decorated collection.
+ */
private final Collection collection;
+ /**
+ * The class of the objects in the collection. Required for creating the
+ * virtual Access denied object
+ */
private final Class clazz;
+ /**
+ * The privilege required for accessing the objects in the collection.
+ */
private final String requiredPrivilege;
-
+
+ /**
+ * Helper class for creating the Access denied object.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Create a new secured collection for the provided collection.
+ *
+ * @param collection The collection to secure.
+ * @param clazz The class of the objects in the collection.
+ * @param requiredPrivilege The privilege required to access the objects
+ * in the collection.
+ */
public SecuredCollection(final Collection collection,
final Class clazz,
final String requiredPrivilege) {
@@ -74,7 +107,8 @@ public class SecuredCollection implements Collection {
@Override
public Iterator iterator() {
- return new SecuredIterator<>(collection.iterator(), clazz, requiredPrivilege);
+ return new SecuredIterator<>(collection.iterator(), clazz,
+ requiredPrivilege);
}
@Override
@@ -91,16 +125,17 @@ public class SecuredCollection implements Collection {
final Object[] objects = collection.toArray();
for (int i = 0; i < objects.length; i++) {
- if (!permissionChecker.isPermitted(requiredPrivilege, (E) objects[i])) {
+ if (!permissionChecker
+ .isPermitted(requiredPrivilege, (E) objects[i])) {
objects[i] = securedHelper.generateAccessDeniedObject();
}
}
-
+
return objects;
}
@Override
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({"unchecked", "PMD.UseVarargs"})
public T[] toArray(final T[] array) {
final PermissionChecker permissionChecker;
final CdiUtil cdiUtil = new CdiUtil();
@@ -110,10 +145,11 @@ public class SecuredCollection implements Collection {
} catch (CdiLookupException ex) {
throw new UncheckedWrapperException(ex);
}
-
+
final T[] objects = collection.toArray(array);
- for(int i = 0; i < objects.length; i++) {
- if (!permissionChecker.isPermitted(requiredPrivilege, (CcmObject) objects[i])) {
+ for (int i = 0; i < objects.length; i++) {
+ if (!permissionChecker.isPermitted(requiredPrivilege,
+ (CcmObject) objects[i])) {
objects[i] = (T) securedHelper.generateAccessDeniedObject();
}
}
@@ -156,18 +192,4 @@ public class SecuredCollection implements Collection {
collection.clear();
}
-// private E generateAccessDeniedObject(final Class clazz) {
-// final E placeholder;
-// try {
-// 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/main/java/org/libreccm/security/SecuredEntry.java b/ccm-core/src/main/java/org/libreccm/security/SecuredEntry.java
index 974d62809..d5e9a6b27 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredEntry.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredEntry.java
@@ -23,16 +23,39 @@ import org.libreccm.core.CcmObject;
import java.util.Map;
/**
- *
+ * A decorator for {@link Map.Entry} which checks if the current subject is
+ * permitted to access the value before returning it. If the current subject
+ * is not permitted to access the value it is replaced with an virtual
+ * Access Denied which is an object of the same class as the value.
+ *
+ * This class is not intended for direct use and is therefore only accessible
+ * from the {@code org.libreccm.security} package. The class is used by the
+ * {@link SecuredMap}.
+ *
* @author Jens Pelzetter
- * @param
- * @param
+ * @param The type of the key of the entry.
+ * @param The type of the value of the entry.
*/
class SecuredEntry implements Map.Entry {
+ /**
+ * The decorated entry.
+ */
private final Map.Entry entry;
+ /**
+ * {@link SecuredHelper} for creating the virtual Access denied
+ * object. Provided by the {@link SecuredMap} which creates the
+ * {@code SecuredEntry}.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured entry.
+ *
+ * @param entry The entry to secure.
+ * @param securedHelper The {@link SecuredHelper} for creating the
+ * virtual Access denied object.
+ */
public SecuredEntry(final Map.Entry entry,
final SecuredHelper securedHelper) {
this.entry = entry;
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredEntryIterator.java b/ccm-core/src/main/java/org/libreccm/security/SecuredEntryIterator.java
index 488721cba..720924a73 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredEntryIterator.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredEntryIterator.java
@@ -24,15 +24,31 @@ import java.util.Iterator;
import java.util.Map;
/**
+ * A decorator for an iterator of {@link Map.Entry} objects which returns
+ * {@link SecuredEntry} objects. Used by the {@link SecuredMap}.
*
* @author Jens Pelzetter
*/
class SecuredEntryIterator, K, V extends CcmObject>
implements Iterator {
+ /**
+ * The decorated iterator.
+ */
private final Iterator iterator;
+ /**
+ * The {@link SecuredHelper} for creating the virtual Access denied
+ * object. Provided by the {@link SecuredMap} creating the iterator.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured iterator for entries.
+ *
+ * @param iterator The iterator to secure.
+ * @param securedHelper The {@link SecuredHelper} for creating the virtual
+ * Access denied object.
+ */
public SecuredEntryIterator(final Iterator iterator,
final SecuredHelper securedHelper) {
this.iterator = iterator;
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredEntrySet.java b/ccm-core/src/main/java/org/libreccm/security/SecuredEntrySet.java
index 551a341e4..4fbc720c5 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredEntrySet.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredEntrySet.java
@@ -30,126 +30,146 @@ import java.util.Map;
import java.util.Set;
/**
+ * A decorator for a set of {@link Map.Entry} objects which returns
+ * {@link SecuredEntry} instances. Used by the {@link SecuredMap}.
*
* @author Jens Pelzetter
*/
-class SecuredEntrySet, K, V extends CcmObject>
+@SuppressWarnings("PMD.TooManyMethods")
+class SecuredEntrySet, K, V extends CcmObject>
implements Set {
- private final Set set;
- private final String requiredPrivilege;
- private final SecuredHelper securedHelper;
-
- public SecuredEntrySet(final Set set,
- final String requiredPrivilege,
- final SecuredHelper securedHelper) {
- this.set = set;
- this.requiredPrivilege = requiredPrivilege;
- this.securedHelper = securedHelper;
- }
-
- @Override
- public int size() {
- return set.size();
- }
-
- @Override
- public boolean isEmpty() {
- return set.isEmpty();
- }
-
- @Override
- public boolean contains(final Object object) {
- return set.contains(object);
- }
-
- @Override
- public Iterator iterator() {
- return new SecuredEntryIterator<>(set.iterator(), securedHelper);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public Object[] toArray() {
- final PermissionChecker permissionChecker;
- final CdiUtil cdiUtil = new CdiUtil();
- try {
- permissionChecker = cdiUtil.findBean(
- PermissionChecker.class);
- } catch (CdiLookupException ex) {
- throw new UncheckedWrapperException(ex);
- }
-
- final Object[] entries = set.toArray();
- for (int i = 0; i < entries.length; i++) {
- final E entry = (E) entries[i];
- if (!permissionChecker.isPermitted(requiredPrivilege,
- entry.getValue())) {
- entries[i] = securedHelper.generateAccessDeniedObject();
- }
- }
-
- return entries;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public T[] toArray(final T[] array) {
- final PermissionChecker permissionChecker;
- final CdiUtil cdiUtil = new CdiUtil();
- try {
- permissionChecker = cdiUtil.findBean(
- PermissionChecker.class);
- } catch (CdiLookupException ex) {
- throw new UncheckedWrapperException(ex);
- }
-
- final E[] entries = (E[]) set.toArray(array);
- for (int i = 0; i < entries.length; i++) {
- if (!permissionChecker.isPermitted(requiredPrivilege,
- entries[i].getValue())) {
- entries[i] = (E) securedHelper.generateAccessDeniedObject();
- }
- }
-
- return (T[]) entries;
- }
-
- @Override
- public boolean add(final E element) {
- return set.add(element);
- }
-
- @Override
- public boolean remove(final Object object) {
- return set.remove(object);
- }
-
- @Override
- public boolean containsAll(final Collection> collection) {
- return set.containsAll(collection);
- }
-
- @Override
- public boolean addAll(final Collection extends E> collection) {
- return set.addAll(collection);
- }
-
- @Override
- public boolean retainAll(final Collection> collection) {
- return set.retainAll(collection);
- }
-
- @Override
- public boolean removeAll(final Collection> collection) {
- return set.removeAll(collection);
- }
-
- @Override
- public void clear() {
- set.clear();
- }
+ /**
+ * The decorated set.
+ */
+ private final Set set;
+ /**
+ * The privilege required to access the values of the entries in the set.
+ */
+ private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} for creating the virtual Access denied
+ * object.
+ */
+ private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured entry set.
+ *
+ * @param set The set of entries to secure.
+ * @param requiredPrivilege The privilege required to access the values of
+ * the entries.
+ * @param securedHelper The {@link SecuredHelper} for creating the
+ * virtual Access denied objects.
+ */
+ public SecuredEntrySet(final Set set,
+ final String requiredPrivilege,
+ final SecuredHelper securedHelper) {
+ this.set = set;
+ this.requiredPrivilege = requiredPrivilege;
+ this.securedHelper = securedHelper;
}
+ @Override
+ public int size() {
+ return set.size();
+ }
+ @Override
+ public boolean isEmpty() {
+ return set.isEmpty();
+ }
+
+ @Override
+ public boolean contains(final Object object) {
+ return set.contains(object);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new SecuredEntryIterator<>(set.iterator(), securedHelper);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object[] toArray() {
+ final PermissionChecker permissionChecker;
+ final CdiUtil cdiUtil = new CdiUtil();
+ try {
+ permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(ex);
+ }
+
+ final Object[] entries = set.toArray();
+ for (int i = 0; i < entries.length; i++) {
+ final E entry = (E) entries[i];
+ if (!permissionChecker.isPermitted(requiredPrivilege,
+ entry.getValue())) {
+ entries[i] = securedHelper.generateAccessDeniedObject();
+ }
+ }
+
+ return entries;
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "PMD.UseVarargs"})
+ public T[] toArray(final T[] array) {
+ final PermissionChecker permissionChecker;
+ final CdiUtil cdiUtil = new CdiUtil();
+ try {
+ permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(ex);
+ }
+
+ final E[] entries = (E[]) set.toArray(array);
+ for (int i = 0; i < entries.length; i++) {
+ if (!permissionChecker.isPermitted(requiredPrivilege,
+ entries[i].getValue())) {
+ entries[i] = (E) securedHelper.generateAccessDeniedObject();
+ }
+ }
+
+ return (T[]) entries;
+ }
+
+ @Override
+ public boolean add(final E element) {
+ return set.add(element);
+ }
+
+ @Override
+ public boolean remove(final Object object) {
+ return set.remove(object);
+ }
+
+ @Override
+ public boolean containsAll(final Collection> collection) {
+ return set.containsAll(collection);
+ }
+
+ @Override
+ public boolean addAll(final Collection extends E> collection) {
+ return set.addAll(collection);
+ }
+
+ @Override
+ public boolean retainAll(final Collection> collection) {
+ return set.retainAll(collection);
+ }
+
+ @Override
+ public boolean removeAll(final Collection> collection) {
+ return set.removeAll(collection);
+ }
+
+ @Override
+ public void clear() {
+ set.clear();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredHelper.java b/ccm-core/src/main/java/org/libreccm/security/SecuredHelper.java
index 310a7ab77..1011e663d 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredHelper.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredHelper.java
@@ -35,7 +35,13 @@ class SecuredHelper {
private final static Logger LOGGER = LogManager.getLogger(
SecuredHelper.class);
+ /**
+ * Class of the objects in the collection.
+ */
private final Class clazz;
+ /**
+ * Privilege required to access the objects in the collection.
+ */
private final String requiredPrivilege;
protected SecuredHelper(final Class clazz,
@@ -51,8 +57,8 @@ class SecuredHelper {
* @param object The object to check.
* @return The provided {@code object} if the current subject has the
* permission to access it with the provided {@code privilege}. Otherwise a
- * placeholder object is returned whichs {@link CcmObject#displayName}
- * property is set to {@code Access denied}.
+ * placeholder object is returned. The {@link CcmObject#displayName}
+ * property of these object is set to {@code Access denied}.
*/
protected E canAccess(final E object) {
if (object == null) {
@@ -76,7 +82,7 @@ class SecuredHelper {
}
/**
- * Helper method for creating an "Access denied" placeholder object.
+ * Helper method for creating an Access denied placeholder object.
*
* @return An object of the provided {@link #clazz} with it's
* {@link CcmObject#displayName} property set to {@code Access denied}.
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java b/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java
index 168386b30..eeb4c1832 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredIterator.java
@@ -18,12 +18,6 @@
*/
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;
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredList.java b/ccm-core/src/main/java/org/libreccm/security/SecuredList.java
index 73daa9721..2c1049671 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredList.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredList.java
@@ -24,19 +24,43 @@ import java.util.ListIterator;
import org.libreccm.core.CcmObject;
/**
+ * A decorator for {@link List}s of {@link CcmObject}s which checks if the
+ * current subject is permitted to access the objects in the list before
+ * returning them.
*
* @author Jens Pelzetter
- * @param
+ *
+ * @param The type of the objects in the list.
*/
public class SecuredList
- extends SecuredCollection
- implements List {
+ extends SecuredCollection
+ implements List {
+ /**
+ * The decorated list.
+ */
private final List list;
+ /**
+ * The class of the objects in the list.
+ */
private final Class clazz;
+ /**
+ * The privilege required to access the objects in the list.
+ */
private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} used by the list.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured list.
+ *
+ * @param list The {@link List} to secure.
+ * @param clazz Class of the objects in the list.
+ * @param requiredPrivilege The privilege required to access the objects in
+ * the list.
+ */
public SecuredList(final List list,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredListIterator.java b/ccm-core/src/main/java/org/libreccm/security/SecuredListIterator.java
index 67b9c91a2..993a792c3 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredListIterator.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredListIterator.java
@@ -19,25 +19,38 @@
package org.libreccm.security;
import java.util.ListIterator;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
+
import org.libreccm.core.CcmObject;
/**
+ * A decorator for {@link ListIterator} which checks if the current subject is
+ * permitted to access an object from the list the iterator iterates over before
+ * returning it.
*
* @author Jens Pelzetter
* @param
*/
public class SecuredListIterator
- extends SecuredIterator
- implements ListIterator {
-
- private final static Logger LOGGER = LogManager.getLogger(
- SecuredListIterator.class);
+ extends SecuredIterator
+ implements ListIterator {
+ /**
+ * The decorated {@link ListIterator}.
+ */
private final ListIterator iterator;
+ /**
+ * {@link SecuredHelper} used by this iterator.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured list iterator.
+ *
+ * @param iterator The iterator to secure.
+ * @param clazz The class of the objects in the list.
+ * @param requiredPrivilege The privilege required to access the objects in
+ * list.
+ */
public SecuredListIterator(final ListIterator iterator,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredMap.java b/ccm-core/src/main/java/org/libreccm/security/SecuredMap.java
index 31279a38d..63786c7df 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredMap.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredMap.java
@@ -18,30 +18,49 @@
*/
package org.libreccm.security;
-import com.arsdigita.util.UncheckedWrapperException;
-
-import org.libreccm.cdi.utils.CdiLookupException;
-import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.core.CcmObject;
import java.util.Collection;
-import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
+ * Decorator for {@link Map} which checks if the current subject is permitted to
+ * access the values of the decorated map before returning them. The keys used
+ * by the map are not checked.
*
* @author Jens Pelzetter
- * @param
- * @param
+ *
+ * @param Type of the keys
+ * @param Type of the values.
*/
public class SecuredMap implements Map {
+ /**
+ * The decorated map.
+ */
private final Map map;
+ /**
+ * Class of the values in map.
+ */
private final Class clazz;
+ /**
+ * The privilege required to access the values of the map.
+ */
private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} used by the map.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured map.
+ *
+ * @param map The map to secure.
+ * @param clazz Class of the values in the map.
+ * @param requiredPrivilege The privilege required to access the values of
+ * the map.
+ */
public SecuredMap(final Map map,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableMap.java b/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableMap.java
index 4a7951e55..aa8215347 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableMap.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableMap.java
@@ -24,20 +24,44 @@ import java.util.NavigableMap;
import java.util.NavigableSet;
/**
- *
+ * A decorator for {@link NavigableMap} which checks if the current subject is
+ * permitted to access the values of the decorated map before returning them.
+ *
* @author Jens Pelzetter
- * @param
- * @param
+ *
+ * @param Type of the keys.
+ * @param Type of the values.
*/
+@SuppressWarnings("PMD.TooManyMethods")
public class SecuredNavigableMap
extends SecuredSortedMap
implements NavigableMap {
+ /**
+ * The decorated navigable map.
+ */
private final NavigableMap navigableMap;
+ /**
+ * Class of the values.
+ */
private final Class clazz;
+ /**
+ * The privilege required to access the values of the decorated map.
+ */
private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} used by the decorator.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured navigable map.
+ *
+ * @param navigableMap The navigable map to secure.
+ * @param clazz The class of the values of the navigable map.
+ * @param requiredPrivilege The privilege required to access the objects of
+ * the navigable map.
+ */
public SecuredNavigableMap(final NavigableMap navigableMap,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableSet.java b/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableSet.java
index d9fd05a5d..39a5dbf39 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableSet.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredNavigableSet.java
@@ -23,19 +23,44 @@ import java.util.NavigableSet;
import org.libreccm.core.CcmObject;
/**
+ * A decorated for {@link NavigableSet} which checks if the current subject is
+ * permitted to access the objects from the decorated navigable set before
+ * returning them.
*
* @author Jens Pelzetter
- * @param
+ *
+ * @param Type of the objects in the navigable set.
*/
+@SuppressWarnings("PMD.TooManyMethods")
public class SecuredNavigableSet
- extends SecuredSortedSet
- implements NavigableSet {
+ extends SecuredSortedSet
+ implements NavigableSet {
+ /**
+ * The decorated navigable set.
+ */
private final NavigableSet set;
+ /**
+ * Class of the objects in the navigable set.
+ */
private final Class clazz;
+ /**
+ * The privilege required to access the objects of the navigable set.
+ */
private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} used by this decorator.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates a new secured navigable set.
+ *
+ * @param set The set to secure.
+ * @param clazz Class of the objects in the set.
+ * @param requiredPrivilege The privilege required to access the objects in
+ * the set.
+ */
public SecuredNavigableSet(final NavigableSet set,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredSet.java b/ccm-core/src/main/java/org/libreccm/security/SecuredSet.java
index da50405db..15533abad 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredSet.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredSet.java
@@ -22,15 +22,26 @@ import java.util.Set;
import org.libreccm.core.CcmObject;
/**
+ * A decorator for {@link Set} which checks if the current subject is permitted
+ * to access the objects from the decorated set before returning them.
*
* @author Jens Pelzetter
- * @param
+ *
+ * @param Type of the objects in the set.
*/
public class SecuredSet
- extends SecuredCollection
- implements Set {
+ extends SecuredCollection
+ implements Set {
- public SecuredSet(final Set set,
+ /**
+ * Creates a new secured set.
+ *
+ * @param set The {@link Set} to secure.
+ * @param clazz Class of the objects in the set.
+ * @param requiredPrivilege Privilege required to access the objects in the
+ * set.
+ */
+ public SecuredSet(final Set set,
final Class clazz,
final String requiredPrivilege) {
super(set, clazz, requiredPrivilege);
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredSortedMap.java b/ccm-core/src/main/java/org/libreccm/security/SecuredSortedMap.java
index 7bfd66843..22da6e185 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredSortedMap.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredSortedMap.java
@@ -24,19 +24,40 @@ import java.util.Comparator;
import java.util.SortedMap;
/**
+ * A decorator for {@link SortedMap} which checks if the current subject is
+ * permitted to access the values from the decorated sorted map before returning
+ * them.
*
* @author Jens Pelzetter
- * @param
- * @param
+ *
+ * @param Type of the keys.
+ * @param Type of the values.
*/
public class SecuredSortedMap
extends SecuredMap
implements SortedMap {
+ /**
+ * The decorated sorted map.
+ */
private final SortedMap sortedMap;
+ /**
+ * Class of the values of the decorated sorted map.
+ */
private final Class clazz;
+ /**
+ * Privilege required to access the values of the decorated sorted map.
+ */
private final String requiredPrivilege;
+ /**
+ * Creates new secured sorted map.
+ *
+ * @param sortedMap The map to secure.
+ * @param clazz Class of the values.
+ * @param requiredPrivilege Privilege required to access the values of the
+ * secured sorted map.
+ */
public SecuredSortedMap(final SortedMap sortedMap,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/SecuredSortedSet.java b/ccm-core/src/main/java/org/libreccm/security/SecuredSortedSet.java
index adc379d3c..43846fe95 100644
--- a/ccm-core/src/main/java/org/libreccm/security/SecuredSortedSet.java
+++ b/ccm-core/src/main/java/org/libreccm/security/SecuredSortedSet.java
@@ -19,24 +19,48 @@
package org.libreccm.security;
import java.util.Comparator;
-import java.util.Set;
import java.util.SortedSet;
+
import org.libreccm.core.CcmObject;
/**
+ * Decorator for {@link SortedSet} which checks if the current subject is
+ * permitted to access the objects from the decorated sorted set before
+ * returning them.
*
* @author Jens Pelzetter
- * @param
+ *
+ * @param Type of the objects in the decorated sorted set.
*/
public class SecuredSortedSet
- extends SecuredSet
- implements SortedSet {
+ extends SecuredSet
+ implements SortedSet {
+ /**
+ * The decorated sorted set.
+ */
private final SortedSet set;
+ /**
+ * The class of the objects in the decorated sorted set.
+ */
private final Class clazz;
+ /**
+ * The privilege required to access the objects in the decorated set.
+ */
private final String requiredPrivilege;
+ /**
+ * {@link SecuredHelper} used by this decorator.
+ */
private final SecuredHelper securedHelper;
+ /**
+ * Creates new secured sorted set.
+ *
+ * @param set The sorted set to secure.
+ * @param clazz The class of the objects in the set.
+ * @param requiredPrivilege The privilege required to access the objects in
+ * the set.
+ */
public SecuredSortedSet(final SortedSet set,
final Class clazz,
final String requiredPrivilege) {
diff --git a/ccm-core/src/main/java/org/libreccm/security/User.java b/ccm-core/src/main/java/org/libreccm/security/User.java
index 56fe95984..44d5f1848 100644
--- a/ccm-core/src/main/java/org/libreccm/security/User.java
+++ b/ccm-core/src/main/java/org/libreccm/security/User.java
@@ -65,7 +65,7 @@ import javax.xml.bind.annotation.XmlTransient;
@XmlRootElement(name = "user", namespace = CORE_XML_NS)
//Supressing a few warnings from PMD because they misleading here.
//User is perfectly fine class name, and the complexity is not to high...
-@SuppressWarnings({"PMD.ShortClassName"})
+@SuppressWarnings({"PMD.ShortClassName", "PMD.LongVariable"})
public class User extends Party implements Serializable {
private static final long serialVersionUID = 4035223413596611393L;
@@ -97,7 +97,6 @@ public class User extends Party implements Serializable {
}))
@NotNull
@XmlElement(name = "primary-email-address", namespace = CORE_XML_NS)
- @SuppressWarnings("PMD.LongVariable")
private EmailAddress primaryEmailAddress;
/**
@@ -136,7 +135,6 @@ public class User extends Party implements Serializable {
*/
@Column(name = "PASSWORD_RESET_REQUIRED")
//Can't shorten the name without making the name cryptic.
- @SuppressWarnings("PMD.LongVariable")
private boolean passwordResetRequired;
/**
@@ -216,7 +214,6 @@ public class User extends Party implements Serializable {
return passwordResetRequired;
}
- @SuppressWarnings("PMD.LongVariable")
protected void setPasswordResetRequired(final boolean passwordResetRequired) {
this.passwordResetRequired = passwordResetRequired;
}
diff --git a/ccm-core/src/main/java/org/libreccm/security/package-info.java b/ccm-core/src/main/java/org/libreccm/security/package-info.java
index 874390218..d89cb7003 100644
--- a/ccm-core/src/main/java/org/libreccm/security/package-info.java
+++ b/ccm-core/src/main/java/org/libreccm/security/package-info.java
@@ -15,27 +15,27 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
-*/
+ */
/**
- * This package contains all classes dealing with authentication and
+ * This package contains all classes dealing with authentication and
* authorisation in LibreCCM.
- *
+ *
* Most of this classes are only relevant for the developers of the core part
* of LibreCCM and and core administration UI. For developers of modules the
* primary interface is the Apache Shiro Library. Module developers usually have
- * the use these classes only in the {@code CcmModule#install(InstallEvent)
- * method to create roles and privileges for their module. Therefore most
- * methods of these classes can only be invoked by the System user.
- *
+ * the use these classes only in the CcmModule#install(InstallEvent
+ * method to create roles and privileges for their module. Therefore most
+ * methods of these classes can only be invoked by the System user.
+ *
* The check if the current user is logged in and/or has a certain permission
- * you have to obtain the current {@link Subject} from Shiro. In LibreCCM the
- * subject is provided using CDI. In classes eligible for injection you simply
- * inject the current subject. In other classes you can use the {@link CdiUtil}
- * class.
- *
- * Another option for method of CDI beans is to use the interceptors provided by
+ * you have to obtain the current Subject from Shiro. In
+ * LibreCCM the subject is provided using CDI. In classes eligible for injection
+ * you simply inject the current subject. In other classes you can use the
+ * CdiUtil class.
+ *
+ * Another option for method of CDI beans is to use the interceptors provided by
* this package.
- *
+ *
* @see CcmModule
*/
package org.libreccm.security;
diff --git a/src/site/apt/auth.apt b/src/site/apt/auth.apt
new file mode 100644
index 000000000..96dca9158
--- /dev/null
+++ b/src/site/apt/auth.apt
@@ -0,0 +1,97 @@
+ --------------------------------------------
+ Authentication and Authorisation in LibreCCM
+ --------------------------------------------
+ Jens Pelzetter
+ --------------------------------------------
+ 2015-12-04
+ --------------------------------------------
+
+Authentication and Authorisation in LibreCCM
+
+ LibreCCM uses {{{http://shiro.apache.org}Apache Shiro}} as the foundation
+ for its authentication and authorisation system. All classes related to
+ authentication and authorisation are provided by the
+ {{{ccm-core/apidocs/index.html?org/libreccm/security/package-summary.html}org.libreccm.security}}
+ from the CCM Core module.
+
+* Definitions
+
+ [User] A user is person (or a system) which accesses LibreCCM.
+
+ [Group] A collection of users. A user can be member of multiple groups.
+
+ [Role] A role is basically a collection of permissions and tasks (workflows)
+ which can be assigned a user or group.
+
+ [Permission] Grants a certain privilege to a role. Permissions are either
+ systemwide permissions or permissions granting a certain
+ privilege on a certain CcmObject.
+
+ [Privilege] Describes an action.
+
+* Managing permissions, roles, users and groups.
+
+ Permissions are granted and revoked using the
+ {{{ccm-core/apidocs/index.html?org/libreccm/security/PermissionManager.html}PermissionManager}}
+ class. This class is a CDI bean which can be injected or manually retrieved
+ using the {{{ccm-core/apidocs/index.html?org/libreccm/cdi/utils/CdiUtil.html}CdiUtil}}
+ utility.
+
+ Users, Groups and Roles are managed using the repository and manager classes
+ provided by the {{{ccm-core/apidocs/index.html?org/libreccm/security/package-summary.html}org.libreccm.security}}
+ package. Most module developers will not have to deal with these classes.
+ Only if the module wants to create special roles it might be necessary
+ for a module to use the {{{ccm-core/apidocs/index.html?org/libreccm/security/RoleManager.html}RoleManager}}
+ or the {{{ccm-core/apidocs/index.html?org/libreccm/security/RoleRepository.html}RoleRepository}}.
+
+* Validating permissions
+
+ Validating permissions can be done in various ways. For CDI enabled beans
+ an Interceptor is provided for securing methods. Also a class for checking
+ permissions is provided which can be injected using CDI or retrieved
+ using {{{ccm-core/apidocs/index.html?org/libreccm/cdi/utils/CdiUtil.html}CdiUtil}}
+ utility.
+
+** Using the CDI interceptor
+
+ A method is secured by
+ annotating the method with the <<<@AuthorizationRequired>>> annotation.
+ Additional at least one of the annotations <<<@RequiresRole>>> and
+ <<<@RequiresPrivilege>>> must be used.
+
+ The <<<@{{{ccm-core/apidocs/index.html?org/libreccm/security/RequiresRole.html}RequiresRole}}>>>
+ annotation can only be used on method. If the current subject is not
+ authenticated (not logged in) or does not have the role given by the
+ annotation the interceptor will throw an <<>>.
+
+ The <<<@{{{ccm-core/apidocs/index.html?org/libreccm/security/RequiresPrivilege.html}RequiresPrivilege}}>>>
+ annotation can be used at the method or on a parameter of the type
+ <<>> of the secured method. If the annotation is put on a method
+ the current subject must be authenticated and have a permission granting
+ the user the given privilege.
+
+ If the annotation is used on a method parameter the current user must be
+ authenticated and have a permission which grants the user the given
+ privilege on the object with which the method is called.
+
+ If the current subject does have appropriate permissions an
+ <<>> is thrown.
+
+ Under the hood the interceptor uses the <<>> class which
+ is described in the next section.
+
+** Using the <<>>
+
+ An instance of the
+ <<<@{{{ccm-core/apidocs/index.html?org/libreccm/security/PermissionChecker.html}PermisssionChecker}}>>>
+ can be obtained by injection or by manual lookup. The <<>>
+ provides various methods for checking permissions. Details can be found in
+ the JavaDoc.
+
+** Secured collections
+
+ Methods in the repository classes often return collections. To make securing
+ these collections easy the {{{ccm-core/apidocs/index.html?org/libreccm/security/package-summary.html}org.libreccm.security}}
+ package provides decorators for the collections from the Java Standard API
+ which check if the current subject is permitted to access an object before
+ returning it. Details can be found in the JavaDoc of the decorators.
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index c6afd255b..f0833a747 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -24,6 +24,8 @@
href="module-system/anatomy.html"/>
+