001/** 002 * Copyright 2005-2011 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.kim.impl.jaxb; 017 018import static org.kuali.rice.core.api.criteria.PredicateFactory.equal; 019 020import java.util.HashMap; 021import java.util.List; 022import java.util.Set; 023 024import javax.xml.bind.UnmarshalException; 025 026import org.apache.commons.lang.StringUtils; 027import org.joda.time.DateTime; 028import org.kuali.rice.core.api.criteria.QueryByCriteria; 029import org.kuali.rice.core.api.membership.MemberType; 030import org.kuali.rice.core.util.jaxb.NameAndNamespacePair; 031import org.kuali.rice.kim.api.group.GroupContract; 032import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 033import org.kuali.rice.kim.api.permission.PermissionContract; 034import org.kuali.rice.kim.api.role.Role; 035import org.kuali.rice.kim.api.role.RoleContract; 036import org.kuali.rice.kim.api.role.RoleMember; 037import org.kuali.rice.kim.api.role.RoleMemberContract; 038import org.kuali.rice.kim.api.role.RoleService; 039import org.kuali.rice.kim.api.services.KimApiServiceLocator; 040 041/** 042 * Helper class containing static methods for aiding in parsing role XML. 043 * 044 * <p>All non-private methods are package-private so that only the KIM-parsing-related code can make use of them. (TODO: Is that necessary?) 045 * 046 * <p>TODO: Should this be converted into a service instead? 047 * 048 * @author Kuali Rice Team (rice.collab@kuali.org) 049 */ 050public final class RoleXmlUtil { 051 // Do not allow outside code to instantiate this class. 052 private RoleXmlUtil() {} 053 054 /** 055 * Performs the necessary validation on the new role, then saves it. 056 * 057 * @param newRole The role to persist. 058 * @return The ID of the persisted role. 059 * @throws IllegalArgumentException if newRole is null. 060 * @throws UnmarshalException if newRole contains invalid data. 061 */ 062 static String validateAndPersistNewRole(RoleXmlDTO newRole) throws UnmarshalException { 063 if (newRole == null) { 064 throw new IllegalArgumentException("Cannot persist a null role"); 065 } 066 067 // Validate the role and (if applicable) retrieve the ID from an existing matching role. 068 validateAndPrepareRole(newRole); 069 070 Role.Builder builder = Role.Builder.create(); 071 builder.setActive(newRole.getActive()); 072 builder.setDescription(newRole.getRoleDescription()); 073 builder.setId(newRole.getRoleId()); 074 builder.setKimTypeId(newRole.getKimTypeId()); 075 builder.setName(newRole.getRoleName()); 076 builder.setNamespaceCode(newRole.getNamespaceCode()); 077 078 //save the role 079 Role role = KimApiServiceLocator.getRoleService().createRole(builder.build()); 080 081 // Set a flag on the role to indicate that it has now been persisted so that the unmarshalling process will not save this role more than once. 082 newRole.setAlreadyPersisted(true); 083 084 return role.getId(); 085 } 086 087 /** 088 * Performs the necessary validation on the new role member, then saves it. 089 * 090 * @param newRoleMember The role member to save. 091 * @return The ID of the persisted role member. 092 * @throws IllegalArgumentException if newRoleMember is null. 093 * @throws UnmarshalException if newRoleMember contains invalid data. 094 */ 095 static String validateAndPersistNewRoleMember(RoleMemberXmlDTO newRoleMember) throws UnmarshalException { 096 097 if (newRoleMember == null) { 098 throw new IllegalArgumentException("Cannot persist a null role member"); 099 } 100 101 // Validate role ID and role name/namespace. 102 validateRoleIdAndRoleNameForMember(newRoleMember); 103 104 // Validate member type, member ID, and member name/namespace. 105 validateMemberIdentity(newRoleMember); 106 107 // Validate the from/to dates, if defined. 108 if (newRoleMember.getActiveFromDate() != null && newRoleMember.getActiveToDate() != null && 109 newRoleMember.getActiveFromDate().compareTo(newRoleMember.getActiveToDate()) > 0) { 110 throw new UnmarshalException("Cannot create a role member whose activeFromDate occurs after its activeToDate"); 111 } 112 113 // Define defaults as needed. 114 if (newRoleMember.getQualifications() == null) { 115 newRoleMember.setQualifications(new HashMap<String, String>()); 116 } 117 118 RoleMember.Builder builder = RoleMember.Builder.create(newRoleMember.getRoleId(), newRoleMember.getRoleIdAsMember(), 119 newRoleMember.getMemberId(), newRoleMember.getMemberType(), 120 newRoleMember.getActiveFromDate() == null ? null : new DateTime(newRoleMember.getActiveFromDate().getMillis()), 121 newRoleMember.getActiveToDate() == null ? null : new DateTime(newRoleMember.getActiveToDate().getMillis()), 122 newRoleMember.getQualifications(), null, null); 123 124 // Save the role member. 125 RoleMemberContract newMember = KimApiServiceLocator.getRoleService().createRoleMember(builder.build()); 126 127 return newMember.getId(); 128 } 129 130 /** 131 * Performs the necessary validation on the role permission, then saves it. 132 * 133 * @param newRolePermission The role permission to save. 134 * @throws IllegalArgumentException if newRolePermission is null 135 * @throws UnmarshalException if newRolePermission contains invalid data. 136 */ 137 static void validateAndPersistNewRolePermission(RolePermissionXmlDTO newRolePermission) throws UnmarshalException { 138 if (newRolePermission == null) { 139 throw new IllegalArgumentException("newRolePermission cannot be null"); 140 } 141 142 // Validate the role permission, and prepare its role ID if necessary. 143 validateAndPrepareRolePermission(newRolePermission); 144 145 // Save the role permission. 146 KimApiServiceLocator.getRoleService().assignPermissionToRole(newRolePermission.getPermissionId(), newRolePermission.getRoleId()); 147 } 148 149 /** 150 * Removes any role members for a given role whose IDs are not listed in a given role member ID set. 151 * 152 * @param roleId The ID of the role containing the role members. 153 * @param existingRoleMemberIds The IDs of the role members that should not be removed. 154 * @throws IllegalArgumentException if roleId is blank or refers to a non-existent role, or if existingRoleMemberIds is null. 155 */ 156 static void removeRoleMembers(String roleId, Set<String> existingRoleMemberIds) { 157 if (StringUtils.isBlank(roleId)) { 158 throw new IllegalArgumentException("roleId cannot be blank"); 159 } else if (existingRoleMemberIds == null) { 160 throw new IllegalArgumentException("existingRoleMemberIds cannot be null"); 161 } 162 RoleService roleUpdateService = KimApiServiceLocator.getRoleService(); 163 RoleContract role = KimApiServiceLocator.getRoleService().getRole(roleId); 164 if (role == null) { 165 throw new IllegalArgumentException("Cannot remove role members for role with ID \"" + roleId + "\" because that role does not exist"); 166 } 167 168 // Remove any role members whose IDs are not in the set. 169 List<RoleMember> roleMembers = KimApiServiceLocator.getRoleService().findRoleMembers( 170 QueryByCriteria.Builder.fromPredicates(equal("roleId", roleId))).getResults(); 171 if (roleMembers != null && !roleMembers.isEmpty()) { 172 for (RoleMemberContract roleMember : roleMembers) { 173 if (!existingRoleMemberIds.contains(roleMember.getId())) { 174 // If the role member needs to be removed, use the member type code to determine which removal method to call. 175 MemberType memberType = roleMember.getType(); 176 if (MemberType.PRINCIPAL.equals(memberType)) { 177 roleUpdateService.removePrincipalFromRole(roleMember.getMemberId(), role.getNamespaceCode(), role.getName(), 178 (roleMember.getAttributes() != null) ? roleMember.getAttributes() : new HashMap<String, String>()); 179 } else if (MemberType.GROUP.equals(memberType)) { 180 roleUpdateService.removeGroupFromRole(roleMember.getMemberId(), role.getNamespaceCode(), role.getName(), 181 (roleMember.getAttributes() != null) ? roleMember.getAttributes() :new HashMap<String, String>()); 182 } else if (MemberType.ROLE.equals(memberType)) { 183 roleUpdateService.removeRoleFromRole(roleMember.getMemberId(), role.getNamespaceCode(), role.getName(), 184 (roleMember.getAttributes() != null) ? roleMember.getAttributes() : new HashMap<String, String>()); 185 } 186 } 187 } 188 } 189 } 190 191 /** 192 * Validates a new role's name, namespace, KIM type, and description, and sets the role's ID if the name and namespace match an existing role. 193 */ 194 private static void validateAndPrepareRole(RoleXmlDTO newRole) throws UnmarshalException { 195 // Ensure that the role name, role namespace, KIM type, and description have all been specified. 196 if (StringUtils.isBlank(newRole.getRoleName()) || StringUtils.isBlank(newRole.getNamespaceCode())) { 197 throw new UnmarshalException("Cannot create or override a role with a blank name or a blank namespace"); 198 } else if (StringUtils.isBlank(newRole.getKimTypeId())) { 199 throw new UnmarshalException("Cannot create or override a role without specikfying a KIM type"); 200 } else if (StringUtils.isBlank(newRole.getRoleDescription())) { 201 throw new UnmarshalException("Cannot create or override a role with a blank description"); 202 } 203 204 // Attempt to find an existing matching role, and assign its ID to the validated role if it exists. 205 String matchingId = KimApiServiceLocator.getRoleService().getRoleIdByNamespaceCodeAndName( 206 newRole.getNamespaceCode(), newRole.getRoleName()); 207 if (StringUtils.isNotBlank(matchingId)) { 208 newRole.setRoleId(matchingId); 209 } 210 } 211 212 /** 213 * Validates a new role member's role ID, role name, and role namespace. 214 */ 215 private static void validateRoleIdAndRoleNameForMember(RoleMemberXmlDTO newRoleMember) throws UnmarshalException { 216 // If the "roleMember" tag was not a descendant of a "role" tag, derive and validate its role information accordingly. 217 if (newRoleMember instanceof RoleMemberXmlDTO.OutsideOfRole) { 218 RoleMemberXmlDTO.OutsideOfRole standaloneMember = (RoleMemberXmlDTO.OutsideOfRole) newRoleMember; 219 if (standaloneMember.getRoleNameAndNamespace() != null) { 220 // If a name + namespace combo is given, verify that the combo maps to an existing role. 221 String existingId = KimApiServiceLocator.getRoleService().getRoleIdByNamespaceCodeAndName( 222 standaloneMember.getRoleNamespaceCode(), standaloneMember.getRoleName()); 223 if (StringUtils.isBlank(existingId)) { 224 throw new UnmarshalException("Cannot create role member for role with name \"" + standaloneMember.getRoleName() + "\" and namespace \"" + 225 standaloneMember.getRoleNamespaceCode() + "\" because such a role does not exist"); 226 } 227 228 // If the role member defines its own role ID, verify that it's the same as the one from the existing role; otherwise, assign the member's role ID. 229 if (StringUtils.isBlank(standaloneMember.getRoleId())) { 230 standaloneMember.setRoleId(existingId); 231 } else if (!standaloneMember.getRoleId().equals(existingId)) { 232 throw new UnmarshalException("Cannot create role member for role with ID \"" + standaloneMember.getRoleId() + "\", name \"" + 233 standaloneMember.getRoleName() + "\", and namespace \"" + standaloneMember.getRoleNamespaceCode() + 234 "\" because the existing role with the same name and namespace has an ID of \"" + existingId + "\" instead"); 235 } 236 } else if (StringUtils.isBlank(standaloneMember.getRoleId())) { 237 throw new UnmarshalException("Cannot create role member without providing the role ID or role name + namespace that the member belongs to"); 238 } else if (KimApiServiceLocator.getRoleService().getRole(standaloneMember.getRoleId()) == null) { 239 throw new UnmarshalException("Cannot create role member for the role with ID \"" + standaloneMember.getRoleId() + "\" because that role does not exist"); 240 } 241 } 242 243 // Ensure that a role ID was explicitly defined or was derived from a name + namespace combo. 244 if (StringUtils.isBlank(newRoleMember.getRoleId())) { 245 throw new UnmarshalException("Cannot create role member without providing the role ID or role name + namespace that the member belongs to"); 246 } 247 } 248 249 /** 250 * Validates a new role member's member type, member ID, member name, and (if applicable) member namespace code. 251 */ 252 private static void validateMemberIdentity(RoleMemberXmlDTO newRoleMember) throws UnmarshalException { 253 // Ensure that sufficient and non-conflicting membership info has been set. (The getMemberTypeCode() method performs such validation.) 254 MemberType memberType = newRoleMember.getMemberType(); 255 if (memberType == null) { 256 throw new UnmarshalException("Cannot create a role member with no member principal/group/role identification information specified"); 257 } 258 259 // Ensure that a valid member ID was specified, if present. 260 if (StringUtils.isNotBlank(newRoleMember.getMemberId())) { 261 if (MemberType.PRINCIPAL.equals(memberType)) { 262 // If the member is a principal, ensure that the principal exists. 263 if (KimApiServiceLocator.getIdentityService().getPrincipal(newRoleMember.getPrincipalId()) == null) { 264 throw new UnmarshalException("Cannot create principal role member with principal ID \"" + 265 newRoleMember.getPrincipalId() + "\" because such a person does not exist"); 266 } 267 } else if (MemberType.GROUP.equals(memberType)) { 268 // If the member is a group, ensure that the group exists. 269 if (KimApiServiceLocator.getGroupService().getGroup(newRoleMember.getGroupId()) == null) { 270 throw new UnmarshalException("Cannot create group role member with group ID \"" + 271 newRoleMember.getGroupId() + "\" because such a group does not exist"); 272 } 273 } else if (MemberType.ROLE.equals(memberType)) { 274 // If the member is another role, ensure that the role exists and that the role is not trying to become a member of itself. 275 if (newRoleMember.getRoleId().equals(newRoleMember.getRoleIdAsMember())) { 276 throw new UnmarshalException("The role with ID \"" + newRoleMember.getRoleIdAsMember() + "\" cannot be made a member of itself"); 277 } else if (KimApiServiceLocator.getRoleService().getRole(newRoleMember.getRoleIdAsMember()) == null) { 278 throw new UnmarshalException("Cannot use role with ID \"" + newRoleMember.getRoleIdAsMember() + 279 "\" as a role member because such a role does not exist"); 280 } 281 } 282 } 283 284 // Ensure that a valid member name (and namespace, if applicable) was specified, if present. 285 if (StringUtils.isNotBlank(newRoleMember.getMemberName())) { 286 if (MemberType.PRINCIPAL.equals(memberType)) { 287 //If the member is a principal, ensure that the principal exists and does not conflict with any existing principal ID information. 288 PrincipalContract tempPrincipal = KimApiServiceLocator.getIdentityService().getPrincipalByPrincipalName(newRoleMember.getPrincipalName()); 289 if (tempPrincipal == null) { 290 throw new UnmarshalException("Cannot create principal role member with principal name \"" + 291 newRoleMember.getPrincipalName() + "\" because such a person does not exist"); 292 } else if (StringUtils.isBlank(newRoleMember.getPrincipalId())) { 293 // If no principal ID was given, assign one from the retrieved principal. 294 newRoleMember.setPrincipalId(tempPrincipal.getPrincipalId()); 295 } else if (!newRoleMember.getPrincipalId().equals(tempPrincipal.getPrincipalId())) { 296 throw new UnmarshalException("Cannot create principal role member with principal ID \"" + newRoleMember.getPrincipalId() + 297 "\" and principal name \"" + newRoleMember.getPrincipalName() + "\" because the principal with that name has an ID of \"" + 298 tempPrincipal.getPrincipalId() + "\" instead"); 299 } 300 } else if (MemberType.GROUP.equals(memberType)) { 301 // If the member is a group, ensure that the group exists and does not conflict with any existing group ID information. 302 NameAndNamespacePair groupNameAndNamespace = newRoleMember.getGroupName(); 303 GroupContract tempGroup = KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName( 304 groupNameAndNamespace.getNamespaceCode(), groupNameAndNamespace.getName()); 305 if (tempGroup == null) { 306 throw new UnmarshalException("Cannot create group role member with namespace \"" + groupNameAndNamespace.getNamespaceCode() + 307 "\" and name \"" + groupNameAndNamespace.getName() + "\" because such a group does not exist"); 308 } else if (StringUtils.isBlank(newRoleMember.getGroupId())) { 309 // If no group ID was given, assign one from the retrieved group. 310 newRoleMember.setGroupId(tempGroup.getId()); 311 } else if (!newRoleMember.getGroupId().equals(tempGroup.getId())) { 312 throw new UnmarshalException("Cannot create group role member with ID \"" + newRoleMember.getGroupId() + "\", namespace \"" + 313 groupNameAndNamespace.getNamespaceCode() + "\", and name \"" + groupNameAndNamespace.getName() + 314 "\" because the group with that namespace and name has an ID of \"" + tempGroup.getId() + "\" instead"); 315 } 316 } else if (MemberType.ROLE.equals(memberType)) { 317 // If the member is another role, ensure that the role exists, does not conflict with any existing role ID information, and is not the member's role. 318 NameAndNamespacePair roleNameAndNamespace = newRoleMember.getRoleNameAsMember(); 319 RoleContract tempRole = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName( 320 roleNameAndNamespace.getNamespaceCode(), roleNameAndNamespace.getName()); 321 if (tempRole == null) { 322 throw new UnmarshalException("Cannot use role with namespace \"" + roleNameAndNamespace.getNamespaceCode() + 323 "\" and name \"" + roleNameAndNamespace.getName() + "\" as a role member because such a role does not exist"); 324 } else if (newRoleMember.getRoleId().equals(tempRole.getId())) { 325 throw new UnmarshalException("The role with namespace \"" + roleNameAndNamespace.getNamespaceCode() + 326 "\" and name \"" + roleNameAndNamespace.getName() + "\" cannot be made a member of itself"); 327 } else if (StringUtils.isBlank(newRoleMember.getRoleId())) { 328 // If no role ID was given, assign one from the retrieved role. 329 newRoleMember.setRoleIdAsMember(tempRole.getId()); 330 } else if (!newRoleMember.getRoleId().equals(tempRole.getId())) { 331 throw new RuntimeException("Cannot use role with ID \"" + newRoleMember.getRoleId() + "\", namespace \"" + 332 roleNameAndNamespace.getNamespaceCode() + "\", and name \"" + roleNameAndNamespace.getName() + 333 "\" as a role member because the role with that namespace and name has an ID of \"" + 334 tempRole.getId() + "\" instead"); 335 } 336 } 337 } 338 339 // Ensure that a member ID was either explicitly defined or was derived from the member name (and namespace, if applicable). 340 if (StringUtils.isBlank(newRoleMember.getMemberId())) { 341 throw new RuntimeException("Cannot create a role member with no member principal/group/role identification information specified"); 342 } 343 344 } 345 346 /** 347 * Validates a role permission's role and permission identification information, and assigns its role ID if needed. 348 */ 349 private static void validateAndPrepareRolePermission(RolePermissionXmlDTO newRolePermission) throws UnmarshalException { 350 351 // If this is a standalone role permission, derive and validate its role information accordingly. 352 if (newRolePermission instanceof RolePermissionXmlDTO.OutsideOfRole) { 353 RolePermissionXmlDTO.OutsideOfRole standaloneRolePerm = (RolePermissionXmlDTO.OutsideOfRole) newRolePermission; 354 if (standaloneRolePerm.getRoleNameAndNamespace() != null) { 355 // If a role name + namespace is given, assign or validate the role ID accordingly. 356 String tempRoleId = KimApiServiceLocator.getRoleService().getRoleIdByNamespaceCodeAndName( 357 standaloneRolePerm.getRoleNamespaceCode(), standaloneRolePerm.getRoleName()); 358 if (StringUtils.isBlank(tempRoleId)) { 359 throw new UnmarshalException("Cannot assign permission to role with namespace \"" + standaloneRolePerm.getRoleNamespaceCode() + 360 "\" and name \"" + standaloneRolePerm.getRoleName() + "\" because that role does not exist"); 361 } else if (StringUtils.isBlank(standaloneRolePerm.getRoleId())) { 362 // If no role ID was given, assign one from the retrieved role. 363 standaloneRolePerm.setRoleId(standaloneRolePerm.getRoleId()); 364 } else if (!standaloneRolePerm.getRoleId().equals(tempRoleId)) { 365 throw new UnmarshalException("Cannot assign permission to role with ID \"" + standaloneRolePerm.getRoleId() + "\", namespace \"" + 366 standaloneRolePerm.getRoleNamespaceCode() + "\", and name \"" + standaloneRolePerm.getRoleName() + 367 "\" because the existing role with that name and namespace has an ID of \"" + tempRoleId + "\" instead"); 368 } 369 } else if (StringUtils.isBlank(standaloneRolePerm.getRoleId())) { 370 throw new UnmarshalException( 371 "Cannot assign permission to role without providing the role ID or role name + namespace that the permission is assigned to"); 372 } else if (KimApiServiceLocator.getRoleService().getRole(standaloneRolePerm.getRoleId()) == null) { 373 throw new UnmarshalException("Cannot assign permission to role with ID \"" + standaloneRolePerm.getRoleId() + 374 "\" because that role does not exist"); 375 } 376 } 377 378 // Ensure that a role ID was explicitly defined or was derived from a name + namespace combo. 379 if (StringUtils.isBlank(newRolePermission.getRoleId())) { 380 throw new UnmarshalException("Cannot assign permission to role without providing the role ID or role name + namespace that the permission is assigned to"); 381 } 382 383 // If the permission is being identified by name and namespace, derive or validate its permission ID accordingly. 384 if (newRolePermission.getPermissionNameAndNamespace() != null) { 385 PermissionContract permission = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName( 386 newRolePermission.getPermissionNamespaceCode(), newRolePermission.getPermissionName()); 387 if (permission == null) { 388 throw new UnmarshalException("Cannot get role assigned to permission with namespace \"" + newRolePermission.getPermissionNamespaceCode() + 389 "\" and name \"" + newRolePermission.getPermissionName() + "\" because that permission does not exist"); 390 } else if (StringUtils.isBlank(newRolePermission.getPermissionId())) { 391 // If no permission ID was given, assign one from the retrieved permission. 392 newRolePermission.setPermissionId(permission.getId()); 393 } else if (!newRolePermission.getPermissionId().equals(permission.getId())) { 394 throw new UnmarshalException("Cannot get role assigned to permission with ID \"" + newRolePermission.getPermissionId() + "\", namespace \"" + 395 newRolePermission.getPermissionNamespaceCode() + "\", and name \"" + newRolePermission.getPermissionName() + 396 "\" because the existing permission with that name and namespace has an ID of \"" + permission.getId() + "\" instead"); 397 } 398 } else if (StringUtils.isBlank(newRolePermission.getPermissionId())) { 399 throw new UnmarshalException("Cannot assign permission to role without specifying the ID or name and namespace of the permission to assign"); 400 } else if (KimApiServiceLocator.getPermissionService().getPermission(newRolePermission.getPermissionId()) == null) { 401 throw new UnmarshalException("Cannot get role assigned to permission with ID \"" + newRolePermission.getPermissionId() + 402 "\" because that permission does not exist"); 403 } 404 } 405 406}