Skip to content

Commit

Permalink
Provisioning runAs and support for that in model (MID-4661)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Sep 6, 2018
1 parent 68763fb commit 1990fe1
Show file tree
Hide file tree
Showing 42 changed files with 595 additions and 190 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 Evolveum
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -78,7 +78,7 @@ public void deleteSyncTokenPerformed(AjaxRequestTarget target) {
if (property == null) {
result.recordWarning("Token is not present in this task."); // should be treated by isVisible
} else {
final ObjectDelta<? extends ObjectType> delta = (ObjectDelta<? extends ObjectType>)
final ObjectDelta<? extends ObjectType> delta =
DeltaBuilder.deltaFor(TaskType.class, parentPage.getPrismContext())
.item(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.SYNC_TOKEN), property.getDefinition()).replace()
.asObjectDelta(parentPage.getTaskDto().getOid());
Expand Down
Expand Up @@ -1627,7 +1627,7 @@ protected void applyModifyMetadata(DummyObject object, OperationOptions options)
} else {
throw new InvalidPasswordException("No runWithPassword");
}
object.setLastModifier(runAsUser);
object.setLastModifier(runAsAccount.getName());
} else {
object.setLastModifier(null);
}
Expand Down
Expand Up @@ -116,6 +116,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttrib
if (account == null) {
throw new UnknownUidException("Account with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(account, options);

// we do this before setting attribute values, in case when description itself would be changed
resource.changeDescriptionIfNeeded(account);
Expand Down Expand Up @@ -179,6 +180,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttrib
if (group == null) {
throw new UnknownUidException("Group with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(group, options);

for (Attribute attr : replaceAttributes) {
if (attr.is(Name.NAME)) {
Expand Down Expand Up @@ -229,6 +231,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttrib
if (priv == null) {
throw new UnknownUidException("Privilege with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(priv, options);

for (Attribute attr : replaceAttributes) {
if (attr.is(Name.NAME)) {
Expand Down Expand Up @@ -271,6 +274,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttrib
if (org == null) {
throw new UnknownUidException("Org with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(org, options);

for (Attribute attr : replaceAttributes) {
if (attr.is(Name.NAME)) {
Expand Down Expand Up @@ -346,6 +350,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> v
if (account == null) {
throw new UnknownUidException("Account with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(account, options);

// we could change the description here, but don't do that not to collide with ADD operation
// TODO add the functionality if needed
Expand Down Expand Up @@ -391,6 +396,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> v
if (group == null) {
throw new UnknownUidException("Group with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(group, options);

for (Attribute attr : valuesToAdd) {

Expand Down Expand Up @@ -435,6 +441,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> v
if (priv == null) {
throw new UnknownUidException("Privilege with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(priv, options);

for (Attribute attr : valuesToAdd) {

Expand Down Expand Up @@ -471,6 +478,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> v
if (org == null) {
throw new UnknownUidException("Org with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(org, options);

for (Attribute attr : valuesToAdd) {

Expand Down Expand Up @@ -538,6 +546,7 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute
if (account == null) {
throw new UnknownUidException("Account with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(account, options);

// we could change the description here, but don't do that not to collide with REMOVE operation
// TODO add the functionality if needed
Expand Down Expand Up @@ -576,6 +585,7 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute
if (group == null) {
throw new UnknownUidException("Group with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(group, options);

for (Attribute attr : valuesToRemove) {
if (attr.is(OperationalAttributeInfos.PASSWORD.getName())) {
Expand Down Expand Up @@ -617,6 +627,7 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute
if (priv == null) {
throw new UnknownUidException("Privilege with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(priv, options);

for (Attribute attr : valuesToRemove) {
if (attr.is(OperationalAttributeInfos.PASSWORD.getName())) {
Expand Down Expand Up @@ -650,6 +661,7 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute
if (org == null) {
throw new UnknownUidException("Org with UID "+uid+" does not exist on resource");
}
applyModifyMetadata(org, options);

for (Attribute attr : valuesToRemove) {
if (attr.is(OperationalAttributeInfos.PASSWORD.getName())) {
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2015 Evolveum
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -36,7 +36,7 @@ public interface S_ItemEntry {
S_ValuesEntry item(ItemPath path, ItemDefinition itemDefinition);

List<ObjectDelta<?>> asObjectDeltas(String oid);
ObjectDelta<?> asObjectDelta(String oid);
<O extends Objectable> ObjectDelta<O> asObjectDelta(String oid);

// TEMPORARY HACK
@SuppressWarnings("unchecked")
Expand Down
Expand Up @@ -399,12 +399,24 @@ public abstract class SchemaConstants {

public static final String NS_GUI = NS_MIDPOINT_PUBLIC + "/gui";
public static final String NS_GUI_CHANNEL = NS_GUI + "/channels-3";

// Init channel, used when system is initializing itself
public static final QName CHANNEL_GUI_INIT_QNAME = new QName(NS_GUI_CHANNEL, "init");
public static final String CHANNEL_GUI_INIT_URI = QNameUtil.qNameToUri(CHANNEL_GUI_INIT_QNAME);

public static final QName CHANNEL_GUI_SELF_REGISTRATION_QNAME = new QName(NS_GUI_CHANNEL, "selfRegistration");
public static final String CHANNEL_GUI_SELF_REGISTRATION_URI = QNameUtil.qNameToUri(CHANNEL_GUI_SELF_REGISTRATION_QNAME);

// Channel for self-service part of the user interface. These are the pages when user is changing his own data.
// E.g. update of his own profile and password change are considered to be self-service.
public static final QName CHANNEL_GUI_SELF_SERVICE_QNAME = new QName(NS_GUI_CHANNEL, "selfService");
public static final String CHANNEL_GUI_SELF_SERVICE_URI = QNameUtil.qNameToUri(CHANNEL_GUI_SELF_SERVICE_QNAME);

// Password reset channel. This is *reset*, which means that the user does not know the old password and cannot log in.
public static final QName CHANNEL_GUI_RESET_PASSWORD_QNAME = new QName(NS_GUI_CHANNEL, "resetPassword");
public static final String CHANNEL_GUI_RESET_PASSWORD_URI = QNameUtil.qNameToUri(CHANNEL_GUI_RESET_PASSWORD_QNAME);

// Catch-all channel for all user operations in user interface.
public static final QName CHANNEL_GUI_USER_QNAME = new QName(NS_GUI_CHANNEL, "user");
public static final String CHANNEL_GUI_USER_URI = QNameUtil.qNameToUri(CHANNEL_GUI_USER_QNAME);

Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 Evolveum
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -903,7 +903,7 @@ public void testCampaign() throws SchemaException, SAXException, IOException, JA
triggerToDelete.setHandlerUri("http://midpoint.evolveum.com/xml/ns/public/certification/trigger/close-stage/handler-3");

@SuppressWarnings({"unchecked", "raw"})
ObjectDelta<AccessCertificationCampaignType> delta = (ObjectDelta<AccessCertificationCampaignType>)
ObjectDelta<AccessCertificationCampaignType> delta =
DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, getPrismContext())
.item(AccessCertificationCampaignType.F_TRIGGER).delete(triggerToDelete)
.asObjectDelta(campaign.getOid());
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2017 Evolveum
* Copyright (c) 2010-2018 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -115,7 +115,7 @@ public void test020ModifyIndigo() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);
@SuppressWarnings({ "unchecked", "raw" })
ObjectDelta<UserType> delta = (ObjectDelta<UserType>) DeltaBuilder.deltaFor(UserType.class, prismContext)
ObjectDelta<UserType> delta = DeltaBuilder.deltaFor(UserType.class, prismContext)
.item(UserType.F_DESCRIPTION).replace("new description")
.item(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).replace(ActivationStatusType.DISABLED)
.asObjectDelta(USER_INDIGO_OID);
Expand Down
Expand Up @@ -63,7 +63,9 @@
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.security.api.OwnerResolver;
import com.evolveum.midpoint.security.api.SecurityContextManager;
import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters;
import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer;
import com.evolveum.midpoint.task.api.Task;
Expand All @@ -77,6 +79,7 @@
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 com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import com.evolveum.prism.xml.ns._public.types_3.RawType;

import org.apache.commons.lang.BooleanUtils;
Expand Down Expand Up @@ -116,6 +119,7 @@ public class ChangeExecutor {
@Autowired private PrismContext prismContext;
@Autowired private ExpressionFactory expressionFactory;
@Autowired private SecurityEnforcer securityEnforcer;
@Autowired private SecurityContextManager securityContextManager;
@Autowired private Clock clock;
@Autowired private ModelObjectResolver objectResolver;
@Autowired private OperationalDataManager metadataManager;
Expand Down Expand Up @@ -1165,11 +1169,16 @@ private ProvisioningOperationOptions copyFromModelOptions(ModelExecuteOptions op
}

private <F extends ObjectType> ProvisioningOperationOptions getProvisioningOptions(LensContext<F> context,
ModelExecuteOptions modelOptions) {
ModelExecuteOptions modelOptions, PrismObject<ShadowType> existingShadow, ObjectDelta<ShadowType> delta) throws SecurityViolationException {
if (modelOptions == null && context != null) {
modelOptions = context.getOptions();
}
ProvisioningOperationOptions provisioningOptions = copyFromModelOptions(modelOptions);

if (executeAsSelf(context, modelOptions, existingShadow, delta)) {
LOGGER.trace("Setting 'execute as self' provisioning option for {}", existingShadow);
provisioningOptions.setRunAsAccountOid(existingShadow.getOid());
}

if (context != null && context.getChannel() != null) {

Expand All @@ -1188,6 +1197,62 @@ private <F extends ObjectType> ProvisioningOperationOptions getProvisioningOptio

return provisioningOptions;
}

// This is a bit of black magic. We only want to execute as self if there a user is changing its own password
// and we also have old password value.
// Later, this should be improved. Maybe we need special model operation option for this? Or maybe it should be somehow
// automatically detected based on resource capabilities? We do not know yet. Therefore let's do the simplest possible
// thing. Otherwise we might do something that we will later regret.
private <F extends ObjectType> boolean executeAsSelf(LensContext<F> context,
ModelExecuteOptions modelOptions, PrismObject<ShadowType> existingShadow, ObjectDelta<ShadowType> delta) throws SecurityViolationException {
if (existingShadow == null) {
return false;
}

if (!context.getChannel().equals(SchemaConstants.CHANNEL_GUI_SELF_SERVICE_URI)) {
return false;
}

if (delta == null) {
return false;
}
if (!delta.isModify()) {
return false;
}
PropertyDelta<ProtectedStringType> passwordDelta = delta.findPropertyDelta(SchemaConstants.PATH_PASSWORD_VALUE);
if (passwordDelta == null) {
return false;
}
if (passwordDelta.getEstimatedOldValues() == null || passwordDelta.getEstimatedOldValues().isEmpty()) {
return false;
}
ProtectedStringType oldPassword = passwordDelta.getEstimatedOldValues().iterator().next().getValue();
if (!oldPassword.canGetCleartext()) {
return false;
}

LensFocusContext<F> focusContext = context.getFocusContext();
if (focusContext == null) {
return false;
}
if (!focusContext.canRepresent(UserType.class)) {
return false;
}

MidPointPrincipal principal = securityContextManager.getPrincipal();
if (principal == null) {
return false;
}
UserType loggedInUser = principal.getUser();
if (loggedInUser == null) {
return false;
}

if (!loggedInUser.getOid().equals(focusContext.getOid())) {
return false;
}
return true;
}

private <T extends ObjectType, F extends ObjectType> void logDeltaExecution(ObjectDelta<T> objectDelta,
LensContext<F> context, ResourceType resource, OperationResult result, Task task) {
Expand Down Expand Up @@ -1268,7 +1333,8 @@ private <T extends ObjectType, F extends ObjectType> PrismObject<T> executeAddit
throw new UnsupportedOperationException("NodeType cannot be added using model interface");
} else if (ObjectTypes.isManagedByProvisioning(objectTypeToAdd)) {

ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options);
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options,
(PrismObject<ShadowType>)objectContext.getObjectCurrent(), (ObjectDelta<ShadowType>)change);

oid = addProvisioningObject(objectToAdd, context, objectContext, provisioningOptions,
resource, task, result);
Expand Down Expand Up @@ -1319,7 +1385,8 @@ private <T extends ObjectType, F extends ObjectType> PrismObject<T> executeDelet
} else if (NodeType.class.isAssignableFrom(objectTypeClass)) {
taskManager.deleteNode(oid, result);
} else if (ObjectTypes.isClassManagedByProvisioning(objectTypeClass)) {
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options);
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options,
(PrismObject<ShadowType>)objectContext.getObjectCurrent(), (ObjectDelta<ShadowType>)change);
try {
objectAfterModification = deleteProvisioningObject(objectTypeClass, oid, context, objectContext,
provisioningOptions, resource, task, result);
Expand Down Expand Up @@ -1387,7 +1454,8 @@ private <T extends ObjectType, F extends ObjectType> void executeModification(Ob
} else if (NodeType.class.isAssignableFrom(objectTypeClass)) {
throw new UnsupportedOperationException("NodeType is not modifiable using model interface");
} else if (ObjectTypes.isClassManagedByProvisioning(objectTypeClass)) {
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options);
ProvisioningOperationOptions provisioningOptions = getProvisioningOptions(context, options,
(PrismObject<ShadowType>)objectContext.getObjectCurrent(), (ObjectDelta<ShadowType>)delta);
String oid = modifyProvisioningObject(objectTypeClass, delta.getOid(),
delta.getModifications(), context, objectContext, provisioningOptions, resource,
task, result);
Expand Down
Expand Up @@ -473,10 +473,13 @@ public <V extends PrismValue, D extends ItemDefinition, T extends ObjectType, F
}
}
targetItemDelta.setValuesToReplace(PrismValue.cloneCollection(valuesToReplace));

applyEstematedOldValueInReplaceCase(targetItemDelta, outputTriple);

} else if (outputTriple.hasMinusSet()) {
LOGGER.trace("{} resulted in null or empty value for {} and there is a minus set, resetting it (replace with empty)", mappingDesc, targetContext);
targetItemDelta.setValueToReplace();
applyEstematedOldValueInReplaceCase(targetItemDelta, outputTriple);

} else {
LOGGER.trace("{} resulted in null or empty value for {}, skipping", mappingDesc, targetContext);
Expand Down Expand Up @@ -534,7 +537,16 @@ public <V extends PrismValue, D extends ItemDefinition, T extends ObjectType, F
return outputTripleMap;
}

private <V extends PrismValue> boolean isMeaningful(PrismValueDeltaSetTriple<V> mappingOutputTriple) {
private <V extends PrismValue, D extends ItemDefinition> void applyEstematedOldValueInReplaceCase(ItemDelta<V, D> targetItemDelta,
PrismValueDeltaSetTriple<V> outputTriple) {
Collection<V> nonPositiveValues = outputTriple.getNonPositiveValues();
if (nonPositiveValues == null || nonPositiveValues.isEmpty()) {
return;
}
targetItemDelta.setEstimatedOldValues(PrismValue.cloneCollection(nonPositiveValues));
}

private <V extends PrismValue> boolean isMeaningful(PrismValueDeltaSetTriple<V> mappingOutputTriple) {
if (mappingOutputTriple == null) {
// this means: mapping not applicable
return false;
Expand Down

0 comments on commit 1990fe1

Please sign in to comment.