From 7935485342e07d415605375a61ed0f5729259c3b Mon Sep 17 00:00:00 2001 From: Manfred Martin Date: Thu, 21 Mar 2024 19:05:41 +0100 Subject: [PATCH] Fix LDAP login page by using internal static resources rather than external resources (bootstrap.css...) --- .../ui/config/auth/LdapSecurityConfig.java | 31 +++++++++++++++++++ .../kafka/ui/controller/AuthController.java | 20 +++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapSecurityConfig.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapSecurityConfig.java index 20ce2aaa583..13fb39463db 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapSecurityConfig.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapSecurityConfig.java @@ -4,9 +4,11 @@ import com.provectus.kafka.ui.service.rbac.AccessControlService; import com.provectus.kafka.ui.service.rbac.extractor.RbacLdapAuthoritiesExtractor; +import java.net.URI; import java.util.Collection; import java.util.List; import java.util.Optional; +import com.provectus.kafka.ui.util.EmptyRedirectStrategy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -17,6 +19,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.http.HttpMethod; import org.springframework.ldap.core.DirContextOperations; import org.springframework.ldap.core.support.BaseLdapPathContextSource; import org.springframework.ldap.core.support.LdapContextSource; @@ -39,6 +42,9 @@ import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper; import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler; +import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler; +import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; @Configuration @EnableWebFluxSecurity @@ -51,6 +57,31 @@ public class LdapSecurityConfig { private final LdapProperties props; + public static final String LOGIN_URL = "/login"; + public static final String LOGOUT_URL = "/login?logout"; + + @Bean + public SecurityWebFilterChain configure(ServerHttpSecurity http) { + log.info("Configuring LDAP authentication."); + final var authHandler = new RedirectServerAuthenticationSuccessHandler(); + authHandler.setRedirectStrategy(new EmptyRedirectStrategy()); + final var logoutSuccessHandler = new RedirectServerLogoutSuccessHandler(); + logoutSuccessHandler.setLogoutSuccessUrl(URI.create(LOGOUT_URL)); + + return http.authorizeExchange(spec -> spec + .pathMatchers(AUTH_WHITELIST) + .permitAll() + .anyExchange() + .authenticated() + ) + .formLogin(spec -> spec.loginPage(LOGIN_URL).authenticationSuccessHandler(authHandler)) + .logout(spec -> spec + .logoutSuccessHandler(logoutSuccessHandler) + .requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout"))) + .csrf(ServerHttpSecurity.CsrfSpec::disable) + .build(); + } + @Bean public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource, LdapAuthoritiesPopulator authoritiesExtractor, diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/AuthController.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/AuthController.java index da453eb79a6..cb190fc56ff 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/AuthController.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/controller/AuthController.java @@ -15,16 +15,25 @@ @Slf4j public class AuthController { + @GetMapping(value = "/login", produces = {"text/html"}) + public Mono getLogin(ServerWebExchange exchange) { + Mono token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty()); + return token + .map(AuthController::csrfToken) + .defaultIfEmpty("") + .map(csrfTokenHtmlInput -> createPage(exchange, csrfTokenHtmlInput, "login")); + } + @GetMapping(value = "/auth", produces = {"text/html"}) public Mono getAuth(ServerWebExchange exchange) { Mono token = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty()); return token .map(AuthController::csrfToken) .defaultIfEmpty("") - .map(csrfTokenHtmlInput -> createPage(exchange, csrfTokenHtmlInput)); + .map(csrfTokenHtmlInput -> createPage(exchange, csrfTokenHtmlInput, "auth")); } - private byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) { + private byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput, String path) { MultiValueMap queryParams = exchange.getRequest() .getQueryParams(); String contextPath = exchange.getRequest().getPath().contextPath().value(); @@ -44,7 +53,7 @@ private byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) + " \n" + " \n" + "
\n" - + formLogin(queryParams, contextPath, csrfTokenHtmlInput) + + formLogin(queryParams, contextPath, csrfTokenHtmlInput, path) + "
\n" + " \n" + ""; @@ -54,12 +63,13 @@ private byte[] createPage(ServerWebExchange exchange, String csrfTokenHtmlInput) private String formLogin( MultiValueMap queryParams, - String contextPath, String csrfTokenHtmlInput) { + String contextPath, String csrfTokenHtmlInput, + String path) { boolean isError = queryParams.containsKey("error"); boolean isLogoutSuccess = queryParams.containsKey("logout"); return - "
\n" + " \n" + " \n" + createError(isError) + createLogoutSuccess(isLogoutSuccess)