diff --git a/CHANGELOG.md b/CHANGELOG.md index 402cbf9bf5548325d1a21c36d60ac3f438b00ffe..35b218e734362076075fe34079639c2e585258b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 2.3.1 +Bug fixes: +* Case-insensitive user IDs are now correctly handled for LDAP authentication. +* LDAP authentication does no longer request unnecessary user attributes. + ## 2.3 Major features: * Improved LDAP authentication support: Additional settings for LDAP search and @@ -17,7 +22,7 @@ Minor features and changes: question format flashcard are now active by default and can no longer be disabled for the whole ARSnova installation. -Bugfixes: +Bug fixes: * The `security.authentication.login-try-limit` setting now works as intended. Changes for developers: @@ -48,7 +53,7 @@ Major features: * The API has been extended to support features introduced with ARSnova Mobile 2.2. -Bugfixes: +Bug fixes: * User content consisting of JSON could not be loaded and rendered connected data unloadable as well. diff --git a/pom.xml b/pom.xml index f485600d09c5bcbff6013377481b70e61c0e8de1..ab0ad876709976976017f35a68a6844b173e4c95 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <packaging>war</packaging> <properties> - <io.spring.platform-version>2.0.3.RELEASE</io.spring.platform-version> + <io.spring.platform-version>2.0.4.RELEASE</io.spring.platform-version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <timestamp>${maven.build.timestamp}</timestamp> <sonar.language>java</sonar.language> @@ -177,7 +177,7 @@ <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> - <version>3.2.1</version> + <version>3.2.2</version> </dependency> <dependency> <groupId>org.slf4j</groupId> @@ -325,7 +325,7 @@ <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <version>9.2.15.v20160210</version> + <version>9.2.16.v20160414</version> <configuration> <scanIntervalSeconds>1</scanIntervalSeconds> <webApp> @@ -347,7 +347,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> - <version>3.5</version> + <version>3.5.1</version> <configuration> <locales>en</locales> </configuration> @@ -355,7 +355,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>sonar-maven-plugin</artifactId> - <version>3.0.1</version> + <version>3.0.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/src/main/java/de/thm/arsnova/config/SecurityConfig.java b/src/main/java/de/thm/arsnova/config/SecurityConfig.java index 6ca21c5afc0307a396cf0c0adef0deb07935ce3d..094dbcbeb3bd5fd628f351a49c62632869c99330 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 @@ -268,6 +274,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter implements Serv @Bean public LdapAuthenticator ldapAuthenticator() throws Exception { BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource()); + authenticator.setUserAttributes(new String[] {ldapUserIdAttr}); if (!"".equals(ldapSearchFilter)) { logger.debug("ldapSearch: {} {}", ldapSearchBase, ldapSearchFilter); authenticator.setUserSearch(new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, ldapContextSource())); @@ -284,6 +291,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 c27b99096d3009f37b658f030ed79687c55f8baf..e42a2a3a4da34691d68a00177d47c263a8bc8473 100644 --- a/src/main/java/de/thm/arsnova/controller/LoginController.java +++ b/src/main/java/de/thm/arsnova/controller/LoginController.java @@ -191,7 +191,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 dacb2294e8176636a48d1e365e356b59e0850669..938a1fe6adc81124d58d829b44c9b61a456fa266 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 0000000000000000000000000000000000000000..13339d0a11a3edc0389ad8768f49b8c071dacb26 --- /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; + } +} diff --git a/src/site/resources/arsnova.png b/src/site/resources/arsnova.png index 6d39185ff15bce763123846b32faae986ab3ce1b..b4e4b9ce65b6a893046baa0a24935d27baa43a03 100644 Binary files a/src/site/resources/arsnova.png and b/src/site/resources/arsnova.png differ