Skip to content
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

Auth: Impl custom auth page #728

Merged
merged 2 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .java-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
21
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
package io.kafbat.ui.config.auth;

import io.kafbat.ui.util.EmptyRedirectStrategy;
import java.net.URI;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler;

abstract class AbstractAuthSecurityConfig {

protected AbstractAuthSecurityConfig() {

}

protected static final String LOGIN_URL = "/login";
protected static final String LOGOUT_URL = "/auth?logout";

protected static final String[] AUTH_WHITELIST = {
"/css/**",
"/js/**",
"/media/**",
/* STATIC */
"/index.html",
"/assets/**",
"/manifest.json",
"/favicon.svg",
"/favicon/**",

"/static/**",
"/resources/**",

/* ACTUATOR */
"/actuator/health/**",
"/actuator/info",
"/actuator/prometheus",
"/auth",

/* AUTH */
"/login",
"/logout",
"/oauth2/**",
"/static/**"
"/api/config/authentication",
"/api/authorization"
};

protected RedirectServerAuthenticationSuccessHandler emptyRedirectSuccessHandler() {
final var authHandler = new RedirectServerAuthenticationSuccessHandler();
authHandler.setRedirectStrategy(new EmptyRedirectStrategy());
return authHandler;
}

protected RedirectServerLogoutSuccessHandler redirectLogoutSuccessHandler() {
final var logoutSuccessHandler = new RedirectServerLogoutSuccessHandler();
logoutSuccessHandler.setLogoutSuccessUrl(URI.create(LOGOUT_URL));
return logoutSuccessHandler;
}

}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package io.kafbat.ui.config.auth;

import io.kafbat.ui.util.EmptyRedirectStrategy;
import io.kafbat.ui.util.StaticFileWebFilter;
import java.net.URI;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
Expand All @@ -20,32 +22,28 @@
@Slf4j
public class BasicAuthSecurityConfig extends AbstractAuthSecurityConfig {

public static final String LOGIN_URL = "/auth";
public static final String LOGOUT_URL = "/auth?logout";

@Bean
public SecurityWebFilterChain configure(ServerHttpSecurity http) {
log.info("Configuring LOGIN_FORM 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
var builder = http.authorizeExchange(spec -> spec
.pathMatchers(AUTH_WHITELIST)
.permitAll()
.anyExchange()
.authenticated()
)
.formLogin(spec -> spec.loginPage(LOGIN_URL).authenticationSuccessHandler(authHandler))
.formLogin(form -> form
.loginPage(LOGIN_URL)
.authenticationSuccessHandler(emptyRedirectSuccessHandler())
)
.logout(spec -> spec
.logoutSuccessHandler(logoutSuccessHandler)
.logoutSuccessHandler(redirectLogoutSuccessHandler())
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout")))
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.build();
.csrf(ServerHttpSecurity.CsrfSpec::disable);
Dismissed Show dismissed Hide dismissed

builder.addFilterAt(new StaticFileWebFilter(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);

return builder.build();
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package io.kafbat.ui.config.auth;

import static io.kafbat.ui.config.auth.AbstractAuthSecurityConfig.AUTH_WHITELIST;

import io.kafbat.ui.service.rbac.AccessControlService;
import io.kafbat.ui.service.rbac.extractor.RbacLdapAuthoritiesExtractor;
import io.kafbat.ui.util.StaticFileWebFilter;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
Expand All @@ -14,15 +13,16 @@
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -36,14 +36,15 @@
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.util.matcher.ServerWebExchangeMatchers;

@Configuration
@EnableWebFluxSecurity
@ConditionalOnProperty(value = "auth.type", havingValue = "LDAP")
@EnableConfigurationProperties(LdapProperties.class)
@RequiredArgsConstructor
@Slf4j
public class LdapSecurityConfig {
public class LdapSecurityConfig extends AbstractAuthSecurityConfig {

private final LdapProperties props;

Expand Down Expand Up @@ -121,16 +122,24 @@
log.info("Active Directory support for LDAP has been enabled.");
}

return http.authorizeExchange(spec -> spec
var builder = http.authorizeExchange(spec -> spec
.pathMatchers(AUTH_WHITELIST)
.permitAll()
.anyExchange()
.authenticated()
)
.formLogin(Customizer.withDefaults())
.logout(Customizer.withDefaults())
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.build();
.formLogin(form -> form
.loginPage(LOGIN_URL)
.authenticationSuccessHandler(emptyRedirectSuccessHandler())
)
.logout(spec -> spec
.logoutSuccessHandler(redirectLogoutSuccessHandler())
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout")))
.csrf(ServerHttpSecurity.CsrfSpec::disable);
Dismissed Show dismissed Hide dismissed

builder.addFilterAt(new StaticFileWebFilter(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);

return builder.build();
}

private static class UserDetailsMapper extends LdapUserDetailsMapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.kafbat.ui.config.auth.logout.OAuthLogoutSuccessHandler;
import io.kafbat.ui.service.rbac.AccessControlService;
import io.kafbat.ui.service.rbac.extractor.ProviderAuthorityExtractor;
import io.kafbat.ui.util.StaticFileWebFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -19,6 +20,7 @@
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
Expand Down Expand Up @@ -50,16 +52,20 @@
public SecurityWebFilterChain configure(ServerHttpSecurity http, OAuthLogoutSuccessHandler logoutHandler) {
log.info("Configuring OAUTH2 authentication.");

return http.authorizeExchange(spec -> spec
var builder = http.authorizeExchange(spec -> spec
.pathMatchers(AUTH_WHITELIST)
.permitAll()
.anyExchange()
.authenticated()
)
.oauth2Login(Customizer.withDefaults())
.logout(spec -> spec.logoutSuccessHandler(logoutHandler))
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.build();
.csrf(ServerHttpSecurity.CsrfSpec::disable);
Dismissed Show dismissed Hide dismissed
Dismissed Show dismissed Hide dismissed


builder.addFilterAt(new StaticFileWebFilter(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING);

return builder.build();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.kafbat.ui.api.ApplicationConfigApi;
import io.kafbat.ui.config.ClustersProperties;
import io.kafbat.ui.model.ActionDTO;
import io.kafbat.ui.model.AppAuthenticationSettingsDTO;
import io.kafbat.ui.model.ApplicationConfigDTO;
import io.kafbat.ui.model.ApplicationConfigPropertiesDTO;
import io.kafbat.ui.model.ApplicationConfigValidationDTO;
Expand Down Expand Up @@ -66,6 +67,13 @@ public Mono<ResponseEntity<ApplicationInfoDTO>> getApplicationInfo(ServerWebExch
return Mono.just(applicationInfoService.getApplicationInfo()).map(ResponseEntity::ok);
}

@Override
public Mono<ResponseEntity<AppAuthenticationSettingsDTO>> getAuthenticationSettings(
ServerWebExchange exchange) {
return Mono.just(applicationInfoService.getAuthenticationProperties())
.map(ResponseEntity::ok);
}

@Override
public Mono<ResponseEntity<ApplicationConfigDTO>> getCurrentConfig(ServerWebExchange exchange) {
var context = AccessContext.builder()
Expand Down Expand Up @@ -109,7 +117,7 @@ public Mono<ResponseEntity<UploadedFileInfoDTO>> uploadConfigRelatedFile(Flux<Pa
.then(fileFlux.single())
.flatMap(file ->
dynamicConfigOperations.uploadConfigRelatedFile((FilePart) file)
.map(path -> new UploadedFileInfoDTO().location(path.toString()))
.map(path -> new UploadedFileInfoDTO(path.toString()))
.map(ResponseEntity::ok))
.doOnEach(sig -> audit(context, sig));
}
Expand Down
99 changes: 0 additions & 99 deletions api/src/main/java/io/kafbat/ui/controller/AuthController.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.kafbat.ui.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@RequiredArgsConstructor
@Slf4j
public class AuthenticationController {

private static final String INDEX_HTML = "/static/index.html";

@GetMapping(value = "/login", produces = {"text/html"})
public Mono<ClassPathResource> getLoginPage() {
return Mono.just(new ClassPathResource(INDEX_HTML));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
@RestController
@RequiredArgsConstructor
@Slf4j
public class AccessController implements AuthorizationApi {
public class AuthorizationController implements AuthorizationApi {

private final AccessControlService accessControlService;

Expand Down
Loading
Loading