001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.security; 029 030import org.opencms.file.CmsGroup; 031import org.opencms.file.CmsObject; 032import org.opencms.file.CmsRequestContext; 033import org.opencms.file.CmsResource; 034import org.opencms.main.CmsException; 035import org.opencms.main.OpenCms; 036import org.opencms.util.CmsStringUtil; 037import org.opencms.util.CmsUUID; 038 039import java.util.ArrayList; 040import java.util.Arrays; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.HashSet; 044import java.util.Iterator; 045import java.util.List; 046import java.util.Locale; 047import java.util.Map; 048import java.util.Set; 049 050import com.google.common.collect.HashMultimap; 051import com.google.common.collect.Sets; 052 053/** 054 * A role is used in the OpenCms security system to check if a user has access to a certain system function.<p> 055 * 056 * Roles are used to ensure access permissions to system function that are not file based. 057 * For example, roles are used to check permissions to functions like "the user can schedule a 058 * job in the <code>{@link org.opencms.scheduler.CmsScheduleManager}</code>" or "the user can export (or import) 059 * the OpenCms database".<p> 060 * 061 * All roles are based on <code>{@link org.opencms.file.CmsGroup}</code>. This means to have access to a role, 062 * the user has to be a member in a certain predefined system group. Each role has exactly one group that 063 * contains all "direct" members of this role.<p> 064 * 065 * All roles have (optional) parent roles. If a user not a member of the role group of a role, but he is 066 * a member of at last one of the parent role groups, he/she also has full access to this role. This is called 067 * "indirect" membership to the role.<p> 068 * 069 * Please note that "indirect" membership does grant the user the same full access to a role that "direct" 070 * membership does. For example, the <code>{@link #ROOT_ADMIN}</code> role is a parent group of all other roles. 071 * So all users that are members of <code>{@link #ROOT_ADMIN}</code> have access to the functions of all other roles.<p> 072 * 073 * Please do not perform automated sorting of members on this compilation unit. That leads 074 * to NPE's<p> 075 * 076 * @since 6.0.0 077 */ 078public final class CmsRole { 079 080 /** The "ACCOUNT_MANAGER" role. */ 081 public static final CmsRole ACCOUNT_MANAGER; 082 083 /** The "ADMINISTRATOR" role, which is a parent to all organizational unit roles. */ 084 public static final CmsRole ADMINISTRATOR; 085 086 /** The "CATEGORY_EDITOR" role. */ 087 public static final CmsRole CATEGORY_EDITOR; 088 089 /** Prefix for individual user confirmation runtime property. */ 090 public static final String CONFIRM_ROLE_PREFIX = "confirm.role."; 091 092 /** The "EXPORT_DATABASE" role. */ 093 public static final CmsRole DATABASE_MANAGER; 094 095 /** The "DEVELOPER" role. */ 096 public static final CmsRole DEVELOPER; 097 098 /** The "EDITOR" role. */ 099 public static final CmsRole EDITOR; 100 101 /** The "ELEMENT_AUTHOR" role. */ 102 public static final CmsRole ELEMENT_AUTHOR; 103 104 /** The "GALLERY_EDITOR" role. */ 105 public static final CmsRole GALLERY_EDITOR; 106 107 /** The "LIST_EDITOR" role. */ 108 public static final CmsRole LIST_EDITOR; 109 110 /** Identifier for role principals. */ 111 public static final String PRINCIPAL_ROLE = "ROLE"; 112 113 /** The "PROJECT_MANAGER" role. */ 114 public static final CmsRole PROJECT_MANAGER; 115 116 /** The "ROOT_ADMIN" role, which is a parent to all other roles. */ 117 public static final CmsRole ROOT_ADMIN; 118 119 /** The "VFS_MANAGER" role. */ 120 public static final CmsRole VFS_MANAGER; 121 122 /** The "WORKPLACE_MANAGER" role. */ 123 public static final CmsRole WORKPLACE_MANAGER; 124 125 /** The "WORKPLACE_USER" role. */ 126 public static final CmsRole WORKPLACE_USER; 127 128 /** The list of system roles. */ 129 private static final List<CmsRole> SYSTEM_ROLES; 130 131 /** 132 * Initializes the system roles with the configured OpenCms system group names.<p> 133 */ 134 static { 135 136 ROOT_ADMIN = new CmsRole("ROOT_ADMIN", null, "/RoleRootAdmins"); 137 WORKPLACE_MANAGER = new CmsRole("WORKPLACE_MANAGER", CmsRole.ROOT_ADMIN, "/RoleWorkplaceManager"); 138 DATABASE_MANAGER = new CmsRole("DATABASE_MANAGER", CmsRole.ROOT_ADMIN, "/RoleDatabaseManager"); 139 140 ADMINISTRATOR = new CmsRole("ADMINISTRATOR", CmsRole.ROOT_ADMIN, "RoleAdministrators"); 141 PROJECT_MANAGER = new CmsRole("PROJECT_MANAGER", CmsRole.ADMINISTRATOR, "RoleProjectmanagers"); 142 ACCOUNT_MANAGER = new CmsRole("ACCOUNT_MANAGER", CmsRole.ADMINISTRATOR, "RoleAccountManagers"); 143 VFS_MANAGER = new CmsRole("VFS_MANAGER", CmsRole.ADMINISTRATOR, "RoleVfsManagers"); 144 DEVELOPER = new CmsRole("DEVELOPER", CmsRole.VFS_MANAGER, "RoleDevelopers"); 145 WORKPLACE_USER = new CmsRole("WORKPLACE_USER", CmsRole.DEVELOPER, "RoleWorkplaceUsers"); 146 147 // the following roles all include the workplace user role 148 PROJECT_MANAGER.m_children.add(WORKPLACE_USER); 149 ACCOUNT_MANAGER.m_children.add(WORKPLACE_USER); 150 151 LIST_EDITOR = new CmsRole("LIST_EDITOR", CmsRole.WORKPLACE_USER, "RoleListEditor"); 152 GALLERY_EDITOR = new CmsRole("GALLERY_EDITOR", CmsRole.WORKPLACE_USER, "RoleGalleryEditor"); 153 CATEGORY_EDITOR = new CmsRole("CATEGORY_EDITOR", CmsRole.WORKPLACE_USER, "RoleCategoryEditor"); 154 EDITOR = new CmsRole("EDITOR", CmsRole.GALLERY_EDITOR, "RoleEditor"); 155 156 // the category editor role also includes the editor role 157 CATEGORY_EDITOR.m_children.add(EDITOR); 158 159 LIST_EDITOR.m_children.add(EDITOR); 160 161 ELEMENT_AUTHOR = new CmsRole("ELEMENT_AUTHOR", CmsRole.EDITOR, "RoleElementAuthor"); 162 163 // create a lookup list for the system roles 164 SYSTEM_ROLES = Collections.unmodifiableList( 165 Arrays.asList( 166 new CmsRole[] { 167 ROOT_ADMIN, 168 WORKPLACE_MANAGER, 169 DATABASE_MANAGER, 170 ADMINISTRATOR, 171 PROJECT_MANAGER, 172 ACCOUNT_MANAGER, 173 VFS_MANAGER, 174 DEVELOPER, 175 WORKPLACE_USER, 176 LIST_EDITOR, 177 GALLERY_EDITOR, 178 CATEGORY_EDITOR, 179 EDITOR, 180 ELEMENT_AUTHOR})); 181 182 // now initialize all system roles 183 for (int i = 0; i < SYSTEM_ROLES.size(); i++) { 184 (SYSTEM_ROLES.get(i)).initialize(); 185 } 186 } 187 188 /** The child roles of this role. */ 189 private final List<CmsRole> m_children = new ArrayList<CmsRole>(); 190 191 /** The distinct group names of this role. */ 192 private List<String> m_distictGroupNames = new ArrayList<String>(); 193 194 /** The name of the group this role is mapped to in the OpenCms database.*/ 195 private final String m_groupName; 196 197 /** The id of the role, does not differentiate for organizational units. */ 198 private final CmsUUID m_id; 199 200 /** Indicates if this role is organizational unit dependent. */ 201 private boolean m_ouDependent; 202 203 /** The organizational unit this role applies to. */ 204 private String m_ouFqn; 205 206 /** The parent role of this role. */ 207 private final CmsRole m_parentRole; 208 209 /** The name of this role. */ 210 private final String m_roleName; 211 212 /** Indicates if this role is a system role or a user defined role. */ 213 private boolean m_systemRole; 214 215 /** 216 * Creates a user defined role.<p> 217 * 218 * @param roleName the name of this role 219 * @param groupName the name of the group the members of this role are stored in 220 * @param parentRole the parent role of this role 221 * @param ouDependent if the role is organizational unit dependent 222 */ 223 public CmsRole(String roleName, CmsRole parentRole, String groupName, boolean ouDependent) { 224 225 this(roleName, parentRole, groupName); 226 m_ouDependent = ouDependent; 227 m_systemRole = false; 228 initialize(); 229 } 230 231 /** 232 * Copy constructor.<p> 233 * 234 * @param role the role to copy 235 */ 236 private CmsRole(CmsRole role) { 237 238 m_roleName = role.m_roleName; 239 m_id = role.m_id; 240 m_groupName = role.m_groupName; 241 m_parentRole = role.m_parentRole; 242 m_systemRole = role.m_systemRole; 243 m_ouDependent = role.m_ouDependent; 244 m_children.addAll(role.m_children); 245 m_distictGroupNames.addAll(Collections.unmodifiableList(role.m_distictGroupNames)); 246 } 247 248 /** 249 * Creates a system role.<p> 250 * 251 * @param roleName the name of this role 252 * @param parentRole the parent role of this role 253 * @param groupName the related group name 254 */ 255 private CmsRole(String roleName, CmsRole parentRole, String groupName) { 256 257 m_roleName = roleName; 258 m_id = CmsUUID.getConstantUUID(m_roleName); 259 m_ouDependent = !groupName.startsWith(CmsOrganizationalUnit.SEPARATOR); 260 m_parentRole = parentRole; 261 m_systemRole = true; 262 if (!m_ouDependent) { 263 m_groupName = groupName.substring(1); 264 } else { 265 m_groupName = groupName; 266 } 267 if (parentRole != null) { 268 parentRole.m_children.add(this); 269 } 270 } 271 272 /** 273 * Applies the system role order to a list of roles.<p> 274 * 275 * @param roles the roles 276 */ 277 public static void applySystemRoleOrder(List<CmsRole> roles) { 278 279 Map<String, CmsRole> ouRoles = new HashMap<String, CmsRole>(); 280 for (CmsRole role : roles) { 281 ouRoles.put(role.getRoleName(), role); 282 } 283 roles.clear(); 284 for (CmsRole sysRole : CmsRole.getSystemRoles()) { 285 if (ouRoles.containsKey(sysRole.getRoleName())) { 286 roles.add(ouRoles.get(sysRole.getRoleName())); 287 } 288 } 289 } 290 291 /** 292 * Returns the list of system defined roles (instances of <code>{@link CmsRole}</code>).<p> 293 * 294 * @return the list of system defined roles 295 */ 296 public static List<CmsRole> getSystemRoles() { 297 298 return SYSTEM_ROLES; 299 } 300 301 /** 302 * Checks if the given String starts with {@link #PRINCIPAL_ROLE} followed by a dot.<p> 303 * 304 * <ul> 305 * <li>Works if the given String is <code>null</code>. 306 * <li>Removes white spaces around the String before the check. 307 * <li>Also works with prefixes not being in upper case. 308 * <li>Does not check if the role after the prefix actually exists. 309 * </ul> 310 * 311 * @param principalName the potential role name to check 312 * 313 * @return <code>true</code> in case the String starts with {@link #PRINCIPAL_ROLE} 314 */ 315 public static boolean hasPrefix(String principalName) { 316 317 return CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName) 318 && (principalName.trim().toUpperCase().startsWith(PRINCIPAL_ROLE + ".")); 319 } 320 321 /** 322 * Removes the prefix if the given String starts with {@link #PRINCIPAL_ROLE} followed by a dot.<p> 323 * 324 * <ul> 325 * <li>Works if the given String is <code>null</code>. 326 * <li>If the given String does not start with {@link #PRINCIPAL_ROLE} followed by a dot it is returned unchanged. 327 * <li>Removes white spaces around the role name. 328 * <li>Also works with prefixes not being in upper case. 329 * <li>Does not check if the role after the prefix actually exists. 330 * </ul> 331 * 332 * @param principalName the role name to remove the prefix from 333 * 334 * @return the given String with the prefix {@link #PRINCIPAL_ROLE} and the following dot removed 335 */ 336 public static String removePrefix(String principalName) { 337 338 String result = principalName; 339 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)) { 340 if (hasPrefix(principalName)) { 341 result = principalName.trim().substring(PRINCIPAL_ROLE.length() + 1); 342 } 343 } 344 return result; 345 } 346 347 /** 348 * Returns the role for the given group.<p> 349 * 350 * @param group a group to check for role representation 351 * 352 * @return the role for the given group 353 */ 354 public static CmsRole valueOf(CmsGroup group) { 355 356 // check groups for internal representing the roles 357 if (group.isRole()) { 358 CmsRole role = valueOfGroupName(group.getName()); 359 if (role != null) { 360 return role; 361 } 362 } 363 // check virtual groups mapping a role 364 if (group.isVirtual()) { 365 int index = (group.getFlags() & (I_CmsPrincipal.FLAG_CORE_LIMIT - 1)); 366 index = index / (I_CmsPrincipal.FLAG_GROUP_VIRTUAL * 2); 367 CmsRole role = getSystemRoles().get(index); 368 return role.forOrgUnit(group.getOuFqn()); 369 } 370 return null; 371 } 372 373 /** 374 * Returns the role for the given group name.<p> 375 * 376 * @param groupName a group name to check for role representation 377 * 378 * @return the role for the given group name 379 */ 380 public static CmsRole valueOfGroupName(String groupName) { 381 382 String groupOu = CmsOrganizationalUnit.getParentFqn(groupName); 383 Iterator<CmsRole> it = SYSTEM_ROLES.iterator(); 384 while (it.hasNext()) { 385 CmsRole role = it.next(); 386 // direct check 387 if (groupName.equals(role.getGroupName())) { 388 return role.forOrgUnit(groupOu); 389 } 390 if (!role.isOrganizationalUnitIndependent()) { 391 // the role group name does not start with "/", but the given group fqn does 392 if (groupName.endsWith(CmsOrganizationalUnit.SEPARATOR + role.getGroupName())) { 393 return role.forOrgUnit(groupOu); 394 } 395 } 396 } 397 return null; 398 } 399 400 /** 401 * Returns the role for the given id.<p> 402 * 403 * @param roleId the id to check for role representation 404 * 405 * @return the role for the given role id 406 */ 407 public static CmsRole valueOfId(CmsUUID roleId) { 408 409 Iterator<CmsRole> it = SYSTEM_ROLES.iterator(); 410 while (it.hasNext()) { 411 CmsRole role = it.next(); 412 if (roleId.equals(role.getId())) { 413 return role; 414 } 415 } 416 return null; 417 } 418 419 /** 420 * Returns the role for the given role name.<p> 421 * 422 * @param roleName a role name to check for role representation 423 * 424 * @return the role for the given role name 425 */ 426 public static CmsRole valueOfRoleName(String roleName) { 427 428 String roleOu = CmsOrganizationalUnit.getParentFqn(roleName); 429 Iterator<CmsRole> it = SYSTEM_ROLES.iterator(); 430 while (it.hasNext()) { 431 CmsRole role = it.next(); 432 // direct check 433 if (roleName.equals(role.getRoleName())) { 434 return role.forOrgUnit(roleOu); 435 } 436 if (!role.isOrganizationalUnitIndependent()) { 437 // the role name does not start with "/", but the given role fqn does 438 if (roleName.endsWith(CmsOrganizationalUnit.SEPARATOR + role.getRoleName())) { 439 return role.forOrgUnit(roleOu); 440 } 441 } 442 } 443 return null; 444 } 445 446 /** 447 * Returns a role violation exception configured with a localized, role specific message 448 * for this role.<p> 449 * 450 * @param requestContext the current users OpenCms request context 451 * 452 * @return a role violation exception configured with a localized, role specific message 453 * for this role 454 */ 455 public CmsRoleViolationException createRoleViolationException(CmsRequestContext requestContext) { 456 457 return new CmsRoleViolationException( 458 Messages.get().container( 459 Messages.ERR_USER_NOT_IN_ROLE_2, 460 requestContext.getCurrentUser().getName(), 461 getName(requestContext.getLocale()))); 462 } 463 464 /** 465 * Returns a role violation exception configured with a localized, role specific message 466 * for this role.<p> 467 * 468 * @param requestContext the current users OpenCms request context 469 * @param orgUnitFqn the organizational unit used for the role check, it may be <code>null</code> 470 * 471 * @return a role violation exception configured with a localized, role specific message 472 * for this role 473 */ 474 public CmsRoleViolationException createRoleViolationExceptionForOrgUnit( 475 CmsRequestContext requestContext, 476 String orgUnitFqn) { 477 478 return new CmsRoleViolationException( 479 Messages.get().container( 480 Messages.ERR_USER_NOT_IN_ROLE_FOR_ORGUNIT_3, 481 requestContext.getCurrentUser().getName(), 482 getName(requestContext.getLocale()), 483 orgUnitFqn)); 484 } 485 486 /** 487 * Returns a role violation exception configured with a localized, role specific message 488 * for this role.<p> 489 * 490 * @param requestContext the current users OpenCms request context 491 * @param resource the resource used for the role check, it may be <code>null</code> 492 * 493 * @return a role violation exception configured with a localized, role specific message 494 * for this role 495 */ 496 public CmsRoleViolationException createRoleViolationExceptionForResource( 497 CmsRequestContext requestContext, 498 CmsResource resource) { 499 500 return new CmsRoleViolationException( 501 Messages.get().container( 502 Messages.ERR_USER_NOT_IN_ROLE_FOR_RESOURCE_3, 503 requestContext.getCurrentUser().getName(), 504 getName(requestContext.getLocale()), 505 requestContext.removeSiteRoot(resource.getRootPath()))); 506 } 507 508 /** 509 * @see java.lang.Object#equals(java.lang.Object) 510 */ 511 @Override 512 public boolean equals(Object obj) { 513 514 if (obj == this) { 515 return true; 516 } 517 if (obj instanceof CmsRole) { 518 CmsRole that = (CmsRole)obj; 519 // first check name 520 if (m_roleName.equals(that.m_roleName)) { 521 if (isOrganizationalUnitIndependent()) { 522 // if ou independent ignore ou info 523 return true; 524 } 525 // then check the org unit 526 if (m_ouFqn == null) { 527 // if org unit not set 528 return (that.m_ouFqn == null); 529 } else { 530 // if org unit set 531 return (m_ouFqn.equals(that.m_ouFqn)); 532 } 533 } 534 } 535 return false; 536 } 537 538 /** 539 * Creates a new role based on this one for the given organizational unit.<p> 540 * 541 * @param ouFqn fully qualified name of the organizational unit 542 * 543 * @return a new role based on this one for the given organizational unit 544 */ 545 public CmsRole forOrgUnit(String ouFqn) { 546 547 CmsRole newRole = new CmsRole(this); 548 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(ouFqn)) { 549 if (!ouFqn.endsWith(CmsOrganizationalUnit.SEPARATOR)) { 550 ouFqn += CmsOrganizationalUnit.SEPARATOR; 551 } 552 } 553 newRole.m_ouFqn = ouFqn; 554 return newRole; 555 } 556 557 /** 558 * Returns a list of all sub roles.<p> 559 * 560 * @param recursive if not set just direct children are returned 561 * 562 * @return all sub roles as a list of {@link CmsRole} objects 563 */ 564 public List<CmsRole> getChildren(boolean recursive) { 565 566 List<CmsRole> children = new ArrayList<CmsRole>(); 567 Iterator<CmsRole> itChildren = m_children.iterator(); 568 while (itChildren.hasNext()) { 569 CmsRole child = itChildren.next(); 570 if (child.isOrganizationalUnitIndependent()) { 571 child = child.forOrgUnit(null); 572 } else { 573 child = child.forOrgUnit(m_ouFqn); 574 } 575 children.add(child); 576 if (recursive) { 577 for (CmsRole grandChild : child.getChildren(true)) { 578 if (!children.contains(grandChild)) { 579 children.add(grandChild); 580 } 581 } 582 } 583 } 584 return children; 585 } 586 587 /** 588 * Returns a localized role description.<p> 589 * 590 * @param locale the locale 591 * 592 * @return the localized role description 593 */ 594 public String getDescription(Locale locale) { 595 596 if (m_systemRole) { 597 // localize role names for system roles 598 return Messages.get().getBundle(locale).key("GUI_ROLE_DESCRIPTION_" + m_roleName + "_0"); 599 } else { 600 return getName(locale); 601 } 602 } 603 604 /** 605 * Returns the display name of this role including the organizational unit.<p> 606 * 607 * @param cms the cms context 608 * @param locale the locale 609 * 610 * @return the display name of this role including the organizational unit 611 * 612 * @throws CmsException if the organizational unit could not be read 613 */ 614 public String getDisplayName(CmsObject cms, Locale locale) throws CmsException { 615 616 return Messages.get().getBundle(locale).key( 617 Messages.GUI_PRINCIPAL_DISPLAY_NAME_2, 618 getName(locale), 619 OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale)); 620 } 621 622 /** 623 * Returns the distinct group names of this role.<p> 624 * 625 * This group names are not fully qualified (organizational unit dependent).<p> 626 * 627 * @return the distinct group names of this role 628 */ 629 public List<String> getDistinctGroupNames() { 630 631 return m_distictGroupNames; 632 } 633 634 /** 635 * Returns the fully qualified name of this role.<p> 636 * 637 * @return the fqn of this role 638 */ 639 public String getFqn() { 640 641 if (getOuFqn() == null) { 642 return getRoleName(); 643 } 644 return getOuFqn() + getRoleName(); 645 } 646 647 /** 648 * Returns the name of the group this role is mapped to in the OpenCms database.<p> 649 * 650 * Here the fully qualified group name is returned.<p> 651 * 652 * @return the name of the group this role is mapped to in the OpenCms database 653 */ 654 public String getGroupName() { 655 656 if ((m_ouFqn == null) || isOrganizationalUnitIndependent()) { 657 return m_groupName; 658 } 659 return m_ouFqn + m_groupName; 660 } 661 662 /** 663 * Returns the id of this role.<p> 664 * 665 * Does not differentiate for organizational units.<p> 666 * 667 * @return the id of this role 668 */ 669 public CmsUUID getId() { 670 671 return m_id; 672 } 673 674 /** 675 * Returns a localized role name.<p> 676 * 677 * @param locale the locale 678 * 679 * @return the localized role name 680 */ 681 public String getName(Locale locale) { 682 683 if (m_systemRole) { 684 // localize role names for system roles 685 return Messages.get().getBundle(locale).key("GUI_ROLENAME_" + m_roleName + "_0"); 686 } else { 687 return getRoleName(); 688 } 689 } 690 691 /** 692 * Returns the fully qualified name of the organizational unit.<p> 693 * 694 * @return the fully qualified name of the organizational unit 695 */ 696 public String getOuFqn() { 697 698 return CmsOrganizationalUnit.removeLeadingSeparator(m_ouFqn); 699 } 700 701 /** 702 * Returns the parent role of this role.<p> 703 * 704 * @return the parent role of this role 705 */ 706 public CmsRole getParentRole() { 707 708 if (m_parentRole == null) { 709 return null; 710 } 711 return m_parentRole.forOrgUnit(m_ouFqn); 712 } 713 714 /** 715 * Returns the name of the role.<p> 716 * 717 * @return the name of the role 718 */ 719 public String getRoleName() { 720 721 return m_roleName; 722 } 723 724 /** 725 * Returns the flags needed for a group to emulate this role.<p> 726 * 727 * @return the flags needed for a group to emulate this role 728 */ 729 public int getVirtualGroupFlags() { 730 731 int flags = I_CmsPrincipal.FLAG_GROUP_VIRTUAL; 732 flags += I_CmsPrincipal.FLAG_GROUP_VIRTUAL * 2 * getSystemRoles().indexOf(forOrgUnit(null)); 733 return flags; 734 } 735 736 /** 737 * @see java.lang.Object#hashCode() 738 */ 739 @Override 740 public int hashCode() { 741 742 return m_roleName.hashCode() 743 + (((m_ouFqn == null) || isOrganizationalUnitIndependent()) ? 13 : m_ouFqn.hashCode()); 744 } 745 746 /** 747 * Checks if this role is organizational unit independent.<p> 748 * 749 * @return <code>true</code> if this role is organizational unit independent 750 */ 751 public boolean isOrganizationalUnitIndependent() { 752 753 return !m_ouDependent; 754 } 755 756 /** 757 * Check if this role is a system role.<p> 758 * 759 * @return <code>true</code> if this role is a system role 760 */ 761 public boolean isSystemRole() { 762 763 return m_systemRole; 764 } 765 766 /** 767 * @see java.lang.Object#toString() 768 */ 769 @Override 770 public String toString() { 771 772 StringBuffer result = new StringBuffer(); 773 774 result.append("["); 775 result.append(this.getClass().getName()); 776 result.append(", role: "); 777 result.append(getRoleName()); 778 result.append(", org unit: "); 779 result.append(getOuFqn()); 780 result.append(", group: "); 781 result.append(getGroupName()); 782 result.append("]"); 783 784 return result.toString(); 785 } 786 787 /** 788 * Returns a set of all roles group names.<p> 789 * 790 * @return a set of all roles group names 791 */ 792 private Set<String> getAllGroupNames() { 793 794 // First get the topmost role (should be root admin) 795 796 CmsRole root = this; 797 while (root.getParentRole() != null) { 798 root = root.getParentRole(); 799 } 800 List<CmsRole> allRoles = root.getChildren(true); 801 allRoles.add(root); 802 803 // now build multimap from children to parent roles for all roles reachable from root 804 805 HashMultimap<CmsRole, CmsRole> mapOfParents = HashMultimap.create(); 806 for (CmsRole parent : allRoles) { 807 for (CmsRole child : parent.getChildren(false)) { 808 mapOfParents.put(child, parent); 809 } 810 } 811 812 // now traverse the parent map, starting from this role, to find all parent roles 813 // (we can't just use the getParentRole() method instead of the parent map here, 814 // since roles may have multiple logical parents (roles which have them as a child). 815 816 Set<CmsRole> visited = Sets.newHashSet(); 817 Set<CmsRole> workingSet = Sets.newHashSet(); 818 Set<String> result = Sets.newHashSet(); 819 workingSet.add(this); 820 while (!workingSet.isEmpty()) { 821 CmsRole current = workingSet.iterator().next(); 822 result.add(current.getGroupName()); 823 workingSet.remove(current); 824 for (CmsRole parent : mapOfParents.get(current)) { 825 if (!visited.contains(parent)) { 826 workingSet.add(parent); 827 } 828 } 829 visited.add(current); 830 } 831 return result; 832 } 833 834 /** 835 * Initializes this role, creating an optimized data structure for 836 * the lookup of the role group names.<p> 837 */ 838 private void initialize() { 839 840 // calculate the distinct groups of this role 841 Set<String> distinctGroups = new HashSet<String>(getAllGroupNames()); 842 // by using a set first we eliminate duplicate names 843 m_distictGroupNames = Collections.unmodifiableList(new ArrayList<String>(distinctGroups)); 844 } 845}