/** * This file Copyright (c) 2011-2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.cms.security; import info.magnolia.cms.security.auth.ACL; import info.magnolia.context.MgnlContext; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.security.auth.Subject; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Collection of methods for handling permission related processing. * * @version $Id$ * */ public class PermissionUtil { private static final Logger log = LoggerFactory.getLogger(PermissionUtil.class); /** * Creates instance of AccessManager configured with subject principal permissions for requested workspace/repository. This method will likely move the AccessManagerProvider in the future version, and while public should not be considered part of the public API. */ public static AccessManager getAccessManager(String workspace, Subject subject) { List<Permission> availablePermissions = PermissionUtil.getPermissions(subject, workspace); if (availablePermissions == null) { log.warn("no permissions found for " + subject.getPrincipals(User.class)); } // TODO: use provider instead of fixed impl AccessManagerImpl ami = new AccessManagerImpl(); ami.setPermissionList(availablePermissions); return ami; } /** * Retrieves permissions for current user. */ static List<Permission> getPermissions(Subject subject, String name) { if (subject == null) { // FIXME: this needs to be cached if we really run anonymous w/o session log.warn("no session == running as anonymous"); SecuritySupport secSupport = SecuritySupport.Factory.getInstance(); Collection<String> roles = secSupport.getUserManager().getAnonymousUser().getAllRoles(); RoleManager roleMan = secSupport.getRoleManager(); List<Permission> permissions = new ArrayList<Permission>(); for (String role : roles) { for (ACL acl : roleMan.getACLs(role).values()) { if (name.equals(acl.getName())) { // merge URI permissions from all roles permissions.addAll(acl.getList()); } } } return permissions; } ACL acl = PrincipalUtil.findAccessControlList(subject, name); return acl != null ? acl.getList() : null; } /** * Convenience call hiding all ugly details of permission conversions. * * @throws RepositoryException * in case node or its parent session is invalid. * */ public static boolean isGranted(Node node, long requiredPermissions) throws RepositoryException { AccessManager ami = MgnlContext.getAccessManager(node.getSession().getWorkspace().getName()); return ami.isGranted(node.getPath(), requiredPermissions); } // isGranted(Node, long) ... NodeUtil, ForumTree ... /** * Convenience call hiding all ugly details of permission conversions. * */ public static boolean isGranted(String workspace, String path, String requiredPermissions) { AccessManager ami = MgnlContext.getAccessManager(workspace); return ami.isGranted(path, PermissionUtil.convertPermissions(requiredPermissions)); } /** * Return whether given session has requested permission on provided path. */ public static boolean isGranted(Session jcrSession, String path, long oldPermissions) { String action = null; try { action = convertPermissions(oldPermissions); } catch (IllegalArgumentException e) { AccessManager ami = MgnlContext.getAccessManager(jcrSession.getWorkspace().getName()); ami.isGranted(path, oldPermissions); } try { return jcrSession.hasPermission(path, action); } catch (RepositoryException e) { return false; } } /** * Return whether given session has requested permission on provided path. * * @throws IllegalArgumentException * when provided action is empty. */ public static boolean isGranted(Session jcrSession, String path, String action) { if (StringUtils.isBlank(action)) { throw new IllegalArgumentException("Empty action value is not valid for permission check. Please make sure you don't check against empty permissions or contact administrator."); } try { return jcrSession.hasPermission( path, action); } catch (RepositoryException e) { return false; } } /** * Return String-representation of permissions convert from provided long-permission (old). */ static long convertPermissions(String newPermissions) { String[] perms = newPermissions.split(", "); long oldPerms = 0; for (String perm : perms) { if (Session.ACTION_ADD_NODE.equals(perm)) { oldPerms += Permission.WRITE; } else if (Session.ACTION_READ.equals(perm)) { oldPerms += Permission.READ; } else if (Session.ACTION_REMOVE.equals(perm)) { oldPerms += Permission.REMOVE; } else if (Session.ACTION_SET_PROPERTY.equals(perm)) { oldPerms += Permission.SET; } } return oldPerms; } /** * Return String-representation of permissions convert from provided long-permission (old). */ static String convertPermissions(long oldPermissions) { StringBuilder permissions = new StringBuilder(); if ((oldPermissions & Permission.ALL) == Permission.ALL) { permissions.append(Session.ACTION_ADD_NODE).append(",").append(Session.ACTION_READ).append(",").append(Session.ACTION_REMOVE + ",").append(Session.ACTION_SET_PROPERTY); // skip the rest to be sure we don't introduce duplicates. } else { if ((oldPermissions & Permission.WRITE) == Permission.WRITE) { if (permissions.length() > 0) { permissions.append(","); } permissions.append(Session.ACTION_ADD_NODE); } if ((oldPermissions & Permission.READ) == Permission.READ) { if (permissions.length() > 0) { permissions.append(","); } permissions.append(Session.ACTION_READ); } if ((oldPermissions & Permission.REMOVE) == Permission.REMOVE) { if (permissions.length() > 0) { permissions.append(","); } permissions.append(Session.ACTION_REMOVE); } if ((oldPermissions & Permission.SET) == Permission.SET) { if (permissions.length() > 0) { permissions.append(","); } permissions.append(Session.ACTION_SET_PROPERTY); } } final String result = permissions.toString(); if (StringUtils.isEmpty(result)) { throw new IllegalArgumentException("Unknown permissions: " + oldPermissions); } return result; } /** * Checks whether given session has requested permission on provided path. Throws an exception if permission is not granted on given path. * @throws AccessDeniedException when permission is not granted. */ public static void verifyIsGrantedOrThrowException(Session jcrSession, String path, String action) throws AccessDeniedException { try { if (!jcrSession.hasPermission( path, action)) { throw new AccessDeniedException("Not allowed to access " + path + " with permission " + action); } } catch (RepositoryException e) { throw new AccessDeniedException("Exception occurred while checking permissions for " + path + " with permission " + action, e); } } }