com.janrain.backplane.provision.ProvisioningController2.java Source code

Java tutorial

Introduction

Here is the source code for com.janrain.backplane.provision.ProvisioningController2.java

Source

/*
 * Copyright 2012 Janrain, Inc.
 *
 * 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 com.janrain.backplane.provision;

import com.janrain.backplane.common.AuthException;
import com.janrain.backplane.common.BackplaneServerException;
import com.janrain.backplane.common.model.Message;
import com.janrain.backplane.common.model.MessageField;
import com.janrain.backplane.config.BackplaneConfig;
import com.janrain.backplane.config.dao.ConfigDAOs;
import com.janrain.backplane.dao.DaoAll;
import com.janrain.backplane.dao.DaoException;
import com.janrain.backplane.server2.dao.BP2DAOs;
import com.janrain.backplane.server2.model.Backplane2MessageFields;
import com.janrain.backplane.server2.model.BusConfig2;
import com.janrain.backplane.server2.model.BusConfig2Fields;
import com.janrain.backplane.server2.oauth2.model.*;
import com.janrain.backplane2.server.*;
import com.janrain.commons.supersimpledb.SimpleDBException;
import com.janrain.oauth2.TokenException;
import com.janrain.util.ServletUtil;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import scala.Option;
import scala.collection.JavaConversions;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * Controller handling the API calls for backplane customer configuration provisioning.
 *
 * @author Johnny Bufu
 */
@Controller
@RequestMapping(value = "/v2/provision/*")
@SuppressWarnings({ "UnusedDeclaration" })
public class ProvisioningController2 {

    // - PUBLIC

    @RequestMapping(value = "/bus/list", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Map<String, String>> busList(HttpServletRequest request,
            @RequestBody ListRequest listRequest) throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(listRequest.getAdmin(), listRequest.getSecret());
        return doList(BusConfig2.class, listRequest.getEntities(), BusConfig2Fields.BUS_NAME());
    }

    @RequestMapping(value = "/user/list", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Map<String, String>> userList(HttpServletRequest request,
            @RequestBody ListRequest listRequest) throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(listRequest.getAdmin(), listRequest.getSecret());
        return doList(BusOwner.class, listRequest.getEntities(), BusOwnerFields.USER());
    }

    @RequestMapping(value = "/client/list", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Map<String, String>> clientList(HttpServletRequest request,
            @RequestBody ListRequest listRequest) throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(listRequest.getAdmin(), listRequest.getSecret());
        return doList(Client.class, listRequest.getEntities(), ClientFields.USER());
    }

    @RequestMapping(value = "/bus/delete", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> busDelete(HttpServletRequest request, @RequestBody ListRequest deleteRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(deleteRequest.getAdmin(), deleteRequest.getSecret());
        return doDelete(BusConfig2.class, deleteRequest.getEntities());
    }

    @RequestMapping(value = "/user/delete", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> userDelete(HttpServletRequest request, @RequestBody ListRequest deleteRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(deleteRequest.getAdmin(), deleteRequest.getSecret());
        return doDelete(BusOwner.class, deleteRequest.getEntities());
    }

    @RequestMapping(value = "/client/delete", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> clientDelete(HttpServletRequest request, @RequestBody ListRequest deleteRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(deleteRequest.getAdmin(), deleteRequest.getSecret());
        return doDelete(Client.class, deleteRequest.getEntities());
    }

    @RequestMapping(value = "/bus/update", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> busUpdate(HttpServletRequest request, @RequestBody UpdateRequest updateRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(updateRequest.getAdmin(), updateRequest.getSecret());
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (Map<String, String> config : updateRequest.getConfigs()) {
            String updateStatus = BACKPLANE_UPDATE_SUCCESS;
            try {
                BusConfig2 busConfig = new BusConfig2(config);
                Option<BusOwner> ownerOption = BP2DAOs.busOwnerDao()
                        .get((String) config.get(BusConfig2Fields.OWNER().name().toUpperCase()));
                BusOwner owner = ownerOption.isDefined() ? ownerOption.get() : null;
                if (owner == null) {
                    throw new BackplaneServerException(
                            "Invalid bus owner: " + config.get(BusConfig2Fields.OWNER().name().toUpperCase()));
                }
                BP2DAOs.busDao().store(busConfig);
            } catch (Exception e) {
                updateStatus = e.getMessage();
            }
            String requestEntryId = config.get(BusConfig2Fields.BUS_NAME().name().toUpperCase());
            result.put(requestEntryId != null ? requestEntryId : "<unknown>", updateStatus);
        }
        return result;
    }

    @RequestMapping(value = "/user/update", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> userUpdate(HttpServletRequest request, @RequestBody UpdateRequest updateRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(updateRequest.getAdmin(), updateRequest.getSecret());
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (Map<String, String> config : updateRequest.getConfigs()) {
            String updateStatus = BACKPLANE_UPDATE_SUCCESS;
            try {
                BP2DAOs.busOwnerDao().store(new BusOwner(config));
            } catch (Exception e) {
                updateStatus = e.getMessage();
            }
            String requestEntryId = config.get(BusOwnerFields.USER().name().toUpperCase());
            result.put(requestEntryId != null ? requestEntryId : "<unknown>", updateStatus);
        }
        return result;
    }

    @RequestMapping(value = "/client/update", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> clientUpdate(HttpServletRequest request, @RequestBody UpdateRequest updateRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        logger.debug("client updateRequest: '" + updateRequest + "'");
        ConfigDAOs.adminDao().getAuthenticated(updateRequest.getAdmin(), updateRequest.getSecret());
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (Map<String, String> config : updateRequest.getConfigs()) {
            String updateStatus = BACKPLANE_UPDATE_SUCCESS;
            try {
                BP2DAOs.clientDao().store(new Client(config));
            } catch (Exception e) {
                updateStatus = e.getMessage();
            }
            String requestEntryId = config.get(ClientFields.USER().name().toUpperCase());
            result.put(requestEntryId != null ? requestEntryId : "<unknown>", updateStatus);
        }
        return result;
    }

    @RequestMapping(value = "/grant/list", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Map<String, String>> grantList(HttpServletRequest request,
            @RequestBody ListRequest listRequest) throws AuthException, DaoException {
        ServletUtil.checkSecure(request);
        ConfigDAOs.adminDao().getAuthenticated(listRequest.getAdmin(), listRequest.getSecret());

        Map<String, Map<String, String>> result = new LinkedHashMap<String, Map<String, String>>();

        for (String clientId : listRequest.getEntities()) {
            try {
                Map<String, String> grantsList = new HashMap<String, String>();
                Map<Scope, Set<Grant2>> scopeGrants = GrantLogic.retrieveClientGrants(clientId, null);
                for (Set<Grant2> grantSets : scopeGrants.values()) {
                    for (Grant2 grant : grantSets) {
                        grantsList.put(grant.id(), grant.getAuthorizedScope().toString());
                    }
                    result.put(clientId, grantsList);
                }
            } catch (final BackplaneServerException e) {
                logger.error("Error looking up grants for client " + clientId, e);
                result.put(clientId, new HashMap<String, String>() {
                    {
                        put("error", e.getMessage());
                    }
                });
            } catch (final TokenException te) {
                logger.error("token (unexpected scope processing?) error: " + te.getMessage(), te);
                result.put(clientId, new HashMap<String, String>() {
                    {
                        put("error", te.getMessage());
                    }
                });
            }
        }
        return result;
    }

    @RequestMapping(value = "/grant/add", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> grantAdd(HttpServletRequest request, @RequestBody GrantRequest grantRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        logger.debug("grant add request: '" + grantRequest + "'");
        return doGrant(grantRequest, true);
    }

    @RequestMapping(value = "/grant/revoke", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> gantRevoke(HttpServletRequest request, @RequestBody GrantRequest grantRequest)
            throws AuthException {
        ServletUtil.checkSecure(request);
        logger.debug("grant revoke request: '" + grantRequest + "'");
        return doGrant(grantRequest, false);
    }

    /**
     * Handle auth errors as part of normal application flow
     */
    @ExceptionHandler
    @ResponseBody
    public Map<String, String> handle(final AuthException e, HttpServletResponse response) {
        logger.info("Provisioning authentication error: " + e.getMessage());
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        return new HashMap<String, String>() {
            {
                put(ERR_MSG_FIELD, e.getMessage());
            }
        };
    }

    /**
     * Handle all other errors
     */
    @ExceptionHandler
    @ResponseBody
    public Map<String, String> handle(final Exception e, HttpServletResponse response) {
        logger.error("Error handling provisioning request", bpConfig.getDebugException(e));
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        return new HashMap<String, String>() {
            {
                put(ERR_MSG_FIELD, e.getMessage());
            }
        };
    }

    // - PRIVATE

    private static final Logger logger = Logger.getLogger(ProvisioningController2.class);

    private static final String BACKPLANE_UPDATE_SUCCESS = "BACKPLANE_UPDATE_SUCCESS";
    private static final String BACKPLANE_DELETE_SUCCESS = "BACKPLANE_DELETE_SUCCESS";
    private static final String GRANT_UPDATE_SUCCESS = "GRANT_UPDATE_SUCCESS";
    private static final String BACKPLANE_ENTRY_NOT_FOUND = "BACKPLANE_ENTRY_NOT_FOUND";
    private static final String ERR_MSG_FIELD = "ERR_MSG";
    private static final String CONFIG_NOT_FOUND = "CONFIG_NOT_FOUND";

    @Inject
    private BackplaneConfig bpConfig;

    private <T extends Message, F extends MessageField> Map<String, Map<String, String>> doList(Class<T> entityType,
            List<String> entityNames, F orderField) {

        if (entityNames.size() == 0)
            return doListAll(entityType);

        final Map<String, Map<String, String>> result = new LinkedHashMap<String, Map<String, String>>();
        for (String entityName : entityNames) {
            T config = null;
            Exception thrown = null;
            try {
                config = (T) getDaoByObjectType(entityType).get(entityName).getOrElse(null);
            } catch (Exception e) {
                thrown = e;
            }
            final String errMgs = thrown != null ? thrown.getMessage() : config == null ? CONFIG_NOT_FOUND : null;

            result.put(entityName, errMgs != null ? new HashMap<String, String>() {
                {
                    put(ERR_MSG_FIELD, errMgs);
                }
            } : JavaConversions.mapAsJavaMap(config));
        }
        return result;
    }

    private <T extends Message> Map<String, Map<String, String>> doListAll(Class<T> entityType) {
        Map<String, Map<String, String>> result = new LinkedHashMap<String, Map<String, String>>();
        try {
            List items = JavaConversions.seqAsJavaList(getDaoByObjectType(entityType).getAll());
            for (Object config : items) {
                result.put(((T) config).id(), JavaConversions.mapAsJavaMap((T) config));
            }
        } catch (final Exception e) {
            result.put(ERR_MSG_FIELD, new HashMap<String, String>() {
                {
                    put(ERR_MSG_FIELD, e.getMessage());
                }
            });
        }
        return result;
    }

    private <T extends Message> Map<String, String> doDelete(Class<T> entityType, List<String> entityNames) {
        Map<String, String> result = new LinkedHashMap<String, String>();
        for (String entityName : entityNames) {
            String deleteStatus = BACKPLANE_DELETE_SUCCESS;
            try {
                if (!getDaoByObjectType(entityType).get(entityName).isDefined()) {
                    deleteStatus = BACKPLANE_ENTRY_NOT_FOUND;
                } else {
                    getDaoByObjectType(entityType).delete(entityName);
                }
            } catch (Exception e) {
                deleteStatus = e.getMessage();
            }
            result.put(entityName, deleteStatus);
        }
        return result;
    }

    private Map<String, String> doGrant(GrantRequest grantRequest, boolean addRevoke) throws AuthException {
        Map<String, String> result = new LinkedHashMap<String, String>();
        ConfigDAOs.adminDao().getAuthenticated(grantRequest.getAdmin(), grantRequest.getSecret());

        for (Map.Entry<String, String> newGrantEntry : grantRequest.getGrants().entrySet()) {
            String clientId = newGrantEntry.getKey();
            String buses = newGrantEntry.getValue();
            try {
                if (!BP2DAOs.clientDao().get(clientId).isDefined()) {
                    result.put(clientId, "invalid client_id");
                } else {
                    List<String> busesAsList = Scope.getScopesAsList(buses);
                    validateBuses(busesAsList);
                    if (addRevoke) {
                        addGrant(grantRequest.getAdmin(), clientId, busesAsList);
                        result.put(clientId, "GRANT_UPDATE_SUCCESS");
                    } else {
                        revokeBuses(clientId, busesAsList);
                        result.put(clientId, "GRANT_UPDATE_SUCCESS");
                    }
                }
            } catch (Exception e) {
                result.put(clientId, e.getMessage());
            }
        }
        return result;
    }

    private void validateBuses(List<String> buses) throws BackplaneServerException, DaoException {
        for (String bus : buses) {
            if (!BP2DAOs.busDao().get(bus).isDefined()) {
                throw new BackplaneServerException("Invalid bus: " + bus);
            }
        }
    }

    private void addGrant(String issuer, String clientId, List<String> buses)
            throws SimpleDBException, BackplaneServerException, DaoException {
        Grant2 grant = new GrantBuilder(GrantType.CLIENT_CREDENTIALS, GrantState.ACTIVE, issuer, clientId,
                Scope.getEncodedScopesAsString(Backplane2MessageFields.BUS(), buses)).buildGrant();
        BP2DAOs.grantDao().store(grant);
    }

    private void revokeBuses(String clientId, List<String> buses)
            throws TokenException, SimpleDBException, BackplaneServerException {
        boolean updated = false;
        Scope busesToRevoke = new Scope(Scope.getEncodedScopesAsString(Backplane2MessageFields.BUS(), buses));
        if (!BP2DAOs.grantDao().revokeBuses(BP2DAOs.grantDao().getByClientId(clientId),
                JavaConversions.collectionAsScalaIterable(buses).toList())) {
            throw new BackplaneServerException("No grants found to revoke for buses: " + buses);
        }
    }

    private static DaoAll getDaoByObjectType(Class<?> obj) {
        if (BusConfig2.class.isAssignableFrom(obj)) {
            return BP2DAOs.busDao();
        } else if (BusOwner.class.isAssignableFrom(obj)) {
            return BP2DAOs.busOwnerDao();
        } else if (Client.class.isAssignableFrom(obj)) {
            return BP2DAOs.clientDao();
        } else if (Grant.class.isAssignableFrom(obj)) {
            return BP2DAOs.grantDao();
        }

        return null;
    }
}