diff --git a/uPortal-api/uPortal-api-rest/src/main/java/org/apereo/portal/rest/PortletsRESTController.java b/uPortal-api/uPortal-api-rest/src/main/java/org/apereo/portal/rest/PortletsRESTController.java
index 871080582d1..a79c259d4ae 100644
--- a/uPortal-api/uPortal-api-rest/src/main/java/org/apereo/portal/rest/PortletsRESTController.java
+++ b/uPortal-api/uPortal-api-rest/src/main/java/org/apereo/portal/rest/PortletsRESTController.java
@@ -16,6 +16,7 @@
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -23,6 +24,9 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apereo.portal.EntityIdentifier;
+import org.apereo.portal.UserPreferencesManager;
+import org.apereo.portal.layout.IUserLayout;
+import org.apereo.portal.layout.IUserLayoutManager;
import org.apereo.portal.layout.LayoutPortlet;
import org.apereo.portal.portlet.om.IPortletDefinition;
import org.apereo.portal.portlet.om.IPortletWindow;
@@ -31,18 +35,20 @@
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.portlets.favorites.FavoritesUtils;
import org.apereo.portal.security.IAuthorizationPrincipal;
+import org.apereo.portal.security.IAuthorizationService;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.security.IPersonManager;
-import org.apereo.portal.services.AuthorizationServiceFacade;
import org.apereo.portal.url.PortalHttpServletFactoryService;
+import org.apereo.portal.user.IUserInstance;
+import org.apereo.portal.user.IUserInstanceManager;
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.GetMapping;
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;
@@ -55,6 +61,11 @@
@Controller
public class PortletsRESTController {
+ public static final String FAVORITE_FLAG = "favorite";
+ public static final String REQUIRED_PERMISSION_TYPE = "requiredPermissionType";
+
+ @Autowired private IAuthorizationService authorizationService;
+
@Autowired private IPortletDefinitionRegistry portletDefinitionRegistry;
@Autowired private IPortletCategoryRegistry portletCategoryRegistry;
@@ -67,35 +78,91 @@ public class PortletsRESTController {
@Autowired private IPortletExecutionManager portletExecutionManager;
+ @Autowired private IUserInstanceManager userInstanceManager;
+
+ @Autowired private FavoritesUtils favoritesUtils;
+
private final Logger logger = LoggerFactory.getLogger(getClass());
+ public enum PortletPermissionType {
+ BROWSE,
+ CONFIGURE,
+ MANAGE,
+ RENDER,
+ SUBSCRIBE
+ }
+
/**
* Provides information about all portlets in the portlet registry. NOTE: The response is
* governed by the IPermission.PORTLET_MANAGER_xyz
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 {
+ @GetMapping(value = "/portlets.json")
+ public ModelAndView getPortlets(HttpServletRequest request) {
+ final boolean limitByFavoriteFlag = request.getParameter(FAVORITE_FLAG) != null;
+ final boolean favoriteValueToMatch =
+ Boolean.parseBoolean(request.getParameter(FAVORITE_FLAG));
+ final String requiredPermissionTypeParameter =
+ request.getParameter(REQUIRED_PERMISSION_TYPE);
+ final PortletPermissionType requiredPermissionType =
+ (requiredPermissionTypeParameter == null)
+ ? PortletPermissionType.MANAGE
+ : PortletPermissionType.valueOf(
+ requiredPermissionTypeParameter.toUpperCase());
+
+ final Set favorites =
+ limitByFavoriteFlag ? getFavorites(request) : Collections.emptySet();
+
// get a list of all channels
List allPortlets = portletDefinitionRegistry.getAllPortletDefinitions();
IAuthorizationPrincipal ap = getAuthorizationPrincipal(request);
- List rslt = new ArrayList();
- for (IPortletDefinition pdef : allPortlets) {
- if (ap.canManage(pdef.getPortletDefinitionId().getStringId())) {
- rslt.add(new PortletTuple(pdef));
+ List results = new ArrayList<>();
+ for (IPortletDefinition portletDefinition : allPortlets) {
+ if (!limitByFavoriteFlag
+ || favorites.contains(portletDefinition) == favoriteValueToMatch) {
+ boolean hasPermission =
+ this.doesUserHavePermissionToViewPortlet(
+ ap, portletDefinition, requiredPermissionType);
+ if (hasPermission) {
+ results.add(new PortletTuple(portletDefinition));
+ }
}
}
- return new ModelAndView("json", "portlets", rslt);
+ return new ModelAndView("json", "portlets", results);
+ }
+
+ private boolean doesUserHavePermissionToViewPortlet(
+ IAuthorizationPrincipal ap,
+ IPortletDefinition portletDefinition,
+ PortletPermissionType requiredPermissionType) {
+ switch (requiredPermissionType) {
+ case BROWSE:
+ return this.authorizationService.canPrincipalBrowse(ap, portletDefinition);
+ case CONFIGURE:
+ return this.authorizationService.canPrincipalConfigure(
+ ap, portletDefinition.getPortletDefinitionId().getStringId());
+ case MANAGE:
+ return this.authorizationService.canPrincipalManage(
+ ap, portletDefinition.getPortletDefinitionId().getStringId());
+ case RENDER:
+ return this.authorizationService.canPrincipalRender(
+ ap, portletDefinition.getPortletDefinitionId().getStringId());
+ case SUBSCRIBE:
+ return this.authorizationService.canPrincipalSubscribe(
+ ap, portletDefinition.getPortletDefinitionId().getStringId());
+ default:
+ throw new IllegalArgumentException(
+ "Unknown requiredPermissionType: " + requiredPermissionType);
+ }
}
/**
- * Provides information about a single portlet in the registry. NOTE: Access to this API enpoint
- * requires only IPermission.PORTAL_SUBSCRIBE
permission.
+ * Provides information about a single portlet in the registry. NOTE: Access to this API
+ * endpoint requires only IPermission.PORTAL_SUBSCRIBE
permission.
*/
- @RequestMapping(value = "/portlet/{fname}.json", method = RequestMethod.GET)
+ @GetMapping(value = "/portlet/{fname}.json")
public ModelAndView getPortlet(
HttpServletRequest request, HttpServletResponse response, @PathVariable String fname)
throws Exception {
@@ -112,10 +179,10 @@ public ModelAndView getPortlet(
}
/**
- * Provides a single, fully-rendered portlet. NOTE: Access to this API enpoint requires only
+ * Provides a single, fully-rendered portlet. NOTE: Access to this API endpoint requires only
* IPermission.PORTAL_SUBSCRIBE
permission.
*/
- @RequestMapping(value = "/v4-3/portlet/{fname}.html", method = RequestMethod.GET)
+ @GetMapping(value = "/v4-3/portlet/{fname}.html")
public @ResponseBody String getRenderedPortlet(
HttpServletRequest req, HttpServletResponse res, @PathVariable String fname) {
@@ -163,8 +230,7 @@ public ModelAndView getPortlet(
private IAuthorizationPrincipal getAuthorizationPrincipal(HttpServletRequest req) {
IPerson user = personManager.getPerson(req);
EntityIdentifier ei = user.getEntityIdentifier();
- IAuthorizationPrincipal rslt =
- AuthorizationServiceFacade.instance().newPrincipal(ei.getKey(), ei.getType());
+ IAuthorizationPrincipal rslt = authorizationService.newPrincipal(ei.getKey(), ei.getType());
return rslt;
}
@@ -177,12 +243,20 @@ private Set getPortletCategories(IPortletDefinition pdef) {
return rslt;
}
+ private Set getFavorites(HttpServletRequest request) {
+ final IUserInstance ui = userInstanceManager.getUserInstance(request);
+ final UserPreferencesManager upm = (UserPreferencesManager) ui.getPreferencesManager();
+ final IUserLayoutManager ulm = upm.getUserLayoutManager();
+ final IUserLayout layout = ulm.getUserLayout();
+ return favoritesUtils.getFavoritePortletDefinitions(layout);
+ }
+
/*
* Nested Types
*/
@SuppressWarnings("unused")
- private /* non-static */ final class PortletTuple implements Serializable {
+ protected /* non-static */ final class PortletTuple implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/uPortal-api/uPortal-api-rest/src/test/java/org/apereo/portal/rest/PortletsRESTControllerTest.java b/uPortal-api/uPortal-api-rest/src/test/java/org/apereo/portal/rest/PortletsRESTControllerTest.java
new file mode 100644
index 00000000000..808a18d78a5
--- /dev/null
+++ b/uPortal-api/uPortal-api-rest/src/test/java/org/apereo/portal/rest/PortletsRESTControllerTest.java
@@ -0,0 +1,311 @@
+package org.apereo.portal.rest;
+
+import static org.apereo.portal.rest.PortletsRESTController.PortletPermissionType.BROWSE;
+import static org.apereo.portal.rest.PortletsRESTController.PortletPermissionType.CONFIGURE;
+import static org.apereo.portal.rest.PortletsRESTController.PortletPermissionType.MANAGE;
+import static org.apereo.portal.rest.PortletsRESTController.PortletPermissionType.RENDER;
+import static org.apereo.portal.rest.PortletsRESTController.PortletPermissionType.SUBSCRIBE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.BDDMockito.given;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpServletRequest;
+import org.apereo.portal.EntityIdentifier;
+import org.apereo.portal.UserPreferencesManager;
+import org.apereo.portal.layout.IUserLayout;
+import org.apereo.portal.layout.IUserLayoutManager;
+import org.apereo.portal.portlet.dao.jpa.PortletTypeImpl;
+import org.apereo.portal.portlet.om.IPortletDefinition;
+import org.apereo.portal.portlet.om.IPortletDefinitionId;
+import org.apereo.portal.portlet.om.IPortletType;
+import org.apereo.portal.portlet.om.PortletLifecycleState;
+import org.apereo.portal.portlet.registry.IPortletCategoryRegistry;
+import org.apereo.portal.portlet.registry.IPortletDefinitionRegistry;
+import org.apereo.portal.portlets.favorites.FavoritesUtils;
+import org.apereo.portal.security.IAuthorizationPrincipal;
+import org.apereo.portal.security.IAuthorizationService;
+import org.apereo.portal.security.IPerson;
+import org.apereo.portal.security.IPersonManager;
+import org.apereo.portal.user.IUserInstance;
+import org.apereo.portal.user.IUserInstanceManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.web.servlet.ModelAndView;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PortletsRESTControllerTest {
+
+ @InjectMocks private PortletsRESTController portletsRESTController;
+
+ @Mock private IAuthorizationService authorizationService;
+ @Mock private IAuthorizationPrincipal authorizationPrincipal;
+ @Mock private IPortletCategoryRegistry portletCategoryRegistry;
+ @Mock private IPortletDefinitionRegistry portletDefinitionRegistry;
+ @Mock private IPerson user;
+ @Mock private IPersonManager personManager;
+ @Mock private EntityIdentifier userEntityIdentifier;
+ @Mock private FavoritesUtils favoritesUtils;
+ @Mock private UserPreferencesManager userPreferencesManager;
+ @Mock private IUserInstanceManager userInstanceManager;
+ @Mock private IUserLayoutManager userLayoutManager;
+
+ @Mock private HttpServletRequest request;
+ @Mock private IPortletDefinition portletDefinition1, portletDefinition2, portletDefinition3;
+ @Mock private IPortletType portletType;
+ @Mock private IUserInstance userInstance;
+ @Mock private IUserLayout userLayout;
+
+ private static final String entityIdentifierKey = "entityIdentifierKey";
+ private static final long portletDefinitionId1Long = 1L;
+ private static final long portletDefinitionId2Long = 2L;
+ private static final long portletDefinitionId3Long = 3L;
+ private static final IPortletDefinitionId portletDefinitionId1 =
+ new PortletDefinitionId(portletDefinitionId1Long);
+ private static final IPortletDefinitionId portletDefinitionId2 =
+ new PortletDefinitionId(portletDefinitionId2Long);
+ private static final IPortletDefinitionId portletDefinitionId3 =
+ new PortletDefinitionId(portletDefinitionId3Long);
+ private List portletsFromRegistry;
+
+ @Before
+ public void setUp() throws Exception {
+ this.portletsFromRegistry = new ArrayList<>();
+ this.userEntityIdentifier = new EntityIdentifier(entityIdentifierKey, IPerson.class);
+ this.portletType = new PortletTypeImpl("portletType", "portletType");
+ this.setupMockPortletDefinition(portletDefinition1, portletDefinitionId1);
+ given(this.portletDefinitionRegistry.getAllPortletDefinitions())
+ .willReturn(portletsFromRegistry);
+ given(this.personManager.getPerson(this.request)).willReturn(this.user);
+ given(this.user.getEntityIdentifier()).willReturn(this.userEntityIdentifier);
+ given(
+ this.authorizationService.newPrincipal(
+ this.userEntityIdentifier.getKey(),
+ this.userEntityIdentifier.getType()))
+ .willReturn(this.authorizationPrincipal);
+ this.setupMockPortletDefinition(this.portletDefinition1, portletDefinitionId1);
+ this.setupMockPortletDefinition(this.portletDefinition2, portletDefinitionId2);
+ this.setupMockPortletDefinition(this.portletDefinition3, portletDefinitionId3);
+ this.givenPortletDefinitionsInRegistry(
+ this.portletDefinition1, this.portletDefinition2, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithNoPermissionsTypeSpecifiedDefaultsToManagePermissionsType() {
+ this.givenUserHasPermissionForPortlets(
+ this.user, MANAGE, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithManagePermissionsTypeSpecified() {
+ this.givenRequestSpecifiesPermissionType(MANAGE);
+ this.givenUserHasPermissionForPortlets(
+ this.user, MANAGE, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithBrowseManagePermissionsTypeSpecified() {
+ this.givenRequestSpecifiesPermissionType(BROWSE);
+ this.givenUserHasPermissionForPortlets(
+ this.user, BROWSE, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithConfigurePermissionsTypeSpecified() {
+ this.givenRequestSpecifiesPermissionType(CONFIGURE);
+ this.givenUserHasPermissionForPortlets(
+ this.user, CONFIGURE, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithRenderPermissionsTypeSpecified() {
+ this.givenRequestSpecifiesPermissionType(RENDER);
+ this.givenUserHasPermissionForPortlets(
+ this.user, RENDER, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWithSubscribePermissionsTypeSpecified() {
+ this.givenRequestSpecifiesPermissionType(SUBSCRIBE);
+ this.givenUserHasPermissionForPortlets(
+ this.user, SUBSCRIBE, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWhenLimitingToFavoritedPortlets() {
+ this.givenRequestSpecifiesFavoriteFlag(true);
+ this.givenUserHasPermissionForPortlets(
+ this.user,
+ MANAGE,
+ this.portletDefinition1,
+ this.portletDefinition2,
+ this.portletDefinition3);
+ this.givenUserHasFavoritePortlets(
+ this.user, this.portletDefinition1, this.portletDefinition3);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition1, this.portletDefinition3);
+ }
+
+ @Test
+ public void testGetPortletsWhenLimitingToNonFavoritedPortlets() {
+ this.givenRequestSpecifiesFavoriteFlag(false);
+ this.givenUserHasPermissionForPortlets(
+ this.user,
+ MANAGE,
+ this.portletDefinition1,
+ this.portletDefinition2,
+ this.portletDefinition3);
+ this.givenUserHasFavoritePortlets(this.user, this.portletDefinition1);
+ final ModelAndView mav = this.portletsRESTController.getPortlets(request);
+ this.verifyPortletResults(mav, this.portletDefinition2, this.portletDefinition3);
+ }
+
+ private void setupMockPortletDefinition(
+ IPortletDefinition portletDefinition, IPortletDefinitionId id) {
+ final String stringId = id.getStringId();
+ given(portletDefinition.getFName()).willReturn("fname" + stringId);
+ given(portletDefinition.getName()).willReturn("name" + stringId);
+ given(portletDefinition.getDescription()).willReturn("description" + stringId);
+ given(portletDefinition.getType()).willReturn(this.portletType);
+ given(portletDefinition.getLifecycleState()).willReturn(PortletLifecycleState.PUBLISHED);
+ given(portletDefinition.getPortletDefinitionId()).willReturn(id);
+ }
+
+ private void givenRequestSpecifiesPermissionType(
+ PortletsRESTController.PortletPermissionType permissionType) {
+ given(this.request.getParameter(PortletsRESTController.REQUIRED_PERMISSION_TYPE))
+ .willReturn(permissionType.toString());
+ }
+
+ private void givenRequestSpecifiesFavoriteFlag(boolean flagValue) {
+ given(this.request.getParameter(PortletsRESTController.FAVORITE_FLAG))
+ .willReturn(flagValue ? "true" : "false");
+ }
+
+ private void givenPortletDefinitionsInRegistry(IPortletDefinition... portletDefinitions) {
+ portletsFromRegistry.addAll(Arrays.asList(portletDefinitions));
+ }
+
+ private void givenUserHasPermissionForPortlets(
+ IPerson user,
+ PortletsRESTController.PortletPermissionType permissionType,
+ IPortletDefinition... portletDefinitions) {
+ for (IPortletDefinition portletDefinition : portletDefinitions) {
+ final String portletDefinitionStringId =
+ portletDefinition.getPortletDefinitionId().getStringId();
+ switch (permissionType) {
+ case BROWSE:
+ given(
+ this.authorizationService.canPrincipalBrowse(
+ this.authorizationPrincipal, portletDefinition))
+ .willReturn(true);
+ break;
+ case CONFIGURE:
+ given(
+ this.authorizationService.canPrincipalConfigure(
+ this.authorizationPrincipal, portletDefinitionStringId))
+ .willReturn(true);
+ break;
+ case MANAGE:
+ given(
+ this.authorizationService.canPrincipalManage(
+ this.authorizationPrincipal, portletDefinitionStringId))
+ .willReturn(true);
+ break;
+ case SUBSCRIBE:
+ given(
+ this.authorizationService.canPrincipalSubscribe(
+ this.authorizationPrincipal, portletDefinitionStringId))
+ .willReturn(true);
+ break;
+ case RENDER:
+ given(
+ this.authorizationService.canPrincipalRender(
+ this.authorizationPrincipal, portletDefinitionStringId))
+ .willReturn(true);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown permission type: " + permissionType);
+ }
+ }
+ }
+
+ private void givenUserHasFavoritePortlets(
+ IPerson user, IPortletDefinition... portletDefinitions) {
+ final Set favoritePortlets = new HashSet<>();
+ for (IPortletDefinition portletDefinition : portletDefinitions) {
+ favoritePortlets.add(portletDefinition);
+ }
+ given(this.userInstanceManager.getUserInstance(this.request)).willReturn(this.userInstance);
+ given(this.userInstance.getPreferencesManager()).willReturn(this.userPreferencesManager);
+ given(this.userPreferencesManager.getUserLayoutManager())
+ .willReturn(this.userLayoutManager);
+ given(this.userLayoutManager.getUserLayout()).willReturn(this.userLayout);
+ given(this.favoritesUtils.getFavoritePortletDefinitions(this.userLayout))
+ .willReturn(favoritePortlets);
+ }
+
+ private void verifyPortletResults(ModelAndView mav, IPortletDefinition... expectedPortlets) {
+ assertNotNull(mav);
+ assertEquals("json", mav.getViewName());
+ final Map model = mav.getModel();
+ assertNotNull(model);
+ final List portlets =
+ (List) model.get("portlets");
+ assertNotNull(portlets);
+ assertEquals(expectedPortlets.length, portlets.size());
+ for (int i = 0; i < expectedPortlets.length; i++) {
+ final IPortletDefinition expectedPortlet = expectedPortlets[i];
+ final PortletsRESTController.PortletTuple portletTuple = portlets.get(i);
+ assertEquals(expectedPortlet.getFName(), portletTuple.getFname());
+ assertEquals(expectedPortlet.getName(), portletTuple.getName());
+ assertEquals(expectedPortlet.getDescription(), portletTuple.getDescription());
+ assertEquals(expectedPortlet.getType().getName(), portletTuple.getType());
+ assertEquals(
+ expectedPortlet.getLifecycleState().toString(),
+ portletTuple.getLifecycleState());
+ assertEquals(
+ expectedPortlet.getPortletDefinitionId().getStringId(), portletTuple.getId());
+ }
+ }
+
+ static class PortletDefinitionId implements IPortletDefinitionId {
+ private long id;
+
+ public PortletDefinitionId(long id) {
+ this.id = id;
+ }
+
+ @Override
+ public long getLongId() {
+ return id;
+ }
+
+ @Override
+ public String getStringId() {
+ return String.valueOf(id);
+ }
+ }
+}