Skip to content

Commit

Permalink
Moved support for Shadow Marks protected flags to provisioning instea…
Browse files Browse the repository at this point in the history
…d of model

Provisioning-impl manages loading and synchronization of shadows and original
determination of ptotectedObject flag,so it makes sense to unify it there
instead of SynchronizationService.

Original support in SynchronizationService would not prevent modification of
protected shadows in cases, where provisioningService is accessed directly.
  • Loading branch information
tonydamage committed Jan 27, 2023
1 parent a2d36ef commit 6a439ca
Show file tree
Hide file tree
Showing 12 changed files with 834 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15612,6 +15612,13 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="00000000-0000-0000-0000-000000000750">
<xsd:annotation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="TAG_PROTECTED_SHADOW"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="00000000-0000-0000-0000-000000001005">
<xsd:annotation>
<xsd:appinfo>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,24 +165,4 @@ public Map<String, TagType> resolveTagNames(Collection<String> tagOids, Operatio
return map;
}

public Collection<TagType> getShadowMarks(List<ObjectReferenceType> tagRefs, @NotNull OperationResult result) {
// FIXME: Consider caching of all shadow marks and doing post-filter only
if (!cacheRepositoryService.supportsTags()) {
return List.of();
}
String[] tagRefIds = tagRefs.stream().map(t -> t.getOid()).collect(Collectors.toList()).toArray(new String[0]);
ObjectQuery query = prismContext.queryFor(TagType.class)
//.item(TagType.F_ARCHETYPE_REF).ref(SystemObjectsType.ARCHETYPE_SHADOW_MARK.value())
// Tag is Shadow Marks
.item(TagType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(SystemObjectsType.ARCHETYPE_SHADOW_MARK.value())
.and()
// Tag is assigned to shadow
.id(tagRefIds)
.build();
try {
return asObjectables(cacheRepositoryService.searchObjects(TagType.class, query, null, result));
} catch (SchemaException e) {
throw new SystemException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,23 +193,8 @@ boolean isSynchronizationEnabled() {
&& synchronizationPolicy.isSynchronizationEnabled();
}

public boolean isProtected(OperationResult result) {
if (BooleanUtils.isTrue(shadowedResourceObject.isProtectedObject())) {
return true;
}
return determineProtectedFromTags(shadowedResourceObject.getTagRef(), result);
}

private boolean determineProtectedFromTags(List<ObjectReferenceType> tagRefs, OperationResult result) {
if (tagRefs == null || tagRefs.isEmpty()) {
return false;
}
Collection<TagType> marks = this.beans.tagManager.getShadowMarks(tagRefs, result);

// Account is protected if any of shadow marks set it to protected.
return marks.stream().anyMatch(
t -> t.getProvisioningPolicy() != null &&
BooleanUtils.isTrue(t.getProvisioningPolicy().isProtected()));
public boolean isProtected() {
return BooleanUtils.isTrue(shadowedResourceObject.isProtectedObject());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ public void notifyChange(
if (shouldSkipSynchronization(syncCtx, result)) {
return; // sync metadata are saved by the above method
}
// FIXME: Somewhere here we should validate preFocus



SynchronizationContext.Complete<?> completeCtx = (SynchronizationContext.Complete<?>) syncCtx;
setupLinkedOwnerAndSituation(completeCtx, change, result);

Expand Down Expand Up @@ -229,7 +233,7 @@ private boolean shouldSkipSynchronization(SynchronizationContext<?> syncCtx, Ope
return true;
}

if (syncCtx.isProtected(result)) {
if (syncCtx.isProtected()) {
String message = String.format(
"SYNCHRONIZATION is skipped for protected shadow %s, ignoring change from channel %s", shadow, channel);
LOGGER.debug(message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ public interface CommonInitialObjects {
TAGS, "735-tag-projection-password-changed.xml",
SystemObjectsType.TAG_PROJECTION_PASSWORD_CHANGED.value());


AbstractTestResource<TagType> TAG_PROTECTED_SHADOW = new ClassPathTestResource<>(
TAGS, "750-tag-protected-shadow.xml",
SystemObjectsType.TAG_PROTECTED_SHADOW.value());


/** To be used when needed. */
static void addTags(AbstractModelIntegrationTest test, Task task, OperationResult result)
throws CommonException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ public class CommonBeans {
@Autowired public ResourceOperationalStateManager operationalStateManager;
@Autowired public SystemObjectCache systemObjectCache;
@Autowired public ShadowCaretaker shadowCaretaker;
@Autowired public ShadowMarkManager shadowMarkManager;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.evolveum.midpoint.provisioning.impl;

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

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.commons.lang3.BooleanUtils;
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 com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
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.evolveum.midpoint.xml.ns._public.common.common_3.TagType;

@Component
public class ShadowMarkManager {

private static ShadowMarkManager instance = null;

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


@PostConstruct
public void init() {
instance = this;
}

@PreDestroy
public void destroy() {
instance = null;
}

public Collection<TagType> getShadowMarks(List<ObjectReferenceType> tagRefs, @NotNull OperationResult result) {
// FIXME: Consider caching of all shadow marks and doing post-filter only
if (!cacheRepositoryService.supportsTags() || tagRefs.isEmpty()) {
return List.of();
}
String[] tagRefIds = tagRefs.stream().map(t -> t.getOid()).collect(Collectors.toList()).toArray(new String[0]);
ObjectQuery query = prismContext.queryFor(TagType.class)
//.item(TagType.F_ARCHETYPE_REF).ref(SystemObjectsType.ARCHETYPE_SHADOW_MARK.value())
// Tag is Shadow Marks
.item(TagType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF).ref(SystemObjectsType.ARCHETYPE_SHADOW_MARK.value())
.and()
// Tag is assigned to shadow
.id(tagRefIds)
.build();
try {
return asObjectables(cacheRepositoryService.searchObjects(TagType.class, query, null, result));
} catch (SchemaException e) {
throw new SystemException(e);
}
}

public ShadowProvisioningPolicyType getShadowMarkPolicy(List<ObjectReferenceType> tagRefs, @NotNull OperationResult result) {
var ret = new ShadowProvisioningPolicyType();
Collection<TagType> marks = getShadowMarks(tagRefs, result);

// Account is protected if any of shadow marks set it to protected.
boolean isProtected = marks.stream().anyMatch(
t -> t.getProvisioningPolicy() != null &&
BooleanUtils.isTrue(t.getProvisioningPolicy().isProtected()));
ret.setProtected(isProtected);

return ret;
}

public static ShadowMarkManager get() {
return instance;
}

public boolean isProtectedShadow(ShadowType shadow, @NotNull OperationResult result) {
return getShadowMarkPolicy(shadow.getTagRef(), result).isProtected();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public AsynchronousOperationReturnValue<ShadowType> addResourceObject(

Collection<ResourceAttribute<?>> resourceAttributesAfterAdd;

if (isProtectedShadow(ctx.getProtectedAccountPatterns(expressionFactory, result), shadowClone)) {
if (isProtectedShadow(ctx.getProtectedAccountPatterns(expressionFactory, result), shadowClone, result)) {
throw new SecurityViolationException("Cannot add protected shadow " + shadowClone);
}

Expand Down Expand Up @@ -499,7 +499,7 @@ private void checkIfProtected(
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException,
ExpressionEvaluationException, SecurityViolationException {
Collection<ResourceObjectPattern> protectedPatterns = ctx.getProtectedAccountPatterns(expressionFactory, result);
if (isProtectedShadow(protectedPatterns, shadow)) {
if (isProtectedShadow(protectedPatterns, shadow, result)) {
// TODO remove this unnecessary logging statement (the error will be logged anyway)
LOGGER.error("Attempt to delete protected resource object " + ctx.getObjectClassDefinition() + ": "
+ identifiers + "; ignoring the request");
Expand Down Expand Up @@ -557,7 +557,7 @@ public AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyVa

Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getAllIdentifiers(repoShadow);

if (isProtectedShadow(ctx.getProtectedAccountPatterns(expressionFactory, result), repoShadow)) {
if (isProtectedShadow(ctx.getProtectedAccountPatterns(expressionFactory, result), repoShadow, result)) {
if (hasChangesOnResource(itemDeltas)) {
// TODO remove this unnecessary logging statement (the error will be logged anyway)
LOGGER.error("Attempt to modify protected resource object {}: {}", objectClassDefinition, identifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions;
import com.evolveum.midpoint.provisioning.impl.ProvisioningContext;
import com.evolveum.midpoint.provisioning.impl.ShadowMarkManager;
import com.evolveum.midpoint.provisioning.ucf.api.AttributesToReturn;
import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation;
import com.evolveum.midpoint.provisioning.ucf.api.ExecuteScriptArgument;
Expand Down Expand Up @@ -315,9 +316,13 @@ public static <T> PropertyDelta<T> narrowPropertyDelta(
return refinedSchema;
}

public static boolean isProtectedShadow(Collection<ResourceObjectPattern> protectedAccountPatterns, ShadowType shadow)
public static boolean isProtectedShadow(Collection<ResourceObjectPattern> protectedAccountPatterns, ShadowType shadow, @NotNull OperationResult result)
throws SchemaException {
boolean isProtected = ResourceObjectPattern.matches(shadow, protectedAccountPatterns);
if (!isProtected) {
// We use shadow marks to determine if shadow is protected (works only for repository shadows).
isProtected = ShadowMarkManager.get().isProtectedShadow(shadow, result);
}
LOGGER.trace("isProtectedShadow: {} = {}", shadow, isProtected);
return isProtected;
}
Expand All @@ -326,7 +331,7 @@ 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)) {
if (isProtectedShadow(ctx.getProtectedAccountPatterns(factory, result), shadow, result)) {
shadow.setProtectedObject(true);
}
}
Expand Down

0 comments on commit 6a439ca

Please sign in to comment.