Skip to content

Commit

Permalink
RBAC: Improve role assigning logging (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
Haarolean authored Jan 25, 2024
1 parent e1baf2c commit c75c5cc
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 52 deletions.
4 changes: 2 additions & 2 deletions .github/kapybro/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ version: "0.1"
settings:

maintainers:
# teams: [ "maintainers" ]
users: [ "haarolean" ]
teams: [ "maintainers" ]
users: []

triage:
enabled: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.kafbat.ui.model.rbac.Role;
import io.kafbat.ui.model.rbac.provider.Provider;
import io.kafbat.ui.service.rbac.AccessControlService;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -36,36 +37,53 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
throw new RuntimeException();
}

Set<String> groupsByUsername = acs.getRoles()
var usernameRoles = extractUsernameRoles(acs, principal);
var groupRoles = extractGroupRoles(acs, principal);

return Mono.just(Sets.union(usernameRoles, groupRoles));
}

private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {
Set<String> rolesByUsername = acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_COGNITO))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equals(principal.getName())))
.anyMatch(s -> s.getValue().equalsIgnoreCase(principal.getName())))
.map(Role::getName)
.collect(Collectors.toSet());

log.debug("Matched user roles: [{}]", String.join(", ", rolesByUsername));

return rolesByUsername;
}

private Set<String> extractGroupRoles(AccessControlService acs, DefaultOAuth2User principal) {
List<String> groups = principal.getAttribute(COGNITO_GROUPS_ATTRIBUTE_NAME);
if (groups == null) {
log.debug("Cognito groups param is not present");
return Mono.just(groupsByUsername);
return Collections.emptySet();
}

Set<String> groupsByGroups = acs.getRoles()
log.debug("Token's groups: [{}]", String.join(",", groups));

Set<String> rolesByGroups = acs.getRoles()
.stream()
.filter(role -> role.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_COGNITO))
.filter(s -> s.getType().equals("group"))
.anyMatch(subject -> groups
.stream()
.anyMatch(cognitoGroup -> cognitoGroup.equals(subject.getValue()))
.anyMatch(cognitoGroup -> cognitoGroup.equalsIgnoreCase(subject.getValue()))
))
.map(Role::getName)
.collect(Collectors.toSet());

return Mono.just(Sets.union(groupsByUsername, groupsByGroups));
log.debug("Matched group roles: [{}]", String.join(", ", rolesByGroups));

return rolesByGroups;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import io.kafbat.ui.service.rbac.AccessControlService;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -43,6 +42,8 @@ public boolean isApplicable(String provider, Map<String, String> customParams) {

@Override
public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<String, Object> additionalParams) {
log.debug("Extracting github user authorities");

DefaultOAuth2User principal;
try {
principal = (DefaultOAuth2User) value;
Expand All @@ -51,21 +52,7 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
throw new RuntimeException();
}

Set<String> rolesByUsername = new HashSet<>();
String username = principal.getAttribute(USERNAME_ATTRIBUTE_NAME);
if (username == null) {
log.debug("Github username param is not present");
} else {
acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GITHUB))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equals(username)))
.map(Role::getName)
.forEach(rolesByUsername::add);
}
var usernameRoles = extractUsernameRoles(principal, acs);

OAuth2UserRequest req = (OAuth2UserRequest) additionalParams.get("request");
String infoEndpoint = req.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
Expand All @@ -81,15 +68,37 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
}
var webClient = WebClient.create(infoEndpoint);

Mono<Set<String>> rolesByOrganization = getOrganizationRoles(principal, additionalParams, acs, webClient);
Mono<Set<String>> rolesByTeams = getTeamRoles(webClient, additionalParams, acs);
Mono<Set<String>> organizationRoles = getOrganizationRoles(principal, additionalParams, acs, webClient);
Mono<Set<String>> teamRoles = getTeamRoles(webClient, additionalParams, acs);

return Mono.zip(rolesByOrganization, rolesByTeams)
.map((t) -> Stream.of(t.getT1(), t.getT2(), rolesByUsername)
return Mono.zip(organizationRoles, teamRoles)
.map((t) -> Stream.of(t.getT1(), t.getT2(), usernameRoles)
.flatMap(Collection::stream)
.collect(Collectors.toSet()));
}

private Set<String> extractUsernameRoles(DefaultOAuth2User principal, AccessControlService acs) {
String username = principal.getAttribute(USERNAME_ATTRIBUTE_NAME);
if (username == null) {
log.debug("Github username param is not present");
return Collections.emptySet();
}

var rolesByUsername = acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GITHUB))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equals(username)))
.map(Role::getName)
.collect(Collectors.toSet());

log.debug("Matched user roles: [{}]", String.join(", ", rolesByUsername));

return rolesByUsername;
}

private Mono<Set<String>> getOrganizationRoles(DefaultOAuth2User principal, Map<String, Object> additionalParams,
AccessControlService acs, WebClient webClient) {
String organization = principal.getAttribute(ORGANIZATION_ATTRIBUTE_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.kafbat.ui.model.rbac.Role;
import io.kafbat.ui.model.rbac.provider.Provider;
import io.kafbat.ui.service.rbac.AccessControlService;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -36,24 +37,32 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
throw new RuntimeException();
}

Set<String> groupsByUsername = acs.getRoles()
var usernameRoles = extractUsernameRoles(acs, principal);
var domainRoles = extractDomainRoles(acs, principal);

return Mono.just(Sets.union(usernameRoles, domainRoles));
}

private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {
return acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GOOGLE))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equals(principal.getAttribute(EMAIL_ATTRIBUTE_NAME))))
.anyMatch(s -> s.getValue().equalsIgnoreCase(principal.getAttribute(EMAIL_ATTRIBUTE_NAME))))
.map(Role::getName)
.collect(Collectors.toSet());
}


private Set<String> extractDomainRoles(AccessControlService acs, DefaultOAuth2User principal) {
String domain = principal.getAttribute(GOOGLE_DOMAIN_ATTRIBUTE_NAME);
if (domain == null) {
log.debug("Google domain param is not present");
return Mono.just(groupsByUsername);
return Collections.emptySet();
}

Set<String> groupsByDomain = acs.getRoles()
return acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
Expand All @@ -62,8 +71,6 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
.anyMatch(s -> s.getValue().equals(domain)))
.map(Role::getName)
.collect(Collectors.toSet());

return Mono.just(Sets.union(groupsByUsername, groupsByDomain));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ public class OauthAuthorityExtractor implements ProviderAuthorityExtractor {

@Override
public boolean isApplicable(String provider, Map<String, String> customParams) {
var typeMatch = OAUTH.equalsIgnoreCase(provider) || OAUTH.equalsIgnoreCase(customParams.get(TYPE));

if (!typeMatch) {
return false;
}

var containsRolesFieldNameParam = customParams.containsKey(ROLES_FIELD_PARAM_NAME);
if (!containsRolesFieldNameParam) {
log.debug("Provider [{}] doesn't contain a roles field param name, mapping won't be performed", provider);
return false;
}

return OAUTH.equalsIgnoreCase(provider) || OAUTH.equalsIgnoreCase(customParams.get(TYPE));
return true;
}

@Override
Expand All @@ -47,11 +53,14 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
throw new RuntimeException();
}

var provider = (OAuthProperties.OAuth2Provider) additionalParams.get("provider");
Assert.notNull(provider, "provider is null");
var rolesFieldName = provider.getCustomParams().get(ROLES_FIELD_PARAM_NAME);
var usernameRoles = extractUsernameRoles(acs, principal);
var roles = extractRoles(acs, principal, additionalParams);

return Mono.just(Sets.union(usernameRoles, roles));
}

Set<String> rolesByUsername = acs.getRoles()
private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2User principal) {
return acs.getRoles()
.stream()
.filter(r -> r.getSubjects()
.stream()
Expand All @@ -60,31 +69,34 @@ public Mono<Set<String>> extract(AccessControlService acs, Object value, Map<Str
.anyMatch(s -> s.getValue().equals(principal.getName())))
.map(Role::getName)
.collect(Collectors.toSet());
}

private Set<String> extractRoles(AccessControlService acs, DefaultOAuth2User principal,
Map<String, Object> additionalParams) {
var provider = (OAuthProperties.OAuth2Provider) additionalParams.get("provider");
Assert.notNull(provider, "provider is null");
var rolesFieldName = provider.getCustomParams().get(ROLES_FIELD_PARAM_NAME);

Set<String> rolesByRolesField = acs.getRoles()
var principalRoles = convertRoles(principal.getAttribute(rolesFieldName));
log.debug("Token's groups: [{}]", String.join(",", principalRoles));

Set<String> roles = acs.getRoles()
.stream()
.filter(role -> role.getSubjects()
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH))
.filter(s -> s.getType().equals("role"))
.anyMatch(subject -> {
var roleName = subject.getValue();
var principalRoles = convertRoles(principal.getAttribute(rolesFieldName));
var roleMatched = principalRoles.contains(roleName);

if (roleMatched) {
log.debug("Assigning role [{}] to user [{}]", roleName, principal.getName());
} else {
log.trace("Role [{}] not found in user [{}] roles", roleName, principal.getName());
}

return roleMatched;
return principalRoles.contains(roleName);
})
)
.map(Role::getName)
.collect(Collectors.toSet());

return Mono.just(Sets.union(rolesByUsername, rolesByRolesField));
log.debug("Matched roles: [{}]", String.join(", ", roles));

return roles;
}

@SuppressWarnings("unchecked")
Expand Down

0 comments on commit c75c5cc

Please sign in to comment.