From fee85fe53cf71a1a708dfef071b01884c20fa41f Mon Sep 17 00:00:00 2001 From: Daniel Gerhardt <code@dgerhardt.net> Date: Wed, 11 May 2016 15:12:15 +0200 Subject: [PATCH] Fix case-insensitive handling of LDAP user IDs --- .../de/thm/arsnova/config/SecurityConfig.java | 15 +++++++- .../arsnova/controller/LoginController.java | 2 +- .../java/de/thm/arsnova/entities/User.java | 1 + .../security/CustomLdapUserDetailsMapper.java | 36 +++++++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/thm/arsnova/security/CustomLdapUserDetailsMapper.java diff --git a/src/main/java/de/thm/arsnova/config/SecurityConfig.java b/src/main/java/de/thm/arsnova/config/SecurityConfig.java index 6ca21c5a..564f483d 100644 --- a/src/main/java/de/thm/arsnova/config/SecurityConfig.java +++ b/src/main/java/de/thm/arsnova/config/SecurityConfig.java @@ -25,6 +25,7 @@ import de.thm.arsnova.CasUserDetailsService; import de.thm.arsnova.LoginAuthenticationFailureHandler; import de.thm.arsnova.LoginAuthenticationSucessHandler; import de.thm.arsnova.security.ApplicationPermissionEvaluator; +import de.thm.arsnova.security.CustomLdapUserDetailsMapper; import de.thm.arsnova.security.DbUserDetailsService; import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; import org.scribe.up.provider.impl.FacebookProvider; @@ -65,6 +66,7 @@ import org.springframework.security.ldap.authentication.LdapAuthenticator; import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; +import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; import org.springframework.security.web.authentication.logout.LogoutFilter; @@ -96,6 +98,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter implements Serv @Value("${security.ldap.enabled}") private boolean ldapEnabled; @Value("${security.ldap.url}") private String ldapUrl; + @Value("${security.ldap.user-id-attr:uid}") private String ldapUserIdAttr; @Value("${security.ldap.user-dn-pattern:}") private String ldapUserDn; @Value("${security.ldap.user-search-base:}") private String ldapSearchBase; @Value("${security.ldap.user-search-filter:}") private String ldapSearchFilter; @@ -249,7 +252,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter implements Serv @Bean public LdapAuthenticationProvider ldapAuthenticationProvider() throws Exception { - return new LdapAuthenticationProvider(ldapAuthenticator(), ldapAuthoritiesPopulator()); + LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(ldapAuthenticator(), ldapAuthoritiesPopulator()); + ldapAuthenticationProvider.setUserDetailsContextMapper(customLdapUserDetailsMapper()); + + return ldapAuthenticationProvider; } @Bean @@ -284,6 +290,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter implements Serv return new DefaultLdapAuthoritiesPopulator(ldapContextSource(), null); } + @Bean + public LdapUserDetailsMapper customLdapUserDetailsMapper() { + logger.debug("ldapUserIdAttr: {}", ldapUserIdAttr); + + return new CustomLdapUserDetailsMapper(ldapUserIdAttr); + } + // CAS Authentication Configuration @Bean diff --git a/src/main/java/de/thm/arsnova/controller/LoginController.java b/src/main/java/de/thm/arsnova/controller/LoginController.java index 92cf3257..84436163 100644 --- a/src/main/java/de/thm/arsnova/controller/LoginController.java +++ b/src/main/java/de/thm/arsnova/controller/LoginController.java @@ -183,7 +183,7 @@ public class LoginController extends AbstractController { try { Authentication auth = ldapAuthenticationProvider.authenticate(token); if (auth.isAuthenticated()) { - SecurityContextHolder.getContext().setAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(auth); request.getSession(true).setAttribute( HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()); diff --git a/src/main/java/de/thm/arsnova/entities/User.java b/src/main/java/de/thm/arsnova/entities/User.java index dacb2294..938a1fe6 100644 --- a/src/main/java/de/thm/arsnova/entities/User.java +++ b/src/main/java/de/thm/arsnova/entities/User.java @@ -24,6 +24,7 @@ import org.scribe.up.profile.google.Google2Profile; import org.scribe.up.profile.twitter.TwitterProfile; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable; diff --git a/src/main/java/de/thm/arsnova/security/CustomLdapUserDetailsMapper.java b/src/main/java/de/thm/arsnova/security/CustomLdapUserDetailsMapper.java new file mode 100644 index 00000000..13339d0a --- /dev/null +++ b/src/main/java/de/thm/arsnova/security/CustomLdapUserDetailsMapper.java @@ -0,0 +1,36 @@ +package de.thm.arsnova.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.core.DirContextOperations; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; + +import java.util.Collection; + +/** + * Replaces the user ID provided by the authenticating user with the one that is part of LDAP object. This is necessary + * to get a consistent ID despite case insensitivity. + */ +public class CustomLdapUserDetailsMapper extends LdapUserDetailsMapper { + public static final Logger LOGGER = LoggerFactory.getLogger(CustomLdapUserDetailsMapper.class); + + private String userIdAttr; + + public CustomLdapUserDetailsMapper(String ldapUserIdAttr) { + this.userIdAttr = ldapUserIdAttr; + } + + public UserDetails mapUserFromContext(DirContextOperations ctx, String username, + Collection<? extends GrantedAuthority> authorities) { + String ldapUsername = ctx.getStringAttribute(userIdAttr); + if (ldapUsername == null) { + LOGGER.warn("LDAP attribute {} not set. Falling back to user provided username.", userIdAttr); + ldapUsername = username; + } + UserDetails userDetails = super.mapUserFromContext(ctx, ldapUsername, authorities); + + return userDetails; + } +} -- GitLab