org.forgerock.openam.forgerockrest.RestDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for org.forgerock.openam.forgerockrest.RestDispatcher.java

Source

/*
 * 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 2012-2013 ForgeRock Inc.
 */
package org.forgerock.openam.forgerockrest;

import com.iplanet.sso.SSOToken;
import com.sun.identity.security.AdminTokenAction;
import com.sun.identity.shared.debug.Debug;
import com.sun.identity.sm.OrganizationConfigManager;
import com.sun.identity.sm.SMSException;
import org.apache.commons.lang.StringUtils;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ActionRequest;
import org.forgerock.json.resource.ConnectionFactory;
import org.forgerock.json.resource.CreateRequest;
import org.forgerock.json.resource.DeleteRequest;
import org.forgerock.json.resource.NotFoundException;
import org.forgerock.json.resource.PatchRequest;
import org.forgerock.json.resource.QueryRequest;
import org.forgerock.json.resource.QueryResultHandler;
import org.forgerock.json.resource.ReadRequest;
import org.forgerock.json.resource.RequestHandler;
import org.forgerock.json.resource.Resource;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.Resources;
import org.forgerock.json.resource.ResultHandler;
import org.forgerock.json.resource.Router;
import org.forgerock.json.resource.RoutingMode;
import org.forgerock.json.resource.ServerContext;
import org.forgerock.json.resource.UpdateRequest;
import org.forgerock.openam.cts.CTSPersistentStore;
import org.forgerock.openam.cts.utils.JSONSerialisation;
import org.forgerock.openam.dashboard.DashboardResource;
import org.forgerock.openam.forgerockrest.cts.CoreTokenResource;
import org.forgerock.openam.forgerockrest.server.ServerInfoResource;
import org.forgerock.openam.forgerockrest.session.SessionResource;
import org.forgerock.openam.guice.InjectorHolder;

import javax.servlet.ServletException;
import java.security.AccessController;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

/**
 * A simple {@code Map} based collection resource provider.
 */
public class RestDispatcher {

    public static Debug debug = Debug.getInstance("frRest");

    /** Rest endpoint for realms. */
    public static final String REALMS = "/realms"; // Protected by AdminAuthorizationFilter
    /** Rest endpoint for users. */
    public static final String USERS = "/users"; // Protected by PassThroughAuthorizationFilter-Just logging
    /** Rest endpoint for groups. */
    public static final String GROUPS = "/groups"; // Protected by PassThroughAuthorizationFilter-Just logging
    /** Rest endpoint for agents. */
    public static final String AGENTS = "/agents"; // Protected by PassThroughAuthorizationFilter-Just logging
    /** Rest endpoint for dashboard. */
    public static final String DASHBOARD = "/dashboard"; // Protected by PassThroughAuthorizationFilter-Just logging
    /** Rest endpoint for sessions. */
    public static final String SESSIONS = "/sessions"; // Protected by SessionResourceAuthZFilter - Admin + custom
    /** Rest endpoint for tokens. */
    public static final String TOKENS = "/tokens"; // Protected by AdminAuthorizationFilter
    /** Rest endpoint for server info. */
    public static final String SERVER_INFO = "/serverinfo"; // Protected by PassThroughAuthorizationFilter-Just logging

    private static RestDispatcher instance = null;
    private ConnectionFactory factory = null;

    private RestDispatcher() {

    }

    public static RestDispatcher getInstance() {
        if (instance == null) {
            instance = new RestDispatcher();
        }
        return instance;
    }

    /**
     * Adds all valid reserved endpoints to a Set
     *
     * @return a Set containing al the valid reserved endpoints
     */
    private static Set<String> getEndpointList() {
        Set<String> endpoints = new HashSet<String>(8);
        endpoints.add(USERS);
        endpoints.add(GROUPS);
        endpoints.add(AGENTS);
        endpoints.add(REALMS);
        endpoints.add(DASHBOARD);
        endpoints.add(SESSIONS);
        endpoints.add(TOKENS);
        endpoints.add(SERVER_INFO);
        return endpoints;
    }

    /**
     * Returns a request handler which will handle all requests for a valid endpoint
     *
     * @param path          The full resource name
     * @param parsedDetails Map of realmPath, resourceName, and resourceID
     * @return A request handler which will handle all requests to a realm,
     *         including sub-realms, users, and groups.
     */
    private static RequestHandler realm(Map parsedDetails, final String path) {
        final Router router = new Router();
        String endpoint = (String) parsedDetails.get("resourceName");
        String realmPath = (String) parsedDetails.get("realmPath");

        if (endpoint.equalsIgnoreCase(USERS)) {
            router.addRoute(endpoint, new IdentityResource("user", realmPath));
            router.addRoute(RoutingMode.STARTS_WITH, "/{user}", subrealms(parsedDetails, path));
        } else if (endpoint.equalsIgnoreCase(AGENTS)) {
            router.addRoute(endpoint, new IdentityResource("agent", realmPath));
            router.addRoute(RoutingMode.STARTS_WITH, "/{agent}", subrealms(parsedDetails, path));
        } else if (endpoint.equalsIgnoreCase(GROUPS)) {
            router.addRoute(endpoint, new IdentityResource("group", realmPath));
            router.addRoute(RoutingMode.STARTS_WITH, "/{group}", subrealms(parsedDetails, path));
        } else if (endpoint.equalsIgnoreCase(REALMS)) {
            router.addRoute(endpoint, new RealmResource(realmPath));
            router.addRoute(RoutingMode.STARTS_WITH, "/{realm}", subrealms(parsedDetails, path));
        } else if (endpoint.equalsIgnoreCase(DASHBOARD)) {
            router.addRoute(endpoint, new DashboardResource());
        } else if (endpoint.equalsIgnoreCase(SESSIONS)) {
            router.addRoute(endpoint, new SessionResource());
        } else if (endpoint.equalsIgnoreCase(TOKENS)) {
            JSONSerialisation serialisation = new JSONSerialisation();
            CTSPersistentStore store = InjectorHolder.getInstance(CTSPersistentStore.class);
            CoreTokenResource resource = new CoreTokenResource(serialisation, store);
            router.addRoute(endpoint, resource);
        } else if (endpoint.equalsIgnoreCase(SERVER_INFO)) {
            router.addRoute(endpoint, new ServerInfoResource());
        }
        return router;
    }

    /**
     * @return connection factory
     * @throws ResourceException if resource name is not valid
     */
    public ConnectionFactory buildConnectionFactory() throws ResourceException {
        factory = Resources.newInternalConnectionFactory(new RequestHandler() {
            public void handleAction(ServerContext serverContext, ActionRequest actionRequest,
                    ResultHandler<JsonValue> jsonValueResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(actionRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, actionRequest.getResourceName());
                    rootRealm.handleAction(serverContext, actionRequest, jsonValueResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    jsonValueResultHandler.handleError(nfe);
                }
            }

            public void handleCreate(ServerContext serverContext, CreateRequest createRequest,
                    ResultHandler<Resource> resourceResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(createRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, createRequest.getResourceName());
                    rootRealm.handleCreate(serverContext, createRequest, resourceResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    resourceResultHandler.handleError(nfe);
                }
            }

            public void handleDelete(ServerContext serverContext, DeleteRequest deleteRequest,
                    ResultHandler<Resource> resourceResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(deleteRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, deleteRequest.getResourceName());
                    rootRealm.handleDelete(serverContext, deleteRequest, resourceResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    resourceResultHandler.handleError(nfe);
                }
            }

            public void handlePatch(ServerContext serverContext, PatchRequest patchRequest,
                    ResultHandler<Resource> resourceResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(patchRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, patchRequest.getResourceName());
                    rootRealm.handlePatch(serverContext, patchRequest, resourceResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    resourceResultHandler.handleError(nfe);
                }
            }

            public void handleQuery(ServerContext serverContext, QueryRequest queryRequest,
                    QueryResultHandler queryResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(queryRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, queryRequest.getResourceName());
                    rootRealm.handleQuery(serverContext, queryRequest, queryResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    queryResultHandler.handleError(nfe);
                }
            }

            public void handleRead(ServerContext serverContext, ReadRequest readRequest,
                    ResultHandler<Resource> resourceResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(readRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, readRequest.getResourceName());
                    rootRealm.handleRead(serverContext, readRequest, resourceResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    resourceResultHandler.handleError(nfe);
                }
            }

            public void handleUpdate(ServerContext serverContext, UpdateRequest updateRequest,
                    ResultHandler<Resource> resourceResultHandler) {
                try {
                    Map parsedDetails = getRequestDetails(updateRequest.getResourceName());
                    final RequestHandler rootRealm = realm(parsedDetails, updateRequest.getResourceName());
                    rootRealm.handleUpdate(serverContext, updateRequest, resourceResultHandler);
                } catch (NotFoundException nfe) {
                    // URL not valid request
                    resourceResultHandler.handleError(nfe);
                }
            }
        });
        return factory;
    }

    /**
     * @return Instance of a connection
     * @throws ServletException if a connection instance cannot be retrieved
     */
    public static ConnectionFactory getConnectionFactory() throws ServletException {
        try {
            return getInstance().buildConnectionFactory();
        } catch (final Exception e) {
            throw new ServletException(e);
        }
    }

    /**
     * Returns a request handler which will handle requests to a sub-realm.
     *
     * @param parentPath    The parent realm.
     * @param parsedDetails Map of realmPath, resourceName, and resourceID
     * @return A request handler which will handle requests to a sub-realm.
     */
    private static RequestHandler subrealms(final Map parsedDetails, final String parentPath) {
        return new RequestHandler() {

            public void handleAction(final ServerContext context, final ActionRequest request,
                    final ResultHandler<JsonValue> handler) {
                subrealm(parentPath, context).handleAction(context, request, handler);
            }

            public void handleCreate(final ServerContext context, final CreateRequest request,
                    final ResultHandler<Resource> handler) {
                subrealm(parentPath, context).handleCreate(context, request, handler);
            }

            public void handleDelete(final ServerContext context, final DeleteRequest request,
                    final ResultHandler<Resource> handler) {
                subrealm(parentPath, context).handleDelete(context, request, handler);
            }

            public void handlePatch(final ServerContext context, final PatchRequest request,
                    final ResultHandler<Resource> handler) {
                subrealm(parentPath, context).handlePatch(context, request, handler);
            }

            public void handleQuery(final ServerContext context, final QueryRequest request,
                    final QueryResultHandler handler) {
                subrealm(parentPath, context).handleQuery(context, request, handler);
            }

            public void handleRead(final ServerContext context, final ReadRequest request,
                    final ResultHandler<Resource> handler) {
                subrealm(parentPath, context).handleRead(context, request, handler);
            }

            public void handleUpdate(final ServerContext context, final UpdateRequest request,
                    final ResultHandler<Resource> handler) {
                subrealm(parentPath, context).handleUpdate(context, request, handler);
            }

            private RequestHandler subrealm(final String parentPath, final ServerContext context) {
                return realm(parsedDetails, parentPath);
            }
        };
    }

    /**
     * Create an amAdmin SSOToken
     *
     * @return SSOToken adminSSOtoken
     */
    private SSOToken getSSOToken() {
        return (SSOToken) AccessController.doPrivileged(AdminTokenAction.getInstance());
    }

    /*
     * Checks endpoint to make sure it has been reserved.
     * @param token endpoint that needs verification
     * @boolean true is the endpoint is valid, false if the endpoint has not been defined
     */
    private boolean checkValidEndpoint(String token) {
        Set<String> endPoints = getEndpointList();
        if (endPoints.contains(token)) {
            return true;
        }
        return false;
    }

    /**
     * Parse Realm Path, Resource Name, and Resource ID
     *
     * @return Map containing realmPath, resourceName, and resourceID
     * @throws NotFoundException when configuration manager cannot retrieve a realm
     */
    public Map<String, String> getRequestDetails(String resourceName) throws NotFoundException {
        Map<String, String> details = new HashMap<String, String>(3);
        if (StringUtils.isBlank(resourceName)) {
            return null;
        }
        StringTokenizer tokenizer = new StringTokenizer(resourceName.trim(), "/", false);
        boolean topLevel = true;
        String lastNonBlank = null;
        String lastNonBlankID = null;
        String tmp = null;
        StringBuilder realmPath = new StringBuilder("/"); // fqdn path to resource
        StringBuilder resourceID = null; // resource id
        StringBuilder endpoint = null; // defined endpoint

        OrganizationConfigManager ocm = null;

        try {
            ocm = new OrganizationConfigManager(getSSOToken(), realmPath.toString());
        } catch (SMSException smse) {
            throw new NotFoundException(smse.getMessage(), smse);
        }
        while (tokenizer.hasMoreElements()) {
            String next = tokenizer.nextToken();
            if (StringUtils.isNotBlank(next)) {
                if (null != lastNonBlank) {
                    try { // test to see if its a realm
                        if (realmPath.toString().equalsIgnoreCase("/") && topLevel) {
                            ocm = new OrganizationConfigManager(getSSOToken(), realmPath.toString() + lastNonBlank);
                            realmPath.append(lastNonBlank);
                            topLevel = false;
                        } else {
                            ocm = new OrganizationConfigManager(getSSOToken(),
                                    realmPath.toString() + "/" + lastNonBlank);
                            realmPath.append("/").append(lastNonBlank);
                        }
                        ocm = new OrganizationConfigManager(getSSOToken(), realmPath.toString());
                    } catch (SMSException smse) {
                        // cannot retrieve realm, must be endpoint
                        debug.warning(next + "is the endpoint because it is not a realm");
                        endpoint = new StringBuilder("/");
                        endpoint.append(lastNonBlank);
                        if (!checkValidEndpoint(endpoint.toString())) {
                            debug.warning(endpoint.toString() + "is the endpoint because it is not a realm");
                            throw new NotFoundException(
                                    "Endpoint " + endpoint.toString() + " is not a defined endpoint.");
                        }
                        // add the rest of tokens as resource name
                        lastNonBlankID = next;
                        while (tokenizer.hasMoreElements()) {
                            next = tokenizer.nextToken();
                            if (StringUtils.isNotBlank(next)) {
                                if (null != lastNonBlankID) {
                                    if (null == resourceID) {
                                        resourceID = new StringBuilder(lastNonBlankID);
                                    } else {
                                        resourceID.append("/").append(lastNonBlankID);
                                    }
                                }
                                lastNonBlankID = next;
                            }
                        }

                    }
                }
                lastNonBlank = next;
            }
        }

        details.put("realmPath", realmPath.toString());

        if (null != endpoint && !endpoint.toString().isEmpty()) {
            details.put("resourceName", endpoint.toString());
        } else {
            endpoint = new StringBuilder("/");
            details.put("resourceName", endpoint.append(lastNonBlank).toString());
        }
        if (null != resourceID) {
            details.put("resourceId", resourceID.append("/").append(lastNonBlankID).toString());
        } else if (null != lastNonBlank) {
            details.put("resourceId", lastNonBlankID);
        } else {
            throw new NotFoundException("Resource ID has not been provided.");
        }

        return details;
    }
}