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