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 }