001 /** 002 * Copyright 2005-2012 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 */ 016 package org.kuali.rice.kim.document.rule; 017 018 import org.apache.commons.lang.StringUtils; 019 import org.kuali.rice.core.api.uif.RemotableAttributeError; 020 import org.kuali.rice.core.api.util.RiceKeyConstants; 021 import org.kuali.rice.kim.api.KimConstants; 022 import org.kuali.rice.kim.api.identity.IdentityService; 023 import org.kuali.rice.kim.api.permission.Permission; 024 import org.kuali.rice.kim.api.responsibility.Responsibility; 025 import org.kuali.rice.kim.api.responsibility.ResponsibilityService; 026 import org.kuali.rice.kim.api.role.Role; 027 import org.kuali.rice.kim.api.role.RoleService; 028 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 029 import org.kuali.rice.kim.api.type.KimAttributeField; 030 import org.kuali.rice.kim.api.type.KimType; 031 import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember; 032 import org.kuali.rice.kim.bo.ui.KimDocumentRolePermission; 033 import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier; 034 import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibility; 035 import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibilityAction; 036 import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember; 037 import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMemberQualifier; 038 import org.kuali.rice.kim.document.IdentityManagementRoleDocument; 039 import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator; 040 import org.kuali.rice.kim.framework.type.KimTypeService; 041 import org.kuali.rice.kim.impl.responsibility.AddResponsibilityEvent; 042 import org.kuali.rice.kim.impl.responsibility.AddResponsibilityRule; 043 import org.kuali.rice.kim.impl.responsibility.KimDocumentResponsibilityRule; 044 import org.kuali.rice.kim.impl.responsibility.ResponsibilityBo; 045 import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService; 046 import org.kuali.rice.kim.impl.services.KimImplServiceLocator; 047 import org.kuali.rice.kim.impl.type.KimTypeLookupableHelperServiceImpl; 048 import org.kuali.rice.kim.rule.event.ui.AddDelegationEvent; 049 import org.kuali.rice.kim.rule.event.ui.AddDelegationMemberEvent; 050 import org.kuali.rice.kim.rule.event.ui.AddMemberEvent; 051 import org.kuali.rice.kim.rule.event.ui.AddPermissionEvent; 052 import org.kuali.rice.kim.rule.ui.AddDelegationMemberRule; 053 import org.kuali.rice.kim.rule.ui.AddDelegationRule; 054 import org.kuali.rice.kim.rule.ui.AddMemberRule; 055 import org.kuali.rice.kim.rule.ui.AddPermissionRule; 056 import org.kuali.rice.kim.rules.ui.KimDocumentMemberRule; 057 import org.kuali.rice.kim.rules.ui.KimDocumentPermissionRule; 058 import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationMemberRule; 059 import org.kuali.rice.kim.rules.ui.RoleDocumentDelegationRule; 060 import org.kuali.rice.kns.kim.type.DataDictionaryTypeServiceHelper; 061 import org.kuali.rice.krad.document.Document; 062 import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase; 063 import org.kuali.rice.krad.service.BusinessObjectService; 064 import org.kuali.rice.krad.service.KRADServiceLocator; 065 import org.kuali.rice.krad.util.GlobalVariables; 066 import org.kuali.rice.krad.util.KRADConstants; 067 import org.kuali.rice.krad.util.MessageMap; 068 069 import java.sql.Timestamp; 070 import java.util.ArrayList; 071 import java.util.Collections; 072 import java.util.HashMap; 073 import java.util.HashSet; 074 import java.util.List; 075 import java.util.Map; 076 import java.util.Set; 077 078 /** 079 * @author Kuali Rice Team (rice.collab@kuali.org) 080 */ 081 public class IdentityManagementRoleDocumentRule extends TransactionalDocumentRuleBase implements AddPermissionRule, AddResponsibilityRule, AddMemberRule, AddDelegationRule, AddDelegationMemberRule { 082 // protected static final Logger LOG = Logger.getLogger( IdentityManagementRoleDocumentRule.class ); 083 084 public static final int PRIORITY_NUMBER_MIN_VALUE = 1; 085 public static final int PRIORITY_NUMBER_MAX_VALUE = 11; 086 087 protected AddResponsibilityRule addResponsibilityRule; 088 protected AddPermissionRule addPermissionRule; 089 protected AddMemberRule addMemberRule; 090 protected AddDelegationRule addDelegationRule; 091 protected AddDelegationMemberRule addDelegationMemberRule; 092 protected BusinessObjectService businessObjectService; 093 protected ResponsibilityService responsibilityService; 094 protected Class<? extends AddResponsibilityRule> addResponsibilityRuleClass = KimDocumentResponsibilityRule.class; 095 protected Class<? extends AddPermissionRule> addPermissionRuleClass = KimDocumentPermissionRule.class; 096 protected Class<? extends AddMemberRule> addMemberRuleClass = KimDocumentMemberRule.class; 097 protected Class<? extends AddDelegationRule> addDelegationRuleClass = RoleDocumentDelegationRule.class; 098 protected Class<? extends AddDelegationMemberRule> addDelegationMemberRuleClass = RoleDocumentDelegationMemberRule.class; 099 100 protected IdentityService identityService; 101 private static ResponsibilityInternalService responsibilityInternalService; 102 103 protected AttributeValidationHelper attributeValidationHelper = new AttributeValidationHelper(); 104 105 // KULRICE-4153 106 protected ActiveRoleMemberHelper activeRoleMemberHelper = new ActiveRoleMemberHelper(); 107 108 public IdentityService getIdentityService() { 109 if ( identityService == null) { 110 identityService = KimApiServiceLocator.getIdentityService(); 111 } 112 return identityService; 113 } 114 115 @Override 116 protected boolean processCustomSaveDocumentBusinessRules(Document document) { 117 if (!(document instanceof IdentityManagementRoleDocument)) { 118 return false; 119 } 120 121 IdentityManagementRoleDocument roleDoc = (IdentityManagementRoleDocument)document; 122 123 boolean valid = true; 124 boolean validateRoleAssigneesAndDelegations = !KimTypeLookupableHelperServiceImpl 125 .hasDerivedRoleTypeService(roleDoc.getKimType()); 126 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 127 valid &= validDuplicateRoleName(roleDoc); 128 valid &= validPermissions(roleDoc); 129 valid &= validResponsibilities(roleDoc); 130 getDictionaryValidationService().validateDocumentAndUpdatableReferencesRecursively(document, getMaxDictionaryValidationDepth(), true, false); 131 validateRoleAssigneesAndDelegations &= validAssignRole(roleDoc); 132 if(validateRoleAssigneesAndDelegations){ 133 //valid &= validAssignRole(roleDoc); 134 // KULRICE-4153 & KULRICE-4818 135 // Use the Active Role Member Helper to retrieve only those Role Members & Delegation member that are active 136 // If a member or delegation is active on a Role, and they have an inactive Role Qualifier, Role Qualifier validation will fail 137 // If a member or delegation is inactive on a Role, and they have an inactive Role Qualifier, Role Qualifier validation will pass 138 List<KimDocumentRoleMember> activeRoleMembers = activeRoleMemberHelper.getActiveRoleMembers(roleDoc.getMembers()); 139 List<RoleDocumentDelegationMember> activeRoleDelegationMembers = activeRoleMemberHelper.getActiveDelegationRoleMembers(roleDoc.getDelegationMembers()); 140 141 valid &= validateRoleQualifier(activeRoleMembers, roleDoc.getKimType()); 142 valid &= validRoleMemberActiveDates(roleDoc.getMembers()); 143 valid &= validateDelegationMemberRoleQualifier(activeRoleMembers, activeRoleDelegationMembers, roleDoc.getKimType()); 144 valid &= validDelegationMemberActiveDates(roleDoc.getDelegationMembers()); 145 valid &= validRoleMembersResponsibilityActions(roleDoc.getMembers()); 146 } 147 valid &= validRoleResponsibilitiesActions(roleDoc.getResponsibilities()); 148 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 149 150 return valid; 151 } 152 153 protected boolean validAssignRole(IdentityManagementRoleDocument document){ 154 boolean rulePassed = true; 155 Map<String,String> additionalPermissionDetails = new HashMap<String,String>(); 156 additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, document.getRoleNamespace()); 157 additionalPermissionDetails.put(KimConstants.AttributeConstants.ROLE_NAME, document.getRoleName()); 158 if((document.getMembers()!=null && document.getMembers().size()>0) || 159 (document.getDelegationMembers()!=null && document.getDelegationMembers().size()>0)){ 160 if(!getDocumentDictionaryService().getDocumentAuthorizer(document).isAuthorizedByTemplate( 161 document, KimConstants.NAMESPACE_CODE, KimConstants.PermissionTemplateNames.ASSIGN_ROLE, 162 GlobalVariables.getUserSession().getPrincipalId(), additionalPermissionDetails, null)){ 163 rulePassed = false; 164 } 165 } 166 return rulePassed; 167 } 168 169 @SuppressWarnings("unchecked") 170 protected boolean validDuplicateRoleName(IdentityManagementRoleDocument roleDoc){ 171 Role role = KimApiServiceLocator.getRoleService().getRoleByNamespaceCodeAndName(roleDoc.getRoleNamespace(), 172 roleDoc.getRoleName()); 173 boolean rulePassed = true; 174 if(role!=null){ 175 if(role.getId().equals(roleDoc.getRoleId())) { 176 rulePassed = true; 177 } 178 else{ 179 GlobalVariables.getMessageMap().putError("document.roleName", 180 RiceKeyConstants.ERROR_DUPLICATE_ENTRY, new String[] {"Role Name"}); 181 rulePassed = false; 182 } 183 } 184 return rulePassed; 185 } 186 187 protected boolean validRoleMemberActiveDates(List<KimDocumentRoleMember> roleMembers) { 188 boolean valid = true; 189 int i = 0; 190 for(KimDocumentRoleMember roleMember: roleMembers) { 191 valid &= validateActiveDate("document.members["+i+"].activeToDate", roleMember.getActiveFromDate(), roleMember.getActiveToDate()); 192 i++; 193 } 194 return valid; 195 } 196 197 protected boolean validDelegationMemberActiveDates(List<RoleDocumentDelegationMember> delegationMembers) { 198 boolean valid = true; 199 int i = 0; 200 for(RoleDocumentDelegationMember delegationMember: delegationMembers) { 201 valid &= validateActiveDate("document.delegationMembers[" + i + "].activeToDate", 202 delegationMember.getActiveFromDate(), delegationMember.getActiveToDate()); 203 i++; 204 } 205 return valid; 206 } 207 208 protected boolean validPermissions(IdentityManagementRoleDocument document){ 209 Permission kimPermissionInfo; 210 boolean valid = true; 211 int i = 0; 212 for(KimDocumentRolePermission permission: document.getPermissions()){ 213 kimPermissionInfo = permission.getPermission(); 214 if(!permission.isActive() && !hasPermissionToGrantPermission(permission.getPermission(), document)){ 215 GlobalVariables.getMessageMap().putError("permissions["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_PERMISSION, 216 new String[] {kimPermissionInfo.getNamespaceCode(), kimPermissionInfo.getTemplate().getName()}); 217 valid = false; 218 } 219 i++; 220 } 221 return valid; 222 } 223 224 protected boolean validResponsibilities(IdentityManagementRoleDocument document){ 225 ResponsibilityBo kimResponsibilityImpl; 226 boolean valid = true; 227 int i = 0; 228 for(KimDocumentRoleResponsibility responsibility: document.getResponsibilities()){ 229 kimResponsibilityImpl = responsibility.getKimResponsibility(); 230 if(!responsibility.isActive() && !hasPermissionToGrantResponsibility(ResponsibilityBo.to(responsibility.getKimResponsibility()), document)){ 231 GlobalVariables.getMessageMap().putError("responsibilities["+i+"].active", RiceKeyConstants.ERROR_ASSIGN_RESPONSIBILITY, 232 new String[] {kimResponsibilityImpl.getNamespaceCode(), kimResponsibilityImpl.getTemplate().getName()}); 233 valid = false; 234 } 235 i++; 236 } 237 return valid; 238 } 239 240 protected boolean validRoleResponsibilitiesActions(List<KimDocumentRoleResponsibility> roleResponsibilities){ 241 int i = 0; 242 boolean rulePassed = true; 243 for(KimDocumentRoleResponsibility roleResponsibility: roleResponsibilities){ 244 if(!getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResponsibility.getResponsibilityId())) { 245 validateRoleResponsibilityAction("document.responsibilities["+i+"].roleRspActions[0].priorityNumber", roleResponsibility.getRoleRspActions().get(0)); 246 } 247 i++; 248 } 249 return rulePassed; 250 } 251 252 protected boolean validRoleMembersResponsibilityActions(List<KimDocumentRoleMember> roleMembers){ 253 int i = 0; 254 int j; 255 boolean rulePassed = true; 256 for(KimDocumentRoleMember roleMember: roleMembers){ 257 j = 0; 258 if(roleMember.getRoleRspActions()!=null && !roleMember.getRoleRspActions().isEmpty()){ 259 for(KimDocumentRoleResponsibilityAction roleRspAction: roleMember.getRoleRspActions()){ 260 validateRoleResponsibilityAction("document.members["+i+"].roleRspActions["+j+"].priorityNumber", roleRspAction); 261 j++; 262 } 263 } 264 i++; 265 } 266 return rulePassed; 267 } 268 269 protected boolean validateRoleResponsibilityAction(String errorPath, KimDocumentRoleResponsibilityAction roleRspAction){ 270 boolean rulePassed = true; 271 /*if(StringUtils.isBlank(roleRspAction.getActionPolicyCode())){ 272 GlobalVariables.getErrorMap().putError(errorPath, 273 RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Policy Code"}); 274 rulePassed = false; 275 } 276 if(roleRspAction.getPriorityNumber()==null){ 277 GlobalVariables.getErrorMap().putError(errorPath, 278 RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Priority Number"}); 279 rulePassed = false; 280 } 281 if(StringUtils.isBlank(roleRspAction.getActionTypeCode())){ 282 GlobalVariables.getErrorMap().putError(errorPath, 283 RiceKeyConstants.ERROR_EMPTY_ENTRY, new String[] {"Action Type Code"}); 284 rulePassed = false; 285 }*/ 286 if(roleRspAction.getPriorityNumber()!=null && 287 (roleRspAction.getPriorityNumber()<PRIORITY_NUMBER_MIN_VALUE 288 || roleRspAction.getPriorityNumber()>PRIORITY_NUMBER_MAX_VALUE)){ 289 GlobalVariables.getMessageMap().putError(errorPath, 290 RiceKeyConstants.ERROR_PRIORITY_NUMBER_RANGE, new String[] {PRIORITY_NUMBER_MIN_VALUE+"", PRIORITY_NUMBER_MAX_VALUE+""}); 291 rulePassed = false; 292 } 293 294 return rulePassed; 295 } 296 297 protected boolean validateRoleQualifier(List<KimDocumentRoleMember> roleMembers, KimType kimType){ 298 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 299 300 int memberCounter = 0; 301 int roleMemberCount = 0; 302 List<RemotableAttributeError> errorsTemp; 303 Map<String, String> mapToValidate; 304 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType); 305 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 306 final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId()); 307 final Set<String> uniqueAttributeNames = figureOutUniqueQualificationSet(roleMembers, attributeDefinitions); 308 309 for(KimDocumentRoleMember roleMember: roleMembers) { 310 errorsTemp = Collections.emptyList(); 311 mapToValidate = attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers()); 312 if(!roleMember.isRole()){ 313 errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate); 314 validationErrors.addAll(attributeValidationHelper.convertErrorsForMappedFields( 315 "document.members[" + memberCounter + "]", errorsTemp)); 316 memberCounter++; 317 } 318 if (uniqueAttributeNames.size() > 0) { 319 validateUniquePersonRoleQualifiersUniqueForRoleMembership(roleMember, roleMemberCount, roleMembers, uniqueAttributeNames, validationErrors); 320 } 321 322 roleMemberCount += 1; 323 } 324 325 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 326 327 if (validationErrors.isEmpty()) { 328 return true; 329 } 330 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 331 return false; 332 } 333 334 /** 335 * Finds the names of the unique qualification attributes which this role should be checking against 336 * 337 * @param memberships the memberships (we take the qualification from the first) 338 * @param attributeDefinitions information about the attributeDefinitions 339 * @return a Set of unique attribute ids (with their indices, for error reporting) 340 */ 341 protected Set<String> figureOutUniqueQualificationSet(List<KimDocumentRoleMember> memberships, List<KimAttributeField> attributeDefinitions) { 342 Set<String> uniqueAttributeIds = new HashSet<String>(); 343 344 if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly 345 KimDocumentRoleMember membership = memberships.get(0); 346 347 for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) { 348 if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) { 349 final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField( 350 qualifier.getKimAttribute().getAttributeName(), attributeDefinitions); 351 352 if (relatedDefinition != null && relatedDefinition.isUnique()) { 353 uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set 354 } 355 } 356 } 357 } 358 359 return uniqueAttributeIds; 360 } 361 362 /** 363 * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique 364 * 365 * @param membershipToCheck the membership to check 366 * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes) 367 * @param validationErrors Map<String, String> of errors to report 368 * @return true if all unique values are indeed unique, false otherwise 369 */ 370 protected boolean validateUniquePersonRoleQualifiersUniqueForRoleMembership(KimDocumentRoleMember membershipToCheck, int membershipToCheckIndex, List<KimDocumentRoleMember> memberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) { 371 boolean foundError = false; 372 int count = 0; 373 374 for (KimDocumentRoleMember membership : memberships) { 375 if (membershipToCheckIndex != count) { 376 if (sameMembership(membershipToCheck, membership)) { 377 if (sameUniqueMembershipQualifications(membershipToCheck, membership, uniqueQualifierIds)) { 378 foundError = true; 379 // add error to each qualifier which is supposed to be unique 380 int qualifierCount = 0; 381 382 for (KimDocumentRoleQualifier qualifier : membership.getQualifiers()) { 383 if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) { 384 validationErrors.add(RemotableAttributeError.Builder.create("document.members["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+qualifier.getKimAttribute().getAttributeName()+";"+qualifier.getAttrVal()).build()); 385 } 386 qualifierCount += 1; 387 } 388 } 389 } 390 } 391 count += 1; 392 } 393 394 return foundError; 395 } 396 397 /** 398 * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id 399 * 400 * @param membershipA the first membership to check 401 * @param membershipB the second membership to check 402 * @return true if the two memberships represent the same member; false if they do not, or if it could not be profitably determined if the members were the same 403 */ 404 protected boolean sameMembership(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB) { 405 if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) { 406 return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId()); 407 } 408 return false; 409 } 410 411 /** 412 * Given two memberships which represent the same member, do they share qualifications? 413 * 414 * @param membershipA the first membership to check 415 * @param membershipB the second membership to check 416 * @param uniqueAttributeIds the Set of attribute definition ids which should be unique 417 * @return 418 */ 419 protected boolean sameUniqueMembershipQualifications(KimDocumentRoleMember membershipA, KimDocumentRoleMember membershipB, Set<String> uniqueAttributeIds) { 420 boolean equalSoFar = true; 421 for (String kimAttributeDefinitionId : uniqueAttributeIds) { 422 final KimDocumentRoleQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId); 423 final KimDocumentRoleQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId); 424 425 if (qualifierA != null && qualifierB != null) { 426 equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal())); 427 } 428 } 429 return equalSoFar; 430 } 431 432 protected KimDocumentRoleMember getRoleMemberForDelegation( 433 List<KimDocumentRoleMember> roleMembers, RoleDocumentDelegationMember delegationMember){ 434 if(roleMembers==null || delegationMember==null || delegationMember.getRoleMemberId()==null) { return null; } 435 for(KimDocumentRoleMember roleMember: roleMembers){ 436 if(delegationMember.getRoleMemberId().equals(roleMember.getRoleMemberId())) { 437 return roleMember; 438 } 439 } 440 return null; 441 } 442 443 protected boolean validateDelegationMemberRoleQualifier(List<KimDocumentRoleMember> roleMembers, 444 List<RoleDocumentDelegationMember> delegationMembers, KimType kimType){ 445 List<RemotableAttributeError> validationErrors = new ArrayList<RemotableAttributeError>(); 446 boolean valid; 447 int memberCounter = 0; 448 List<RemotableAttributeError> errorsTemp; 449 Map<String, String> mapToValidate; 450 KimTypeService kimTypeService = KimFrameworkServiceLocator.getKimTypeService(kimType); 451 GlobalVariables.getMessageMap().removeFromErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 452 KimDocumentRoleMember roleMember; 453 String errorPath; 454 final List<KimAttributeField> attributeDefinitions = kimTypeService.getAttributeDefinitions(kimType.getId()); 455 final Set<String> uniqueQualifierAttributes = figureOutUniqueQualificationSetForDelegation(delegationMembers, attributeDefinitions); 456 457 for(RoleDocumentDelegationMember delegationMember: delegationMembers) { 458 errorPath = "delegationMembers["+memberCounter+"]"; 459 mapToValidate = attributeValidationHelper.convertQualifiersToMap(delegationMember.getQualifiers()); 460 if(!delegationMember.isRole()){ 461 errorsTemp = kimTypeService.validateAttributes(kimType.getId(), mapToValidate); 462 validationErrors.addAll( 463 attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp)); 464 } 465 roleMember = getRoleMemberForDelegation(roleMembers, delegationMember); 466 if(roleMember==null){ 467 valid = false; 468 GlobalVariables.getMessageMap().putError("document.delegationMembers["+memberCounter+"]", RiceKeyConstants.ERROR_DELEGATE_ROLE_MEMBER_ASSOCIATION, new String[]{}); 469 } else{ 470 errorsTemp = kimTypeService.validateUnmodifiableAttributes( 471 kimType.getId(), 472 attributeValidationHelper.convertQualifiersToMap(roleMember.getQualifiers()), 473 mapToValidate); 474 validationErrors.addAll( 475 attributeValidationHelper.convertErrorsForMappedFields(errorPath, errorsTemp) ); 476 } 477 if (uniqueQualifierAttributes.size() > 0) { 478 validateUniquePersonRoleQualifiersUniqueForRoleDelegation(delegationMember, memberCounter, delegationMembers, uniqueQualifierAttributes, validationErrors); 479 } 480 memberCounter++; 481 } 482 GlobalVariables.getMessageMap().addToErrorPath(KRADConstants.DOCUMENT_PROPERTY_NAME); 483 if (validationErrors.isEmpty()) { 484 valid = true; 485 } else { 486 attributeValidationHelper.moveValidationErrorsToErrorMap(validationErrors); 487 valid = false; 488 } 489 return valid; 490 } 491 492 /** 493 * Finds the names of the unique qualification attributes which this role should be checking against 494 * 495 * @param memberships the memberships (we take the qualification from the first) 496 * @param attributeDefinitions information about the attributeDefinitions 497 * @return a Set of unique attribute ids (with their indices, for error reporting) 498 */ 499 protected Set<String> figureOutUniqueQualificationSetForDelegation(List<RoleDocumentDelegationMember> memberships, List<KimAttributeField> attributeDefinitions) { 500 Set<String> uniqueAttributeIds = new HashSet<String>(); 501 502 if (memberships != null && memberships.size() > 1) { // if there aren't two or more members, doing this whole check is kinda silly 503 RoleDocumentDelegationMember membership = memberships.get(0); 504 505 for (RoleDocumentDelegationMemberQualifier qualifier : membership.getQualifiers()) { 506 if (qualifier != null && qualifier.getKimAttribute() != null && !StringUtils.isBlank(qualifier.getKimAttribute().getAttributeName())) { 507 final KimAttributeField relatedDefinition = DataDictionaryTypeServiceHelper.findAttributeField( 508 qualifier.getKimAttribute().getAttributeName(), attributeDefinitions); 509 510 if (relatedDefinition.isUnique()) { 511 uniqueAttributeIds.add(qualifier.getKimAttrDefnId()); // it's unique - add it to the Set 512 } 513 } 514 } 515 } 516 517 return uniqueAttributeIds; 518 } 519 520 /** 521 * Checks all the qualifiers for the given membership, so that all qualifiers which should be unique are guaranteed to be unique 522 * 523 * @param delegationMembershipToCheck the membership to check 524 * @param membershipToCheckIndex the index of the person's membership in the role (for error reporting purposes) 525 * @param validationErrors Map<String, String> of errors to report 526 * @return true if all unique values are indeed unique, false otherwise 527 */ 528 protected boolean validateUniquePersonRoleQualifiersUniqueForRoleDelegation(RoleDocumentDelegationMember delegationMembershipToCheck, int membershipToCheckIndex, List<RoleDocumentDelegationMember> delegationMemberships, Set<String> uniqueQualifierIds, List<RemotableAttributeError> validationErrors) { 529 boolean foundError = false; 530 int count = 0; 531 532 for (RoleDocumentDelegationMember delegationMembership : delegationMemberships) { 533 if (membershipToCheckIndex != count) { 534 if (sameDelegationMembership(delegationMembershipToCheck, delegationMembership)) { 535 if (sameUniqueDelegationMembershipQualifications(delegationMembershipToCheck, delegationMembership, uniqueQualifierIds)) { 536 foundError = true; 537 // add error to each qualifier which is supposed to be unique 538 int qualifierCount = 0; 539 540 for (RoleDocumentDelegationMemberQualifier qualifier : delegationMembership.getQualifiers()) { 541 if (qualifier != null && uniqueQualifierIds.contains(qualifier.getKimAttrDefnId())) { 542 validationErrors.add(RemotableAttributeError.Builder.create("document.delegationMembers["+membershipToCheckIndex+"].qualifiers["+qualifierCount+"].attrVal", RiceKeyConstants.ERROR_DOCUMENT_IDENTITY_MANAGEMENT_PERSON_QUALIFIER_VALUE_NOT_UNIQUE+":"+qualifier.getKimAttribute().getAttributeName()+";"+qualifier.getAttrVal()).build()); 543 } 544 qualifierCount += 1; 545 } 546 } 547 } 548 } 549 count += 1; 550 } 551 552 return foundError; 553 } 554 555 /** 556 * Determines if two memberships represent the same member being added: that is, the two memberships have the same type code and id 557 * 558 * @param membershipA the first membership to check 559 * @param membershipB the second membership to check 560 * @return true if the two memberships represent the same member; false if they do not, or if it could not be profitably determined if the members were the same 561 */ 562 protected boolean sameDelegationMembership(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB) { 563 if (!StringUtils.isBlank(membershipA.getMemberTypeCode()) && !StringUtils.isBlank(membershipB.getMemberTypeCode()) && !StringUtils.isBlank(membershipA.getMemberId()) && !StringUtils.isBlank(membershipB.getMemberId())) { 564 return membershipA.getMemberTypeCode().equals(membershipB.getMemberTypeCode()) && membershipA.getMemberId().equals(membershipB.getMemberId()); 565 } 566 return false; 567 } 568 569 /** 570 * Given two memberships which represent the same member, do they share qualifications? 571 * 572 * @param membershipA the first membership to check 573 * @param membershipB the second membership to check 574 * @param uniqueAttributeIds the Set of attribute definition ids which should be unique 575 * @return 576 */ 577 protected boolean sameUniqueDelegationMembershipQualifications(RoleDocumentDelegationMember membershipA, RoleDocumentDelegationMember membershipB, Set<String> uniqueAttributeIds) { 578 boolean equalSoFar = true; 579 for (String kimAttributeDefinitionId : uniqueAttributeIds) { 580 final RoleDocumentDelegationMemberQualifier qualifierA = membershipA.getQualifier(kimAttributeDefinitionId); 581 final RoleDocumentDelegationMemberQualifier qualifierB = membershipB.getQualifier(kimAttributeDefinitionId); 582 583 if (qualifierA != null && qualifierB != null) { 584 equalSoFar &= (qualifierA.getAttrVal() == null && qualifierB.getAttrVal() == null) || (qualifierA.getAttrVal() == null || qualifierA.getAttrVal().equals(qualifierB.getAttrVal())); 585 } 586 } 587 return equalSoFar; 588 } 589 590 protected boolean validateActiveDate(String errorPath, Timestamp activeFromDate, Timestamp activeToDate) { 591 // TODO : do not have detail bus rule yet, so just check this for now. 592 boolean valid = true; 593 if (activeFromDate != null && activeToDate !=null && activeToDate.before(activeFromDate)) { 594 MessageMap errorMap = GlobalVariables.getMessageMap(); 595 errorMap.putError(errorPath, RiceKeyConstants.ERROR_ACTIVE_TO_DATE_BEFORE_FROM_DATE); 596 valid = false; 597 598 } 599 return valid; 600 } 601 602 /** 603 * 604 * This method checks to see if adding a role to role membership 605 * creates a circular reference. 606 * 607 * @param addMemberEvent 608 * @return true - ok to assign, no circular references 609 * false - do not make assignment, will create circular reference. 610 */ 611 protected boolean checkForCircularRoleMembership(AddMemberEvent addMemberEvent) 612 { 613 KimDocumentRoleMember newMember = addMemberEvent.getMember(); 614 if (newMember == null || StringUtils.isBlank(newMember.getMemberId())){ 615 MessageMap errorMap = GlobalVariables.getMessageMap(); 616 errorMap.putError("member.memberId", RiceKeyConstants.ERROR_INVALID_ROLE, new String[] {""}); 617 return false; 618 } 619 Set<String> roleMemberIds = null; 620 // if the role member is a role, check to make sure we won't be creating a circular reference. 621 // Verify that the new role is not already related to the role either directly or indirectly 622 if (newMember.isRole()){ 623 // get all nested role member ids that are of type role 624 RoleService roleService = KimApiServiceLocator.getRoleService(); 625 roleMemberIds = roleService.getRoleTypeRoleMemberIds(newMember.getMemberId()); 626 627 // check to see if the document role is not a member of the new member role 628 IdentityManagementRoleDocument document = (IdentityManagementRoleDocument)addMemberEvent.getDocument(); 629 if (roleMemberIds.contains(document.getRoleId())){ 630 MessageMap errorMap = GlobalVariables.getMessageMap(); 631 errorMap.putError("member.memberId", RiceKeyConstants.ERROR_ASSIGN_ROLE_MEMBER_CIRCULAR, new String[] {newMember.getMemberId()}); 632 return false; 633 } 634 } 635 return true; 636 } 637 638 /** 639 * @return the addResponsibilityRule 640 */ 641 public AddResponsibilityRule getAddResponsibilityRule() { 642 if(addResponsibilityRule == null){ 643 try { 644 addResponsibilityRule = addResponsibilityRuleClass.newInstance(); 645 } catch ( Exception ex ) { 646 throw new RuntimeException( "Unable to create AddResponsibilityRule instance using class: " + addResponsibilityRuleClass, ex ); 647 } 648 } 649 return addResponsibilityRule; 650 } 651 652 /** 653 * @return the addPermissionRule 654 */ 655 public AddPermissionRule getAddPermissionRule() { 656 if(addPermissionRule == null){ 657 try { 658 addPermissionRule = addPermissionRuleClass.newInstance(); 659 } catch ( Exception ex ) { 660 throw new RuntimeException( "Unable to create AddPermissionRule instance using class: " + addPermissionRuleClass, ex ); 661 } 662 } 663 return addPermissionRule; 664 } 665 666 /** 667 * @return the addMemberRule 668 */ 669 public AddMemberRule getAddMemberRule() { 670 if(addMemberRule == null){ 671 try { 672 addMemberRule = addMemberRuleClass.newInstance(); 673 } catch ( Exception ex ) { 674 throw new RuntimeException( "Unable to create AddMemberRule instance using class: " + addMemberRuleClass, ex ); 675 } 676 } 677 return addMemberRule; 678 } 679 680 /** 681 * @return the addDelegationRule 682 */ 683 public AddDelegationRule getAddDelegationRule() { 684 if(addDelegationRule == null){ 685 try { 686 addDelegationRule = addDelegationRuleClass.newInstance(); 687 } catch ( Exception ex ) { 688 throw new RuntimeException( "Unable to create AddDelegationRule instance using class: " + addDelegationRuleClass, ex ); 689 } 690 } 691 return addDelegationRule; 692 } 693 694 /** 695 * @return the addDelegationMemberRule 696 */ 697 public AddDelegationMemberRule getAddDelegationMemberRule() { 698 if(addDelegationMemberRule == null){ 699 try { 700 addDelegationMemberRule = addDelegationMemberRuleClass.newInstance(); 701 } catch ( Exception ex ) { 702 throw new RuntimeException( "Unable to create AddDelegationMemberRule instance using class: " + addDelegationMemberRuleClass, ex ); 703 } 704 } 705 return addDelegationMemberRule; 706 } 707 708 public boolean processAddPermission(AddPermissionEvent addPermissionEvent) { 709 return getAddPermissionRule().processAddPermission(addPermissionEvent); 710 } 711 712 public boolean hasPermissionToGrantPermission(Permission kimPermissionInfo , IdentityManagementRoleDocument document){ 713 return getAddPermissionRule().hasPermissionToGrantPermission(kimPermissionInfo, document); 714 } 715 716 public boolean processAddResponsibility(AddResponsibilityEvent addResponsibilityEvent) { 717 return getAddResponsibilityRule().processAddResponsibility(addResponsibilityEvent); 718 } 719 720 public boolean hasPermissionToGrantResponsibility(Responsibility kimResponsibilityInfo, IdentityManagementRoleDocument document) { 721 return getAddResponsibilityRule().hasPermissionToGrantResponsibility(kimResponsibilityInfo, document); 722 } 723 724 public boolean processAddMember(AddMemberEvent addMemberEvent) { 725 boolean success = new KimDocumentMemberRule().processAddMember(addMemberEvent); 726 success &= validateActiveDate("member.activeFromDate", addMemberEvent.getMember().getActiveFromDate(), addMemberEvent.getMember().getActiveToDate()); 727 success &= checkForCircularRoleMembership(addMemberEvent); 728 return success; 729 } 730 731 public boolean processAddDelegation(AddDelegationEvent addDelegationEvent) { 732 return getAddDelegationRule().processAddDelegation(addDelegationEvent); 733 } 734 735 public boolean processAddDelegationMember(AddDelegationMemberEvent addDelegationMemberEvent) { 736 boolean success = new RoleDocumentDelegationMemberRule().processAddDelegationMember(addDelegationMemberEvent); 737 RoleDocumentDelegationMember roleDocumentDelegationMember = addDelegationMemberEvent.getDelegationMember(); 738 success &= validateActiveDate("delegationMember.activeFromDate", roleDocumentDelegationMember.getActiveFromDate(), roleDocumentDelegationMember.getActiveToDate()); 739 return success; 740 } 741 742 public ResponsibilityService getResponsibilityService() { 743 if(responsibilityService == null){ 744 responsibilityService = KimApiServiceLocator.getResponsibilityService(); 745 } 746 return responsibilityService; 747 } 748 749 public ResponsibilityInternalService getResponsibilityInternalService() { 750 if ( responsibilityInternalService == null ) { 751 responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService(); 752 } 753 return responsibilityInternalService; 754 } 755 756 757 /** 758 * @return the businessObjectService 759 */ 760 public BusinessObjectService getBusinessObjectService() { 761 if(businessObjectService == null){ 762 businessObjectService = KRADServiceLocator.getBusinessObjectService(); 763 } 764 return businessObjectService; 765 } 766 }