How do I provide Admin username and password to ActiveDirectoryLdapAuthenticationProvider? - spring

Typically, we configure a vanilla LdapAuthenticationProvider with a Dn and a password.
auth
.ldapAuthentication()
.contextSource()
.url(env.getProperty("spring.ldap.urls") + "/" + env.getProperty("spring.ldap.base"))
.managerDn(env.getProperty("ldap.managerDn") + "," + env.getProperty("spring.ldap.base"))
.managerPassword(env.getProperty("spring.ldap.password"))
How should I go about achieving a similar configuration with ActiveDirectoryLdapAuthenticationProvider ?
Snippet of my current progress:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider adProvider = new ActiveDirectoryLdapAuthenticationProvider("domain.org",
"ldap://activedirectory-url:389");
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
auth.authenticationProvider(adProvider);
}
}
I'm scratching my head about how to implement the same functionality with ActiveDirectoryLdapAuthenticationProvider. I have searched high and low but I'm at a loss on how to replicate the previous configuration with the Active Directory.

Related

How to configure ldap in spring-security 5.7 while retaining basic form login

I'm trying to configure my webSecurity to use both ldap and basic authentication (jdbc) with the new component-based security configuration (no WebSecurityConfigurerAdapter) but I can't get it to use both.
The required result is for spring to first attempt ldap, and if it doesn't find (or just fails for now is good enough) attempt to login using basic autentication.
The project is a migration from an older Spring-Boot version and with WebSecurityConfigurerAdapter the following code is what worked:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/services/**").permitAll().anyRequest().authenticated();
http.httpBasic();
http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
http.logout().permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userDetails);
//#formatter:off
auth.ldapAuthentication()
.userSearchFilter("(uid={0})")
.userSearchBase("ou=people")
.groupSearchFilter("(uniqueMember={0})")
.groupSearchBase("ou=groups")
.groupRoleAttribute("cn")
.rolePrefix("ROLE_")
.userDetailsContextMapper(customLdapUserDetailsContextMapper())
.contextSource()
.url(ldapUrl);
//#formatter:on
}
#Bean
CustomLdapUserDetailsContextMapper customLdapUserDetailsContextMapper()
{
CustomLdapUserDetailsContextMapper mapper = new CustomLdapUserDetailsContextMapper();
mapper.setCustomUserDetailsService(userDetailsService());
return mapper;
}
//Implementation of custom contextMapper is not relevant for example i believe, basicly it maps some ldap roles, but for testing i don't use roles yet
}
and this is what my conversion to the new style looks like:
#Configuration
public class WebSecurityConfig
{
#Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager ldapAuthenticationManager) throws Exception
{
// #formatter:off
http.authorizeRequests()
.mvcMatchers("/services/**").permitAll()
.mvcMatchers("/resources/**").permitAll()
.mvcMatchers("/webjars/**").permitAll()
.anyRequest().authenticated();
http.httpBasic();
http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
http.logout().permitAll();
http.csrf().disable();
http.authenticationManager(ldapAuthenticationManager); //THIS LINE SEEMS TO BE PROBLEMATIC
// #formatter:on
return http.build();
}
#Bean
public AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource ldapContextSource, UserDetailsService userDetailsService)
{
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(ldapContextSource);
UserDetailsServiceLdapAuthoritiesPopulator ldapAuthoritiesPopulator = new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService);
factory.setUserSearchFilter("(uid={0})");
factory.setUserSearchBase("ou=people");
factory.setLdapAuthoritiesPopulator(ldapAuthoritiesPopulator);
return factory.createAuthenticationManager();
}
}
when in the above new code the line http.authenticationManager(ldapAuthenticationManager); is enabled ldap login works fine (and it even binds roles from database user), but basic login doesn't work. however when the line is disabled basic login works but ldap does not.
Any help on how to get spring to use both logins would be much appreciated.
Instead of creating a custom AuthenticationManager, you can create the AuthenticationProvider that will be used for LDAP authentication.
You can configure the provider on HttpSecurity:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, LdapAuthenticator authenticator) throws Exception {
// ...
http.authenticationProvider(
new LdapAuthenticationProvider(authenticator, ldapAuthoritiesPopulator));
// ...
return http.build();
}
#Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
BindAuthenticator authenticator = new BindAuthenticator(contextSource);
authenticator.setUserSearch(
new FilterBasedLdapUserSearch("ou=people", "(uid={0})", contextSource));
return authenticator;
}

disable spring formlogin and basic auth

I have the following spring boot 2.0 config but I am still getting the basic auth login screen. I DO NOT want to disable all spring security like almost every post on the internet suggests. I only want to stop the form login page and basic auth so I can use my own.
I have seen all the suggestions with permitAll and exclude = {SecurityAutoConfiguration.class} and a few others that I can't remember anymore. Those are not what I want. I want to use spring security but I wan my config not Spring Boots. Yes I know many people are going to say this is a duplicate but I disagree because all the other answers are to disable spring security completely and not just stop the stupid login page.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomSecurity extends WebSecurityConfigurerAdapter {
private final RememberMeServices rememberMeService;
private final AuthenticationProvider customAuthProvider;
#Value("${server.session.cookie.secure:true}")
private boolean useSecureCookie;
#Inject
public CustomSecurity(RememberMeServices rememberMeService, AuthenticationProvider customAuthProvider) {
super(true);
this.rememberMeService = rememberMeService;
this.bouncerAuthProvider = bouncerAuthProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/**").antMatchers("/webjars/**").antMatchers("/swagger-resources/**")
.antMatchers("/swagger-ui.html");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().formLogin().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).headers().frameOptions().disable();
http.authenticationProvider(customAuthProvider).authorizeRequests().antMatchers("/health").permitAll()
.anyRequest().authenticated();
http.rememberMe().rememberMeServices(rememberMeService).useSecureCookie(useSecureCookie);
http.exceptionHandling().authenticationEntryPoint(new ForbiddenEntryPoint());
}
}
If you want to redirect to your own login page, i can show your sample code and configuration
remove the http.httpBasic().disable().formLogin().disable();, you should set your own login page to redirect instead of disable form login
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/my_login").permitAll().and().authorizeRequests().anyRequest().authenticated();
http.formLogin().loginPage("/my_login");
}
then create your own LoginController
#Controller
public class LoginController {
#RequestMapping("/my_login")
public ModelAndView myLogin() {
return new ModelAndView("login");
}
}
you can specified the login with thymeleaf view resolver

Http Basic authentication using Spring Security with Angular 6

I am developing a Angular 6 application for which I am using Spring Boot for REST services And using Spring security Basic authentication for authorization purpose.
The issue is , basic authentication is not working and it shows pop up everytime i login into the application. I am using interceptor in angular 6 for adding the authorization header.
Can anybody please help as to where I am going wrong ?
Code :
#Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
let username=localStorage.getItem("username")
let password=localStorage.getItem("password")
const customReq = req.clone({
headers: req.headers.set("Authorization", "Basic " + btoa(username+":"+password) )
});
return next.handle(customReq);
}
constructor() { }
}
And the spring boot code
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.httpBasic().and().authorizeRequests().antMatchers("/index.html", "/", "/home/**", "/login").permitAll()
.anyRequest().authenticated().and().csrf().disable().httpBasic();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}
Since I am new to Spring please let me know if you need any other files.
This generally happens when you #SpringBootApplication class is in one package and your SecurityConfig class is not present in sub package.
For example, if Application is in com.example.app package and your security config is in com.security.example then Spring cannot find your configuration and goes ahead with Spring security defaults like form based login page.
Please check the package structure and add #ComponentScan if requried.

How to Connect to an LDAP Server and how to validate AD Groups in a Spring Boot Application

I have a Spring Boot application.
I want to connect to an LDAP server and query for valid AD groups.
I hope you are familiar with Spring Security. Write a configuration class which extends WebSecurityConfigurerAdapter and configure AD authentication provider. Refer below code. Change antMatchers to match your app.
Add dependencies spring-security-ldap,spring-boot-starter-security as well.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers(AppConstants.LOGIN_URI).fullyAuthenticated().and().formLogin().permitAll();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
String ldapUrl = env.getProperty(AppConstants.LDAP_URL);
String ldapDomain = env.getProperty(AppConstants.LDAP_DOMAIN);
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(ldapDomain,
ldapUrl);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
auth.authenticationProvider(provider);
}
To get the groups to which user is assigned inject instance of Authentication/Principal in controller class. Get
To get all attributes against the user like email, manager etc. you may will need to write a custom mapper though. Refer code below.
#GetMapping(value = { AppConstants.REST_API_LOGIN })
public ResponseEntity<User> authenticateUser(Authentication auth) {
List<String> ldapRoles = new ArrayList<String>();
auth.getAuthorities().forEach(a -> ldapRoles.add(a.getAuthority()));
/*
Write service class methods to compare ldapRoles against app specific roles from DB and exception handlers to handle exceptions
*/
User user = userService.getUser(auth);
if (!user.isAuthorized()) {
logger.info("User:" + auth.getName() + " is not authorized to access program.");
throw new UserAuthenticationException(AppConstants.NOT_VALID_MEMBER, user);
}
logger.info("User:" + auth.getName() + " logged in");
return new ResponseEntity<User>(user, HttpStatus.OK);
}

Understanding Spring Boot's Oauth2 starter

I started off looking at the Oauth2 starter project and minimal configuration.
https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/jdbc/src/main/java/demo/Application.java
All the examples either use in memory configuration or jdbc configuration for storing client roles (e.g ClientDetailsServiceConfigurer) . In my case the details should come in LDAP. So I have two questions.
How do override the default to go to ldap instead of memory or jdbc.
In general , where how do I unravel the Spring Boot thread and read the starter source code and how to change default config ? All I see is a high level annotation.
org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer
This indirection in Spring Boot makes it extremely difficult to follow and scant documentation doesn't help. Or maybe I am missing something?
thanks !!! this has been bugging me for a while.
To implement Oauth2 with LDAP, you may follow this tutorial : https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security.
You may also take a look a this other question: spring-security-oauth2 2.0.7 refresh token UserDetailsService Configuration - UserDetailsService is required
As for your other question "I want to follow the request and see what components get invoked and when": I suggest you add logging.
(1) Add logging in every method
(2) Set log level for security package in application.properties:
logging.level.org.springframework.security=DEBUG
(3) Add CommonsRequestLoggingFilter:
#Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
LOGGER.info("Creating CommonsRequestLoggingFilter");
CommonsRequestLoggingFilter crlf = new CommonsRequestLoggingFilter();
crlf.setIncludeClientInfo(true);
crlf.setIncludeQueryString(true);
crlf.setIncludePayload(true);
return crlf;
}
(4) Add log level for CommonsRequestLoggingFilter (in application.properties):
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
For the OAuth/LDAP tutorial, here's the notable parts (quoted from https://raymondhlee.wordpress.com/2015/12/05/oauth2-authorization-server-with-spring-security):
Authorization Server Configuration Below is my implementation of the
AuthorizationServerConfigurerAdapter. The database schema for JDBC
client details and token services can be found in here.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private DataSource dataSource;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new JdbcTokenStore(dataSource)).authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
Login Security Configuration Below is the security configuration
handling user authorization.
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE) // note 1
public class LoginConfig extends WebSecurityConfigurerAdapter {
#Value("${ldap.domain}")
private String DOMAIN;
#Value("${ldap.url}")
private String URL;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresSecure();
// Only requests matching regex are handled by this security configurer
http.requestMatchers().regexMatchers("/login", "/login.+", "/oauth/.+", "/j_spring_security_check", "/logout"); //
AuthenticationEntryPoint entryPoint = entryPoint();
http.exceptionHandling().authenticationEntryPoint(entryPoint);
http.formLogin(); // note 3i
http.addFilter(usernamePasswordAuthenticationFilter());
http.authorizeRequests().antMatchers("/login").permitAll();
http.authorizeRequests().antMatchers("/oauth/**").authenticated();
http.authorizeRequests().antMatchers("/j_spring_security_check").anonymous().and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception { // note 4
authManagerBuilder.parentAuthenticationManager(authenticationManager());
}
protected AuthenticationManager authenticationManager() {
return new ProviderManager(Arrays.asList(activeDirectoryLdapAuthenticationProvider()));
}
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(DOMAIN, URL);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
private AuthenticationEntryPoint entryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
private UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager();
AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?login_error=true");
filter.setAuthenticationFailureHandler(failureHandler);
return filter;
}
}

Resources