/*
* 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.model.intest.password;
import static com.evolveum.midpoint.test.IntegrationTestTools.display;
import static org.testng.AssertJUnit.*;
import java.io.File;
import java.util.Collection;
import javax.xml.datatype.XMLGregorianCalendar;
import com.evolveum.icf.dummy.resource.DummyResource;
import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder;
import com.evolveum.midpoint.test.DummyResourceContoller;
import com.evolveum.midpoint.test.util.MidPointTestConstants;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;
import com.evolveum.icf.dummy.resource.ConflictException;
import com.evolveum.icf.dummy.resource.SchemaViolationException;
import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.internals.InternalsConfig;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
/**
* Sketchy tests for deprecated password policy settings.
* Modified subset of AbstractPasswordTest. Just makes sure that the
* password policy configured in a deprecated way is applied and that it
* roughly works. It is not meant to be comprehensive.
*
* @author semancik
*/
@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"})
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class TestPasswordDeprecated extends AbstractInitializedModelIntegrationTest {
protected static final String USER_PASSWORD_0_CLEAR = "d3adM3nT3llN0Tal3s";
protected static final String USER_PASSWORD_JACK_CLEAR = "12jAcK34"; // contains username
protected static final String USER_PASSWORD_SPARROW_CLEAR = "saRRow123"; // contains familyName
protected static final String USER_PASSWORD_VALID_1 = "abcd123";
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 File TEST_DIR = AbstractPasswordTest.TEST_DIR;
protected static final File PASSWORD_POLICY_DEPRECATED_FILE = new File(TEST_DIR, "password-policy-deprecated.xml");
protected static final String PASSWORD_POLICY_DEPRECATED_OID = "44bb6516-0d61-11e7-af71-73b639b25b04";
protected String accountJackOid;
protected XMLGregorianCalendar lastPasswordChangeStart;
protected XMLGregorianCalendar lastPasswordChangeEnd;
@Override
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
super.initSystem(initTask, initResult);
importObjectFromFile(PASSWORD_POLICY_DEPRECATED_FILE);
setGlobalSecurityPolicy(null, initResult);
login(USER_ADMINISTRATOR_USERNAME);
}
@Test
public void test051ModifyUserJackPassword() throws Exception {
final String TEST_NAME = "test051ModifyUserJackPassword";
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(AbstractPasswordTest.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
XMLGregorianCalendar startCal = clock.currentTimeXMLGregorianCalendar();
// WHEN
TestUtil.displayWhen(TEST_NAME);
modifyUserChangePassword(USER_JACK_OID, USER_PASSWORD_0_CLEAR, task, result);
// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertSuccess("executeChanges result", result);
XMLGregorianCalendar endCal = clock.currentTimeXMLGregorianCalendar();
PrismObject<UserType> userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack, "Jack Sparrow");
assertUserPassword(userJack, USER_PASSWORD_0_CLEAR);
assertPasswordMetadata(userJack, false, startCal, endCal);
// Password policy is not active yet. No history should be kept.
assertPasswordHistoryEntries(userJack);
}
@Test
public void test100ModifyUserJackAssignAccount() throws Exception {
final String TEST_NAME = "test100ModifyUserJackAssignAccount";
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
// WHEN
assignAccount(USER_JACK_OID, RESOURCE_DUMMY_OID, null, task, result);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
PrismObject<UserType> userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertUserJack(userJack);
accountJackOid = getSingleLinkOid(userJack);
// Check shadow
PrismObject<ShadowType> accountShadow = repositoryService.getObject(ShadowType.class, accountJackOid, null, result);
assertDummyAccountShadowRepo(accountShadow, accountJackOid, "jack");
// Check account
PrismObject<ShadowType> accountModel = modelService.getObject(ShadowType.class, accountJackOid, null, task, result);
assertDummyAccountShadowModel(accountModel, accountJackOid, "jack", "Jack Sparrow");
// Check account in dummy resource
assertDefaultDummyAccount(ACCOUNT_JACK_DUMMY_USERNAME, "Jack Sparrow", true);
assertDummyPassword(null, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_0_CLEAR);
}
@Test
public void test200ApplyPasswordPolicy() throws Exception {
final String TEST_NAME = "test200ApplyPasswordPolicy";
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
PrismReferenceValue passPolicyRef = new PrismReferenceValue(PASSWORD_POLICY_DEPRECATED_OID, ValuePolicyType.COMPLEX_TYPE);
// WHEN
modifyObjectReplaceReference(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(),
new ItemPath(SystemConfigurationType.F_GLOBAL_PASSWORD_POLICY_REF),
task, result, passPolicyRef);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
}
/**
* Change to password that complies with password policy.
*/
@Test
public void test210ModifyUserJackPasswordGood() throws Exception {
doTestModifyUserJackPasswordSuccessWithHistory("test210ModifyUserJackPasswordGood",
USER_PASSWORD_VALID_1, USER_PASSWORD_0_CLEAR);
}
/**
* Reconcile user. Nothing should be changed.
* MID-3567
*/
@Test
public void test212ReconcileUserJack() throws Exception {
final String TEST_NAME = "test212ReconcileUserJack";
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
// WHEN
reconcileUser(USER_JACK_OID, task, result);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
assertJackPasswordsWithHistory(USER_PASSWORD_VALID_1, USER_PASSWORD_0_CLEAR);
}
/**
* Recompute user. Nothing should be changed.
* MID-3567
*/
@Test
public void test214RecomputeUserJack() throws Exception {
final String TEST_NAME = "test214RecomputeUserJack";
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
// WHEN
recomputeUser(USER_JACK_OID, task, result);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
assertJackPasswordsWithHistory(USER_PASSWORD_VALID_1, USER_PASSWORD_0_CLEAR);
}
/**
* Change to password that violates the password policy (but is still OK for yellow resource).
*/
@Test
public void test220ModifyUserJackPasswordBadA() throws Exception {
doTestModifyUserJackPasswordFailureWithHistory("test220ModifyUserJackPasswordBadA",
USER_PASSWORD_0_CLEAR, USER_PASSWORD_VALID_1, USER_PASSWORD_0_CLEAR);
}
/**
* Change to password that violates the password policy (contains username)
* MID-1657
*/
@Test
public void test224ModifyUserJackPasswordBadJack() throws Exception {
doTestModifyUserJackPasswordFailureWithHistory("test224ModifyUserJackPasswordBadJack",
USER_PASSWORD_JACK_CLEAR, USER_PASSWORD_VALID_1, USER_PASSWORD_0_CLEAR);
}
/**
* Change to password that complies with password policy. Again. See that
* the change is applied correctly and that it is included in the history.
*/
@Test
public void test230ModifyUserJackPasswordGoodAgain() throws Exception {
doTestModifyUserJackPasswordSuccessWithHistory("test230ModifyUserJackPasswordGoodAgain",
USER_PASSWORD_VALID_2, USER_PASSWORD_0_CLEAR, USER_PASSWORD_VALID_1);
}
/**
* Change to password that complies with password policy. Again.
* This time there are enough passwords in the history. So the history should
* be truncated.
*/
@Test
public void test240ModifyUserJackPasswordGoodAgainOverHistory() throws Exception {
doTestModifyUserJackPasswordSuccessWithHistory("test240ModifyUserJackPasswordGoodAgainOverHistory",
USER_PASSWORD_VALID_3, USER_PASSWORD_VALID_1, USER_PASSWORD_VALID_2);
}
/**
* Change to password that complies with password policy. Again.
* This time there are enough passwords in the history. So the history should
* be truncated.
*/
@Test
public void test241ModifyUserJackPasswordGoodAgainOverHistoryAgain() throws Exception {
doTestModifyUserJackPasswordSuccessWithHistory("test241ModifyUserJackPasswordGoodAgainOverHistoryAgain",
USER_PASSWORD_VALID_4, USER_PASSWORD_VALID_2, USER_PASSWORD_VALID_3);
}
/**
* Reuse old password. Now the password should be out of the history, so
* the system should allow its reuse.
*/
@Test
public void test248ModifyUserJackPasswordGoodReuse() throws Exception {
doTestModifyUserJackPasswordSuccessWithHistory("test248ModifyUserJackPasswordGoodReuse",
USER_PASSWORD_VALID_1, USER_PASSWORD_VALID_3, USER_PASSWORD_VALID_4);
}
private void doTestModifyUserJackPasswordSuccessWithHistory(final String TEST_NAME,
String newPassword, String... expectedPasswordHistory) throws Exception {
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
lastPasswordChangeStart = clock.currentTimeXMLGregorianCalendar();
// WHEN
modifyUserChangePassword(USER_JACK_OID, newPassword, task, result);
// THEN
result.computeStatus();
TestUtil.assertSuccess(result);
lastPasswordChangeEnd = clock.currentTimeXMLGregorianCalendar();
assertJackPasswordsWithHistory(newPassword, expectedPasswordHistory);
}
private void doTestModifyUserJackPasswordFailureWithHistory(final String TEST_NAME,
String newPassword, String oldPassword, String... expectedPasswordHistory) throws Exception {
TestUtil.displayTestTile(this, TEST_NAME);
// GIVEN
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();
assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL);
try {
// WHEN
modifyUserChangePassword(USER_JACK_OID, newPassword, task, result);
AssertJUnit.fail("Unexpected success");
} catch (PolicyViolationException e) {
// This is expected
display("Exected exception", e);
}
// THEN
result.computeStatus();
TestUtil.assertFailure(result);
assertJackPasswordsWithHistory(oldPassword, expectedPasswordHistory);
}
private void assertJackPasswordsWithHistory(String expectedCurrentPassword, String... expectedPasswordHistory) throws Exception {
PrismObject<UserType> userJack = getUser(USER_JACK_OID);
display("User after change execution", userJack);
assertLinks(userJack, 1);
assertUserPassword(userJack, expectedCurrentPassword);
assertPasswordMetadata(userJack, false, lastPasswordChangeStart, lastPasswordChangeEnd);
assertDummyPassword(null, ACCOUNT_JACK_DUMMY_USERNAME, expectedCurrentPassword);
assertPasswordHistoryEntries(userJack, expectedPasswordHistory);
}
}