diff --git a/build-system/pom.xml b/build-system/pom.xml index aaf561f3b18..057e40f7977 100644 --- a/build-system/pom.xml +++ b/build-system/pom.xml @@ -83,7 +83,7 @@ 5.22.0 1.3 2.0.6 - 1.4.3.41 + 1.4.3.43 6.5.0 10.11.1.1 1.8.0 diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskController.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskController.java index 573128e6f5a..a3460a73f73 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskController.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/PageTaskController.java @@ -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. @@ -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 delta = (ObjectDelta) + final ObjectDelta delta = DeltaBuilder.deltaFor(TaskType.class, parentPage.getPrismContext()) .item(new ItemPath(TaskType.F_EXTENSION, SchemaConstants.SYNC_TOKEN), property.getDefinition()).replace() .asObjectDelta(parentPage.getTaskDto().getOid()); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskBasicTabPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskBasicTabPanel.html index 197c13f92fd..c089569fdd4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskBasicTabPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/TaskBasicTabPanel.html @@ -23,44 +23,46 @@
- - - - - - - - - - -
+
+ + + + + + + + + + +
+
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ResourceRelatedHandlerPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ResourceRelatedHandlerPanel.html index 4d2c916c9a6..c73e721f6e1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ResourceRelatedHandlerPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ResourceRelatedHandlerPanel.html @@ -21,34 +21,36 @@
- - - - - - - - - - - - - - - - - - - - - - -
- -
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScannerHandlerPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScannerHandlerPanel.html index 7cfc4ebf242..0c820175ceb 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScannerHandlerPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScannerHandlerPanel.html @@ -21,12 +21,14 @@
- - - - - -
+
+ + + + + +
+
diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScriptExecutionHandlerPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScriptExecutionHandlerPanel.html index c2c6a7b0f35..bd93bcd2ef7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScriptExecutionHandlerPanel.html +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/server/handlers/ScriptExecutionHandlerPanel.html @@ -21,12 +21,14 @@
- - - - - -
+
+ + + + + +
+
diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/AbstractDummyConnector.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/AbstractDummyConnector.java index a5cc62910e4..47fee87865a 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/AbstractDummyConnector.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/AbstractDummyConnector.java @@ -23,6 +23,7 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.ConnectorIOException; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; +import org.identityconnectors.framework.common.exceptions.InvalidPasswordException; import org.identityconnectors.framework.common.exceptions.UnknownUidException; import org.identityconnectors.framework.common.objects.*; @@ -390,7 +391,8 @@ public Schema schema() { throw new UnsupportedOperationException(); } - SchemaBuilder builder = new SchemaBuilder(AbstractDummyConnector.class); + SchemaBuilder builder = new SchemaBuilder(this.getClass()); + log.ok("Building schema for {0}", this.getClass()); try { @@ -418,11 +420,17 @@ public Schema schema() { builder.defineOperationOption(OperationOptionInfoBuilder.buildPageSize(), SearchOp.class); builder.defineOperationOption(OperationOptionInfoBuilder.buildSortKeys(), SearchOp.class); } + + extendSchema(builder); log.info("schema::end"); return builder.build(); } + protected void extendSchema(SchemaBuilder builder) { + // for subclasses + } + private String getAccountObjectClassName() { if (configuration.getUseLegacySchema()) { return ObjectClass.ACCOUNT_NAME; @@ -1598,6 +1606,32 @@ private boolean attributesToGetHasAttribute(Collection attributesToGet, } return attributesToGet.contains(attrName); } + + protected void applyModifyMetadata(DummyObject object, OperationOptions options) throws ConnectException, FileNotFoundException, SchemaViolationException, ConflictException { + String runAsUser = options.getRunAsUser(); + if (runAsUser != null) { + if (!configuration.getSupportRunAs()) { + throw new UnsupportedOperationException("runAsUser option is not supported"); + } + DummyAccount runAsAccount = resource.getAccountByUsername(runAsUser); + if (runAsAccount == null) { + new ConfigurationException("No runAsUser "+runAsUser); + } + GuardedString runWithPassword = options.getRunWithPassword(); + if (runWithPassword != null) { + runWithPassword.access((clearChars) -> { + if (!runAsAccount.getPassword().equals(new String(clearChars))) { + throw new InvalidPasswordException("Wrong runWithPassword"); + } + }); + } else { + throw new InvalidPasswordException("No runWithPassword"); + } + object.setLastModifier(runAsAccount.getName()); + } else { + object.setLastModifier(null); + } + } public void validate(ObjectClass oc) { if (oc == null) { diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java index 0e6a3788027..c816942e080 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConfiguration.java @@ -45,6 +45,7 @@ public class DummyConfiguration extends AbstractConfiguration { private boolean supportSchema = true; private boolean supportActivation = true; private boolean supportValidity = false; + private boolean supportRunAs = true; private String uidMode = UID_MODE_NAME; private boolean enforceUniqueName = true; private String passwordReadabilityMode = PASSWORD_READABILITY_MODE_UNREADABLE; @@ -126,6 +127,16 @@ public boolean getSupportValidity() { public void setSupportValidity(boolean supportValidity) { this.supportValidity = supportValidity; } + + @ConfigurationProperty(displayMessageKey = "UI_SUPPORT_RUN_AS", + helpMessageKey = "UI_SUPPORT_RUN_AS_HELP") + public boolean getSupportRunAs() { + return supportRunAs; + } + + public void setSupportRunAs(boolean supportRunAs) { + this.supportRunAs = supportRunAs; + } @ConfigurationProperty(displayMessageKey = "UI_UID_MODE", helpMessageKey = "UI_UID_MODE_HELP") diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java index 51c1fd8eaff..df5e37a839d 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnector.java @@ -23,6 +23,7 @@ import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.ConnectorIOException; import org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException; +import org.identityconnectors.framework.common.exceptions.InvalidPasswordException; import org.identityconnectors.framework.common.exceptions.UnknownUidException; import org.identityconnectors.framework.common.objects.*; @@ -125,6 +126,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid 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); @@ -174,6 +176,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid } } + } else if (ObjectClass.GROUP.is(objectClass.getObjectClassValue())) { @@ -188,6 +191,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid if (group == null) { throw new UnknownUidException("Group with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(group, options); for (AttributeDelta delta : modifications) { if (delta.is(Name.NAME)) { @@ -219,6 +223,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid applyOrdinaryAttributeDelta(group, delta, valuesTransformer); } } + } else if (objectClass.is(OBJECTCLASS_PRIVILEGE_NAME)) { @@ -233,6 +238,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid if (priv == null) { throw new UnknownUidException("Privilege with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(priv, options); for (AttributeDelta delta : modifications) { if (delta.is(Name.NAME)) { @@ -257,6 +263,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid applyOrdinaryAttributeDelta(priv, delta, null); } } + } else if (objectClass.is(OBJECTCLASS_ORG_NAME)) { @@ -271,6 +278,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid if (org == null) { throw new UnknownUidException("Org with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(org, options); for (AttributeDelta delta : modifications) { if (delta.is(Name.NAME)) { @@ -295,7 +303,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid applyOrdinaryAttributeDelta(org, delta, null); } } - + } else { throw new ConnectorException("Unknown object class "+objectClass); @@ -321,7 +329,7 @@ public Set updateDelta(final ObjectClass objectClass, final Uid log.info("update::end {0}", instanceName); return sideEffectChanges; } - + private void applyAuxiliaryObjectClassDelta(DummyObject dummyObject, AttributeDelta delta) { List replaceValues = getReplaceValues(delta, String.class); if (replaceValues != null) { @@ -490,4 +498,15 @@ protected void addAdditionalCommonAttributes(ConnectorObjectBuilder builder, Dum } } + @Override + protected void extendSchema(SchemaBuilder builder) { + super.extendSchema(builder); + + if (configuration.getSupportRunAs()) { + log.ok("Adding runAs options to schema"); + builder.defineOperationOption(OperationOptionInfoBuilder.buildRunWithUser(), UpdateDeltaOp.class); + builder.defineOperationOption(OperationOptionInfoBuilder.buildRunWithPassword(), UpdateDeltaOp.class); + } + } + } diff --git a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnectorLegacyUpdate.java b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnectorLegacyUpdate.java index 046ccc3505c..f3d1c2eef8a 100644 --- a/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnectorLegacyUpdate.java +++ b/icf-connectors/dummy-connector/src/main/java/com/evolveum/icf/dummy/connector/DummyConnectorLegacyUpdate.java @@ -116,6 +116,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set 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); @@ -179,6 +180,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set 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)) { @@ -229,6 +231,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set 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)) { @@ -271,6 +274,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set 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)) { @@ -346,6 +350,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set 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 @@ -391,6 +396,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set v if (group == null) { throw new UnknownUidException("Group with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(group, options); for (Attribute attr : valuesToAdd) { @@ -435,6 +441,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set v if (priv == null) { throw new UnknownUidException("Privilege with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(priv, options); for (Attribute attr : valuesToAdd) { @@ -471,6 +478,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set v if (org == null) { throw new UnknownUidException("Org with UID "+uid+" does not exist on resource"); } + applyModifyMetadata(org, options); for (Attribute attr : valuesToAdd) { @@ -538,6 +546,7 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set auxiliaryObjectClassNames = new HashSet<>(); @@ -117,6 +118,14 @@ public void setValidTo(Date validTo) throws ConnectException, FileNotFoundExcept recordModify(); } + public String getLastModifier() { + return lastModifier; + } + + public void setLastModifier(String lastModifier) { + this.lastModifier = lastModifier; + } + public BreakMode getModifyBreakMode() { return modifyBreakMode; } @@ -460,11 +469,6 @@ protected String toStringContent() { return "name=" + name + ", attributes=" + attributes + ", enabled=" + enabled; } - @Override - public String debugDump() { - return debugDump(0); - } - @Override public String debugDump(int indent) { StringBuilder sb = new StringBuilder(); @@ -483,6 +487,8 @@ public String debugDump(int indent) { sb.append(" ").append(PrettyPrinter.prettyPrint(validFrom)).append(" - ").append(PrettyPrinter.prettyPrint(validTo)); } sb.append("\n"); + DebugUtil.debugDumpWithLabel(sb, "lastModifier", lastModifier, indent + 1); + sb.append("\n"); DebugUtil.debugDumpWithLabel(sb, "Attributes", attributes, indent + 1); extendDebugDump(sb, indent); return sb.toString(); diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/DeltaBuilder.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/DeltaBuilder.java index 6eadc1e9af3..1af1aab7ded 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/DeltaBuilder.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/DeltaBuilder.java @@ -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. @@ -199,6 +199,14 @@ public S_ValuesEntry oldRealValues(Collection realValues) { } return this; } + + @Override + public S_ValuesEntry oldRealValue(T realValue) { + if (realValue != null) { + currentDelta.addEstimatedOldValue(toPrismValue(currentDelta, realValue)); + } + return this; + } @Override public S_ValuesEntry old(PrismValue... values) { diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ItemEntry.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ItemEntry.java index ee323185ee1..34e869b4504 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ItemEntry.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ItemEntry.java @@ -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. @@ -36,7 +36,7 @@ public interface S_ItemEntry { S_ValuesEntry item(ItemPath path, ItemDefinition itemDefinition); List> asObjectDeltas(String oid); - ObjectDelta asObjectDelta(String oid); + ObjectDelta asObjectDelta(String oid); // TEMPORARY HACK @SuppressWarnings("unchecked") diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ValuesEntry.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ValuesEntry.java index 25237c86a6d..7973340c7aa 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ValuesEntry.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/builder/S_ValuesEntry.java @@ -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. @@ -44,6 +44,7 @@ public interface S_ValuesEntry { S_ItemEntry replace(Collection values); S_ValuesEntry old(Object... realValues); S_ValuesEntry oldRealValues(Collection realValues); + S_ValuesEntry oldRealValue(T realValue); S_ValuesEntry old(PrismValue... values); S_ValuesEntry old(Collection values); } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java index 58e5f65b884..53881c8e404 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/constants/SchemaConstants.java @@ -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); diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java index 2c1d699c67b..c2a4ccf8003 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/processor/ResourceObjectIdentification.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016 Evolveum + * Copyright (c) 2015-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import com.evolveum.midpoint.schema.util.ShadowUtil; import com.evolveum.midpoint.util.PrettyPrinter; @@ -72,6 +73,19 @@ public ResourceAttribute getSecondaryIdentifier() throws SchemaException return (ResourceAttribute) secondaryIdentifiers.iterator().next(); } + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Collection> getAllIdentifiers() { + if (primaryIdentifiers == null) { + return secondaryIdentifiers; + } + if (secondaryIdentifiers == null) { + return primaryIdentifiers; + } + List allIdentifiers = new ArrayList<>(primaryIdentifiers.size() + secondaryIdentifiers.size()); + allIdentifiers.addAll(primaryIdentifiers); + allIdentifiers.addAll(secondaryIdentifiers); + return allIdentifiers; + } public ObjectClassComplexTypeDefinition getObjectClassDefinition() { return objectClassDefinition; diff --git a/infra/schema/src/main/resources/xml/ns/public/resource/capabilities-3.xsd b/infra/schema/src/main/resources/xml/ns/public/resource/capabilities-3.xsd index 1a59a093334..ea7ab029782 100644 --- a/infra/schema/src/main/resources/xml/ns/public/resource/capabilities-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/resource/capabilities-3.xsd @@ -1,6 +1,6 @@ + + + + + + + diff --git a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java index f3b6d074786..32d3a8e905e 100644 --- a/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java +++ b/infra/schema/src/test/java/com/evolveum/midpoint/schema/TestParseDiffPatch.java @@ -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. @@ -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 delta = (ObjectDelta) + ObjectDelta delta = DeltaBuilder.deltaFor(AccessCertificationCampaignType.class, getPrismContext()) .item(AccessCertificationCampaignType.F_TRIGGER).delete(triggerToDelete) .asObjectDelta(campaign.getOid()); diff --git a/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestAdHocCertification.java b/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestAdHocCertification.java index e1c018cd1d8..a9ace748634 100644 --- a/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestAdHocCertification.java +++ b/model/certification-impl/src/test/java/com/evolveum/midpoint/certification/test/TestAdHocCertification.java @@ -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. @@ -115,7 +115,7 @@ public void test020ModifyIndigo() throws Exception { // WHEN TestUtil.displayWhen(TEST_NAME); @SuppressWarnings({ "unchecked", "raw" }) - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta 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); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java index b3de8c25cbd..28553178c8f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java @@ -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; @@ -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; @@ -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; @@ -1165,11 +1169,16 @@ private ProvisioningOperationOptions copyFromModelOptions(ModelExecuteOptions op } private ProvisioningOperationOptions getProvisioningOptions(LensContext context, - ModelExecuteOptions modelOptions) { + ModelExecuteOptions modelOptions, PrismObject existingShadow, ObjectDelta 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) { @@ -1188,6 +1197,62 @@ private 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 boolean executeAsSelf(LensContext context, + ModelExecuteOptions modelOptions, PrismObject existingShadow, ObjectDelta delta) throws SecurityViolationException { + if (existingShadow == null) { + return false; + } + + if (!SchemaConstants.CHANNEL_GUI_SELF_SERVICE_URI.equals(context.getChannel())) { + return false; + } + + if (delta == null) { + return false; + } + if (!delta.isModify()) { + return false; + } + PropertyDelta 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 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 void logDeltaExecution(ObjectDelta objectDelta, LensContext context, ResourceType resource, OperationResult result, Task task) { @@ -1268,7 +1333,8 @@ private PrismObject 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)objectContext.getObjectCurrent(), (ObjectDelta)change); oid = addProvisioningObject(objectToAdd, context, objectContext, provisioningOptions, resource, task, result); @@ -1319,7 +1385,8 @@ private PrismObject 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)objectContext.getObjectCurrent(), (ObjectDelta)change); try { objectAfterModification = deleteProvisioningObject(objectTypeClass, oid, context, objectContext, provisioningOptions, resource, task, result); @@ -1387,7 +1454,8 @@ private 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)objectContext.getObjectCurrent(), (ObjectDelta)delta); String oid = modifyProvisioningObject(objectTypeClass, delta.getOid(), delta.getModifications(), context, objectContext, provisioningOptions, resource, task, result); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java index 7b1bb9b77eb..c565aa18be7 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java @@ -473,10 +473,13 @@ public boolean isMeaningful(PrismValueDeltaSetTriple mappingOutputTriple) { + private void applyEstematedOldValueInReplaceCase(ItemDelta targetItemDelta, + PrismValueDeltaSetTriple outputTriple) { + Collection nonPositiveValues = outputTriple.getNonPositiveValues(); + if (nonPositiveValues == null || nonPositiveValues.isEmpty()) { + return; + } + targetItemDelta.setEstimatedOldValues(PrismValue.cloneCollection(nonPositiveValues)); + } + + private boolean isMeaningful(PrismValueDeltaSetTriple mappingOutputTriple) { if (mappingOutputTriple == null) { // this means: mapping not applicable return false; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java index c4ffd52b4f2..78f1515d4ce 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/credentials/ProjectionCredentialsProcessor.java @@ -190,7 +190,7 @@ private void processProjectionPasswordMapping(LensContext< boolean evaluateWeak = getEvaluateWeak(projCtx); - final ItemDeltaItem, PrismPropertyDefinition> userPasswordIdi = focusContext + final ItemDeltaItem, PrismPropertyDefinition> userPasswordIdi = focusContext .getObjectDeltaObject().findIdi(SchemaConstants.PATH_PASSWORD_VALUE); ValuePolicyResolver stringPolicyResolver = new ValuePolicyResolver() { @@ -249,6 +249,30 @@ public ValuePolicyType resolve() { } } } + + Collection> minusSet = outputTriple.getMinusSet(); + if (minusSet != null && !minusSet.isEmpty()) { + if (!canGetCleartext(minusSet)) { + // We have hashed values in minus set. That is not great, we won't be able to get + // cleartext from that if we need it (e.g. for runAs in provisioning). + // Therefore try to get old value from user password delta. If that matches with + // hashed value then we have the cleartext. + ProtectedStringType oldProjectionPassword = minusSet.iterator().next().getRealValue(); + PropertyDelta userPasswordDelta = (PropertyDelta) userPasswordIdi.getDelta(); + Collection> userPasswordDeltaOldValues = userPasswordDelta.getEstimatedOldValues(); + if (userPasswordDeltaOldValues != null && !userPasswordDeltaOldValues.isEmpty()) { + ProtectedStringType oldUserPassword = userPasswordDeltaOldValues.iterator().next().getRealValue(); + try { + if (oldUserPassword.canGetCleartext() && protector.compare(oldUserPassword, oldProjectionPassword)) { + outputTriple.clearMinusSet(); + outputTriple.addToMinusSet(new PrismPropertyValue<>(oldUserPassword)); + } + } catch (EncryptionException e) { + throw new SystemException(e.getMessage(), e); + } + } + } + } return true; }; diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java index 082a64ded58..ba6e6a8c2c8 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestAssignmentProcessor2.java @@ -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. @@ -374,7 +374,7 @@ public void test050JackDeputyOfBarbossa() throws Exception { rule.setName("barbossa-0"); policyRuleAssignment.setPolicyRule(rule); @SuppressWarnings({"unchecked", "raw" }) - ObjectDelta objectDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta objectDelta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT).add( ObjectTypeUtil.createAssignmentTo(ROLE_R1_OID, ObjectTypes.ROLE, prismContext), policyRuleAssignment) @@ -459,7 +459,7 @@ public void test060JackDeputyOfGuybrushDeputyOfBarbossa() throws Exception { rule.setName("guybrush-0"); policyRuleAssignment.setPolicyRule(rule); @SuppressWarnings({"unchecked", "raw" }) - ObjectDelta objectDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta objectDelta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT).add(deputyOfBarbossaAssignment, policyRuleAssignment) .asObjectDelta(USER_GUYBRUSH_OID); executeChangesAssertSuccess(objectDelta, null, task, result); diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules.java index 4a2893b8947..59db6d44d1f 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules.java @@ -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. @@ -630,7 +630,7 @@ public void test300DrakeChangeEmployeeType() throws Exception { // WHEN TestUtil.displayWhen(TEST_NAME); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT) .add(ObjectTypeUtil.createAssignmentTo(ROLE_JUDGE_OID, ObjectTypes.ROLE, prismContext)) .item(UserType.F_EMPLOYEE_TYPE).replace("T") diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules2.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules2.java index f68c8729b0d..cd63e124e9b 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules2.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPolicyRules2.java @@ -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. @@ -244,7 +244,7 @@ public void test120JackAttemptToMoveTo1900AndAssignRoleStudent() throws Exceptio fillContextWithUser(context, USER_JACK_OID, result); AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(roleStudentOid, ObjectTypes.ROLE, prismContext); assignment.beginActivation().validTo("2099-01-01T00:00:00"); - context.getFocusContext().addPrimaryDelta((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + context.getFocusContext().addPrimaryDelta(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(assignment) .item(UserType.F_COST_CENTER).replace("1900") .asObjectDelta(USER_JACK_OID)); @@ -297,7 +297,7 @@ public void test130JackMoveTo1900AndAssignRoleStudent() throws Exception { fillContextWithUser(context, USER_JACK_OID, result); AssignmentType assignment = ObjectTypeUtil.createAssignmentTo(roleStudentOid, ObjectTypes.ROLE, prismContext); assignment.beginActivation().validTo("2099-01-01T00:00:00"); - context.getFocusContext().addPrimaryDelta((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + context.getFocusContext().addPrimaryDelta(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(assignment) .item(UserType.F_COST_CENTER).replace("1900") .asObjectDelta(USER_JACK_OID)); @@ -353,7 +353,7 @@ public void test135JackChangeValidTo() throws Exception { LensContext context = createUserLensContext(); fillContextWithUser(context, USER_JACK_OID, result); - context.getFocusContext().addPrimaryDelta((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + context.getFocusContext().addPrimaryDelta(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT, assignmentId, AssignmentType.F_ACTIVATION, ActivationType.F_VALID_TO).replace() .asObjectDelta(USER_JACK_OID)); display("Input context", context); @@ -501,7 +501,7 @@ public void test150FrankAttemptToAssignRoleStudentButDisabled() throws Exception LensContext context = createUserLensContext(); fillContextWithUser(context, userFrankOid, result); - context.getFocusContext().addPrimaryDelta((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + context.getFocusContext().addPrimaryDelta(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add( ObjectTypeUtil.createAssignmentTo(roleStudentOid, ObjectTypes.ROLE, prismContext) .beginActivation() diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/visualizer/TestVisualizer.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/visualizer/TestVisualizer.java index d68a41a774f..6b86cbb83ad 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/visualizer/TestVisualizer.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/visualizer/TestVisualizer.java @@ -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. @@ -138,7 +138,7 @@ public void test200UserDeltaBasic() throws Exception { final String TEST_NAME = "test200UserDeltaBasic"; Task task = createTask(TEST_NAME); - ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_NAME).replace("admin") .asObjectDelta(USER_ADMINISTRATOR_OID); @@ -164,7 +164,7 @@ public void test210UserDeltaContainers() throws Exception { ass1.getActivation().setValidTo(XmlTypeConverter.createXMLGregorianCalendar(2017, 1, 1, 0, 0, 0)); ass1.setTargetRef(createObjectRef(ROLE_SUPERUSER_OID, ROLE)); - ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_NAME).replace("admin") .item(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).replace(ActivationStatusType.ENABLED) .item(UserType.F_ASSIGNMENT, 1, AssignmentType.F_TARGET_REF).replace(createObjectRef("123", ROLE).asReferenceValue()) @@ -188,7 +188,7 @@ public void test212UserDeltaContainerSimple() throws Exception { final String TEST_NAME = "test212UserDeltaContainerSimple"; Task task = createTask(TEST_NAME); - ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS).replace(ActivationStatusType.ENABLED) .item(UserType.F_ACTIVATION, ActivationType.F_ENABLE_TIMESTAMP).replace(XmlTypeConverter.createXMLGregorianCalendar(new Date())) .asObjectDelta(USER_ADMINISTRATOR_OID); @@ -218,7 +218,7 @@ public void test220UserContainerReplace() throws Exception { ActivationType act1 = new ActivationType(prismContext); act1.setAdministrativeStatus(ActivationStatusType.DISABLED); - ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_NAME).replace("admin") .item(UserType.F_ACTIVATION).replace(act1) .item(UserType.F_ASSIGNMENT).replace(ass1) @@ -246,7 +246,7 @@ public void test230UserContainerDelete() throws Exception { AssignmentType ass2 = new AssignmentType(prismContext); ass2.setId(99999L); - ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_NAME).replace("admin") .item(UserType.F_ASSIGNMENT).delete(ass1, ass2) .asObjectDelta(USER_ADMINISTRATOR_OID); @@ -274,7 +274,7 @@ public void test300UserAssignmentPreview() throws Exception { ass1.setConstruction(new ConstructionType(prismContext)); ass1.getConstruction().setResourceRef(createObjectRef(RESOURCE_DUMMY_OID, RESOURCE)); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT).add(ass1) .asObjectDelta(USER_JACK_OID); @@ -309,7 +309,7 @@ public void test305UserAssignmentAdd() throws Exception { ass1.setConstruction(new ConstructionType()); ass1.getConstruction().setResourceRef(createObjectRef(RESOURCE_DUMMY_OID, RESOURCE)); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT).add(ass1) .asObjectDelta(USER_JACK_OID); @@ -332,7 +332,7 @@ public void test307UserDisablePreview() throws Exception { final String TEST_NAME = "test307UserDisablePreview"; Task task = createTask(TEST_NAME); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).replace(ActivationStatusType.DISABLED) .asObjectDelta(USER_JACK_OID); @@ -381,7 +381,7 @@ public void test310UserLinkRefDelete() throws Exception { assertEquals("wrong # of linkrefs", 1, jack.getLinkRef().size()); dummyAccountOid = jack.getLinkRef().get(0).getOid(); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).delete(createObjectRef(dummyAccountOid, SHADOW).asReferenceValue()) .asObjectDelta(USER_JACK_OID); @@ -401,7 +401,7 @@ public void test320UserLinkRefAdd() throws Exception { final String TEST_NAME = "test320UserLinkRefAdd"; Task task = createTask(TEST_NAME); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).add(createObjectRef(dummyAccountOid, SHADOW).asReferenceValue()) .asObjectDelta(USER_JACK_OID); @@ -421,7 +421,7 @@ public void test330UserLinkRefReplaceNoOp() throws Exception { final String TEST_NAME = "test330UserLinkRefReplaceNoOp"; Task task = createTask(TEST_NAME); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).replace(createObjectRef(dummyAccountOid, SHADOW).asReferenceValue()) .asObjectDelta(USER_JACK_OID); @@ -441,7 +441,7 @@ public void test340UserLinkRefReplaceOp() throws Exception { final String TEST_NAME = "test340UserLinkRefReplaceOp"; Task task = createTask(TEST_NAME); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).replace(createObjectRef("777", SHADOW).asReferenceValue()) .asObjectDelta(USER_JACK_OID); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java index 13bb1d23e5a..d648332103b 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java @@ -297,6 +297,9 @@ public class AbstractConfiguredModelIntegrationTest extends AbstractModelIntegra protected static final File ROLE_ADMINS_FILE = new File(COMMON_DIR, "role-admins.xml"); protected static final String ROLE_ADMINS_OID = "be835a70-e3f4-11e6-82cb-9b47ecb57v15"; + protected static final File ROLE_END_USER_FILE = new File(COMMON_DIR, "role-end-user.xml"); + protected static final String ROLE_END_USER_OID = "00000000-0000-0000-0000-00000000aa0f"; + public static final File USER_JACK_FILE = new File(COMMON_DIR, "user-jack.xml"); public static final String USER_JACK_OID = "c0c010c0-d34d-b33f-f00d-111111111111"; public static final String USER_JACK_USERNAME = "jack"; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestConsistencySimple.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestConsistencySimple.java index df98cd94e6d..b12c3cf4ba9 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestConsistencySimple.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestConsistencySimple.java @@ -175,7 +175,7 @@ private void executeTest(final String TEST_NAME, FocusOperation focusOperation, if (shadowOperation != ShadowOperation.KEEP) { @SuppressWarnings("unchecked") - ObjectDelta removeLinkRefDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta removeLinkRefDelta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).replace() .asObjectDelta(USER_JACK_OID); executeChanges(removeLinkRefDelta, ModelExecuteOptions.createRaw(), task, result); @@ -188,7 +188,7 @@ private void executeTest(final String TEST_NAME, FocusOperation focusOperation, } else { if (shadowOperation == ShadowOperation.UNLINK_AND_TOMBSTONE) { @SuppressWarnings("unchecked") - ObjectDelta markAsDead = (ObjectDelta) DeltaBuilder.deltaFor(ShadowType.class, prismContext) + ObjectDelta markAsDead = deltaFor(ShadowType.class) .item(ShadowType.F_DEAD).replace(Boolean.TRUE) .asObjectDelta(shadowBefore.getOid()); executeChanges(markAsDead, ModelExecuteOptions.createRaw(), task, result); @@ -280,7 +280,7 @@ private void cleanUpBeforeTest(Task task, OperationResult result) throws Excepti for (ObjectReferenceType ref : jack.asObjectable().getLinkRef()) { deleteObject(ShadowType.class, ref.getOid()); } - ObjectDelta killLinkRefDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta killLinkRefDelta = deltaFor(UserType.class) .item(UserType.F_LINK_REF).replace().asObjectDelta(USER_JACK_OID); executeChanges(killLinkRefDelta, ModelExecuteOptions.createRaw(), task, result); } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestRaceConditions.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestRaceConditions.java index f96d8af30b1..00ea556eadf 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestRaceConditions.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestRaceConditions.java @@ -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. @@ -70,7 +70,7 @@ public void test100AssignRoles() throws Exception { // WHEN TestUtil.displayWhen(TEST_NAME); @SuppressWarnings({"unchecked", "raw"}) - ObjectDelta objectDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta objectDelta = deltaFor(UserType.class) .item(UserType.F_ASSIGNMENT).add( ObjectTypeUtil.createAssignmentTo(ROLE_PIRATE_OID, ObjectTypes.ROLE, prismContext), ObjectTypeUtil.createAssignmentTo(ROLE_SAILOR_OID, ObjectTypes.ROLE, prismContext)) @@ -131,7 +131,7 @@ private void deleteAssignment(PrismObject user, int index, Task task, try { login(userAdministrator); @SuppressWarnings({ "unchecked", "raw" }) - ObjectDelta objectDelta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta objectDelta = deltaFor(UserType.class) .item(FocusType.F_ASSIGNMENT).delete(user.asObjectable().getAssignment().get(index).clone()) .asObjectDelta(USER_JACK_OID); modelService.executeChanges(Collections.singletonList(objectDelta), null, task, diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java index 9241b041645..36c453b25a3 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java @@ -27,6 +27,7 @@ import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.test.DummyResourceContoller; import com.evolveum.midpoint.test.IntegrationTestTools; +import com.evolveum.midpoint.test.asserter.UserAsserter; import com.evolveum.midpoint.test.util.MidPointTestConstants; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -80,6 +81,8 @@ public abstract class AbstractPasswordTest extends AbstractInitializedModelInteg protected static final String USER_PASSWORD_VALID_2 = "abcd223"; protected static final String USER_PASSWORD_VALID_3 = "abcd323"; protected static final String USER_PASSWORD_VALID_4 = "abcd423"; + protected static final String USER_PASSWORD_VALID_5 = "abcd523"; + protected static final String USER_PASSWORD_VALID_6 = "abcd623"; // Very long and very simple password. This is meant to violate the policies. protected static final String USER_PASSWORD_LLL_CLEAR = "lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll"; @@ -152,6 +155,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(USER_THREE_HEADED_MONKEY_FILE, UserType.class, true, initResult); + repoAddObjectFromFile(ROLE_END_USER_FILE, initResult); + importObjectFromFile(PASSWORD_POLICY_MAVERICK_FILE); initDummyResourcePirate(RESOURCE_DUMMY_MAVERICK_NAME, RESOURCE_DUMMY_MAVERICK_FILE, RESOURCE_DUMMY_MAVERICK_OID, initTask, initResult); @@ -1280,8 +1285,7 @@ public void test222ModifyUserJackPasswordBadContainer() throws Exception { } // THEN - result.computeStatus(); - TestUtil.assertFailure(result); + assertFailure(result); assertJackPasswordsWithHistory(USER_PASSWORD_VALID_1, USER_PASSWORD_AA_CLEAR); assertNoUserPasswordNotifications(); @@ -1391,8 +1395,7 @@ private void doTestModifyUserJackPasswordSuccessWithHistory(final String TEST_NA modifyUserChangePassword(USER_JACK_OID, newPassword, task, result); // THEN - result.computeStatus(); - TestUtil.assertSuccess(result); + assertSuccess(result); lastPasswordChangeEnd = clock.currentTimeXMLGregorianCalendar(); @@ -1426,8 +1429,7 @@ private void doTestModifyUserJackPasswordFailureWithHistory(final String TEST_NA } // THEN - result.computeStatus(); - TestUtil.assertFailure(result); + assertFailure(result); assertJackPasswordsWithHistory(oldPassword, expectedPasswordHistory); @@ -1478,7 +1480,7 @@ public void test300TwoParentOrgRefs() throws Exception { display("jack", jack); assertEquals("Wrong # of parentOrgRefs", 2, jack.getParentOrgRef().size()); - ObjectDelta orgDelta = (ObjectDelta) DeltaBuilder.deltaFor(OrgType.class, prismContext) + ObjectDelta orgDelta = DeltaBuilder.deltaFor(OrgType.class, prismContext) .item(OrgType.F_PASSWORD_POLICY_REF).replace(new PrismReferenceValue(PASSWORD_POLICY_GLOBAL_OID)) .asObjectDelta(ORG_GOVERNOR_OFFICE_OID); executeChanges(orgDelta, null, task, result); @@ -1498,8 +1500,7 @@ public void test300TwoParentOrgRefs() throws Exception { } // THEN - result.computeStatus(); - TestUtil.assertFailure(result); + assertFailure(result); PrismObject userJack = getUser(USER_JACK_OID); display("User after change execution", userJack); @@ -1779,8 +1780,8 @@ public void test316UserRecompute() throws Exception { } /** - * Change password again so we have predictable password instead of - * randomly-generated one. + * Change password to set predictable password instead of randomly-generated one. + * That will be nicer for future tests. */ @Test public void test318ChangeUserPassword() throws Exception { @@ -1791,27 +1792,33 @@ public void test318ChangeUserPassword() throws Exception { Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); prepareTest(); - + // WHEN - TestUtil.displayWhen(TEST_NAME); + displayWhen(TEST_NAME); modifyUserChangePassword(USER_JACK_OID, USER_PASSWORD_VALID_3, task, result); // THEN - TestUtil.displayThen(TEST_NAME); - result.computeStatus(); - TestUtil.assertSuccess(result); - - PrismObject userAfter = getUser(USER_JACK_OID); - display("User after change execution", userAfter); - assertUserPassword(userAfter, USER_PASSWORD_VALID_3); - assertLinks(userAfter, 4); - - // password mapping is normal - assertDummyPassword(ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_VALID_3); + displayThen(TEST_NAME); + assertSuccess(result); - // password mapping is strong - assertDummyAccount(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_JACK_FULL_NAME, true); - assertDummyPassword(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_VALID_3); + PrismObject userAfter = assertUserAfter(USER_JACK_OID) + .assertPassword(USER_PASSWORD_VALID_3, getPasswordStorageType()) + .assertLinks(4) + .getObject(); + + // default password mapping is normal + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_3) + // Admin password reset, no runAs + .assertLastModifier(null); + + // RED password mapping is strong + assertDummyAccountByUsername(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME) + .assertFullName(USER_JACK_FULL_NAME) + .assertEnabled() + .assertPassword(USER_PASSWORD_VALID_3) + // and RED resource has no runAs capability + .assertLastModifier(null); // password mapping is weak assertDummyAccount(RESOURCE_DUMMY_BLUE_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_JACK_FULL_NAME, true); @@ -3486,7 +3493,237 @@ private void testJackManyPasswordChangesAttempt(String TEST_NAME, String passwor assertDummyPassword(ACCOUNT_JACK_DUMMY_USERNAME, newPassword); assertDummyPassword(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME, newPassword); } + + /** + * Jack changing his own password. He does it as an admin + * and there is no old password specified. RunAs should NOT be used. + * + * This also sets predictable password for next test. + * + * MID-4661 + */ + @Test + public void test560ChangeJackPasswordSuperuser() throws Exception { + final String TEST_NAME = "test560ChangeJackPasswordSuperuser"; + displayTestTitle(TEST_NAME); + + // GIVEN + prepareTest(); + + assignRole(USER_JACK_OID, ROLE_SUPERUSER_OID); + + // preconditions + assertUserBefore(USER_JACK_OID) + .displayWithProjections() + .assertLinks(4); + + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertLastModifier(null); + + login(USER_JACK_USERNAME); + + Task task = createTask(TEST_NAME, getSecurityContextPrincipal()); + task.setChannel(SchemaConstants.CHANNEL_GUI_USER_URI); + OperationResult result = task.getResult(); + + // WHEN + displayWhen(TEST_NAME); + modifyUserChangePassword(USER_JACK_OID, USER_PASSWORD_VALID_4, task, result); + + // THEN + displayThen(TEST_NAME); + login(USER_ADMINISTRATOR_USERNAME); + assertSuccess(result); + + assertUserAfter(USER_JACK_OID) + .assertPassword(USER_PASSWORD_VALID_4, getPasswordStorageType()) + .assertLinks(4) + .getObject(); + + // default password mapping is normal + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_4) + .assertLastModifier(null); + + // RED password mapping is strong + assertDummyAccountByUsername(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME) + .assertFullName(USER_JACK_FULL_NAME) + .assertEnabled() + .assertPassword(USER_PASSWORD_VALID_4) + // and RED resource has no runAs capability + .assertLastModifier(null); + + // BLUE password mapping is weak, we do not really care about password change here + // we do not really care about ugly resource either + + displayAccountPasswordNotifications(); + assertAccountPasswordNotifications(2); + assertHasAccountPasswordNotification(null, USER_JACK_USERNAME, USER_PASSWORD_VALID_4); + assertHasAccountPasswordNotification(RESOURCE_DUMMY_RED_NAME, USER_JACK_USERNAME, USER_PASSWORD_VALID_4); + // not BLUE, it already has a password + assertSingleUserPasswordNotification(USER_JACK_USERNAME, USER_PASSWORD_VALID_4); + } + + private ObjectDelta createOldNewPasswordDelta(String oid, String oldPassword, String newPassword) throws SchemaException { + ProtectedStringType oldPasswordPs = new ProtectedStringType(); + oldPasswordPs.setClearValue(oldPassword); + + ProtectedStringType newPasswordPs = new ProtectedStringType(); + newPasswordPs.setClearValue(newPassword); + + return deltaFor(UserType.class) + .item(PASSWORD_VALUE_PATH) + .oldRealValue(oldPasswordPs) + .replace(newPasswordPs) + .asObjectDelta(USER_JACK_OID); + } + + /** + * Self-service password change. User's own identity should be used + * to change password on resource that have runAs capability. + * + * MID-4661 + */ + @Test + public void test562ChangeJackPasswordSelfService() throws Exception { + final String TEST_NAME = "test562ChangeJackPasswordSelfService"; + displayTestTitle(TEST_NAME); + + // GIVEN + prepareTest(); + + // preconditions + assertUserBefore(USER_JACK_OID) + .displayWithProjections() + .assertLinks(4); + + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_4) + .assertLastModifier(null); + + login(USER_JACK_USERNAME); + + Task task = createTask(TEST_NAME, getSecurityContextPrincipal()); + task.setChannel(SchemaConstants.CHANNEL_GUI_SELF_SERVICE_URI); + OperationResult result = task.getResult(); + + ObjectDelta objectDelta = createOldNewPasswordDelta(USER_JACK_OID, + USER_PASSWORD_VALID_4, USER_PASSWORD_VALID_5); + + // WHEN + displayWhen(TEST_NAME); + executeChanges(objectDelta, null, task, result); + + // THEN + displayThen(TEST_NAME); + login(USER_ADMINISTRATOR_USERNAME); + assertSuccess(result); + + assertUserAfter(USER_JACK_OID) + .assertPassword(USER_PASSWORD_VALID_5, getPasswordStorageType()) + .assertLinks(4) + .getObject(); + + // default password mapping is normal + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_5) + .assertLastModifier(ACCOUNT_JACK_DUMMY_USERNAME); + + // RED password mapping is strong + assertDummyAccountByUsername(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME) + .assertFullName(USER_JACK_FULL_NAME) + .assertEnabled() + .assertPassword(USER_PASSWORD_VALID_5) + // and RED resource has no runAs capability + .assertLastModifier(null); + + // BLUE password mapping is weak, we do not really care about password change here + // we do not really care about ugly resource either + + displayAccountPasswordNotifications(); + assertAccountPasswordNotifications(2); + assertHasAccountPasswordNotification(null, USER_JACK_USERNAME, USER_PASSWORD_VALID_5); + assertHasAccountPasswordNotification(RESOURCE_DUMMY_RED_NAME, USER_JACK_USERNAME, USER_PASSWORD_VALID_5); + // not BLUE, it already has a password + assertSingleUserPasswordNotification(USER_JACK_USERNAME, USER_PASSWORD_VALID_5); + } + + /** + * Admin is changing Jack's password. + * Old password is (strangely) specified. + * But as this is not user changing its own password then RunAs + * should NOT be used. + * + * MID-4661 + */ + @Test + public void test564ChangeJackPasswordAdmin() throws Exception { + final String TEST_NAME = "test564ChangeJackPasswordAdmin"; + displayTestTitle(TEST_NAME); + + // GIVEN + + prepareTest(); + + // preconditions + assertUserBefore(USER_JACK_OID) + .displayWithProjections() + .assertLinks(4); + + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_5) + .assertLastModifier(ACCOUNT_JACK_DUMMY_USERNAME); + + login(USER_ADMINISTRATOR_USERNAME); + + Task task = createTask(TEST_NAME, getSecurityContextPrincipal()); + task.setChannel(SchemaConstants.CHANNEL_GUI_USER_URI); + OperationResult result = task.getResult(); + + ObjectDelta objectDelta = createOldNewPasswordDelta(USER_JACK_OID, + USER_PASSWORD_VALID_5, USER_PASSWORD_VALID_6); + + // WHEN + displayWhen(TEST_NAME); + executeChanges(objectDelta, null, task, result); + + // THEN + displayThen(TEST_NAME); + login(USER_ADMINISTRATOR_USERNAME); + assertSuccess(result); + + assertUserAfter(USER_JACK_OID) + .assertPassword(USER_PASSWORD_VALID_6, getPasswordStorageType()) + .assertLinks(4) + .getObject(); + + // default password mapping is normal + assertDummyAccountByUsername(null, ACCOUNT_JACK_DUMMY_USERNAME) + .assertPassword(USER_PASSWORD_VALID_6) + .assertLastModifier(null); + + // RED password mapping is strong + assertDummyAccountByUsername(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME) + .assertFullName(USER_JACK_FULL_NAME) + .assertEnabled() + .assertPassword(USER_PASSWORD_VALID_6) + // and RED resource has no runAs capability + .assertLastModifier(null); + + // BLUE password mapping is weak, we do not really care about password change here + // we do not really care about ugly resource either + + displayAccountPasswordNotifications(); + assertAccountPasswordNotifications(2); + assertHasAccountPasswordNotification(null, USER_JACK_USERNAME, USER_PASSWORD_VALID_6); + assertHasAccountPasswordNotification(RESOURCE_DUMMY_RED_NAME, USER_JACK_USERNAME, USER_PASSWORD_VALID_6); + // not BLUE, it already has a password + assertSingleUserPasswordNotification(USER_JACK_USERNAME, USER_PASSWORD_VALID_6); + } + + + protected ObjectDelta createAccountInitializationDelta(String accountOid, String newAccountPassword) { ObjectDelta shadowDelta = ObjectDelta.createEmptyModifyDelta(ShadowType.class, accountOid, prismContext); ProtectedStringType passwordPs = new ProtectedStringType(); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java index b3edd4562b4..4821aced12f 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/security/AbstractSecurityTest.java @@ -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. @@ -254,9 +254,6 @@ public abstract class AbstractSecurityTest extends AbstractInitializedModelInteg protected static final File ROLE_FILTER_OBJECT_USER_TYPE_SHADOWS_FILE = new File(TEST_DIR, "role-filter-object-user-type-shadow.xml"); protected static final String ROLE_FILTER_OBJECT_USER_TYPE_SHADOWS_OID = "00000000-0000-0000-0000-00000000aa0h"; - protected static final File ROLE_END_USER_FILE = new File(TEST_DIR, "role-end-user.xml"); - protected static final String ROLE_END_USER_OID = "00000000-0000-0000-0000-00000000aa0f"; - protected static final File ROLE_MODIFY_USER_FILE = new File(TEST_DIR, "role-modify-user.xml"); protected static final String ROLE_MODIFY_USER_OID = "00000000-0000-0000-0000-00000000aa0g"; diff --git a/model/model-intest/src/test/resources/common/resource-dummy-red.xml b/model/model-intest/src/test/resources/common/resource-dummy-red.xml index e16bdd305a2..43a4893a2fd 100644 --- a/model/model-intest/src/test/resources/common/resource-dummy-red.xml +++ b/model/model-intest/src/test/resources/common/resource-dummy-red.xml @@ -1,6 +1,6 @@ + has mostly STRONG mappings, therefore it is used in strong mapping tests. Also: + * has pre-create and delayed delete configuration. + * does NOT support runAs + --> red + false useless diff --git a/model/model-intest/src/test/resources/security/role-end-user.xml b/model/model-intest/src/test/resources/common/role-end-user.xml similarity index 100% rename from model/model-intest/src/test/resources/security/role-end-user.xml rename to model/model-intest/src/test/resources/common/role-end-user.xml diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 34fd61718ea..648dc2323dc 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -68,7 +68,7 @@ - + @@ -97,8 +97,8 @@ - - + + diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 48fe34455f6..4c8a19b22a4 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -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. @@ -717,7 +717,7 @@ protected void modifyUserSetPassword(String userOid, String newPassword, Task ta userPasswordPs.setClearValue(newPassword); PasswordType passwordType = new PasswordType(); passwordType.setValue(userPasswordPs); - ObjectDelta delta = (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) .item(SchemaConstants.PATH_PASSWORD).add(passwordType) .asObjectDelta(userOid); executeChanges(delta, null, task, result); @@ -5368,6 +5368,7 @@ protected PendingOperationType findPendingOperation(PrismObject shad protected void initializeAsserter(AbstractAsserter asserter) { asserter.setPrismContext(prismContext); asserter.setObjectResolver(repoSimpleObjectResolver); + asserter.setProtector(protector); } protected UserAsserter assertUserAfter(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java index d1253941b4e..c00d19db439 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/entitlements/AddAssociationAspect.java @@ -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. @@ -270,7 +270,7 @@ private ObjectTreeDeltas associationAdditionToDelta(ModelContext modelContext ResourceShadowDiscriminator shadowDiscriminator = ResourceShadowDiscriminator.fromResourceShadowDiscriminatorType(addition.getResourceShadowDiscriminator()); String projectionOid = modelContext.findProjectionContext(shadowDiscriminator).getOid(); - ObjectDelta objectDelta = (ObjectDelta) DeltaBuilder.deltaFor(ShadowType.class, prismContext) + ObjectDelta objectDelta = DeltaBuilder.deltaFor(ShadowType.class, prismContext) .item(ShadowType.F_ASSOCIATION).add(addition.getAssociation().clone()) .asObjectDelta(projectionOid); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java index e129f9648e4..cdb5d69ad23 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processors/primary/policy/AssignmentPolicyAspectPart.java @@ -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. @@ -348,7 +348,7 @@ private ObjectDelta assignmentToDelta(Class) op.asObjectDelta(objectOid); + return op.asObjectDelta(objectOid); } } diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/AbstractTestAssignmentApproval.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/AbstractTestAssignmentApproval.java index eaff8805155..48ebb5ee65a 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/AbstractTestAssignmentApproval.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/AbstractTestAssignmentApproval.java @@ -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. @@ -100,7 +100,7 @@ public void test020DeleteRole1Assignment() throws Exception { LensContext context = createUserLensContext(); fillContextWithUser(context, userJackOid, result); addFocusDeltaToContext(context, - (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).delete(createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid)); clockwork.run(context, task, result); @@ -288,7 +288,7 @@ private void executeAssignRole1ToJack(String TEST_NAME, boolean immediate, boole PrismObject jack = getUser(userJackOid); AssignmentType assignment = createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext); assignment.getTargetRef().setRelation(relation); - ObjectDelta addRole1Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole1Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(assignment) .asObjectDelta(userJackOid); @@ -387,27 +387,27 @@ protected void assertActiveWorkItems(String approverOid, int expectedCount) thro private void executeAssignRoles123ToJack(String TEST_NAME, boolean immediate, boolean approve1, boolean approve2, boolean approve3) throws Exception { PrismObject jack = getUser(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole1Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole1Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(1), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole2Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole2Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(2), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole3Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole3Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(3), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole4Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole4Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(getRoleOid(4), ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta changeDescriptionDelta = (ObjectDelta) DeltaBuilder + ObjectDelta changeDescriptionDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace(TEST_NAME) .asObjectDelta(userJackOid); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/metarole/TestAssignmentsWithDifferentMetaroles.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/metarole/TestAssignmentsWithDifferentMetaroles.java index 5c4eb308ad2..1c922e6b4ac 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/metarole/TestAssignmentsWithDifferentMetaroles.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/metarole/TestAssignmentsWithDifferentMetaroles.java @@ -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. @@ -1044,22 +1044,22 @@ private void executeAssignRoles123ToJack(String TEST_NAME, boolean immediate, Task task = createTask("executeAssignRoles123ToJack"); PrismObject jack = getUser(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole1Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole1Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(roleRole21Oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole2Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole2Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(roleRole22Oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRole3Delta = (ObjectDelta) DeltaBuilder + ObjectDelta addRole3Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(roleRole23Oid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta changeDescriptionDelta = (ObjectDelta) DeltaBuilder + ObjectDelta changeDescriptionDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace(TEST_NAME) .asObjectDelta(userJackOid); @@ -1194,7 +1194,7 @@ private void previewAssignRolesToJack(String TEST_NAME, boolean immediate, boole assignmentsToAdd.add(createAssignmentTo(roleRole24Oid, ObjectTypes.ROLE, prismContext)); } @SuppressWarnings("unchecked") - ObjectDelta primaryDelta = (ObjectDelta) DeltaBuilder + ObjectDelta primaryDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).addRealValues(assignmentsToAdd) .item(UserType.F_DESCRIPTION).replace(TEST_NAME) @@ -1307,22 +1307,22 @@ private void executeUnassignRoles123ToJack(String TEST_NAME, boolean immediate, AssignmentType del2 = toDelete(a2, byId); AssignmentType del3 = toDelete(a3, byId); @SuppressWarnings("unchecked") - ObjectDelta deleteRole1Delta = has1and2 ? (ObjectDelta) DeltaBuilder + ObjectDelta deleteRole1Delta = has1and2 ? DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).delete(del1) .asObjectDelta(userJackOid) : null; @SuppressWarnings("unchecked") - ObjectDelta deleteRole2Delta = has1and2 ? (ObjectDelta) DeltaBuilder + ObjectDelta deleteRole2Delta = has1and2 ? DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).delete(del2) .asObjectDelta(userJackOid) : null; @SuppressWarnings("unchecked") - ObjectDelta deleteRole3Delta = (ObjectDelta) DeltaBuilder + ObjectDelta deleteRole3Delta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).delete(del3) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta changeDescriptionDelta = (ObjectDelta) DeltaBuilder + ObjectDelta changeDescriptionDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace(TEST_NAME) .asObjectDelta(userJackOid); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/plain/TestAssignmentApprovalPlainExplicit.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/plain/TestAssignmentApprovalPlainExplicit.java index 2538494e6e5..682a6847afd 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/plain/TestAssignmentApprovalPlainExplicit.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/assignments/plain/TestAssignmentApprovalPlainExplicit.java @@ -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. @@ -59,7 +59,7 @@ protected String getRoleName(int number) { @Override protected void importLead10(Task task, OperationResult result) throws Exception { super.importLead10(task, result); - executeChangesAssertSuccess((ObjectDelta) DeltaBuilder.deltaFor(RoleType.class, prismContext) + executeChangesAssertSuccess(DeltaBuilder.deltaFor(RoleType.class, prismContext) .item(RoleType.F_APPROVER_REF) .add(prv(userLead10Oid)) .asObjectDelta(getRoleOid(10)), null, task, result); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/lifecycle/AbstractTestLifecycle.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/lifecycle/AbstractTestLifecycle.java index 8fd1c7c2f10..aea7b322898 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/lifecycle/AbstractTestLifecycle.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/lifecycle/AbstractTestLifecycle.java @@ -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. @@ -103,7 +103,7 @@ public void test010CreateRolePirate() throws Exception { PrismReferenceValue pirateOwner = new PrismReferenceValue(rolePirateOid, RoleType.COMPLEX_TYPE); pirateOwner.setRelation(SchemaConstants.ORG_OWNER); - executeChanges((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + executeChanges(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(ObjectTypeUtil.createAssignmentTo(pirateOwner, prismContext)) .asObjectDelta(userPirateOwnerOid), null, task, result); @@ -128,7 +128,7 @@ public void test100ModifyRolePirateDescription() throws Exception { TestUtil.displayTestTitle(this, TEST_NAME); login(userAdministrator); - ObjectDelta descriptionDelta = (ObjectDelta) DeltaBuilder.deltaFor(RoleType.class, prismContext) + ObjectDelta descriptionDelta = DeltaBuilder.deltaFor(RoleType.class, prismContext) .item(RoleType.F_DESCRIPTION).replace("Bloody pirate") .asObjectDelta(rolePirateOid); ObjectDelta delta0 = ObjectDelta.createModifyDelta(rolePirateOid, Collections.emptyList(), RoleType.class, prismContext); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/object/TestObjectConstraints.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/object/TestObjectConstraints.java index 8ca75daa07f..306c5ac0186 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/object/TestObjectConstraints.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/object/TestObjectConstraints.java @@ -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. @@ -177,7 +177,7 @@ protected Boolean decideOnApproval(String executionId, org.activiti.engine.task. roleEmployeeOid = searchObjectByName(RoleType.class, "employee").getOid(); PrismReferenceValue employeeOwner = new PrismReferenceValue(roleEmployeeOid, RoleType.COMPLEX_TYPE).relation(SchemaConstants.ORG_OWNER); - executeChanges((ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + executeChanges(DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(ObjectTypeUtil.createAssignmentTo(employeeOwner, prismContext)) .asObjectDelta(userEmployeeOwnerOid), null, task, result); @@ -195,7 +195,7 @@ public void test020ActivateIncompleteRole() throws Exception { OperationResult result = task.getResult(); @SuppressWarnings({"unchecked", "raw"}) - ObjectDelta activateRoleDelta = (ObjectDelta) DeltaBuilder.deltaFor(RoleType.class, prismContext) + ObjectDelta activateRoleDelta = DeltaBuilder.deltaFor(RoleType.class, prismContext) .item(RoleType.F_LIFECYCLE_STATE).replace(SchemaConstants.LIFECYCLE_ACTIVE) .asObjectDelta(roleEmployeeOid); @@ -230,7 +230,7 @@ public void test030ActivateIncompleteRoleAgain() throws Exception { OperationResult result = task.getResult(); @SuppressWarnings({"unchecked", "raw"}) - ObjectDelta activateRoleDelta = (ObjectDelta) DeltaBuilder.deltaFor(RoleType.class, prismContext) + ObjectDelta activateRoleDelta = DeltaBuilder.deltaFor(RoleType.class, prismContext) .item(RoleType.F_LIFECYCLE_STATE).replace(SchemaConstants.LIFECYCLE_ACTIVE) .item(RoleType.F_DESCRIPTION).replace("hi") .asObjectDelta(roleEmployeeOid); @@ -278,7 +278,7 @@ public void test045ActivateCompleteRole() throws Exception { OperationResult result = task.getResult(); @SuppressWarnings({"unchecked", "raw"}) - ObjectDelta activateRoleDelta = (ObjectDelta) DeltaBuilder.deltaFor(RoleType.class, prismContext) + ObjectDelta activateRoleDelta = DeltaBuilder.deltaFor(RoleType.class, prismContext) .item(RoleType.F_LIFECYCLE_STATE).replace(SchemaConstants.LIFECYCLE_ACTIVE) .item(RoleType.F_DESCRIPTION).replace("hi") .asObjectDelta(roleEmployeeOid); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestMiscellaneous.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestMiscellaneous.java index eb154717ec3..8aeb0ca66d0 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestMiscellaneous.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestMiscellaneous.java @@ -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. @@ -315,7 +315,7 @@ public void test250SkippingApprovals() throws Exception { // WHEN @SuppressWarnings({"raw", "unchecked"}) ObjectDelta delta = - (ObjectDelta) DeltaBuilder.deltaFor(UserType.class, prismContext) + DeltaBuilder.deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT) .add(ObjectTypeUtil.createAssignmentTo(roleRole1aOid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/sod/AbstractTestSoD.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/sod/AbstractTestSoD.java index d1bcb70b502..cd36bf8641d 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/sod/AbstractTestSoD.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/sod/AbstractTestSoD.java @@ -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. @@ -119,12 +119,12 @@ public void test020AssignRolePirate() throws Exception { String originalDescription = jack.asObjectable().getDescription(); @SuppressWarnings("unchecked") - ObjectDelta addPirateDelta = (ObjectDelta) DeltaBuilder + ObjectDelta addPirateDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(rolePirateOid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta changeDescriptionDelta = (ObjectDelta) DeltaBuilder + ObjectDelta changeDescriptionDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_DESCRIPTION).replace("Pirate Judge") .asObjectDelta(userJackOid); @@ -240,7 +240,7 @@ public void test030AssignRoleRespectable() throws Exception { // WHEN+THEN PrismObject jack = getUser(userJackOid); @SuppressWarnings("unchecked") - ObjectDelta addRespectableDelta = (ObjectDelta) DeltaBuilder + ObjectDelta addRespectableDelta = DeltaBuilder .deltaFor(UserType.class, prismContext) .item(UserType.F_ASSIGNMENT).add(createAssignmentTo(roleRespectableOid, ObjectTypes.ROLE, prismContext)) .asObjectDelta(userJackOid); diff --git a/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningOperationOptions.java b/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningOperationOptions.java index 48da97ad134..ee9f54eecd0 100644 --- a/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningOperationOptions.java +++ b/provisioning/provisioning-api/src/main/java/com/evolveum/midpoint/provisioning/api/ProvisioningOperationOptions.java @@ -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. @@ -37,6 +37,24 @@ public class ProvisioningOperationOptions implements Serializable { private Boolean overwrite; + /** + * Run the operations on resource using the specified identity. + * The value is a OID of an account shadow. + * + * This option should be considered a suggestion, not a command. + * Therefore if the connector can run operation as specified user + * the it should run it. But if it is not capable to run such + * operation as specified user but it can run it as administrator + * then it should run the operation as administrator. + * That case may happen e.g. if account cleartext password is not + * known at that time. + * + * Note: maybe later we need some kind of flag that makes this + * option "critical", i.e. that an error is thrown if the + * operation cannot be executed as specified identity. + */ + private String runAsAccountOid; + public Boolean getCompletePostponed() { return completePostponed; } @@ -182,6 +200,20 @@ public static ProvisioningOperationOptions createRaw() { opts.setRaw(true); return opts; } + + public String getRunAsAccountOid() { + return runAsAccountOid; + } + + public void setRunAsAccountOid(String runAsAccountOid) { + this.runAsAccountOid = runAsAccountOid; + } + + public static ProvisioningOperationOptions createRunAsAccountOid(String runAsAccountOid) { + ProvisioningOperationOptions opts = new ProvisioningOperationOptions(); + opts.setRunAsAccountOid(runAsAccountOid); + return opts; + } @Override public String toString() { @@ -192,6 +224,9 @@ public String toString() { appendFlag(sb, "postpone", postpone); appendFlag(sb, "doNotDiscovery", doNotDiscovery); appendFlag(sb, "overwrite", overwrite); + if (runAsAccountOid != null) { + sb.append("runAsAccountOid=").append(runAsAccountOid).append(","); + } if (sb.charAt(sb.length() - 1) == ',') { sb.deleteCharAt(sb.length() - 1); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java index e5506c70308..9993432a2d5 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ProvisioningContext.java @@ -24,6 +24,7 @@ import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.task.api.StateReporter; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; @@ -313,7 +314,11 @@ private ConnectorInstance getConnectorInstance(Class< throw e; } } - + + public T getResourceEffectiveCapability(Class capabilityClass) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + return ResourceTypeUtil.getEffectiveCapability(getResource(), capabilityClass); + } + public CapabilitiesType getConnectorCapabilities(Class operationCapabilityClass) { return resourceManager.getConnectorCapabilities(resource.asPrismObject(), operationCapabilityClass); } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java index 5d6957fc1a8..059b30baa3d 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceObjectConverter.java @@ -31,6 +31,7 @@ import com.evolveum.midpoint.prism.util.JavaTypeConverter; import com.evolveum.midpoint.prism.util.PrismUtil; import com.evolveum.midpoint.provisioning.api.GenericConnectorException; +import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions; import com.evolveum.midpoint.provisioning.ucf.api.*; import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.repo.cache.RepositoryCache; @@ -234,9 +235,10 @@ private boolean hasAllIdentifiers(Collection> att public AsynchronousOperationReturnValue> addResourceObject(ProvisioningContext ctx, - PrismObject shadow, OperationProvisioningScriptsType scripts, boolean skipExplicitUniquenessCheck, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, CommunicationException, - ObjectAlreadyExistsException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + PrismObject shadow, OperationProvisioningScriptsType scripts, ConnectorOperationOptions connOptions, + boolean skipExplicitUniquenessCheck, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, CommunicationException, + ObjectAlreadyExistsException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { OperationResult result = parentResult.createSubresult(OPERATION_ADD_RESOURCE_OBJECT); @@ -308,7 +310,7 @@ public AsynchronousOperationReturnValue> addResourceObje } // Execute entitlement modification on other objects (if needed) - executeEntitlementChangesAdd(ctx, shadowClone, scripts, result); + executeEntitlementChangesAdd(ctx, shadowClone, scripts, connOptions, result); LOGGER.trace("Added resource object {}", shadow); @@ -375,7 +377,7 @@ private void checkForAddConflicts(ProvisioningContext ctx, PrismObject shadow, - OperationProvisioningScriptsType scripts, OperationResult parentResult) + OperationProvisioningScriptsType scripts, ConnectorOperationOptions connOptions, OperationResult parentResult) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { @@ -404,7 +406,7 @@ public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx, } // Execute entitlement modification on other objects (if needed) - executeEntitlementChangesDelete(ctx, shadow, scripts, result); + executeEntitlementChangesDelete(ctx, shadow, scripts, connOptions, result); Collection additionalOperations = new ArrayList<>(); addExecuteScriptOperation(additionalOperations, ProvisioningOperationTypeType.DELETE, scripts, ctx.getResource(), @@ -483,6 +485,7 @@ public AsynchronousOperationReturnValue repoShadow, OperationProvisioningScriptsType scripts, + ConnectorOperationOptions connOptions, Collection itemDeltas, XMLGregorianCalendar now, OperationResult parentResult) @@ -608,7 +611,7 @@ public AsynchronousOperationReturnValue> convertToPropertyDelta( @SuppressWarnings("rawtypes") private AsynchronousOperationReturnValue> executeModify(ProvisioningContext ctx, PrismObject currentShadow, Collection> identifiers, - Collection operations, OperationResult parentResult) + Collection operations, ConnectorOperationOptions connOptions, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException { Collection sideEffectChanges = new HashSet<>(); @@ -816,7 +819,8 @@ private AsynchronousOperationReturnValue sideEffects = connectorAsyncOpRet.getReturnValue(); if (sideEffects != null) { sideEffectChanges.addAll(sideEffects); @@ -1100,21 +1104,22 @@ private boolean isIdentifierDelta(ProvisioningContext ctx, PropertyDelta ctx.getObjectClassDefinition().isSecondaryIdentifier(propertyDelta.getElementName()); } - private PrismObject executeEntitlementChangesAdd(ProvisioningContext ctx, PrismObject shadow, OperationProvisioningScriptsType scripts, + private PrismObject executeEntitlementChangesAdd(ProvisioningContext ctx, PrismObject shadow, + OperationProvisioningScriptsType scripts, ConnectorOperationOptions connOptions, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException { Map roMap = new HashMap<>(); shadow = entitlementConverter.collectEntitlementsAsObjectOperationInShadowAdd(ctx, roMap, shadow, parentResult); - executeEntitlements(ctx, roMap, parentResult); + executeEntitlements(ctx, roMap, connOptions, parentResult); return shadow; } private PrismObject executeEntitlementChangesModify(ProvisioningContext ctx, PrismObject subjectShadowBefore, PrismObject subjectShadowAfter, - OperationProvisioningScriptsType scripts, Collection subjectDeltas, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException { + OperationProvisioningScriptsType scripts, ConnectorOperationOptions connOptions, Collection subjectDeltas, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException { Map roMap = new HashMap<>(); @@ -1177,13 +1182,13 @@ private PrismObject executeEntitlementChangesModify(ProvisioningCont } } - executeEntitlements(ctx, roMap, parentResult); + executeEntitlements(ctx, roMap, connOptions, parentResult); return subjectShadowAfter; } private void executeEntitlementChangesDelete(ProvisioningContext ctx, PrismObject subjectShadow, - OperationProvisioningScriptsType scripts, + OperationProvisioningScriptsType scripts, ConnectorOperationOptions connOptions, OperationResult parentResult) throws SchemaException { try { @@ -1193,7 +1198,7 @@ private void executeEntitlementChangesDelete(ProvisioningContext ctx, PrismObjec entitlementConverter.collectEntitlementsAsObjectOperationDelete(ctx, roMap, subjectShadow, parentResult); - executeEntitlements(ctx, roMap, parentResult); + executeEntitlements(ctx, roMap, connOptions, parentResult); // TODO: now just log the errors, but not NOT re-throw the exception (except for some exceptions) // we want the original delete to take place, throwing an exception would spoil that @@ -1206,7 +1211,7 @@ private void executeEntitlementChangesDelete(ProvisioningContext ctx, PrismObjec } private void executeEntitlements(ProvisioningContext subjectCtx, - Map roMap, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException { + Map roMap, ConnectorOperationOptions connOptions, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, SchemaException, SecurityViolationException, ConfigurationException, ObjectAlreadyExistsException { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Excuting entitlement chanes, roMap:\n{}", DebugUtil.debugDump(roMap, 1)); @@ -1230,7 +1235,7 @@ private void executeEntitlements(ProvisioningContext subjectCtx, OperationResult result = parentResult.createMinorSubresult(OPERATION_MODIFY_ENTITLEMENT); try { - executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, result); + executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, connOptions, result); result.recordSuccess(); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java index 981df025ba9..d466f4c45e8 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCache.java @@ -37,6 +37,7 @@ import com.evolveum.midpoint.provisioning.impl.errorhandling.ErrorHandlerLocator; import com.evolveum.midpoint.provisioning.ucf.api.Change; import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; +import com.evolveum.midpoint.provisioning.ucf.api.ConnectorOperationOptions; import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.repo.api.RepositoryService; @@ -67,6 +68,7 @@ import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsSimulateType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ReadCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.RunAsCapabilityType; import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; @@ -559,13 +561,15 @@ private String addShadowAttempt(ProvisioningContext ctx, if (shouldExecuteResourceOperationDirectly(ctx)) { + ConnectorOperationOptions connOptions = createConnectorOperationOptions(ctx, options, parentResult); + LOGGER.trace("ADD {}: resource operation, execution starting", shadowToAdd); try { // RESOURCE OPERATION: add AsynchronousOperationReturnValue> asyncReturnValue = - resouceObjectConverter.addResourceObject(ctx, shadowToAdd, scripts, false, parentResult); + resouceObjectConverter.addResourceObject(ctx, shadowToAdd, scripts, connOptions, false, parentResult); opState.processAsyncResult(asyncReturnValue); addedShadow = asyncReturnValue.getReturnValue(); @@ -595,7 +599,7 @@ private String addShadowAttempt(ProvisioningContext ctx, LOGGER.trace("ADD {}: retrying resource operation without uniquness check (previous dead shadow found), execution starting", shadowToAdd); AsynchronousOperationReturnValue> asyncReturnValue = - resouceObjectConverter.addResourceObject(ctx, shadowToAdd, scripts, true, parentResult); + resouceObjectConverter.addResourceObject(ctx, shadowToAdd, scripts, connOptions, true, parentResult); opState.processAsyncResult(asyncReturnValue); addedShadow = asyncReturnValue.getReturnValue(); @@ -937,7 +941,8 @@ public String modifyShadow(PrismObject repoShadow, } private String modifyShadowAttempt(ProvisioningContext ctx, - Collection modifications, OperationProvisioningScriptsType scripts, + Collection modifications, + OperationProvisioningScriptsType scripts, ProvisioningOperationOptions options, ProvisioningOperationState>>> opState, Task task, OperationResult parentResult) @@ -973,10 +978,12 @@ private String modifyShadowAttempt(ProvisioningContext ctx, LOGGER.trace("MODIFY {}: resource modification, execution starting\n{}", repoShadow, DebugUtil.debugDump(modifications)); } + ConnectorOperationOptions connOptions = createConnectorOperationOptions(ctx, options, parentResult); + try { AsynchronousOperationReturnValue>> asyncReturnValue = - resouceObjectConverter.modifyResourceObject(ctx, repoShadow, scripts, modifications, now, parentResult); + resouceObjectConverter.modifyResourceObject(ctx, repoShadow, scripts, connOptions, modifications, now, parentResult); opState.processAsyncResult(asyncReturnValue); Collection> sideEffectChanges = asyncReturnValue.getReturnValue(); @@ -1051,6 +1058,8 @@ private ProvisioningOperationState>>> opState = new ProvisioningOperationState<>(); opState.setRepoShadow(repoShadow); + ConnectorOperationOptions connOptions = createConnectorOperationOptions(ctx, options, parentResult); + try { if (LOGGER.isTraceEnabled()) { @@ -1058,7 +1067,7 @@ private ProvisioningOperationState>> asyncReturnValue = - resouceObjectConverter.modifyResourceObject(ctx, repoShadow, scripts, modifications, now, parentResult); + resouceObjectConverter.modifyResourceObject(ctx, repoShadow, scripts, connOptions, modifications, now, parentResult); opState.processAsyncResult(asyncReturnValue); Collection> sideEffectChanges = asyncReturnValue.getReturnValue(); @@ -1160,11 +1169,13 @@ private PrismObject deleteShadowAttempt(ProvisioningContext ctx, } else { + ConnectorOperationOptions connOptions = createConnectorOperationOptions(ctx, options, parentResult); + LOGGER.trace("DELETE {}: resource deletion, execution starting", repoShadow); try { - AsynchronousOperationResult asyncReturnValue = resouceObjectConverter.deleteResourceObject(ctx, repoShadow, scripts, parentResult); + AsynchronousOperationResult asyncReturnValue = resouceObjectConverter.deleteResourceObject(ctx, repoShadow, scripts, connOptions, parentResult); opState.processAsyncResult(asyncReturnValue); resourceManager.modifyResourceAvailabilityStatus(ctx.getResource().asPrismObject(), @@ -1226,9 +1237,11 @@ private ProvisioningOperationState executeResourceD if (shadow.asObjectable().getFailedOperationType() == null || (shadow.asObjectable().getFailedOperationType() != null && FailedOperationTypeType.ADD != shadow.asObjectable().getFailedOperationType())) { + + ConnectorOperationOptions connOptions = createConnectorOperationOptions(ctx, options, parentResult); try { - AsynchronousOperationResult asyncReturnValue = resouceObjectConverter.deleteResourceObject(ctx, shadow, scripts, parentResult); + AsynchronousOperationResult asyncReturnValue = resouceObjectConverter.deleteResourceObject(ctx, shadow, scripts, connOptions , parentResult); opState.processAsyncResult(asyncReturnValue); } catch (Exception ex) { @@ -1244,7 +1257,6 @@ private ProvisioningOperationState executeResourceD return opState; } - private void notifyAfterDelete( ProvisioningContext ctx, PrismObject shadow, @@ -3168,6 +3180,34 @@ private PasswordCompareStrategyType getPasswordCompareStrategy(RefinedObjectClas return passwordDefinition.getCompareStrategy(); } + private ConnectorOperationOptions createConnectorOperationOptions(ProvisioningContext ctx, ProvisioningOperationOptions options, OperationResult result) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, ExpressionEvaluationException { + if (options == null) { + return null; + } + String runAsAccountOid = options.getRunAsAccountOid(); + if (runAsAccountOid == null) { + return null; + } + RunAsCapabilityType capRunAs = ctx.getResourceEffectiveCapability(RunAsCapabilityType.class); + if (capRunAs == null) { + LOGGER.trace("Operation runAs requested, but resource does not have the capability. Ignoring runAs"); + return null; + } + PrismObject runAsShadow; + try { + runAsShadow = shadowManager.getRepoShadow(runAsAccountOid, result); + } catch (ObjectNotFoundException e) { + throw new ConfigurationException("Requested non-existing 'runAs' shadow", e); + } + ProvisioningContext runAsCtx = ctxFactory.create(runAsShadow, null, ctx.getTask(), result); + shadowCaretaker.applyAttributesDefinition(runAsCtx, runAsShadow); + ResourceObjectIdentification runAsIdentification = ResourceObjectIdentification.createFromShadow(runAsCtx.getObjectClassDefinition(), runAsShadow.asObjectable()); + ConnectorOperationOptions connOptions = new ConnectorOperationOptions(); + connOptions.setRunAsIdentification(runAsIdentification); + return connOptions; + } + + // ----------------------- LEGACY ------ to be removed later (MID-4780) private void cleanLegacyShadowInRepository(PrismObject shadow, OperationResult parentResult) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException{ @@ -3197,31 +3237,5 @@ private void cleanLegacyShadowInRepository(PrismObject shadow, Opera // throw ex; // } } - - private List> createShadowLegacyCleanupAndReconciliationDeltas(PrismObject currentShadow, - PrismObject repoShadowBefore) throws SchemaException { - List> itemDeltas = new ArrayList<>(); - S_ItemEntry i = DeltaBuilder.deltaFor(ShadowType.class, prismContext); - ShadowType repo = repoShadowBefore.asObjectable(); - if (repo.getAttemptNumber() != null) { - i = i.item(ShadowType.F_ATTEMPT_NUMBER).replace(); - } - if (repo.getFailedOperationType() != null) { - i = i.item(ShadowType.F_FAILED_OPERATION_TYPE).replace(); - } - if (repo.getObjectChange() != null) { - i = i.item(ShadowType.F_OBJECT_CHANGE).replace(); - } - if (repo.getResult() != null) { - i = i.item(ShadowType.F_RESULT).replace(); - } - if (repo.getCredentials() != null) { - i = i.item(ShadowType.F_CREDENTIALS).replace(); - } - itemDeltas.addAll(i.asItemDeltas()); - itemDeltas.addAll(ProvisioningUtil.createShadowAttributesReconciliationDeltas(currentShadow, repoShadowBefore, getPrismContext())); - itemDeltas.addAll(ProvisioningUtil.createShadowActivationCleanupDeltas(repo, getPrismContext())); - return itemDeltas; - } } diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCaretaker.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCaretaker.java index 75847606100..6baec6b1e4d 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCaretaker.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowCaretaker.java @@ -42,10 +42,14 @@ import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; import com.evolveum.midpoint.schema.DeltaConvertor; import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer; import com.evolveum.midpoint.schema.processor.ResourceAttributeContainerDefinition; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; +import com.evolveum.midpoint.schema.processor.ResourceObjectIdentification; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; @@ -76,6 +80,7 @@ public class ShadowCaretaker { @Autowired private Clock clock; @Autowired private PrismContext prismContext; + @Autowired private ProvisioningContextFactory ctxFactory; private static final Trace LOGGER = TraceManager.getTrace(ShadowCaretaker.class); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java index 14d98d5cce4..1d039ca0710 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ShadowManager.java @@ -158,7 +158,10 @@ public class ShadowManager { @Autowired private Protector protector; private static final Trace LOGGER = TraceManager.getTrace(ShadowManager.class); - + + public PrismObject getRepoShadow(String oid, OperationResult result) throws ObjectNotFoundException, SchemaException { + return repositoryService.getObject(ShadowType.class, oid, null, result); + } public void deleteConflictedShadowFromRepo(PrismObject shadow, OperationResult parentResult){ diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/TestConnectorManager.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/TestConnectorManager.java index e5237c235c6..c47ca2d1361 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/TestConnectorManager.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/TestConnectorManager.java @@ -44,19 +44,16 @@ @DirtiesContext public class TestConnectorManager extends AbstractIntegrationTest { - private static final String CONNID_FRAMEWORK_VERSION = "1.4.3.41"; + private static final String CONNID_FRAMEWORK_VERSION = "1.4.3.43"; - @Autowired - private ProvisioningService provisioningService; - - @Autowired - private ConnectorManager connectorManager; + @Autowired private ProvisioningService provisioningService; + @Autowired private ConnectorManager connectorManager; private static Trace LOGGER = TraceManager.getTrace(TestConnectorManager.class); @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { - // do NOT postInit proviosioning. postInit would start connector disovery + // do NOT postInit provisioning. postInit would start connector discovery // we want to test the state before discovery // provisioningService.postInit(initResult); } diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractBasicDummyTest.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractBasicDummyTest.java index 78054b03135..8aefebf2fc8 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractBasicDummyTest.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractBasicDummyTest.java @@ -121,6 +121,7 @@ import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PasswordCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ReadCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.RunAsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.TestConnectionCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.UpdateCapabilityType; @@ -594,12 +595,10 @@ public void test028Capabilities() throws Exception { display("Resource", resourceType); List nativeCapabilitiesList = nativeCapabilities.getAny(); assertFalse("Empty capabilities returned", nativeCapabilitiesList.isEmpty()); - CredentialsCapabilityType capCred = CapabilityUtil.getCapability(nativeCapabilitiesList, - CredentialsCapabilityType.class); + CredentialsCapabilityType capCred = CapabilityUtil.getCapability(nativeCapabilitiesList, CredentialsCapabilityType.class); assertNativeCredentialsCapability(capCred); - ActivationCapabilityType capAct = CapabilityUtil.getCapability(nativeCapabilitiesList, - ActivationCapabilityType.class); + ActivationCapabilityType capAct = CapabilityUtil.getCapability(nativeCapabilitiesList, ActivationCapabilityType.class); if (supportsActivation()) { assertNotNull("native activation capability not present", capAct); assertNotNull("native activation status capability not present", capAct.getStatus()); @@ -607,20 +606,20 @@ public void test028Capabilities() throws Exception { assertNull("native activation capability sneaked in", capAct); } - TestConnectionCapabilityType capTest = CapabilityUtil.getCapability(nativeCapabilitiesList, - TestConnectionCapabilityType.class); + TestConnectionCapabilityType capTest = CapabilityUtil.getCapability(nativeCapabilitiesList, TestConnectionCapabilityType.class); assertNotNull("native test capability not present", capTest); - ScriptCapabilityType capScript = CapabilityUtil.getCapability(nativeCapabilitiesList, - ScriptCapabilityType.class); + ScriptCapabilityType capScript = CapabilityUtil.getCapability(nativeCapabilitiesList, ScriptCapabilityType.class); assertNotNull("native script capability not present", capScript); assertNotNull("No host in native script capability", capScript.getHost()); assertFalse("No host in native script capability", capScript.getHost().isEmpty()); // TODO: better look inside - UpdateCapabilityType capUpdate = CapabilityUtil.getCapability(nativeCapabilitiesList, - UpdateCapabilityType.class); + UpdateCapabilityType capUpdate = CapabilityUtil.getCapability(nativeCapabilitiesList, UpdateCapabilityType.class); assertUpdateCapability(capUpdate); + RunAsCapabilityType capRunAs = CapabilityUtil.getCapability(nativeCapabilitiesList, RunAsCapabilityType.class); + assertRunAsCapability(capRunAs); + capabilitiesCachingMetadataType = resourceType.getCapabilities().getCachingMetadata(); assertNotNull("No capabilities caching metadata", capabilitiesCachingMetadataType); assertNotNull("No capabilities caching metadata timestamp", capabilitiesCachingMetadataType.getRetrievalTimestamp()); @@ -663,6 +662,10 @@ protected void assertUpdateCapability(UpdateCapabilityType capUpdate) { assertTrue("native update capability is NOT delta", capUpdate.isDelta()); } + protected void assertRunAsCapability(RunAsCapabilityType capRunAs) { + assertNotNull("native runAs capability not present", capRunAs); + } + protected void assertCountConfiguredCapability(CountObjectsCapabilityType capCount) { CountObjectsSimulateType expectedCountSimulation = getCountSimulationMode(); if (expectedCountSimulation == null) { diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractDummyTest.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractDummyTest.java index c06783014b9..ca8be3454da 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractDummyTest.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/AbstractDummyTest.java @@ -87,7 +87,8 @@ public abstract class AbstractDummyTest extends AbstractProvisioningIntegrationT protected static final String ACCOUNT_WILL_OID = "c0c010c0-d34d-b44f-f11d-33322212dddd"; protected static final String ACCOUNT_WILL_USERNAME = "Will"; protected static final String ACCOUNT_WILL_PASSWORD = "3lizab3th"; - protected static final String ACCOUNT_WILL_PASSWORD_NEW = "3lizab3th123"; + protected static final String ACCOUNT_WILL_PASSWORD_123 = "3lizab3th123"; + protected static final String ACCOUNT_WILL_PASSWORD_321 = "3lizab3th321"; protected static final XMLGregorianCalendar ACCOUNT_WILL_ENABLE_TIMESTAMP = XmlTypeConverter.createXMLGregorianCalendar(2013, 5, 30, 12, 30, 42); protected static final File ACCOUNT_ELIZABETH_FILE = new File(TEST_DIR, "account-elizabeth.xml"); diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java index 320bee6f907..a610df19a07 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummy.java @@ -73,6 +73,7 @@ import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.provisioning.api.ItemComparisonResult; +import com.evolveum.midpoint.provisioning.api.ProvisioningOperationOptions; import com.evolveum.midpoint.provisioning.api.ResourceObjectShadowChangeDescription; import com.evolveum.midpoint.provisioning.impl.ProvisioningTestUtil; import com.evolveum.midpoint.schema.GetOperationOptions; @@ -1126,7 +1127,7 @@ public void test126ModifyAccountWillPassword() throws Exception { OperationResult result = task.getResult(); syncServiceMock.reset(); - ObjectDelta delta = createAccountPaswordDelta(ACCOUNT_WILL_OID, ACCOUNT_WILL_PASSWORD_NEW); + ObjectDelta delta = createAccountPaswordDelta(ACCOUNT_WILL_OID, ACCOUNT_WILL_PASSWORD_123, null); display("ObjectDelta", delta); // WHEN @@ -1138,11 +1139,11 @@ public void test126ModifyAccountWillPassword() throws Exception { displayThen(TEST_NAME); assertSuccess(result); - // Check if the account was created in the dummy resource - DummyAccount dummyAccount = getDummyAccountAssert(transformNameFromResource(ACCOUNT_WILL_USERNAME), willIcfUid); - assertNotNull("No dummy account", dummyAccount); - assertEquals("Wrong password", ACCOUNT_WILL_PASSWORD_NEW, dummyAccount.getPassword()); - accountWillCurrentPassword = ACCOUNT_WILL_PASSWORD_NEW; + assertDummyAccount(transformNameFromResource(ACCOUNT_WILL_USERNAME), willIcfUid) + .assertPassword(ACCOUNT_WILL_PASSWORD_123) + .assertLastModifier(null); + + accountWillCurrentPassword = ACCOUNT_WILL_PASSWORD_123; // Check if the shadow is in the repo PrismObject repoShadow = getShadowRepo(ACCOUNT_WILL_OID); @@ -1150,7 +1151,7 @@ public void test126ModifyAccountWillPassword() throws Exception { display("Repository shadow", repoShadow); checkRepoAccountShadow(repoShadow); - assertRepoShadowCredentials(repoShadow, ACCOUNT_WILL_PASSWORD_NEW); + assertRepoShadowCredentials(repoShadow, ACCOUNT_WILL_PASSWORD_123); syncServiceMock.assertNotifySuccessOnly(); @@ -3649,6 +3650,57 @@ public void test310ModifyMorganEnlistTimestamp() throws Exception { assertSteadyResource(); } + + /** + * Change password, using runAsAccountOid option. + * MID-4397 + */ + @Test + public void test330ModifyAccountWillPasswordSelfService() throws Exception { + final String TEST_NAME = "test330ModifyAccountWillPasswordSelfService"; + displayTestTitle(TEST_NAME); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + syncServiceMock.reset(); + + ObjectDelta delta = createAccountPaswordDelta(ACCOUNT_WILL_OID, ACCOUNT_WILL_PASSWORD_321, ACCOUNT_WILL_PASSWORD_123); + display("ObjectDelta", delta); + + ProvisioningOperationOptions options = ProvisioningOperationOptions.createRunAsAccountOid(ACCOUNT_WILL_OID); + + // WHEN + displayWhen(TEST_NAME); + provisioningService.modifyObject(ShadowType.class, delta.getOid(), delta.getModifications(), + new OperationProvisioningScriptsType(), options, task, result); + + // THEN + displayThen(TEST_NAME); + assertSuccess(result); + + // Check if the account was created in the dummy resource + assertDummyAccount(transformNameFromResource(ACCOUNT_WILL_USERNAME), willIcfUid) + .assertPassword(ACCOUNT_WILL_PASSWORD_321) + .assertLastModifier(getLastModifierName(ACCOUNT_WILL_USERNAME)); + + accountWillCurrentPassword = ACCOUNT_WILL_PASSWORD_321; + + // Check if the shadow is in the repo + PrismObject repoShadow = getShadowRepo(ACCOUNT_WILL_OID); + assertNotNull("Shadow was not created in the repository", repoShadow); + display("Repository shadow", repoShadow); + + checkRepoAccountShadow(repoShadow); + assertRepoShadowCredentials(repoShadow, ACCOUNT_WILL_PASSWORD_321); + + syncServiceMock.assertNotifySuccessOnly(); + + assertSteadyResource(); + } + + protected String getLastModifierName(String expected) { + return transformNameToResource(expected); + } // test4xx reserved for subclasses diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyLimited.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyLimited.java index 81ac299f65f..59532a682ca 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyLimited.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyLimited.java @@ -15,6 +15,7 @@ */ package com.evolveum.midpoint.provisioning.impl.dummy; +import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; @@ -40,12 +41,14 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationProvisioningScriptsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CountObjectsSimulateType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.RunAsCapabilityType; /** * Almost the same as TestDummy but quite limited: * - no activation support * - no paging * - no count simulation using sequential search + * - no runAs * Let's test that we are able to do all the operations without NPEs and other side effects. * * @author Radovan Semancik @@ -85,7 +88,19 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti protected CountObjectsSimulateType getCountSimulationMode() { return CountObjectsSimulateType.SEQUENTIAL_SEARCH; } + + @Override + protected void assertRunAsCapability(RunAsCapabilityType capRunAs) { + assertNull("Unexpected native runAs capability", capRunAs); + } + // No runAs capability, modifier is always the default one. + // No matter what kind of runAs was requested. + @Override + protected String getLastModifierName(String expected) { + return null; + } + @Test @Override public void test150DisableAccount() throws Exception { diff --git a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyUuidNonUniqueName.java b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyUuidNonUniqueName.java index 6f74ee4105b..fd8aa08b049 100644 --- a/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyUuidNonUniqueName.java +++ b/provisioning/provisioning-impl/src/test/java/com/evolveum/midpoint/provisioning/impl/dummy/TestDummyUuidNonUniqueName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 Evolveum + * Copyright (c) 2013-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; import java.io.File; import java.io.IOException; @@ -52,6 +53,7 @@ import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.RunAsCapabilityType; /** * Almost the same as TestDummy but this is using a UUID as ICF UID. @@ -84,6 +86,18 @@ protected File getResourceDummyFile() { protected boolean isNameUnique() { return false; } + + // runAs is using name as an identifier. But name is not unique in this case. runAs won't work. + @Override + protected void assertRunAsCapability(RunAsCapabilityType capRunAs) { + assertNull("Unexpected native runAs capability", capRunAs); + } + + // runAs is using name as an identifier. But name is not unique in this case. runAs won't work. + @Override + protected String getLastModifierName(String expected) { + return null; + } @Test public void test770AddAccountFettuciniAlfredo() throws Exception { diff --git a/provisioning/provisioning-impl/src/test/resources/dummy/dummy-limited/resource-dummy.xml b/provisioning/provisioning-impl/src/test/resources/dummy/dummy-limited/resource-dummy.xml index 9189c65b540..c00bbd0ff51 100644 --- a/provisioning/provisioning-impl/src/test/resources/dummy/dummy-limited/resource-dummy.xml +++ b/provisioning/provisioning-impl/src/test/resources/dummy/dummy-limited/resource-dummy.xml @@ -1,6 +1,6 @@ false + false none false Shiver me timbers! diff --git a/provisioning/provisioning-impl/src/test/resources/dummy/dummy-uuid-nonunique-name/resource-dummy.xml b/provisioning/provisioning-impl/src/test/resources/dummy/dummy-uuid-nonunique-name/resource-dummy.xml index 1143e131457..c346fc64c5d 100644 --- a/provisioning/provisioning-impl/src/test/resources/dummy/dummy-uuid-nonunique-name/resource-dummy.xml +++ b/provisioning/provisioning-impl/src/test/resources/dummy/dummy-uuid-nonunique-name/resource-dummy.xml @@ -34,6 +34,8 @@ uuid false + + false Shiver me timbers! Dead men tell no tales diff --git a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorInstance.java b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorInstance.java index a826434d647..573a40bf8ae 100644 --- a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorInstance.java +++ b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorInstance.java @@ -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. @@ -264,14 +264,14 @@ AsynchronousOperationReturnValue>> addObject(Pri * E.g. in case of connect timeout or connection refused. Timeout during operation should not cause the * exception as something might have been done already. * - * @param identifiers The set of identifiers. Their values may change as a result of the operation, e.g. when the resource object is renamed. - * @param changes - * @throws CommunicationException - * @throws SchemaException * @throws ObjectAlreadyExistsException in case that the modified object conflicts with another existing object (e.g. while renaming an object) */ - AsynchronousOperationReturnValue> modifyObject(ObjectClassComplexTypeDefinition objectClass, PrismObject shadow, Collection> identifiers, Collection changes, StateReporter reporter, - OperationResult parentResult) + AsynchronousOperationReturnValue> modifyObject( + ResourceObjectIdentification identification, + PrismObject shadow, + Collection changes, + ConnectorOperationOptions options, + StateReporter reporter, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException, ConfigurationException; diff --git a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorOperationOptions.java b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorOperationOptions.java new file mode 100644 index 00000000000..59aecc0fe85 --- /dev/null +++ b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/ConnectorOperationOptions.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2018 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.provisioning.ucf.api; + +import com.evolveum.midpoint.schema.processor.ResourceObjectIdentification; + +/** + * @author semancik + * + */ +public class ConnectorOperationOptions { + + /** + * Run the operations on resource using the specified identity. + * Provided identification should identify valid, active account. + */ + private ResourceObjectIdentification runAsIdentification; + + public ResourceObjectIdentification getRunAsIdentification() { + return runAsIdentification; + } + + public void setRunAsIdentification(ResourceObjectIdentification runAsIdentification) { + this.runAsIdentification = runAsIdentification; + } +} diff --git a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/Operation.java b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/Operation.java index cceabfaba1e..93e90985e21 100644 --- a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/Operation.java +++ b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/Operation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 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. @@ -30,9 +30,4 @@ */ public abstract class Operation implements DebugDumpable { - @Override - public String debugDump() { - return debugDump(0); - } - } \ No newline at end of file diff --git a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/PasswordChangeOperation.java b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/PasswordChangeOperation.java deleted file mode 100644 index 90e4db3f045..00000000000 --- a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/PasswordChangeOperation.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2010-2017 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.provisioning.ucf.api; - -import com.evolveum.midpoint.schema.util.SchemaDebugUtil; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; - -/** - * @author Radovan Semancik - * - */ -public class PasswordChangeOperation extends Operation { - - private ProtectedStringType newPassword; - private ProtectedStringType oldPassword; - - public PasswordChangeOperation(ProtectedStringType newPassword) { - super(); - this.newPassword = newPassword; - } - - public PasswordChangeOperation(ProtectedStringType newPassword, ProtectedStringType oldPassword) { - super(); - this.newPassword = newPassword; - this.oldPassword = oldPassword; - } - - public ProtectedStringType getNewPassword() { - return newPassword; - } - - public void setNewPassword(ProtectedStringType newPassword) { - this.newPassword = newPassword; - } - - public ProtectedStringType getOldPassword() { - return oldPassword; - } - - public void setOldPassword(ProtectedStringType oldPassword) { - this.oldPassword = oldPassword; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((newPassword == null) ? 0 : newPassword.hashCode()); - result = prime * result + ((oldPassword == null) ? 0 : oldPassword.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - PasswordChangeOperation other = (PasswordChangeOperation) obj; - if (newPassword == null) { - if (other.newPassword != null) { - return false; - } - } else if (!newPassword.equals(other.newPassword)) { - return false; - } - if (oldPassword == null) { - if (other.oldPassword != null) { - return false; - } - } else if (!oldPassword.equals(other.oldPassword)) { - return false; - } - return true; - } - - @Override - public String debugDump(int indent) { - StringBuilder sb = new StringBuilder(); - SchemaDebugUtil.indentDebugDump(sb, indent); - sb.append("Password change: new password "); - appendPasswordDescription(sb,newPassword); - sb.append("; old password "); - appendPasswordDescription(sb,oldPassword); - return sb.toString(); - } - - private void appendPasswordDescription(StringBuilder sb, ProtectedStringType passwd) { - if (passwd != null) { - sb.append("present"); - if (passwd.getClearValue() != null) { - sb.append(" in clear"); - if (passwd.getClearValue().isEmpty()) { - sb.append(" and empty"); - } - } - if (passwd.getEncryptedDataType() != null) { - sb.append(" encrypted"); - } - } else { - sb.append("null"); - } - } - -} diff --git a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/connectors/AbstractManualConnectorInstance.java b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/connectors/AbstractManualConnectorInstance.java index 9d97f60df3d..b7de86e97a6 100644 --- a/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/connectors/AbstractManualConnectorInstance.java +++ b/provisioning/ucf-api/src/main/java/com/evolveum/midpoint/provisioning/ucf/api/connectors/AbstractManualConnectorInstance.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017 Evolveum + * Copyright (c) 2017-2018 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.provisioning.ucf.api.AttributesToReturn; import com.evolveum.midpoint.provisioning.ucf.api.Change; +import com.evolveum.midpoint.provisioning.ucf.api.ConnectorOperationOptions; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; import com.evolveum.midpoint.provisioning.ucf.api.ManagedConnector; @@ -139,8 +140,10 @@ public AsynchronousOperationReturnValue>> addObj @Override public AsynchronousOperationReturnValue> modifyObject( - ObjectClassComplexTypeDefinition objectClass, PrismObject shadow, - Collection> identifiers, Collection changes, + ResourceObjectIdentification identification, + PrismObject shadow, + Collection changes, + ConnectorOperationOptions options, StateReporter reporter, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException, ConfigurationException { @@ -154,7 +157,7 @@ public AsynchronousOperationReturnValue covertAuxiliaryObjectClassValuesToConnId(pvals, midPointAttributeName, auxiliaryObjectClassMap)); } - PropertyDelta passwordDelta = null; - PasswordChangeOperation passwordChangeOperation = null; - for (Operation operation : changes) { if (operation instanceof PropertyModificationOperation) { PropertyModificationOperation change = (PropertyModificationOperation) operation; @@ -256,8 +243,7 @@ public void convert() throws SchemaException { } else if (delta.getParentPath().equivalent(new ItemPath(ShadowType.F_ACTIVATION))) { convertFromActivation(delta); - } else if (delta.getParentPath().equivalent( - new ItemPath(new ItemPath(ShadowType.F_CREDENTIALS), CredentialsType.F_PASSWORD))) { + } else if (delta.getParentPath().equivalent(SchemaConstants.PATH_PASSWORD)) { convertFromPassword((PropertyDelta) delta); } else if (delta.getPath().equivalent(new ItemPath(ShadowType.F_AUXILIARY_OBJECT_CLASS))) { // already processed @@ -265,10 +251,6 @@ public void convert() throws SchemaException { throw new SchemaException("Change of unknown attribute " + delta.getPath()); } - } else if (operation instanceof PasswordChangeOperation) { - passwordChangeOperation = (PasswordChangeOperation) operation; - // TODO: check for multiple occurrences and fail - } else if (operation instanceof ExecuteProvisioningScriptOperation) { ExecuteProvisioningScriptOperation scriptOperation = (ExecuteProvisioningScriptOperation) operation; additionalOperations.add(scriptOperation); diff --git a/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnectorInstanceConnIdImpl.java b/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnectorInstanceConnIdImpl.java index 47a7e5113f9..db82a1c9aff 100644 --- a/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnectorInstanceConnIdImpl.java +++ b/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnectorInstanceConnIdImpl.java @@ -81,6 +81,7 @@ import org.identityconnectors.framework.impl.api.local.operations.ConnectorOperationalContext; import org.identityconnectors.framework.spi.Connector; import org.identityconnectors.framework.spi.PoolableConnector; +import org.identityconnectors.framework.spi.operations.UpdateAttributeValuesOp; import org.jfree.util.Log; import com.evolveum.midpoint.prism.ComplexTypeDefinition; @@ -111,6 +112,7 @@ import com.evolveum.midpoint.provisioning.ucf.api.AttributesToReturn; import com.evolveum.midpoint.provisioning.ucf.api.Change; import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; +import com.evolveum.midpoint.provisioning.ucf.api.ConnectorOperationOptions; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; import com.evolveum.midpoint.provisioning.ucf.api.ExecuteScriptArgument; import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; @@ -181,6 +183,7 @@ import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PagedSearchCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.PasswordCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ReadCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.RunAsCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.SchemaCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType; import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType.Host; @@ -690,7 +693,7 @@ private void addBasicReadCapability() { } } - private void parseResourceSchema(org.identityconnectors.framework.common.objects.Schema icfSchema, List generateObjectClasses) { + private void parseResourceSchema(org.identityconnectors.framework.common.objects.Schema connIdSchema, List generateObjectClasses) { AttributeInfo passwordAttributeInfo = null; AttributeInfo enableAttributeInfo = null; @@ -703,12 +706,12 @@ private void parseResourceSchema(org.identityconnectors.framework.common.objects setResourceSchema(new ResourceSchemaImpl(getSchemaNamespace(), prismContext)); if (legacySchema == null) { - legacySchema = detectLegacySchema(icfSchema); + legacySchema = detectLegacySchema(connIdSchema); } LOGGER.trace("Converting resource schema (legacy mode: {})", legacySchema); LOGGER.trace("Generating object classes: {}", generateObjectClasses); - Set objectClassInfoSet = icfSchema.getObjectClassInfo(); + Set objectClassInfoSet = connIdSchema.getObjectClassInfo(); // Let's convert every objectclass in the ConnId schema ... for (ObjectClassInfo objectClassInfo : objectClassInfoSet) { @@ -1000,7 +1003,7 @@ private void parseResourceSchema(org.identityconnectors.framework.common.objects boolean canPageOffset = false; boolean canSort = false; boolean supportsReturnDefaultAttributes = false; - for (OperationOptionInfo searchOption: icfSchema.getSupportedOptionsByOperation(SearchApiOp.class)) { + for (OperationOptionInfo searchOption: connIdSchema.getSupportedOptionsByOperation(SearchApiOp.class)) { switch (searchOption.getName()) { case OperationOptions.OP_PAGE_SIZE: canPageSize = true; @@ -1015,23 +1018,42 @@ private void parseResourceSchema(org.identityconnectors.framework.common.objects supportsReturnDefaultAttributes = true; break; } - + } + if (canPageSize || canPageOffset || canSort) { + PagedSearchCapabilityType capPage = new PagedSearchCapabilityType(); + capabilities.add(CAPABILITY_OBJECT_FACTORY.createPagedSearch(capPage)); } Set> supportedOperations = connIdConnectorFacade.getSupportedOperations(); - if (supportedOperations.contains(GetApiOp.class) || supportedOperations.contains(SearchApiOp.class)){ + if (supportedOperations.contains(GetApiOp.class) || supportedOperations.contains(SearchApiOp.class)) { ReadCapabilityType capRead = new ReadCapabilityType(); capRead.setReturnDefaultAttributesOption(supportsReturnDefaultAttributes); capabilities.add(CAPABILITY_OBJECT_FACTORY.createRead(capRead)); } - - if (canPageSize || canPageOffset || canSort) { - PagedSearchCapabilityType capPage = new PagedSearchCapabilityType(); - capabilities.add(CAPABILITY_OBJECT_FACTORY.createPagedSearch(capPage)); + if (supportedOperations.contains(UpdateDeltaApiOp.class)) { + processUpdateOperationOptions(connIdSchema.getSupportedOptionsByOperation(UpdateDeltaApiOp.class)); + } else if (supportedOperations.contains(UpdateApiOp.class)) { + processUpdateOperationOptions(connIdSchema.getSupportedOptionsByOperation(UpdateApiOp.class)); } } + private void processUpdateOperationOptions(Set supportedOptions) { + boolean canRunAsUser = false; + for (OperationOptionInfo searchOption: supportedOptions) { + switch (searchOption.getName()) { + case OperationOptions.OP_RUN_AS_USER: + canRunAsUser = true; + break; + // TODO: run as password + } + } + if (canRunAsUser) { + RunAsCapabilityType capRunAs = new RunAsCapabilityType(); + capabilities.add(CAPABILITY_OBJECT_FACTORY.createRunAs(capRunAs)); + } + } + private boolean detectLegacySchema(Schema icfSchema) { Set objectClassInfoSet = icfSchema.getObjectClassInfo(); for (ObjectClassInfo objectClassInfo : objectClassInfoSet) { @@ -1549,19 +1571,17 @@ private void validateShadow(PrismObject shadow, String ope @Override public AsynchronousOperationReturnValue> modifyObject( - ObjectClassComplexTypeDefinition objectClassDef, + ResourceObjectIdentification identification, PrismObject shadow, - Collection> identifiers, Collection changes, + ConnectorOperationOptions options, StateReporter reporter, OperationResult parentResult) throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException { - OperationResult result = parentResult.createSubresult(ConnectorInstance.class.getName() - + ".modifyObject"); - result.addArbitraryObjectAsParam("objectClass", objectClassDef); - result.addArbitraryObjectCollectionAsParam("identifiers", identifiers); + OperationResult result = parentResult.createSubresult(ConnectorInstance.class.getName() + ".modifyObject"); + result.addArbitraryObjectAsParam("identification", identification); result.addArbitraryObjectCollectionAsParam("changes", changes); if (changes.isEmpty()){ @@ -1570,25 +1590,25 @@ public AsynchronousOperationReturnValue(0), result); } - ObjectClass objClass = connIdNameMapper.objectClassToIcf(objectClassDef, getSchemaNamespace(), connectorType, legacySchema); + ObjectClass objClass = connIdNameMapper.objectClassToIcf(identification.getObjectClassDefinition(), getSchemaNamespace(), connectorType, legacySchema); Uid uid; try { - uid = getUid(objectClassDef, identifiers); + uid = getUid(identification); } catch (SchemaException e) { result.recordFatalError(e); throw e; } if (uid == null) { - result.recordFatalError("Cannot detemine UID from identifiers: " + identifiers); - throw new IllegalArgumentException("Cannot detemine UID from identifiers: " + identifiers); + result.recordFatalError("Cannot detemine UID from identification: " + identification); + throw new IllegalArgumentException("Cannot detemine UID from identification: " + identification); } if (supportsDeltaUpdateOp()) { - return modifyObjectDelta(objectClassDef, objClass, uid, shadow, identifiers, changes, reporter, result); + return modifyObjectDelta(identification, objClass, uid, shadow, changes, options, reporter, result); } else { - return modifyObjectUpdate(objectClassDef, objClass, uid, shadow, identifiers, changes, reporter, result); + return modifyObjectUpdate(identification, objClass, uid, shadow, changes, options, reporter, result); } } @@ -1596,17 +1616,18 @@ public AsynchronousOperationReturnValue> modifyObjectDelta( - ObjectClassComplexTypeDefinition objectClassDef, + ResourceObjectIdentification identification, ObjectClass objClass, Uid uid, PrismObject shadow, - Collection> identifiers, Collection changes, + ConnectorOperationOptions options, StateReporter reporter, OperationResult result) throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException { + ObjectClassComplexTypeDefinition objectClassDef = identification.getObjectClassDefinition(); Set sideEffect = new HashSet<>(); String originalUid = uid.getUidValue(); @@ -1639,12 +1660,12 @@ private AsynchronousOperationReturnValue attributesDelta = converter.getAttributesDelta(); if (!attributesDelta.isEmpty()) { - OperationOptions options = new OperationOptionsBuilder().build(); + OperationOptions connIdOptions = createConnIdOptions(options, changes); connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".updateDelta"); connIdResult.addParam("objectClass", objectClassDef.toString()); connIdResult.addParam("uid", uid==null? "null":uid.getUidValue()); connIdResult.addParam("attributesDelta", attributesDelta.toString()); - connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addArbitraryObjectAsParam("options", connIdOptions); connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { @@ -1657,7 +1678,7 @@ private AsynchronousOperationReturnValue uidDelta = createUidDelta(newUid, getUidDefinition(objectClassDef, identifiers)); + PropertyDelta uidDelta = createUidDelta(newUid, getUidDefinition(identification)); PropertyModificationOperation uidMod = new PropertyModificationOperation(uidDelta); sideEffectChanges.add(uidMod); - replaceUidValue(objectClassDef, identifiers, newUid); + replaceUidValue(identification, newUid); } else if(name.equals(Name.NAME)){ Name newName = new Name((String)attrDeltaSideEffect.getValuesToReplace().get(0)); - PropertyDelta nameDelta = createNameDelta(newName, getNameDefinition(objectClassDef, identifiers)); + PropertyDelta nameDelta = createNameDelta(newName, getNameDefinition(identification)); PropertyModificationOperation nameMod = new PropertyModificationOperation(nameDelta); sideEffectChanges.add(nameMod); - replaceNameValue(objectClassDef, identifiers, new Name((String)attrDeltaSideEffect.getValuesToReplace().get(0))); + replaceNameValue(identification, new Name((String)attrDeltaSideEffect.getValuesToReplace().get(0))); } else { ResourceAttributeDefinition definition = objectClassDef.findAttributeDefinition(name); @@ -1754,18 +1775,19 @@ private AsynchronousOperationReturnValue> modifyObjectUpdate( - ObjectClassComplexTypeDefinition objectClassDef, + ResourceObjectIdentification identification, ObjectClass objClass, Uid uid, PrismObject shadow, - Collection> identifiers, Collection changes, + ConnectorOperationOptions options, StateReporter reporter, OperationResult result) throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, SecurityViolationException, ObjectAlreadyExistsException { + ObjectClassComplexTypeDefinition objectClassDef = identification.getObjectClassDefinition(); String originalUid = uid.getUidValue(); UpdateModificationConverter converter = new UpdateModificationConverter(); @@ -1801,12 +1823,12 @@ private AsynchronousOperationReturnValue attributesToAdd = converter.getAttributesToAdd(); if (!attributesToAdd.isEmpty()) { - OperationOptions options = new OperationOptionsBuilder().build(); + OperationOptions connIdOptions = createConnIdOptions(options, changes); connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".addAttributeValues"); connIdResult.addArbitraryObjectAsParam("objectClass", objectClassDef); connIdResult.addParam("uid", uid.getUidValue()); connIdResult.addArbitraryObjectAsParam("attributes", attributesToAdd); - connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addArbitraryObjectAsParam("options", connIdOptions); connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { @@ -1821,7 +1843,7 @@ private AsynchronousOperationReturnValue attributesToUpdate = converter.getAttributesToUpdate(); if (!attributesToUpdate.isEmpty()) { - OperationOptions options = new OperationOptionsBuilder().build(); + OperationOptions connIdOptions = createConnIdOptions(options, changes); connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".update"); connIdResult.addArbitraryObjectAsParam("objectClass", objectClassDef); connIdResult.addParam("uid", uid==null?"null":uid.getUidValue()); connIdResult.addArbitraryObjectAsParam("attributes", attributesToUpdate); - connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addArbitraryObjectAsParam("options", connIdOptions); connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { @@ -1879,7 +1901,7 @@ private AsynchronousOperationReturnValue attributesToRemove = converter.getAttributesToRemove(); if (!attributesToRemove.isEmpty()) { - OperationOptions options = new OperationOptionsBuilder().build(); + OperationOptions connIdOptions = createConnIdOptions(options, changes); connIdResult = result.createSubresult(ConnectorFacade.class.getName() + ".removeAttributeValues"); connIdResult.addArbitraryObjectAsParam("objectClass", objectClassDef); connIdResult.addParam("uid", uid.getUidValue()); connIdResult.addArbitraryObjectAsParam("attributes", attributesToRemove); - connIdResult.addArbitraryObjectAsParam("options", options); + connIdResult.addArbitraryObjectAsParam("options", connIdOptions); connIdResult.addContext("connector", connIdConnectorFacade.getClass()); if (LOGGER.isTraceEnabled()) { @@ -1938,7 +1960,7 @@ private AsynchronousOperationReturnValue uidDelta = createUidDelta(uid, getUidDefinition(objectClassDef, identifiers)); + PropertyDelta uidDelta = createUidDelta(uid, getUidDefinition(identification)); PropertyModificationOperation uidMod = new PropertyModificationOperation(uidDelta); // TODO what about matchingRuleQName ? sideEffectChanges.add(uidMod); - replaceUidValue(objectClassDef, identifiers, uid); + replaceUidValue(identification, uid); } return AsynchronousOperationReturnValue.wrap(sideEffectChanges, result); } - private void replaceNameValue(ObjectClassComplexTypeDefinition objectClass, Collection> identifiers, Name newName){ - if (identifiers.size() == 0) { - throw new IllegalStateException("No identifiers"); - } - if (identifiers.size() == 1) { - return; - } - for (ResourceAttribute attr : identifiers) { - if (objectClass.isSecondaryIdentifier(attr.getElementName())) { - ((ResourceAttribute) attr).setValue(new PrismPropertyValue(newName.getNameValue())); - return; - } - } - // fallback, compatibility - for (ResourceAttribute attr : identifiers) { - if (attr.getElementName().equals(SchemaConstants.ICFS_NAME)) { - attr.setValue(new PrismPropertyValue(newName.getNameValue())); // expecting the NAME property is of type String - return; + private void replaceNameValue(ResourceObjectIdentification identification, Name newName) throws SchemaException { + ResourceAttribute secondaryIdentifier = identification.getSecondaryIdentifier(); + if (secondaryIdentifier == null) { + // fallback, compatibility + for (ResourceAttribute attr : identification.getAllIdentifiers()) { + if (attr.getElementName().equals(SchemaConstants.ICFS_NAME)) { + attr.setValue(new PrismPropertyValue(newName.getNameValue())); // expecting the NAME property is of type String + return; + } } + throw new IllegalStateException("No identifiers"); } + secondaryIdentifier.setValue(new PrismPropertyValue(newName.getNameValue())); } private PropertyDelta createNameDelta(Name name, ResourceAttributeDefinition nameDefinition) { @@ -2717,11 +2732,7 @@ private Uid getUid(ResourceObjectIdentification resourceObjectIdentification) th return null; } String uidValue = primaryIdentifier.getRealValue(); - String nameValue = null; - Collection> secondaryIdentifiers = resourceObjectIdentification.getSecondaryIdentifiers(); - if (secondaryIdentifiers != null && secondaryIdentifiers.size() == 1) { - nameValue = (String) secondaryIdentifiers.iterator().next().getRealValue(); - } + String nameValue = getNameValue(resourceObjectIdentification); if (uidValue != null) { if (nameValue == null) { return new Uid(uidValue); @@ -2731,6 +2742,15 @@ private Uid getUid(ResourceObjectIdentification resourceObjectIdentification) th } return null; } + + private String getNameValue(ResourceObjectIdentification resourceObjectIdentification) { + String nameValue = null; + Collection> secondaryIdentifiers = resourceObjectIdentification.getSecondaryIdentifiers(); + if (secondaryIdentifiers != null && secondaryIdentifiers.size() == 1) { + nameValue = (String) secondaryIdentifiers.iterator().next().getRealValue(); + } + return nameValue; + } /** * Looks up ConnId Uid identifier in a (potentially multi-valued) set of @@ -2782,70 +2802,48 @@ private Uid getUid(ObjectClassComplexTypeDefinition objectClass, Collection> identifiers, Uid newUid) { - if (identifiers.size() == 0) { - throw new IllegalStateException("No identifiers"); - } - if (identifiers.size() == 1) { - identifiers.iterator().next().setValue(new PrismPropertyValue(newUid.getUidValue())); - return; - } - for (ResourceAttribute attr : identifiers) { - if (objectClass.isPrimaryIdentifier(attr.getElementName())) { - ((ResourceAttribute) attr).setValue(new PrismPropertyValue(newUid.getUidValue())); - return; - } - } - // fallback, compatibility - for (ResourceAttribute attr : identifiers) { - if (attr.getElementName().equals(SchemaConstants.ICFS_UID)) { - attr.setValue(new PrismPropertyValue(newUid.getUidValue())); // expecting the UID property is of type String - return; + private void replaceUidValue(ResourceObjectIdentification identification, Uid newUid) throws SchemaException { + ResourceAttribute primaryIdentifier = identification.getPrimaryIdentifier(); + if (primaryIdentifier == null) { + // fallback, compatibility + Collection> identifiers = identification.getAllIdentifiers(); + for (ResourceAttribute attr : identifiers) { + if (attr.getElementName().equals(SchemaConstants.ICFS_UID)) { + attr.setValue(new PrismPropertyValue(newUid.getUidValue())); // expecting the UID property is of type String + return; + } } + throw new IllegalStateException("No UID attribute in " + identifiers); } - throw new IllegalStateException("No UID attribute in " + identifiers); + primaryIdentifier.setValue(new PrismPropertyValue(newUid.getUidValue())); } - private ResourceAttributeDefinition getNameDefinition(ObjectClassComplexTypeDefinition objectClass, Collection> identifiers) { - if (identifiers.size() == 0) { - return null; - } - if (identifiers.size() == 1) { - return null; - } - for (ResourceAttribute attr : identifiers) { - if (objectClass.isSecondaryIdentifier(attr.getElementName())) { - return ((ResourceAttribute) attr).getDefinition(); - } - } - // fallback, compatibility - for (ResourceAttribute attr : identifiers) { - if (attr.getElementName().equals(SchemaConstants.ICFS_NAME)) { - return attr.getDefinition(); + private ResourceAttributeDefinition getNameDefinition(ResourceObjectIdentification identification) throws SchemaException { + ResourceAttribute secondaryIdentifier = identification.getSecondaryIdentifier(); + if (secondaryIdentifier == null) { + // fallback, compatibility + for (ResourceAttribute attr : identification.getAllIdentifiers()) { + if (attr.getElementName().equals(SchemaConstants.ICFS_NAME)) { + return attr.getDefinition(); + } } + return null; } - return null; + return secondaryIdentifier.getDefinition(); } - private ResourceAttributeDefinition getUidDefinition(ObjectClassComplexTypeDefinition objectClass, Collection> identifiers) { - if (identifiers.size() == 0) { - return null; - } - if (identifiers.size() == 1) { - return identifiers.iterator().next().getDefinition(); - } - for (ResourceAttribute attr : identifiers) { - if (objectClass.isPrimaryIdentifier(attr.getElementName())) { - return ((ResourceAttribute) attr).getDefinition(); - } - } - // fallback, compatibility - for (ResourceAttribute attr : identifiers) { - if (attr.getElementName().equals(SchemaConstants.ICFS_UID)) { - return attr.getDefinition(); + private ResourceAttributeDefinition getUidDefinition(ResourceObjectIdentification identification) throws SchemaException { + ResourceAttribute primaryIdentifier = identification.getPrimaryIdentifier(); + if (primaryIdentifier == null) { + // fallback, compatibility + for (ResourceAttribute attr : identification.getAllIdentifiers()) { + if (attr.getElementName().equals(SchemaConstants.ICFS_UID)) { + return attr.getDefinition(); + } } + return null; } - return null; + return primaryIdentifier.getDefinition(); } @@ -3218,5 +3216,37 @@ private void recordIcfOperationEnd(StateReporter reporter, ProvisioningOperation LOGGER.warn("Couldn't record ConnId operation end as reporter is null."); } } + + private OperationOptions createConnIdOptions(ConnectorOperationOptions options, Collection changes) { + OperationOptionsBuilder connIdOptionsBuilder = new OperationOptionsBuilder(); + if (options != null) { + ResourceObjectIdentification runAsIdentification = options.getRunAsIdentification(); + if (runAsIdentification != null) { + connIdOptionsBuilder.setRunAsUser(getNameValue(runAsIdentification)); + // We are going to figure out what the runAsPassword may be. + // If there is a password change then there should be old value in the delta. + // This is quite a black magic. But we do not have a better way now. + for (Operation change : changes) { + if (change instanceof PropertyModificationOperation) { + PropertyDelta propertyDelta = ((PropertyModificationOperation)change).getPropertyDelta(); + if (!propertyDelta.getPath().equivalent(SchemaConstants.PATH_PASSWORD_VALUE)) { + continue; + } + Collection> oldValues = propertyDelta.getEstimatedOldValues(); + if (oldValues == null || oldValues.isEmpty()) { + continue; + } + ProtectedStringType oldPassword = oldValues.iterator().next().getValue(); + if (oldPassword != null) { + GuardedString oldPasswordGs = ConnIdUtil.toGuardedString(oldPassword, "runAs password", protector); + connIdOptionsBuilder.setRunWithPassword(oldPasswordGs); + } + } + } + } + } + return connIdOptionsBuilder.build(); + } + } diff --git a/provisioning/ucf-impl-connid/src/test/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/TestUcfOpenDj.java b/provisioning/ucf-impl-connid/src/test/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/TestUcfOpenDj.java index 055675b891b..3f9e4f751e4 100644 --- a/provisioning/ucf-impl-connid/src/test/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/TestUcfOpenDj.java +++ b/provisioning/ucf-impl-connid/src/test/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/TestUcfOpenDj.java @@ -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. @@ -351,11 +351,11 @@ public void test110ChangeModifyObject() throws Exception { changes.add(createDeleteAttributeChange("givenName", "John")); ObjectClassComplexTypeDefinition accountDefinition = resourceSchema.findObjectClassDefinition(OpenDJController.OBJECT_CLASS_INETORGPERSON_NAME); - - cc.modifyObject(accountDefinition, null, identifiers, changes, null, result); - ResourceObjectIdentification identification = ResourceObjectIdentification.createFromAttributes( accountDefinition, identifiers); + + cc.modifyObject(identification, null, changes, null, null, result); + PrismObject shadow = cc.fetchObject(identification, null, null, result); ResourceAttributeContainer resObj = ShadowUtil.getAttributesContainer(shadow); @@ -747,13 +747,14 @@ public void test610ChangePassword() throws Exception { //set the modificaion type propMod.setModificationType(ModificationTypeType.REPLACE); - PropertyDelta passDelta = (PropertyDelta)DeltaConvertor.createItemDelta(propMod, shadow.getDefinition()); - PropertyModificationOperation passwordModification = new PropertyModificationOperation(passDelta); + PropertyDelta passDelta = (PropertyDelta)DeltaConvertor.createItemDelta(propMod, shadow.getDefinition()); + PropertyModificationOperation passwordModification = new PropertyModificationOperation(passDelta); changes.add(passwordModification); + + ResourceObjectIdentification identification = ResourceObjectIdentification.createFromAttributes( + accountDefinition, identifiers); -// PasswordChangeOperation passwordChange = new PasswordChangeOperation(passPs); -// changes.add(passwordChange); - cc.modifyObject(accountDefinition, null, identifiers, changes, null, result); + cc.modifyObject(identification, null, changes, null, null, result); // THEN diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java index 614e7658246..bcf0869f3ad 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/AbstractIntegrationTest.java @@ -1392,36 +1392,7 @@ protected void assertUserNoPassword(PrismObject user) throws Encryptio } protected void assertProtectedString(String message, String expectedClearValue, ProtectedStringType actualValue, CredentialsStorageTypeType storageType) throws EncryptionException, SchemaException { - switch (storageType) { - - case NONE: - assertNull(message+": unexpected value: "+actualValue, actualValue); - break; - - case ENCRYPTION: - assertNotNull(message+": no value", actualValue); - assertTrue(message+": unencrypted value: "+actualValue, actualValue.isEncrypted()); - String actualClearPassword = protector.decryptString(actualValue); - assertEquals(message+": wrong value", expectedClearValue, actualClearPassword); - assertFalse(message+": unexpected hashed value: "+actualValue, actualValue.isHashed()); - assertNull(message+": unexpected clear value: "+actualValue, actualValue.getClearValue()); - break; - - case HASHING: - assertNotNull(message+": no value", actualValue); - assertTrue(message+": value not hashed: "+actualValue, actualValue.isHashed()); - ProtectedStringType expectedPs = new ProtectedStringType(); - expectedPs.setClearValue(expectedClearValue); - assertTrue(message+": hash does not match, expected "+expectedClearValue+", but was "+actualValue, - protector.compare(actualValue, expectedPs)); - assertFalse(message+": unexpected encrypted value: "+actualValue, actualValue.isEncrypted()); - assertNull(message+": unexpected clear value: "+actualValue, actualValue.getClearValue()); - break; - - default: - throw new IllegalArgumentException("Unknown storage "+storageType); - } - + IntegrationTestTools.assertProtectedString(message, expectedClearValue, actualValue, storageType, protector); } protected boolean compareProtectedString(String expectedClearValue, ProtectedStringType actualValue, CredentialsStorageTypeType storageType) throws EncryptionException, SchemaException { @@ -2358,10 +2329,19 @@ protected ItemPath getAttributePath(PrismObject resource, String a return new ItemPath(ShadowType.F_ATTRIBUTES, getAttributeQName(resource, attributeLocalName)); } - protected ObjectDelta createAccountPaswordDelta(String shadowOid, String newPassword) { - ProtectedStringType userPasswordPs = new ProtectedStringType(); - userPasswordPs.setClearValue(newPassword); - return ObjectDelta.createModificationReplaceProperty(ShadowType.class, shadowOid, SchemaConstants.PATH_PASSWORD_VALUE, prismContext, userPasswordPs); + protected ObjectDelta createAccountPaswordDelta(String shadowOid, String newPassword, String oldPassword) throws SchemaException { + ProtectedStringType newPasswordPs = new ProtectedStringType(); + newPasswordPs.setClearValue(newPassword); + ProtectedStringType oldPasswordPs = null; + if (oldPassword != null) { + oldPasswordPs = new ProtectedStringType(); + oldPasswordPs.setClearValue(oldPassword); + } + return deltaFor(ShadowType.class) + .item(SchemaConstants.PATH_PASSWORD_VALUE) + .oldRealValue(oldPasswordPs) + .replace(newPasswordPs) + .asObjectDelta(shadowOid); } protected PrismObject getShadowRepo(String shadowOid) throws ObjectNotFoundException, SchemaException { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/IntegrationTestTools.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/IntegrationTestTools.java index 20af933c452..33864abac35 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/IntegrationTestTools.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/IntegrationTestTools.java @@ -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. @@ -20,6 +20,8 @@ import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.match.MatchingRule; import com.evolveum.midpoint.prism.path.ItemPath; @@ -61,6 +63,7 @@ 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.PolyStringType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang.StringUtils; @@ -1049,4 +1052,36 @@ public static void clearLog() throws IOException { System.out.println("Log cleared."); } + public static void assertProtectedString(String message, String expectedClearValue, ProtectedStringType actualValue, CredentialsStorageTypeType storageType, Protector protector) throws EncryptionException, SchemaException { + switch (storageType) { + + case NONE: + assertNull(message+": unexpected value: "+actualValue, actualValue); + break; + + case ENCRYPTION: + assertNotNull(message+": no value", actualValue); + assertTrue(message+": unencrypted value: "+actualValue, actualValue.isEncrypted()); + String actualClearPassword = protector.decryptString(actualValue); + assertEquals(message+": wrong value", expectedClearValue, actualClearPassword); + assertFalse(message+": unexpected hashed value: "+actualValue, actualValue.isHashed()); + assertNull(message+": unexpected clear value: "+actualValue, actualValue.getClearValue()); + break; + + case HASHING: + assertNotNull(message+": no value", actualValue); + assertTrue(message+": value not hashed: "+actualValue, actualValue.isHashed()); + ProtectedStringType expectedPs = new ProtectedStringType(); + expectedPs.setClearValue(expectedClearValue); + assertTrue(message+": hash does not match, expected "+expectedClearValue+", but was "+actualValue, + protector.compare(actualValue, expectedPs)); + assertFalse(message+": unexpected encrypted value: "+actualValue, actualValue.isEncrypted()); + assertNull(message+": unexpected clear value: "+actualValue, actualValue.getClearValue()); + break; + + default: + throw new IllegalArgumentException("Unknown storage "+storageType); + } + + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java index bc544bba91a..0645deca846 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AbstractAsserter.java @@ -19,6 +19,7 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.SimpleObjectResolver; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; @@ -35,6 +36,7 @@ public abstract class AbstractAsserter { private RA returnAsserter; private PrismContext prismContext; private SimpleObjectResolver objectResolver; + private Protector protector; public AbstractAsserter() { this(null); @@ -67,6 +69,14 @@ public void setObjectResolver(SimpleObjectResolver objectResolver) { this.objectResolver = objectResolver; } + protected Protector getProtector() { + return protector; + } + + public void setProtector(Protector protector) { + this.protector = protector; + } + protected String getDetails() { return details; } @@ -98,7 +108,8 @@ protected PrismObject resolveObject(Class type, Str abstract protected String desc(); protected void copySetupTo(AbstractAsserter other) { - other.setPrismContext(getPrismContext()); - other.setObjectResolver(getObjectResolver()); + other.setPrismContext(this.getPrismContext()); + other.setObjectResolver(this.getObjectResolver()); + other.setProtector(this.getProtector()); } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyAccountAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyAccountAsserter.java index cc7a2480508..d27392ca27c 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyAccountAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyAccountAsserter.java @@ -78,6 +78,12 @@ public DummyAccountAsserter assertEnabled() { return this; } + @Override + public DummyAccountAsserter assertLastModifier(String expected) { + super.assertLastModifier(expected); + return this; + } + public DummyAccountAsserter assertFullName(String expected) { assertAttribute(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_FULLNAME_NAME, expected); return this; diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java index 4afbe7e992a..149771584a5 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java @@ -114,6 +114,11 @@ public DummyObjectAsserter assertEnabled() { return this; } + public DummyObjectAsserter assertLastModifier(String expected) { + assertEquals("Wrong lastModifier in " + desc(), expected, getDummyObjectAssertExists().getLastModifier()); + return this; + } + protected String desc() { if (dummyResourceName == null) { return descWithDetails(dummyObject) + " on default dummy resource"; diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java index 8aafdc1f4a5..cf868d8dbdd 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/UserAsserter.java @@ -27,16 +27,22 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.test.IntegrationTestTools; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageTypeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PendingOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowKindType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** * @author semancik @@ -129,6 +135,21 @@ private ActivationType getActivation() { return getObject().asObjectable().getActivation(); } + public UserAsserter assertPassword(String expectedClearPassword) throws SchemaException, EncryptionException { + assertPassword(expectedClearPassword, CredentialsStorageTypeType.ENCRYPTION); + return this; + } + + public UserAsserter assertPassword(String expectedClearPassword, CredentialsStorageTypeType storageType) throws SchemaException, EncryptionException { + CredentialsType creds = getObject().asObjectable().getCredentials(); + assertNotNull("No credentials in "+desc(), creds); + PasswordType password = creds.getPassword(); + assertNotNull("No password in "+desc(), password); + ProtectedStringType protectedActualPassword = password.getValue(); + IntegrationTestTools.assertProtectedString("Password for "+desc(), expectedClearPassword, protectedActualPassword, storageType, getProtector()); + return this; + } + public UserAsserter display() { super.display(); return this;