Skip to content

Commit

Permalink
implementing of recording authentication behavior to focus for all mo…
Browse files Browse the repository at this point in the history
…dules
  • Loading branch information
skublik committed Jun 18, 2020
1 parent 28c6b07 commit 1f57514
Show file tree
Hide file tree
Showing 9 changed files with 862 additions and 657 deletions.
Expand Up @@ -7,43 +7,46 @@

package com.evolveum.midpoint.web.security.provider;

import com.evolveum.midpoint.audit.api.AuditEventRecord;
import com.evolveum.midpoint.audit.api.AuditEventStage;
import com.evolveum.midpoint.audit.api.AuditEventType;
import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.model.api.AuthenticationEvaluator;
import com.evolveum.midpoint.model.api.ModelAuditRecorder;
import com.evolveum.midpoint.model.api.authentication.*;
import com.evolveum.midpoint.model.api.util.AuthenticationEvaluatorUtil;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.equivalence.ParameterizedEquivalenceStrategy;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.security.api.ConnectionEnvironment;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.security.api.SecurityUtil;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.security.module.authentication.LdapAuthenticationToken;
import com.evolveum.midpoint.web.security.module.authentication.LdapModuleAuthentication;
import com.evolveum.midpoint.web.security.util.SecurityUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.Collection;
import java.util.List;

Expand All @@ -53,11 +56,11 @@ public class MidPointLdapAuthenticationProvider extends MidPointAbstractAuthenti

private LdapAuthenticationProvider authenticatorProvider;

@Autowired
private ModelAuditRecorder auditProvider;
@Autowired private ModelAuditRecorder auditProvider;
@Autowired private PrismContext prismContext;
@Autowired private Clock clock;
@Autowired private GuiProfiledPrincipalManager focusProfileService;

@Autowired
private PrismContext prismContext;

public MidPointLdapAuthenticationProvider(LdapAuthenticator authenticator) {
this.authenticatorProvider = createAuthenticatorProvider(authenticator);
Expand Down Expand Up @@ -107,22 +110,28 @@ protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenti

Object principal = authNCtx.getPrincipal();
if (!(principal instanceof MidPointPrincipal)) {
recordPasswordAuthenticationFailure(authentication.getName(), "not contains required assignment");
throw new BadCredentialsException("LdapAuthentication.incorrect.value");
}
MidPointPrincipal midPointPrincipal = (MidPointPrincipal) principal;
FocusType focusType = midPointPrincipal.getFocus();

if (focusType == null) {
recordPasswordAuthenticationFailure(authentication.getName(), "not contains required assignment");
throw new BadCredentialsException("LdapAuthentication.bad.user");
}

String channel = SchemaConstants.CHANNEL_GUI_USER_URI;
Authentication actualAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (actualAuthentication instanceof MidpointAuthentication && ((MidpointAuthentication) actualAuthentication).getAuthenticationChannel() != null) {
channel = ((MidpointAuthentication) actualAuthentication).getAuthenticationChannel().getChannelId();
if (actualAuthentication instanceof MidpointAuthentication) {
MidpointAuthentication mpAuthentication = (MidpointAuthentication) actualAuthentication;
List<ObjectReferenceType> requireAssignment = mpAuthentication.getSequence().getRequireAssignmentTarget();
if (!AuthenticationEvaluatorUtil.checkRequiredAssignment(focusType.getAssignment(), requireAssignment)) {
recordPasswordAuthenticationFailure(midPointPrincipal.getUsername(), "not contains required assignment");
throw new InternalAuthenticationServiceException("web.security.flexAuth.invalid.required.assignment");
}
}

auditProvider.auditLoginSuccess(focusType, ConnectionEnvironment.create(channel));
recordPasswordAuthenticationSuccess(midPointPrincipal);
return authNCtx;
}
};
Expand Down Expand Up @@ -167,6 +176,7 @@ protected Authentication internalAuthentication(Authentication authentication, L
token = this.authenticatorProvider.authenticate(authentication);
} else {
LOGGER.error("Unsupported authentication {}", authentication);
recordPasswordAuthenticationFailure(authentication.getName(), "unavailable provider");
throw new AuthenticationServiceException("web.security.provider.unavailable");
}

Expand All @@ -180,16 +190,16 @@ protected Authentication internalAuthentication(Authentication authentication, L
// This sometimes happens ... for unknown reasons the underlying libraries cannot
// figure out correct exception. Which results to wrong error message (MID-4518)
// So, be smart here and try to figure out correct error.
auditProvider.auditLoginFailure(authentication.getName(), null, connEnv, e.getMessage());
recordPasswordAuthenticationFailure(authentication.getName(), e.getMessage());
throw processInternalAuthenticationException(e, e);

} catch (IncorrectResultSizeDataAccessException e) {
LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
auditProvider.auditLoginFailure(authentication.getName(), null, connEnv, "bad user");
recordPasswordAuthenticationFailure(authentication.getName(), "bad user");
throw new BadCredentialsException("LdapAuthentication.bad.user", e);
} catch (RuntimeException e) {
LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
auditProvider.auditLoginFailure(authentication.getName(), null, connEnv, "bad credentials");
recordPasswordAuthenticationFailure(authentication.getName(), "bad credentials");
throw e;
}
}
Expand Down Expand Up @@ -221,4 +231,106 @@ public int hashCode() {
public boolean equals(Object obj) {
return this.authenticatorProvider.equals(obj);
}

public void recordPasswordAuthenticationSuccess(@NotNull MidPointPrincipal principal) {
String channel = getChannel();
AuthenticationBehavioralDataType behavior = AuthenticationEvaluatorUtil.getBehavior(principal.getFocus());

FocusType focusBefore = principal.getFocus().clone();
Integer failedLogins = behavior.getFailedLogins();
if (failedLogins != null && failedLogins > 0) {
behavior.setFailedLogins(0);
}
LoginEventType event = new LoginEventType();
event.setTimestamp(clock.currentTimeXMLGregorianCalendar());
event.setFrom(SecurityUtil.getCurrentConnectionInformation().getRemoteHostAddress());

behavior.setPreviousSuccessfulLogin(behavior.getLastSuccessfulLogin());
behavior.setLastSuccessfulLogin(event);

focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
recordAuthenticationSuccess(principal.getFocus(), channel);
}

private Collection<? extends ItemDelta<?, ?>> computeModifications(@NotNull FocusType before, @NotNull FocusType after) {
ObjectDelta<? extends FocusType> delta = ((PrismObject<FocusType>)before.asPrismObject()).diff((PrismObject<FocusType>) after.asPrismObject(), ParameterizedEquivalenceStrategy.LITERAL);
assert delta.isModify();
return delta.getModifications();
}

private String getChannel() {
String channel = SchemaConstants.CHANNEL_GUI_USER_URI;
Authentication actualAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (actualAuthentication instanceof MidpointAuthentication && ((MidpointAuthentication) actualAuthentication).getAuthenticationChannel() != null) {
channel = ((MidpointAuthentication) actualAuthentication).getAuthenticationChannel().getChannelId();
}
return channel;
}

private void recordAuthenticationSuccess(@NotNull FocusType focusType, @NotNull String channel) {
auditProvider.auditLoginSuccess(focusType, createConnectEnviroment(channel));
}

private ConnectionEnvironment createConnectEnviroment(String channel) {
ConnectionEnvironment env = ConnectionEnvironment.create(channel);
Authentication actualAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (actualAuthentication instanceof MidpointAuthentication && ((MidpointAuthentication) actualAuthentication).getSessionId() != null) {
env.setSessionIdOverride(((MidpointAuthentication) actualAuthentication).getSessionId());
}
return env;
}

public void recordPasswordAuthenticationFailure(String name, String reason) {

FocusType focus = null;
String channel = getChannel();
MidPointPrincipal principal = null;
try {
principal = focusProfileService.getPrincipal(name, getFocusType());
focus = principal.getFocus();
} catch (Exception e) {
//ignore if non-exist
}

if (principal != null && focus != null) {
AuthenticationBehavioralDataType behavior = AuthenticationEvaluatorUtil.getBehavior(focus);

FocusType focusBefore = focus.clone();
Integer failedLogins = behavior.getFailedLogins();

if (failedLogins == null) {
failedLogins = 1;
} else {
failedLogins++;
}

behavior.setFailedLogins(failedLogins);

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

behavior.setLastFailedLogin(event);
focusProfileService.updateFocus(principal, computeModifications(focusBefore, principal.getFocus()));
}

recordAuthenticationFailure(name, focus, channel, reason);
}

private Class<? extends FocusType> getFocusType() {
Class<? extends FocusType> focusType = UserType.class;
Authentication actualAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (actualAuthentication instanceof MidpointAuthentication) {
MidpointAuthentication mpAuthentication = (MidpointAuthentication) actualAuthentication;
ModuleAuthentication moduleAuthentication = getProcessingModule(mpAuthentication);
if (moduleAuthentication != null && moduleAuthentication.getFocusType() != null) {
focusType = WebComponentUtil.qnameToClass(prismContext, moduleAuthentication.getFocusType(), FocusType.class);
}
}
return focusType;
}

protected void recordAuthenticationFailure(String name, FocusType focus, String channel, String reason) {
auditProvider.auditLoginFailure(name, focus, createConnectEnviroment(channel), "bad credentials");
}
}
Expand Up @@ -211,6 +211,8 @@ public abstract class SchemaConstants {
public static final ItemPath PATH_ASSIGNMENT_DESCRIPTION = ItemPath.create(FocusType.F_ASSIGNMENT, AssignmentType.F_DESCRIPTION);
public static final ItemPath PATH_ASSOCIATION = ItemPath.create(C_ASSOCIATION);
public static final ItemPath PATH_TRIGGER = ItemPath.create(ObjectType.F_TRIGGER);
public static final ItemPath PATH_AUTHENTICATION_BEHAVIOR_FAILED_LOGINS = ItemPath.create(FocusType.F_BEHAVIOR,
BehaviorType.F_AUTHENTICATION, AuthenticationBehavioralDataType.F_FAILED_LOGINS);
public static final ItemPath PATH_CREDENTIALS_PASSWORD_FAILED_LOGINS = ItemPath.create(
UserType.F_CREDENTIALS, CredentialsType.F_PASSWORD, PasswordType.F_FAILED_LOGINS);
public static final ItemPath PATH_CREDENTIALS_NONCE_FAILED_LOGINS = ItemPath.create(
Expand Down
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2019 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.model.api.util;

import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import java.util.List;

/**
* @author skublik
*/

public class AuthenticationEvaluatorUtil {

public static boolean checkRequiredAssignment(List<AssignmentType> assignments, List<ObjectReferenceType> requireAssignments) {
if (requireAssignments == null || requireAssignments.isEmpty()){
return true;
}
if (assignments == null || assignments.isEmpty()) {
return false;
}

for (ObjectReferenceType requiredAssignment : requireAssignments) {
if (requiredAssignment == null) {
throw new IllegalStateException("Required assignment is null");
}
if (requiredAssignment.getOid() == null){
throw new IllegalStateException("Oid of required assignment is null");
}
boolean match = false;
for (AssignmentType assignment : assignments) {
ObjectReferenceType targetRef = assignment.getTargetRef();
if (targetRef != null) {
if (targetRef.getOid() != null && targetRef.getOid().equals(requiredAssignment.getOid())) {
match = true;
break;
}
} else if (assignment.getConstruction() != null && requiredAssignment.getType() != null
&& QNameUtil.match(requiredAssignment.getType(), ResourceType.COMPLEX_TYPE)) {
if (assignment.getConstruction().getResourceRef() != null &&
assignment.getConstruction().getResourceRef().getOid() != null &&
assignment.getConstruction().getResourceRef().getOid().equals(requiredAssignment.getOid())) {
match = true;
break;
}
}
}
if (!match) {
return false;
}
}
return true;
}

public static AuthenticationBehavioralDataType getBehavior(FocusType focus) {

if (focus.getBehavior() == null){
focus.setBehavior(new BehaviorType());
}
if (focus.getBehavior().getAuthentication() == null){
focus.getBehavior().setAuthentication(new AuthenticationBehavioralDataType());
}
return focus.getBehavior().getAuthentication();
}
}
Expand Up @@ -18,6 +18,8 @@
import com.evolveum.midpoint.model.impl.lens.projector.util.ProcessorMethod;
import com.evolveum.midpoint.model.impl.lens.projector.util.SkipWhenFocusDeleted;

import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -45,18 +47,6 @@
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.AbstractCredentialType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LifecycleStateModelType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LockoutStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TimeIntervalStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

/**
* @author katkav
Expand Down Expand Up @@ -270,6 +260,10 @@ private <F extends FocusType> void processActivationLockout(LensFocusContext<F>
resetFailedLogins(focusContext, credentialsTypeNew.getNonce(), SchemaConstants.PATH_CREDENTIALS_NONCE_FAILED_LOGINS);
resetFailedLogins(focusContext, credentialsTypeNew.getSecurityQuestions(), SchemaConstants.PATH_CREDENTIALS_SECURITY_QUESTIONS_FAILED_LOGINS);
}
BehaviorType behavior = focusNew.asObjectable().getBehavior();
if (behavior != null) {
resetFailedLogins(focusContext, behavior.getAuthentication(), SchemaConstants.PATH_AUTHENTICATION_BEHAVIOR_FAILED_LOGINS);
}

if (activationNew.getLockoutExpirationTimestamp() != null) {
PrismContainerDefinition<ActivationType> activationDefinition = getActivationDefinition();
Expand All @@ -282,7 +276,7 @@ private <F extends FocusType> void processActivationLockout(LensFocusContext<F>
}
}

private <F extends FocusType> void resetFailedLogins(LensFocusContext<F> focusContext, AbstractCredentialType credentialTypeNew, ItemPath path)
private <F extends FocusType> void resetFailedLogins(LensFocusContext<F> focusContext, AuthenticationBehavioralDataType credentialTypeNew, ItemPath path)
throws SchemaException {
if (credentialTypeNew != null) {
Integer failedLogins = credentialTypeNew.getFailedLogins();
Expand Down

0 comments on commit 1f57514

Please sign in to comment.