Skip to content

Commit 1995620

Browse files
authored
Fixes resource roles consideration (#501)
* 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]>
1 parent 31ac86c commit 1995620

File tree

5 files changed

+201
-140
lines changed

5 files changed

+201
-140
lines changed

basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/java/org/eclipse/digitaltwin/basyx/aasrepository/feature/authorization/TestAuthorizedAasRepository.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@ public void getAasWithCorrectRoleAndPermission() throws IOException {
150150
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
151151
}
152152

153+
@Test
154+
public void getAasWithOnlyResourceRole() throws IOException {
155+
DummyCredential dummyCredential = DummyCredentialStore.USER_CREDENTIAL;
156+
157+
String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());
158+
159+
CloseableHttpResponse retrievalResponse = getElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID), accessToken);
160+
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
161+
}
162+
163+
@Test
164+
public void getAasWithBothRealmAndResourceRole() throws IOException {
165+
DummyCredential dummyCredential = DummyCredentialStore.VISITOR_CREDENTIAL;
166+
167+
String accessToken = tokenProvider.getAccessToken(dummyCredential.getUsername(), dummyCredential.getPassword());
168+
169+
CloseableHttpResponse retrievalResponse = getElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID), accessToken);
170+
assertEquals(HttpStatus.OK.value(), retrievalResponse.getCode());
171+
}
172+
153173
@Test
154174
public void getAasWithCorrectRoleAndSpecificAasPermission() throws IOException {
155175
DummyCredential dummyCredential = DummyCredentialStore.BASYX_READER_TWO_CREDENTIAL;
@@ -196,6 +216,16 @@ public void createAasWithCorrectRoleAndPermission() throws IOException {
196216
deleteElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID_2), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL));
197217
}
198218

219+
@Test
220+
public void createAasWithBothRealmAndResourceRole() throws IOException {
221+
String accessToken = getAccessToken(DummyCredentialStore.VISITOR_CREDENTIAL);
222+
223+
CloseableHttpResponse retrievalResponse = createAasOnRepositoryWithAuthorization(getAasJSONString(AAS_SIMPLE_2_JSON), accessToken);
224+
assertEquals(HttpStatus.CREATED.value(), retrievalResponse.getCode());
225+
226+
deleteElementWithAuthorization(getSpecificAasAccessURL(SPECIFIC_SHELL_ID_2), getAccessToken(DummyCredentialStore.ADMIN_CREDENTIAL));
227+
}
228+
199229
@Test
200230
public void createAasWithInsufficientPermissionRole() throws IOException {
201231
String accessToken = getAccessToken(DummyCredentialStore.BASYX_READER_CREDENTIAL);

basyx.aasrepository/basyx.aasrepository-feature-authorization/src/test/resources/rbac_rules.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,30 @@
1515
"aasIds": "*"
1616
}
1717
},
18+
{
19+
"role": "basyx-user",
20+
"action": "READ",
21+
"targetInformation": {
22+
"@type": "aas",
23+
"aasIds": "specificAasId"
24+
}
25+
},
26+
{
27+
"role": "basyx-user",
28+
"action": "CREATE",
29+
"targetInformation": {
30+
"@type": "aas",
31+
"aasIds": "specificAasId-2"
32+
}
33+
},
34+
{
35+
"role": "visitor",
36+
"action": "READ",
37+
"targetInformation": {
38+
"@type": "aas",
39+
"aasIds": "specificAasId"
40+
}
41+
},
1842
{
1943
"role": "basyx-reader-two",
2044
"action": "READ",

basyx.common/basyx.authorization/src/main/java/org/eclipse/digitaltwin/basyx/authorization/rbac/KeycloakRoleProvider.java

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.HashMap;
3131
import java.util.List;
3232
import java.util.Map;
33+
import java.util.stream.Collectors;
3334

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

7071
Map<String, Collection<String>> realmAccess = new HashMap<>();
71-
Map<String, Collection<String>> resourceAccess = new HashMap<>();
72+
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();
7273

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

82-
private List<String> extractRolesFromClaims(Map<String, Collection<String>> realmAccess, Map<String, Collection<String>> resourceAccess) {
83+
private List<String> extractRolesFromClaims(Map<String, Collection<String>> realmAccess, Map<String, Map<String, Collection<String>>> resourceAccess) {
8384
if (realmAccess.isEmpty() && resourceAccess.isEmpty())
8485
return new ArrayList<>();
8586

86-
Collection<String> realmRoles = realmAccess.get(CLAIM_ROLES);
87-
Collection<String> resourceRoles = resourceAccess.get(CLAIM_ROLES);
88-
89-
if ((realmRoles == null || realmRoles.isEmpty()) && (resourceRoles == null || resourceRoles.isEmpty()))
90-
return new ArrayList<>();
91-
92-
return mergeRoles(realmRoles, resourceRoles);
93-
}
94-
95-
private List<String> mergeRoles(Collection<String> realmRoles, Collection<String> resourceRoles) {
96-
97-
if (realmRoles == null || realmRoles.isEmpty())
98-
return new ArrayList<>(resourceRoles);
99-
100-
if (resourceRoles == null || resourceRoles.isEmpty())
101-
return new ArrayList<>(realmRoles);
87+
List<String> roles = new ArrayList<>();
10288

103-
List<String> rolesUnion = new ArrayList<>(realmRoles);
89+
Collection<String> realmRoles = realmAccess.get(CLAIM_ROLES);
10490

105-
resourceRoles.stream().filter(resourceRole -> !rolesUnion.contains(resourceRole)).forEach(rolesUnion::add);
106-
107-
return rolesUnion;
91+
if (realmRoles != null && !realmRoles.isEmpty())
92+
roles.addAll(realmRoles);
93+
94+
for (Map.Entry<String, Map<String, Collection<String>>> entry : resourceAccess.entrySet()) {
95+
Map<String, Collection<String>> clientRolesMap = entry.getValue();
96+
Collection<String> clientRoles = clientRolesMap.get(CLAIM_ROLES);
97+
98+
if (clientRoles != null)
99+
roles.addAll(clientRoles);
100+
}
101+
102+
return roles.stream().distinct().collect(Collectors.toList());
108103
}
109104

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

124-
}
119+
}

basyx.common/basyx.authorization/src/test/java/org/eclipse/digitaltwin/basyx/authorization/TestKeycloakRoleProvider.java

Lines changed: 98 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -47,143 +47,142 @@
4747
public class TestKeycloakRoleProvider {
4848

4949
@Mock
50-
private SubjectInformationProvider<Object> subjectInformationProvider;
50+
private SubjectInformationProvider<Object> subjectInformationProvider;
5151

5252
@Mock
53-
private Jwt jwt;
53+
private Jwt jwt;
5454

5555
@InjectMocks
56-
private KeycloakRoleProvider keycloakRoleProvider;
56+
private KeycloakRoleProvider keycloakRoleProvider;
5757

5858
@Before
59-
public void setUp() {
60-
MockitoAnnotations.openMocks(this);
59+
public void setUp() {
60+
MockitoAnnotations.openMocks(this);
6161

62-
@SuppressWarnings("unchecked")
63-
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
64-
when(subjectInfo.get()).thenReturn(jwt);
65-
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
66-
}
62+
@SuppressWarnings("unchecked")
63+
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
64+
when(subjectInfo.get()).thenReturn(jwt);
65+
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
66+
}
6767

6868
@Test
69-
public void getRoles_whenBothRealmAndResourceRolesPresent() {
70-
Map<String, Collection<String>> realmAccess = new HashMap<>();
71-
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
69+
public void getRoles_whenBothRealmAndResourceRolesPresent() {
70+
Map<String, Collection<String>> realmAccess = new HashMap<>();
71+
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
7272

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

76-
when(jwt.hasClaim("realm_access")).thenReturn(true);
77-
when(jwt.hasClaim("resource_access")).thenReturn(true);
78-
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
79-
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
77+
when(jwt.hasClaim("realm_access")).thenReturn(true);
78+
when(jwt.hasClaim("resource_access")).thenReturn(true);
79+
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
80+
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
8081

81-
List<String> roles = keycloakRoleProvider.getRoles();
82+
List<String> roles = keycloakRoleProvider.getRoles();
8283

83-
assertEquals(3, roles.size());
84-
assertTrue(roles.contains("ROLE_USER"));
85-
assertTrue(roles.contains("ROLE_ADMIN"));
86-
assertTrue(roles.contains("ROLE_SUPERUSER"));
87-
}
84+
assertEquals(4, roles.size());
85+
assertTrue(roles.contains("ROLE_USER"));
86+
assertTrue(roles.contains("ROLE_ADMIN"));
87+
assertTrue(roles.contains("ROLE_SUPERUSER"));
88+
assertTrue(roles.contains("ROLE_SUPPORT"));
89+
}
8890

8991
@Test
90-
public void getRoles_whenOnlyRealmRolesPresent() {
91-
Map<String, Collection<String>> realmAccess = new HashMap<>();
92-
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
92+
public void getRoles_whenOnlyRealmRolesPresent() {
93+
Map<String, Collection<String>> realmAccess = new HashMap<>();
94+
realmAccess.put("roles", Arrays.asList("ROLE_USER", "ROLE_ADMIN"));
9395

94-
when(jwt.hasClaim("realm_access")).thenReturn(true);
95-
when(jwt.hasClaim("resource_access")).thenReturn(true);
96-
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
97-
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());
96+
when(jwt.hasClaim("realm_access")).thenReturn(true);
97+
when(jwt.hasClaim("resource_access")).thenReturn(true);
98+
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
99+
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());
98100

99-
List<String> roles = keycloakRoleProvider.getRoles();
101+
List<String> roles = keycloakRoleProvider.getRoles();
100102

101-
assertEquals(2, roles.size());
102-
assertTrue(roles.contains("ROLE_USER"));
103-
assertTrue(roles.contains("ROLE_ADMIN"));
104-
}
103+
assertEquals(2, roles.size());
104+
assertTrue(roles.contains("ROLE_USER"));
105+
assertTrue(roles.contains("ROLE_ADMIN"));
106+
}
105107

106108
@Test
107-
public void getRoles_whenOnlyResourceRolesPresent() {
108-
Map<String, Collection<String>> resourceAccess = new HashMap<>();
109-
resourceAccess.put("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_SUPPORT"));
109+
public void getRoles_whenOnlyResourceRolesPresent() {
110+
Map<String, Map<String, Collection<String>>> resourceAccess = new HashMap<>();
111+
resourceAccess.put("client1", new HashMap<>(Collections.singletonMap("roles", Arrays.asList("ROLE_SUPERUSER", "ROLE_SUPPORT"))));
110112

111-
when(jwt.hasClaim("realm_access")).thenReturn(true);
112-
when(jwt.hasClaim("resource_access")).thenReturn(true);
113-
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
114-
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
113+
when(jwt.hasClaim("realm_access")).thenReturn(true);
114+
when(jwt.hasClaim("resource_access")).thenReturn(true);
115+
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
116+
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
115117

116-
List<String> roles = keycloakRoleProvider.getRoles();
118+
List<String> roles = keycloakRoleProvider.getRoles();
117119

118-
assertEquals(2, roles.size());
119-
assertTrue(roles.contains("ROLE_SUPERUSER"));
120-
assertTrue(roles.contains("ROLE_SUPPORT"));
121-
}
120+
assertEquals(2, roles.size());
121+
assertTrue(roles.contains("ROLE_SUPERUSER"));
122+
assertTrue(roles.contains("ROLE_SUPPORT"));
123+
}
122124

123125
@Test
124-
public void getRoles_whenNoRolesPresent() {
125-
when(jwt.hasClaim("realm_access")).thenReturn(true);
126-
when(jwt.hasClaim("resource_access")).thenReturn(true);
127-
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
128-
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());
129-
130-
List<String> roles = keycloakRoleProvider.getRoles();
131-
132-
assertTrue(roles.isEmpty());
133-
}
126+
public void getRoles_whenNoRolesPresent() {
127+
when(jwt.hasClaim("realm_access")).thenReturn(true);
128+
when(jwt.hasClaim("resource_access")).thenReturn(true);
129+
when(jwt.getClaim("realm_access")).thenReturn(Collections.emptyMap());
130+
when(jwt.getClaim("resource_access")).thenReturn(Collections.emptyMap());
134131

135-
@Test(expected = NullSubjectException.class)
136-
public void getRoles_whenJwtIsNull() {
132+
List<String> roles = keycloakRoleProvider.getRoles();
137133

138-
@SuppressWarnings("unchecked")
139-
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
140-
when(subjectInfo.get()).thenReturn(null);
141-
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
134+
assertTrue(roles.isEmpty());
135+
}
142136

143-
keycloakRoleProvider.getRoles();
144-
}
137+
@Test(expected = NullSubjectException.class)
138+
public void getRoles_whenJwtIsNull() {
139+
@SuppressWarnings("unchecked")
140+
SubjectInformation<Object> subjectInfo = mock(SubjectInformation.class);
141+
when(subjectInfo.get()).thenReturn(null);
142+
when(subjectInformationProvider.get()).thenReturn(subjectInfo);
145143

146-
@Test
147-
public void getRoles_whenRealmAccessNotPresentButResourceAccessPresent() {
148-
Map<String, Collection<String>> resourceAccess = new HashMap<>();
149-
resourceAccess.put("roles", Arrays.asList("ROLE_SUPPORT", "ROLE_USER"));
144+
keycloakRoleProvider.getRoles();
145+
}
150146

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

153-
when(jwt.hasClaim("resource_access")).thenReturn(true);
154-
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
152+
when(jwt.hasClaim("realm_access")).thenReturn(false);
153+
when(jwt.hasClaim("resource_access")).thenReturn(true);
154+
when(jwt.getClaim("resource_access")).thenReturn(resourceAccess);
155155

156-
List<String> roles = keycloakRoleProvider.getRoles();
156+
List<String> roles = keycloakRoleProvider.getRoles();
157157

158-
assertEquals(2, roles.size());
159-
assertTrue(roles.contains("ROLE_SUPPORT"));
160-
assertTrue(roles.contains("ROLE_USER"));
161-
}
158+
assertEquals(2, roles.size());
159+
assertTrue(roles.contains("ROLE_SUPPORT"));
160+
assertTrue(roles.contains("ROLE_USER"));
161+
}
162162

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

169-
when(jwt.hasClaim("realm_access")).thenReturn(true);
170-
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
168+
when(jwt.hasClaim("resource_access")).thenReturn(false);
169+
when(jwt.hasClaim("realm_access")).thenReturn(true);
170+
when(jwt.getClaim("realm_access")).thenReturn(realmAccess);
171171

172-
List<String> roles = keycloakRoleProvider.getRoles();
172+
List<String> roles = keycloakRoleProvider.getRoles();
173173

174-
assertEquals(2, roles.size());
175-
assertTrue(roles.contains("ROLE_USER"));
176-
assertTrue(roles.contains("ROLE_ADMIN"));
177-
}
174+
assertEquals(2, roles.size());
175+
assertTrue(roles.contains("ROLE_USER"));
176+
assertTrue(roles.contains("ROLE_ADMIN"));
177+
}
178178

179179
@Test
180-
public void getRoles_whenClaimNotPresent() {
181-
182-
when(jwt.hasClaim("realm_access")).thenReturn(false);
183-
when(jwt.hasClaim("resource_access")).thenReturn(false);
184-
185-
List<String> roles = keycloakRoleProvider.getRoles();
186-
187-
assertTrue(roles.isEmpty());
188-
}
180+
public void getRoles_whenClaimNotPresent() {
181+
when(jwt.hasClaim("realm_access")).thenReturn(false);
182+
when(jwt.hasClaim("resource_access")).thenReturn(false);
183+
184+
List<String> roles = keycloakRoleProvider.getRoles();
185+
186+
assertTrue(roles.isEmpty());
187+
}
189188
}

0 commit comments

Comments
 (0)