/** * Copyright (c) 2016-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.security; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.io.File; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.LockedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.testng.AssertJUnit; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.evolveum.midpoint.common.Clock; import com.evolveum.midpoint.model.api.AuthenticationEvaluator; import com.evolveum.midpoint.model.api.context.AbstractAuthenticationContext; import com.evolveum.midpoint.model.impl.AbstractInternalModelIntegrationTest; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.Authorization; import com.evolveum.midpoint.security.api.ConnectionEnvironment; import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.UserProfileService; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.MidPointAsserts; import com.evolveum.midpoint.test.util.MidPointTestConstants; import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.PolicyViolationException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractCredentialType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LockoutStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.LoginEventType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; /** * @author semancik * */ @ContextConfiguration(locations = {"classpath:ctx-model-test-main.xml"}) @DirtiesContext @Listeners({ com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class }) public abstract class TestAbstractAuthenticationEvaluator<V, AC extends AbstractAuthenticationContext, T extends AuthenticationEvaluator<AC>> extends AbstractInternalModelIntegrationTest { protected static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "security"); private static final Trace LOGGER = TraceManager.getTrace(TestAbstractAuthenticationEvaluator.class); protected static final String USER_GUYBRUSH_PASSWORD = "XmarksTHEspot"; @Autowired(required=true) private UserProfileService userProfileService; @Autowired(required = true) private Clock clock; /* (non-Javadoc) * @see com.evolveum.midpoint.test.AbstractIntegrationTest#initSystem(com.evolveum.midpoint.task.api.Task, com.evolveum.midpoint.schema.result.OperationResult) */ public abstract T getAuthenticationEvaluator(); public abstract AC getAuthenticationContext(String username, V value); public abstract V getGoodPasswordJack(); public abstract V getBadPasswordJack(); public abstract V getGoodPasswordGuybrush(); public abstract V getBadPasswordGuybrush(); public abstract V get103EmptyPasswordJack(); public abstract AbstractCredentialType getCredentialUsedForAuthentication(UserType user); public abstract QName getCredentialType(); public abstract void modifyUserCredential(Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, ObjectAlreadyExistsException, PolicyViolationException, SecurityViolationException; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); ((AuthenticationEvaluatorImpl)getAuthenticationEvaluator()).userProfileService = new UserProfileService() { @Override public <F extends FocusType, O extends ObjectType> PrismObject<F> resolveOwner(PrismObject<O> object) { return userProfileService.resolveOwner(object); } @Override public void updateUser(MidPointPrincipal principal) { userProfileService.updateUser(principal); } @Override public MidPointPrincipal getPrincipal(PrismObject<UserType> user) throws SchemaException { MidPointPrincipal principal = userProfileService.getPrincipal(user); addFakeAuthorization(principal); return principal; } @Override public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException, SchemaException { MidPointPrincipal principal = userProfileService.getPrincipal(username); addFakeAuthorization(principal); return principal; } }; } @Test public void test000Sanity() throws Exception { final String TEST_NAME = "test000Sanity"; TestUtil.displayTestTile(TEST_NAME); assertNotNull(getAuthenticationEvaluator()); MidPointPrincipal principal = userProfileService.getPrincipal(USER_JACK_USERNAME); assertPrincipalJack(principal); } @Test public void test100PasswordLoginGoodPasswordJack() throws Exception { final String TEST_NAME = "test100PasswordLoginGoodPasswordJack"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_JACK_USERNAME); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); } @Test public void test101PasswordLoginBadPasswordJack() throws Exception { final String TEST_NAME = "test101PasswordLoginBadPasswordJack"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 1); assertUserLockout(userAfter, LockoutStatusType.NORMAL); assertLastFailedLogin(userAfter, startTs, endTs); } @Test public void test102PasswordLoginNullPasswordJack() throws Exception { final String TEST_NAME = "test102PasswordLoginNullPasswordJack"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, null)); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertPasswordEncodingException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 1); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } @Test public void test103PasswordLoginEmptyPasswordJack() throws Exception { final String TEST_NAME = "test103PasswordLoginEmptyPasswordJack"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, get103EmptyPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertPasswordEncodingException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 1); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } @Test public void test105PasswordLoginNullUsernameNullPassword() throws Exception { final String TEST_NAME = "test105PasswordLoginNullUsernameNullPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(null, null)); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertPasswordEncodingException(e, null); } } @Test public void test106PasswordLoginEmptyUsernameBadPassword() throws Exception { final String TEST_NAME = "test106PasswordLoginEmptyUsernameBadPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext("", getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (UsernameNotFoundException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertNoUserException(e, null); } } @Test public void test107PasswordLoginBadUsernameBadPassword() throws Exception { final String TEST_NAME = "test107PasswordLoginBadUsernameBadPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext("NoSuchUser", getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (UsernameNotFoundException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertNoUserException(e, null); } } /** * Wait for 5 minutes. The failed login count should reset after 3 minutes. Therefore bad login * count should be one after we try to make a bad login. */ @Test public void test125PasswordLoginBadPasswordJackAfterLockoutFailedAttemptsDuration() throws Exception { final String TEST_NAME = "test125PasswordLoginBadPasswordJackAfterLockoutFailedAttemptsDuration"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("PT5M"); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 1); assertLastFailedLogin(userAfter, startTs, endTs); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } @Test public void test130PasswordLoginLockout() throws Exception { final String TEST_NAME = "test130PasswordLoginLockout"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } PrismObject<UserType> userBetween = getUser(USER_JACK_OID); display("user after", userBetween); assertFailedLogins(userBetween, 2); assertUserLockout(userBetween, LockoutStatusType.NORMAL); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); // THEN TestUtil.displayThen(TEST_NAME); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 3); assertLastFailedLogin(userAfter, startTs, endTs); assertUserLockout(userAfter, LockoutStatusType.LOCKED); } @Test public void test132PasswordLoginLockedoutGoodPassword() throws Exception { final String TEST_NAME = "test132PasswordLoginLockedoutGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (LockedException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertLockedException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 3); assertUserLockout(userAfter, LockoutStatusType.LOCKED); } @Test public void test133PasswordLoginLockedoutBadPassword() throws Exception { final String TEST_NAME = "test133PasswordLoginLockedoutBadPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (LockedException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); // this is important. The exception should give no indication whether the password is // good or bad assertLockedException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 3); assertUserLockout(userAfter, LockoutStatusType.LOCKED); } @Test public void test135PasswordLoginLockedoutLockExpires() throws Exception { final String TEST_NAME = "test135PasswordLoginLockedoutLockExpires"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("PT30M"); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_JACK_USERNAME); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } @Test public void test136PasswordLoginLockoutAgain() throws Exception { final String TEST_NAME = "test136PasswordLoginLockoutAgain"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } PrismObject<UserType> userBetween = getUser(USER_JACK_OID); display("user after", userBetween); assertFailedLogins(userBetween, 1); assertUserLockout(userBetween, LockoutStatusType.NORMAL); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } userBetween = getUser(USER_JACK_OID); display("user after", userBetween); assertFailedLogins(userBetween, 2); assertUserLockout(userBetween, LockoutStatusType.NORMAL); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getBadPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_JACK_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 3); assertLastFailedLogin(userAfter, startTs, endTs); assertUserLockout(userAfter, LockoutStatusType.LOCKED); } @Test public void test137PasswordLoginLockedoutGoodPasswordAgain() throws Exception { final String TEST_NAME = "test137PasswordLoginLockedoutGoodPasswordAgain"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (LockedException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertLockedException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 3); assertUserLockout(userAfter, LockoutStatusType.LOCKED); } @Test public void test138UnlockUserGoodPassword() throws Exception { final String TEST_NAME = "test138UnlockUserGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); ConnectionEnvironment connEnv = createConnectionEnvironment(); // WHEN TestUtil.displayWhen(TEST_NAME); modifyUserReplace(USER_JACK_OID, SchemaConstants.PATH_ACTIVATION_LOCKOUT_STATUS, task, result, LockoutStatusType.NORMAL); // THEN TestUtil.displayThen(TEST_NAME); PrismObject<UserType> userBetween = getUser(USER_JACK_OID); display("user after", userBetween); assertFailedLogins(userBetween, 0); assertUserLockout(userBetween, LockoutStatusType.NORMAL); // GIVEN XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_JACK_USERNAME); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } /** * MID-2862 */ @Test public void test139TryToLockByModelService() throws Exception { final String TEST_NAME = "test139TryToLockByModelService"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); // WHEN TestUtil.displayWhen(TEST_NAME); try { modifyUserReplace(USER_JACK_OID, SchemaConstants.PATH_ACTIVATION_LOCKOUT_STATUS, task, result, LockoutStatusType.LOCKED); AssertJUnit.fail("Unexpected success"); } catch (SchemaException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertUserLockout(userAfter, LockoutStatusType.NORMAL); } @Test public void test150PasswordLoginDisabledGoodPassword() throws Exception { final String TEST_NAME = "test150PasswordLoginDisabledGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, ACTIVATION_ADMINISTRATIVE_STATUS_PATH, task, result, ActivationStatusType.DISABLED); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test152PasswordLoginEnabledGoodPassword() throws Exception { final String TEST_NAME = "test152PasswordLoginEnabledGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, ACTIVATION_ADMINISTRATIVE_STATUS_PATH, task, result, ActivationStatusType.ENABLED); loginJackGoodPasswordExpectSuccess(TEST_NAME, task, result); } @Test public void test154PasswordLoginNotValidYetGoodPassword() throws Exception { final String TEST_NAME = "test154PasswordLoginNotValidYetGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); XMLGregorianCalendar validFrom = XmlTypeConverter.addDuration(clock.currentTimeXMLGregorianCalendar(), "PT1H"); XMLGregorianCalendar validTo = XmlTypeConverter.addDuration(clock.currentTimeXMLGregorianCalendar(), "P2D"); modifyUserReplace(USER_JACK_OID, ACTIVATION_ADMINISTRATIVE_STATUS_PATH, task, result); modifyUserReplace(USER_JACK_OID, ACTIVATION_VALID_FROM_PATH, task, result, validFrom); modifyUserReplace(USER_JACK_OID, ACTIVATION_VALID_TO_PATH, task, result, validTo); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test155PasswordLoginValidGoodPassword() throws Exception { final String TEST_NAME = "test155PasswordLoginValidGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("PT2H"); Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); loginJackGoodPasswordExpectSuccess(TEST_NAME, task, result); } @Test public void test156PasswordLoginNotValidAnyLongerGoodPassword() throws Exception { final String TEST_NAME = "test156PasswordLoginNotValidAnyLongerGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("P2D"); Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test159PasswordLoginNoLongerValidEnabledGoodPassword() throws Exception { final String TEST_NAME = "test159PasswordLoginNoLongerValidEnabledGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, ACTIVATION_ADMINISTRATIVE_STATUS_PATH, task, result, ActivationStatusType.ENABLED); loginJackGoodPasswordExpectSuccess(TEST_NAME, task, result); } @Test public void test160PasswordLoginLifecycleActiveGoodPassword() throws Exception { final String TEST_NAME = "test160PasswordLoginLifecycleActiveGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_ACTIVE); loginJackGoodPasswordExpectSuccess(TEST_NAME, task, result); } @Test public void test162PasswordLoginLifecycleDraftGoodPassword() throws Exception { final String TEST_NAME = "test162PasswordLoginLifecycleDraftGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_DRAFT); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test164PasswordLoginLifecycleDeprecatedGoodPassword() throws Exception { final String TEST_NAME = "test164PasswordLoginLifecycleDeprecatedGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_DEPRECATED); loginJackGoodPasswordExpectSuccess(TEST_NAME, task, result); } @Test public void test166PasswordLoginLifecycleProposedGoodPassword() throws Exception { final String TEST_NAME = "test166PasswordLoginLifecycleProposedGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_PROPOSED); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test168PasswordLoginLifecycleArchivedGoodPassword() throws Exception { final String TEST_NAME = "test168PasswordLoginLifecycleArchivedGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); modifyUserReplace(USER_JACK_OID, UserType.F_LIFECYCLE_STATE, task, result, SchemaConstants.LIFECYCLE_ARCHIVED); loginJackGoodPasswordExpectDenied(TEST_NAME, task, result); } @Test public void test200UserGuybrushSetCredentials() throws Exception { final String TEST_NAME = "test200UserGuybrushSetPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN Task task = createTask(TestAbstractAuthenticationEvaluator.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); modifyUserCredential(task, result); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_GUYBRUSH_OID); display("user after", userAfter); // assertEncryptedUserPassword(userAfter, USER_GUYBRUSH_PASSWORD); assertPasswordMetadata(userAfter, getCredentialType(), false, startTs, endTs, null, SchemaConstants.CHANNEL_GUI_USER_URI); assertFailedLogins(userAfter, 0); } @Test public void test201UserGuybrushPasswordLoginGoodPassword() throws Exception { final String TEST_NAME = "test201UserGuybrushPasswordLoginGoodPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_GUYBRUSH_USERNAME, getGoodPasswordGuybrush())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_GUYBRUSH_USERNAME); PrismObject<UserType> userAfter = getUser(USER_GUYBRUSH_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); } @Test public void test202UserGuybrushPasswordLoginBadPassword() throws Exception { final String TEST_NAME = "test202UserGuybrushPasswordLoginBadPassword"; TestUtil.displayTestTile(TEST_NAME); // GIVEN ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_GUYBRUSH_USERNAME, getBadPasswordGuybrush())); AssertJUnit.fail("Unexpected success"); } catch (BadCredentialsException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertBadPasswordException(e, USER_GUYBRUSH_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_GUYBRUSH_OID); display("user after", userAfter); assertFailedLogins(userAfter, 1); assertLastFailedLogin(userAfter, startTs, endTs); } @Test public void test209UserGuybrushPasswordLoginGoodPasswordBeforeExpiration() throws Exception { final String TEST_NAME = "test209UserGuybrushPasswordLoginGoodPasswordBeforeExpiration"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("P29D"); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_GUYBRUSH_USERNAME, getGoodPasswordGuybrush())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_GUYBRUSH_USERNAME); PrismObject<UserType> userAfter = getUser(USER_GUYBRUSH_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); } @Test public void test210UserGuybrushPasswordLoginGoodPasswordExpired() throws Exception { final String TEST_NAME = "test210UserGuybrushPasswordLoginGoodPasswordExpired"; TestUtil.displayTestTile(TEST_NAME); // GIVEN clock.overrideDuration("P2D"); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); try { // WHEN TestUtil.displayWhen(TEST_NAME); getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_GUYBRUSH_USERNAME, getGoodPasswordGuybrush())); AssertJUnit.fail("Unexpected success"); } catch (CredentialsExpiredException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); assertExpiredException(e, USER_GUYBRUSH_USERNAME); } XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); PrismObject<UserType> userAfter = getUser(USER_GUYBRUSH_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); } private void assertGoodPasswordAuthentication(Authentication authentication, String expectedUsername) { assertNotNull("No authentication", authentication); assertTrue("authentication: not authenticated", authentication.isAuthenticated()); MidPointAsserts.assertInstanceOf("authentication", authentication, UsernamePasswordAuthenticationToken.class); assertEquals("authentication: principal mismatch", expectedUsername, ((MidPointPrincipal)authentication.getPrincipal()).getUsername()); } private void assertBadPasswordException(BadCredentialsException e, String username) { assertEquals("Wrong exception meessage (key)", "web.security.provider.invalid", e.getMessage()); } private void assertPasswordEncodingException(BadCredentialsException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.password.encoding", e.getMessage()); } private void assertDeniedException(AccessDeniedException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.access.denied", e.getMessage()); } private void assertLockedException(LockedException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.locked", e.getMessage()); } private void assertDisabledException(DisabledException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.disabled", e.getMessage()); } private void assertExpiredException(CredentialsExpiredException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.password.bad", e.getMessage()); } private void assertNoUserException(UsernameNotFoundException e, String principal) { assertEquals("Wrong exception meessage (key)", "web.security.provider.invalid", e.getMessage()); } private ConnectionEnvironment createConnectionEnvironment() { ConnectionEnvironment connEnv = new ConnectionEnvironment(); connEnv.setRemoteHost("remote.example.com"); return connEnv; } private void assertFailedLogins(PrismObject<UserType> user, int expected) { if (expected == 0 && getCredentialUsedForAuthentication(user.asObjectable()).getFailedLogins() == null) { return; } assertEquals("Wrong failed logins in "+user, (Integer)expected, getCredentialUsedForAuthentication(user.asObjectable()).getFailedLogins()); } private void assertLastSuccessfulLogin(PrismObject<UserType> user, XMLGregorianCalendar startTs, XMLGregorianCalendar endTs) { LoginEventType lastSuccessfulLogin = getCredentialUsedForAuthentication(user.asObjectable()).getLastSuccessfulLogin(); assertNotNull("no last successful login in "+user, lastSuccessfulLogin); XMLGregorianCalendar successfulLoginTs = lastSuccessfulLogin.getTimestamp(); TestUtil.assertBetween("wrong last successful login timestamp", startTs, endTs, successfulLoginTs); } private void assertLastFailedLogin(PrismObject<UserType> user, XMLGregorianCalendar startTs, XMLGregorianCalendar endTs) { LoginEventType lastFailedLogin = getCredentialUsedForAuthentication(user.asObjectable()).getLastFailedLogin(); assertNotNull("no last failed login in "+user, lastFailedLogin); XMLGregorianCalendar failedLoginTs = lastFailedLogin.getTimestamp(); TestUtil.assertBetween("wrong last failed login timestamp", startTs, endTs, failedLoginTs); } private void addFakeAuthorization(MidPointPrincipal principal) { if (principal == null) { return; } if (principal.getAuthorities().isEmpty()) { AuthorizationType authorizationType = new AuthorizationType(); authorizationType.getAction().add("FAKE"); principal.getAuthorities().add(new Authorization(authorizationType)); } } private void assertPrincipalJack(MidPointPrincipal principal) { display("principal", principal); assertEquals("Bad principal name", USER_JACK_USERNAME, principal.getName().getOrig()); assertEquals("Bad principal name", USER_JACK_USERNAME, principal.getUsername()); UserType user = principal.getUser(); assertNotNull("No user in principal",user); assertEquals("Bad name in user in principal", USER_JACK_USERNAME, user.getName().getOrig()); } private void loginJackGoodPasswordExpectSuccess(final String TEST_NAME, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException { display("now", clock.currentTimeXMLGregorianCalendar()); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); Authentication authentication = getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); // THEN TestUtil.displayThen(TEST_NAME); XMLGregorianCalendar endTs = clock.currentTimeXMLGregorianCalendar(); assertGoodPasswordAuthentication(authentication, USER_JACK_USERNAME); PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); assertLastSuccessfulLogin(userAfter, startTs, endTs); } private void loginJackGoodPasswordExpectDenied(final String TEST_NAME, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException { display("now", clock.currentTimeXMLGregorianCalendar()); ConnectionEnvironment connEnv = createConnectionEnvironment(); XMLGregorianCalendar startTs = clock.currentTimeXMLGregorianCalendar(); // WHEN TestUtil.displayWhen(TEST_NAME); try { getAuthenticationEvaluator().authenticate(connEnv, getAuthenticationContext(USER_JACK_USERNAME, getGoodPasswordJack())); AssertJUnit.fail("Unexpected success"); } catch (DisabledException e) { // This is expected // THEN TestUtil.displayThen(TEST_NAME); display("expected exception", e); // this is important. The exception should give no indication whether the password is // good or bad assertDisabledException(e, USER_JACK_USERNAME); } PrismObject<UserType> userAfter = getUser(USER_JACK_OID); display("user after", userAfter); assertFailedLogins(userAfter, 0); } }