Skip to content

Commit

Permalink
Store deltas and bulk actions encrypted (MID-5359)
Browse files Browse the repository at this point in the history
Also refactored CryptoUtil to be more generic and maintainable.
Fixed executeChangesAsynchronously to return task with new OID.

(cherry-picked from c42307ca4db8919dc5e236b81a4d897511a45a81 and adapted)
  • Loading branch information
mederly committed May 22, 2019
1 parent 5fe30bb commit 2c1b087
Show file tree
Hide file tree
Showing 8 changed files with 1,212 additions and 223 deletions.

Large diffs are not rendered by default.

@@ -0,0 +1,64 @@
<!--
~ Copyright (c) 2010-2019 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.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" oid="9de76345-0f02-48de-86bf-e7a887cb374a">
<name>Task 1555581798624-0-1</name>
<extension xmlns:se="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3">
<se:executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:pipeline list="true">
<s:search>
<s:type>c:UserType</s:type>
<s:searchFilter>
<q:equal>
<q:path>c:name</q:path>
<q:value>jack</q:value>
</q:equal>
</s:searchFilter>
</s:search>
<s:action>
<s:type>modify</s:type>
<s:parameter>
<s:name>delta</s:name>
<value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="t:ObjectDeltaType">
<t:changeType>modify</t:changeType>
<t:objectType>UserType</t:objectType>
<t:itemDelta>
<t:modificationType>replace</t:modificationType>
<t:path>credentials/password/value</t:path>
<t:value xsi:type="t:ProtectedStringType">
<t:clearValue>pass1234word</t:clearValue>
</t:value>
</t:itemDelta>
</value>
</s:parameter>
</s:action>
</s:pipeline>
</se:executeScript>
</extension>
<taskIdentifier>1555581798624-0-1</taskIdentifier>
<ownerRef oid="00000000-0000-0000-0000-000000000002" relation="org:default" type="c:UserType">
<!-- administrator -->
</ownerRef>
<executionStatus>runnable</executionStatus>
<category>BulkActions</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3</handlerUri>
<recurrence>single</recurrence>
<binding>tight</binding>
</task>
Expand Up @@ -2,6 +2,8 @@

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.prism.xnode.PrimitiveXNode;
import com.evolveum.midpoint.prism.xnode.RootXNode;
import com.evolveum.midpoint.prism.xnode.XNode;
Expand Down Expand Up @@ -60,6 +62,70 @@ public RawType(PrismValue parsed, @NotNull PrismContext prismContext) {
this.parsed = parsed;
}

/**
* Extracts a "real value" from a potential RawType object without expecting any specific type beforehand.
* (Useful e.g. for determining value of xsd:anyType XML property.)
*/
public static Object getValue(Object value) throws SchemaException {
if (value instanceof RawType) {
return ((RawType) value).getValue();
} else {
return value;
}
}

public Object getValue() throws SchemaException {
return getValue(false);
}

/**
* Extracts a "real value" from RawType object without expecting any specific type beforehand.
* If no explicit type is present, assumes xsd:string (and fails if the content is structured).
*/
public Object getValue(boolean store) throws SchemaException {
if (parsed != null) {
return parsed.getRealValue();
}
if (xnode == null) {
return null;
}
if (xnode.getTypeQName() != null) {
TypeDefinition typeDefinition = prismContext.getSchemaRegistry().findTypeDefinitionByType(xnode.getTypeQName());
if (typeDefinition != null && typeDefinition.getCompileTimeClass() != null) {
return storeIfRequested(getParsedRealValue(typeDefinition.getCompileTimeClass()), store);
}
Class<?> javaClass = XsdTypeMapper.getXsdToJavaMapping(xnode.getTypeQName());
if (javaClass != null) {
return storeIfRequested(getParsedRealValue(javaClass), store);
}
}
// unknown or null type -- try parsing as string
if (!(xnode instanceof PrimitiveXNode<?>)) {
throw new SchemaException("Trying to parse non-primitive XNode as type '" + xnode.getTypeQName() + "'");
} else {
return ((PrimitiveXNode) xnode).getStringValue();
}
}

private Object storeIfRequested(Object parsedValue, boolean store) {
if (parsed == null && store) {
if (parsedValue instanceof Containerable) {
parsed = ((Containerable) parsedValue).asPrismContainerValue();
xnode = null;
} else if (parsedValue instanceof Referencable) {
parsed = ((Referencable) parsedValue).asReferenceValue();
xnode = null;
} else if (parsedValue instanceof PolyStringType) {
parsed = new PrismPropertyValue<>(PolyString.toPolyString((PolyStringType) parsedValue)); // hack
xnode = null;
} else if (parsedValue != null) {
parsed = new PrismPropertyValue<>(parsedValue);
xnode = null;
}
}
return parsedValue;
}

@Override
public void revive(PrismContext prismContext) throws SchemaException {
Validate.notNull(prismContext);
Expand Down
Expand Up @@ -37,6 +37,7 @@
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.util.LogfileTestTailer;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.midpoint.xml.ns._public.model.scripting_3.*;
Expand All @@ -51,6 +52,7 @@
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -66,8 +68,11 @@
@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class TestScriptingBasic extends AbstractInitializedModelIntegrationTest {

public static final File TEST_DIR = new File("src/test/resources/scripting");

public static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml");

private static final String DOT_CLASS = TestScriptingBasic.class.getName() + ".";
private static final File LOG_FILE = new File(TEST_DIR, "log.xml");
private static final File SEARCH_FOR_USERS_FILE = new File(TEST_DIR, "search-for-users.xml");
Expand All @@ -84,6 +89,9 @@ public class TestScriptingBasic extends AbstractInitializedModelIntegrationTest
private static final File DELETE_AND_ADD_JACK_FILE = new File(TEST_DIR, "delete-and-add-jack.xml");
private static final File MODIFY_JACK_FILE = new File(TEST_DIR, "modify-jack.xml");
private static final File MODIFY_JACK_BACK_FILE = new File(TEST_DIR, "modify-jack-back.xml");
private static final File MODIFY_JACK_PASSWORD_FILE = new File(TEST_DIR, "modify-jack-password.xml");
private static final File MODIFY_JACK_PASSWORD_TASK_FILE = new File(TEST_DIR, "modify-jack-password-task.xml");
private static final String MODIFY_JACK_PASSWORD_TASK_OID = "9de76345-0f02-48de-86bf-e7a887cb374a";
private static final File RECOMPUTE_JACK_FILE = new File(TEST_DIR, "recompute-jack.xml");
private static final File ASSIGN_TO_JACK_FILE = new File(TEST_DIR, "assign-to-jack.xml");
private static final File ASSIGN_TO_JACK_2_FILE = new File(TEST_DIR, "assign-to-jack-2.xml");
Expand All @@ -97,7 +105,11 @@ public class TestScriptingBasic extends AbstractInitializedModelIntegrationTest
private static final File GENERATE_PASSWORDS_3_FILE = new File(TEST_DIR, "generate-passwords-3.xml");
private static final File ECHO_FILE = new File(TEST_DIR, "echo.xml");

@Autowired
private static final String PASSWORD_PLAINTEXT_FRAGMENT = "pass1234wor";
private static final String PASSWORD_PLAINTEXT_1 = "pass1234wor1";
private static final String PASSWORD_PLAINTEXT_2 = "pass1234wor2";

@Autowired
private ScriptingExpressionEvaluator scriptingExpressionEvaluator;

@Override
Expand All @@ -108,9 +120,16 @@ public void initSystem(Task initTask, OperationResult initResult)

// InternalMonitor.setTraceShadowFetchOperation(true);
// InternalMonitor.setTraceResourceSchemaOperations(true);

DebugUtil.setPrettyPrintBeansAs(PrismContext.LANG_YAML);
}

@Test
@Override
protected File getSystemConfigurationFile() {
return SYSTEM_CONFIGURATION_FILE;
}

@Test
public void test100EmptySequence() throws Exception {
final String TEST_NAME = "test100EmptySequence";
TestUtil.displayTestTitle(this, TEST_NAME);
Expand Down Expand Up @@ -872,7 +891,82 @@ public void test545SearchUserResolveRoleMembershipRef() throws Exception {
}
}

private void assertNoOutputData(ExecutionContext output) {
// MID-5359
@Test
public void test600ModifyJackPasswordInBackground() throws Exception {
final String TEST_NAME = "test600ModifyJackPasswordInBackground";
TestUtil.displayTestTitle(this, TEST_NAME);

// GIVEN
OperationResult result = new OperationResult(DOT_CLASS + TEST_NAME);
PrismProperty<ScriptingExpressionType> expression = parseAnyData(MODIFY_JACK_PASSWORD_FILE);

prepareNotifications();
dummyAuditService.clear();

// WHEN
Task task = taskManager.createTaskInstance();
task.setOwner(getUser(USER_ADMINISTRATOR_OID));
scriptingExpressionEvaluator.evaluateExpressionInBackground(expression.getAnyValue().getValue(), task, result);
waitForTaskFinish(task.getOid(), false);
task.refresh(result);

// THEN
display(task.getResult());
TestUtil.assertSuccess(task.getResult());
PrismObject<UserType> jack = getUser(USER_JACK_OID);
display("jack after password change", jack);
assertEncryptedUserPassword(jack, PASSWORD_PLAINTEXT_1);

String xml = prismContext.xmlSerializer().serialize(task.getTaskPrismObject());
display("task", xml);
assertFalse("Plaintext password is present in the task", xml.contains(PASSWORD_PLAINTEXT_FRAGMENT));

display("Dummy transport", dummyTransport);
display("Audit", dummyAuditService);
}

// MID-5359
@Test
public void test610ModifyJackPasswordImportingTask() throws Exception {
final String TEST_NAME = "test610ModifyJackPasswordImportingTask";
TestUtil.displayTestTitle(this, TEST_NAME);

// GIVEN
Task opTask = taskManager.createTaskInstance(DOT_CLASS + TEST_NAME);
opTask.setOwner(getUser(USER_ADMINISTRATOR_OID));
OperationResult result = opTask.getResult();

prepareNotifications();
dummyAuditService.clear();

// WHEN
FileInputStream stream = new FileInputStream(MODIFY_JACK_PASSWORD_TASK_FILE);
modelService.importObjectsFromStream(stream, null, opTask, result);
stream.close();

result.computeStatus();
assertSuccess(result);

waitForTaskFinish(MODIFY_JACK_PASSWORD_TASK_OID, false);
Task task = taskManager.getTask(MODIFY_JACK_PASSWORD_TASK_OID, result);

// THEN
display(task.getResult());
TestUtil.assertSuccess(task.getResult());
PrismObject<UserType> jack = getUser(USER_JACK_OID);
display("jack after password change", jack);
assertEncryptedUserPassword(jack, PASSWORD_PLAINTEXT_2);

String xml = prismContext.xmlSerializer().serialize(task.getTaskPrismObject());
display("task", xml);
assertFalse("Plaintext password is present in the task", xml.contains(PASSWORD_PLAINTEXT_FRAGMENT));

display("Dummy transport", dummyTransport);
display("Audit", dummyAuditService);
}

private void assertNoOutputData(ExecutionContext output) {
assertTrue("Script returned unexpected data", output.getFinalOutput() == null || output.getFinalOutput().getData().isEmpty());
}

Expand Down
@@ -0,0 +1,64 @@
<!--
~ Copyright (c) 2010-2019 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.
-->

<task xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3" oid="9de76345-0f02-48de-86bf-e7a887cb374a">
<name>Task 1555581798624-0-1</name>
<extension xmlns:se="http://midpoint.evolveum.com/xml/ns/public/model/scripting/extension-3">
<se:executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:pipeline list="true">
<s:search>
<s:type>c:UserType</s:type>
<s:searchFilter>
<q:equal>
<q:path>c:name</q:path>
<q:value>jack</q:value>
</q:equal>
</s:searchFilter>
</s:search>
<s:action>
<s:type>modify</s:type>
<s:parameter>
<s:name>delta</s:name>
<value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="t:ObjectDeltaType">
<t:changeType>modify</t:changeType>
<t:objectType>UserType</t:objectType>
<t:itemDelta>
<t:modificationType>replace</t:modificationType>
<t:path>credentials/password/value</t:path>
<t:value xsi:type="t:ProtectedStringType">
<t:clearValue>pass1234wor2</t:clearValue>
</t:value>
</t:itemDelta>
</value>
</s:parameter>
</s:action>
</s:pipeline>
</se:executeScript>
</extension>
<taskIdentifier>1555581798624-0-1</taskIdentifier>
<ownerRef oid="00000000-0000-0000-0000-000000000002" relation="org:default" type="c:UserType">
<!-- administrator -->
</ownerRef>
<executionStatus>runnable</executionStatus>
<category>BulkActions</category>
<handlerUri>http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3</handlerUri>
<recurrence>single</recurrence>
<binding>tight</binding>
</task>

0 comments on commit 2c1b087

Please sign in to comment.