-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LDAP cahcing #288
base: master
Are you sure you want to change the base?
LDAP cahcing #288
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.ericsson.eiffel.remrem.publish.config; | ||
|
||
import org.springframework.cache.concurrent.ConcurrentMapCache; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.userdetails.UserCache; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.cache.NullUserCache; | ||
import org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache; | ||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; | ||
import org.springframework.security.ldap.authentication.LdapAuthenticator; | ||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; | ||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.util.StringUtils; | ||
|
||
public class CachingLdapAuthenticationProvider extends LdapAuthenticationProvider { | ||
|
||
private UserCache userCache = new NullUserCache(); | ||
|
||
/** | ||
* Create an instance with the supplied authenticator and authorities populator | ||
* implementations. | ||
* | ||
* @param authenticator the authentication strategy (bind, password comparison, etc) | ||
* to be used by this provider for authenticating users. | ||
* @param authoritiesPopulator the strategy for obtaining the authorities for a given | ||
*/ | ||
|
||
public CachingLdapAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) { | ||
super(authenticator, authoritiesPopulator); | ||
} | ||
|
||
public void setUserCache(UserCache userCache) { | ||
this.userCache = userCache; | ||
} | ||
|
||
|
||
@Override | ||
public Authentication authenticate(Authentication authentication) { | ||
String userName = authentication.getName(); | ||
UsernamePasswordAuthenticationToken userToken = (UsernamePasswordAuthenticationToken) authentication; | ||
UserDetails userDetailsFromCache = userCache.getUserFromCache(userName); | ||
if (userDetailsFromCache != null) { | ||
additionalAuthenticationChecks(userDetailsFromCache, userToken); | ||
return createSuccessfulAuthentication(userToken, userDetailsFromCache); | ||
} else { | ||
Authentication authenticationFromProvider = super.authenticate(authentication); | ||
userCache.putUserInCache((UserDetails)authenticationFromProvider.getPrincipal()); | ||
return authenticationFromProvider; | ||
} | ||
|
||
} | ||
|
||
protected void additionalAuthenticationChecks(UserDetails userDetails, | ||
UsernamePasswordAuthenticationToken authentication) { | ||
Object credentials = authentication.getCredentials(); | ||
if (!StringUtils.isEmpty(credentials)) { | ||
String presentedPassword = authentication.getCredentials().toString(); | ||
if (userDetails.getPassword().equals(presentedPassword)) { | ||
// password matches | ||
return; | ||
} | ||
} | ||
throw new BadCredentialsException(messages.getMessage( | ||
"AbstractUserDetailsAuthenticationProvider.badCredentials", | ||
"Bad credentials")); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,37 @@ | |
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.cache.annotation.Cacheable; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.cache.concurrent.ConcurrentMapCache; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.ldap.core.support.BaseLdapPathContextSource; | ||
import org.springframework.ldap.core.support.LdapContextSource; | ||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | ||
import org.springframework.security.core.userdetails.UserCache; | ||
import org.springframework.security.core.userdetails.cache.SpringCacheBasedUserCache; | ||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.ldap.authentication.BindAuthenticator; | ||
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.cache.CacheManager; | ||
import org.springframework.cache.caffeine.CaffeineCacheManager; | ||
import com.github.benmanes.caffeine.cache.Caffeine; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* This class is used to enable the ldap authentication based on property | ||
|
@@ -64,6 +95,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
@Value("${activedirectory.connectionTimeOut:#{127000}}") | ||
private Integer ldapTimeOut = DEFAULT_LDAP_CONNECTION_TIMEOUT; | ||
|
||
@Value("${LdapCacheTTL}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The property should be part of configuration file (including appropriate description). |
||
private Integer LdapCacheTTL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Java conventions should be followed: start with lower-case letter. |
||
|
||
// built in connection timeout value for ldap if the network issue happens | ||
public static final Integer DEFAULT_LDAP_CONNECTION_TIMEOUT = 127000; | ||
|
||
|
@@ -74,20 +108,54 @@ public Integer getTimeOut() { | |
@Autowired | ||
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint; | ||
|
||
@Autowired | ||
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { | ||
@Bean | ||
public UserCache userCache() { | ||
if (cacheManager().getCache("authenticationCache") == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, define a constant for "authenticationCache". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I don't understand it, but I was unable to run it in debugger. We should have a call and discuss that. |
||
throw new IllegalStateException ("Cache 'authenticationCache' is required but not available"); | ||
} | ||
return new SpringCacheBasedUserCache(cacheManager().getCache("authenticationCache")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should always a new instance be created? Is it just a wrapper? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. refactored to use a single isntance |
||
} | ||
|
||
@Bean | ||
public CacheManager cacheManager() { | ||
CaffeineCacheManager cacheManager = new CaffeineCacheManager(); | ||
cacheManager.setCaffeine(Caffeine.newBuilder() | ||
.expireAfterWrite(LdapCacheTTL, TimeUnit.MINUTES)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it be in minutes? All other timeouts are in seconds. It'll be very confused for users... |
||
return cacheManager; | ||
} | ||
|
||
@Bean | ||
public LdapAuthoritiesPopulator ldapAuthoritiesPopulator() { | ||
LdapContextSource contextSource = ldapContextSource(); | ||
return new DefaultLdapAuthoritiesPopulator(contextSource, null); | ||
} | ||
|
||
@Override | ||
public void configure(AuthenticationManagerBuilder auth) throws Exception { | ||
final String jasyptKey = RabbitMqPropertiesConfig.readJasyptKeyFile(jasyptKeyFilePath); | ||
if (managerPassword.startsWith("{ENC(") && managerPassword.endsWith("}")) { | ||
managerPassword = DecryptionUtils.decryptString( | ||
managerPassword.substring(1, managerPassword.length() - 1), jasyptKey); | ||
} | ||
LOGGER.debug("LDAP server url: " + ldapUrl); | ||
auth.ldapAuthentication() | ||
.userSearchFilter(userSearchFilter) | ||
.contextSource(ldapContextSource()); | ||
LdapContextSource contextSource = ldapContextSource(); | ||
BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource); | ||
bindAuthenticator.setUserSearch(new FilterBasedLdapUserSearch("", userSearchFilter, contextSource)); | ||
|
||
|
||
LdapAuthoritiesPopulator ldapAuthoritiesPopulator = ldapAuthoritiesPopulator(); | ||
|
||
// Create and use the caching LDAP authentication provider | ||
CachingLdapAuthenticationProvider cachingProvider = | ||
new CachingLdapAuthenticationProvider(bindAuthenticator, ldapAuthoritiesPopulator); | ||
|
||
cachingProvider.setUserCache(userCache()); | ||
auth.authenticationProvider(cachingProvider); | ||
|
||
} | ||
|
||
public BaseLdapPathContextSource ldapContextSource() { | ||
@Bean | ||
public LdapContextSource ldapContextSource() { | ||
LdapContextSource ldap = new LdapContextSource(); | ||
ldap.setUrl(ldapUrl); | ||
ldap.setBase(rootDn); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a default TTL? I mean something like for ldapTimeOut, see line 95.