/*
* Copyright (c) 2010-2015 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.model.impl.expr;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.model.api.expr.OrgStructFunctions;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.polystring.PolyString;
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.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectQueryUtil;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.task.api.Task;
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.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.jetbrains.annotations.NotNull;
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.*;
/**
* @author mederly
*/
@Component
public class OrgStructFunctionsImpl implements OrgStructFunctions {
private static final Trace LOGGER = TraceManager.getTrace(OrgStructFunctionsImpl.class);
@Autowired
@Qualifier("cacheRepositoryService")
private RepositoryService repositoryService;
@Autowired
private ModelService modelService;
@Autowired
private PrismContext prismContext;
/**
* Returns a list of user's managers. Formally, for each Org O which this user has (any) relation to,
* all managers of O are added to the result.
*
* Some customizations are probably necessary here, e.g. filter out project managers (keep only line managers),
* or defining who is a manager of a user who is itself a manager in its org.unit. (A parent org unit manager,
* perhaps.)
*
* @param user
* @return list of oids of the respective managers
* @throws SchemaException
* @throws ObjectNotFoundException
*/
@Override
public Collection<String> getManagersOids(UserType user, boolean preAuthorized) throws SchemaException, ObjectNotFoundException, SecurityViolationException {
Set<String> retval = new HashSet<String>();
for (UserType u : getManagers(user, preAuthorized)) {
retval.add(u.getOid());
}
return retval;
}
@Override
public Collection<String> getManagersOidsExceptUser(UserType user, boolean preAuthorized) throws SchemaException, ObjectNotFoundException, SecurityViolationException {
Set<String> retval = new HashSet<>();
for (UserType u : getManagers(user, preAuthorized)) {
if (!u.getOid().equals(user.getOid())) {
retval.add(u.getOid());
}
}
return retval;
}
@Override
public Collection<String> getManagersOidsExceptUser(@NotNull Collection<ObjectReferenceType> userRefList, boolean preAuthorized)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException {
Set<String> rv = new HashSet<>();
for (ObjectReferenceType ref : userRefList) {
UserType user = getObject(UserType.class, ref.getOid(), preAuthorized);
rv.addAll(getManagersOidsExceptUser(user, preAuthorized));
}
return rv;
}
@Override
public Collection<UserType> getManagers(UserType user, boolean preAuthorized) throws SchemaException, ObjectNotFoundException, SecurityViolationException {
return getManagers(user, null, false, preAuthorized);
}
@Override
public Collection<UserType> getManagersByOrgType(UserType user, String orgType, boolean preAuthorized) throws SchemaException, ObjectNotFoundException, SecurityViolationException {
return getManagers(user, orgType, false, preAuthorized);
}
@Override
public Collection<UserType> getManagers(UserType user, String orgType, boolean allowSelf, boolean preAuthorized) throws SchemaException, ObjectNotFoundException, SecurityViolationException {
Set<UserType> retval = new HashSet<UserType>();
if (user == null) {
return retval;
}
Collection<String> orgOids = getOrgUnits(user, null, preAuthorized);
while (!orgOids.isEmpty()) {
LOGGER.trace("orgOids: {}", orgOids);
Collection<OrgType> thisLevelOrgs = new ArrayList<OrgType>();
for (String orgOid : orgOids) {
if (orgType != null) {
OrgType org = getOrgByOid(orgOid, preAuthorized);
if (org == null || org.getOrgType() == null) {
continue;
}
if (!org.getOrgType().contains(orgType)) {
continue;
} else {
thisLevelOrgs.add(org);
}
}
Collection<UserType> managersOfOrg = getManagersOfOrg(orgOid, preAuthorized);
for (UserType managerOfOrg: managersOfOrg) {
if (allowSelf || !managerOfOrg.getOid().equals(user.getOid())) {
retval.add(managerOfOrg);
}
}
}
LOGGER.trace("retval: {}", retval);
if (!retval.isEmpty()) {
return retval;
}
Collection<String> nextLevelOids = new ArrayList<String>();
if (orgType == null) {
for (String orgOid : orgOids) {
OrgType org = getOrgByOid(orgOid, preAuthorized);
if (org != null) {
thisLevelOrgs.add(org);
}
}
}
for (OrgType org: thisLevelOrgs) {
for (ObjectReferenceType parentOrgRef: org.getParentOrgRef()) {
if (!nextLevelOids.contains(parentOrgRef.getOid())) {
nextLevelOids.add(parentOrgRef.getOid());
}
}
}
LOGGER.trace("nextLevelOids: {}",nextLevelOids);
orgOids = nextLevelOids;
}
return retval;
}
// todo here we could select "functional" org.units in order to filter out e.g. project managers from the list of managers
// however, the syntax of orgType attribute is not standardized
@Override
public Collection<String> getOrgUnits(UserType user, boolean preAuthorized) {
Set<String> retval = new HashSet<String>();
if (user == null){
return retval;
}
for (ObjectReferenceType orgRef : user.getParentOrgRef()) {
retval.add(orgRef.getOid());
}
return retval;
}
@Override
public Collection<String> getOrgUnits(UserType user, QName relation, boolean preAuthorized) {
Set<String> retval = new HashSet<>();
if (user == null) {
return retval;
}
for (ObjectReferenceType orgRef : user.getParentOrgRef()) {
if (ObjectTypeUtil.relationMatches(relation, orgRef.getRelation())) {
retval.add(orgRef.getOid());
}
}
return retval;
}
@Override
public OrgType getOrgByOid(String oid, boolean preAuthorized) throws SchemaException {
try {
return getObject(OrgType.class, oid, preAuthorized);
} catch (ObjectNotFoundException|SecurityViolationException e) {
return null;
} catch (CommunicationException|ConfigurationException e) {
throw new SystemException("Couldn't get org: " + e.getMessage(), e); // really shouldn't occur
}
}
@Override
public OrgType getOrgByName(String name, boolean preAuthorized) throws SchemaException, SecurityViolationException {
PolyString polyName = new PolyString(name);
ObjectQuery q = ObjectQueryUtil.createNameQuery(polyName, prismContext);
List<PrismObject<OrgType>> result = searchObjects(OrgType.class, q, getCurrentResult(), preAuthorized);
if (result.isEmpty()) {
return null;
}
if (result.size() > 1) {
throw new IllegalStateException("More than one organizational unit with the name '" + name + "' (there are " + result.size() + " of them)");
}
return result.get(0).asObjectable();
}
@Override
public OrgType getParentOrgByOrgType(ObjectType object, String orgType, boolean preAuthorized) throws SchemaException, SecurityViolationException {
Collection<OrgType> parentOrgs = getParentOrgs(object, PrismConstants.Q_ANY, orgType, preAuthorized);
if (parentOrgs.isEmpty()) {
return null;
}
if (parentOrgs.size() > 1) {
throw new IllegalArgumentException("Expected that there will be just one parent org of type "+orgType+" for "+object+", but there were "+parentOrgs.size());
}
return parentOrgs.iterator().next();
}
@Override
public Collection<OrgType> getParentOrgsByRelation(ObjectType object, QName relation, boolean preAuthorized) throws SchemaException, SecurityViolationException {
return getParentOrgs(object, relation, null, preAuthorized);
}
@Override
public Collection<OrgType> getParentOrgsByRelation(ObjectType object, String relation, boolean preAuthorized) throws SchemaException, SecurityViolationException {
return getParentOrgs(object, relation, null, preAuthorized);
}
@Override
public Collection<OrgType> getParentOrgs(ObjectType object, boolean preAuthorized) throws SchemaException, SecurityViolationException {
return getParentOrgs(object, PrismConstants.Q_ANY, null, preAuthorized);
}
@Override
public Collection<OrgType> getParentOrgs(ObjectType object, String relation, String orgType, boolean preAuthorized) throws SchemaException, SecurityViolationException {
return getParentOrgs(object, new QName(null, relation), orgType, preAuthorized);
}
@Override
public Collection<OrgType> getParentOrgs(ObjectType object, QName relation, String orgType, boolean preAuthorized) throws SchemaException, SecurityViolationException {
List<ObjectReferenceType> parentOrgRefs = object.getParentOrgRef();
List<OrgType> parentOrgs = new ArrayList<>(parentOrgRefs.size());
for (ObjectReferenceType parentOrgRef: parentOrgRefs) {
if (!ObjectTypeUtil.relationMatches(relation, parentOrgRef.getRelation())) {
continue;
}
OrgType parentOrg;
try {
parentOrg = getObject(OrgType.class, parentOrgRef.getOid(), preAuthorized);
} catch (ObjectNotFoundException e) {
LOGGER.warn("Org "+parentOrgRef.getOid()+" specified in parentOrgRef in "+object+" was not found: "+e.getMessage(), e);
// but do not rethrow, just skip this
continue;
} catch (CommunicationException | ConfigurationException e) {
// This should not happen.
throw new SystemException(e.getMessage(), e);
}
if (orgType == null || parentOrg.getOrgType().contains(orgType)) {
parentOrgs.add(parentOrg);
}
}
return parentOrgs;
}
@Override
public Collection<UserType> getManagersOfOrg(String orgOid, boolean preAuthorized) throws SchemaException, SecurityViolationException {
Set<UserType> retval = new HashSet<UserType>();
OperationResult result = new OperationResult("getManagerOfOrg");
PrismReferenceValue parentOrgRefVal = new PrismReferenceValue(orgOid, OrgType.COMPLEX_TYPE);
parentOrgRefVal.setRelation(SchemaConstants.ORG_MANAGER);
ObjectQuery objectQuery = QueryBuilder.queryFor(ObjectType.class, prismContext)
.item(ObjectType.F_PARENT_ORG_REF).ref(parentOrgRefVal)
.build();
List<PrismObject<ObjectType>> members = searchObjects(ObjectType.class, objectQuery, result, preAuthorized);
for (PrismObject<ObjectType> member : members) {
if (member.asObjectable() instanceof UserType) {
UserType user = (UserType) member.asObjectable();
retval.add(user);
}
}
return retval;
}
@Override
public boolean isManagerOf(UserType user, String orgOid, boolean preAuthorized) {
for (ObjectReferenceType objectReferenceType : user.getParentOrgRef()) {
if (orgOid.equals(objectReferenceType.getOid()) && ObjectTypeUtil.isManagerRelation(objectReferenceType.getRelation())) {
return true;
}
}
return false;
}
@Override
public boolean isManager(UserType user) {
for (ObjectReferenceType objectReferenceType : user.getParentOrgRef()) {
if (ObjectTypeUtil.isManagerRelation(objectReferenceType.getRelation())) {
return true;
}
}
return false;
}
@Override
public boolean isManagerOfOrgType(UserType user, String orgType, boolean preAuthorized) throws SchemaException {
for (ObjectReferenceType objectReferenceType : user.getParentOrgRef()) {
if (ObjectTypeUtil.isManagerRelation(objectReferenceType.getRelation())) {
OrgType org = getOrgByOid(objectReferenceType.getOid(), preAuthorized);
if (org.getOrgType().contains(orgType)) {
return true;
}
}
}
return false;
}
public <T extends ObjectType> T getObject(Class<T> type, String oid, boolean preAuthorized) throws ObjectNotFoundException, SchemaException,
CommunicationException, ConfigurationException, SecurityViolationException {
PrismObject<T> prismObject;
if (preAuthorized) {
prismObject = repositoryService.getObject(type, oid, null, getCurrentResult());
} else {
prismObject = modelService.getObject(type, oid, null, getCurrentTask(), getCurrentResult());
}
return prismObject.asObjectable();
}
private <T extends ObjectType> List<PrismObject<T>> searchObjects(Class<T> clazz, ObjectQuery query, OperationResult result, boolean preAuthorized)
throws SchemaException, SecurityViolationException {
if (preAuthorized) {
return repositoryService.searchObjects(clazz, query, null, result);
} else {
try {
return modelService.searchObjects(clazz, query, null, getCurrentTask(), result);
} catch (ObjectNotFoundException|CommunicationException|ConfigurationException e) {
throw new SystemException("Couldn't search objects: " + e.getMessage(), e);
}
}
}
private Task getCurrentTask() {
return ModelExpressionThreadLocalHolder.getCurrentTask();
}
private OperationResult getCurrentResult() {
return ModelExpressionThreadLocalHolder.getCurrentResult();
}
}