Use the spring-provided authority populator with a single group search base
This commit is contained in:
parent
727f38401b
commit
7b36aa8de2
3 changed files with 14 additions and 77 deletions
|
@ -14,13 +14,11 @@ public class LdapProperties {
|
|||
private String adminPassword;
|
||||
private String userFilterSearchBase;
|
||||
private String userFilterSearchFilter;
|
||||
private String groupFilterSearchBase;
|
||||
|
||||
@Value("${oauth2.ldap.activeDirectory:false}")
|
||||
private boolean isActiveDirectory;
|
||||
@Value("${oauth2.ldap.aсtiveDirectory.domain:@null}")
|
||||
private String activeDirectoryDomain;
|
||||
|
||||
@Value("${oauth2.ldap.groupRoleAttribute:cn}")
|
||||
private String groupRoleAttribute;
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ 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 javax.annotation.Nullable;
|
||||
|
@ -12,7 +11,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
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.Configuration;
|
||||
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.search.FilterBasedLdapUserSearch;
|
||||
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.web.server.SecurityWebFilterChain;
|
||||
|
||||
|
@ -50,7 +50,7 @@ public class LdapSecurityConfig {
|
|||
|
||||
@Bean
|
||||
public ReactiveAuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource,
|
||||
ApplicationContext context,
|
||||
LdapAuthoritiesPopulator ldapAuthoritiesPopulator,
|
||||
@Nullable AccessControlService acs) {
|
||||
var rbacEnabled = acs != null && acs.isRbacEnabled();
|
||||
BindAuthenticator ba = new BindAuthenticator(contextSource);
|
||||
|
@ -67,7 +67,7 @@ public class LdapSecurityConfig {
|
|||
AbstractLdapAuthenticationProvider authenticationProvider;
|
||||
if (!props.isActiveDirectory()) {
|
||||
authenticationProvider = rbacEnabled
|
||||
? new LdapAuthenticationProvider(ba, new RbacLdapAuthoritiesExtractor(context))
|
||||
? new LdapAuthenticationProvider(ba, ldapAuthoritiesPopulator)
|
||||
: new LdapAuthenticationProvider(ba);
|
||||
} else {
|
||||
authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
|
||||
|
@ -95,6 +95,15 @@ public class LdapSecurityConfig {
|
|||
return ctx;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public LdapAuthoritiesPopulator ldapAuthoritiesPopulator(BaseLdapPathContextSource contextSource) {
|
||||
var authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource, props.getGroupFilterSearchBase());
|
||||
authoritiesPopulator.setRolePrefix("");
|
||||
authoritiesPopulator.setConvertToUpperCase(false);
|
||||
return authoritiesPopulator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
|
||||
log.info("Configuring LDAP authentication.");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue