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