/* * 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.impl.lens; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import java.io.File; import java.io.IOException; import javax.xml.bind.JAXBException; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; 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.midpoint.model.common.stringpolicy.StringPolicyUtils; import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyProcessor; import com.evolveum.midpoint.model.impl.AbstractInternalModelIntegrationTest; import com.evolveum.midpoint.model.impl.lens.projector.credentials.CredentialPolicyEvaluator; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.StringLimitType; import com.evolveum.midpoint.xml.ns._public.common.common_3.StringPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; @ContextConfiguration(locations = {"classpath:ctx-model-test-main.xml"}) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) public class TestPasswordPolicy extends AbstractInternalModelIntegrationTest { public static final File TEST_DIR = new File("src/test/resources/lens/ppolicy/"); private static final transient Trace LOGGER = TraceManager.getTrace(TestPasswordPolicy.class); private static final String USER_AB_USERNAME = "ab"; private static final String USER_AB_FULL_NAME = "Ad Fel"; private static final String USER_AB_GIVEN_NAME = "Ad"; private static final String USER_AB_FAMILY_NAME = "Fel"; private static final String USER_AB_ADDITIONAL_NAME = "x"; private static final int USERNAME_ATTEMPTS = 200; private static final int USER_PROPS_ATTEMPTS = 5000; @Autowired(required = true) private ValuePolicyProcessor valuePolicyProcessor; @Test public void stringPolicyUtilsMinimalTest() throws JAXBException, SchemaException, IOException { File file = new File(TEST_DIR, "password-policy-minimal.xml"); ValuePolicyType pp = (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); StringPolicyType sp = pp.getStringPolicy(); StringPolicyUtils.normalize(sp); AssertJUnit.assertNotNull(sp.getCharacterClass()); AssertJUnit.assertNotNull(sp.getLimitations().getLimit()); AssertJUnit.assertTrue(-1 == sp.getLimitations().getMaxLength()); AssertJUnit.assertTrue(0 == sp.getLimitations().getMinLength()); AssertJUnit.assertTrue(0 == " !\"#$%&'()*+,-.01234567890:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" .compareTo(sp.getCharacterClass().getValue())); } @Test public void stringPolicyUtilsComplexTest() { final String TEST_NAME = "stringPolicyUtilsComplexTest"; TestUtil.displayTestTile(TEST_NAME); File file = new File(TEST_DIR, "password-policy-complex.xml"); ValuePolicyType pp = null; try { pp = (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); } catch (Exception e) { e.printStackTrace(); } StringPolicyType sp = pp.getStringPolicy(); StringPolicyUtils.normalize(sp); } @Test public void testPasswordGeneratorComplexNegative() throws Exception { final String TEST_NAME = "testPasswordGeneratorComplexNegative"; TestUtil.displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); File file = new File(TEST_DIR, "password-policy-complex.xml"); ValuePolicyType pp = (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); // Make switch some cosistency pp.getStringPolicy().getLimitations().setMinLength(2); pp.getStringPolicy().getLimitations().setMinUniqueChars(5); // WHEN TestUtil.displayWhen(TEST_NAME); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, null, TEST_NAME, task, result); // THEN TestUtil.displayThen(TEST_NAME); display("Generated password", psswd); result.computeStatus(); AssertJUnit.assertTrue(result.isAcceptable()); assertNotNull(psswd); // Switch to all must be first :-) to test if there is error for (StringLimitType l : pp.getStringPolicy().getLimitations().getLimit()) { l.setMustBeFirst(true); } LOGGER.info("Negative testing: passwordGeneratorComplexTest"); try { valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, null, TEST_NAME, task, result); assertNotReached(); } catch (ExpressionEvaluationException e) { result.computeStatus(); TestUtil.assertFailure(result); } } @Test public void testPasswordGeneratorComplex() throws Exception { passwordGeneratorTest("testPasswordGeneratorComplex", "password-policy-complex.xml"); } @Test public void testPasswordGeneratorLong() throws Exception { passwordGeneratorTest("testPasswordGeneratorLong", "password-policy-long.xml"); } @Test public void testPasswordGeneratorNumeric() throws Exception { passwordGeneratorTest("testPasswordGeneratorNumeric", "password-policy-numeric.xml"); } @Test public void testValueGeneratorMustBeFirst() throws Exception { passwordGeneratorTest("testValueGeneratorMustBeFirst", "value-policy-must-be-first.xml"); } @Test public void testValueGenerateRandomPin() throws Exception { final String TEST_NAME = "testValueGenerateRandomPin"; TestUtil.displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); ValuePolicyType pp = parsePasswordPolicy("value-policy-random-pin.xml"); // WHEN TestUtil.displayWhen(TEST_NAME); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, null, TEST_NAME, task, result); // THEN TestUtil.displayThen(TEST_NAME); display("Generated password", psswd); result.computeStatus(); TestUtil.assertSuccess(result); assertNotNull(psswd); assertPassword(psswd, pp); } @Test public void testValueGenerate() throws Exception { final String TEST_NAME = "testValueGenerate"; TestUtil.displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); ValuePolicyType pp = parsePasswordPolicy("value-policy-generate.xml"); // WHEN TestUtil.displayWhen(TEST_NAME); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, null, TEST_NAME, task, result); // THEN TestUtil.displayThen(TEST_NAME); display("Generated password", psswd); result.computeStatus(); TestUtil.assertSuccess(result); assertNotNull(psswd); assertPassword(psswd, pp); } @Test public void testValueGenerateEmpty() throws Exception { final String TEST_NAME = "testValueGenerateEmpty"; TestUtil.displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); File file = new File(TEST_DIR, "value-policy-generate-empty.xml"); LOGGER.info("Positive testing {}: {}", "testValueGenerate", "value-policy-generate-empty.xml"); ValuePolicyType pp = (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); // WHEN TestUtil.displayWhen(TEST_NAME); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, null, TEST_NAME, task, result); // THEN TestUtil.displayThen(TEST_NAME); display("Generated password", psswd); result.computeStatus(); TestUtil.assertSuccess(result); assertNotNull(psswd); assertPassword(psswd, pp); } public void passwordGeneratorTest(final String TEST_NAME, String policyFilename) throws JAXBException, SchemaException, IOException, ExpressionEvaluationException, ObjectNotFoundException { TestUtil.displayTestTile(TEST_NAME); Task task = createTask(TEST_NAME); OperationResult result = task.getResult(); File file = new File(TEST_DIR, policyFilename); LOGGER.info("Positive testing {}: {}", TEST_NAME, policyFilename); ValuePolicyType pp = (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); String psswd; // generate minimal size passwd for (int i = 0; i < 100; i++) { psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, null, TEST_NAME, task, result); LOGGER.info("Generated password:" + psswd); result.computeStatus(); if (!result.isSuccess()) { LOGGER.info("Result:" + result.debugDump()); AssertJUnit.fail("Password generator failed:\n"+result.debugDump()); } assertNotNull(psswd); assertPassword(psswd, pp); } // genereata to meet as possible LOGGER.info("-------------------------"); // Generate up to possible for (int i = 0; i < 100; i++) { psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, false, null, TEST_NAME, task, result); LOGGER.info("Generated password:" + psswd); result.computeStatus(); if (!result.isSuccess()) { LOGGER.info("Result:" + result.debugDump()); } AssertJUnit.assertTrue(result.isSuccess()); assertNotNull(psswd); } } private void assertPassword(String passwd, ValuePolicyType pp) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { assertPassword(passwd, pp, null); } private <O extends ObjectType> void assertPassword(String passwd, ValuePolicyType pp, PrismObject<O> object) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { Task task = createTask("assertPassword"); OperationResult result = task.getResult(); boolean isValid = valuePolicyProcessor.validateValue(passwd, pp, object, "assertPassword", task, result); result.computeStatus(); if (!result.isSuccess()) { AssertJUnit.fail(result.debugDump()); } AssertJUnit.assertTrue("Password not valid (but result is success)", isValid); } @Test public void passwordValidationTestComplex() throws Exception { final String TEST_NAME = "passwordValidationTestComplex"; TestUtil.displayTestTile(TEST_NAME); ValuePolicyType pp = parsePasswordPolicy("password-policy-complex.xml"); // WHEN, THEN AssertJUnit.assertTrue(pwdValidHelper("582a**A", pp)); AssertJUnit.assertFalse(pwdValidHelper("58", pp)); AssertJUnit.assertFalse(pwdValidHelper("333a**aGaa", pp)); AssertJUnit.assertFalse(pwdValidHelper("AAA4444", pp)); } @Test public void passwordValidationTestTri() throws Exception { final String TEST_NAME = "passwordValidationTestTri"; TestUtil.displayTestTile(TEST_NAME); ValuePolicyType pp = parsePasswordPolicy("password-policy-tri.xml"); // WHEN, THEN AssertJUnit.assertTrue(pwdValidHelper("Password1", pp)); AssertJUnit.assertFalse(pwdValidHelper("password1", pp)); // no capital letter AssertJUnit.assertFalse(pwdValidHelper("1PASSWORD", pp)); // no lowecase letter AssertJUnit.assertFalse(pwdValidHelper("Password", pp)); // no numeral AssertJUnit.assertFalse(pwdValidHelper("Pa1", pp)); // too short AssertJUnit.assertFalse(pwdValidHelper("PPPPPPPPPPPPPPPPPPPPPPPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa11111111111111111111111111111111111111111111111111111111111111111111", pp)); // too long } /** * MID-1657 */ @Test public void testUsername() throws Exception { final String TEST_NAME = "testUsername"; TestUtil.displayTestTile(TEST_NAME); PrismObject<UserType> user = createUserAb(); ValuePolicyType pp = parsePasswordPolicy("password-policy-username.xml"); // WHEN TestUtil.displayWhen(TEST_NAME); for (int i = 0; i < USERNAME_ATTEMPTS; i++) { Task task = createTask(TEST_NAME+":"+i); OperationResult result = task.getResult(); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, user, TEST_NAME, task, result); display("Generated password ("+i+")", psswd); result.computeStatus(); TestUtil.assertSuccess(result); assertNotNull(psswd); assertPassword(psswd, pp, user); assertFalse("Generated password that matches the username: "+psswd, psswd.equals(USER_AB_USERNAME)); } // THEN TestUtil.displayThen(TEST_NAME); } /** * MID-1657 */ @Test public void testUserProps() throws Exception { final String TEST_NAME = "testUserProps"; TestUtil.displayTestTile(TEST_NAME); PrismObject<UserType> user = createUserAb(); display("User", user); ValuePolicyType pp = parsePasswordPolicy("password-policy-props.xml"); // WHEN TestUtil.displayWhen(TEST_NAME); for (int i = 0; i < USER_PROPS_ATTEMPTS; i++) { Task task = createTask(TEST_NAME+":"+i); OperationResult result = task.getResult(); String psswd = valuePolicyProcessor.generate(SchemaConstants.PATH_PASSWORD_VALUE, pp.getStringPolicy(), 10, true, user, TEST_NAME, task, result); display("Generated password ("+i+")", psswd); result.computeStatus(); TestUtil.assertSuccess(result); assertNotNull(psswd); assertPassword(psswd, pp, user); assertNotContains(psswd, USER_AB_USERNAME); assertNotContains(psswd, USER_AB_GIVEN_NAME); assertNotContains(psswd, USER_AB_ADDITIONAL_NAME); } // THEN TestUtil.displayThen(TEST_NAME); } private PrismObject<UserType> createUserAb() throws SchemaException { PrismObject<UserType> user = createUser(USER_AB_USERNAME, USER_AB_GIVEN_NAME, USER_AB_FAMILY_NAME, true); user.asObjectable().setAdditionalName(createPolyStringType(USER_AB_ADDITIONAL_NAME)); return user; } private void assertNotContains(String psswd, String val) { assertFalse("Generated password "+psswd+" contains value "+val, StringUtils.containsIgnoreCase(psswd, val)); } private ValuePolicyType parsePasswordPolicy(String filename) throws SchemaException, IOException { File file = new File(TEST_DIR, filename); return (ValuePolicyType) PrismTestUtil.parseObject(file).asObjectable(); } private boolean pwdValidHelper(String password, ValuePolicyType pp) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { Task task = createTask("pwdValidHelper"); OperationResult result = task.getResult(); valuePolicyProcessor.validateValue(password, pp, null, "pwdValidHelper", task, result); result.computeStatus(); String msg = "-> Policy "+pp.getName()+", password '"+password+"': "+result.getStatus(); System.out.println(msg); LOGGER.info(msg); LOGGER.trace(result.debugDump()); return (result.isSuccess()); } }