Skip to content

Commit

Permalink
Merge pull request #175 from orchitech/fix-authorization-issue
Browse files Browse the repository at this point in the history
Fix login broken by insufficient tokenId checking
  • Loading branch information
pavelhoral authored Jun 7, 2024
2 parents 0ec6976 + 1eb243a commit d233674
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -416,20 +410,8 @@ public Collection<String> getAllServerIds() {
)
})
public Promise<ActionResponse, ResourceException> 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);
Expand All @@ -439,31 +421,6 @@ public Promise<ActionResponse, ResourceException> 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<String> 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<String> 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:
* <ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,35 @@
* 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;
import com.iplanet.sso.SSOToken;
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;
Expand Down Expand Up @@ -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:
* <ol>
* <li>Path of the request</li>
* <li>URL parameters of the request</li>
* <li>Cookies</li>
* <li>HTTP headers</li>
* </ol>
* 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<String> 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<String> 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.
Expand Down Expand Up @@ -124,7 +192,7 @@ public Collection<String> getAllServerIds() {
* @return A non null collection of SessionInfos from the named server.
*/
public Collection<SessionInfo> generateNamedServerSession(String serverId) {
List<String> serverList = Arrays.asList(new String[]{serverId});
List<String> serverList = List.of(serverId);
Collection<SessionInfo> sessions = queryManager.getAllSessions(serverList);
if (LOGGER.messageEnabled()) {
LOGGER.message("SessionResource.generateNmaedServerSession :: retrieved session list for server, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<String, ActionHandler> actionHandlers;
private final SessionResourceUtil sessionResourceUtil;
private final SessionService sessionService;

/**
Expand All @@ -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,
Expand Down Expand Up @@ -265,19 +257,8 @@ public SessionResourceV2(final SSOTokenManager ssoTokenManager, AuthUtilsWrapper
})
@Override
public Promise<ActionResponse, ResourceException> 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);
Expand All @@ -287,31 +268,6 @@ public Promise<ActionResponse, ResourceException> 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<String> 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<String> 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.
Expand Down
Loading

0 comments on commit d233674

Please sign in to comment.