org.opencms.security.CmsRole Maven / Gradle / Ivy
Show all versions of opencms-test Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* 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.
*
* For further information about Alkacon Software GmbH & Co. KG, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.security;
import org.opencms.file.CmsGroup;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsResource;
import org.opencms.main.CmsException;
import org.opencms.main.OpenCms;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Sets;
/**
* A role is used in the OpenCms security system to check if a user has access to a certain system function.
*
* Roles are used to ensure access permissions to system function that are not file based.
* For example, roles are used to check permissions to functions like "the user can schedule a
* job in the {@link org.opencms.scheduler.CmsScheduleManager}
" or "the user can export (or import)
* the OpenCms database".
*
* All roles are based on {@link org.opencms.file.CmsGroup}
. This means to have access to a role,
* the user has to be a member in a certain predefined system group. Each role has exactly one group that
* contains all "direct" members of this role.
*
* All roles have (optional) parent roles. If a user not a member of the role group of a role, but he is
* a member of at last one of the parent role groups, he/she also has full access to this role. This is called
* "indirect" membership to the role.
*
* Please note that "indirect" membership does grant the user the same full access to a role that "direct"
* membership does. For example, the {@link #ROOT_ADMIN}
role is a parent group of all other roles.
* So all users that are members of {@link #ROOT_ADMIN}
have access to the functions of all other roles.
*
* Please do not perform automated sorting of members on this compilation unit. That leads
* to NPE's
*
* @since 6.0.0
*/
public final class CmsRole {
/** The "ACCOUNT_MANAGER" role. */
public static final CmsRole ACCOUNT_MANAGER;
/** The "ADMINISTRATOR" role, which is a parent to all organizational unit roles. */
public static final CmsRole ADMINISTRATOR;
/** The "CATEGORY_EDITOR" role. */
public static final CmsRole CATEGORY_EDITOR;
/** Prefix for individual user confirmation runtime property. */
public static final String CONFIRM_ROLE_PREFIX = "confirm.role.";
/** The "EXPORT_DATABASE" role. */
public static final CmsRole DATABASE_MANAGER;
/** The "DEVELOPER" role. */
public static final CmsRole DEVELOPER;
/** The "EDITOR" role. */
public static final CmsRole EDITOR;
/** The "ELEMENT_AUTHOR" role. */
public static final CmsRole ELEMENT_AUTHOR;
/** The "GALLERY_EDITOR" role. */
public static final CmsRole GALLERY_EDITOR;
/** Identifier for role principals. */
public static final String PRINCIPAL_ROLE = "ROLE";
/** The "PROJECT_MANAGER" role. */
public static final CmsRole PROJECT_MANAGER;
/** The "ROOT_ADMIN" role, which is a parent to all other roles. */
public static final CmsRole ROOT_ADMIN;
/** The "VFS_MANAGER" role. */
public static final CmsRole VFS_MANAGER;
/** The "WORKPLACE_MANAGER" role. */
public static final CmsRole WORKPLACE_MANAGER;
/** The "WORKPLACE_USER" role. */
public static final CmsRole WORKPLACE_USER;
/** The list of system roles. */
private static final List SYSTEM_ROLES;
/** The child roles of this role. */
private final List m_children = new ArrayList();
/** The distinct group names of this role. */
private List m_distictGroupNames = new ArrayList();
/** The name of the group this role is mapped to in the OpenCms database.*/
private final String m_groupName;
/** The id of the role, does not differentiate for organizational units. */
private final CmsUUID m_id;
/** Indicates if this role is organizational unit dependent. */
private boolean m_ouDependent;
/** The organizational unit this role applies to. */
private String m_ouFqn;
/** The parent role of this role. */
private final CmsRole m_parentRole;
/** The name of this role. */
private final String m_roleName;
/** Indicates if this role is a system role or a user defined role. */
private boolean m_systemRole;
/**
* Creates a user defined role.
*
* @param roleName the name of this role
* @param groupName the name of the group the members of this role are stored in
* @param parentRole the parent role of this role
* @param ouDependent if the role is organizational unit dependent
*/
public CmsRole(String roleName, CmsRole parentRole, String groupName, boolean ouDependent) {
this(roleName, parentRole, groupName);
m_ouDependent = ouDependent;
m_systemRole = false;
initialize();
}
/**
* Copy constructor.
*
* @param role the role to copy
*/
private CmsRole(CmsRole role) {
m_roleName = role.m_roleName;
m_id = role.m_id;
m_groupName = role.m_groupName;
m_parentRole = role.m_parentRole;
m_systemRole = role.m_systemRole;
m_ouDependent = role.m_ouDependent;
m_children.addAll(role.m_children);
m_distictGroupNames.addAll(Collections.unmodifiableList(role.m_distictGroupNames));
}
/**
* Creates a system role.
*
* @param roleName the name of this role
* @param parentRole the parent role of this role
* @param groupName the related group name
*/
private CmsRole(String roleName, CmsRole parentRole, String groupName) {
m_roleName = roleName;
m_id = CmsUUID.getConstantUUID(m_roleName);
m_ouDependent = !groupName.startsWith(CmsOrganizationalUnit.SEPARATOR);
m_parentRole = parentRole;
m_systemRole = true;
if (!m_ouDependent) {
m_groupName = groupName.substring(1);
} else {
m_groupName = groupName;
}
if (parentRole != null) {
parentRole.m_children.add(this);
}
}
/**
* Initializes the system roles with the configured OpenCms system group names.
*/
static {
ROOT_ADMIN = new CmsRole("ROOT_ADMIN", null, "/RoleRootAdmins");
WORKPLACE_MANAGER = new CmsRole("WORKPLACE_MANAGER", CmsRole.ROOT_ADMIN, "/RoleWorkplaceManager");
DATABASE_MANAGER = new CmsRole("DATABASE_MANAGER", CmsRole.ROOT_ADMIN, "/RoleDatabaseManager");
ADMINISTRATOR = new CmsRole("ADMINISTRATOR", CmsRole.ROOT_ADMIN, "RoleAdministrators");
PROJECT_MANAGER = new CmsRole("PROJECT_MANAGER", CmsRole.ADMINISTRATOR, "RoleProjectmanagers");
ACCOUNT_MANAGER = new CmsRole("ACCOUNT_MANAGER", CmsRole.ADMINISTRATOR, "RoleAccountManagers");
VFS_MANAGER = new CmsRole("VFS_MANAGER", CmsRole.ADMINISTRATOR, "RoleVfsManagers");
DEVELOPER = new CmsRole("DEVELOPER", CmsRole.VFS_MANAGER, "RoleDevelopers");
WORKPLACE_USER = new CmsRole("WORKPLACE_USER", CmsRole.DEVELOPER, "RoleWorkplaceUsers");
// the following roles all include the workplace user role
PROJECT_MANAGER.m_children.add(WORKPLACE_USER);
ACCOUNT_MANAGER.m_children.add(WORKPLACE_USER);
GALLERY_EDITOR = new CmsRole("GALLERY_EDITOR", CmsRole.WORKPLACE_USER, "RoleGalleryEditor");
CATEGORY_EDITOR = new CmsRole("CATEGORY_EDITOR", CmsRole.WORKPLACE_USER, "RoleCategoryEditor");
EDITOR = new CmsRole("EDITOR", CmsRole.GALLERY_EDITOR, "RoleEditor");
// the category editor role also includes the editor role
CATEGORY_EDITOR.m_children.add(EDITOR);
ELEMENT_AUTHOR = new CmsRole("ELEMENT_AUTHOR", CmsRole.EDITOR, "RoleElementAuthor");
// create a lookup list for the system roles
SYSTEM_ROLES = Collections.unmodifiableList(
Arrays.asList(
new CmsRole[] {
ROOT_ADMIN,
WORKPLACE_MANAGER,
DATABASE_MANAGER,
ADMINISTRATOR,
PROJECT_MANAGER,
ACCOUNT_MANAGER,
VFS_MANAGER,
DEVELOPER,
WORKPLACE_USER,
GALLERY_EDITOR,
CATEGORY_EDITOR,
EDITOR,
ELEMENT_AUTHOR}));
// now initialize all system roles
for (int i = 0; i < SYSTEM_ROLES.size(); i++) {
(SYSTEM_ROLES.get(i)).initialize();
}
}
/**
* Applies the system role order to a list of roles.
*
* @param roles the roles
*/
public static void applySystemRoleOrder(List roles) {
Map ouRoles = new HashMap();
for (CmsRole role : roles) {
ouRoles.put(role.getRoleName(), role);
}
roles.clear();
for (CmsRole sysRole : CmsRole.getSystemRoles()) {
if (ouRoles.containsKey(sysRole.getRoleName())) {
roles.add(ouRoles.get(sysRole.getRoleName()));
}
}
}
/**
* Returns the list of system defined roles (instances of {@link CmsRole}
).
*
* @return the list of system defined roles
*/
public static List getSystemRoles() {
return SYSTEM_ROLES;
}
/**
* Checks if the given String starts with {@link #PRINCIPAL_ROLE} followed by a dot.
*
*
* - Works if the given String is
null
.
* - Removes white spaces around the String before the check.
*
- Also works with prefixes not being in upper case.
*
- Does not check if the role after the prefix actually exists.
*
*
* @param principalName the potential role name to check
*
* @return true
in case the String starts with {@link #PRINCIPAL_ROLE}
*/
public static boolean hasPrefix(String principalName) {
return CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)
&& (principalName.trim().toUpperCase().startsWith(PRINCIPAL_ROLE + "."));
}
/**
* Removes the prefix if the given String starts with {@link #PRINCIPAL_ROLE} followed by a dot.
*
*
* - Works if the given String is
null
.
* - If the given String does not start with {@link #PRINCIPAL_ROLE} followed by a dot it is returned unchanged.
*
- Removes white spaces around the role name.
*
- Also works with prefixes not being in upper case.
*
- Does not check if the role after the prefix actually exists.
*
*
* @param principalName the role name to remove the prefix from
*
* @return the given String with the prefix {@link #PRINCIPAL_ROLE} and the following dot removed
*/
public static String removePrefix(String principalName) {
String result = principalName;
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)) {
if (hasPrefix(principalName)) {
result = principalName.trim().substring(PRINCIPAL_ROLE.length() + 1);
}
}
return result;
}
/**
* Returns the role for the given group.
*
* @param group a group to check for role representation
*
* @return the role for the given group
*/
public static CmsRole valueOf(CmsGroup group) {
// check groups for internal representing the roles
if (group.isRole()) {
CmsRole role = valueOfGroupName(group.getName());
if (role != null) {
return role;
}
}
// check virtual groups mapping a role
if (group.isVirtual()) {
int index = (group.getFlags() & (I_CmsPrincipal.FLAG_CORE_LIMIT - 1));
index = index / (I_CmsPrincipal.FLAG_GROUP_VIRTUAL * 2);
CmsRole role = getSystemRoles().get(index);
return role.forOrgUnit(group.getOuFqn());
}
return null;
}
/**
* Returns the role for the given group name.
*
* @param groupName a group name to check for role representation
*
* @return the role for the given group name
*/
public static CmsRole valueOfGroupName(String groupName) {
String groupOu = CmsOrganizationalUnit.getParentFqn(groupName);
Iterator it = SYSTEM_ROLES.iterator();
while (it.hasNext()) {
CmsRole role = it.next();
// direct check
if (groupName.equals(role.getGroupName())) {
return role.forOrgUnit(groupOu);
}
if (!role.isOrganizationalUnitIndependent()) {
// the role group name does not start with "/", but the given group fqn does
if (groupName.endsWith(CmsOrganizationalUnit.SEPARATOR + role.getGroupName())) {
return role.forOrgUnit(groupOu);
}
}
}
return null;
}
/**
* Returns the role for the given id.
*
* @param roleId the id to check for role representation
*
* @return the role for the given role id
*/
public static CmsRole valueOfId(CmsUUID roleId) {
Iterator it = SYSTEM_ROLES.iterator();
while (it.hasNext()) {
CmsRole role = it.next();
if (roleId.equals(role.getId())) {
return role;
}
}
return null;
}
/**
* Returns the role for the given role name.
*
* @param roleName a role name to check for role representation
*
* @return the role for the given role name
*/
public static CmsRole valueOfRoleName(String roleName) {
String roleOu = CmsOrganizationalUnit.getParentFqn(roleName);
Iterator it = SYSTEM_ROLES.iterator();
while (it.hasNext()) {
CmsRole role = it.next();
// direct check
if (roleName.equals(role.getRoleName())) {
return role.forOrgUnit(roleOu);
}
if (!role.isOrganizationalUnitIndependent()) {
// the role name does not start with "/", but the given role fqn does
if (roleName.endsWith(CmsOrganizationalUnit.SEPARATOR + role.getRoleName())) {
return role.forOrgUnit(roleOu);
}
}
}
return null;
}
/**
* Returns a role violation exception configured with a localized, role specific message
* for this role.
*
* @param requestContext the current users OpenCms request context
*
* @return a role violation exception configured with a localized, role specific message
* for this role
*/
public CmsRoleViolationException createRoleViolationException(CmsRequestContext requestContext) {
return new CmsRoleViolationException(
Messages.get().container(
Messages.ERR_USER_NOT_IN_ROLE_2,
requestContext.getCurrentUser().getName(),
getName(requestContext.getLocale())));
}
/**
* Returns a role violation exception configured with a localized, role specific message
* for this role.
*
* @param requestContext the current users OpenCms request context
* @param orgUnitFqn the organizational unit used for the role check, it may be null
*
* @return a role violation exception configured with a localized, role specific message
* for this role
*/
public CmsRoleViolationException createRoleViolationExceptionForOrgUnit(
CmsRequestContext requestContext,
String orgUnitFqn) {
return new CmsRoleViolationException(
Messages.get().container(
Messages.ERR_USER_NOT_IN_ROLE_FOR_ORGUNIT_3,
requestContext.getCurrentUser().getName(),
getName(requestContext.getLocale()),
orgUnitFqn));
}
/**
* Returns a role violation exception configured with a localized, role specific message
* for this role.
*
* @param requestContext the current users OpenCms request context
* @param resource the resource used for the role check, it may be null
*
* @return a role violation exception configured with a localized, role specific message
* for this role
*/
public CmsRoleViolationException createRoleViolationExceptionForResource(
CmsRequestContext requestContext,
CmsResource resource) {
return new CmsRoleViolationException(
Messages.get().container(
Messages.ERR_USER_NOT_IN_ROLE_FOR_RESOURCE_3,
requestContext.getCurrentUser().getName(),
getName(requestContext.getLocale()),
requestContext.removeSiteRoot(resource.getRootPath())));
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof CmsRole) {
CmsRole that = (CmsRole)obj;
// first check name
if (m_roleName.equals(that.m_roleName)) {
if (isOrganizationalUnitIndependent()) {
// if ou independent ignore ou info
return true;
}
// then check the org unit
if (m_ouFqn == null) {
// if org unit not set
return (that.m_ouFqn == null);
} else {
// if org unit set
return (m_ouFqn.equals(that.m_ouFqn));
}
}
}
return false;
}
/**
* Creates a new role based on this one for the given organizational unit.
*
* @param ouFqn fully qualified name of the organizational unit
*
* @return a new role based on this one for the given organizational unit
*/
public CmsRole forOrgUnit(String ouFqn) {
CmsRole newRole = new CmsRole(this);
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(ouFqn)) {
if (!ouFqn.endsWith(CmsOrganizationalUnit.SEPARATOR)) {
ouFqn += CmsOrganizationalUnit.SEPARATOR;
}
}
newRole.m_ouFqn = ouFqn;
return newRole;
}
/**
* Returns a list of all sub roles.
*
* @param recursive if not set just direct children are returned
*
* @return all sub roles as a list of {@link CmsRole} objects
*/
public List getChildren(boolean recursive) {
List children = new ArrayList();
Iterator itChildren = m_children.iterator();
while (itChildren.hasNext()) {
CmsRole child = itChildren.next();
if (child.isOrganizationalUnitIndependent()) {
child = child.forOrgUnit(null);
} else {
child = child.forOrgUnit(m_ouFqn);
}
children.add(child);
if (recursive) {
for (CmsRole grandChild : child.getChildren(true)) {
if (!children.contains(grandChild)) {
children.add(grandChild);
}
}
}
}
return children;
}
/**
* Returns a localized role description.
*
* @param locale the locale
*
* @return the localized role description
*/
public String getDescription(Locale locale) {
if (m_systemRole) {
// localize role names for system roles
return Messages.get().getBundle(locale).key("GUI_ROLE_DESCRIPTION_" + m_roleName + "_0");
} else {
return getName(locale);
}
}
/**
* Returns the display name of this role including the organizational unit.
*
* @param cms the cms context
* @param locale the locale
*
* @return the display name of this role including the organizational unit
*
* @throws CmsException if the organizational unit could not be read
*/
public String getDisplayName(CmsObject cms, Locale locale) throws CmsException {
return Messages.get().getBundle(locale).key(
Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
getName(locale),
OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
}
/**
* Returns the distinct group names of this role.
*
* This group names are not fully qualified (organizational unit dependent).
*
* @return the distinct group names of this role
*/
public List getDistinctGroupNames() {
return m_distictGroupNames;
}
/**
* Returns the fully qualified name of this role.
*
* @return the fqn of this role
*/
public String getFqn() {
if (getOuFqn() == null) {
return getRoleName();
}
return getOuFqn() + getRoleName();
}
/**
* Returns the name of the group this role is mapped to in the OpenCms database.
*
* Here the fully qualified group name is returned.
*
* @return the name of the group this role is mapped to in the OpenCms database
*/
public String getGroupName() {
if ((m_ouFqn == null) || isOrganizationalUnitIndependent()) {
return m_groupName;
}
return m_ouFqn + m_groupName;
}
/**
* Returns the id of this role.
*
* Does not differentiate for organizational units.
*
* @return the id of this role
*/
public CmsUUID getId() {
return m_id;
}
/**
* Returns a localized role name.
*
* @param locale the locale
*
* @return the localized role name
*/
public String getName(Locale locale) {
if (m_systemRole) {
// localize role names for system roles
return Messages.get().getBundle(locale).key("GUI_ROLENAME_" + m_roleName + "_0");
} else {
return getRoleName();
}
}
/**
* Returns the fully qualified name of the organizational unit.
*
* @return the fully qualified name of the organizational unit
*/
public String getOuFqn() {
return CmsOrganizationalUnit.removeLeadingSeparator(m_ouFqn);
}
/**
* Returns the parent role of this role.
*
* @return the parent role of this role
*/
public CmsRole getParentRole() {
if (m_parentRole == null) {
return null;
}
return m_parentRole.forOrgUnit(m_ouFqn);
}
/**
* Returns the name of the role.
*
* @return the name of the role
*/
public String getRoleName() {
return m_roleName;
}
/**
* Returns the flags needed for a group to emulate this role.
*
* @return the flags needed for a group to emulate this role
*/
public int getVirtualGroupFlags() {
int flags = I_CmsPrincipal.FLAG_GROUP_VIRTUAL;
flags += I_CmsPrincipal.FLAG_GROUP_VIRTUAL * 2 * getSystemRoles().indexOf(forOrgUnit(null));
return flags;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return m_roleName.hashCode()
+ (((m_ouFqn == null) || isOrganizationalUnitIndependent()) ? 13 : m_ouFqn.hashCode());
}
/**
* Checks if this role is organizational unit independent.
*
* @return true
if this role is organizational unit independent
*/
public boolean isOrganizationalUnitIndependent() {
return !m_ouDependent;
}
/**
* Check if this role is a system role.
*
* @return true
if this role is a system role
*/
public boolean isSystemRole() {
return m_systemRole;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("[");
result.append(this.getClass().getName());
result.append(", role: ");
result.append(getRoleName());
result.append(", org unit: ");
result.append(getOuFqn());
result.append(", group: ");
result.append(getGroupName());
result.append("]");
return result.toString();
}
/**
* Returns a set of all roles group names.
*
* @return a set of all roles group names
*/
private Set getAllGroupNames() {
// First get the topmost role (should be root admin)
CmsRole root = this;
while (root.getParentRole() != null) {
root = root.getParentRole();
}
List allRoles = root.getChildren(true);
allRoles.add(root);
// now build multimap from children to parent roles for all roles reachable from root
HashMultimap mapOfParents = HashMultimap.create();
for (CmsRole parent : allRoles) {
for (CmsRole child : parent.getChildren(false)) {
mapOfParents.put(child, parent);
}
}
// now traverse the parent map, starting from this role, to find all parent roles
// (we can't just use the getParentRole() method instead of the parent map here,
// since roles may have multiple logical parents (roles which have them as a child).
Set visited = Sets.newHashSet();
Set workingSet = Sets.newHashSet();
Set result = Sets.newHashSet();
workingSet.add(this);
while (!workingSet.isEmpty()) {
CmsRole current = workingSet.iterator().next();
result.add(current.getGroupName());
workingSet.remove(current);
for (CmsRole parent : mapOfParents.get(current)) {
if (!visited.contains(parent)) {
workingSet.add(parent);
}
}
visited.add(current);
}
return result;
}
/**
* Initializes this role, creating an optimized data structure for
* the lookup of the role group names.
*/
private void initialize() {
// calculate the distinct groups of this role
Set distinctGroups = new HashSet(getAllGroupNames());
// by using a set first we eliminate duplicate names
m_distictGroupNames = Collections.unmodifiableList(new ArrayList(distinctGroups));
}
}