Skip to content

Commit

Permalink
Shadow Marks: Implemented computation of read only policy
Browse files Browse the repository at this point in the history
  • Loading branch information
tonydamage committed Feb 7, 2023
1 parent efbb0bc commit 92ab8c8
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4321,6 +4321,24 @@
</xsd:annotation>
</xsd:element>
<xsd:element name="policyStatement" type="tns:PolicyStatementType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Policy statements to manually add or exclude effective marks of shadow.
</xsd:documentation>
<xsd:appinfo>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="effectiveProvisioningPolicy" type="tns:ShadowProvisioningPolicyType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Effective provisioning policy derived from Shadow marks and resource configuration.
</xsd:documentation>
<xsd:appinfo>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import static com.evolveum.midpoint.schema.util.ObjectTypeUtil.asObjectables;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
Expand All @@ -20,6 +22,7 @@
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.processor.ResourceObjectPattern;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
Expand All @@ -30,19 +33,40 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowProvisioningPolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType;
import com.google.common.base.Objects;

import static com.evolveum.midpoint.xml.ns._public.common.common_3.PolicyStatementTypeType.*;

@Component
public class ShadowMarkManager {

private abstract class Impl {

protected abstract Collection<ObjectReferenceType> getEffectiveMarkRefs(ShadowType shadow, OperationResult result);

protected abstract boolean isProtectedByResourcePolicy(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs);

protected abstract boolean policyNotExcluded(ShadowType shadow, String markProtectedShadowOid);

protected abstract ShadowProvisioningPolicyType computeEffectivePolicy(Collection<ObjectReferenceType> effectiveMarkRefs,
ShadowType shadow, OperationResult result);

protected abstract void setEffectiveMarks(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs);

}

private static final String MARK_PROTECTED_SHADOW_OID = SystemObjectsType.MARK_PROTECTED_SHADOW.value();

private static ShadowMarkManager instance = null;

@Autowired @Qualifier("cacheRepositoryService") private RepositoryService cacheRepositoryService;
@Autowired private PrismContext prismContext;

private Impl behaviour;

@PostConstruct
public void init() {
behaviour = cacheRepositoryService.supportsMarks() ? new MarkSupport() : new Legacy();
instance = this;
}

Expand Down Expand Up @@ -119,4 +143,203 @@ public boolean isProtectedShadowPolicyNotExcluded(ShadowType shadow) {
}
return true;
}

public void updateEffectiveMarksAndPolicies(Collection<ResourceObjectPattern> protectedAccountPatterns,
ShadowType shadow, OperationResult result) throws SchemaException {

Collection<ObjectReferenceType> effectiveMarkRefs = behaviour.getEffectiveMarkRefs(shadow, result);

if (behaviour.isProtectedByResourcePolicy(shadow, effectiveMarkRefs)) {
// Account was originally marked by resource policy
// removing mark, so we can recompute if it still applies
removeRefByOid(effectiveMarkRefs, MARK_PROTECTED_SHADOW_OID);
}

if (needsToEvaluateResourcePolicy(shadow, effectiveMarkRefs)) {
// Resource protection policy was not explicitly excluded
// so we need to check if shadow is protected
if (ResourceObjectPattern.matches(shadow, protectedAccountPatterns)) {
// Shadow is protected by resource protected object configuration
// FIXME: Add metadata to policy mark
effectiveMarkRefs.add(resourceProtectedShadowMark());
}
}

ShadowProvisioningPolicyType effectivePolicy = behaviour.computeEffectivePolicy(effectiveMarkRefs, shadow, result);
updateShadowObject(shadow, effectiveMarkRefs, effectivePolicy);
}


private ObjectReferenceType resourceProtectedShadowMark() {
ObjectReferenceType ret = new ObjectReferenceType();
ret.setOid(MARK_PROTECTED_SHADOW_OID);
ret.setType(MarkType.COMPLEX_TYPE);

// TODO Maybe add metadata with provenance pointing that this was added by resource configuration
return ret;
}

private static void removeRefByOid(Collection<ObjectReferenceType> refs, String oid) {
var refIter = refs.iterator();
while (refIter.hasNext()) {
var current = refIter.next();
if (oid.equals(current.getOid())) {
refIter.remove();
}
}
}

private boolean needsToEvaluateResourcePolicy(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs) {

if (containsOid(effectiveMarkRefs, MARK_PROTECTED_SHADOW_OID)) {
return false;
}
return behaviour.policyNotExcluded(shadow, MARK_PROTECTED_SHADOW_OID);
}

private static boolean containsOid(Collection<ObjectReferenceType> refs, @NotNull String oid) {
for (var ref : refs) {
if (oid.equals(ref.getOid())) {
return true;
}
}
return false;
}

private void updateShadowObject(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs,
ShadowProvisioningPolicyType effectivePolicy) {
behaviour.setEffectiveMarks(shadow, effectiveMarkRefs);

shadow.setEffectiveProvisioningPolicy(effectivePolicy);
if (Boolean.TRUE.equals(effectivePolicy.isProtected())) {
shadow.setProtectedObject(true);
}
}

private class MarkSupport extends Impl {

@Override
protected void setEffectiveMarks(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs) {
shadow.getEffectiveMarkRef().clear();
shadow.getEffectiveMarkRef().addAll(effectiveMarkRefs);
}

@Override
protected Collection<ObjectReferenceType> getEffectiveMarkRefs(ShadowType shadow, OperationResult result) {
List<ObjectReferenceType> ret = new ArrayList<>();
for (var mark : shadow.getEffectiveMarkRef()) {
if (mark.getOid() != null && policyNotExcluded(shadow, mark.getOid())) {
// Mark is effective if it was not excluded
ret.add(mark);
}
}

for (var statement : shadow.getPolicyStatement()) {
if (APPLY.equals(statement.getType())
&& statement.getMarkRef() != null && statement.getMarkRef().getOid() != null) {
// Add to effective refs
ret.add(statement.getMarkRef().clone());
}
}
return ret;
}

@Override
protected boolean isProtectedByResourcePolicy(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs) {
if (containsPolicyStatement(shadow, MARK_PROTECTED_SHADOW_OID, APPLY)) {
// Protected Shadow Mark was added manually
return false;
}
return containsOid(effectiveMarkRefs, MARK_PROTECTED_SHADOW_OID);
}

@Override
protected boolean policyNotExcluded(ShadowType shadow, String markOid) {
return containsPolicyStatement(shadow, markOid, EXCLUDE);
}

protected boolean containsPolicyStatement(@NotNull ShadowType shadow, @NotNull String markOid, @NotNull PolicyStatementTypeType policyType) {
for (var statement : shadow.getPolicyStatement()) {
if (policyType.equals(statement.getType())) {
var markRef = statement.getMarkRef();
if (markRef != null && markOid.equals(markRef.getOid())) {
return true;
}
}
}
return false;
}


@Override
protected ShadowProvisioningPolicyType computeEffectivePolicy(Collection<ObjectReferenceType> effectiveMarkRefs,
ShadowType shadow, OperationResult result) {
var ret = new ShadowProvisioningPolicyType();
Collection<MarkType> marks = getShadowMarks(effectiveMarkRefs, result);

// Account is protected if any of shadow marks set it to protected.

ret.setProtected(firstNonDefaultValue(marks, ShadowProvisioningPolicyType::isProtected, false));

// If account is protected all other flags must be marked so.
if (Boolean.TRUE.equals(ret.isProtected())) {
ret.setReadOnly(true);
} else {
ret.setReadOnly(firstNonDefaultValue(marks, ShadowProvisioningPolicyType::isReadOnly, false));
}
return ret;
}

}

private class Legacy extends Impl {

@Override
protected Collection<ObjectReferenceType> getEffectiveMarkRefs(ShadowType shadow, OperationResult result) {
return new ArrayList<>();
}

@Override
protected boolean isProtectedByResourcePolicy(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs) {
return false;
}

@Override
protected boolean policyNotExcluded(ShadowType shadow, String markProtectedShadowOid) {
return true;
}

@Override
protected ShadowProvisioningPolicyType computeEffectivePolicy(Collection<ObjectReferenceType> effectiveMarkRefs,
ShadowType shadow, OperationResult result) {
if (containsOid(effectiveMarkRefs, MARK_PROTECTED_SHADOW_OID)) {
return new ShadowProvisioningPolicyType()
._protected(true)
.readOnly(true);
}
return new ShadowProvisioningPolicyType()
._protected(false)
.readOnly(false);
}

@Override
protected void setEffectiveMarks(ShadowType shadow, Collection<ObjectReferenceType> effectiveMarkRefs) {
// NOOP, since marks are not supported by repository
}
}

public static boolean firstNonDefaultValue(Collection<MarkType> marks,
Function<ShadowProvisioningPolicyType, Boolean> extractor, boolean defaultValue) {
for (var mark : marks) {
if (mark.getProvisioningPolicy() != null) {
var value = extractor.apply(mark.getProvisioningPolicy());
// If value is different from default, we return and use it
if (value != null && !Objects.equal(defaultValue, value)) {
return value;
}
}
}

return defaultValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1710,7 +1710,7 @@ void postProcessResourceObjectRead(ProvisioningContext ctx, PrismObject<ShadowTy
}
ShadowType resourceObjectBean = resourceObject.asObjectable();

ProvisioningUtil.setProtectedFlag(ctx, resourceObjectBean, expressionFactory, result);
ProvisioningUtil.setEffectiveProvisioningPolicy(ctx, resourceObjectBean, expressionFactory, result);

if (resourceObjectBean.isExists() == null) {
resourceObjectBean.setExists(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ private void processRepoShadow(PrismObject<ShadowType> shadow, OperationResult r

// Fixing MID-1640; hoping that the protected object filter uses only identifiers (that are stored in repo)
// TODO we will eventually store the "protected" flag right in the repo shadow, so this code will be obsolete
ProvisioningUtil.setProtectedFlag(ctx, shadowBean, localBeans.expressionFactory, result);
ProvisioningUtil.setEffectiveProvisioningPolicy(ctx, shadowBean, localBeans.expressionFactory, result);

ProvisioningUtil.validateShadow(shadow, true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ static ShadowedObjectConstruction create(

copyIgnored();
mergeCredentials();
setProtectedFlag(result);
setEffectiveProvisioningPolicy(result);

// exists, dead
// This may seem strange, but always take exists and dead flags from the repository.
Expand Down Expand Up @@ -185,9 +185,9 @@ private void copyAndAdoptAssociations(OperationResult result) throws SchemaExcep
}
}

private void setProtectedFlag(OperationResult result) throws SchemaException, ConfigurationException,
private void setEffectiveProvisioningPolicy(OperationResult result) throws SchemaException, ConfigurationException,
ObjectNotFoundException, CommunicationException, ExpressionEvaluationException, SecurityViolationException {
ProvisioningUtil.setProtectedFlag(ctx, resultingShadowedObject, beans.expressionFactory, result);
ProvisioningUtil.setEffectiveProvisioningPolicy(ctx, resultingShadowedObject, beans.expressionFactory, result);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ public void addNewProposedShadow(
repoShadow.setProtectedObject(null);
}

if (repoShadow.getEffectiveProvisioningPolicy() != null) {
repoShadow.setEffectiveProvisioningPolicy(null);
}

normalizeAttributes(repoShadow, ctx.getObjectDefinitionRequired());

return repoShadow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,26 +318,33 @@ public static <T> PropertyDelta<T> narrowPropertyDelta(

public static boolean isProtectedShadow(Collection<ResourceObjectPattern> protectedAccountPatterns, ShadowType shadow, @NotNull OperationResult result)
throws SchemaException {
boolean isProtected =
ShadowMarkManager.get().isProtectedShadowPolicyNotExcluded(shadow) &&
ResourceObjectPattern.matches(shadow, protectedAccountPatterns);

return getEffectiveProvisioningPolicy(protectedAccountPatterns, shadow, result).isProtected();
}

if (!isProtected) {
// We use shadow marks to determine if shadow is protected (works only for repository shadows).
isProtected = ShadowMarkManager.get().isProtectedShadow(shadow, result);
private static ShadowProvisioningPolicyType getEffectiveProvisioningPolicy(Collection<ResourceObjectPattern> protectedAccountPatterns,
ShadowType shadow, @NotNull OperationResult result) throws SchemaException {
if (shadow.getEffectiveProvisioningPolicy() != null) {
return shadow.getEffectiveProvisioningPolicy();
}
LOGGER.trace("isProtectedShadow: {} = {}", shadow, isProtected);
return isProtected;
ShadowMarkManager.get().updateEffectiveMarksAndPolicies(
protectedAccountPatterns, shadow, result);
return shadow.getEffectiveProvisioningPolicy();
}

public static void setEffectiveProvisioningPolicy (
ProvisioningContext ctx, ShadowType shadow, ExpressionFactory factory, OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException,
ExpressionEvaluationException, SecurityViolationException {
ShadowMarkManager.get().updateEffectiveMarksAndPolicies(
ctx.getProtectedAccountPatterns(factory, result), shadow, result);
}

@Deprecated
public static void setProtectedFlag(
ProvisioningContext ctx, ShadowType shadow, ExpressionFactory factory, OperationResult result)
throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException,
ExpressionEvaluationException, SecurityViolationException {
if (isProtectedShadow(ctx.getProtectedAccountPatterns(factory, result), shadow, result)) {
shadow.setProtectedObject(true);
}
setEffectiveProvisioningPolicy(ctx, shadow, factory, result);
}

public static void recordWarningNotRethrowing(Trace logger, OperationResult result, String message, Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ protected ItemComparisonResult getExpectedPasswordComparisonResultMismatch() {
@Override
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
super.initSystem(initTask, initResult);
repoAdd(ARCHETYPE_SHADOW_MARK, initResult);
repoAdd(TAG_PROTECTED_SHADOW, initResult);
if (areMarksSupported()) {
repoAdd(ARCHETYPE_SHADOW_MARK, initResult);
repoAdd(TAG_PROTECTED_SHADOW, initResult);
}
}

// test000-test100 in the superclasses
Expand Down

0 comments on commit 92ab8c8

Please sign in to comment.