|
From dd069c1728845b885f270ea96a4b8d1b5709a453 Mon Sep 17 00:00:00 2001 |
|
From: drewwills <wills.drew@gmail.com> |
|
Date: Wed, 14 May 2014 05:09:39 -0700 |
|
Subject: [PATCH] UP-4066 Manage Portlets: Group and category selection use |
|
permissions to get forest root |
|
|
|
This change provides several necessary fixes for delegated portlet administration. |
|
First, it updaes the 'Select Categories' and 'Select Groups' steps by filtering |
|
the available choices based on the user's permissions. (In the past, Portlet |
|
Administration users were almost always superusers.) Secondly -- and also in |
|
furtherance of delegated portlet admin -- this change includes several additional |
|
permissions checks during and at the end of the Portlet Manager publishing wizard. |
|
These check are aimed at preventing a Portlet Manager user who is a non-superuser |
|
from stepping outside the bounds of granted authority. |
|
--- |
|
pom.xml | 6 ++ |
|
uportal-war/pom.xml | 5 + |
|
.../layout/dlm/remoting/GroupListHelperImpl.java | 119 ++++++++++++++++++++- |
|
.../layout/dlm/remoting/IGroupListHelper.java | 49 ++++++--- |
|
.../portal/layout/dlm/remoting/JsonEntityBean.java | 6 ++ |
|
.../portletadmin/PortletAdministrationHelper.java | 119 +++++++++++++++------ |
|
.../security/AuthorizationPrincipalHelper.java | 43 ++++++++ |
|
.../org/jasig/portal/security/IPermission.java | 40 +++++-- |
|
.../evaluator/PortalPermissionEvaluator.java | 2 +- |
|
.../WEB-INF/flows/edit-portlet/edit-portlet.xml | 31 +++--- |
|
.../flows/portlet-manager/portlet-manager.xml | 28 +++-- |
|
11 files changed, 356 insertions(+), 92 deletions(-) |
|
create mode 100644 uportal-war/src/main/java/org/jasig/portal/security/AuthorizationPrincipalHelper.java |
|
|
|
diff --git a/pom.xml b/pom.xml |
|
index f65d7de..7871bae 100755 |
|
--- a/pom.xml |
|
+++ b/pom.xml |
|
@@ -142,6 +142,7 @@ |
|
<commons-httpcomponents.version>4.2.4</commons-httpcomponents.version> |
|
<commons-io.version>2.4</commons-io.version> |
|
<commons-lang.version>2.6</commons-lang.version> |
|
+ <commons-lang3.version>3.1</commons-lang3.version> |
|
<commons-logging.version>1.1.3</commons-logging.version> |
|
<commons-logging-api.version>1.1</commons-logging-api.version> |
|
<commons-pool.version>1.6</commons-pool.version> |
|
@@ -378,6 +379,11 @@ |
|
<version>${commons-lang.version}</version> |
|
</dependency> |
|
<dependency> |
|
+ <groupId>org.apache.commons</groupId> |
|
+ <artifactId>commons-lang3</artifactId> |
|
+ <version>${commons-lang3.version}</version> |
|
+ </dependency> |
|
+ <dependency> |
|
<groupId>commons-logging</groupId> |
|
<artifactId>commons-logging</artifactId> |
|
<version>${commons-logging.version}</version> |
|
diff --git a/uportal-war/pom.xml b/uportal-war/pom.xml |
|
index b4f49ac..c36cf22 100644 |
|
--- a/uportal-war/pom.xml |
|
+++ b/uportal-war/pom.xml |
|
@@ -116,6 +116,11 @@ |
|
</dependency> |
|
|
|
<dependency> |
|
+ <groupId>org.apache.commons</groupId> |
|
+ <artifactId>commons-lang3</artifactId> |
|
+ </dependency> |
|
+ |
|
+ <dependency> |
|
<groupId>commons-logging</groupId> |
|
<artifactId>commons-logging</artifactId> |
|
</dependency> |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/GroupListHelperImpl.java b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/GroupListHelperImpl.java |
|
index 9469f79..e26b09e 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/GroupListHelperImpl.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/GroupListHelperImpl.java |
|
@@ -20,6 +20,7 @@ |
|
package org.jasig.portal.layout.dlm.remoting; |
|
|
|
import java.util.ArrayList; |
|
+import java.util.Arrays; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.Iterator; |
|
@@ -35,7 +36,10 @@ import org.jasig.portal.groups.IEntityNameFinder; |
|
import org.jasig.portal.groups.IGroupConstants; |
|
import org.jasig.portal.groups.IGroupMember; |
|
import org.jasig.portal.portlets.groupselector.EntityEnum; |
|
+import org.jasig.portal.security.AuthorizationPrincipalHelper; |
|
import org.jasig.portal.security.IAuthorizationPrincipal; |
|
+import org.jasig.portal.security.IPermission; |
|
+import org.jasig.portal.security.IPerson; |
|
import org.jasig.portal.services.AuthorizationService; |
|
import org.jasig.portal.services.EntityNameFinderService; |
|
import org.jasig.portal.services.GroupService; |
|
@@ -113,7 +117,120 @@ public class GroupListHelperImpl implements IGroupListHelper { |
|
|
|
return bean; |
|
} |
|
- |
|
+ |
|
+ @Override |
|
+ public JsonEntityBean getIndividualBestRootEntity(final IPerson person, |
|
+ final String groupType, final String permissionOwner, |
|
+ final String permissionActivity) { |
|
+ return getIndividualBestRootEntity(person, groupType, permissionOwner, new String[] { permissionActivity }); |
|
+ } |
|
+ |
|
+ @Override |
|
+ public JsonEntityBean getIndividualBestRootEntity(final IPerson person, |
|
+ final String groupType, final String permissionOwner, |
|
+ final String[] permissionActivities) { |
|
+ |
|
+ if (log.isDebugEnabled()) { |
|
+ log.debug("Choosing best root group for user='" + person.getUserName() + |
|
+ "', groupType='" + groupType + "', permissionOwner='" + |
|
+ permissionOwner + "', permissionActivities='" + |
|
+ Arrays.toString(permissionActivities) + "'"); |
|
+ } |
|
+ |
|
+ final IAuthorizationPrincipal principal = AuthorizationPrincipalHelper.principalFromUser(person); |
|
+ final JsonEntityBean canonicalRootGroup = getRootEntity(groupType); |
|
+ |
|
+ if (log.isDebugEnabled()) { |
|
+ log.debug("Found for groupType='" + groupType + |
|
+ "' the following canonicalRootGroup: " + |
|
+ canonicalRootGroup); |
|
+ } |
|
+ |
|
+ // First check the appropriate canonical super-target for the specified type |
|
+ String canonicalSuperTarget = null; |
|
+ if (JsonEntityBean.ENTITY_GROUP.equals(groupType)) { |
|
+ canonicalSuperTarget = IPermission.ALL_GROUPS_TARGET; |
|
+ } else if (JsonEntityBean.ENTITY_CATEGORY.equals(groupType)) { |
|
+ canonicalSuperTarget = IPermission.ALL_CATEGORIES_TARGET; |
|
+ } else { |
|
+ throw new RuntimeException("Unrecognized groupType: " + groupType); |
|
+ } |
|
+ if (log.isDebugEnabled()) { |
|
+ log.debug("Identified for groupType='" + groupType + |
|
+ "' the following canonicalSuperTarget: " + |
|
+ canonicalSuperTarget); |
|
+ } |
|
+ for (String activity : permissionActivities) { |
|
+ if (principal.hasPermission(permissionOwner, activity, canonicalSuperTarget)) { |
|
+ return canonicalRootGroup; |
|
+ } |
|
+ } |
|
+ |
|
+ // Next check the canonical root group itself |
|
+ for (String activity : permissionActivities) { |
|
+ if (principal.hasPermission(permissionOwner, activity, canonicalRootGroup.getId())) { |
|
+ return canonicalRootGroup; |
|
+ } |
|
+ } |
|
+ |
|
+ // So much for the easy paths -- see if the user has any records at all for this specific owner/activity |
|
+ JsonEntityBean rslt = null; // Default |
|
+ final List<IPermission> permissionsOfRelevantActivity = new ArrayList<IPermission>(); |
|
+ for (String activity : permissionActivities) { |
|
+ permissionsOfRelevantActivity.addAll( |
|
+ Arrays.asList(principal.getAllPermissions(permissionOwner, activity, null)) |
|
+ ); |
|
+ } |
|
+ if (log.isDebugEnabled()) { |
|
+ log.debug("For user='" + person.getUserName() + |
|
+ "', groupType='" + groupType + "', permissionOwner='" + |
|
+ permissionOwner + "', permissionActivities='" + |
|
+ Arrays.toString(permissionActivities) + "' permissionsOfRelevantTypes.size()=" + |
|
+ permissionsOfRelevantActivity.size()); |
|
+ } |
|
+ switch (permissionsOfRelevantActivity.size()) { |
|
+ case 0: |
|
+ // No problem -- user doesn't have any of this sort of permission (leave it null) |
|
+ break; |
|
+ default: |
|
+ // We need to make some sort of determination as to the best |
|
+ // root group to send back. With luck there aren't many matches. |
|
+ for (IPermission p : permissionsOfRelevantActivity) { |
|
+ final JsonEntityBean candidate = getEntity(groupType, p.getTarget(), true); |
|
+ // Pass on any matches of the wrong groupType... |
|
+ if (!candidate.getEntityTypeAsString().equalsIgnoreCase(groupType)) { |
|
+ continue; |
|
+ } |
|
+ if (rslt == null) { |
|
+ // First allowable selection; run with this one |
|
+ // unless/until we're forced to make a choice. |
|
+ rslt = candidate; |
|
+ } else { |
|
+ // For the present we'll assume the match with the most |
|
+ // children is the best; this approach should work |
|
+ // decently unless folks start putting redundant |
|
+ // permissions records in the DB for multiple levels of |
|
+ // the same rich hierarchy. |
|
+ if (candidate.getChildren().size() > rslt.getChildren().size()) { |
|
+ rslt = candidate; |
|
+ } |
|
+ } |
|
+ } |
|
+ break; |
|
+ } |
|
+ |
|
+ if (log.isDebugEnabled()) { |
|
+ log.debug("Selected for user='" + person.getUserName() + |
|
+ "', groupType='" + groupType + "', permissionOwner='" + |
|
+ permissionOwner + "', permissionActivities='" + |
|
+ Arrays.toString(permissionActivities) + |
|
+ "' the following best root group: " + rslt); |
|
+ } |
|
+ |
|
+ return rslt; |
|
+ |
|
+ } |
|
+ |
|
/* |
|
* (non-Javadoc) |
|
* @see org.jasig.portal.layout.dlm.remoting.IGroupListHelper#getEntityTypesForGroupType(java.lang.String) |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/IGroupListHelper.java b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/IGroupListHelper.java |
|
index b617460..02a0494 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/IGroupListHelper.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/IGroupListHelper.java |
|
@@ -25,6 +25,7 @@ import java.util.Set; |
|
import org.jasig.portal.groups.IGroupMember; |
|
import org.jasig.portal.portlets.groupselector.EntityEnum; |
|
import org.jasig.portal.security.IAuthorizationPrincipal; |
|
+import org.jasig.portal.security.IPerson; |
|
|
|
/** |
|
* Helper methods for retrieving portal entities. |
|
@@ -42,16 +43,30 @@ public interface IGroupListHelper { |
|
* @param searchTerm search string |
|
* @return set of matching JsonEntityBeans |
|
*/ |
|
- public Set<JsonEntityBean> search(String entityType, String searchTerm); |
|
+ Set<JsonEntityBean> search(String entityType, String searchTerm); |
|
+ |
|
+ /** |
|
+ * Get the root entity for a particular type of group entity. |
|
+ * |
|
+ * @param groupType |
|
+ * @return |
|
+ */ |
|
+ JsonEntityBean getRootEntity(String groupType); |
|
+ |
|
+ /** |
|
+ * Obtain the user's best root (starting) entity available given the |
|
+ * specified permissions activity. |
|
+ */ |
|
+ JsonEntityBean getIndividualBestRootEntity(IPerson person, |
|
+ String groupType, String permissionOwner, String permissionActivity); |
|
+ |
|
+ /** |
|
+ * Obtain the user's best root (starting) entity available given the |
|
+ * specified permissions activities. |
|
+ */ |
|
+ JsonEntityBean getIndividualBestRootEntity(IPerson person, String groupType, |
|
+ String permissionOwner, String[] permissionActivities); |
|
|
|
- /** |
|
- * Get the root entity for a particular type of group entity. |
|
- * |
|
- * @param groupType |
|
- * @return |
|
- */ |
|
- public JsonEntityBean getRootEntity(String groupType); |
|
- |
|
/** |
|
* Get the set of entity types allowed as children of the specified group |
|
* type. |
|
@@ -59,7 +74,7 @@ public interface IGroupListHelper { |
|
* @param groupType |
|
* @return |
|
*/ |
|
- public Set<String> getEntityTypesForGroupType(String groupType); |
|
+ Set<String> getEntityTypesForGroupType(String groupType); |
|
|
|
/** |
|
* Return the string representation of the type of a specified entity object. |
|
@@ -67,7 +82,7 @@ public interface IGroupListHelper { |
|
* @param entity Entity whose type needs to be determined |
|
* @return One of the possible EntityEnum string representations |
|
*/ |
|
- public EntityEnum getEntityType(IGroupMember entity); |
|
+ EntityEnum getEntityType(IGroupMember entity); |
|
|
|
/** |
|
* Find the name of a specified entity. |
|
@@ -75,7 +90,7 @@ public interface IGroupListHelper { |
|
* @param entityBean JsonEntityBean representation of an entity |
|
* @return Entity name, or <code>null</code> if none is found |
|
*/ |
|
- public String lookupEntityName(JsonEntityBean entityBean); |
|
+ String lookupEntityName(JsonEntityBean entityBean); |
|
|
|
/** |
|
* Return a JsonEntityBean for the supplied IGroupMember instance. |
|
@@ -83,7 +98,7 @@ public interface IGroupListHelper { |
|
* @param member |
|
* @return |
|
*/ |
|
- public JsonEntityBean getEntity(IGroupMember member); |
|
+ JsonEntityBean getEntity(IGroupMember member); |
|
|
|
/** |
|
* Retrieve an individual entity matching the specified type and id. If |
|
@@ -97,7 +112,7 @@ public interface IGroupListHelper { |
|
* @param populateChildren <code>true</code> to populate the bean with children |
|
* @return JsonEntityBean representation or <code>null</code> |
|
*/ |
|
- public JsonEntityBean getEntity(String entityType, String entityId, boolean populateChildren); |
|
+ JsonEntityBean getEntity(String entityType, String entityId, boolean populateChildren); |
|
|
|
/** |
|
* Get a list of JsonEntityBeans for a supplied list of string identifiers, |
|
@@ -108,10 +123,10 @@ public interface IGroupListHelper { |
|
* @param params List of string identifiers |
|
* @return List of matching JsonEntityBeans |
|
*/ |
|
- public List<JsonEntityBean> getEntityBeans(List<String> params); |
|
+ List<JsonEntityBean> getEntityBeans(List<String> params); |
|
|
|
- public JsonEntityBean getEntityForPrincipal(String principalString); |
|
+ JsonEntityBean getEntityForPrincipal(String principalString); |
|
|
|
- public IAuthorizationPrincipal getPrincipalForEntity(JsonEntityBean entity); |
|
+ IAuthorizationPrincipal getPrincipalForEntity(JsonEntityBean entity); |
|
|
|
} |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/JsonEntityBean.java b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/JsonEntityBean.java |
|
index b014684..194a5f4 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/JsonEntityBean.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/layout/dlm/remoting/JsonEntityBean.java |
|
@@ -143,11 +143,17 @@ public class JsonEntityBean implements Serializable, Comparable<JsonEntityBean> |
|
* Identifies this bean uniquely as a permissions target. NOTE: This id is |
|
* not the fname (for portlets) or name field (for groups), but rater a |
|
* unique String like 'PORTLET_ID.19' or 'local.36' or 'pags.Authenticated Users' |
|
+ * |
|
+ * @since uPortal 4.0.14 |
|
*/ |
|
public String getTargetString() { |
|
return targetString; |
|
} |
|
|
|
+ /** |
|
+ * |
|
+ * @since uPortal 4.0.14 |
|
+ */ |
|
public void setTargetString(String targetString) { |
|
this.targetString = targetString; |
|
} |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/portlets/portletadmin/PortletAdministrationHelper.java b/uportal-war/src/main/java/org/jasig/portal/portlets/portletadmin/PortletAdministrationHelper.java |
|
index 180686b..a8b7a15 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/portlets/portletadmin/PortletAdministrationHelper.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/portlets/portletadmin/PortletAdministrationHelper.java |
|
@@ -182,10 +182,10 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
* @param chanId |
|
* @return |
|
*/ |
|
- public PortletDefinitionForm getPortletDefinitionForm(String portletId) { |
|
- |
|
- IPortletDefinition def = portletDefinitionRegistry.getPortletDefinition(portletId); |
|
- |
|
+ public PortletDefinitionForm getPortletDefinitionForm(IPerson person, String portletId) { |
|
+ |
|
+ IPortletDefinition def = portletDefinitionRegistry.getPortletDefinition(portletId); |
|
+ |
|
// create the new form |
|
final PortletDefinitionForm form; |
|
if (def != null) { |
|
@@ -248,18 +248,54 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
form.addGroup(new JsonEntityBean(everyoneGroup, groupListHelper.getEntityType(everyoneGroup))); |
|
} |
|
|
|
- return form; |
|
- } |
|
- |
|
- /** |
|
- * Persist a new or edited PortletDefinition. |
|
- * |
|
- * @param form |
|
- * @param publisher |
|
- */ |
|
- public PortletDefinitionForm savePortletRegistration(PortletDefinitionForm form, |
|
- IPerson publisher) throws Exception { |
|
- |
|
+ /* TODO: Service-Layer Security Reboot (great need of refactoring with a community-approved plan in place) */ |
|
+ // User must have SOME FORM of lifecycle permission over AT LEAST ONE |
|
+ // category in which this portlet resides; lifecycle permissions are |
|
+ // hierarchical, so we'll test with the weakest. |
|
+ if (!hasLifecyclePermission(person, PortletLifecycleState.CREATED, form.getCategories())) { |
|
+ logger.warn("User '" + person.getUserName() + |
|
+ "' attempted to edit the following portlet without MANAGE permission: " + def); |
|
+ throw new SecurityException("Not Authorized"); |
|
+ } |
|
+ |
|
+ return form; |
|
+ } |
|
+ |
|
+ /** |
|
+ * Persist a new or edited PortletDefinition. |
|
+ * |
|
+ * @param form |
|
+ * @param publisher |
|
+ */ |
|
+ public PortletDefinitionForm savePortletRegistration(IPerson publisher, |
|
+ PortletDefinitionForm form) throws Exception { |
|
+ |
|
+ /* TODO: Service-Layer Security Reboot (great need of refactoring with a community-approved plan in place) */ |
|
+ |
|
+ // User must have the selected lifecycle permission over AT LEAST ONE |
|
+ // category in which this portlet resides. (This is the same check that |
|
+ // is made when the user enteres the lifecycle-selection step in the wizard.) |
|
+ if (!hasLifecyclePermission(publisher, form.getLifecycleState(), form.getCategories())) { |
|
+ logger.warn("User '" + publisher.getUserName() + |
|
+ "' attempted to save the following portlet without the selected MANAGE permission: " + form); |
|
+ throw new SecurityException("Not Authorized"); |
|
+ } |
|
+ if (form.getId() != null && !form.getId().equals("-1")) { |
|
+ // Portlet is not new; user must have the previous MANAGE permission |
|
+ // in AT LEAST ONE previous category as well |
|
+ IPortletDefinition def = this.portletDefinitionRegistry.getPortletDefinition(form.getId()); |
|
+ Set<PortletCategory> categories = portletCategoryRegistry.getParentCategories(def); |
|
+ List<JsonEntityBean> categoryBeans = new ArrayList<JsonEntityBean>(); |
|
+ for (PortletCategory cat : categories) { |
|
+ categoryBeans.add(new JsonEntityBean(cat)); |
|
+ } |
|
+ if (!hasLifecyclePermission(publisher, def.getLifecycleState(), categoryBeans)) { |
|
+ logger.warn("User '" + publisher.getUserName() + |
|
+ "' attempted to save the following portlet without the previous MANAGE permission: " + form); |
|
+ throw new SecurityException("Not Authorized"); |
|
+ } |
|
+ } |
|
+ |
|
// create the group array from the form's group list |
|
IGroupMember[] groupMembers = new IGroupMember[form.getGroups().size()]; |
|
for (int i = 0; i < groupMembers.length; i++) { |
|
@@ -272,7 +308,7 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
|
|
} |
|
} |
|
- |
|
+ |
|
// create the category array from the form's category list |
|
PortletCategory[] categories = new PortletCategory[form.getCategories().size()]; |
|
for (ListIterator<JsonEntityBean> iter = form.getCategories().listIterator(); iter.hasNext();) { |
|
@@ -420,20 +456,38 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
|
|
portletPublishingService.savePortletDefinition(portletDef, publisher, Arrays.asList(categories), Arrays.asList(groupMembers)); |
|
|
|
- return this.getPortletDefinitionForm(portletDef.getPortletDefinitionId().getStringId()); |
|
+ return this.getPortletDefinitionForm(publisher, portletDef.getPortletDefinitionId().getStringId()); |
|
} |
|
- |
|
- /** |
|
- * Delete the portlet with the given portlet ID. |
|
- * |
|
- * @param portletID the portlet ID |
|
- * @param person the person removing the portlet |
|
- */ |
|
- public void removePortletRegistration(String portletId, IPerson person) { |
|
- IPortletDefinition def = portletDefinitionRegistry.getPortletDefinition(portletId); |
|
- portletDefinitionRegistry.deletePortletDefinition(def); |
|
- } |
|
- |
|
+ |
|
+ /** |
|
+ * Delete the portlet with the given portlet ID. |
|
+ * |
|
+ * @param person the person removing the portlet |
|
+ * @param form |
|
+ */ |
|
+ public void removePortletRegistration(IPerson person, PortletDefinitionForm form) { |
|
+ |
|
+ /* TODO: Service-Layer Security Reboot (great need of refactoring with a community-approved plan in place) */ |
|
+ // Arguably a check here is redundant since -- in the current |
|
+ // portlet-manager webflow -- you can't get to this point in the |
|
+ // conversation with out first obtaining a PortletDefinitionForm; but |
|
+ // it makes sense to check permissions here as well since the route(s) |
|
+ // to reach this method could evolve in the future. |
|
+ |
|
+ // Let's enforce the policy that you may only delete a portlet thet's |
|
+ // currently in a lifecycle state you have permission to MANAGE. |
|
+ // (They're hierarchical.) |
|
+ if (!hasLifecyclePermission(person, form.getLifecycleState(), form.getCategories())) { |
|
+ logger.warn("User '" + person.getUserName() + "' attempted to remove portlet '" + |
|
+ form.getFname() + "' without the proper MANAGE permission"); |
|
+ throw new SecurityException("Not Authorized"); |
|
+ } |
|
+ |
|
+ IPortletDefinition def = portletDefinitionRegistry.getPortletDefinition(form.getId()); |
|
+ portletDefinitionRegistry.deletePortletDefinition(def); |
|
+ |
|
+ } |
|
+ |
|
/** |
|
* Get a list of the key names of the currently-set arbitrary portlet |
|
* preferences. |
|
@@ -794,9 +848,10 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
} |
|
|
|
public boolean configModeAction(ExternalContext externalContext, String fname) throws IOException { |
|
+ |
|
final ActionRequest actionRequest = (ActionRequest)externalContext.getNativeRequest(); |
|
final ActionResponse actionResponse = (ActionResponse)externalContext.getNativeResponse(); |
|
- |
|
+ |
|
final IPortletWindowId portletWindowId = this.getDelegateWindowId(externalContext, fname); |
|
if (portletWindowId == null) { |
|
throw new IllegalStateException("Cannot execute configModeAciton without a delegate window ID in the session for key: " + RenderPortletTag.DEFAULT_SESSION_KEY_PREFIX + fname); |
|
@@ -815,7 +870,7 @@ public class PortletAdministrationHelper implements ServletContextAware { |
|
//The portlet sent a redirect OR changed it's mode away from CONFIG, assume it is done |
|
return true; |
|
} |
|
- |
|
+ |
|
return false; |
|
} |
|
|
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/security/AuthorizationPrincipalHelper.java b/uportal-war/src/main/java/org/jasig/portal/security/AuthorizationPrincipalHelper.java |
|
new file mode 100644 |
|
index 0000000..7853420 |
|
--- /dev/null |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/security/AuthorizationPrincipalHelper.java |
|
@@ -0,0 +1,43 @@ |
|
+package org.jasig.portal.security; |
|
+ |
|
+import org.apache.commons.lang3.Validate; |
|
+import org.jasig.portal.EntityIdentifier; |
|
+import org.jasig.portal.services.AuthorizationService; |
|
+ |
|
+/** |
|
+ * Static convenience methods for working with IAuthorizationPricipal. |
|
+ * |
|
+ * Currently offers one method which encapsulates the magic for |
|
+ * converting a uPortal IPerson to an IAuthorizationPrincipal. |
|
+ * |
|
+ * @since uPortal 4.1 |
|
+ */ |
|
+public final class AuthorizationPrincipalHelper { |
|
+ |
|
+ /** |
|
+ * Convenience method for converting an IPerson to an IAuthorizationPrincipal. |
|
+ * @param user a non-null valid IPerson |
|
+ * @return an IAuthorizationPrincipal representing that user |
|
+ * @throws IllegalArgumentException if the user object is null or defective. |
|
+ * @since uPortal 4.1 |
|
+ */ |
|
+ public static IAuthorizationPrincipal principalFromUser(final IPerson user) { |
|
+ |
|
+ Validate.notNull(user, "Cannot determine an authorization principal for null user."); |
|
+ |
|
+ final EntityIdentifier userEntityIdentifier = user.getEntityIdentifier(); |
|
+ Validate.notNull(user, "The user object is defective: lacks entity identifier."); |
|
+ |
|
+ final String userEntityKey = userEntityIdentifier.getKey(); |
|
+ Validate.notNull(userEntityKey, "The user object is defective: lacks entity key."); |
|
+ final Class userEntityType = userEntityIdentifier.getType(); |
|
+ Validate.notNull(userEntityType, "The user object is defective: lacks entity type."); |
|
+ |
|
+ final IAuthorizationPrincipal principal = |
|
+ AuthorizationService.instance().newPrincipal(userEntityKey, userEntityType); |
|
+ |
|
+ return principal; |
|
+ |
|
+ } |
|
+ |
|
+} |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/security/IPermission.java b/uportal-war/src/main/java/org/jasig/portal/security/IPermission.java |
|
index 427d510..18d3f10 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/security/IPermission.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/security/IPermission.java |
|
@@ -56,12 +56,26 @@ public interface IPermission { |
|
public static final String PORTLET_MANAGER_CREATED_ACTIVITY = "MANAGE_CREATED"; |
|
public static final String PORTLET_MANAGER_APPROVED_ACTIVITY = "MANAGE_APPROVED"; |
|
public static final String PORTLET_MANAGER_EXPIRED_ACTIVITY = "MANAGE_EXPIRED"; |
|
- |
|
+ |
|
+ /* |
|
+ * All management permissions in one handy array |
|
+ */ |
|
+ public static final String[] PORTLET_MANAGER_MANAGE_ACTIVITIES = new String[] { |
|
+ PORTLET_MANAGER_CREATED_ACTIVITY, PORTLET_MANAGER_APPROVED_ACTIVITY, |
|
+ PORTLET_MANAGER_ACTIVITY, PORTLET_MANAGER_EXPIRED_ACTIVITY |
|
+ }; |
|
+ |
|
/* |
|
* PortletMode permissions |
|
*/ |
|
public static final String PORTLET_MODE_CONFIG = "PORTLET_MODE_CONFIG"; |
|
- |
|
+ |
|
+ /* |
|
+ * UP_GROUP (GaP) Permissions |
|
+ */ |
|
+ |
|
+ public static final String VIEW_GROUP_ACTIVITY = "VIEW_GROUP"; |
|
+ |
|
/* |
|
Permission types. At present only 2, but that could change. |
|
*/ |
|
@@ -69,14 +83,24 @@ public interface IPermission { |
|
public static final String PERMISSION_TYPE_DENY = "DENY"; |
|
|
|
/* |
|
- A String representing the uPortal framework, used, for example, for |
|
- Permission.owner when the framework grants a Permission. |
|
- */ |
|
+ * Permission Owner Strings |
|
+ */ |
|
+ |
|
+ /** |
|
+ * A String representing the uPortal framework, used, for example, for |
|
+ * Permission.owner when the framework grants a Permission. |
|
+ */ |
|
+ public static final String PORTAL_SYSTEM = "UP_SYSTEM"; |
|
+ |
|
+ /** |
|
+ * Represents the GaP subsystem as a permissions owner |
|
+ */ |
|
+ public static final String PORTAL_GROUPS = "UP_GROUPS"; |
|
+ |
|
public static final String PORTAL_PUBLISH = "UP_PORTLET_PUBLISH"; |
|
- |
|
+ |
|
public static final String PORTAL_SUBSCRIBE = "UP_PORTLET_SUBSCRIBE"; |
|
- |
|
- public static final String PORTAL_SYSTEM = "UP_SYSTEM"; |
|
+ |
|
|
|
/* |
|
A String which, when concatentated with a portlet id, represents a portal |
|
diff --git a/uportal-war/src/main/java/org/jasig/portal/spring/security/evaluator/PortalPermissionEvaluator.java b/uportal-war/src/main/java/org/jasig/portal/spring/security/evaluator/PortalPermissionEvaluator.java |
|
index 902f510..8709b24 100644 |
|
--- a/uportal-war/src/main/java/org/jasig/portal/spring/security/evaluator/PortalPermissionEvaluator.java |
|
+++ b/uportal-war/src/main/java/org/jasig/portal/spring/security/evaluator/PortalPermissionEvaluator.java |
|
@@ -69,7 +69,7 @@ public class PortalPermissionEvaluator implements PermissionEvaluator { |
|
// Assume it already represents a valid uPortal permission target |
|
targetId = (String) targetDomainObject; |
|
} else if (targetDomainObject instanceof JsonEntityBean) { |
|
- // The id field of a JsonEntityBean is the target String in permissions records |
|
+ // JsonEntityBean objects now have a targetString member |
|
targetId = ((JsonEntityBean) targetDomainObject).getTargetString(); |
|
} |
|
|
|
diff --git a/uportal-war/src/main/webapp/WEB-INF/flows/edit-portlet/edit-portlet.xml b/uportal-war/src/main/webapp/WEB-INF/flows/edit-portlet/edit-portlet.xml |
|
index 890b5ab..f864185 100644 |
|
--- a/uportal-war/src/main/webapp/WEB-INF/flows/edit-portlet/edit-portlet.xml |
|
+++ b/uportal-war/src/main/webapp/WEB-INF/flows/edit-portlet/edit-portlet.xml |
|
@@ -35,9 +35,14 @@ |
|
|
|
<!-- Initialize a list of available portlet types --> |
|
<on-start> |
|
+ <set name="flashScope.servletRequest" |
|
+ value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
+ <set name="flowScope.person" |
|
+ value="personManager.getPerson(servletRequest)"/> |
|
<set name="flowScope.portletTypes" |
|
value="channelPublishingDefinitionDao.getChannelPublishingDefinitions()"/> |
|
- <set name="flowScope.completed" value="portlet.id != null and portlet.id != '-1'"/> |
|
+ <set name="flowScope.completed" |
|
+ value="portlet.id != null and portlet.id != '-1'"/> |
|
</on-start> |
|
|
|
<!-- If we're creating a new portlet, display the first step in the workflow. |
|
@@ -102,7 +107,7 @@ |
|
<on-entry> |
|
<set name="flashScope.entityTypes" value="new java.util.HashSet()"/> |
|
<evaluate expression="entityTypes.add('category')"/> |
|
- <set name="flashScope.rootEntity" value="groupListHelper.getEntity('category', 'local.1', false)"/> |
|
+ <set name="flashScope.rootEntity" value="groupListHelper.getIndividualBestRootEntity(person, 'category', T(org.jasig.portal.security.IPermission).PORTAL_PUBLISH, T(org.jasig.portal.security.IPermission).PORTLET_MANAGER_MANAGE_ACTIVITIES)"/> |
|
</on-entry> |
|
<!-- View Parameters --> |
|
<input name="selectTypes" value="entityTypes"/> |
|
@@ -137,7 +142,7 @@ |
|
<set name="flashScope.entityTypes" value="new java.util.HashSet()"/> |
|
<evaluate expression="entityTypes.add('person')"/> |
|
<evaluate expression="entityTypes.add('group')"/> |
|
- <set name="flashScope.rootEntity" value="groupListHelper.getEntity('group', 'local.0', false)"/> |
|
+ <set name="flashScope.rootEntity" value="groupListHelper.getIndividualBestRootEntity(person, 'group', T(org.jasig.portal.security.IPermission).PORTAL_GROUPS, T(org.jasig.portal.security.IPermission).VIEW_GROUP_ACTIVITY)"/> |
|
</on-entry> |
|
<!-- View Parameters --> |
|
<input name="selectTypes" value="entityTypes"/> |
|
@@ -157,7 +162,7 @@ |
|
<!-- Group input/output mapping --> |
|
<input name="selectedGroups" value="portlet.groups"/> |
|
<output name="selectedGroups" value="portlet.groups"/> |
|
- |
|
+ |
|
<transition on="back" to="chooseCategory"/> |
|
<transition on="finish" to="chooseGroupNextScreen"/> |
|
</subflow-state> |
|
@@ -182,8 +187,6 @@ |
|
<on-entry> |
|
<set name="flashScope.servletRequest" |
|
value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
- <set name="flashScope.person" |
|
- value="personManager.getPerson(servletRequest)"/> |
|
<set name="viewScope.lifecycleStates" value="portletAdministrationHelper.getAllowedLifecycleStates(person, portlet.categories)"/> |
|
</on-entry> |
|
<transition on="back" to="chooseGroup" validate="false"/> |
|
@@ -217,20 +220,12 @@ |
|
|
|
<!-- save the portlet --> |
|
<transition on="save" to="finishPortletEdit"> |
|
- <set name="flashScope.servletRequest" |
|
- value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
- <set name="flashScope.person" |
|
- value="personManager.getPerson(servletRequest)"/> |
|
- <evaluate expression="portletAdministrationHelper.savePortletRegistration(portlet, person)"></evaluate> |
|
+ <evaluate expression="portletAdministrationHelper.savePortletRegistration(person, portlet)"></evaluate> |
|
</transition> |
|
|
|
- <!-- save the portlet --> |
|
+ <!-- save and configure the portlet --> |
|
<transition on="saveAndConfig" to="configMode"> |
|
- <set name="flashScope.servletRequest" |
|
- value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
- <set name="flashScope.person" |
|
- value="personManager.getPerson(servletRequest)"/> |
|
- <set name="flowScope.portlet" value="portletAdministrationHelper.savePortletRegistration(portlet, person)" /> |
|
+ <set name="flowScope.portlet" value="portletAdministrationHelper.savePortletRegistration(person, portlet)" /> |
|
</transition> |
|
|
|
<!-- cancel our portlet edit and exit the sub-flow --> |
|
@@ -250,7 +245,7 @@ |
|
<set name="flashScope.configComplete" value="portletAdministrationHelper.configModeAction(externalContext, portlet.getFname())" /> |
|
<transition to="configMode" on="#{!flashScope.configComplete}" /> |
|
<transition to="reviewPortlet" on="#{flashScope.configComplete}"> |
|
- <set name="flowScope.portlet" value="portletAdministrationHelper.getPortletDefinitionForm(portlet.id)"/> |
|
+ <set name="flowScope.portlet" value="portletAdministrationHelper.getPortletDefinitionForm(person, portlet.id)"/> |
|
</transition> |
|
</action-state> |
|
|
|
diff --git a/uportal-war/src/main/webapp/WEB-INF/flows/portlet-manager/portlet-manager.xml b/uportal-war/src/main/webapp/WEB-INF/flows/portlet-manager/portlet-manager.xml |
|
index 291f0b4..a8ffacd 100644 |
|
--- a/uportal-war/src/main/webapp/WEB-INF/flows/portlet-manager/portlet-manager.xml |
|
+++ b/uportal-war/src/main/webapp/WEB-INF/flows/portlet-manager/portlet-manager.xml |
|
@@ -25,13 +25,17 @@ |
|
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> |
|
|
|
<on-start> |
|
+ <set name="flashScope.servletRequest" |
|
+ value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
+ <set name="flowScope.person" |
|
+ value="personManager.getPerson(servletRequest)"/> |
|
<set name="flowScope.portletTypes" |
|
value="portletTypeRegistry.getPortletTypes()"/> |
|
</on-start> |
|
|
|
<!-- |
|
| Portlet selection view |
|
- | |
|
+ | |
|
| Present a list of currently configured portlets and allow the |
|
| administrative user to either select a portlet to edit or to elect to |
|
| create a new portlet |
|
@@ -45,7 +49,7 @@ |
|
populate it with the selected portlet's data --> |
|
<transition on="editPortlet" to="edit-portlet"> |
|
<set name="flowScope.portlet" |
|
- value="portletAdministrationHelper.getPortletDefinitionForm(requestParameters.portletId)"/> |
|
+ value="portletAdministrationHelper.getPortletDefinitionForm(person, requestParameters.portletId)"/> |
|
</transition> |
|
<!-- If we're creating a new portlet, create a new form --> |
|
<transition on="createPortlet" to="edit-portlet"> |
|
@@ -55,15 +59,13 @@ |
|
<!-- If we're deleting a portlet, confirm the user wants to delete it --> |
|
<transition on="removePortlet" to="confirmRemove"> |
|
<set name="flowScope.portlet" |
|
- value="portletAdministrationHelper.getPortletDefinitionForm(requestParameters.portletId)"/> |
|
- <set name="flowScope.portletId" |
|
- value="requestParameters.portletId"/> |
|
+ value="portletAdministrationHelper.getPortletDefinitionForm(person, requestParameters.portletId)"/> |
|
</transition> |
|
</view-state> |
|
- |
|
+ |
|
<!-- |
|
| Portlet editing subflow |
|
- | |
|
+ | |
|
| Edit or create the selected portlet |
|
+--> |
|
<subflow-state id="edit-portlet" subflow="edit-portlet"> |
|
@@ -72,22 +74,18 @@ |
|
<transition on="cancelPortletEdit" to="finishPortletEdit"/> |
|
</subflow-state> |
|
|
|
- <!-- |
|
+ <!-- |
|
| Portlet deletion |
|
+--> |
|
<view-state id="confirmRemove"> |
|
<transition on="remove" to="listChannels"> |
|
<!-- Remove the portlet --> |
|
- <set name="flashScope.servletRequest" |
|
- value="portalRequestUtils.getPortletHttpRequest(externalContext.getNativeRequest())"/> |
|
- <set name="flashScope.person" |
|
- value="personManager.getPerson(servletRequest)"/> |
|
- <evaluate expression="portletAdministrationHelper.removePortletRegistration(flowScope.portletId,flashScope.person)"/> |
|
+ <evaluate expression="portletAdministrationHelper.removePortletRegistration(person, portlet)"/> |
|
</transition> |
|
<transition on="cancel" to="listChannels"/> |
|
</view-state> |
|
- |
|
+ |
|
<!-- End state --> |
|
<end-state id="finishPortletEdit" /> |
|
- |
|
+ |
|
</flow> |
|
-- |
|
1.8.4.2 |
|
|