From 3af09674ae6cde4546bb74596e1dba3360522cab Mon Sep 17 00:00:00 2001 From: Aruna Vemulapalli Date: Thu, 9 Nov 2023 14:50:26 -0600 Subject: [PATCH] token order to fetch caller claims - add unittests --- .../bnd.bnd | 4 - .../client/jose4j/util/Jose4jUtilTest.java | 441 +++++++++++++++++- 2 files changed, 439 insertions(+), 6 deletions(-) diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd b/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd index 235ba3d6da0b..9a8bb4715454 100644 --- a/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd +++ b/dev/com.ibm.ws.security.openidconnect.clients.common/bnd.bnd @@ -85,9 +85,6 @@ Private-Package: \ io.openliberty.security.oidcclientcore.internal;version=latest,\ io.openliberty.security.common.jwt;version=latest,\ com.ibm.ws.security.oauth.2.0;version=latest,\ - jakarta.security.enterprise-api,\ - jakarta.security.enterprise-api,\ - jakarta.security.enterprise-api,\ jakarta.security.enterprise-api -testpath: \ @@ -110,5 +107,4 @@ Private-Package: \ com.ibm.ws.logging;version=latest,\ com.ibm.ws.container.service.compat;version=latest,\ com.ibm.ws.org.slf4j.api;version=latest,\ - com.ibm.ws.org.apache.httpcomponents,\ com.ibm.ws.org.apache.httpcomponents diff --git a/dev/com.ibm.ws.security.openidconnect.clients.common/test/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtilTest.java b/dev/com.ibm.ws.security.openidconnect.clients.common/test/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtilTest.java index 8ab682dbbf1f..8632f981474d 100644 --- a/dev/com.ibm.ws.security.openidconnect.clients.common/test/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtilTest.java +++ b/dev/com.ibm.ws.security.openidconnect.clients.common/test/com/ibm/ws/security/openidconnect/client/jose4j/util/Jose4jUtilTest.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2018, 2021 IBM Corporation and others. + * Copyright (c) 2018, 2023 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -19,10 +19,14 @@ import static org.junit.Assert.fail; import java.security.PublicKey; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.jmock.Expectations; +import org.jose4j.jwt.JwtClaims; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -423,6 +427,350 @@ public void testGetVerifyKey_getKeyFromJwkUrl() throws Exception { } } + /************************************** getUserName **************************************/ + + private static final String CLAIM_NAME_USER_NAME = "user_name"; + private static final String CLAIM_USER_ANOTHER = "user_another"; + private static final String CLAIM_NAME_REALM_NAME = "realm_name"; + private static final String CLAIM_NAME_UNIQUE_SECURITY_NAME = "unique_security_name"; + private static final String CLAIM_NAME_GROUP_IDENTIFIER = "groupIds"; + + private static final String USER_1 = "User1"; + private static final String REALM_NAME_1 = "RealmName1"; + private static final String USN_1 = "USN1"; + private static final List GROUP_IDS_1 = Arrays.asList(new String[] { "GID11", "GID12", "GID13" }); + + private static final String USER_2 = "User2"; + private static final String REALM_NAME_2 = "RealmName2"; + private static final String USN_2 = "USN2"; + private static final String S_GROUP_ID_2 = "GID20"; + private static final List GROUP_IDS_2 = Arrays.asList(new String[] { "GID21", "GID22", "GID23" }); + + private static final String USER_3 = "User3"; + private static final String REALM_NAME_3 = "RealmName3"; + private static final String USN_3 = "USN3"; + private static final List GROUP_IDS_3 = Arrays.asList(new String[] { "GID31", "GID32", "GID33" }); + + @Test + public void testGetUserName_fromIdToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).isSocial(); + will(returnValue(false)); + one(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_NAME_USER_NAME)); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapIdTokenOnly()); + assertEquals("Expected userName:" + USER_2 + " but received:" + userNameValue + ".", USER_2, userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromIdToken", e); + } + } + + @Test + public void testGetUserName_fromIdToken_usingTokenOrder() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).isSocial(); + will(returnValue(false)); + one(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_NAME_USER_NAME)); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createEmptyClaimsMapAccessToken()); + assertEquals("Expected userName:" + USER_2 + " but received:" + userNameValue + ".", USER_2, userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromIdToken_usingTokenOrder", e); + } + } + + @Test + public void testGetUserName_fromIdToken_missingClaim() throws Exception { + mockery.checking(new Expectations() { + { + allowing(clientConfig).isSocial(); + will(returnValue(false)); + allowing(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_NAME_USER_NAME)); + one(clientConfig).getClientId(); + will(returnValue("clientId")); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapUserInfoOnly()); + assertNull("user claim should have been null but was [" + userNameValue + "].", userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromIdToken_missingClaim", e); + } + } + + @Test + public void testGetUserName_fromAllTokens_missingClaim() throws Exception { + mockery.checking(new Expectations() { + { + allowing(clientConfig).isSocial(); + will(returnValue(false)); + allowing(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_USER_ANOTHER)); + one(clientConfig).getClientId(); + will(returnValue("clientId")); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens()); + assertNull("user claim should have been null but was [" + userNameValue + "].", userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromAllTokens_missingClaim", e); + } + } + + @Test + public void testGetClaim_fromAllTokens_nullClaimMap() throws Exception { + mockery.checking(new Expectations() { + { + allowing(clientConfig).isSocial(); + will(returnValue(false)); + allowing(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_USER_ANOTHER)); + one(clientConfig).getClientId(); + will(returnValue("clientId")); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createNullClaimsMapATAndUI()); + assertNull("user claim should have been null but was [" + userNameValue + "].", userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetClaim_fromAllTokens_nullClaimMap", e); + } + } + + @Test + public void testGetUserName_fromAccessToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).isSocial(); + will(returnValue(false)); + one(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_NAME_USER_NAME)); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens()); + assertEquals("Expected userName:" + USER_1 + " but received:" + userNameValue + ".", USER_1, userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromAccessToken", e); + } + } + + @Test + public void testGetUserName_fromUserInfo() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).isSocial(); + will(returnValue(false)); + one(clientConfig).getUserIdentifier(); + will(returnValue(CLAIM_NAME_USER_NAME)); + } + }); + try { + Object userNameValue = util.getUserName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapUserInfoOnly()); + assertEquals("Expected userName:" + USER_3 + " but received:" + userNameValue + ".", USER_3, userNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUserName_fromUserInfo", e); + } + } + + /************************************** testGetRealmName **************************************/ + + @Test + public void testGetRealmName_fromIdToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getRealmName(); + will(returnValue(null)); + one(clientConfig).getRealmIdentifier(); + will(returnValue(CLAIM_NAME_REALM_NAME)); + } + }); + try { + Object realmNameValue = util.getRealmName(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapIdTokenOnly()); + assertEquals("Expected realmName:" + REALM_NAME_2 + " but received:" + realmNameValue + ".", REALM_NAME_2, realmNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetRealmName_fromIdToken", e); + } + } + + @Test + public void testGetRealmName_fromAccessToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getRealmName(); + will(returnValue(null)); + one(clientConfig).getRealmIdentifier(); + will(returnValue(CLAIM_NAME_REALM_NAME)); + } + }); + try { + Object realmNameValue = util.getRealmName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens()); + assertEquals("Expected realmName:" + REALM_NAME_1 + " but received:" + realmNameValue + ".", REALM_NAME_1, realmNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetRealmName_fromAccessToken", e); + } + } + + @Test + public void testGetRealmName_fromUserInfo() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getRealmName(); + will(returnValue(null)); + one(clientConfig).getRealmIdentifier(); + will(returnValue(CLAIM_NAME_REALM_NAME)); + } + }); + try { + Object realmNameValue = util.getRealmName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapUserInfoOnly()); + assertEquals("Expected realmName:" + REALM_NAME_3 + " but received:" + realmNameValue + ".", REALM_NAME_3, realmNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetRealmName_fromUserInfo", e); + } + } + + /************************************** getUniqueSecurityName **************************************/ + + @Test + public void testGetUniqueSecurityName_fromIdToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getUniqueUserIdentifier(); + will(returnValue(CLAIM_NAME_UNIQUE_SECURITY_NAME)); + } + }); + try { + Object uniqueSecurityNameValue = util.getUniqueSecurityName(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapIdTokenOnly(), USER_2); + assertEquals("Expected uniqueSecurityNameValue:" + USN_2 + " but received:" + uniqueSecurityNameValue + ".", USN_2, uniqueSecurityNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUniqueSecurityName_fromIdToken", e); + } + } + + @Test + public void testGetUniqueSecurityName_fromAccessToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getUniqueUserIdentifier(); + will(returnValue(CLAIM_NAME_UNIQUE_SECURITY_NAME)); + } + }); + try { + Object uniqueSecurityNameValue = util.getUniqueSecurityName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens(), USER_1); + assertEquals("Expected uniqueSecurityNameValue:" + USN_1 + " but received:" + uniqueSecurityNameValue + ".", USN_1, uniqueSecurityNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUniqueSecurityName_fromAccessToken", e); + } + } + + @Test + public void testGetUniqueSecurityName_fromUserInfo() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getUniqueUserIdentifier(); + will(returnValue(CLAIM_NAME_UNIQUE_SECURITY_NAME)); + } + }); + try { + Object uniqueSecurityNameValue = util.getUniqueSecurityName(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapUserInfoOnly(), USER_3); + assertEquals("Expected uniqueSecurityNameValue:" + USN_3 + " but received:" + uniqueSecurityNameValue + ".", USN_3, uniqueSecurityNameValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetUniqueSecurityName_fromUserInfo", e); + } + } + + /************************************** getGroups **************************************/ + + @Test + public void testGetGroupIds_Single_fromIdToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + } + }); + try { + List groupIdsValue = util.getGroupIds(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapSingleGroupIdTokenOnly()); + assertEquals("Expected list of groupIds: {" + S_GROUP_ID_2 + "} matches the received", Arrays.asList(new String[] { S_GROUP_ID_2 }), groupIdsValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetGroupIds_Single_fromIdToken", e); + } + } + + @Test + public void testGetGroups_fromIdToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + } + }); + try { + List groupIdsValue = util.getGroupIds(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapIdTokenOnly()); + assertEquals("Expected list of groupIds: " + toString(GROUP_IDS_2) + " matches the received", GROUP_IDS_2, groupIdsValue); + List groupsValue = util.getGroups(clientConfig, getTokensOrderToFetchCallerClaimsIdTokenOnly(), createClaimsMapIdTokenOnly(), REALM_NAME_2); + List expectedGroups = convertGroups(GROUP_IDS_2, REALM_NAME_2); + assertEquals("Expected list of groups: " + toString(expectedGroups) + " matches the received", expectedGroups, groupsValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetGroups_fromIdToken", e); + } + } + + @Test + public void testGetGroups_fromAccessToken() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + } + }); + try { + List groupIdsValue = util.getGroupIds(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens()); + assertEquals("Expected list of groupIds: " + toString(GROUP_IDS_1) + " matches the received", GROUP_IDS_1, groupIdsValue); + List groupsValue = util.getGroups(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapAllTokens(), REALM_NAME_1); + List expectedGroups = convertGroups(GROUP_IDS_1, REALM_NAME_1); + assertEquals("Expected list of groups: " + toString(expectedGroups) + " matches the received", expectedGroups, groupsValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetGroups_fromAccessToken", e); + } + } + + @Test + public void testGetGroups_fromUserInfo() throws Exception { + mockery.checking(new Expectations() { + { + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + one(clientConfig).getGroupIdentifier(); + will(returnValue(CLAIM_NAME_GROUP_IDENTIFIER)); + } + }); + + try { + List groupIdsValue = util.getGroupIds(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapUserInfoOnly()); + assertEquals("Expected list of groupIds: " + toString(GROUP_IDS_3) + " matches the received", GROUP_IDS_3, groupIdsValue); + List groupsValue = util.getGroups(clientConfig, getTokensOrderToFetchCallerClaimsAll(), createClaimsMapUserInfoOnly(), REALM_NAME_3); + List expectedGroups = convertGroups(GROUP_IDS_3, REALM_NAME_3); + assertEquals("Expected list of groups: " + toString(expectedGroups) + " matches the received", expectedGroups, groupsValue); + } catch (Exception e) { + outputMgr.failWithThrowable("testGetGroups_fromAccessToken", e); + } + } + /************************************** Helper methods **************************************/ private void mockUseAccessTokenAsIdTokenValue(final boolean useAccessTokenAsIdToken) { @@ -449,4 +797,93 @@ private Map getTokensWithIdTokenKey(String idTokenValue) { return tokens; } + private List getTokensOrderToFetchCallerClaimsAll() { + return Arrays.asList(new String[] { Constants.TOKEN_TYPE_ACCESS_TOKEN, Constants.TOKEN_TYPE_ID_TOKEN, Constants.TOKEN_TYPE_USER_INFO }); + } + + private List getTokensOrderToFetchCallerClaimsIdTokenOnly() { + return Arrays.asList(new String[] { Constants.TOKEN_TYPE_ID_TOKEN }); + } + + private JwtClaims createClaims(String userName, String realmName, String usn, Object groupIds) { + JwtClaims rvalue = new JwtClaims(); + rvalue.setClaim(CLAIM_NAME_USER_NAME, userName); + rvalue.setClaim(CLAIM_NAME_REALM_NAME, realmName); + rvalue.setClaim(CLAIM_NAME_UNIQUE_SECURITY_NAME, usn); + rvalue.setClaim(CLAIM_NAME_GROUP_IDENTIFIER, groupIds); + return rvalue; + } + + // private JwtClaims createDefaultClaims() { + // return createClaims(BASIC_USER, BASIC_REALM_NAME, BASIC_USN, BASIC_GROUP_IDS); + // } + + private JwtClaims createEmptyClaims() { + return new JwtClaims(); + } + + private Map createClaimsMapIdTokenOnly() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createClaims(USER_2, REALM_NAME_2, USN_2, GROUP_IDS_2)); + + return claimsMap; + } + + private Map createClaimsMapSingleGroupIdTokenOnly() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createClaims(USER_2, REALM_NAME_2, USN_2, S_GROUP_ID_2)); + + return claimsMap; + } + + private Map createClaimsMapAllTokens() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ACCESS_TOKEN, createClaims(USER_1, REALM_NAME_1, USN_1, GROUP_IDS_1)); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createClaims(USER_2, REALM_NAME_2, USN_2, GROUP_IDS_2)); + claimsMap.put(Constants.TOKEN_TYPE_USER_INFO, createClaims(USER_3, REALM_NAME_3, USN_3, GROUP_IDS_3)); + + return claimsMap; + } + + private Map createEmptyClaimsMapAccessToken() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ACCESS_TOKEN, createEmptyClaims()); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createClaims(USER_2, REALM_NAME_2, USN_2, GROUP_IDS_2)); + claimsMap.put(Constants.TOKEN_TYPE_USER_INFO, createClaims(USER_3, REALM_NAME_3, USN_3, GROUP_IDS_3)); + + return claimsMap; + } + + private Map createNullClaimsMapATAndUI() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ACCESS_TOKEN, null); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createClaims(USER_2, REALM_NAME_2, USN_2, GROUP_IDS_2)); + claimsMap.put(Constants.TOKEN_TYPE_USER_INFO, null); + + return claimsMap; + } + + private Map createClaimsMapUserInfoOnly() { + Map claimsMap = new HashMap(); + claimsMap.put(Constants.TOKEN_TYPE_ACCESS_TOKEN, createEmptyClaims()); + claimsMap.put(Constants.TOKEN_TYPE_ID_TOKEN, createEmptyClaims()); + claimsMap.put(Constants.TOKEN_TYPE_USER_INFO, createClaims(USER_3, REALM_NAME_3, USN_3, GROUP_IDS_3)); + + return claimsMap; + } + + private List convertGroups(List groupIds, String realm) { + List groups = new ArrayList(); + for (String gid : groupIds) { + String group = new StringBuffer("group:").append(realm).append("/").append(gid).toString(); + groups.add(group); + } + + return groups; + } + + private static String toString(List myList) { + return Arrays.toString(myList.toArray()); + } + }