Skip to content

Commit

Permalink
fix bugs from schrodinger ldap auth tests
Browse files Browse the repository at this point in the history
  • Loading branch information
skublik committed May 22, 2023
1 parent aed7f0b commit d776719
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ FocusType checkCredentials(ConnectionEnvironment connEnv, T authnCtx)
* @param connEnv
* @param authnCtx
* @return token with {@link com.evolveum.midpoint.security.api.MidPointPrincipal}
* @throws DisabledException when object found by authentication identifier is disabled
* @throws AuthenticationServiceException when occur some internal server error during authentication
* @throws UsernameNotFoundException when object not found by authentication identifier
*/
<AC extends AbstractAuthenticationContext> PreAuthenticatedAuthenticationToken authenticateUserPreAuthenticated(ConnectionEnvironment connEnv, AC authnCtx);
<AC extends AbstractAuthenticationContext> PreAuthenticatedAuthenticationToken authenticateUserPreAuthenticated(
ConnectionEnvironment connEnv, AC authnCtx)
throws DisabledException, AuthenticationServiceException, UsernameNotFoundException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ public String getAndCheckUserPassword(ConnectionEnvironment connEnv, String user
}

@Override
public <AC extends AbstractAuthenticationContext> PreAuthenticatedAuthenticationToken authenticateUserPreAuthenticated(ConnectionEnvironment connEnv, AC authnCtx) {
public <AC extends AbstractAuthenticationContext> PreAuthenticatedAuthenticationToken authenticateUserPreAuthenticated(
ConnectionEnvironment connEnv, AC authnCtx)
throws DisabledException, AuthenticationServiceException, UsernameNotFoundException {

MidPointPrincipal principal = getAndCheckPrincipal(connEnv, authnCtx, authnCtx.isSupportActivationByChannel());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ public class LdapModuleFactory extends AbstractModuleFactory {
@Autowired
private Protector protector;

@Autowired
private GuiProfiledPrincipalManager principalManager;

@Override
public boolean match(AbstractAuthenticationModuleType moduleType, AuthenticationChannel authenticationChannel) {
return moduleType instanceof LdapAuthenticationModuleType;
Expand Down Expand Up @@ -111,7 +108,7 @@ private AuthenticationProvider getProvider(LdapAuthenticationModuleType moduleTy
getObjectObjectPostProcessor().postProcess(auth);

MidPointLdapAuthenticationProvider provider = new MidPointLdapAuthenticationProvider(auth);
provider.setUserDetailsContextMapper(new MidpointPrincipalContextMapper(principalManager));
provider.setUserDetailsContextMapper(getObjectObjectPostProcessor().postProcess(new MidpointPrincipalContextMapper()));
getObjectObjectPostProcessor().postProcess(provider.getAuthenticatorProvider());
getObjectObjectPostProcessor().postProcess(provider);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2010-2023 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.authentication.impl.ldap;

import org.springframework.security.core.AuthenticationException;

public class AuditedAuthenticationException extends AuthenticationException {
public AuditedAuthenticationException(AuthenticationException cause) {
super(cause.getMessage(), cause);
}

@Override
public synchronized AuthenticationException getCause() {
return (AuthenticationException) super.getCause();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
*/
package com.evolveum.midpoint.authentication.impl.ldap;

import com.evolveum.midpoint.authentication.api.AuthenticationChannel;
import com.evolveum.midpoint.security.api.ConnectionEnvironment;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;

import org.springframework.ldap.core.DirContextAdapter;

import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

import org.springframework.security.core.Authentication;

import java.util.List;

/**
* Define focus during processing of Ldap authentication module.
*
Expand All @@ -24,8 +32,11 @@ public LdapDirContextAdapter(DirContextAdapter dirContextAdapter){
}

private String namingAttr;

private Class<? extends FocusType> focusType = UserType.class;
private List<ObjectReferenceType> requireAssignment = null;
private AuthenticationChannel channel = null;

private ConnectionEnvironment connectionEnvironment = null;

public void setNamingAttr(String namingAttr) {
this.namingAttr = namingAttr;
Expand All @@ -42,4 +53,28 @@ public void setFocusType(Class<? extends FocusType> focusType) {
public Class<? extends FocusType> getFocusType() {
return focusType;
}

public List<ObjectReferenceType> getRequireAssignment() {
return requireAssignment;
}

public void setRequireAssignment(List<ObjectReferenceType> requireAssignment) {
this.requireAssignment = requireAssignment;
}

public AuthenticationChannel getChannel() {
return channel;
}

public void setChannel(AuthenticationChannel channel) {
this.channel = channel;
}

public ConnectionEnvironment getConnectionEnvironment() {
return connectionEnvironment;
}

public void setConnectionEnvironment(ConnectionEnvironment connectionEnvironment) {
this.connectionEnvironment = connectionEnvironment;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,93 @@
*/
package com.evolveum.midpoint.authentication.impl.ldap;

import com.evolveum.midpoint.authentication.api.AuthenticationChannel;
import com.evolveum.midpoint.authentication.api.config.AuthenticationEvaluator;
import com.evolveum.midpoint.authentication.impl.provider.MidPointLdapAuthenticationProvider;
import com.evolveum.midpoint.model.api.authentication.GuiProfiledPrincipalManager;
import com.evolveum.midpoint.model.api.context.PasswordAuthenticationContext;
import com.evolveum.midpoint.model.api.context.PreAuthenticationContext;
import com.evolveum.midpoint.security.api.ConnectionEnvironment;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import javax.naming.AuthenticationException;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* @author skublik
*/

public class MidpointPrincipalContextMapper implements UserDetailsContextMapper {

GuiProfiledPrincipalManager principalManager;
private static final Trace LOGGER = TraceManager.getTrace(MidpointPrincipalContextMapper.class);

public MidpointPrincipalContextMapper(GuiProfiledPrincipalManager principalManager) {
this.principalManager = principalManager;
@Autowired
@Qualifier("passwordAuthenticationEvaluator")
private AuthenticationEvaluator<PasswordAuthenticationContext> authenticationEvaluator;

public MidpointPrincipalContextMapper() {
}

@Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<? extends GrantedAuthority> authorities) {

String userNameEffective = username;
Class<? extends FocusType> focusType = UserType.class;
try {
if (ctx instanceof LdapDirContextAdapter && ((LdapDirContextAdapter) ctx).getNamingAttr() != null) {
userNameEffective = resolveLdapName(ctx, username, ((LdapDirContextAdapter) ctx).getNamingAttr());
focusType = ((LdapDirContextAdapter) ctx).getFocusType();
}
return principalManager.getPrincipal(userNameEffective, focusType);
if (!(ctx instanceof LdapDirContextAdapter) || ((LdapDirContextAdapter) ctx).getNamingAttr() == null) {
LOGGER.debug("Couldn't define midpoint user");
throw new AuthenticationServiceException("web.security.provider.invalid");
}

String userNameEffective;
try {
userNameEffective = resolveLdapName(ctx, username, ((LdapDirContextAdapter) ctx).getNamingAttr());
} catch (ObjectNotFoundException e) {
throw new UsernameNotFoundException("UserProfileServiceImpl.unknownUser", e);
} catch (SchemaException | CommunicationException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException | NamingException e) {
throw new UsernameNotFoundException("web.security.provider.invalid.credentials", e);
} catch (NamingException e) {
throw new SystemException(e.getMessage(), e);
}

Class<? extends FocusType> focusType = ((LdapDirContextAdapter) ctx).getFocusType();
List<ObjectReferenceType> requireAssignment = ((LdapDirContextAdapter) ctx).getRequireAssignment();
AuthenticationChannel channel = ((LdapDirContextAdapter) ctx).getChannel();
ConnectionEnvironment connEnv = ((LdapDirContextAdapter) ctx).getConnectionEnvironment();

PreAuthenticationContext authContext = new PreAuthenticationContext(userNameEffective, focusType, requireAssignment);
if (channel != null) {
authContext.setSupportActivationByChannel(channel.isSupportActivationByChannel());
}

try {
PreAuthenticatedAuthenticationToken token = authenticationEvaluator.authenticateUserPreAuthenticated(
connEnv, authContext);
return (UserDetails) token.getPrincipal();
} catch (DisabledException | AuthenticationServiceException | UsernameNotFoundException e) {
throw new AuditedAuthenticationException(e);
}

}

private String resolveLdapName(DirContextOperations ctx, String username, String ldapNamingAttr) throws NamingException, ObjectNotFoundException {
Expand All @@ -63,8 +104,12 @@ private String resolveLdapName(DirContextOperations ctx, String username, String
if (namingAttrValue != null) {
return namingAttrValue.toString().toLowerCase();
}
} else if (ldapResponse.size() == 0) {
LOGGER.debug("LDAP attribute, which define username is empty");
throw new AuthenticationServiceException("web.security.provider.invalid");
} else {
throw new ObjectNotFoundException("Bad response"); // naming attribute contains multiple values
LOGGER.debug("LDAP attribute, which define username contains more values {}", ldapResponse.getAll());
throw new AuthenticationServiceException("web.security.provider.invalid"); // naming attribute contains multiple values
}
}
return username; // fallback to typed-in username in case ldap value is missing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,21 @@ private Authentication initAuthRequirements(Authentication processingAuthenticat
return processingAuthentication;
}

protected AuthenticationRequirements initAuthRequirements(Authentication actualAuthentication) {
AuthenticationRequirements authRequirements = new AuthenticationRequirements();
if (actualAuthentication instanceof MidpointAuthentication) {
MidpointAuthentication mpAuthentication = (MidpointAuthentication) actualAuthentication;
ModuleAuthentication moduleAuthentication = getProcessingModule(mpAuthentication);
if (moduleAuthentication != null && moduleAuthentication.getFocusType() != null) {
authRequirements.focusType = PrismContext.get().getSchemaRegistry()
.determineCompileTimeClass(moduleAuthentication.getFocusType());
}
authRequirements.requireAssignment = mpAuthentication.getSequence().getRequireAssignmentTarget();
authRequirements.channel = mpAuthentication.getAuthenticationChannel();
}
return authRequirements;
}

protected void writeAuthentication(
Authentication originalAuthentication, MidpointAuthentication mpAuthentication,
ModuleAuthenticationImpl moduleAuthentication, Authentication token) {
Expand Down Expand Up @@ -211,7 +226,7 @@ public boolean equals(Object obj) {
return delta.getModifications();
}

private static class AuthenticationRequirements {
static class AuthenticationRequirements {
List<ObjectReferenceType> requireAssignment = null;
AuthenticationChannel channel = null;
Class<? extends FocusType> focusType = UserType.class;
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.api.util.AuthUtil;
import com.evolveum.midpoint.authentication.impl.ldap.AuditedAuthenticationException;
import com.evolveum.midpoint.authentication.impl.ldap.LdapDirContextAdapter;
import com.evolveum.midpoint.authentication.impl.util.AuthSequenceUtil;
import com.evolveum.midpoint.security.api.*;
Expand Down Expand Up @@ -71,7 +72,7 @@ private LdapAuthenticationProvider createAuthenticatorProvider(LdapAuthenticator
@Override
protected DirContextOperations doAuthentication(UsernamePasswordAuthenticationToken authentication) {
DirContextOperations originalDirContextOperations = super.doAuthentication(authentication);
return MidPointLdapAuthenticationProvider.this.doAuthentication(originalDirContextOperations);
return MidPointLdapAuthenticationProvider.this.doAuthentication(authentication, originalDirContextOperations);
}

@Override
Expand All @@ -84,7 +85,8 @@ protected Authentication createSuccessfulAuthentication(UsernamePasswordAuthenti
};
}

protected DirContextOperations doAuthentication(DirContextOperations originalDirContextOperations) {
protected DirContextOperations doAuthentication(
UsernamePasswordAuthenticationToken authentication, DirContextOperations originalDirContextOperations) {
if (originalDirContextOperations instanceof DirContextAdapter) {
Authentication actualAuthentication = SecurityContextHolder.getContext().getAuthentication();
if (actualAuthentication instanceof MidpointAuthentication) {
Expand All @@ -98,10 +100,17 @@ protected DirContextOperations doAuthentication(DirContextOperations originalDir
}
LdapDirContextAdapter mpDirContextAdapter = new LdapDirContextAdapter((DirContextAdapter)originalDirContextOperations);
mpDirContextAdapter.setNamingAttr(((LdapModuleAuthentication) moduleAuthentication).getNamingAttribute());
if (moduleAuthentication.getFocusType() != null) {
Class<FocusType> focusType = PrismContext.get().getSchemaRegistry().determineCompileTimeClass(moduleAuthentication.getFocusType());
mpDirContextAdapter.setFocusType(focusType);

AuthenticationRequirements authRequirements = initAuthRequirements(actualAuthentication);

if (authRequirements.focusType != null) {
mpDirContextAdapter.setFocusType(authRequirements.focusType);
}

mpDirContextAdapter.setChannel(authRequirements.channel);
mpDirContextAdapter.setRequireAssignment(authRequirements.requireAssignment);
mpDirContextAdapter.setConnectionEnvironment(createEnvironment(authRequirements.channel, authentication));

return mpDirContextAdapter;
}
}
Expand Down Expand Up @@ -167,7 +176,7 @@ protected Authentication internalAuthentication(Authentication authentication, L
if (authentication instanceof LdapAuthenticationToken) {
token = this.authenticatorProvider.authenticate(authentication);
} else {
LOGGER.error("Unsupported authentication {}", authentication);
LOGGER.debug("Unsupported authentication {}", authentication);
recordPasswordAuthenticationFailure(authentication.getName(), "unavailable provider");
throw new AuthenticationServiceException("web.security.provider.unavailable");
}
Expand All @@ -178,6 +187,8 @@ protected Authentication internalAuthentication(Authentication authentication, L
authentication.getClass().getSimpleName(), principal.getAuthorities());
return token;

} catch (AuditedAuthenticationException e) {
throw e.getCause();
} catch (InternalAuthenticationServiceException e) {
// This sometimes happens ... for unknown reasons the underlying libraries cannot
// figure out correct exception. Which results to wrong error message (MID-4518)
Expand All @@ -186,11 +197,11 @@ protected Authentication internalAuthentication(Authentication authentication, L
throw processInternalAuthenticationException(e, e);

} catch (IncorrectResultSizeDataAccessException e) {
LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
LOGGER.debug("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
recordPasswordAuthenticationFailure(authentication.getName(), "bad user");
throw new BadCredentialsException("LdapAuthentication.bad.user", e);
throw new BadCredentialsException("web.security.provider.invalid.credentials", e);
} catch (RuntimeException e) {
LOGGER.error("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
LOGGER.debug("Failed to authenticate user {}. Error: {}", authentication.getName(), e.getMessage(), e);
recordPasswordAuthenticationFailure(authentication.getName(), "bad credentials");
throw e;
}
Expand Down

0 comments on commit d776719

Please sign in to comment.