Skip to content

Commit

Permalink
Fixes resource roles consideration (#501)
Browse files Browse the repository at this point in the history
* Enhancements on Role provider to also consider resource_roles

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Addresses review remarks

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Improves formatting

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Fixes the resource roles consideration

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Changes the Datastructure to make compatible

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Fixes tests

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Refactors test

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Improves formatting

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Addresses review remarks

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Adjust rules

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

* Removes warnings

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>

---------

Signed-off-by: Mohammad Ghazanfar Ali Danish <[email protected]>
  • Loading branch information
mdanish98 authored Oct 28, 2024
1 parent 31ac86c commit 1995620
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 140 deletions.
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

0 comments on commit 1995620

Please sign in to comment.