Cognito logout + revamp (#1063)
* application yml example * Update SSO guide * Add cognito logout handler * Fix annotations * impl a separate config file for cognito * cleanup * Rollback auth.type change * Add compose example * Auth revamp * Review suggestions * Use configurationProperties, rename urls to uris
This commit is contained in:
parent
6df2d0b602
commit
5db2c17994
8 changed files with 206 additions and 6 deletions
|
@ -11,3 +11,4 @@
|
|||
9. [kafka-ui-reverse-proxy.yaml](./kafka-ui-reverse-proxy.yaml) - An example for using the app behind a proxy (like nginx).
|
||||
10. [kafka-ui-sasl.yaml](./kafka-ui-sasl.yaml) - SASL auth for Kafka.
|
||||
11. [kafka-ui-traefik-proxy.yaml](./kafka-ui-traefik-proxy.yaml) - Traefik specific proxy configuration.
|
||||
12. [oauth-cognito.yaml](./oauth-cognito.yaml) - OAuth2 with Cognito
|
||||
|
|
22
documentation/compose/oauth-cognito.yaml
Normal file
22
documentation/compose/oauth-cognito.yaml
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
version: '3.4'
|
||||
services:
|
||||
|
||||
kafka-ui:
|
||||
container_name: kafka-ui
|
||||
image: provectuslabs/kafka-ui:local
|
||||
ports:
|
||||
- 8080:8080
|
||||
depends_on:
|
||||
- kafka0 # OMITTED, TAKE UP AN EXAMPLE FROM OTHER COMPOSE FILES
|
||||
environment:
|
||||
KAFKA_CLUSTERS_0_NAME: local
|
||||
KAFKA_CLUSTERS_0_PROPERTIES_SECURITY_PROTOCOL: SSL
|
||||
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka0:29092
|
||||
AUTH_TYPE: OAUTH2_COGNITO
|
||||
AUTH_COGNITO_ISSUER_URI: "https://cognito-idp.eu-central-1.amazonaws.com/eu-central-xxxxxx"
|
||||
AUTH_COGNITO_CLIENT_ID: ""
|
||||
AUTH_COGNITO_CLIENT_SECRET: ""
|
||||
AUTH_COGNITO_SCOPE: "openid"
|
||||
AUTH_COGNITO_USER_NAME_ATTRIBUTE: "username"
|
||||
AUTH_COGNITO_LOGOUT_URI: "https://<domain>.auth.eu-central-1.amazoncognito.com/logout"
|
|
@ -0,0 +1,53 @@
|
|||
package com.provectus.kafka.ui.config;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.web.server.WebSession;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class CognitoOidcLogoutSuccessHandler implements ServerLogoutSuccessHandler {
|
||||
|
||||
private final String logoutUrl;
|
||||
private final String clientId;
|
||||
|
||||
@Override
|
||||
public Mono<Void> onLogoutSuccess(final WebFilterExchange exchange, final Authentication authentication) {
|
||||
final ServerHttpResponse response = exchange.getExchange().getResponse();
|
||||
response.setStatusCode(HttpStatus.FOUND);
|
||||
|
||||
final var requestUri = exchange.getExchange().getRequest().getURI();
|
||||
|
||||
final var fullUrl = UrlUtils.buildFullRequestUrl(requestUri.getScheme(),
|
||||
requestUri.getHost(), requestUri.getPort(),
|
||||
requestUri.getPath(), requestUri.getQuery());
|
||||
|
||||
final UriComponents baseUrl = UriComponentsBuilder
|
||||
.fromHttpUrl(fullUrl)
|
||||
.replacePath("/")
|
||||
.replaceQuery(null)
|
||||
.fragment(null)
|
||||
.build();
|
||||
|
||||
final var uri = UriComponentsBuilder
|
||||
.fromUri(URI.create(logoutUrl))
|
||||
.queryParam("client_id", clientId)
|
||||
.queryParam("logout_uri", baseUrl)
|
||||
.encode(StandardCharsets.UTF_8)
|
||||
.build()
|
||||
.toUri();
|
||||
|
||||
response.getHeaders().setLocation(uri);
|
||||
return exchange.getExchange().getSession().flatMap(WebSession::invalidate);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package com.provectus.kafka.ui.config.auth;
|
|||
|
||||
import com.provectus.kafka.ui.util.EmptyRedirectStrategy;
|
||||
import java.net.URI;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -17,7 +17,7 @@ import org.springframework.security.web.server.ui.LogoutPageGeneratingWebFilter;
|
|||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@ConditionalOnProperty(value = "auth.type", havingValue = "LOGIN_FORM")
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
public class BasicAuthSecurityConfig extends AbstractAuthSecurityConfig {
|
||||
|
||||
public static final String LOGIN_URL = "/auth";
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package com.provectus.kafka.ui.config.auth;
|
||||
|
||||
import com.provectus.kafka.ui.config.CognitoOidcLogoutSuccessHandler;
|
||||
import com.provectus.kafka.ui.config.auth.props.CognitoProperties;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrations;
|
||||
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
|
||||
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@ConditionalOnProperty(value = "auth.type", havingValue = "OAUTH2_COGNITO")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CognitoOAuthSecurityConfig extends AbstractAuthSecurityConfig {
|
||||
|
||||
private static final String COGNITO = "cognito";
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain configure(ServerHttpSecurity http, CognitoProperties props) {
|
||||
log.info("Configuring Cognito OAUTH2 authentication.");
|
||||
|
||||
String clientId = props.getClientId();
|
||||
String logoutUrl = props.getLogoutUri();
|
||||
|
||||
final ServerLogoutSuccessHandler logoutHandler = new CognitoOidcLogoutSuccessHandler(logoutUrl, clientId);
|
||||
|
||||
return http.authorizeExchange()
|
||||
.pathMatchers(AUTH_WHITELIST)
|
||||
.permitAll()
|
||||
.anyExchange()
|
||||
.authenticated()
|
||||
|
||||
.and()
|
||||
.oauth2Login()
|
||||
|
||||
.and()
|
||||
.oauth2Client()
|
||||
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessHandler(logoutHandler)
|
||||
|
||||
.and()
|
||||
.csrf().disable()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public InMemoryReactiveClientRegistrationRepository clientRegistrationRepository(CognitoProperties props) {
|
||||
ClientRegistration.Builder builder = ClientRegistrations
|
||||
.fromIssuerLocation(props.getIssuerUri())
|
||||
.registrationId(COGNITO);
|
||||
|
||||
builder.clientId(props.getClientId());
|
||||
builder.clientSecret(props.getClientSecret());
|
||||
|
||||
Optional.ofNullable(props.getScope()).ifPresent(builder::scope);
|
||||
Optional.ofNullable(props.getUserNameAttribute()).ifPresent(builder::userNameAttributeName);
|
||||
|
||||
return new InMemoryReactiveClientRegistrationRepository(builder.build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("auth.cognito")
|
||||
public CognitoProperties cognitoProperties() {
|
||||
return new CognitoProperties();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.provectus.kafka.ui.config.auth;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -14,7 +14,7 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
|
|||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@ConditionalOnProperty(value = "auth.type", havingValue = "DISABLED")
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
public class DisabledAuthSecurityConfig extends AbstractAuthSecurityConfig {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.provectus.kafka.ui.config.auth;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -25,7 +25,7 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
|
|||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@ConditionalOnProperty(value = "auth.type", havingValue = "LDAP")
|
||||
@Log4j2
|
||||
@Slf4j
|
||||
public class LdapSecurityConfig extends AbstractAuthSecurityConfig {
|
||||
|
||||
@Value("${spring.ldap.urls}")
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package com.provectus.kafka.ui.config.auth.props;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Data
|
||||
@ToString(exclude = "clientSecret")
|
||||
public class CognitoProperties {
|
||||
|
||||
String clientId;
|
||||
String logoutUri;
|
||||
String issuerUri;
|
||||
String clientSecret;
|
||||
@Nullable
|
||||
String scope;
|
||||
@Nullable
|
||||
String userNameAttribute;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public String getLogoutUri() {
|
||||
return logoutUri;
|
||||
}
|
||||
|
||||
public String getIssuerUri() {
|
||||
return issuerUri;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public @Nullable String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public @Nullable String getUserNameAttribute() {
|
||||
return userNameAttribute;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue