Skip to content

Commit

Permalink
adding support of option for updating of focus authentication behavio…
Browse files Browse the repository at this point in the history
…r (MID-7687)
  • Loading branch information
skublik committed Feb 23, 2022
1 parent b298720 commit 8d92deb
Show file tree
Hide file tree
Showing 18 changed files with 581 additions and 334 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -887,14 +887,14 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="issuerUri" type="xsd:string" minOccurs="1" maxOccurs="1" default="false">
<xsd:element name="issuerUri" type="xsd:string" minOccurs="0" maxOccurs="1" >
<xsd:annotation>
<xsd:documentation>
Issuer identifier uri for the OpenID Connect provider.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="jwkSetUri" type="xsd:string" minOccurs="0" maxOccurs="1" default="false">
<xsd:element name="jwkSetUri" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Uri for the JSON Web Key (JWK) Set endpoint.
Expand Down Expand Up @@ -1313,9 +1313,69 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="updatingFocusBehavior" type="tns:UpdatingFocusBehaviorType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:since>4.5</a:since>
</xsd:appinfo>
<xsd:documentation>
Option for updating focus authentication behaviour attributes. We can enable/disable updating of focus
behavior during every login, or we can use option for updating behaviour only when login failed and
during success login after fails login. Default value is 'everyLogin'.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="UpdatingFocusBehaviorType">
<xsd:annotation>
<xsd:documentation>
Option for updating focus authentication behaviour attributes. We can enable/disable updating of focus
behavior during every login, or we can use option for updating behaviour only when login failed and
during success login after fails login.
</xsd:documentation>
<xsd:appinfo>
<a:since>4.5</a:since>
<jaxb:typesafeEnumClass/>
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="enabled">
<xsd:annotation>
<xsd:documentation>
Behaviour attributes will be updated every login.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ENABLED"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="disabled">
<xsd:annotation>
<xsd:documentation>
Authentication behaviour attributes will not be updated during login.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="DISABLED"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="onlyUnsuccessfulLogin">
<xsd:annotation>
<xsd:documentation>
Authentication behaviour attributes will be updated when login failed and when login will be success,
but previous login was unsuccessful and midPoint need update attributes as is number of login fails
and lockout state.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ONLY_UNSUCCESSFUL_LOGIN"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="AuthenticationSequenceChannelType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import com.evolveum.midpoint.authentication.api.config.MidpointAuthentication;
import com.evolveum.midpoint.authentication.impl.util.AuthSequenceUtil;
import com.evolveum.midpoint.model.api.ModelAuditRecorder;
import com.evolveum.midpoint.model.api.context.PreAuthenticationContext;
import com.evolveum.midpoint.security.api.Authorization;
Expand Down Expand Up @@ -38,6 +40,8 @@
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

Expand Down Expand Up @@ -459,26 +463,33 @@ private boolean isLockoutExpired(AbstractCredentialType credentialsType, Credent
}

protected void recordPasswordAuthenticationSuccess(@NotNull MidPointPrincipal principal, @NotNull ConnectionEnvironment connEnv,
@NotNull AuthenticationBehavioralDataType passwordType, boolean audit) {
@NotNull AuthenticationBehavioralDataType behavioralData, boolean audit) {
FocusType focusBefore = principal.getFocus().clone();
Integer failedLogins = passwordType.getFailedLogins();
Integer failedLogins = behavioralData.getFailedLogins();
boolean successLoginAfterFail = false;
if (failedLogins != null && failedLogins > 0) {
passwordType.setFailedLogins(0);
behavioralData.setFailedLogins(0);
successLoginAfterFail = true;
}
LoginEventType event = new LoginEventType();
event.setTimestamp(clock.currentTimeXMLGregorianCalendar());
event.setFrom(connEnv.getRemoteHostAddress());

passwordType.setPreviousSuccessfulLogin(passwordType.getLastSuccessfulLogin());
passwordType.setLastSuccessfulLogin(event);
behavioralData.setPreviousSuccessfulLogin(behavioralData.getLastSuccessfulLogin());
behavioralData.setLastSuccessfulLogin(event);

ActivationType activation = principal.getFocus().getActivation();
if (activation != null) {
if (LockoutStatusType.LOCKED.equals(activation.getLockoutStatus())) {
successLoginAfterFail = true;
}
activation.setLockoutStatus(LockoutStatusType.NORMAL);
activation.setLockoutExpirationTimestamp(null);
}

focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
if (AuthSequenceUtil.isAllowUpdatingAuthBehavior(successLoginAfterFail)) {
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
}
if (audit) {
recordAuthenticationSuccess(principal, connEnv);
}
Expand Down Expand Up @@ -510,10 +521,10 @@ public void recordAuthenticationBehavior(String username, MidPointPrincipal prin
}

protected void recordPasswordAuthenticationFailure(@NotNull MidPointPrincipal principal, @NotNull ConnectionEnvironment connEnv,
@NotNull AuthenticationBehavioralDataType passwordType, CredentialPolicyType credentialsPolicy, String reason, boolean audit) {
@NotNull AuthenticationBehavioralDataType behavioralData, CredentialPolicyType credentialsPolicy, String reason, boolean audit) {
FocusType focusBefore = principal.getFocus().clone();
Integer failedLogins = passwordType.getFailedLogins();
LoginEventType lastFailedLogin = passwordType.getLastFailedLogin();
Integer failedLogins = behavioralData.getFailedLogins();
LoginEventType lastFailedLogin = behavioralData.getLastFailedLogin();
XMLGregorianCalendar lastFailedLoginTs = null;
if (lastFailedLogin != null) {
lastFailedLoginTs = lastFailedLogin.getTimestamp();
Expand All @@ -536,13 +547,13 @@ protected void recordPasswordAuthenticationFailure(@NotNull MidPointPrincipal pr
failedLogins++;
}

passwordType.setFailedLogins(failedLogins);
behavioralData.setFailedLogins(failedLogins);

LoginEventType event = new LoginEventType();
event.setTimestamp(clock.currentTimeXMLGregorianCalendar());
event.setFrom(connEnv.getRemoteHostAddress());

passwordType.setLastFailedLogin(event);
behavioralData.setLastFailedLogin(event);

ActivationType activationType = principal.getFocus().getActivation();

Expand All @@ -560,7 +571,9 @@ protected void recordPasswordAuthenticationFailure(@NotNull MidPointPrincipal pr
activationType.setLockoutExpirationTimestamp(lockoutExpirationTs);
}

focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
if (AuthSequenceUtil.isAllowUpdatingAuthBehavior(true)) {
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
}
if (audit) {
recordAuthenticationFailure(principal, connEnv, reason);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import com.evolveum.midpoint.authentication.api.AuthenticationChannel;
import com.evolveum.midpoint.authentication.impl.ldap.LdapDirContextAdapter;
import com.evolveum.midpoint.authentication.impl.util.AuthSequenceUtil;
import com.evolveum.midpoint.security.api.*;
import com.evolveum.midpoint.authentication.api.config.MidpointAuthentication;
import com.evolveum.midpoint.authentication.impl.module.authentication.ModuleAuthenticationImpl;
Expand Down Expand Up @@ -228,8 +229,10 @@ public void recordPasswordAuthenticationSuccess(@NotNull MidPointPrincipal princ

FocusType focusBefore = principal.getFocus().clone();
Integer failedLogins = behavior.getFailedLogins();
boolean successLoginAfterFail = false;
if (failedLogins != null && failedLogins > 0) {
behavior.setFailedLogins(0);
successLoginAfterFail = true;
}
LoginEventType event = new LoginEventType();
event.setTimestamp(clock.currentTimeXMLGregorianCalendar());
Expand All @@ -241,7 +244,9 @@ public void recordPasswordAuthenticationSuccess(@NotNull MidPointPrincipal princ
behavior.setPreviousSuccessfulLogin(behavior.getLastSuccessfulLogin());
behavior.setLastSuccessfulLogin(event);

focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
if (AuthSequenceUtil.isAllowUpdatingAuthBehavior(successLoginAfterFail)) {
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
}
recordAuthenticationSuccess(principal.getFocus(), channel);
}

Expand Down Expand Up @@ -283,7 +288,9 @@ public void recordPasswordAuthenticationFailure(String name, String reason) {
}

behavior.setLastFailedLogin(event);
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
if (AuthSequenceUtil.isAllowUpdatingAuthBehavior(true)) {
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
}
}

recordAuthenticationFailure(name, focus, channel, reason);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,4 +586,19 @@ public static void doRemoteFilter(ServletRequest req, ServletResponse res, Filte
throw new AuthenticationServiceException("Unsupported type of Authentication");
}
}

public static boolean isAllowUpdatingAuthBehavior(boolean isUpdatingDuringUnsuccessfulLogin){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof MidpointAuthentication && ((MidpointAuthentication)authentication).getSequence() != null) {
UpdatingFocusBehaviorType actualOption = ((MidpointAuthentication) authentication).getSequence().getUpdatingFocusBehavior();
if (actualOption == null && UpdatingFocusBehaviorType.ENABLED.equals(actualOption)) {
return true;
} else if (UpdatingFocusBehaviorType.DISABLED.equals(actualOption)) {
return false;
} else if (UpdatingFocusBehaviorType.ONLY_UNSUCCESSFUL_LOGIN.equals(actualOption)) {
return isUpdatingDuringUnsuccessfulLogin;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2016-2022 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/
package com.evolveum.midpoint.testing.rest.authentication;

import com.evolveum.midpoint.common.rest.MidpointAbstractProvider;
import com.evolveum.midpoint.common.rest.MidpointJsonProvider;
import com.evolveum.midpoint.model.common.SystemObjectCache;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.testing.rest.RestServiceInitializer;

import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType;

import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterMethod;

import javax.ws.rs.core.MediaType;
import java.io.File;
import java.io.IOException;

public abstract class TestAbstractAuthentication extends RestServiceInitializer {

protected static final File BASE_AUTHENTICATION_DIR = new File("src/test/resources/authentication/");
protected static final File BASE_REPO_DIR = new File(BASE_AUTHENTICATION_DIR,"repo/");

@Autowired
protected MidpointJsonProvider jsonProvider;

@Autowired
private SystemObjectCache systemObjectCache;

@Override
protected String getAcceptHeader() {
return MediaType.APPLICATION_JSON;
}

@Override
protected String getContentType() {
return MediaType.APPLICATION_JSON;
}

@Override
protected MidpointAbstractProvider getProvider() {
return jsonProvider;
}

@AfterMethod
public void clearCache(){
systemObjectCache.invalidateCaches();
}

protected void replaceSecurityPolicy(File securityPolicy) throws CommonException, IOException {
Task task = getTestTask();
OperationResult result = task.getResult();
PrismObject<SecurityPolicyType> secPolicy = parseObject(securityPolicy);
addObject(secPolicy, executeOptions().overwrite(), task, result);
getDummyAuditService().clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@

import static org.testng.AssertJUnit.assertNotNull;

public abstract class TestAbstractOidcRestModule extends RestServiceInitializer {

protected static final File BASE_AUTHENTICATION_DIR = new File("src/test/resources/authentication/");
protected static final File BASE_REPO_DIR = new File(BASE_AUTHENTICATION_DIR,"repo/");
public abstract class TestAbstractOidcRestModule extends TestAbstractAuthentication {

public static final String USER_ADMINISTRATOR_USERNAME = "administrator";
public static final String USER_ADMINISTRATOR_PASSWORD = "secret";
Expand All @@ -53,34 +50,8 @@ public abstract class TestAbstractOidcRestModule extends RestServiceInitializer
public static final File SECURITY_POLICY_SYMMETRIC_KEY = new File(BASE_REPO_DIR, "security-policy-symmetric-key.xml");
public static final File SECURITY_POLICY_SYMMETRIC_KEY_WRONG_KEY = new File(BASE_REPO_DIR, "security-policy-symmetric-key-wrong-alg.xml");

@Autowired
protected MidpointJsonProvider jsonProvider;

@Autowired
private SystemObjectCache systemObjectCache;

@Override
protected String getAcceptHeader() {
return MediaType.APPLICATION_JSON;
}

@Override
protected String getContentType() {
return MediaType.APPLICATION_JSON;
}

@Override
protected MidpointAbstractProvider getProvider() {
return jsonProvider;
}

public abstract AuthzClient getAuthzClient();

@AfterMethod
public void clearCache(){
systemObjectCache.invalidateCaches();
}

@Test
public void oidcAuthByIssuerUriTest() throws Exception {
replaceSecurityPolicy(SECURITY_POLICY_ISSUER_URI);
Expand Down Expand Up @@ -172,14 +143,6 @@ public void oidcAuthBySymmetricKeyWithWrongAlgTest() throws Exception {
assertUnsuccess(response);
}

private void replaceSecurityPolicy(File securityPolicy) throws CommonException, IOException {
Task task = getTestTask();
OperationResult result = task.getResult();
PrismObject<SecurityPolicyType> secPolicy = parseObject(securityPolicy);
addObject(secPolicy, executeOptions().overwrite(), task, result);
getDummyAuditService().clear();
}

protected abstract void assertForAuthByPublicKey(Response response);
protected abstract void assertForAuthByHMac(Response response);

Expand Down

0 comments on commit 8d92deb

Please sign in to comment.