/* * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.certification.impl; import com.evolveum.midpoint.model.api.expr.OrgStructFunctions; import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment; import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.prism.query.builder.QueryBuilder; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.CertCampaignTypeUtil; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import javax.xml.namespace.QName; import java.util.*; import java.util.stream.Collectors; /** * @author mederly */ @Component public class AccCertReviewersHelper { private static final transient Trace LOGGER = TraceManager.getTrace(AccCertReviewersHelper.class); @Autowired @Qualifier("cacheRepositoryService") private RepositoryService repositoryService; @Autowired private OrgStructFunctions orgStructFunctions; @Autowired private PrismContext prismContext; @Autowired private AccCertExpressionHelper expressionHelper; public AccessCertificationReviewerSpecificationType findReviewersSpecification(AccessCertificationCampaignType campaign, int stage, Task task, OperationResult result) { AccessCertificationStageDefinitionType stageDef = CertCampaignTypeUtil.findStageDefinition(campaign, stage); return stageDef.getReviewerSpecification(); } public List<ObjectReferenceType> getReviewersForCase(AccessCertificationCaseType _case, AccessCertificationCampaignType campaign, AccessCertificationReviewerSpecificationType reviewerSpec, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (reviewerSpec == null) { return Collections.emptyList(); // TODO issue a warning here? } List<ObjectReferenceType> reviewers = new ArrayList<>(); if (Boolean.TRUE.equals(reviewerSpec.isUseTargetOwner())) { cloneAndMerge(reviewers, getTargetObjectOwners(_case, task, result)); } if (Boolean.TRUE.equals(reviewerSpec.isUseTargetApprover())) { cloneAndMerge(reviewers, getTargetObjectApprovers(_case, task, result)); } if (Boolean.TRUE.equals(reviewerSpec.isUseObjectOwner())) { cloneAndMerge(reviewers, getObjectOwners(_case, task, result)); } if (Boolean.TRUE.equals(reviewerSpec.isUseObjectApprover())) { cloneAndMerge(reviewers, getObjectApprovers(_case, task, result)); } if (reviewerSpec.getUseObjectManager() != null) { cloneAndMerge(reviewers, getObjectManagers(_case, reviewerSpec.getUseObjectManager(), task, result)); } for (ExpressionType reviewerExpression : reviewerSpec.getReviewerExpression()) { ExpressionVariables variables = new ExpressionVariables(); variables.addVariableDefinition(ExpressionConstants.VAR_CERTIFICATION_CASE, _case); variables.addVariableDefinition(ExpressionConstants.VAR_CAMPAIGN, campaign); variables.addVariableDefinition(ExpressionConstants.VAR_REVIEWER_SPECIFICATION, reviewerSpec); List<ObjectReferenceType> refList = expressionHelper .evaluateRefExpressionChecked(reviewerExpression, variables, "reviewer expression", task, result); cloneAndMerge(reviewers, refList); } resolveRoleReviewers(reviewers, task, result); if (reviewers.isEmpty()) { cloneAndMerge(reviewers, reviewerSpec.getDefaultReviewerRef()); } cloneAndMerge(reviewers, reviewerSpec.getAdditionalReviewerRef()); resolveRoleReviewers(reviewers, task, result); return reviewers; } private void resolveRoleReviewers(List<ObjectReferenceType> reviewers, Task task, OperationResult result) throws SchemaException { List<ObjectReferenceType> resolved = new ArrayList<>(); for (Iterator<ObjectReferenceType> iterator = reviewers.iterator(); iterator.hasNext(); ) { ObjectReferenceType reviewer = iterator.next(); if (QNameUtil.match(reviewer.getType(), RoleType.COMPLEX_TYPE) || QNameUtil.match(reviewer.getType(), OrgType.COMPLEX_TYPE) || QNameUtil.match(reviewer.getType(), ServiceType.COMPLEX_TYPE)) { iterator.remove(); resolved.addAll(getMembers(reviewer, task, result)); } } for (ObjectReferenceType ref : resolved) { if (!containsOid(reviewers, ref.getOid())) { reviewers.add(ref); } } } private List<ObjectReferenceType> getMembers(ObjectReferenceType abstractRoleRef, Task task, OperationResult result) throws SchemaException { ObjectQuery query = QueryBuilder.queryFor(UserType.class, prismContext) .item(UserType.F_ROLE_MEMBERSHIP_REF).ref(abstractRoleRef.getOid()) .build(); return repositoryService.searchObjects(UserType.class, query, null, result).stream() .map(obj -> ObjectTypeUtil.createObjectRef(obj)) .collect(Collectors.toList()); } private void cloneAndMerge(List<ObjectReferenceType> reviewers, Collection<ObjectReferenceType> newReviewers) { if (newReviewers == null) { return; } for (ObjectReferenceType newReviewer : newReviewers) { if (!containsOid(reviewers, newReviewer.getOid())) { reviewers.add(newReviewer.clone()); } } } private boolean containsOid(List<ObjectReferenceType> reviewers, String oid) { for (ObjectReferenceType reviewer : reviewers) { if (reviewer.getOid().equals(oid)) { return true; } } return false; } private Collection<ObjectReferenceType> getObjectManagers(AccessCertificationCaseType _case, ManagerSearchType managerSearch, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException { ModelExpressionThreadLocalHolder.pushExpressionEnvironment(new ExpressionEnvironment<>(task, result)); try { ObjectReferenceType objectRef = _case.getObjectRef(); ObjectType object = resolveReference(objectRef, ObjectType.class, result); String orgType = managerSearch.getOrgType(); boolean allowSelf = Boolean.TRUE.equals(managerSearch.isAllowSelf()); Collection<UserType> managers; if (object instanceof UserType) { managers = orgStructFunctions.getManagers((UserType) object, orgType, allowSelf, true); } else if (object instanceof OrgType) { // TODO more elaborate behavior; eliminate unneeded resolveReference above managers = orgStructFunctions.getManagersOfOrg(object.getOid(), true); } else if (object instanceof RoleType || object instanceof ServiceType) { // TODO implement managers = new HashSet<>(); } else { // TODO warning? managers = new HashSet<>(); } List<ObjectReferenceType> retval = new ArrayList<>(managers.size()); for (UserType manager : managers) { retval.add(ObjectTypeUtil.createObjectRef(manager)); } return retval; } catch (SecurityViolationException e) { // never occurs, as preAuthorized is TRUE above throw new IllegalStateException("Impossible has happened: " + e.getMessage(), e); } finally { ModelExpressionThreadLocalHolder.popExpressionEnvironment(); } } protected List<ObjectReferenceType> getTargetObjectOwners(AccessCertificationCaseType _case, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (_case.getTargetRef() == null) { return null; } ObjectType target = resolveReference(_case.getTargetRef(), ObjectType.class, result); if (target instanceof AbstractRoleType) { return getAssignees((AbstractRoleType) target, SchemaConstants.ORG_OWNER, task, result); } else if (target instanceof ResourceType) { return ResourceTypeUtil.getOwnerRef((ResourceType) target); } else { return null; } } private List<ObjectReferenceType> getAssignees(AbstractRoleType role, QName relation, Task task, OperationResult result) throws SchemaException { List<ObjectReferenceType> rv = new ArrayList<>(); if (SchemaConstants.ORG_OWNER.equals(relation)) { CollectionUtils.addIgnoreNull(rv, role.getOwnerRef()); } else if (SchemaConstants.ORG_APPROVER.equals(relation)) { rv.addAll(role.getApproverRef()); } else { throw new AssertionError(relation); } // TODO in theory, we could look for approvers/owners of UserType, right? PrismReferenceValue ref = new PrismReferenceValue(role.getOid()); ref.setRelation(relation); ObjectQuery query = QueryBuilder.queryFor(FocusType.class, prismContext) .item(FocusType.F_ROLE_MEMBERSHIP_REF).ref(ref) .build(); List<PrismObject<FocusType>> assignees = repositoryService.searchObjects(FocusType.class, query, null, result); LOGGER.trace("Looking for '{}' of {} using {}: found: {}", relation.getLocalPart(), role, query, assignees); assignees.forEach(o -> rv.add(ObjectTypeUtil.createObjectRef(o))); return rv; } protected List<ObjectReferenceType> getObjectOwners(AccessCertificationCaseType _case, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (_case.getObjectRef() == null) { return null; } ObjectType object = resolveReference(_case.getObjectRef(), ObjectType.class, result); if (object instanceof AbstractRoleType) { return getAssignees((AbstractRoleType) object, SchemaConstants.ORG_OWNER, task, result); } else { return null; } } private Collection<ObjectReferenceType> getTargetObjectApprovers(AccessCertificationCaseType _case, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (_case.getTargetRef() == null) { return null; } ObjectType target = resolveReference(_case.getTargetRef(), ObjectType.class, result); if (target instanceof AbstractRoleType) { return getAssignees((AbstractRoleType) target, SchemaConstants.ORG_APPROVER, task, result); } else if (target instanceof ResourceType) { return ResourceTypeUtil.getApproverRef((ResourceType) target); } else { return null; } } private Collection<ObjectReferenceType> getObjectApprovers(AccessCertificationCaseType _case, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { if (_case.getObjectRef() == null) { return null; } ObjectType object = resolveReference(_case.getObjectRef(), ObjectType.class, result); if (object instanceof AbstractRoleType) { return getAssignees((AbstractRoleType) object, SchemaConstants.ORG_APPROVER, task, result); } else { return null; } } private ObjectType resolveReference(ObjectReferenceType objectRef, Class<? extends ObjectType> defaultObjectTypeClass, OperationResult result) throws SchemaException, ObjectNotFoundException { final Class<? extends ObjectType> objectTypeClass; if (objectRef.getType() != null) { objectTypeClass = (Class<? extends ObjectType>) prismContext.getSchemaRegistry().getCompileTimeClassForObjectType(objectRef.getType()); if (objectTypeClass == null) { throw new SchemaException("No object class found for " + objectRef.getType()); } } else { objectTypeClass = defaultObjectTypeClass; } PrismObject<? extends ObjectType> object = repositoryService.getObject(objectTypeClass, objectRef.getOid(), null, result); return object.asObjectable(); } }