Skip to content

Commit

Permalink
Prioritize groups path over groups claim
Browse files Browse the repository at this point in the history
But fallback to the groups claim if the claim speficied in `smallrye.jwt.path.groups` is empty.
If the groups claim is also empty, fallback to the default claim configured by `smallrye.jwt.claims.groups`.

Signed-off-by: Lukas Ziefle <[email protected]>
  • Loading branch information
oemel09 committed Mar 8, 2024
1 parent d7d7e99 commit 62025e5
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,28 @@ public static void setClaims(JwtClaims claimsSet, String token, JWTAuthContextIn
String sub = findSubject(authContextInfo, claimsSet);
claimsSet.setClaim(Claims.sub.name(), sub);
}
Object groupsClaim = claimsSet.getClaimValue(Claims.groups.name());
if (groupsClaim == null || groupsClaim instanceof Map
|| authContextInfo.getGroupsPath() != null) { // specified groups path takes precedence over existing groups and replaces them
List<String> groups = findGroups(authContextInfo, claimsSet);
claimsSet.setClaim(Claims.groups.name(), groups);
} else if (groupsClaim instanceof String) {
claimsSet.setClaim(Claims.groups.name(),
splitStringClaimValue(groupsClaim.toString(), authContextInfo));

List<String> roles = null;
if (authContextInfo.getGroupsPath() != null) {
roles = findGroups(authContextInfo, claimsSet);
}
if (authContextInfo.getGroupsPath() == null || roles == null) {
Object groupsClaim = claimsSet.getClaimValue(Claims.groups.name());
if (groupsClaim instanceof String) {
roles = splitStringClaimValue(groupsClaim.toString(), authContextInfo);
} else if (groupsClaim != null && !(groupsClaim instanceof Map)) {
@SuppressWarnings("")
List<String> rolesFromGroupsClaim = List.class.cast(groupsClaim);
roles = rolesFromGroupsClaim;
}
}
if (roles == null || roles.isEmpty()) {
final String defaultGroupsClaim = authContextInfo.getDefaultGroupsClaim();
if (defaultGroupsClaim != null) {
roles = Collections.singletonList(defaultGroupsClaim);
}
}
claimsSet.setClaim(Claims.groups.name(), roles);

// Process the rolesMapping claim
if (claimsSet.hasClaim(ROLE_MAPPINGS)) {
Expand All @@ -79,29 +92,23 @@ private static String findSubject(JWTAuthContextInfo authContextInfo, JwtClaims
}

private static List<String> findGroups(JWTAuthContextInfo authContextInfo, JwtClaims claimsSet) {
if (authContextInfo.getGroupsPath() != null) {
final String[] pathSegments = splitClaimPath(authContextInfo.getGroupsPath());
Object claimValue = findClaimValue(authContextInfo.getGroupsPath(), claimsSet.getClaimsMap(), pathSegments, 0);
final String[] pathSegments = splitClaimPath(authContextInfo.getGroupsPath());
Object claimValue = findClaimValue(authContextInfo.getGroupsPath(), claimsSet.getClaimsMap(), pathSegments, 0);

if (claimValue instanceof List) {
@SuppressWarnings("unchecked")
List<String> groups = List.class.cast(claimValue);
// Force a check that a list contains the string values only
try {
return Arrays.asList(groups.toArray(new String[] {}));
} catch (ArrayStoreException ex) {
PrincipalLogging.log.claimAtPathIsNotAnArrayOfStrings(authContextInfo.getGroupsPath());
}
} else if (claimValue instanceof String) {
return splitStringClaimValue(claimValue.toString(), authContextInfo);
} else {
PrincipalLogging.log.claimAtPathIsNeitherAnArrayOfStringsNorString(authContextInfo.getGroupsPath());
if (claimValue instanceof List) {
@SuppressWarnings("unchecked")
List<String> groups = List.class.cast(claimValue);
// Force a check that a list contains the string values only
try {
return Arrays.asList(groups.toArray(new String[] {}));
} catch (ArrayStoreException ex) {
PrincipalLogging.log.claimAtPathIsNotAnArrayOfStrings(authContextInfo.getGroupsPath());
}
} else if (claimValue instanceof String) {
return splitStringClaimValue(claimValue.toString(), authContextInfo);
} else {
PrincipalLogging.log.claimAtPathIsNeitherAnArrayOfStringsNorString(authContextInfo.getGroupsPath());
}
if (authContextInfo.getDefaultGroupsClaim() != null) {
return Collections.singletonList(authContextInfo.getDefaultGroupsClaim());
}

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright 2019 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.smallrye.jwt.auth.principal;

import static org.junit.jupiter.api.Assertions.assertIterableEquals;

import java.util.List;

import org.eclipse.microprofile.jwt.Claims;
import org.jose4j.jwt.JwtClaims;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opentest4j.AssertionFailedError;

@ExtendWith(MockitoExtension.class)
class PrincipalUtilsTest {

private static final List<String> rolesInGroupsClaim = List.of("group1", "group2");
private static final String rolesInGroupsClaimAsString = "group1 group2";
private static final String defaultRole = "default1";
private static final List<String> rolesInDefaultGroup = List.of(defaultRole);
private static final List<String> rolesInCustomGroupsClaim = List.of("custom1", "custom2");
private static final String rolesInCustomGroupsClaimAsString = "custom1 custom2";

private static final List<TestData> tests = List.of(
new TestData("group claim is set, custom groups are not set",
rolesInGroupsClaim,
rolesInGroupsClaim, null, false, null),
new TestData("group claim is set, custom groups are not set, default role is set",
rolesInGroupsClaim,
rolesInGroupsClaim, null, false, defaultRole),

new TestData("group claim is set, custom groups are set",
rolesInCustomGroupsClaim,
rolesInGroupsClaim, rolesInCustomGroupsClaim, true, null),
new TestData("group claim is set, custom groups are set, default role is set",
rolesInCustomGroupsClaim,
rolesInGroupsClaim, rolesInCustomGroupsClaim, true, defaultRole),

new TestData("group claim is set, custom groups are set but empty",
rolesInGroupsClaim,
rolesInGroupsClaim, null, true, null),
new TestData("group claim is set, custom groups are set but empty, default role is set",
rolesInGroupsClaim,
rolesInGroupsClaim, null, true, defaultRole),

new TestData("group claim is empty, custom groups are set",
rolesInCustomGroupsClaim,
null, rolesInCustomGroupsClaim, true, null),
new TestData("group claim is empty, custom groups are set, and default role is set",
rolesInCustomGroupsClaim,
null, rolesInCustomGroupsClaim, true, defaultRole),

new TestData("group claim is empty, custom groups are not set",
null,
null, null, false, null),
new TestData("group claim is empty, custom groups are not set, default role is set",
rolesInDefaultGroup,
null, null, false, defaultRole),

new TestData("group claim is empty, custom groups are set but empty",
null,
null, null, true, null),
new TestData("group claim is empty, custom groups are set but empty, default role is set",
rolesInDefaultGroup,
null, null, true, defaultRole),

new TestData("group claim is set as string, custom groups are not set",
rolesInGroupsClaim,
rolesInGroupsClaimAsString, null, false, null),
new TestData("group claim is empty, custom groups are set as string",
rolesInCustomGroupsClaim,
null, rolesInCustomGroupsClaimAsString, true, null));

@Test
void testGroupsClaimSettings() throws Exception {
for (TestData td : tests) {
JwtClaims claimSet = td.getClaimSet();
PrincipalUtils.setClaims(claimSet, td.getToken(), td.getAuthContextInfo());

@SuppressWarnings("unchecked")
List<String> actualRoles = List.class.cast(claimSet.getClaimValue(Claims.groups.name()));
try {
assertIterableEquals(td.getExpectedRoles(), actualRoles);
} catch (AssertionFailedError e) {
throw new AssertionFailedError(td.getName(), e);
}
}
}

private static class TestData {

private static final String CUSTOM_GROUPS_PATH = "testroles";

private final String name;
private final List<String> expectedRoles;
private final Object rolesInGroupsClaim;
private final Object rolesInCustomClaim;
private final boolean setCustomGroupsPath;
private final String defaultGroupsClaim;

public TestData(String name, List<String> expectedRoles, Object rolesInGroupsClaim, Object rolesInCustomClaim,
boolean setCustomGroupsPath, String defaultGroupsClaim) {
this.name = name;
this.expectedRoles = expectedRoles;
this.rolesInGroupsClaim = rolesInGroupsClaim;
this.rolesInCustomClaim = rolesInCustomClaim;
this.setCustomGroupsPath = setCustomGroupsPath;
this.defaultGroupsClaim = defaultGroupsClaim;
}

public String getName() {
return name;
}

public List<String> getExpectedRoles() {
return expectedRoles;
}

public JwtClaims getClaimSet() {
JwtClaims claimSet = new JwtClaims();
if (rolesInGroupsClaim != null) {
claimSet.setClaim(Claims.groups.name(), rolesInGroupsClaim);
}
if (rolesInCustomClaim != null) {
claimSet.setClaim(CUSTOM_GROUPS_PATH, rolesInCustomClaim);
}
return claimSet;
}

public String getToken() {
return "test.token.signature";
}

public JWTAuthContextInfo getAuthContextInfo() {
JWTAuthContextInfo authContextInfo = new JWTAuthContextInfo();
if (setCustomGroupsPath) {
authContextInfo.setGroupsPath(CUSTOM_GROUPS_PATH);
}
authContextInfo.setDefaultGroupsClaim(defaultGroupsClaim);
return authContextInfo;
}

}

}

0 comments on commit 62025e5

Please sign in to comment.