Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes resource roles consideration #501

Merged
merged 13 commits into from
Oct 28, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,26 @@ public void getAasWithCorrectRoleAndPermission() throws IOException {
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
}

@Test
public void getAasWithOnlyResourceRole() throws IOException {
DummyCredential dummyCredential = DummyCredentialStore.USER_CREDENTIAL;

String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());

CloseableHttpResponse retrievalResponse = getElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID), accessToken);
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
}

@Test
public void getAasWithBothRealmAndResourceRole() throws IOException {
DummyCredential dummyCredential = DummyCredentialStore.VISITOR_CREDENTIAL;

String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());

CloseableHttpResponse retrievalResponse = getElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID), accessToken);
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
}

@Test
public void getAasWithCorrectRoleAndSpecificAasPermission() throws IOException {
DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_TWO_CREDENTIAL;
Expand Down Expand Up @@ -196,6 +216,16 @@ public void createAasWithCorrectRoleAndPermission() throws IOException {
deleteElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID_2), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL));
}

@Test
public void createAasWithBothRealmAndResourceRole() throws IOException {
String accessToken = getAccessToken(DummyCredentialStore.VISITOR_CREDENTIAL);

CloseableHttpResponse retrievalResponse = createAasOnRepositoryWithAuthorization(getAasJSONString(AAS_SIMPLE_2_JSON), accessToken);
assertEquals(HttpStatus.CREATED.value(), retrievalResponse.getCode());

deleteElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID_2), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL));
}

@Test
public void createAasWithInsufficientPermissionRole() throws IOException {
String accessToken = getAccessToken(DummyCredentialStore.BASYX_READER_CREDENTIAL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,30 @@
"aasIds": "*"
}
},
{
"role": "basyx-user",
"action": "READ",
"targetInformation": {
"@type": "aas",
"aasIds": "specificAasId"
}
},
{
"role": "basyx-user",
"action": "CREATE",
"targetInformation": {
"@type": "aas",
"aasIds": "specificAasId-2"
}
},
{
"role": "visitor",
"action": "READ",
"targetInformation": {
"@type": "aas",
"aasIds": "specificAasId"
}
},
{
"role": "basyx-reader-two",
"action": "READ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.eclipse.digitaltwin.basyx.authorization.CommonAuthorizationProperties;
import org.eclipse.digitaltwin.basyx.authorization.SubjectInformation;
Expand Down Expand Up @@ -68,7 +69,7 @@ public List<String> getRoles() {
validateJwt(jwt);

Map<String, Collection<String>> realmAccess = new HashMap<>();
Map<String, Collection<String>> resourceAccess = new HashMap<>();
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();

if (jwt.hasClaim(CLAIM_REALM_ACCESS))
realmAccess = jwt.getClaim(CLAIM_REALM_ACCESS);
Expand All @@ -79,32 +80,26 @@ public List<String> getRoles() {
return extractRolesFromClaims(realmAccess, resourceAccess);
}

private List<String> extractRolesFromClaims(Map<String, Collection<String>> realmAccess, Map<String, Collection<String>> resourceAccess) {
private List<String> extractRolesFromClaims(Map<String, Collection<String>> realmAccess, Map<String, Map<String, Collection<String>>> resourceAccess) {
if (realmAccess.isEmpty() && resourceAccess.isEmpty())
return new ArrayList<>();

Collection<String> realmRoles = realmAccess.get(CLAIM_ROLES);
Collection<String> resourceRoles = resourceAccess.get(CLAIM_ROLES);

if ((realmRoles == null || realmRoles.isEmpty()) && (resourceRoles == null || resourceRoles.isEmpty()))
return new ArrayList<>();

return mergeRoles(realmRoles, resourceRoles);
}

private List<String> mergeRoles(Collection<String> realmRoles, Collection<String> resourceRoles) {

if (realmRoles == null || realmRoles.isEmpty())
return new ArrayList<>(resourceRoles);

if (resourceRoles == null || resourceRoles.isEmpty())
return new ArrayList<>(realmRoles);
List<String> roles = new ArrayList<>();

List<String> rolesUnion = new ArrayList<>(realmRoles);
Collection<String> realmRoles = realmAccess.get(CLAIM_ROLES);

resourceRoles.stream().filter(resourceRole -> !rolesUnion.contains(resourceRole)).forEach(rolesUnion::add);

return rolesUnion;
if (realmRoles != null && !realmRoles.isEmpty())
roles.addAll(realmRoles);

for (Map.Entry<String, Map<String, Collection<String>>> entry : resourceAccess.entrySet()) {
Map<String, Collection<String>> clientRolesMap = entry.getValue();
Collection<String> clientRoles = clientRolesMap.get(CLAIM_ROLES);

if (clientRoles != null)
roles.addAll(clientRoles);
}

return roles.stream().distinct().collect(Collectors.toList());
}

private void validateJwt(Jwt jwt) {
Expand All @@ -121,4 +116,4 @@ private SubjectInformation<Object> getSubjectInformation() {
return subjectInfo;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,143 +47,142 @@
public class TestKeycloakRoleProvider {

@Mock
private SubjectInformationProvider<Object> subjectInformationProvider;
private SubjectInformationProvider<Object> subjectInformationProvider;

@Mock
private Jwt jwt;
private Jwt jwt;

@InjectMocks
private KeycloakRoleProvider keycloakRoleProvider;
private KeycloakRoleProvider keycloakRoleProvider;

@Before
public void setUp() {
MockitoAnnotations.openMocks(this);
public void setUp() {
MockitoAnnotations.openMocks(this);

@SuppressWarnings("unchecked")
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
when(subjectInfo.get()).thenReturn(jwt);
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
}
@SuppressWarnings("unchecked")
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
when(subjectInfo.get()).thenReturn(jwt);
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
}

@Test
public void getRoles_whenBothRealmAndResourceRolesPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
public void getRoles_whenBothRealmAndResourceRolesPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));

Map<String, Collection<String>> resourceAccess = new HashMap<>();
resourceAccess.put("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_ADMIN"));
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();
resourceAccess.put("client1", new HashMap<>(Collections.singletonMap("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_ADMIN"))));
resourceAccess.put("client2", new HashMap<>(Collections.singletonMap("roles", Arrays.asList("ROLE_SUPPORT"))));

when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);

List<String> roles = keycloakRoleProvider.getRoles();
List<String> roles = keycloakRoleProvider.getRoles();

assertEquals(3, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
assertTrue(roles.contains("ROLE_SUPERUSER"));
}
assertEquals(4, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
assertTrue(roles.contains("ROLE_SUPERUSER"));
assertTrue(roles.contains("ROLE_SUPPORT"));
}

@Test
public void getRoles_whenOnlyRealmRolesPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
public void getRoles_whenOnlyRealmRolesPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));

when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());

List<String> roles = keycloakRoleProvider.getRoles();
List<String> roles = keycloakRoleProvider.getRoles();

assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
}
assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
}

@Test
public void getRoles_whenOnlyResourceRolesPresent() {
Map<String, Collection<String>> resourceAccess = new HashMap<>();
resourceAccess.put("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_SUPPORT"));
public void getRoles_whenOnlyResourceRolesPresent() {
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();
resourceAccess.put("client1", new HashMap<>(Collections.singletonMap("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_SUPPORT"))));

when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);

List<String> roles = keycloakRoleProvider.getRoles();
List<String> roles = keycloakRoleProvider.getRoles();

assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_SUPERUSER"));
assertTrue(roles.contains("ROLE_SUPPORT"));
}
assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_SUPERUSER"));
assertTrue(roles.contains("ROLE_SUPPORT"));
}

@Test
public void getRoles_whenNoRolesPresent() {
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());

List<String> roles = keycloakRoleProvider.getRoles();

assertTrue(roles.isEmpty());
}
public void getRoles_whenNoRolesPresent() {
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());

@Test(expected = NullSubjectException.class)
public void getRoles_whenJwtIsNull() {
List<String> roles = keycloakRoleProvider.getRoles();

@SuppressWarnings("unchecked")
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
when(subjectInfo.get()).thenReturn(null);
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
assertTrue(roles.isEmpty());
}

keycloakRoleProvider.getRoles();
}
@Test(expected = NullSubjectException.class)
public void getRoles_whenJwtIsNull() {
@SuppressWarnings("unchecked")
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
when(subjectInfo.get()).thenReturn(null);
when(subjectInformationProvider.get()).thenReturn(subjectInfo);

@Test
public void getRoles_whenRealmAccessNotPresentButResourceAccessPresent() {
Map<String, Collection<String>> resourceAccess = new HashMap<>();
resourceAccess.put("roles", Arrays.asList("ROLE_SUPPORT", "ROLE_USER"));
keycloakRoleProvider.getRoles();
}

when(jwt.hasClaim("realm_access")).thenReturn(false);
@Test
public void getRoles_whenRealmAccessNotPresentButResourceAccessPresent() {
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();
resourceAccess.put("client1", new HashMap<>(Collections.singletonMap("roles", Arrays.asList("ROLE_SUPPORT", "ROLE_USER"))));

when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
when(jwt.hasClaim("realm_access")).thenReturn(false);
when(jwt.hasClaim("resource_access")).thenReturn(true);
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);

List<String> roles = keycloakRoleProvider.getRoles();
List<String> roles = keycloakRoleProvider.getRoles();

assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_SUPPORT"));
assertTrue(roles.contains("ROLE_USER"));
}
assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_SUPPORT"));
assertTrue(roles.contains("ROLE_USER"));
}

@Test
public void getRoles_whenResourceAccessNotPresentButRealmAccessPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
when(jwt.hasClaim("resource_access")).thenReturn(false);
public void getRoles_whenResourceAccessNotPresentButRealmAccessPresent() {
Map<String, Collection<String>> realmAccess = new HashMap<>();
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));

when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
when(jwt.hasClaim("resource_access")).thenReturn(false);
when(jwt.hasClaim("realm_access")).thenReturn(true);
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);

List<String> roles = keycloakRoleProvider.getRoles();
List<String> roles = keycloakRoleProvider.getRoles();

assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
}
assertEquals(2, roles.size());
assertTrue(roles.contains("ROLE_USER"));
assertTrue(roles.contains("ROLE_ADMIN"));
}

@Test
public void getRoles_whenClaimNotPresent() {

when(jwt.hasClaim("realm_access")).thenReturn(false);
when(jwt.hasClaim("resource_access")).thenReturn(false);

List<String> roles = keycloakRoleProvider.getRoles();

assertTrue(roles.isEmpty());
}
public void getRoles_whenClaimNotPresent() {
when(jwt.hasClaim("realm_access")).thenReturn(false);
when(jwt.hasClaim("resource_access")).thenReturn(false);

List<String> roles = keycloakRoleProvider.getRoles();

assertTrue(roles.isEmpty());
}
}
Loading