Java tutorial
/* * 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; } }