Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_COGNITO))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equalsIgnoreCase(principal.getName())))
.anyMatch(s -> principal.getName() != null && principal.getName().matches(s.getValue())))
.map(Role::getName)
.collect(Collectors.toSet());

Expand All @@ -76,7 +76,7 @@ private Set<String> extractGroupRoles(AccessControlService acs, DefaultOAuth2Use
.filter(s -> s.getType().equals("group"))
.anyMatch(subject -> groups
.stream()
.anyMatch(cognitoGroup -> cognitoGroup.equalsIgnoreCase(subject.getValue()))
.anyMatch(cognitoGroup -> cognitoGroup.matches(subject.getValue()))
))
.map(Role::getName)
.collect(Collectors.toSet());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private Set<String> extractUsernameRoles(DefaultOAuth2User principal, AccessCont
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GITHUB))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equals(username)))
.anyMatch(s -> username.matches(s.getValue())))
.map(Role::getName)
.collect(Collectors.toSet());

Expand Down Expand Up @@ -131,7 +131,7 @@ private Mono<Set<String>> getOrganizationRoles(DefaultOAuth2User principal, Map<
.filter(s -> s.getType().equals(ORGANIZATION))
.anyMatch(subject -> orgsMap.stream()
.map(org -> org.get(ORGANIZATION_NAME).toString())
.anyMatch(orgName -> orgName.equalsIgnoreCase(subject.getValue()))
.anyMatch(orgName -> orgName.matches(subject.getValue()))
))
.map(Role::getName)
.collect(Collectors.toSet()));
Expand Down Expand Up @@ -189,7 +189,7 @@ private Mono<Set<String>> getTeamRoles(WebClient webClient, Map<String, Object>
.filter(s -> s.getProvider().equals(Provider.OAUTH_GITHUB))
.filter(s -> s.getType().equals("team"))
.anyMatch(subject -> teams.stream()
.anyMatch(teamName -> teamName.equalsIgnoreCase(subject.getValue()))
.anyMatch(teamName -> teamName.matches(subject.getValue()))
))
.map(Role::getName)
.collect(Collectors.toSet()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GOOGLE))
.filter(s -> s.getType().equals("user"))
.anyMatch(s -> s.getValue().equalsIgnoreCase(principal.getAttribute(EMAIL_ATTRIBUTE_NAME))))
.anyMatch(s -> {
String email = principal.getAttribute(EMAIL_ATTRIBUTE_NAME);
return email != null && email.matches(s.getValue());
}))
.map(Role::getName)
.collect(Collectors.toSet());
}
Expand All @@ -68,7 +71,7 @@ private Set<String> extractDomainRoles(AccessControlService acs, DefaultOAuth2Us
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH_GOOGLE))
.filter(s -> s.getType().equals("domain"))
.anyMatch(s -> s.getValue().equals(domain)))
.anyMatch(s -> domain.matches(s.getValue())))
.map(Role::getName)
.collect(Collectors.toSet());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private Set<String> extractUsernameRoles(AccessControlService acs, DefaultOAuth2
.filter(s -> s.getType().equals("user"))
.peek(s -> log.trace("[{}] matches [{}]? [{}]", s.getValue(), principalName,
s.getValue().equalsIgnoreCase(principalName)))
.anyMatch(s -> s.getValue().equalsIgnoreCase(principalName)))
.anyMatch(s -> principalName != null && principalName.matches(s.getValue())))
.map(Role::getName)
.collect(Collectors.toSet());

Expand Down Expand Up @@ -94,11 +94,7 @@ private Set<String> extractRoles(AccessControlService acs, DefaultOAuth2User pri
.stream()
.filter(s -> s.getProvider().equals(Provider.OAUTH))
.filter(s -> s.getType().equals("role"))
.anyMatch(subject -> {
var roleName = subject.getValue();
return principalRoles.contains(roleName);
})
)
.anyMatch(subject -> principalRoles.stream().anyMatch(s -> s.matches(subject.getValue()))))
.map(Role::getName)
.collect(Collectors.toSet());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package io.kafbat.ui.config;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import static org.springframework.security.oauth2.client.registration.ClientRegistration.withRegistrationId;

import io.kafbat.ui.config.auth.OAuthProperties;
import io.kafbat.ui.model.rbac.Role;
import io.kafbat.ui.service.rbac.AccessControlService;
import io.kafbat.ui.service.rbac.extractor.CognitoAuthorityExtractor;
import io.kafbat.ui.service.rbac.extractor.GithubAuthorityExtractor;
import io.kafbat.ui.service.rbac.extractor.GoogleAuthorityExtractor;
import io.kafbat.ui.service.rbac.extractor.OauthAuthorityExtractor;
import io.kafbat.ui.service.rbac.extractor.ProviderAuthorityExtractor;
import io.kafbat.ui.util.AccessControlServiceMock;
import java.io.InputStream;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.BeanAccess;

public class RegexBasedProviderAuthorityExtractorTest {


private final AccessControlService accessControlService = new AccessControlServiceMock().getMock();
Yaml yaml;
ProviderAuthorityExtractor extractor;

@BeforeEach
void setUp() {
yaml = new Yaml();
yaml.setBeanAccess(BeanAccess.FIELD);

InputStream rolesFile = this.getClass()
.getClassLoader()
.getResourceAsStream("roles_definition.yaml");

Role[] roleArray = yaml.loadAs(rolesFile, Role[].class);
when(accessControlService.getRoles()).thenReturn(List.of(roleArray));

}

@SneakyThrows
@Test
void extractOauth2Authorities() {

extractor = new OauthAuthorityExtractor();

OAuth2User oauth2User = new DefaultOAuth2User(
AuthorityUtils.createAuthorityList("SCOPE_message:read"),
Map.of("role_definition", Set.of("ROLE-ADMIN", "ANOTHER-ROLE"), "user_name", "[email protected]"),
"user_name");

HashMap<String, Object> additionalParams = new HashMap<>();
OAuthProperties.OAuth2Provider provider = new OAuthProperties.OAuth2Provider();
provider.setCustomParams(Map.of("roles-field", "role_definition"));
additionalParams.put("provider", provider);

Set<String> roles = extractor.extract(accessControlService, oauth2User, additionalParams).block();

assertEquals(Set.of("viewer", "admin"), roles);

}

@SneakyThrows
@Test
void extractCognitoAuthorities() {

extractor = new CognitoAuthorityExtractor();

OAuth2User oauth2User = new DefaultOAuth2User(
AuthorityUtils.createAuthorityList("SCOPE_message:read"),
Map.of("cognito:groups", List.of("ROLE-ADMIN", "ANOTHER-ROLE"), "user_name", "[email protected]"),
"user_name");

HashMap<String, Object> additionalParams = new HashMap<>();

OAuthProperties.OAuth2Provider provider = new OAuthProperties.OAuth2Provider();
provider.setCustomParams(Map.of("roles-field", "role_definition"));
additionalParams.put("provider", provider);

Set<String> roles = extractor.extract(accessControlService, oauth2User, additionalParams).block();

assertEquals(Set.of("viewer", "admin"), roles);

}

@SneakyThrows
@Test
void extractGithubAuthorities() {

extractor = new GithubAuthorityExtractor();

OAuth2User oauth2User = new DefaultOAuth2User(
AuthorityUtils.createAuthorityList("SCOPE_message:read"),
Map.of("login", "[email protected]"),
"login");

HashMap<String, Object> additionalParams = new HashMap<>();

OAuthProperties.OAuth2Provider provider = new OAuthProperties.OAuth2Provider();
additionalParams.put("provider", provider);

additionalParams.put("request", new OAuth2UserRequest(
withRegistrationId("registration-1")
.clientId("client-1")
.clientSecret("secret")
.redirectUri("https://client.com")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationUri("https://provider.com/oauth2/authorization")
.tokenUri("https://provider.com/oauth2/token")
.clientName("Client 1")
.build(),
new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "XXXX", Instant.now(),
Instant.now().plus(10, ChronoUnit.HOURS))));

Set<String> roles = extractor.extract(accessControlService, oauth2User, additionalParams).block();

assertEquals(Set.of("viewer"), roles);

}

@SneakyThrows
@Test
void extractGoogleAuthorities() {

extractor = new GoogleAuthorityExtractor();

OAuth2User oauth2User = new DefaultOAuth2User(
AuthorityUtils.createAuthorityList("SCOPE_message:read"),
Map.of("hd", "test.domain.com", "email", "[email protected]"),
"email");

HashMap<String, Object> additionalParams = new HashMap<>();

OAuthProperties.OAuth2Provider provider = new OAuthProperties.OAuth2Provider();
provider.setCustomParams(Map.of("roles-field", "role_definition"));
additionalParams.put("provider", provider);

Set<String> roles = extractor.extract(accessControlService, oauth2User, additionalParams).block();

assertEquals(Set.of("viewer", "admin"), roles);

}

}
49 changes: 49 additions & 0 deletions api/src/test/resources/roles_definition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
- name: 'admin'
subjects:
- provider: 'OAUTH'
value: 'ROLE-[A-Z]+'
type: 'role'
- provider: 'OAUTH_COGNITO'
value: 'ROLE-[A-Z]+'
type: 'group'
- provider: 'OAUTH_GOOGLE'
value: '.*.domain.com'
type: 'domain'
clusters:
- local
- remote
permissions:
- resource: APPLICATIONCONFIG
actions: [ all ]
- name: 'viewer'
subjects:
- provider: 'LDAP'
value: 'CS-XXX'
type: 'kafka-viewer'
- provider: 'OAUTH'
value: '.*@kafka.com'
type: 'user'
- provider: 'OAUTH_COGNITO'
value: '.*@kafka.com'
type: 'user'
- provider: 'OAUTH_GITHUB'
value: '.*@kafka.com'
type: 'user'
- provider: 'OAUTH_GOOGLE'
value: '.*@kafka.com'
type: 'user'
clusters:
- remote
permissions:
- resource: APPLICATIONCONFIG
actions: [ all ]
- name: 'editor'
subjects:
- provider: 'OAUTH'
value: 'ROLE_EDITOR'
type: 'role'
clusters:
- local
permissions:
- resource: APPLICATIONCONFIG
actions: [ all ]
Loading