Skip to content

Commit ebe2a2c

Browse files
committed
BE: address PR review comments
1 parent e0cba88 commit ebe2a2c

12 files changed

+65
-122
lines changed
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,38 @@
11
package io.kafbat.ui.config.auth;
22

3+
import io.kafbat.ui.model.rbac.DefaultRole;
34
import io.kafbat.ui.model.rbac.Role;
5+
import jakarta.annotation.Nullable;
46
import jakarta.annotation.PostConstruct;
57
import java.util.ArrayList;
68
import java.util.List;
7-
import javax.annotation.Nullable;
89
import org.springframework.boot.context.properties.ConfigurationProperties;
910

1011
@ConfigurationProperties("rbac")
1112
public class RoleBasedAccessControlProperties {
1213

1314
private final List<Role> roles = new ArrayList<>();
1415

15-
private Role defaultRole;
16+
private DefaultRole defaultRole;
1617

1718
@PostConstruct
1819
public void init() {
1920
roles.forEach(Role::validate);
2021
if (defaultRole != null) {
21-
defaultRole.validateDefaultRole();
22+
defaultRole.validate();
2223
}
2324
}
2425

2526
public List<Role> getRoles() {
2627
return roles;
2728
}
2829

29-
public void setDefaultRole(Role defaultRole) {
30+
public void setDefaultRole(DefaultRole defaultRole) {
3031
this.defaultRole = defaultRole;
3132
}
3233

3334
@Nullable
34-
public Role getDefaultRole() {
35+
public DefaultRole getDefaultRole() {
3536
return defaultRole;
3637
}
3738
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.kafbat.ui.model.rbac;
2+
3+
import static com.google.common.base.Preconditions.checkArgument;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import lombok.Data;
8+
9+
@Data
10+
public class DefaultRole {
11+
12+
private String name;
13+
private List<String> clusters;
14+
private List<Permission> permissions = new ArrayList<>();
15+
16+
public void validate() {
17+
checkArgument(clusters != null && !clusters.isEmpty(), "Default role clusters cannot be empty");
18+
permissions.forEach(Permission::validate);
19+
permissions.forEach(Permission::transform);
20+
}
21+
}

api/src/main/java/io/kafbat/ui/model/rbac/Role.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,4 @@ public void validate() {
2121
permissions.forEach(Permission::transform);
2222
subjects.forEach(Subject::validate);
2323
}
24-
25-
// default role need only permissions
26-
public void validateDefaultRole() {
27-
permissions.forEach(Permission::validate);
28-
permissions.forEach(Permission::transform);
29-
}
3024
}

api/src/main/java/io/kafbat/ui/service/rbac/AccessControlService.java

Lines changed: 24 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import io.kafbat.ui.model.ClusterDTO;
77
import io.kafbat.ui.model.ConnectDTO;
88
import io.kafbat.ui.model.InternalTopic;
9-
import io.kafbat.ui.model.KafkaCluster;
109
import io.kafbat.ui.model.rbac.AccessContext;
10+
import io.kafbat.ui.model.rbac.DefaultRole;
1111
import io.kafbat.ui.model.rbac.Permission;
1212
import io.kafbat.ui.model.rbac.Role;
1313
import io.kafbat.ui.model.rbac.Subject;
@@ -69,44 +69,25 @@ public void init() {
6969
log.trace("No roles provided, disabling RBAC");
7070
return;
7171
}
72-
if (properties.getDefaultRole() != null) {
73-
log.trace("Set Default Role Clusters");
74-
// set default role for all clusters
75-
properties.getDefaultRole().setClusters(
76-
clustersStorage.getKafkaClusters().stream()
77-
.map(KafkaCluster::getName)
78-
.collect(Collectors.toList())
79-
);
80-
}
8172
rbacEnabled = true;
8273

83-
if (properties.getDefaultRole() != null) {
84-
// set all extractors for default role because it is applied to all clusters
85-
this.oauthExtractors = Set.of(
86-
new CognitoAuthorityExtractor(),
87-
new GoogleAuthorityExtractor(),
88-
new GithubAuthorityExtractor(),
89-
new OauthAuthorityExtractor()
90-
);
91-
} else {
92-
this.oauthExtractors = properties.getRoles()
74+
this.oauthExtractors = properties.getRoles()
75+
.stream()
76+
.map(role -> role.getSubjects()
9377
.stream()
94-
.map(role -> role.getSubjects()
95-
.stream()
96-
.map(Subject::getProvider)
97-
.distinct()
98-
.map(provider -> switch (provider) {
99-
case OAUTH_COGNITO -> new CognitoAuthorityExtractor();
100-
case OAUTH_GOOGLE -> new GoogleAuthorityExtractor();
101-
case OAUTH_GITHUB -> new GithubAuthorityExtractor();
102-
case OAUTH -> new OauthAuthorityExtractor();
103-
default -> null;
104-
}
105-
).filter(Objects::nonNull)
106-
.collect(Collectors.toSet()))
107-
.flatMap(Set::stream)
108-
.collect(Collectors.toSet());
109-
}
78+
.map(Subject::getProvider)
79+
.distinct()
80+
.map(provider -> switch (provider) {
81+
case OAUTH_COGNITO -> new CognitoAuthorityExtractor();
82+
case OAUTH_GOOGLE -> new GoogleAuthorityExtractor();
83+
case OAUTH_GITHUB -> new GithubAuthorityExtractor();
84+
case OAUTH -> new OauthAuthorityExtractor();
85+
default -> null;
86+
}
87+
).filter(Objects::nonNull)
88+
.collect(Collectors.toSet()))
89+
.flatMap(Set::stream)
90+
.collect(Collectors.toSet());
11091

11192
if (!(properties.getRoles().isEmpty() && properties.getDefaultRole() == null)
11293
&& "oauth2".equalsIgnoreCase(environment.getProperty("auth.type"))
@@ -162,13 +143,15 @@ public static Mono<AuthenticatedUser> getUser() {
162143

163144
private boolean isClusterAccessible(String clusterName, AuthenticatedUser user) {
164145
Assert.isTrue(StringUtils.isNotEmpty(clusterName), "cluster value is empty");
165-
if (properties.getDefaultRole() != null) {
166-
return true;
167-
}
168-
return properties.getRoles()
146+
boolean isAccessible = properties.getRoles()
169147
.stream()
170148
.filter(filterRole(user))
171149
.anyMatch(role -> role.getClusters().stream().anyMatch(clusterName::equalsIgnoreCase));
150+
151+
if (!isAccessible && properties.getDefaultRole() != null) {
152+
return properties.getDefaultRole().getClusters().stream().anyMatch(clusterName::equalsIgnoreCase);
153+
}
154+
return isAccessible;
172155
}
173156

174157
public Mono<Boolean> isClusterAccessible(ClusterDTO cluster) {
@@ -233,7 +216,7 @@ public List<Role> getRoles() {
233216
return Collections.unmodifiableList(properties.getRoles());
234217
}
235218

236-
public Role getDefaultRole() {
219+
public DefaultRole getDefaultRole() {
237220
return properties.getDefaultRole();
238221
}
239222

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,8 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
3939

4040
var usernameRoles = extractUsernameRoles(acs, principal);
4141
var groupRoles = extractGroupRoles(acs, principal);
42-
var unionRoles = Sets.union(usernameRoles, groupRoles);
4342

44-
if (unionRoles.isEmpty() && acs.getDefaultRole() != null) {
45-
return Mono.just(Set.of(acs.getDefaultRole().getName()));
46-
}
47-
48-
return Mono.just(unionRoles);
43+
return Mono.just(Sets.union(usernameRoles, groupRoles));
4944
}
5045

5146
private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,10 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
7171
Mono<Set<String>> organizationRoles = getOrganizationRoles(principal, additionalParams, acs, webClient);
7272
Mono<Set<String>> teamRoles = getTeamRoles(webClient, additionalParams, acs);
7373

74-
Set<String> defaultRoles = acs.getDefaultRole() == null
75-
? Collections.emptySet()
76-
: Set.of(acs.getDefaultRole().getName());
77-
7874
return Mono.zip(organizationRoles, teamRoles)
7975
.map((t) -> Stream.of(t.getT1(), t.getT2(), usernameRoles)
8076
.flatMap(Collection::stream)
81-
.collect(Collectors.toSet()))
82-
.map(roles -> roles.isEmpty() ? defaultRoles : roles);
77+
.collect(Collectors.toSet()));
8378
}
8479

8580
private Set<String> extractUsernameRoles(DefaultOAuth2User principal, AccessControlService acs) {

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,8 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
3939

4040
var usernameRoles = extractUsernameRoles(acs, principal);
4141
var domainRoles = extractDomainRoles(acs, principal);
42-
var unionRoles = Sets.union(usernameRoles, domainRoles);
4342

44-
if (unionRoles.isEmpty() && acs.getDefaultRole() != null) {
45-
return Mono.just(Set.of(acs.getDefaultRole().getName()));
46-
}
47-
48-
return Mono.just(unionRoles);
43+
return Mono.just(Sets.union(usernameRoles, domainRoles));
4944
}
5045

5146
private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,8 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
4343

4444
var usernameRoles = extractUsernameRoles(acs, principal);
4545
var roles = extractRoles(acs, principal, additionalParams);
46-
var unionRoles = Sets.union(usernameRoles, roles);
4746

48-
if (unionRoles.isEmpty() && acs.getDefaultRole() != null) {
49-
return Mono.just(Set.of(acs.getDefaultRole().getName()));
50-
}
51-
52-
return Mono.just(unionRoles);
47+
return Mono.just(Sets.union(usernameRoles, roles));
5348
}
5449

5550
private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import io.kafbat.ui.model.rbac.provider.Provider;
55
import io.kafbat.ui.service.rbac.AccessControlService;
66
import java.util.Collection;
7-
import java.util.Set;
87
import java.util.stream.Collectors;
98
import lombok.extern.slf4j.Slf4j;
109
import org.springframework.context.ApplicationContext;
@@ -32,7 +31,7 @@ public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOp
3231
.peek(group -> log.trace("Found AD group [{}] for user [{}]", group, username))
3332
.collect(Collectors.toSet());
3433

35-
var grantedAuthorities = acs.getRoles()
34+
return acs.getRoles()
3635
.stream()
3736
.filter(r -> r.getSubjects()
3837
.stream()
@@ -47,11 +46,5 @@ public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOp
4746
.peek(role -> log.trace("Mapped role [{}] for user [{}]", role, username))
4847
.map(SimpleGrantedAuthority::new)
4948
.collect(Collectors.toSet());
50-
51-
// If no roles are found, return default role
52-
if (grantedAuthorities.isEmpty() && acs.getDefaultRole() != null) {
53-
return Set.of(new SimpleGrantedAuthority(acs.getDefaultRole().getName()));
54-
}
55-
return grantedAuthorities;
5649
}
5750
}

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import io.kafbat.ui.model.rbac.Role;
44
import io.kafbat.ui.model.rbac.provider.Provider;
55
import io.kafbat.ui.service.rbac.AccessControlService;
6-
import java.util.HashSet;
76
import java.util.Set;
87
import java.util.stream.Collectors;
98
import lombok.extern.slf4j.Slf4j;
@@ -34,7 +33,7 @@ protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, St
3433
.peek(group -> log.trace("Found LDAP group [{}] for user [{}]", group, username))
3534
.collect(Collectors.toSet());
3635

37-
var simpleGrantedAuthorities = acs.getRoles()
36+
return acs.getRoles()
3837
.stream()
3938
.filter(r -> r.getSubjects()
4039
.stream()
@@ -49,11 +48,5 @@ protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, St
4948
.peek(role -> log.trace("Mapped role [{}] for user [{}]", role, username))
5049
.map(SimpleGrantedAuthority::new)
5150
.collect(Collectors.toSet());
52-
53-
// If no roles are found, return default role
54-
if (simpleGrantedAuthorities.isEmpty() && acs.getDefaultRole() != null) {
55-
return Set.of(new SimpleGrantedAuthority(acs.getDefaultRole().getName()));
56-
}
57-
return new HashSet<>(simpleGrantedAuthorities);
5851
}
5952
}

api/src/test/java/io/kafbat/ui/service/rbac/AccessControlServiceDefaultRoleRbacEnabledTest.java

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.kafbat.ui.model.ClusterDTO;
1515
import io.kafbat.ui.model.KafkaCluster;
1616
import io.kafbat.ui.model.rbac.AccessContext;
17+
import io.kafbat.ui.model.rbac.DefaultRole;
1718
import io.kafbat.ui.model.rbac.Role;
1819
import io.kafbat.ui.service.ClustersStorage;
1920
import java.util.List;
@@ -23,6 +24,7 @@
2324
import org.mockito.MockedStatic;
2425
import org.mockito.Mockito;
2526
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.security.access.AccessDeniedException;
2628
import org.springframework.security.core.Authentication;
2729
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
2830
import org.springframework.security.core.context.SecurityContext;
@@ -51,7 +53,7 @@ public class AccessControlServiceDefaultRoleRbacEnabledTest extends AbstractInte
5153
RbacUser user;
5254

5355
@Mock
54-
Role defaultRole;
56+
DefaultRole defaultRole;
5557

5658
@Mock
5759
ClustersStorage clustersStorage;
@@ -67,14 +69,6 @@ void setUp() {
6769

6870
ReflectionTestUtils.setField(accessControlService, "properties", properties);
6971
ReflectionTestUtils.setField(accessControlService, "rbacEnabled", true);
70-
ReflectionTestUtils.setField(accessControlService, "clustersStorage", clustersStorage);
71-
72-
KafkaCluster prodCluster = KafkaCluster.builder().name(PROD_CLUSTER).build();
73-
KafkaCluster devCluster = KafkaCluster.builder().name(DEV_CLUSTER).build();
74-
75-
// set default role for all clusters
76-
when(clustersStorage.getKafkaClusters()).thenReturn(List.of(prodCluster, devCluster));
77-
accessControlService.init();
7872

7973
// Mock security context
8074
when(securityContext.getAuthentication()).thenReturn(authentication);
@@ -90,17 +84,6 @@ public void withSecurityContext(Runnable runnable) {
9084
}
9185
}
9286

93-
@Test
94-
void validateSetCluster() {
95-
withSecurityContext(() -> {
96-
97-
List<String> clusters = defaultRole.getClusters();
98-
assertThat(clusters)
99-
.isNotNull()
100-
.containsExactlyInAnyOrder(PROD_CLUSTER, DEV_CLUSTER);
101-
});
102-
}
103-
10487
@Test
10588
void validateAccess() {
10689
withSecurityContext(() -> {
@@ -125,11 +108,4 @@ void isClusterAccessible() {
125108
.verify();
126109
});
127110
}
128-
129-
@Test
130-
void testGetDefaultRole() {
131-
Role defaultRole = accessControlService.getDefaultRole();
132-
assertThat(defaultRole).isNotNull()
133-
.isEqualTo(this.defaultRole);
134-
}
135111
}

api/src/test/java/io/kafbat/ui/service/rbac/MockedRbacUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.mockito.Mockito.when;
66

77
import io.kafbat.ui.model.rbac.AccessContext;
8+
import io.kafbat.ui.model.rbac.DefaultRole;
89
import io.kafbat.ui.model.rbac.Permission;
910
import io.kafbat.ui.model.rbac.Resource;
1011
import io.kafbat.ui.model.rbac.Role;
@@ -100,9 +101,10 @@ public static Role getDevRole() {
100101
return role;
101102
}
102103

103-
public static Role getDefaultRole() {
104-
Role role = new Role();
104+
public static DefaultRole getDefaultRole() {
105+
DefaultRole role = new DefaultRole();
105106
role.setName(DEFAULT_ROLE);
107+
role.setClusters(List.of(DEV_CLUSTER, PROD_CLUSTER));
106108

107109
Permission topicViewPermission = new Permission();
108110
topicViewPermission.setResource(Resource.TOPIC.name());
@@ -131,7 +133,7 @@ public static Role getDefaultRole() {
131133
connectPermission
132134
);
133135
role.setPermissions(permissions);
134-
role.validateDefaultRole();
136+
role.validate();
135137
return role;
136138
}
137139

0 commit comments

Comments
 (0)