Skip to content

Commit 50bae9f

Browse files
committed
Impl AD role extractor, refactor configs
1 parent 9d257c9 commit 50bae9f

File tree

3 files changed

+103
-56
lines changed

3 files changed

+103
-56
lines changed

api/src/main/java/io/kafbat/ui/config/auth/LdapSecurityConfig.java

+53-52
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package io.kafbat.ui.config.auth;
22

3-
import static io.kafbat.ui.config.auth.AbstractAuthSecurityConfig.AUTH_WHITELIST;
4-
53
import io.kafbat.ui.service.rbac.AccessControlService;
4+
import io.kafbat.ui.service.rbac.extractor.RbacActiveDirectoryAuthoritiesExtractor;
65
import io.kafbat.ui.service.rbac.extractor.RbacLdapAuthoritiesExtractor;
76
import java.util.Collection;
87
import java.util.List;
@@ -17,7 +16,6 @@
1716
import org.springframework.ldap.core.DirContextOperations;
1817
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
1918
import org.springframework.ldap.core.support.LdapContextSource;
20-
import org.springframework.security.authentication.AuthenticationManager;
2119
import org.springframework.security.authentication.ProviderManager;
2220
import org.springframework.security.authentication.ReactiveAuthenticationManager;
2321
import org.springframework.security.authentication.ReactiveAuthenticationManagerAdapter;
@@ -29,10 +27,11 @@
2927
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider;
3028
import org.springframework.security.ldap.authentication.BindAuthenticator;
3129
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
30+
import org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator;
3231
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
32+
import org.springframework.security.ldap.authentication.ad.DefaultActiveDirectoryAuthoritiesPopulator;
3333
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
3434
import org.springframework.security.ldap.search.LdapUserSearch;
35-
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
3635
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
3736
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
3837
import org.springframework.security.web.server.SecurityWebFilterChain;
@@ -43,52 +42,55 @@
4342
@EnableConfigurationProperties(LdapProperties.class)
4443
@RequiredArgsConstructor
4544
@Slf4j
46-
public class LdapSecurityConfig {
45+
public class LdapSecurityConfig extends AbstractAuthSecurityConfig {
4746

4847
private final LdapProperties props;
4948

5049
@Bean
51-
public ReactiveAuthenticationManager authenticationManager(LdapContextSource ldapContextSource,
52-
LdapAuthoritiesPopulator authoritiesExtractor,
53-
AccessControlService acs) {
50+
public ReactiveAuthenticationManager authenticationManager(AbstractLdapAuthenticationProvider authProvider) {
51+
return new ReactiveAuthenticationManagerAdapter(new ProviderManager(List.of(authProvider)));
52+
}
53+
54+
@Bean
55+
public AbstractLdapAuthenticationProvider authenticationProvider(LdapAuthoritiesPopulator authoritiesExtractor,
56+
BindAuthenticator bindAuthenticator,
57+
AccessControlService acs) {
5458
var rbacEnabled = acs.isRbacEnabled();
59+
60+
AbstractLdapAuthenticationProvider authProvider;
61+
62+
if (!props.isActiveDirectory()) {
63+
authProvider = new LdapAuthenticationProvider(bindAuthenticator, authoritiesExtractor);
64+
} else {
65+
authProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
66+
props.getUrls());
67+
authProvider.setUseAuthenticationRequestCredentials(true);
68+
((ActiveDirectoryLdapAuthenticationProvider) authProvider).setAuthoritiesPopulator(authoritiesExtractor);
69+
}
70+
71+
if (rbacEnabled) {
72+
authProvider.setUserDetailsContextMapper(new RbacUserDetailsMapper());
73+
}
74+
75+
return authProvider;
76+
}
77+
78+
@Bean
79+
public BindAuthenticator ldapBindAuthentication(LdapContextSource ldapContextSource) {
5580
BindAuthenticator ba = new BindAuthenticator(ldapContextSource);
81+
5682
if (props.getBase() != null) {
5783
ba.setUserDnPatterns(new String[] {props.getBase()});
5884
}
85+
5986
if (props.getUserFilterSearchFilter() != null) {
6087
LdapUserSearch userSearch =
6188
new FilterBasedLdapUserSearch(props.getUserFilterSearchBase(), props.getUserFilterSearchFilter(),
6289
ldapContextSource);
6390
ba.setUserSearch(userSearch);
6491
}
6592

66-
var authenticationProvider = getAuthenticationProvider(authoritiesExtractor, rbacEnabled, ba);
67-
68-
AuthenticationManager am = new ProviderManager(List.of(authenticationProvider));
69-
70-
return new ReactiveAuthenticationManagerAdapter(am);
71-
}
72-
73-
private AbstractLdapAuthenticationProvider getAuthenticationProvider(LdapAuthoritiesPopulator authoritiesExtractor,
74-
boolean rbacEnabled,
75-
BindAuthenticator bindAuthenticator) {
76-
AbstractLdapAuthenticationProvider authenticationProvider;
77-
78-
if (!props.isActiveDirectory()) {
79-
authenticationProvider = rbacEnabled
80-
? new LdapAuthenticationProvider(bindAuthenticator, authoritiesExtractor)
81-
: new LdapAuthenticationProvider(bindAuthenticator);
82-
} else {
83-
authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
84-
props.getUrls());
85-
authenticationProvider.setUseAuthenticationRequestCredentials(true);
86-
}
87-
88-
if (rbacEnabled) {
89-
authenticationProvider.setUserDetailsContextMapper(new UserDetailsMapper());
90-
}
91-
return authenticationProvider;
93+
return ba;
9294
}
9395

9496
@Bean
@@ -102,28 +104,27 @@ public LdapContextSource ldapContextSource() {
102104
}
103105

104106
@Bean
105-
public DefaultLdapAuthoritiesPopulator ldapAuthoritiesExtractor(ApplicationContext context,
106-
BaseLdapPathContextSource contextSource,
107-
AccessControlService acs) {
108-
if (props.isActiveDirectory()) {
109-
return null;
110-
}
107+
public LdapAuthoritiesPopulator authoritiesExtractor(ApplicationContext ctx,
108+
BaseLdapPathContextSource ldapCtx,
109+
AccessControlService acs) {
110+
if (!props.isActiveDirectory()) {
111+
if (!acs.isRbacEnabled()) {
112+
return new NullLdapAuthoritiesPopulator();
113+
}
111114

112-
var rbacEnabled = acs != null && acs.isRbacEnabled();
115+
var extractor = new RbacLdapAuthoritiesExtractor(ctx, ldapCtx, props.getGroupFilterSearchBase());
113116

114-
DefaultLdapAuthoritiesPopulator extractor;
117+
Optional.ofNullable(props.getGroupFilterSearchFilter()).ifPresent(extractor::setGroupSearchFilter);
118+
extractor.setRolePrefix("");
119+
extractor.setConvertToUpperCase(false);
120+
extractor.setSearchSubtree(true);
115121

116-
if (rbacEnabled) {
117-
extractor = new RbacLdapAuthoritiesExtractor(context, contextSource, props.getGroupFilterSearchBase());
122+
return extractor;
118123
} else {
119-
extractor = new DefaultLdapAuthoritiesPopulator(contextSource, props.getGroupFilterSearchBase());
124+
return acs.isRbacEnabled()
125+
? new RbacActiveDirectoryAuthoritiesExtractor(ctx)
126+
: new DefaultActiveDirectoryAuthoritiesPopulator();
120127
}
121-
122-
Optional.ofNullable(props.getGroupFilterSearchFilter()).ifPresent(extractor::setGroupSearchFilter);
123-
extractor.setRolePrefix("");
124-
extractor.setConvertToUpperCase(false);
125-
extractor.setSearchSubtree(true);
126-
return extractor;
127128
}
128129

129130
@Bean
@@ -145,7 +146,7 @@ public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
145146
.build();
146147
}
147148

148-
private static class UserDetailsMapper extends LdapUserDetailsMapper {
149+
private static class RbacUserDetailsMapper extends LdapUserDetailsMapper {
149150
@Override
150151
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
151152
Collection<? extends GrantedAuthority> authorities) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.kafbat.ui.service.rbac.extractor;
2+
3+
import io.kafbat.ui.model.rbac.Role;
4+
import io.kafbat.ui.model.rbac.provider.Provider;
5+
import io.kafbat.ui.service.rbac.AccessControlService;
6+
import java.util.Collection;
7+
import java.util.stream.Collectors;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.context.ApplicationContext;
10+
import org.springframework.ldap.core.DirContextOperations;
11+
import org.springframework.security.core.GrantedAuthority;
12+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
13+
import org.springframework.security.ldap.authentication.ad.DefaultActiveDirectoryAuthoritiesPopulator;
14+
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
15+
16+
@Slf4j
17+
public class RbacActiveDirectoryAuthoritiesExtractor implements LdapAuthoritiesPopulator {
18+
19+
private final DefaultActiveDirectoryAuthoritiesPopulator populator = new DefaultActiveDirectoryAuthoritiesPopulator();
20+
private final AccessControlService acs;
21+
22+
public RbacActiveDirectoryAuthoritiesExtractor(ApplicationContext context) {
23+
this.acs = context.getBean(AccessControlService.class);
24+
}
25+
26+
@Override
27+
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
28+
var adGroups = populator.getGrantedAuthorities(userData, username)
29+
.stream()
30+
.map(GrantedAuthority::getAuthority)
31+
.peek(group -> log.trace("Found AD group [{}] for user [{}]", group, username))
32+
.collect(Collectors.toSet());
33+
34+
return acs.getRoles()
35+
.stream()
36+
.filter(r -> r.getSubjects()
37+
.stream()
38+
.filter(subject -> subject.getProvider().equals(Provider.LDAP_AD))
39+
.filter(subject -> subject.getType().equals("group"))
40+
.anyMatch(subject -> adGroups.contains(subject.getValue()))
41+
)
42+
.map(Role::getName)
43+
.peek(role -> log.trace("Mapped role [{}] for user [{}]", role, username))
44+
.map(SimpleGrantedAuthority::new)
45+
.collect(Collectors.toSet());
46+
}
47+
}

api/src/main/java/io/kafbat/ui/service/rbac/extractor/RbacLdapAuthoritiesExtractor.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@
1616
@Slf4j
1717
public class RbacLdapAuthoritiesExtractor extends NestedLdapAuthoritiesPopulator {
1818

19-
private static final Set<Provider> SUPPORTED_PROVIDERS = Set.of(Provider.LDAP, Provider.LDAP_AD);
20-
2119
private final AccessControlService acs;
2220

2321
public RbacLdapAuthoritiesExtractor(ApplicationContext context,
24-
BaseLdapPathContextSource contextSource, String groupFilterSearchBase) {
22+
BaseLdapPathContextSource contextSource,
23+
String groupFilterSearchBase) {
2524
super(contextSource, groupFilterSearchBase);
2625
this.acs = context.getBean(AccessControlService.class);
2726
}
@@ -38,7 +37,7 @@ protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, St
3837
.stream()
3938
.filter(r -> r.getSubjects()
4039
.stream()
41-
.filter(subject -> SUPPORTED_PROVIDERS.contains(subject.getProvider()))
40+
.filter(subject -> subject.getProvider().equals(Provider.LDAP))
4241
.filter(subject -> subject.getType().equals("group"))
4342
.anyMatch(subject -> ldapGroups.contains(subject.getValue()))
4443
)

0 commit comments

Comments
 (0)