|
@@ -1,13 +1,23 @@
|
|
package com.provectus.kafka.ui.config.auth;
|
|
package com.provectus.kafka.ui.config.auth;
|
|
|
|
|
|
|
|
+import static com.provectus.kafka.ui.config.auth.AbstractAuthSecurityConfig.AUTH_WHITELIST;
|
|
|
|
+
|
|
|
|
+import com.provectus.kafka.ui.service.rbac.AccessControlService;
|
|
|
|
+import com.provectus.kafka.ui.service.rbac.extractor.RbacLdapAuthoritiesExtractor;
|
|
|
|
+import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import javax.annotation.Nullable;
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
-import org.springframework.beans.factory.annotation.Value;
|
|
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
|
import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration;
|
|
|
|
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Bean;
|
|
import org.springframework.context.annotation.Configuration;
|
|
import org.springframework.context.annotation.Configuration;
|
|
import org.springframework.context.annotation.Import;
|
|
import org.springframework.context.annotation.Import;
|
|
|
|
+import org.springframework.context.annotation.Primary;
|
|
|
|
+import org.springframework.ldap.core.DirContextOperations;
|
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
|
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
|
import org.springframework.ldap.core.support.LdapContextSource;
|
|
import org.springframework.ldap.core.support.LdapContextSource;
|
|
import org.springframework.security.authentication.AuthenticationManager;
|
|
import org.springframework.security.authentication.AuthenticationManager;
|
|
@@ -16,70 +26,71 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
|
|
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
|
|
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
|
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|
|
|
+import org.springframework.security.core.GrantedAuthority;
|
|
|
|
+import org.springframework.security.core.userdetails.UserDetails;
|
|
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
|
import org.springframework.security.ldap.authentication.BindAuthenticator;
|
|
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
|
|
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
|
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
|
import org.springframework.security.ldap.search.LdapUserSearch;
|
|
import org.springframework.security.ldap.search.LdapUserSearch;
|
|
|
|
+import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
|
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
|
|
|
|
|
@Configuration
|
|
@Configuration
|
|
@EnableWebFluxSecurity
|
|
@EnableWebFluxSecurity
|
|
@ConditionalOnProperty(value = "auth.type", havingValue = "LDAP")
|
|
@ConditionalOnProperty(value = "auth.type", havingValue = "LDAP")
|
|
@Import(LdapAutoConfiguration.class)
|
|
@Import(LdapAutoConfiguration.class)
|
|
|
|
+@EnableConfigurationProperties(LdapProperties.class)
|
|
|
|
+@RequiredArgsConstructor
|
|
@Slf4j
|
|
@Slf4j
|
|
-public class LdapSecurityConfig extends AbstractAuthSecurityConfig {
|
|
|
|
-
|
|
|
|
- @Value("${spring.ldap.urls}")
|
|
|
|
- private String ldapUrls;
|
|
|
|
- @Value("${spring.ldap.dn.pattern:#{null}}")
|
|
|
|
- private String ldapUserDnPattern;
|
|
|
|
- @Value("${spring.ldap.adminUser:#{null}}")
|
|
|
|
- private String adminUser;
|
|
|
|
- @Value("${spring.ldap.adminPassword:#{null}}")
|
|
|
|
- private String adminPassword;
|
|
|
|
- @Value("${spring.ldap.userFilter.searchBase:#{null}}")
|
|
|
|
- private String userFilterSearchBase;
|
|
|
|
- @Value("${spring.ldap.userFilter.searchFilter:#{null}}")
|
|
|
|
- private String userFilterSearchFilter;
|
|
|
|
-
|
|
|
|
- @Value("${oauth2.ldap.activeDirectory:false}")
|
|
|
|
- private boolean isActiveDirectory;
|
|
|
|
- @Value("${oauth2.ldap.aсtiveDirectory.domain:#{null}}")
|
|
|
|
- private String activeDirectoryDomain;
|
|
|
|
|
|
+public class LdapSecurityConfig {
|
|
|
|
+
|
|
|
|
+ private final LdapProperties props;
|
|
|
|
|
|
@Bean
|
|
@Bean
|
|
- public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
|
|
|
|
|
+ public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource,
|
|
|
|
+ ApplicationContext context,
|
|
|
|
+ @Nullable AccessControlService acs) {
|
|
|
|
+ var rbacEnabled = acs != null && acs.isRbacEnabled();
|
|
BindAuthenticator ba = new BindAuthenticator(contextSource);
|
|
BindAuthenticator ba = new BindAuthenticator(contextSource);
|
|
- if (ldapUserDnPattern != null) {
|
|
|
|
- ba.setUserDnPatterns(new String[] {ldapUserDnPattern});
|
|
|
|
|
|
+ if (props.getBase() != null) {
|
|
|
|
+ ba.setUserDnPatterns(new String[] {props.getBase()});
|
|
}
|
|
}
|
|
- if (userFilterSearchFilter != null) {
|
|
|
|
|
|
+ if (props.getUserFilterSearchFilter() != null) {
|
|
LdapUserSearch userSearch =
|
|
LdapUserSearch userSearch =
|
|
- new FilterBasedLdapUserSearch(userFilterSearchBase, userFilterSearchFilter, contextSource);
|
|
|
|
|
|
+ new FilterBasedLdapUserSearch(props.getUserFilterSearchBase(), props.getUserFilterSearchFilter(),
|
|
|
|
+ contextSource);
|
|
ba.setUserSearch(userSearch);
|
|
ba.setUserSearch(userSearch);
|
|
}
|
|
}
|
|
|
|
|
|
AbstractLdapAuthenticationProvider authenticationProvider;
|
|
AbstractLdapAuthenticationProvider authenticationProvider;
|
|
- if (!isActiveDirectory) {
|
|
|
|
- authenticationProvider = new LdapAuthenticationProvider(ba);
|
|
|
|
|
|
+ if (!props.isActiveDirectory()) {
|
|
|
|
+ authenticationProvider = rbacEnabled
|
|
|
|
+ ? new LdapAuthenticationProvider(ba, new RbacLdapAuthoritiesExtractor(context))
|
|
|
|
+ : new LdapAuthenticationProvider(ba);
|
|
} else {
|
|
} else {
|
|
- authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(activeDirectoryDomain, ldapUrls);
|
|
|
|
|
|
+ authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
|
|
|
|
+ props.getUrls()); // TODO Issue #3741
|
|
authenticationProvider.setUseAuthenticationRequestCredentials(true);
|
|
authenticationProvider.setUseAuthenticationRequestCredentials(true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (rbacEnabled) {
|
|
|
|
+ authenticationProvider.setUserDetailsContextMapper(new UserDetailsMapper());
|
|
|
|
+ }
|
|
|
|
+
|
|
AuthenticationManager am = new ProviderManager(List.of(authenticationProvider));
|
|
AuthenticationManager am = new ProviderManager(List.of(authenticationProvider));
|
|
|
|
|
|
return new ReactiveAuthenticationManagerAdapter(am);
|
|
return new ReactiveAuthenticationManagerAdapter(am);
|
|
}
|
|
}
|
|
|
|
|
|
@Bean
|
|
@Bean
|
|
|
|
+ @Primary
|
|
public BaseLdapPathContextSource contextSource() {
|
|
public BaseLdapPathContextSource contextSource() {
|
|
LdapContextSource ctx = new LdapContextSource();
|
|
LdapContextSource ctx = new LdapContextSource();
|
|
- ctx.setUrl(ldapUrls);
|
|
|
|
- ctx.setUserDn(adminUser);
|
|
|
|
- ctx.setPassword(adminPassword);
|
|
|
|
|
|
+ ctx.setUrl(props.getUrls());
|
|
|
|
+ ctx.setUserDn(props.getAdminUser());
|
|
|
|
+ ctx.setPassword(props.getAdminPassword());
|
|
ctx.afterPropertiesSet();
|
|
ctx.afterPropertiesSet();
|
|
return ctx;
|
|
return ctx;
|
|
}
|
|
}
|
|
@@ -87,20 +98,35 @@ public class LdapSecurityConfig extends AbstractAuthSecurityConfig {
|
|
@Bean
|
|
@Bean
|
|
public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
|
|
public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
|
|
log.info("Configuring LDAP authentication.");
|
|
log.info("Configuring LDAP authentication.");
|
|
- if (isActiveDirectory) {
|
|
|
|
|
|
+ if (props.isActiveDirectory()) {
|
|
log.info("Active Directory support for LDAP has been enabled.");
|
|
log.info("Active Directory support for LDAP has been enabled.");
|
|
}
|
|
}
|
|
|
|
|
|
- http
|
|
|
|
|
|
+ return http
|
|
.authorizeExchange()
|
|
.authorizeExchange()
|
|
.pathMatchers(AUTH_WHITELIST)
|
|
.pathMatchers(AUTH_WHITELIST)
|
|
.permitAll()
|
|
.permitAll()
|
|
.anyExchange()
|
|
.anyExchange()
|
|
.authenticated()
|
|
.authenticated()
|
|
|
|
+
|
|
.and()
|
|
.and()
|
|
- .httpBasic();
|
|
|
|
|
|
+ .formLogin()
|
|
|
|
|
|
- return http.csrf().disable().build();
|
|
|
|
|
|
+ .and()
|
|
|
|
+ .logout()
|
|
|
|
+
|
|
|
|
+ .and()
|
|
|
|
+ .csrf().disable()
|
|
|
|
+ .build();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static class UserDetailsMapper extends LdapUserDetailsMapper {
|
|
|
|
+ @Override
|
|
|
|
+ public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
|
|
|
|
+ Collection<? extends GrantedAuthority> authorities) {
|
|
|
|
+ UserDetails userDetails = super.mapUserFromContext(ctx, username, authorities);
|
|
|
|
+ return new RbacLdapUser(userDetails);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|