diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResource.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResource.java index 370401612a..1ac821bedb 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResource.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResource.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2013-2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. */ package org.forgerock.openam.core.rest.session; @@ -32,12 +33,14 @@ import static org.forgerock.openam.utils.Time.currentTimeMillis; import static org.forgerock.util.promise.Promises.newResultPromise; +import com.iplanet.dpro.session.share.SessionInfo; +import com.iplanet.services.naming.WebtopNaming; +import com.iplanet.sso.SSOTokenManager; +import com.sun.identity.common.CaseInsensitiveHashMap; +import com.sun.identity.shared.debug.Debug; import java.util.Collection; -import java.util.List; import java.util.Map; - import javax.inject.Inject; - import org.forgerock.api.annotations.Action; import org.forgerock.api.annotations.Actions; import org.forgerock.api.annotations.ApiError; @@ -49,7 +52,6 @@ import org.forgerock.api.annotations.Query; import org.forgerock.api.annotations.Schema; import org.forgerock.api.enums.QueryType; -import org.forgerock.http.header.CookieHeader; import org.forgerock.json.JsonValue; import org.forgerock.json.resource.ActionRequest; import org.forgerock.json.resource.ActionResponse; @@ -86,14 +88,6 @@ import org.forgerock.services.context.Context; import org.forgerock.util.promise.Promise; -import com.iplanet.am.util.SystemProperties; -import com.iplanet.dpro.session.share.SessionInfo; -import com.iplanet.services.naming.WebtopNaming; -import com.iplanet.sso.SSOTokenManager; -import com.sun.identity.common.CaseInsensitiveHashMap; -import com.sun.identity.shared.Constants; -import com.sun.identity.shared.debug.Debug; - /** * Represents Sessions that can queried via a REST interface. * @@ -416,20 +410,8 @@ public Collection getAllServerIds() { ) }) public Promise actionCollection(Context context, ActionRequest request) { - final String cookieName = SystemProperties.get(Constants.AM_COOKIE_NAME, "iPlanetDirectoryPro"); - - String tokenId = getTokenIdFromUrlParam(request); + String tokenId = SessionResourceUtil.getTokenId(context.asContext(HttpContext.class), request); - if (tokenId == null) { - tokenId = getTokenIdFromHeader(context, cookieName); - } - - if (tokenId == null) { - tokenId = getTokenIdFromCookie(context, cookieName); - } - - // Should any of these actions in the future be allowed to function without an SSO token, this - // code will have to be moved/changed. if (tokenId == null) { final BadRequestException e = new BadRequestException("iPlanetDirectoryCookie not set on request"); LOGGER.message("SessionResource.handleNullSSOToken :: iPlanetDirectoryCookie not set on request", e); @@ -439,31 +421,6 @@ public Promise actionCollection(Context conte return internalHandleAction(tokenId, context, request); } - protected String getTokenIdFromUrlParam(ActionRequest request) { - return request.getAdditionalParameter("tokenId"); - } - - protected String getTokenIdFromCookie(Context context, String cookieName) { - final List header = context.asContext(HttpContext.class).getHeader(cookieName.toLowerCase()); - if (!header.isEmpty()) { - return header.get(0); - } - return null; - } - - protected String getTokenIdFromHeader(Context context, String cookieName) { - final List headers = context.asContext(HttpContext.class).getHeader("cookie"); - - for (String header : headers) { - for (org.forgerock.http.protocol.Cookie cookie : CookieHeader.valueOf(header).getCookies()) { - if (cookie.getName().equalsIgnoreCase(cookieName)) { - return cookie.getValue(); - } - } - } - return null; - } - /** * Actions supported are: *
    diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceAuthzModule.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceAuthzModule.java index ae67658ff2..ec87ae1a27 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceAuthzModule.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceAuthzModule.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. */ package org.forgerock.openam.core.rest.session; @@ -39,7 +40,7 @@ public class SessionResourceAuthzModule extends TokenOwnerAuthzModule { @Inject public SessionResourceAuthzModule(SSOTokenManager ssoTokenManager) { - super("tokenId", ssoTokenManager, + super(ssoTokenManager, SessionResource.DELETE_PROPERTY_ACTION_ID, SessionResource.GET_PROPERTY_ACTION_ID, SessionResource.GET_PROPERTY_NAMES_ACTION_ID, SessionResource.SET_PROPERTY_ACTION_ID, SessionResource.GET_TIME_LEFT_ACTION_ID, SessionResource.GET_MAX_IDLE_ACTION_ID, diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceUtil.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceUtil.java index 48763ec33c..fa264829cd 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceUtil.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceUtil.java @@ -12,18 +12,17 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. */ package org.forgerock.openam.core.rest.session; -import static org.forgerock.json.JsonValue.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; +import static org.forgerock.json.JsonValue.field; +import static org.forgerock.json.JsonValue.json; +import static org.forgerock.json.JsonValue.object; import com.google.inject.Inject; +import com.iplanet.am.util.SystemProperties; import com.iplanet.dpro.session.share.SessionInfo; import com.iplanet.services.naming.WebtopNamingQuery; import com.iplanet.sso.SSOException; @@ -31,9 +30,17 @@ import com.iplanet.sso.SSOTokenManager; import com.sun.identity.idm.AMIdentity; import com.sun.identity.idm.IdRepoException; +import com.sun.identity.shared.Constants; import com.sun.identity.shared.debug.Debug; import com.sun.identity.sm.DNMapper; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.forgerock.http.header.CookieHeader; +import org.forgerock.http.protocol.Cookie; import org.forgerock.json.JsonValue; +import org.forgerock.json.resource.Request; +import org.forgerock.json.resource.http.HttpContext; import org.forgerock.openam.core.rest.session.query.SessionQueryManager; import org.forgerock.openam.session.SessionConstants; import org.forgerock.openam.utils.StringUtils; @@ -76,6 +83,67 @@ public SessionResourceUtil(final SSOTokenManager ssoTokenManager, this.webtopNamingQuery = webtopNamingQuery; } + /** + * Retrieves the token ID from the given context and request. The method attempts to extract the token ID + * from various sources in the following order: + *
      + *
    1. Path of the request
    2. + *
    3. URL parameters of the request
    4. + *
    5. Cookies
    6. + *
    7. HTTP headers
    8. + *
    + * If the token ID is not found in any of these sources, the method returns {@code null}. + * + * @return The token ID if found; {@code null} otherwise. + */ + static String getTokenId(HttpContext context, Request request) { + String cookieName = SystemProperties.get(Constants.AM_COOKIE_NAME, "iPlanetDirectoryPro"); + + String tokenId = getTokenIdFromPath(request); + + if (StringUtils.isEmpty(tokenId)) { + tokenId = getTokenIdFromUrlParam(request); + } + + if (StringUtils.isEmpty(tokenId)) { + tokenId = getTokenIdFromCookie(context, cookieName); + } + + if (StringUtils.isEmpty(tokenId)) { + tokenId = getTokenIdFromHeader(context, cookieName); + } + + return StringUtils.isEmpty(tokenId) ? null : tokenId; + } + + private static String getTokenIdFromPath(Request request) { + return request.getResourcePath(); + } + + private static String getTokenIdFromUrlParam(Request request) { + return request.getAdditionalParameter("tokenId"); + } + + private static String getTokenIdFromCookie(HttpContext context, String cookieName) { + final List headers = context.getHeader("cookie"); + for (String header : headers) { + for (Cookie cookie : CookieHeader.valueOf(header).getCookies()) { + if (cookie.getName().equalsIgnoreCase(cookieName)) { + return cookie.getValue(); + } + } + } + return null; + } + + private static String getTokenIdFromHeader(HttpContext context, String headerName) { + final List header = context.getHeader(headerName); + if (!header.isEmpty()) { + return header.get(0); + } + return null; + } + /** * tokenId may, or may not, specify a valid token. If it does, retrieve it and the carefully refresh it so * as not to alter its idle time setting. If it does not exist, or is invalid, throw an SSOException. @@ -124,7 +192,7 @@ public Collection getAllServerIds() { * @return A non null collection of SessionInfos from the named server. */ public Collection generateNamedServerSession(String serverId) { - List serverList = Arrays.asList(new String[]{serverId}); + List serverList = List.of(serverId); Collection sessions = queryManager.getAllSessions(serverList); if (LOGGER.messageEnabled()) { LOGGER.message("SessionResource.generateNmaedServerSession :: retrieved session list for server, " + diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceV2.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceV2.java index 26221628a4..8cf175b176 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceV2.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/SessionResourceV2.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. */ package org.forgerock.openam.core.rest.session; @@ -24,15 +25,12 @@ import javax.inject.Inject; import java.util.Collection; -import java.util.List; import java.util.Map; -import com.iplanet.am.util.SystemProperties; import com.iplanet.dpro.session.SessionException; import com.iplanet.dpro.session.service.SessionService; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.common.CaseInsensitiveHashMap; -import com.sun.identity.shared.Constants; import com.sun.identity.shared.debug.Debug; import org.forgerock.api.annotations.Action; import org.forgerock.api.annotations.Actions; @@ -45,7 +43,6 @@ import org.forgerock.api.annotations.Schema; import org.forgerock.api.enums.ParameterSource; import org.forgerock.api.enums.QueryType; -import org.forgerock.http.header.CookieHeader; import org.forgerock.json.resource.ActionRequest; import org.forgerock.json.resource.ActionResponse; import org.forgerock.json.resource.BadRequestException; @@ -120,10 +117,7 @@ public class SessionResourceV2 implements CollectionResourceProvider { public static final String UPDATE_SESSION_PROPERTIES_ACTION_ID = "updateSessionProperties"; public static final String LOGOUT_BY_HANDLE_ACTION_ID = "logoutByHandle"; - private final SessionPropertyWhitelist sessionPropertyWhitelist; - private final Map actionHandlers; - private final SessionResourceUtil sessionResourceUtil; private final SessionService sessionService; /** @@ -138,8 +132,6 @@ public class SessionResourceV2 implements CollectionResourceProvider { public SessionResourceV2(final SSOTokenManager ssoTokenManager, AuthUtilsWrapper authUtilsWrapper, final SessionResourceUtil sessionResourceUtil, SessionPropertyWhitelist sessionPropertyWhitelist, SessionService sessionService, PartialSessionFactory partialSessionFactory) { - this.sessionResourceUtil = sessionResourceUtil; - this.sessionPropertyWhitelist = sessionPropertyWhitelist; this.sessionService = sessionService; actionHandlers = new CaseInsensitiveHashMap<>(); actionHandlers.put(REFRESH_ACTION_ID, @@ -265,19 +257,8 @@ public SessionResourceV2(final SSOTokenManager ssoTokenManager, AuthUtilsWrapper }) @Override public Promise actionCollection(Context context, ActionRequest request) { - final String cookieName = SystemProperties.get(Constants.AM_COOKIE_NAME, "iPlanetDirectoryPro"); - String tokenId = getTokenIdFromUrlParam(request); + String tokenId = SessionResourceUtil.getTokenId(context.asContext(HttpContext.class), request); - if (tokenId == null) { - tokenId = getTokenIdFromHeader(context, cookieName); - } - - if (tokenId == null) { - tokenId = getTokenIdFromCookie(context, cookieName); - } - - // Should any of these actions in the future be allowed to function without an SSO token, this - // code will have to be moved/changed. if (tokenId == null) { final BadRequestException e = new BadRequestException("iPlanetDirectoryCookie not set on request"); LOGGER.message("SessionResource.handleNullSSOToken :: iPlanetDirectoryCookie not set on request", e); @@ -287,31 +268,6 @@ public Promise actionCollection(Context conte return internalHandleAction(tokenId, context, request); } - protected String getTokenIdFromUrlParam(ActionRequest request) { - return request.getAdditionalParameter("tokenId"); - } - - protected String getTokenIdFromCookie(Context context, String cookieName) { - final List header = context.asContext(HttpContext.class).getHeader(cookieName.toLowerCase()); - if (!header.isEmpty()) { - return header.get(0); - } - return null; - } - - protected String getTokenIdFromHeader(Context context, String cookieName) { - final List headers = context.asContext(HttpContext.class).getHeader("cookie"); - - for (String header : headers) { - for (org.forgerock.http.protocol.Cookie cookie : CookieHeader.valueOf(header).getCookies()) { - if (cookie.getName().equalsIgnoreCase(cookieName)) { - return cookie.getValue(); - } - } - } - return null; - } - /** * Handle the action specified by the user (i.e. one of those in the validActions set). * @param tokenId The id of the token. diff --git a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModule.java b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModule.java index 48c3f4f36a..65015797b5 100644 --- a/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModule.java +++ b/openam-core-rest/src/main/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModule.java @@ -12,6 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. */ package org.forgerock.openam.core.rest.session; @@ -24,7 +25,6 @@ import com.iplanet.sso.SSOToken; import com.iplanet.sso.SSOTokenManager; import com.sun.identity.shared.Constants; -import org.apache.commons.lang.StringUtils; import org.forgerock.authz.filter.api.AuthorizationResult; import org.forgerock.authz.filter.crest.api.CrestAuthorizationModule; import org.forgerock.json.resource.ActionRequest; @@ -37,6 +37,7 @@ import org.forgerock.json.resource.Request; import org.forgerock.json.resource.ResourceException; import org.forgerock.json.resource.UpdateRequest; +import org.forgerock.json.resource.http.HttpContext; import org.forgerock.openam.rest.resource.SSOTokenContext; import org.forgerock.services.context.Context; import org.forgerock.util.Reject; @@ -64,23 +65,19 @@ public class TokenOwnerAuthzModule implements CrestAuthorizationModule { private final SSOTokenManager ssoTokenManager; private final Set allowedActions; - private final String tokenIdParameter; /** * Creates an authz module that will verify that a tokenId provided by the user (via query params) * is the same user (via universal identifier) as the user requesting the action. * - * @param tokenIdParameter The tokenId query parameter. May not be null. * @param ssoTokenManager An instance of the SSOTokenManager. * @param allowedActions A list of allowed actions. Will be matched ignoring case. */ - public TokenOwnerAuthzModule(String tokenIdParameter, SSOTokenManager ssoTokenManager, String... allowedActions) { + public TokenOwnerAuthzModule(SSOTokenManager ssoTokenManager, String... allowedActions) { Reject.ifNull(allowedActions); - Reject.ifTrue(StringUtils.isEmpty(tokenIdParameter)); this.ssoTokenManager = ssoTokenManager; this.allowedActions = new HashSet<>(Arrays.asList(allowedActions)); - this.tokenIdParameter = tokenIdParameter; } @Override @@ -136,12 +133,7 @@ public Promise authorizeQuery(Context co boolean isTokenOwner(Context context, Request request) throws ResourceException, SSOException { String loggedInUserId = getUserId(context); - - String tokenId = request.getResourcePath(); // infer from the token from resource path - if (StringUtils.isEmpty(tokenId)) { - //if there's no tokenId then is it supplied as additional parameter - tokenId = request.getAdditionalParameter(tokenIdParameter); - } + String tokenId = SessionResourceUtil.getTokenId(context.asContext(HttpContext.class), request); final String queryUsername = ssoTokenManager.createSSOToken(tokenId).getProperty(Constants.UNIVERSAL_IDENTIFIER); return queryUsername.equalsIgnoreCase(loggedInUserId); diff --git a/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceTest.java b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceTest.java index de84b404f5..915cb64d34 100644 --- a/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceTest.java +++ b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceTest.java @@ -12,7 +12,7 @@ * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2013-2015 ForgeRock AS. - * Portions Copyright 2021 Wren Security. + * Portions Copyright 2021-2024 Wren Security. */ package org.forgerock.openam.core.rest.session; @@ -70,6 +70,7 @@ import org.forgerock.json.resource.QueryRequest; import org.forgerock.json.resource.QueryResourceHandler; import org.forgerock.json.resource.ResourceException; +import org.forgerock.json.resource.http.HttpContext; import org.forgerock.openam.authentication.service.AuthUtilsWrapper; import org.forgerock.openam.core.realms.Realm; import org.forgerock.openam.core.realms.RealmTestHelper; @@ -82,7 +83,6 @@ import org.forgerock.services.context.AttributesContext; import org.forgerock.services.context.ClientContext; import org.forgerock.services.context.Context; -import org.forgerock.services.context.RootContext; import org.forgerock.services.context.SecurityContext; import org.forgerock.util.promise.Promise; import org.testng.annotations.AfterMethod; @@ -115,12 +115,6 @@ public class SessionResourceTest { private AMIdentity amIdentity; - private String headerResponse; - private String urlResponse; - private String cookieResponse; - - - @BeforeMethod public void setUp() throws Exception { SessionQueryManager sessionQueryManager = mock(SessionQueryManager.class); @@ -128,9 +122,6 @@ public void setUp() throws Exception { authUtilsWrapper = mock(AuthUtilsWrapper.class); propertyWhitelist = mock(SessionPropertyWhitelist.class); webtopNamingQuery = mock(WebtopNamingQuery.class); - headerResponse = null; - urlResponse = null; - cookieResponse = null; given(mockContext.getCallerSSOToken()).willReturn(ssoToken); @@ -164,25 +155,7 @@ public String convertDNToRealm(String dn) { } }); - sessionResource = new SessionResource(ssoTokenManager, authUtilsWrapper, - propertyWhitelist, sessionResourceUtil) { - - @Override - protected String getTokenIdFromHeader(Context context, String cookieName) { - return headerResponse; - } - - @Override - protected String getTokenIdFromUrlParam(ActionRequest request) { - return urlResponse; - } - - @Override - protected String getTokenIdFromCookie(Context context, String cookieName) { - return cookieResponse; - } - - }; + sessionResource = new SessionResource(ssoTokenManager, authUtilsWrapper, propertyWhitelist, sessionResourceUtil); } @AfterMethod @@ -251,10 +224,11 @@ public void shouldQueryNamedServerInServerMode() { @Test public void actionCollectionShouldFailToValidateSessionWhenSSOTokenIdNotSet() { //Given - final SSOTokenContext tokenContext = mock(SSOTokenContext.class); - final Context context = ClientContext.newInternalClientContext(tokenContext); + final HttpContext httpContext = mock(HttpContext.class); + final Context context = mock(ClientContext.class); final ActionRequest request = mock(ActionRequest.class); + given(context.asContext(HttpContext.class)).willReturn(httpContext); given(request.getAction()).willReturn(VALIDATE_ACTION_ID); //When @@ -267,13 +241,15 @@ public void actionCollectionShouldFailToValidateSessionWhenSSOTokenIdNotSet() { @Test public void actionCollectionShouldValidateSessionAndReturnTrueWhenSSOTokenValid() throws SSOException { //Given - cookieResponse = "SSO_TOKEN_ID"; final SSOTokenContext tokenContext = mock(SSOTokenContext.class); - final Context context = ClientContext.newInternalClientContext(tokenContext); + final HttpContext httpContext = mock(HttpContext.class); + final Context context = mock(ClientContext.class); final ActionRequest request = mock(ActionRequest.class); final SSOToken ssoToken = mock(SSOToken.class); final SSOTokenID ssoTokenId = mock(SSOTokenID.class); + given(context.asContext(HttpContext.class)).willReturn(httpContext); + given(httpContext.getHeader("cookie")).willReturn(List.of("iPlanetDirectoryPro=SSO_TOKEN_ID")); given(request.getAction()).willReturn(VALIDATE_ACTION_ID); given(tokenContext.getCallerSSOToken()).willReturn(ssoToken); given(ssoTokenManager.isValidToken(ssoToken)).willReturn(true); @@ -294,14 +270,15 @@ public void actionCollectionShouldValidateSessionAndReturnTrueWhenSSOTokenValid( @Test public void actionCollectionShouldLogoutSessionAndReturnEmptyJsonObjectWhenSSOTokenValid() throws SSOException { //Given - cookieResponse = "SSO_TOKEN_ID"; - final AttributesContext attrContext = new AttributesContext(new SessionContext(new RootContext(), mock(Session.class))); - final AdviceContext adviceContext = new AdviceContext(attrContext, Collections.emptySet()); + final HttpContext httpContext = mock(HttpContext.class); + final AttributesContext attrContext = new AttributesContext(new SessionContext(httpContext, mock(Session.class))); + final AdviceContext adviceContext = new AdviceContext(attrContext, Collections.emptySet()); final SecurityContext securityContext = new SecurityContext(adviceContext, null, null); final Context context = ClientContext.newInternalClientContext(new SSOTokenContext(mock(Debug.class), null, securityContext)); final ActionRequest request = mock(ActionRequest.class); final SSOTokenID ssoTokenId = mock(SSOTokenID.class); + given(request.getAdditionalParameter("tokenId")).willReturn("SSO_TOKEN_ID"); given(request.getAction()).willReturn(LOGOUT_ACTION_ID); given(authUtilsWrapper.logout(ssoTokenId.toString(), null, null)).willReturn(true); diff --git a/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceUtilTest.java b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceUtilTest.java new file mode 100644 index 0000000000..baf19240ad --- /dev/null +++ b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/SessionResourceUtilTest.java @@ -0,0 +1,107 @@ +/* + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2024 Wren Security. + */ + +package org.forgerock.openam.core.rest.session; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.util.Collections; +import java.util.List; +import org.forgerock.json.resource.Request; +import org.forgerock.json.resource.http.HttpContext; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class SessionResourceUtilTest { + + private HttpContext context; + private Request request; + + @BeforeMethod + public void setUp() { + context = mock(HttpContext.class); + request = mock(Request.class); + } + + @Test + public void testGetTokenIdFromPath() { + when(request.getResourcePath()).thenReturn("tokenIdFromPath"); + + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertEquals(tokenId, "tokenIdFromPath"); + } + + @Test + public void testGetTokenIdFromUrlParam() { + when(request.getAdditionalParameter("tokenId")).thenReturn("tokenIdFromUrlParam"); + + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertEquals(tokenId, "tokenIdFromUrlParam"); + } + + @Test + public void testGetTokenIdFromCookie() { + when(context.getHeader("cookie")).thenReturn(List.of("iPlanetDirectoryPro=tokenIdFromCookie")); + + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertEquals(tokenId, "tokenIdFromCookie"); + } + + @Test + public void testGetTokenIdFromHeader() { + when(context.getHeader("iPlanetDirectoryPro")).thenReturn(Collections.singletonList("tokenIdFromHeader")); + + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertEquals(tokenId, "tokenIdFromHeader"); + } + + @Test + public void testGetTokenIdNotFound() { + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertNull(tokenId); + } + + @Test(dataProvider = "tokenDataProvider") + public void testGetTokenIdPreferredSource(String pathToken, String urlParamToken, String cookieToken, String headerToken, String expectedToken) { + when(request.getResourcePath()).thenReturn(pathToken); + when(request.getAdditionalParameter("tokenId")).thenReturn(urlParamToken); + when(context.getHeader("cookie")).thenReturn(Collections.singletonList("iPlanetDirectoryPro=" + cookieToken)); + when(context.getHeader("iPlanetDirectoryPro")).thenReturn(Collections.singletonList(headerToken)); + + String tokenId = SessionResourceUtil.getTokenId(context, request); + + assertEquals(tokenId, expectedToken); + } + + @DataProvider(name = "tokenDataProvider") + public String[][] tokenDataProvider() { + return new String[][] { + {"tokenIdFromPath", "tokenIdFromUrlParam", "tokenIdFromCookie", "tokenIdFromHeader", "tokenIdFromPath"}, + {"", "tokenIdFromUrlParam", "tokenIdFromCookie", "tokenIdFromHeader", "tokenIdFromUrlParam"}, + {"", "", "tokenIdFromCookie", "tokenIdFromHeader", "tokenIdFromCookie"}, + {"", "", "", "tokenIdFromHeader", "tokenIdFromHeader"} + }; + } +} diff --git a/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModuleTest.java b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModuleTest.java index aba2dc1c72..ffa381deae 100644 --- a/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModuleTest.java +++ b/openam-core-rest/src/test/java/org/forgerock/openam/core/rest/session/TokenOwnerAuthzModuleTest.java @@ -1,18 +1,19 @@ /* -* The contents of this file are subject to the terms of the Common Development and -* Distribution License (the License). You may not use this file except in compliance with the -* License. -* -* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the -* specific language governing permission and limitations under the License. -* -* When distributing Covered Software, include this CDDL Header Notice in each file and include -* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL -* Header, with the fields enclosed by brackets [] replaced by your own identifying -* information: "Portions copyright [year] [name of copyright owner]". -* -* Copyright 2015-2016 ForgeRock AS. -*/ + * The contents of this file are subject to the terms of the Common Development and + * Distribution License (the License). You may not use this file except in compliance with the + * License. + * + * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the + * specific language governing permission and limitations under the License. + * + * When distributing Covered Software, include this CDDL Header Notice in each file and include + * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL + * Header, with the fields enclosed by brackets [] replaced by your own identifying + * information: "Portions copyright [year] [name of copyright owner]". + * + * Copyright 2015-2016 ForgeRock AS. + * Portions copyright 2024 Wren Security. + */ package org.forgerock.openam.core.rest.session; import static org.forgerock.util.test.assertj.AssertJPromiseAssert.*; @@ -64,7 +65,7 @@ public void theSetUp() throws SSOException { given(mockConfig.get()).willReturn(mockService); mockContext = setupUser("universal_id"); - testModule = new TokenOwnerAuthzModule("tokenId", mockTokenManager, "deleteProperty"); + testModule = new TokenOwnerAuthzModule(mockTokenManager, "deleteProperty"); } @Test