/**
* 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.layout.dlm.remoting;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.portal.EntityIdentifier;
import org.apereo.portal.groups.IEntityGroup;
import org.apereo.portal.groups.IEntityNameFinder;
import org.apereo.portal.groups.IGroupConstants;
import org.apereo.portal.groups.IGroupMember;
import org.apereo.portal.portlet.om.IPortletDefinition;
import org.apereo.portal.portlets.groupselector.EntityEnum;
import org.apereo.portal.security.AuthorizationPrincipalHelper;
import org.apereo.portal.security.IAuthorizationPrincipal;
import org.apereo.portal.security.IPermission;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.services.AuthorizationService;
import org.apereo.portal.services.EntityNameFinderService;
import org.apereo.portal.services.GroupService;
import org.apereo.portal.spring.locator.EntityTypesLocator;
public class GroupListHelperImpl implements IGroupListHelper {
private static final String PRINCIPAL_SEPARATOR = "\\.";
private static final Log log = LogFactory.getLog(GroupListHelperImpl.class);
/*
* (non-Javadoc)
* @see org.apereo.portal.layout.dlm.remoting.IGroupListHelper#search(java.lang.String, java.lang.String)
*/
@SuppressWarnings("unchecked")
public Set<JsonEntityBean> search(String entityType, String searchTerm) {
Set<JsonEntityBean> results = new HashSet<JsonEntityBean>();
EntityEnum entityEnum = EntityEnum.getEntityEnum(entityType);
EntityIdentifier[] identifiers;
Class identifierType;
// if the entity type is a group, use the group service's findGroup method
// to locate it
if (entityEnum.isGroup()) {
identifiers =
GroupService.searchForGroups(
searchTerm, GroupService.CONTAINS, entityEnum.getClazz());
identifierType = IEntityGroup.class;
}
// otherwise use the getGroupMember method
else {
identifiers =
GroupService.searchForEntities(
searchTerm, GroupService.CONTAINS, entityEnum.getClazz());
identifierType = entityEnum.getClazz();
}
for (int i = 0; i < identifiers.length; i++) {
if (identifiers[i].getType().equals(identifierType)) {
IGroupMember entity = GroupService.getGroupMember(identifiers[i]);
if (entity != null) {
JsonEntityBean jsonBean = getEntity(entity);
results.add(jsonBean);
} else {
log.warn("Grouper member entity of " + identifiers[i].getKey() + " is null.");
}
}
}
return results;
}
/*
* (non-Javadoc)
* @see org.apereo.portal.layout.dlm.remoting.IGroupListHelper#getRootEntity(java.lang.String)
*/
public JsonEntityBean getRootEntity(String groupType) {
EntityEnum type = EntityEnum.getEntityEnum(groupType);
String rootKey;
if (EntityEnum.GROUP.equals(type)) {
rootKey = "local.0";
} else if (EntityEnum.CATEGORY.equals(type)) {
IEntityGroup categoryGroup =
GroupService.getDistinguishedGroup(IPortletDefinition.DISTINGUISHED_GROUP);
return new JsonEntityBean(categoryGroup, EntityEnum.CATEGORY);
} else {
throw new IllegalArgumentException(
"Unable to determine a root entity for group type '" + groupType + "'");
}
JsonEntityBean bean = getEntity(groupType, rootKey, false);
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 canonical root group for the applicable activities
* (NOTE: the uPortal permissions infrastructure handles checking of
* special, collective targets like "ALL_GROUPS" and "All_categories").
*/
for (String activity : permissionActivities) {
if (principal.hasPermission(permissionOwner, activity, canonicalRootGroup.getId())) {
return canonicalRootGroup;
}
}
// So much for the easy path -- 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) {
IEntityGroup groupMember = GroupService.findGroup(p.getTarget());
final JsonEntityBean candidate = getEntity(groupMember);
// 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.apereo.portal.layout.dlm.remoting.IGroupListHelper#getEntityTypesForGroupType(java.lang.String)
*/
public Set<String> getEntityTypesForGroupType(String groupType) {
// add the group type itself to the allowed list
Set<String> set = new HashSet<String>();
set.add(groupType);
/*
* If the supplied type is a person group, add the person entity type.
* If the supplied type is a category, add the channel type. Otherwise,
* throw an exception.
*
* This method will require an update if more entity types are added
* in the future.
*/
EntityEnum type = EntityEnum.getEntityEnum(groupType);
if (EntityEnum.GROUP.equals(type)) {
set.add(EntityEnum.PERSON.toString());
} else if (EntityEnum.CATEGORY.equals(type)) {
set.add(EntityEnum.PORTLET.toString());
} else {
throw new IllegalArgumentException(
"Unable to determine a root entity for group type '" + groupType + "'");
}
return set;
}
/*
* (non-Javadoc)
* @see org.apereo.portal.layout.dlm.remoting.IGroupListHelper#getEntity(java.lang.String, java.lang.String, boolean)
*/
public JsonEntityBean getEntity(String entityType, String entityId, boolean populateChildren) {
// get the EntityEnum for the specified entity type
EntityEnum entityEnum = EntityEnum.getEntityEnum(entityType);
// if the entity type is a group, use the group service's findGroup method
// to locate it
if (entityEnum.isGroup()) {
// attempt to find the entity
IEntityGroup entity = GroupService.findGroup(entityId);
if (entity == null) {
return null;
} else {
JsonEntityBean jsonBean = new JsonEntityBean(entity, entityEnum);
if (populateChildren) {
Iterator<IGroupMember> members = entity.getChildren().iterator();
jsonBean = populateChildren(jsonBean, members);
}
if (jsonBean.getEntityType().isGroup()
|| EntityEnum.PERSON.equals(jsonBean.getEntityType())) {
IAuthorizationPrincipal principal = getPrincipalForEntity(jsonBean);
jsonBean.setPrincipalString(principal.getPrincipalString());
}
return jsonBean;
}
}
// otherwise use the getGroupMember method
else {
IGroupMember entity = GroupService.getGroupMember(entityId, entityEnum.getClazz());
if (entity == null || entity instanceof IEntityGroup) {
return null;
}
JsonEntityBean jsonBean = new JsonEntityBean(entity, entityEnum);
// the group member interface doesn't include the entity name, so
// we'll need to look that up manually
jsonBean.setName(lookupEntityName(jsonBean));
if (EntityEnum.GROUP.equals(jsonBean.getEntityType())
|| EntityEnum.PERSON.equals(jsonBean.getEntityType())) {
IAuthorizationPrincipal principal = getPrincipalForEntity(jsonBean);
jsonBean.setPrincipalString(principal.getPrincipalString());
}
return jsonBean;
}
}
/*
* (non-Javadoc)
* @see org.apereo.portal.layout.dlm.remoting.IGroupListHelper#getEntity(org.apereo.portal.groups.IGroupMember)
*/
public JsonEntityBean getEntity(IGroupMember member) {
// get the type of this member entity
EntityEnum entityEnum = getEntityType(member);
// construct a new entity bean for this entity
JsonEntityBean entity;
if (entityEnum.isGroup()) {
entity = new JsonEntityBean((IEntityGroup) member, entityEnum);
} else {
entity = new JsonEntityBean(member, entityEnum);
}
// if the name hasn't been set yet, look up the entity name
if (entity.getName() == null) {
entity.setName(lookupEntityName(entity));
}
if (EntityEnum.GROUP.equals(entity.getEntityType())
|| EntityEnum.PERSON.equals(entity.getEntityType())) {
IAuthorizationPrincipal principal = getPrincipalForEntity(entity);
entity.setPrincipalString(principal.getPrincipalString());
}
return entity;
}
public JsonEntityBean getEntityForPrincipal(String principalString) {
// split the principal string into its type and key components
String[] parts = principalString.split(PRINCIPAL_SEPARATOR, 2);
String key = parts[1];
int typeId = Integer.parseInt(parts[0]);
// get the EntityEnum type for the entity id number
@SuppressWarnings("unchecked")
Class type = EntityTypesLocator.getEntityTypes().getEntityTypeFromID(typeId);
String entityType = "person";
if (IEntityGroup.class.isAssignableFrom(type)) {
entityType = "group";
}
// get the JsonEntityBean for this type and key
JsonEntityBean bean = getEntity(entityType, key, false);
return bean;
}
public IAuthorizationPrincipal getPrincipalForEntity(JsonEntityBean entity) {
// attempt to determine the entity type class for this principal
Class entityType;
EntityEnum jsonType = entity.getEntityType();
if (jsonType.isGroup()) {
entityType = IEntityGroup.class;
} else {
entityType = jsonType.getClazz();
}
// construct an authorization principal for this JsonEntityBean
AuthorizationService authService = AuthorizationService.instance();
IAuthorizationPrincipal p = authService.newPrincipal(entity.getId(), entityType);
return p;
}
/**
* Populates the children of the JsonEntityBean. Creates new JsonEntityBeans for the known types
* (person, group, or category), and adds them as children to the current bean.
*
* @param jsonBean Entity bean to which the children are added
* @param children An Iterator containing IGroupMember elements. Usually obtained from
* entity.getMembers().
* @return jsonBean with the children populated
*/
private JsonEntityBean populateChildren(
JsonEntityBean jsonBean, Iterator<IGroupMember> children) {
while (children.hasNext()) {
IGroupMember member = children.next();
// add the entity bean to the list of children
JsonEntityBean jsonChild = getEntity(member);
jsonBean.addChild(jsonChild);
}
// mark this entity bean as having had it's child list initialized
jsonBean.setChildrenInitialized(true);
return jsonBean;
}
/*
* (non-Javadoc)
* @see org.apereo.portal.layout.dlm.remoting.IGroupListHelper#getEntityType(org.apereo.portal.groups.IGroupMember)
*/
public EntityEnum getEntityType(IGroupMember entity) {
if (IEntityGroup.class.isAssignableFrom(entity.getClass())) {
return EntityEnum.getEntityEnum(entity.getLeafType(), true);
} else {
return EntityEnum.getEntityEnum(entity.getLeafType(), false);
}
}
/*
* (non-Javadoc)
* @see org.apereo.portal.portlets.groupselector.GroupsSelectorHelper#getEntityBeans(java.util.List)
*/
public List<JsonEntityBean> getEntityBeans(List<String> params) {
// if no parameters have been supplied, just return an empty list
if (params == null || params.isEmpty()) {
return Collections.<JsonEntityBean>emptyList();
}
List<JsonEntityBean> beans = new ArrayList<JsonEntityBean>();
for (String param : params) {
String[] parts = param.split(":", 2);
JsonEntityBean member = getEntity(parts[0], parts[1], false);
beans.add(member);
}
return beans;
}
/**
* Convenience method that looks up the name of the given group member. Used for person types.
*
* @param groupMember Entity to look up
* @return groupMember's name or null if there's an error
*/
public String lookupEntityName(JsonEntityBean entity) {
EntityEnum entityEnum = entity.getEntityType();
IEntityNameFinder finder;
if (entityEnum.isGroup()) {
finder = EntityNameFinderService.instance().getNameFinder(IEntityGroup.class);
} else {
finder = EntityNameFinderService.instance().getNameFinder(entityEnum.getClazz());
}
try {
return finder.getName(entity.getId());
} catch (Exception e) {
/* An exception here isn't the end of the world. Just log it
and return null. */
log.warn("Couldn't find name for entity " + entity.getId(), e);
return null;
}
}
}