/** * 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 java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apereo.portal.EntityIdentifier; import org.apereo.portal.portlet.om.IPortletDefinition; import org.apereo.portal.portlet.om.IPortletWindow; import org.apereo.portal.portlet.om.PortletCategory; import org.apereo.portal.portlet.registry.IPortletCategoryRegistry; import org.apereo.portal.portlet.registry.IPortletDefinitionRegistry; import org.apereo.portal.portlet.registry.IPortletWindowRegistry; import org.apereo.portal.portlet.rendering.IPortletExecutionManager; import org.apereo.portal.rest.layout.LayoutPortlet; import org.apereo.portal.security.IAuthorizationPrincipal; import org.apereo.portal.security.IPerson; import org.apereo.portal.security.IPersonManager; import org.apereo.portal.services.AuthorizationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; /** * This controller logic is derived from {@link * org.apereo.portal.layout.dlm.remoting.ChannelListController} * * @since 4.1 */ @Controller public class PortletsRESTController { @Autowired private IPortletDefinitionRegistry portletDefinitionRegistry; @Autowired private IPortletCategoryRegistry portletCategoryRegistry; @Autowired private IPersonManager personManager; @Autowired private IPortletWindowRegistry portletWindowRegistry; @Autowired private IPortletExecutionManager portletExecutionManager; private final Logger logger = LoggerFactory.getLogger(getClass()); /** * Provides information about all portlets in the portlet registry. NOTE: The response is * governed by the <code>IPermission.PORTLET_MANAGER_xyz</code> series of permissions. The * actual level of permission required is based on the current lifecycle state of the portlet. */ @RequestMapping(value = "/portlets.json", method = RequestMethod.GET) public ModelAndView getManageablePortlets( HttpServletRequest request, HttpServletResponse response) throws Exception { // get a list of all channels List<IPortletDefinition> allPortlets = portletDefinitionRegistry.getAllPortletDefinitions(); IAuthorizationPrincipal ap = getAuthorizationPrincipal(request); List<PortletTuple> rslt = new ArrayList<PortletTuple>(); for (IPortletDefinition pdef : allPortlets) { if (ap.canManage(pdef.getPortletDefinitionId().getStringId())) { rslt.add(new PortletTuple(pdef)); } } return new ModelAndView("json", "portlets", rslt); } /** * Provides information about a single portlet in the registry. NOTE: Access to this API enpoint * requires only <code>IPermission.PORTAL_SUBSCRIBE</code> permission. */ @RequestMapping(value = "/portlet/{fname}.json", method = RequestMethod.GET) public ModelAndView getPortlet( HttpServletRequest request, HttpServletResponse response, @PathVariable String fname) throws Exception { IAuthorizationPrincipal ap = getAuthorizationPrincipal(request); IPortletDefinition portletDef = portletDefinitionRegistry.getPortletDefinitionByFname(fname); if (portletDef != null && ap.canRender(portletDef.getPortletDefinitionId().getStringId())) { LayoutPortlet portlet = new LayoutPortlet(portletDef); return new ModelAndView("json", "portlet", portlet); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return new ModelAndView("json"); } } /** * Provides a single, fully-rendered portlet. NOTE: Access to this API enpoint requires only * <code>IPermission.PORTAL_SUBSCRIBE</code> permission. */ @RequestMapping(value = "/v4-3/portlet/{fname}.html", method = RequestMethod.GET) public @ResponseBody String getRenderedPortlet( HttpServletRequest req, HttpServletResponse res, @PathVariable String fname) throws Exception { // Does the portlet exist in the registry? final IPortletDefinition portletDef = portletDefinitionRegistry.getPortletDefinitionByFname(fname); if (portletDef == null) { res.setStatus(HttpServletResponse.SC_NOT_FOUND); return "Portlet not found"; } // Is the user permitted to access it? final IAuthorizationPrincipal ap = getAuthorizationPrincipal(req); if (!ap.canRender(portletDef.getPortletDefinitionId().getStringId())) { res.setStatus(HttpServletResponse.SC_FORBIDDEN); return "Access denied"; } // Proceed... try { final IPortletWindow portletWindow = portletWindowRegistry.getOrCreateDefaultPortletWindow( req, portletDef.getPortletDefinitionId()); final String rslt = portletExecutionManager.getPortletOutput( portletWindow.getPortletWindowId(), req, res); return rslt; } catch (Exception e) { logger.error("Failed to render the requested portlet '{}'", fname, e); res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return "Internal error"; } } /* * Implementation */ private IAuthorizationPrincipal getAuthorizationPrincipal(HttpServletRequest req) { IPerson user = personManager.getPerson(req); EntityIdentifier ei = user.getEntityIdentifier(); IAuthorizationPrincipal rslt = AuthorizationService.instance().newPrincipal(ei.getKey(), ei.getType()); return rslt; } private Set<String> getPortletCategories(IPortletDefinition pdef) { Set<PortletCategory> categories = portletCategoryRegistry.getParentCategories(pdef); Set<String> rslt = new HashSet<String>(); for (PortletCategory category : categories) { rslt.add(StringUtils.capitalize(category.getName().toLowerCase())); } return rslt; } /* * Nested Types */ @SuppressWarnings("unused") private /* non-static */ final class PortletTuple implements Serializable { private static final long serialVersionUID = 1L; private final String id; private final String name; private final String fname; private final String description; private final String type; private final String lifecycleState; private final Set<String> categories; public PortletTuple(IPortletDefinition pdef) { this.id = pdef.getPortletDefinitionId().getStringId(); this.name = pdef.getName(); this.fname = pdef.getFName(); this.description = pdef.getDescription(); this.type = pdef.getType().getName(); this.lifecycleState = pdef.getLifecycleState().toString(); this.categories = getPortletCategories(pdef); } public String getId() { return id; } public String getName() { return name; } public String getFname() { return fname; } public String getDescription() { return description; } public String getType() { return type; } public String getLifecycleState() { return lifecycleState; } public Set<String> getCategories() { return categories; } } }