Skip to content

Commit daa488c

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

File tree

3 files changed

+105
-56
lines changed

3 files changed

+105
-56
lines changed

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

Lines changed: 55 additions & 52 deletions
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;
@@ -30,6 +28,7 @@
3028
import org.springframework.security.ldap.authentication.BindAuthenticator;
3129
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
3230
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
31+
import org.springframework.security.ldap.authentication.ad.DefaultActiveDirectoryAuthoritiesPopulator;
3332
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
3433
import org.springframework.security.ldap.search.LdapUserSearch;
3534
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
@@ -43,52 +42,59 @@
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) {
54-
var rbacEnabled = acs.isRbacEnabled();
55-
BindAuthenticator ba = new BindAuthenticator(ldapContextSource);
56-
if (props.getBase() != null) {
57-
ba.setUserDnPatterns(new String[] {props.getBase()});
58-
}
59-
if (props.getUserFilterSearchFilter() != null) {
60-
LdapUserSearch userSearch =
61-
new FilterBasedLdapUserSearch(props.getUserFilterSearchBase(), props.getUserFilterSearchFilter(),
62-
ldapContextSource);
63-
ba.setUserSearch(userSearch);
64-
}
65-
66-
var authenticationProvider = getAuthenticationProvider(authoritiesExtractor, rbacEnabled, ba);
67-
68-
AuthenticationManager am = new ProviderManager(List.of(authenticationProvider));
69-
70-
return new ReactiveAuthenticationManagerAdapter(am);
50+
public ReactiveAuthenticationManager authenticationManager(AbstractLdapAuthenticationProvider authProvider) {
51+
return new ReactiveAuthenticationManagerAdapter(new ProviderManager(List.of(authProvider)));
7152
}
7253

73-
private AbstractLdapAuthenticationProvider getAuthenticationProvider(LdapAuthoritiesPopulator authoritiesExtractor,
74-
boolean rbacEnabled,
75-
BindAuthenticator bindAuthenticator) {
76-
AbstractLdapAuthenticationProvider authenticationProvider;
54+
@Bean
55+
public AbstractLdapAuthenticationProvider authenticationProvider(LdapAuthoritiesPopulator authoritiesExtractor,
56+
BindAuthenticator bindAuthenticator,
57+
AccessControlService acs) {
58+
var rbacEnabled = acs.isRbacEnabled();
59+
60+
AbstractLdapAuthenticationProvider authProvider;
7761

7862
if (!props.isActiveDirectory()) {
79-
authenticationProvider = rbacEnabled
63+
authProvider = rbacEnabled
8064
? new LdapAuthenticationProvider(bindAuthenticator, authoritiesExtractor)
8165
: new LdapAuthenticationProvider(bindAuthenticator);
8266
} else {
83-
authenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
67+
authProvider = new ActiveDirectoryLdapAuthenticationProvider(props.getActiveDirectoryDomain(),
8468
props.getUrls());
85-
authenticationProvider.setUseAuthenticationRequestCredentials(true);
69+
authProvider.setUseAuthenticationRequestCredentials(true);
70+
if (rbacEnabled) {
71+
((ActiveDirectoryLdapAuthenticationProvider) authProvider).setAuthoritiesPopulator(authoritiesExtractor);
72+
}
8673
}
8774

8875
if (rbacEnabled) {
89-
authenticationProvider.setUserDetailsContextMapper(new UserDetailsMapper());
76+
authProvider.setUserDetailsContextMapper(new RbacUserDetailsMapper());
9077
}
91-
return authenticationProvider;
78+
79+
return authProvider;
80+
}
81+
82+
@Bean
83+
public BindAuthenticator ldapBindAuthentication(LdapContextSource ldapContextSource) {
84+
BindAuthenticator ba = new BindAuthenticator(ldapContextSource);
85+
86+
if (props.getBase() != null) {
87+
ba.setUserDnPatterns(new String[] {props.getBase()});
88+
}
89+
90+
if (props.getUserFilterSearchFilter() != null) {
91+
LdapUserSearch userSearch =
92+
new FilterBasedLdapUserSearch(props.getUserFilterSearchBase(), props.getUserFilterSearchFilter(),
93+
ldapContextSource);
94+
ba.setUserSearch(userSearch);
95+
}
96+
97+
return ba;
9298
}
9399

94100
@Bean
@@ -102,28 +108,25 @@ public LdapContextSource ldapContextSource() {
102108
}
103109

104110
@Bean
105-
public DefaultLdapAuthoritiesPopulator ldapAuthoritiesExtractor(ApplicationContext context,
106-
BaseLdapPathContextSource contextSource,
107-
AccessControlService acs) {
108-
if (props.isActiveDirectory()) {
109-
return null;
110-
}
111-
112-
var rbacEnabled = acs != null && acs.isRbacEnabled();
111+
public LdapAuthoritiesPopulator authoritiesExtractor(ApplicationContext ctx,
112+
BaseLdapPathContextSource ldapCtx,
113+
AccessControlService acs) {
114+
if (!props.isActiveDirectory()) {
115+
DefaultLdapAuthoritiesPopulator extractor = acs.isRbacEnabled()
116+
? new RbacLdapAuthoritiesExtractor(ctx, ldapCtx, props.getGroupFilterSearchBase())
117+
: new DefaultLdapAuthoritiesPopulator(ldapCtx, props.getGroupFilterSearchBase());
113118

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

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

129132
@Bean
@@ -145,7 +148,7 @@ public SecurityWebFilterChain configureLdap(ServerHttpSecurity http) {
145148
.build();
146149
}
147150

148-
private static class UserDetailsMapper extends LdapUserDetailsMapper {
151+
private static class RbacUserDetailsMapper extends LdapUserDetailsMapper {
149152
@Override
150153
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
151154
Collection<? extends GrantedAuthority> authorities) {
Lines changed: 47 additions & 0 deletions
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

Lines changed: 3 additions & 4 deletions
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)