Skip to content

Commit

Permalink
WIP: add auth-mode LDAP for LDAP passthrough auth option, #4
Browse files Browse the repository at this point in the history
  • Loading branch information
eidottermihi committed May 27, 2024
1 parent b88fdb5 commit c0a417b
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
package de.muenchen.oss.ezldap.spring;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.LdapTemplate;
Expand All @@ -44,7 +45,8 @@
public class LdapConfiguration {

@Bean
LdapContextSource ldapContextSource(final EzLdapConfigurationProperties props) {
@ConditionalOnProperty(name = "app.auth-mode", havingValue = "none")
LdapContextSource ldapContextSourceDefault(final EzLdapConfigurationProperties props) {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(props.getLdap().getUrl());
contextSource.setUserDn(props.getLdap().getUserDn());
Expand All @@ -63,7 +65,8 @@ LdapTemplate ldapTemplate(final LdapContextSource ldapContextSource) {
LdapService ldapService(final LdapTemplate template, final EzLdapConfigurationProperties props) {
final LdapBaseUserAttributesMapper ldapBaseUserAttributesMapper = new LdapBaseUserAttributesMapper();
final LdapOuAttributesMapper ldapOuAttributesMapper = new LdapOuAttributesMapper();
final LdapUserAttributesMapper ldapUserAttributesMapper = new LdapUserAttributesMapper(ldapBaseUserAttributesMapper);
final LdapUserAttributesMapper ldapUserAttributesMapper = new LdapUserAttributesMapper(
ldapBaseUserAttributesMapper);
return new LdapService(template, ldapUserAttributesMapper, ldapBaseUserAttributesMapper, ldapOuAttributesMapper,
new DtoMapperImpl(), props.getLdap().getUserSearchBase(), props.getLdap().getOuSearchBase());
}
Expand Down
9 changes: 8 additions & 1 deletion microservice/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
Expand Down Expand Up @@ -61,6 +63,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>


<!-- Testing -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ public class AppConfigurationProperties {
@Valid
private BasicAuthConfigurationProperties basicAuth;

@NestedConfigurationProperty
@Valid
private LdapAuthConfigurationProperties ldapAuth;

public enum AuthMode {
NONE, BASIC;
NONE, BASIC, LDAP;
}

private String swaggerDescription;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* The MIT License
* Copyright © 2023 Landeshauptstadt München | it@M
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.muenchen.oss.ezldap.config;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

/**
* @author michael.prankl
*
*/
@Data
public class LdapAuthConfigurationProperties {

/**
* LDAP user search base for authentification.
*/
@NotBlank
private String userSearchBase;

/**
* LDAP user search filter for authentification.
*/
@NotBlank
private String userSearchFilter = "(uid={0})";

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@
*/
package de.muenchen.oss.ezldap.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.config.Customizer;
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.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

Expand All @@ -49,22 +53,33 @@
public class SecurityConfiguration {

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, EzLdapConfigurationProperties configProps, AppConfigurationProperties appProps)
throws Exception {
SecurityFilterChain securityFilterChain(HttpSecurity http, EzLdapConfigurationProperties configProps,
AppConfigurationProperties appProps) throws Exception {
if (AuthMode.NONE.equals(appProps.getAuthMode())) {
log.info("Bootstrapping Spring Security filter chain for auth-mode 'none' ...");
http
.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.authorizeHttpRequests(authz -> authz.anyRequest().permitAll());
http.sessionManagement(
sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
} else if (AuthMode.LDAP.equals(appProps.getAuthMode())) {
log.info("Bootstrapping Spring Security filter chain for auth-mode 'ldap' ...");
http.authorizeHttpRequests(authz -> {
authz.requestMatchers("/openapi/v3/**", "/swagger-ui/**").permitAll();
authz.requestMatchers("/actuator/prometheus", "/actuator/info", "/actuator/health/**").permitAll();
authz.anyRequest().authenticated();
});
http.sessionManagement(
sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
// sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.ALWAYS));
http.httpBasic(Customizer.withDefaults());
} else {
log.info("Bootstrapping Spring Security filter chain for auth-mode 'basic' ...");
http
.authorizeHttpRequests(authz -> {
authz.requestMatchers("/openapi/v3/**", "/swagger-ui/**").permitAll();
authz.requestMatchers("/actuator/prometheus", "/actuator/info", "/actuator/health/**").permitAll();
authz.anyRequest().authenticated();
});
http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.authorizeHttpRequests(authz -> {
authz.requestMatchers("/openapi/v3/**", "/swagger-ui/**").permitAll();
authz.requestMatchers("/actuator/prometheus", "/actuator/info", "/actuator/health/**").permitAll();
authz.anyRequest().authenticated();
});
http.sessionManagement(
sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.httpBasic(Customizer.withDefaults());
}
return http.build();
Expand All @@ -73,8 +88,31 @@ SecurityFilterChain securityFilterChain(HttpSecurity http, EzLdapConfigurationPr
@Bean
@ConditionalOnProperty(name = "app.auth-mode", havingValue = "basic")
InMemoryUserDetailsManager userDetailsService(AppConfigurationProperties appProps) {
UserDetails userDetails = User.withUsername(appProps.getBasicAuth().getUser()).password(appProps.getBasicAuth().getPassword()).roles("USER").build();
UserDetails userDetails = User.withUsername(appProps.getBasicAuth().getUser())
.password(appProps.getBasicAuth().getPassword()).roles("USER").build();
return new InMemoryUserDetailsManager(userDetails);
}

@Autowired
public void configure(AuthenticationManagerBuilder auth, final EzLdapConfigurationProperties props,
final AppConfigurationProperties appProps) throws Exception {
if (appProps.getAuthMode().equals(AuthMode.LDAP)) {
auth.ldapAuthentication().userSearchBase(appProps.getLdapAuth().getUserSearchBase())
.userSearchFilter(appProps.getLdapAuth().getUserSearchFilter())
.groupSearchBase(props.getLdap().getOuSearchBase()).contextSource().url(props.getLdap().getUrl());
auth.eraseCredentials(false);
}
}

@Bean
@ConditionalOnProperty(name = "app.auth-mode", havingValue = "ldap")
LdapContextSource ldapContextSourcePassthrough(final EzLdapConfigurationProperties props) {
final LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(props.getLdap().getUrl());
contextSource.setAuthenticationSource(new SpringSecurityAuthenticationSource());
log.info("Initiating LDAP context-source with url='{}' and SpringSecurityAuthenticationSource.",
props.getLdap().getUrl());
return contextSource;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,8 @@ public class SpringdocsSwaggerConfig {

@Bean
GroupedOpenApi publicApi(OpenApiCustomizer globalResponseCodeCustomiser) {
return GroupedOpenApi.builder()
.group("ezLDAP")
.packagesToScan("de.muenchen.oss.ezldap.spring.rest.v1")
.addOpenApiCustomizer(globalResponseCodeCustomiser)
.build();
return GroupedOpenApi.builder().group("ezLDAP").packagesToScan("de.muenchen.oss.ezldap.spring.rest.v1")
.addOpenApiCustomizer(globalResponseCodeCustomiser).build();
}

@Bean
Expand All @@ -71,12 +68,10 @@ OpenApiCustomizer globalResponseCodeCustomiser(AppConfigurationProperties appPro
apiResponses.addApiResponse("500", response500);
}));

if (AuthMode.BASIC.equals(appProps.getAuthMode())) {
if (AuthMode.BASIC.equals(appProps.getAuthMode()) || AuthMode.LDAP.equals(appProps.getAuthMode())) {
openApi.addSecurityItem(new SecurityRequirement().addList("basicAuth"))
.components(new Components()
.addSecuritySchemes("basicAuth", new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("basic")));
.components(new Components().addSecuritySchemes("basicAuth",
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("basic")));
}
};
}
Expand Down

0 comments on commit c0a417b

Please sign in to comment.