use of com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty in project midpoint by Evolveum.
the class SqaleRepoBaseTest method searchObjectTest.
protected <T extends ObjectType> SearchResultList<T> searchObjectTest(String description, Class<T> type, Function<S_FilterEntryOrEmpty, S_FilterExit> filter, String... expectedOids) throws SchemaException {
String typeName = type.getSimpleName().replaceAll("Type$", "").toLowerCase();
when("searching for " + typeName + "(s) " + description);
OperationResult operationResult = createOperationResult();
SearchResultList<T> result = searchObjects(type, filter.apply(prismContext.queryFor(type)).build(), operationResult);
then(typeName + "(s) " + description + " are returned");
assertThatOperationResult(operationResult).isSuccess();
assertThat(result).extracting(o -> o.getOid()).containsExactlyInAnyOrder(expectedOids);
return result;
}
use of com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty in project midpoint by Evolveum.
the class SecurityEnforcerImpl method computeSecurityFilterPhase.
/**
* @return additional security filter. This filter is supposed to be added (operation "AND") to the original filter.
*
* See also {@link com.evolveum.midpoint.repo.common.query.SelectorToFilterTranslator} (should be merged eventually?)
*/
private <T extends ObjectType, O extends ObjectType, F> F computeSecurityFilterPhase(MidPointPrincipal principal, String[] operationUrls, AuthorizationPhaseType phase, boolean includeNullPhase, Class<T> objectType, PrismObject<O> object, boolean includeSpecial, ObjectFilter origFilter, String limitAuthorizationAction, List<OrderConstraintsType> paramOrderConstraints, FilterGizmo<F> gizmo, String desc, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
Collection<Authorization> authorities = getAuthorities(principal);
F securityFilterAllow = null;
F securityFilterDeny = null;
QueryAutzItemPaths queryItemsSpec = new QueryAutzItemPaths();
// MID-3916
queryItemsSpec.addRequiredItems(origFilter);
LOGGER.trace(" phase={}, initial query items spec {}", phase, queryItemsSpec.shortDumpLazily());
if (authorities != null) {
for (GrantedAuthority authority : authorities) {
if (authority instanceof Authorization) {
Authorization autz = (Authorization) authority;
String autzHumanReadableDesc = autz.getHumanReadableDesc();
LOGGER.trace(" Evaluating {}", autzHumanReadableDesc);
// action
if (!isApplicableForActions(autz, operationUrls)) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" Authorization not applicable for operation {}", prettyActionUrl(operationUrls));
}
continue;
}
// phase
if (autz.getPhase() == phase || (includeNullPhase && autz.getPhase() == null)) {
LOGGER.trace(" Authorization is applicable for phases {} (continuing evaluation)", phase);
} else {
LOGGER.trace(" Authorization is not applicable for phase {} (includeNullPhase={})", phase, includeNullPhase);
continue;
}
if (!isApplicableLimitations(autz, limitAuthorizationAction)) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" Authorization is limited to other action, not applicable for operation {}", prettyActionUrl(operationUrls));
}
continue;
}
// orderConstraints
if (!isApplicableOrderConstraints(autz, paramOrderConstraints)) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" Authorization not applicable for orderConstraints {}", SchemaDebugUtil.shortDumpOrderConstraintsList(paramOrderConstraints));
}
continue;
}
// object or target
String objectTargetSpec;
ObjectFilter autzObjSecurityFilter = null;
List<OwnedObjectSelectorType> objectSpecTypes;
if (object == null) {
// object not present. Therefore we are looking for object here
objectSpecTypes = autz.getObject();
objectTargetSpec = "object";
} else {
// object present. Therefore we are looking for target
objectSpecTypes = autz.getTarget();
objectTargetSpec = "target";
// .. but we need to decide whether this authorization is applicable to the object
if (isApplicable(autz.getObject(), object, principal, null, "object", autzHumanReadableDesc, task, result)) {
LOGGER.trace(" Authorization is applicable for object {}", object);
} else {
LOGGER.trace(" Authorization is not applicable for object {}", object);
continue;
}
}
boolean applicable = true;
if (objectSpecTypes == null || objectSpecTypes.isEmpty()) {
LOGGER.trace(" No {} specification in authorization (authorization is universally applicable)", objectTargetSpec);
autzObjSecurityFilter = FilterCreationUtil.createAll(prismContext);
} else {
applicable = false;
for (OwnedObjectSelectorType objectSpecType : objectSpecTypes) {
ObjectFilter objSpecSecurityFilter = null;
TypeFilter objSpecTypeFilter = null;
SearchFilterType specFilterType = objectSpecType.getFilter();
ObjectReferenceType specOrgRef = objectSpecType.getOrgRef();
List<ObjectReferenceType> archetypeRefs = objectSpecType.getArchetypeRef();
OrgRelationObjectSpecificationType specOrgRelation = objectSpecType.getOrgRelation();
RoleRelationObjectSpecificationType specRoleRelation = objectSpecType.getRoleRelation();
TenantSelectorType specTenant = objectSpecType.getTenant();
QName specTypeQName = objectSpecType.getType();
PrismObjectDefinition<T> objectDefinition = null;
// Type
if (specTypeQName != null) {
specTypeQName = prismContext.getSchemaRegistry().qualifyTypeName(specTypeQName);
PrismObjectDefinition<?> specObjectDef = prismContext.getSchemaRegistry().findObjectDefinitionByType(specTypeQName);
if (specObjectDef == null) {
throw new SchemaException("Unknown object type " + specTypeQName + " in " + autzHumanReadableDesc);
}
Class<?> specObjectClass = specObjectDef.getCompileTimeClass();
if (objectType.equals(specObjectClass)) {
traceClassMatch("Authorization is applicable for object because of type exact match", specObjectClass, objectType);
} else if (!objectType.isAssignableFrom(specObjectClass)) {
traceClassMatch("Authorization not applicable for object because of type mismatch", specObjectClass, objectType);
continue;
} else {
traceClassMatch("Authorization is applicable for object because of type match, adding more specific type filter", specObjectClass, objectType);
// The spec type is a subclass of requested type. So it might be returned from the search.
// We need to use type filter.
objSpecTypeFilter = prismContext.queryFactory().createType(specTypeQName, null);
// and now we have a more specific object definition to use later in filter processing
objectDefinition = (PrismObjectDefinition<T>) specObjectDef;
}
}
// Owner
if (objectSpecType.getOwner() != null) {
if (objectDefinition == null) {
objectDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectType);
}
// TODO what if owner is specified not as "self" ?
if (TaskType.class.isAssignableFrom(objectType)) {
objSpecSecurityFilter = applyOwnerFilterOwnerRef(TaskType.F_OWNER_REF, objSpecSecurityFilter, principal, objectDefinition);
} else {
LOGGER.trace(" Authorization not applicable for object because it has owner specification (this is not applicable for search)");
continue;
}
}
// Requestor
if (objectSpecType.getRequester() != null) {
if (CaseType.class.isAssignableFrom(objectType)) {
objSpecSecurityFilter = applyRequestorFilter(objSpecSecurityFilter, principal);
} else {
LOGGER.trace(" Authorization not applicable for object because it has requester specification (this is not applicable for search for objects other than CaseType)");
continue;
}
}
// Related object
if (objectSpecType.getRelatedObject() != null) {
if (CaseType.class.isAssignableFrom(objectType) || TaskType.class.isAssignableFrom(objectType)) {
objSpecSecurityFilter = applyRelatedObjectFilter(objectType, objSpecSecurityFilter, principal);
} else {
LOGGER.trace(" Authorization not applicable for object because it has related object specification (this is not applicable for search for objects other than CaseType and TaskType)");
continue;
}
}
// Assignee
if (objectSpecType.getAssignee() != null) {
if (CaseType.class.isAssignableFrom(objectType)) {
objSpecSecurityFilter = applyAssigneeFilter(objSpecSecurityFilter, principal);
} else {
LOGGER.trace(" Authorization not applicable for object because it has assignee specification (this is not applicable for search for objects other than CaseType)");
continue;
}
}
// Delegator
if (objectSpecType.getDelegator() != null) {
// TODO: MID-3899
LOGGER.trace(" Authorization not applicable for object because it has delegator specification (this is not applicable for search)");
continue;
}
applicable = true;
// Special
List<SpecialObjectSpecificationType> specSpecial = objectSpecType.getSpecial();
if (specSpecial != null && !specSpecial.isEmpty()) {
if (!includeSpecial) {
LOGGER.trace(" Skipping authorization, because specials are present: {}", specSpecial);
applicable = false;
}
if (specFilterType != null || specOrgRef != null || specOrgRelation != null || specRoleRelation != null || specTenant != null || !archetypeRefs.isEmpty()) {
throw new SchemaException("Both filter/org/role/archetype/tenant and special object specification specified in authorization");
}
ObjectFilter specialFilter = null;
for (SpecialObjectSpecificationType special : specSpecial) {
if (special == SpecialObjectSpecificationType.SELF) {
String principalOid = principal.getOid();
specialFilter = ObjectQueryUtil.filterOr(specialFilter, prismContext.queryFactory().createInOid(principalOid), prismContext);
} else {
throw new SchemaException("Unsupported special object specification specified in authorization: " + special);
}
}
objSpecSecurityFilter = specTypeQName != null ? prismContext.queryFactory().createType(specTypeQName, specialFilter) : specialFilter;
} else {
LOGGER.trace(" specials empty: {}", specSpecial);
}
// Filter
if (specFilterType != null) {
if (objectDefinition == null) {
objectDefinition = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(objectType);
}
ObjectFilter specFilter = parseAndEvaluateFilter(principal, objectDefinition, specFilterType, objectTargetSpec, autzHumanReadableDesc, task, result);
if (specFilter != null) {
ObjectQueryUtil.assertNotRaw(specFilter, "Filter in authorization object has undefined items. Maybe a 'type' specification is missing in the authorization?");
ObjectQueryUtil.assertPropertyOnly(specFilter, "Filter in authorization object is not property-only filter");
}
LOGGER.trace(" applying property filter {}", specFilter);
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, specFilter, prismContext);
} else {
LOGGER.trace(" filter empty (assuming \"all\")");
if (objSpecSecurityFilter == null) {
objSpecSecurityFilter = prismContext.queryFactory().createAll();
}
}
// Archetypes
if (!archetypeRefs.isEmpty()) {
ObjectFilter archsFilter = null;
for (ObjectReferenceType archetypeRef : archetypeRefs) {
ObjectFilter archFilter = prismContext.queryFor(AssignmentHolderType.class).item(AssignmentHolderType.F_ARCHETYPE_REF).ref(archetypeRef.getOid()).buildFilter();
archsFilter = ObjectQueryUtil.filterOr(archsFilter, archFilter, prismContext);
}
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, archsFilter, prismContext);
LOGGER.trace(" applying archetype filter {}", archsFilter);
} else {
LOGGER.trace(" archetype empty");
}
// Org
if (specOrgRef != null) {
ObjectFilter orgFilter = prismContext.queryFor(ObjectType.class).isChildOf(specOrgRef.getOid()).buildFilter();
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, orgFilter, prismContext);
LOGGER.trace(" applying org filter {}", orgFilter);
} else {
LOGGER.trace(" org empty");
}
// orgRelation
if (specOrgRelation != null) {
ObjectFilter objSpecOrgRelationFilter = null;
QName subjectRelation = specOrgRelation.getSubjectRelation();
for (ObjectReferenceType subjectParentOrgRef : principal.getFocus().getParentOrgRef()) {
if (prismContext.relationMatches(subjectRelation, subjectParentOrgRef.getRelation())) {
S_FilterEntryOrEmpty q = prismContext.queryFor(ObjectType.class);
S_AtomicFilterExit q2;
if (specOrgRelation.getScope() == null || specOrgRelation.getScope() == OrgScopeType.ALL_DESCENDANTS) {
q2 = q.isChildOf(subjectParentOrgRef.getOid());
} else if (specOrgRelation.getScope() == OrgScopeType.DIRECT_DESCENDANTS) {
q2 = q.isDirectChildOf(subjectParentOrgRef.getOid());
} else if (specOrgRelation.getScope() == OrgScopeType.ALL_ANCESTORS) {
q2 = q.isParentOf(subjectParentOrgRef.getOid());
} else {
throw new UnsupportedOperationException("Unknown orgRelation scope " + specOrgRelation.getScope());
}
if (BooleanUtils.isTrue(specOrgRelation.isIncludeReferenceOrg())) {
q2 = q2.or().id(subjectParentOrgRef.getOid());
}
objSpecOrgRelationFilter = ObjectQueryUtil.filterOr(objSpecOrgRelationFilter, q2.buildFilter(), prismContext);
}
}
if (objSpecOrgRelationFilter == null) {
objSpecOrgRelationFilter = FilterCreationUtil.createNone(prismContext);
}
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, objSpecOrgRelationFilter, prismContext);
LOGGER.trace(" applying orgRelation filter {}", objSpecOrgRelationFilter);
} else {
LOGGER.trace(" orgRelation empty");
}
// roleRelation
if (specRoleRelation != null) {
ObjectFilter objSpecRoleRelationFilter = processRoleRelationFilter(principal, autz, specRoleRelation, queryItemsSpec, origFilter);
if (objSpecRoleRelationFilter == null) {
if (autz.maySkipOnSearch()) {
LOGGER.trace(" not applying roleRelation filter {} because it is not efficient and maySkipOnSearch is set", objSpecRoleRelationFilter);
applicable = false;
} else {
objSpecRoleRelationFilter = FilterCreationUtil.createNone(prismContext);
}
}
if (objSpecRoleRelationFilter != null) {
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, objSpecRoleRelationFilter, prismContext);
LOGGER.trace(" applying roleRelation filter {}", objSpecRoleRelationFilter);
}
} else {
LOGGER.trace(" roleRelation empty");
}
// tenant
if (specTenant != null) {
ObjectFilter objSpecTenantFilter = processTenantFilter(principal, autz, specTenant, queryItemsSpec, origFilter);
if (objSpecTenantFilter == null) {
if (autz.maySkipOnSearch()) {
LOGGER.trace(" not applying tenant filter {} because it is not efficient and maySkipOnSearch is set", objSpecTenantFilter);
applicable = false;
} else {
objSpecTenantFilter = FilterCreationUtil.createNone(prismContext);
}
}
if (objSpecTenantFilter != null) {
objSpecSecurityFilter = ObjectQueryUtil.filterAnd(objSpecSecurityFilter, objSpecTenantFilter, prismContext);
LOGGER.trace(" applying tenant filter {}", objSpecTenantFilter);
}
} else {
LOGGER.trace(" tenant empty");
}
if (objSpecTypeFilter != null) {
objSpecTypeFilter.setFilter(objSpecSecurityFilter);
objSpecSecurityFilter = objSpecTypeFilter;
}
traceFilter("objSpecSecurityFilter", objectSpecType, objSpecSecurityFilter);
autzObjSecurityFilter = ObjectQueryUtil.filterOr(autzObjSecurityFilter, objSpecSecurityFilter, prismContext);
}
}
traceFilter("autzObjSecurityFilter", autz, autzObjSecurityFilter);
if (applicable) {
autzObjSecurityFilter = ObjectQueryUtil.simplify(autzObjSecurityFilter, prismContext);
F autzObjSecurityF = gizmo.adopt(autzObjSecurityFilter, autz);
// authority is applicable to this situation. now we can process the decision.
AuthorizationDecisionType decision = autz.getDecision();
if (decision == null || decision == AuthorizationDecisionType.ALLOW) {
// allow
securityFilterAllow = gizmo.or(securityFilterAllow, autzObjSecurityF);
traceFilter("securityFilterAllow", autz, securityFilterAllow, gizmo);
if (!gizmo.isNone(autzObjSecurityF)) {
queryItemsSpec.collectItems(autz);
}
} else {
// deny
if (autz.hasItemSpecification()) {
// This is a tricky situation. We have deny authorization, but it only denies access to
// some items. Therefore we need to find the objects and then filter out the items.
// Therefore do not add this authorization into the filter.
} else {
if (gizmo.isAll(autzObjSecurityF)) {
// There is no point in continuing the evaluation.
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" phase={} done: principal={}, operation={}, {}: deny all", phase, getUsername(principal), prettyActionUrl(operationUrls), desc);
}
F secFilter = gizmo.createDenyAll();
traceFilter("secFilter", null, secFilter, gizmo);
return secFilter;
}
securityFilterDeny = gizmo.or(securityFilterDeny, autzObjSecurityF);
}
}
}
traceFilter("securityFilterAllow", autz, securityFilterAllow, gizmo);
traceFilter("securityFilterDeny", autz, securityFilterDeny, gizmo);
} else {
LOGGER.warn(" Unknown authority type {} in user {}", authority.getClass(), getUsername(principal));
}
}
}
traceFilter("securityFilterAllow", null, securityFilterAllow, gizmo);
traceFilter("securityFilterDeny", null, securityFilterDeny, gizmo);
LOGGER.trace(" final items: {}", queryItemsSpec.shortDumpLazily());
List<ItemPath> unsatisfiedItems = queryItemsSpec.evaluateUnsatisfiedItems();
if (!unsatisfiedItems.isEmpty()) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" phase={} done: principal={}, operation={}, {}: deny because items {} are not allowed", phase, getUsername(principal), prettyActionUrl(operationUrls), desc, unsatisfiedItems);
}
F secFilter = gizmo.createDenyAll();
traceFilter("secFilter", null, secFilter, gizmo);
return secFilter;
}
securityFilterAllow = gizmo.simplify(securityFilterAllow);
if (securityFilterAllow == null) {
// Nothing has been allowed. This means default deny.
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" phase={} done: principal={}, operation={}, {}: default deny", phase, getUsername(principal), prettyActionUrl(operationUrls), desc);
}
F secFilter = gizmo.createDenyAll();
traceFilter("secFilter", null, secFilter, gizmo);
return secFilter;
}
if (securityFilterDeny == null) {
// Nothing has been denied. We have "allow" filter only.
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" phase={} done: principal={}, operation={}, {}: allow\n Filter:\n{}", phase, getUsername(principal), prettyActionUrl(operationUrls), desc, securityFilterAllow == null ? "null" : gizmo.debugDumpFilter(securityFilterAllow, 2));
}
traceFilter("securityFilterAllow", null, securityFilterAllow, gizmo);
return securityFilterAllow;
} else {
// Both "allow" and "deny" filters
F secFilter = gizmo.and(securityFilterAllow, gizmo.not(securityFilterDeny));
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(" phase={} done: principal={}, operation={}, {}: allow (with deny clauses)\n Filter:\n{}", phase, getUsername(principal), prettyActionUrl(operationUrls), desc, secFilter == null ? "null" : gizmo.debugDumpFilter(secFilter, 2));
}
traceFilter("secFilter", null, secFilter, gizmo);
return secFilter;
}
}
Aggregations