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