Przeglądaj źródła

RBAC: LDAP: Use the spring-provided authority populator with a single group search base (#3769)

Roman Zabaluev 2 lat temu
rodzic
commit
c813e74609

+ 1 - 3
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapProperties.java

@@ -14,13 +14,11 @@ public class LdapProperties {
   private String adminPassword;
   private String adminPassword;
   private String userFilterSearchBase;
   private String userFilterSearchBase;
   private String userFilterSearchFilter;
   private String userFilterSearchFilter;
+  private String groupFilterSearchBase;
 
 
   @Value("${oauth2.ldap.activeDirectory:false}")
   @Value("${oauth2.ldap.activeDirectory:false}")
   private boolean isActiveDirectory;
   private boolean isActiveDirectory;
   @Value("${oauth2.ldap.aсtiveDirectory.domain:@null}")
   @Value("${oauth2.ldap.aсtiveDirectory.domain:@null}")
   private String activeDirectoryDomain;
   private String activeDirectoryDomain;
 
 
-  @Value("${oauth2.ldap.groupRoleAttribute:cn}")
-  private String groupRoleAttribute;
-
 }
 }

+ 13 - 4
kafka-ui-api/src/main/java/com/provectus/kafka/ui/config/auth/LdapSecurityConfig.java

@@ -3,7 +3,6 @@ package com.provectus.kafka.ui.config.auth;
 import static com.provectus.kafka.ui.config.auth.AbstractAuthSecurityConfig.AUTH_WHITELIST;
 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.AccessControlService;
-import com.provectus.kafka.ui.service.rbac.extractor.RbacLdapAuthoritiesExtractor;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.List;
 import java.util.List;
 import javax.annotation.Nullable;
 import javax.annotation.Nullable;
@@ -12,7 +11,6 @@ import lombok.extern.slf4j.Slf4j;
 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.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;
@@ -34,6 +32,8 @@ import org.springframework.security.ldap.authentication.LdapAuthenticationProvid
 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.DefaultLdapAuthoritiesPopulator;
+import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
 import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
 import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
 import org.springframework.security.web.server.SecurityWebFilterChain;
 import org.springframework.security.web.server.SecurityWebFilterChain;
 
 
@@ -50,7 +50,7 @@ public class LdapSecurityConfig {
 
 
   @Bean
   @Bean
   public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource,
   public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource,
-                                                             ApplicationContext context,
+                                                             LdapAuthoritiesPopulator ldapAuthoritiesPopulator,
                                                              @Nullable AccessControlService acs) {
                                                              @Nullable AccessControlService acs) {
     var rbacEnabled = acs != null && acs.isRbacEnabled();
     var rbacEnabled = acs != null && acs.isRbacEnabled();
     BindAuthenticator ba = new BindAuthenticator(contextSource);
     BindAuthenticator ba = new BindAuthenticator(contextSource);
@@ -67,7 +67,7 @@ public class LdapSecurityConfig {
     AbstractLdapAuthenticationProvider authenticationProvider;
     AbstractLdapAuthenticationProvider authenticationProvider;
     if (!props.isActiveDirectory()) {
     if (!props.isActiveDirectory()) {
       authenticationProvider = rbacEnabled
       authenticationProvider = rbacEnabled
-          ? new LdapAuthenticationProvider(ba, new RbacLdapAuthoritiesExtractor(context))
+          ? new LdapAuthenticationProvider(ba, ldapAuthoritiesPopulator)
           : new LdapAuthenticationProvider(ba);
           : new LdapAuthenticationProvider(ba);
     } else {
     } else {
       authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
       authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
@@ -95,6 +95,15 @@ public class LdapSecurityConfig {
     return ctx;
     return ctx;
   }
   }
 
 
+  @Bean
+  @Primary
+  public LdapAuthoritiesPopulator ldapAuthoritiesPopulator(BaseLdapPathContextSource contextSource) {
+    var authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource, props.getGroupFilterSearchBase());
+    authoritiesPopulator.setRolePrefix("");
+    authoritiesPopulator.setConvertToUpperCase(false);
+    return authoritiesPopulator;
+  }
+
   @Bean
   @Bean
   public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
   public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
     log.info("Configuring LDAP authentication.");
     log.info("Configuring LDAP authentication.");

+ 0 - 70
kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java

@@ -1,70 +0,0 @@
-package com.provectus.kafka.ui.service.rbac.extractor;
-
-import com.provectus.kafka.ui.config.auth.LdapProperties;
-import com.provectus.kafka.ui.model.rbac.Role;
-import com.provectus.kafka.ui.model.rbac.provider.Provider;
-import com.provectus.kafka.ui.service.rbac.AccessControlService;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.ApplicationContext;
-import org.springframework.ldap.core.DirContextOperations;
-import org.springframework.ldap.core.support.BaseLdapPathContextSource;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
-import org.springframework.util.Assert;
-
-@Slf4j
-public class RbacLdapAuthoritiesExtractor extends DefaultLdapAuthoritiesPopulator {
-
-  private final AccessControlService acs;
-  private final LdapProperties props;
-
-  private final Function<Map<String, List<String>>, GrantedAuthority> authorityMapper = (record) -> {
-    String role = record.get(getGroupRoleAttribute()).get(0);
-    return new SimpleGrantedAuthority(role);
-  };
-
-  public RbacLdapAuthoritiesExtractor(ApplicationContext context) {
-    super(context.getBean(BaseLdapPathContextSource.class), null);
-    this.acs = context.getBean(AccessControlService.class);
-    this.props = context.getBean(LdapProperties.class);
-  }
-
-  @Override
-  public Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
-    return acs.getRoles()
-        .stream()
-        .map(Role::getSubjects)
-        .flatMap(List::stream)
-        .filter(s -> s.getProvider().equals(Provider.LDAP))
-        .filter(s -> s.getType().equals("group"))
-        .flatMap(subject -> getRoles(subject.getValue(), user.getNameInNamespace(), username).stream())
-        .collect(Collectors.toSet());
-  }
-
-  private Set<GrantedAuthority> getRoles(String groupSearchBase, String userDn, String username) {
-    Assert.notNull(groupSearchBase, "groupSearchBase is empty");
-
-    log.trace(
-        "Searching for roles for user [{}] with DN [{}], groupRoleAttribute [{}] and filter [{}] in search base [{}]",
-        username, userDn, props.getGroupRoleAttribute(), getGroupSearchFilter(), groupSearchBase);
-
-    var ldapTemplate = getLdapTemplate();
-    ldapTemplate.setIgnoreNameNotFoundException(true);
-
-    Set<Map<String, List<String>>> userRoles = ldapTemplate.searchForMultipleAttributeValues(
-        groupSearchBase, getGroupSearchFilter(), new String[] {userDn, username},
-        new String[] {props.getGroupRoleAttribute()});
-
-    return userRoles.stream()
-        .map(authorityMapper)
-        .peek(a -> log.debug("Mapped role [{}] for user [{}]", a, username))
-        .collect(Collectors.toSet());
-  }
-
-}