/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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 org.apereo.portal.rest; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apereo.portal.EntityIdentifier; import org.apereo.portal.groups.IEntityGroup; import org.apereo.portal.groups.IGroupConstants; import org.apereo.portal.groups.pags.dao.IPersonAttributesGroupDefinition; import org.apereo.portal.groups.pags.dao.IPersonAttributesGroupTestGroupDefinition; import org.apereo.portal.groups.pags.dao.PagsService; import org.apereo.portal.groups.pags.dao.jpa.PersonAttributesGroupDefinitionImpl; import org.apereo.portal.security.IPerson; import org.apereo.portal.security.IPersonManager; import org.apereo.portal.security.RuntimeAuthorizationException; import org.apereo.portal.services.GroupService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * REST Controller that leverages PagsAdministrationHelper and related form classes to provide a * RESTful API for AJAX management of PAGS. * */ @Controller public class PagsRESTController { /* Tried to use ResponseEntities but the ran into conflicts when using <mvc:annotation-driven/> */ /** Help other Java classes use this API well. */ public static final String URL_FORMAT_STRING = "/api/v4-3/pags/%s.json"; @Autowired private PagsService pagsService; @Autowired private IPersonManager personManager; @Autowired private ObjectMapper objectMapper; @SuppressWarnings("unused") private final Logger logger = LoggerFactory.getLogger(getClass()); @RequestMapping( value = "/v4-3/pags/{pagsGroupName}.json", produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET ) public @ResponseBody String findPagsGroup( HttpServletRequest request, HttpServletResponse res, @PathVariable("pagsGroupName") String pagsGroupName) { res.setContentType(MediaType.APPLICATION_JSON_VALUE); /* * This step is necessary; the incoming URLs will sometimes have '+' * characters for spaces, and the @PathVariable magic doesn't convert them. */ String name; try { name = URLDecoder.decode(pagsGroupName, "UTF-8"); } catch (UnsupportedEncodingException e) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.toString() + "' }"; } IPerson person = personManager.getPerson(request); IPersonAttributesGroupDefinition pagsGroup = this.pagsService.getPagsDefinitionByName(person, name); return respondPagsGroupJson(res, pagsGroup, person, HttpServletResponse.SC_FOUND); } @RequestMapping( value = "/v4-3/pags/{parentGroupName}.json", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST ) public @ResponseBody String createPagsGroup( HttpServletRequest request, HttpServletResponse res, @PathVariable("parentGroupName") String parentGroupName, @RequestBody String json) { res.setContentType(MediaType.APPLICATION_JSON_VALUE); /* * This step is necessary; the incoming URLs will sometimes have '+' * characters for spaces, and the @PathVariable magic doesn't convert them. */ String name; try { name = URLDecoder.decode(parentGroupName, "UTF-8"); } catch (UnsupportedEncodingException e) { res.setStatus(HttpServletResponse.SC_BAD_REQUEST); return "{ 'error': '" + e.getMessage() + "' }"; } IPersonAttributesGroupDefinition inpt; try { inpt = objectMapper.readValue(json, PersonAttributesGroupDefinitionImpl.class); } catch (Exception e) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.getMessage() + "' }"; // should be escaped } // Obtain a real reference to the parent group EntityIdentifier[] eids = GroupService.searchForGroups(name, IGroupConstants.IS, IPerson.class); if (eids.length == 0) { res.setStatus(HttpServletResponse.SC_BAD_REQUEST); return "{ 'error': 'Parent group does not exist: " + name + "' }"; } IEntityGroup parentGroup = (IEntityGroup) GroupService.getGroupMember(eids[0]); // Names must be unique IPerson person = personManager.getPerson(request); IPersonAttributesGroupDefinition rslt; try { // A little weird that we need to do both; // need some PAGS DAO/Service refactoring rslt = pagsService.createPagsDefinition( person, parentGroup, inpt.getName(), inpt.getDescription()); // NOTE: We are also obligated to establish the backlink // testGroupDef --> groupDef; arguably this backlink serves // little purpose and could be removed. for (IPersonAttributesGroupTestGroupDefinition testGroupDef : inpt.getTestGroups()) { // NOTE: The deserializer handles testDef --> testGroupDef testGroupDef.setGroup(rslt); } rslt.setTestGroups(inpt.getTestGroups()); rslt.setMembers(inpt.getMembers()); pagsService.updatePagsDefinition(person, rslt); } catch (RuntimeAuthorizationException rae) { res.setStatus(HttpServletResponse.SC_FORBIDDEN); return "{ 'error': 'not authorized' }"; } catch (IllegalArgumentException iae) { res.setStatus(HttpServletResponse.SC_CONFLICT); return "{ 'error': '" + iae.getMessage() + "' }"; } catch (Exception e) { e.printStackTrace(); res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.getMessage() + "' }"; } return respondPagsGroupJson(res, rslt, person, HttpServletResponse.SC_CREATED); } @RequestMapping( value = "/v4-3/pags/{pagsGroupName}.json", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT ) public @ResponseBody String updatePagsGroup( HttpServletRequest req, HttpServletResponse res, @PathVariable("pagsGroupName") String pagsGroupName, @RequestBody String json) { res.setContentType(MediaType.APPLICATION_JSON_VALUE); /* * This step is necessary; the incoming URLs will sometimes have '+' * characters for spaces, and the @PathVariable magic doesn't convert them. */ String name; try { name = URLDecoder.decode(pagsGroupName, "UTF-8"); } catch (UnsupportedEncodingException e) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.toString() + "' }"; } IPersonAttributesGroupDefinition inpt; try { inpt = objectMapper.readValue(json, PersonAttributesGroupDefinitionImpl.class); } catch (Exception e) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.toString() + "' }"; // should be escaped } if (inpt == null) { res.setStatus(HttpServletResponse.SC_NOT_FOUND); return "{ 'error': 'Not found' }"; } if (!name.equals(inpt.getName())) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': 'Group name in URL parameter must match name in JSON payload' }"; } IPerson person = personManager.getPerson(req); IPersonAttributesGroupDefinition rslt; try { IPersonAttributesGroupDefinition currentDef = pagsService.getPagsDefinitionByName(person, name); if (currentDef == null) { res.setStatus(HttpServletResponse.SC_NOT_FOUND); return "{ 'error': 'Not found' }"; } /* * Copy over the information being passed in to the JPA-managed * instance; the following do not support updates (currently): * - Name * - Members */ currentDef.setDescription(inpt.getDescription()); // NOTE: We are also obligated to establish the backlink // testGroupDef --> groupDef; arguably this backlink serves // little purpose and could be removed. for (IPersonAttributesGroupTestGroupDefinition testGroupDef : inpt.getTestGroups()) { // NOTE: The deserializer handles testDef --> testGroupDef testGroupDef.setGroup(currentDef); } currentDef.setTestGroups(inpt.getTestGroups()); rslt = pagsService.updatePagsDefinition(person, currentDef); } catch (IllegalArgumentException iae) { res.setStatus(HttpServletResponse.SC_NOT_FOUND); return "{ 'error': '" + iae.getMessage() + "' }"; // should be escaped } catch (RuntimeAuthorizationException rae) { res.setStatus(HttpServletResponse.SC_FORBIDDEN); return "{ 'error': 'not authorized' }"; } catch (Exception e) { res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.toString() + "' }"; } return respondPagsGroupJson(res, rslt, person, HttpServletResponse.SC_ACCEPTED); } /* * Implementation */ private String respondPagsGroupJson( HttpServletResponse response, IPersonAttributesGroupDefinition pagsGroup, IPerson person, int status) { if (pagsGroup == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return "{ 'error': 'Not Found' }"; } try { response.setStatus(status); return objectMapper.writeValueAsString(pagsGroup); } catch (Exception e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "{ 'error': '" + e.toString() + "' }"; } } }