/* vim: set ts=2 et sw=2 cindent fo=qroca: */
package com.globant.katari.core.security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.globant.katari.core.web.MenuNode;
/**
* This class is used to filter a list of menu nodes and only return the ones
* that the user is allowed to click.
*
* {@link SecureUrlAccessHelper}.
* @see SecureUrlAccessHelper
* @author pablo.saavedra
* @author ulises.bochio
* @author jair.tabares
*/
public class MenuAccessFilterer {
/**
* The logger.
*/
private static Logger log = LoggerFactory.getLogger(MenuAccessFilterer.class);
/**
* The {@link SecureUrlAccessHelper}.
* It is never null.
*/
private final SecureUrlAccessHelper urlAccessHelper;
/**
* The MenuAccessFilterer Constructor.
*
* @param theUrlAccessHelper validates if the current user can access to the
* given menu url. It cannot be null.
*/
public MenuAccessFilterer(final SecureUrlAccessHelper theUrlAccessHelper) {
Validate.notNull(theUrlAccessHelper, "theUrlMacroHelper cannot be null.");
urlAccessHelper = theUrlAccessHelper;
}
/** It returns a filtered list of menu nodes and the filter policy is defined
* by the {@link SecureUrlAccessHelper}.
*
* To be added to the output list, the user must have access to the leaf
* node, or must have access to at least one descendent of a non leaf node.
*
* @param nodes A list of nodes to be filtered. It cannot be null.
*
* @return A filtered list of menu nodes. If the user cannot access any menu
* node, it returns an empty list. It never returns null.
*/
public List<MenuNode> filterMenuNodes(final List<MenuNode> nodes) {
log.trace("Entering filterMenuNodes()");
Validate.notNull(nodes, "The List of Menu Nodes cannot be null");
List<MenuNode> result = new ArrayList<MenuNode>();
for (MenuNode node : nodes) {
if (node.isLeaf()) {
if (isAccessible(node)) {
result.add(node);
}
} else {
List<MenuNode> childNodes = node.getChildNodes();
List<MenuNode> filteredNodes = filterMenuNodes(childNodes);
if (!filteredNodes.isEmpty()) {
result.add(node);
}
}
}
log.trace("Leaving filterMenuNodes()");
return Collections.unmodifiableList(result);
}
/** Returns true if the user has access to the leaf node.
*
* This operation is intended to be used as an optimization when the client
* is forced to walk the whole tree anyway. It only applies to leaf nodes.
*
* @param leafNode the leaf node to check if the user has access. It must be
* a non null leaf node.
*
* @return true if the leaf node is accessible by the user.
*/
public boolean isAccessible(final MenuNode leafNode) {
Validate.notNull(leafNode, "The leaf node cannot be null.");
Validate.isTrue(leafNode.isLeaf(), "The node must be a leaf.");
/* urlAccessHelper.canAccessUrl needs a url with a context path. But when
* the url is absolute, this operation ignores the context path, so we
* simply pass a dummy context path. This is a hack to avoid passing the
* request object from the view layer.
*/
final String url = "/dummy-ctx" + leafNode.getLinkPath();
return urlAccessHelper.canAccessUrl(null, url);
}
}