View Javadoc

1   /**
2    * This file Copyright (c) 2011-2013 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.security;
35  
36  import info.magnolia.cms.security.auth.ACL;
37  import info.magnolia.context.MgnlContext;
38  
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.List;
42  
43  import javax.jcr.Node;
44  import javax.jcr.RepositoryException;
45  import javax.jcr.Session;
46  import javax.security.auth.Subject;
47  
48  import org.apache.commons.lang.StringUtils;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  /**
53   * Collection of methods for handling permission related processing.
54   *
55   * @version $Id$
56   *
57   */
58  public class PermissionUtil {
59  
60      private static final Logger log = LoggerFactory.getLogger(PermissionUtil.class);
61  
62      /**
63       * 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.
64       */
65      public static AccessManager getAccessManager(String workspace, Subject subject) {
66          List<Permission> availablePermissions = PermissionUtil.getPermissions(subject, workspace);
67          if (availablePermissions == null) {
68              log.warn("no permissions found for " + subject.getPrincipals(User.class));
69          }
70          // TODO: use provider instead of fixed impl
71          AccessManagerImpl ami = new AccessManagerImpl();
72          ami.setPermissionList(availablePermissions);
73          return ami;
74      }
75  
76      /**
77       * Retrieves permissions for current user.
78       */
79      static List<Permission> getPermissions(Subject subject, String name) {
80          if (subject == null) {
81              // FIXME: this needs to be cached if we really run anonymous w/o session
82              log.warn("no session == running as anonymous");
83              SecuritySupport secSupport = SecuritySupport.Factory.getInstance();
84              Collection<String> roles = secSupport.getUserManager().getAnonymousUser().getAllRoles();
85              RoleManager roleMan = secSupport.getRoleManager();
86              List<Permission> permissions = new ArrayList<Permission>();
87              for (String role : roles) {
88                  for (ACL acl : roleMan.getACLs(role).values()) {
89                      if (name.equals(acl.getName())) {
90                          // merge URI permissions from all roles
91                          permissions.addAll(acl.getList());
92                      }
93                  }
94              }
95              return permissions;
96          }
97  
98          ACL acl = PrincipalUtil.findAccessControlList(subject, name);
99          return acl != null ? acl.getList() : null;
100     }
101 
102     /**
103      * Convenience call hiding all ugly details of permission conversions.
104      * 
105      * @throws RepositoryException
106      *             in case node or its parent session is invalid.
107      * 
108      */
109     public static boolean isGranted(Node node, long requiredPermissions) throws RepositoryException {
110         AccessManager ami = MgnlContext.getAccessManager(node.getSession().getWorkspace().getName());
111         return ami.isGranted(node.getPath(), requiredPermissions);
112     }
113 
114     // isGranted(Node, long) ... NodeUtil, ForumTree ...
115     /**
116      * Convenience call hiding all ugly details of permission conversions.
117      * 
118      */
119     public static boolean isGranted(String workspace, String path, String requiredPermissions) {
120         AccessManager ami = MgnlContext.getAccessManager(workspace);
121         return ami.isGranted(path, PermissionUtil.convertPermissions(requiredPermissions));
122     }
123 
124     /**
125      * Return whether given session has requested permission on provided path.
126      */
127     public static boolean isGranted(Session jcrSession, String path, long oldPermissions) {
128         String action = null;
129         try {
130             action = convertPermissions(oldPermissions);
131         } catch (IllegalArgumentException e) {
132             AccessManager ami = MgnlContext.getAccessManager(jcrSession.getWorkspace().getName());
133             ami.isGranted(path, oldPermissions);
134         }
135         try {
136             return jcrSession.hasPermission(path, action);
137         } catch (RepositoryException e) {
138             return false;
139         }
140     }
141 
142     /**
143      * Return whether given session has requested permission on provided path.
144      * 
145      * @throws IllegalArgumentException
146      *             when provided action is empty.
147      */
148     public static boolean isGranted(Session jcrSession, String path, String action) {
149         if (StringUtils.isBlank(action)) {
150             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.");
151         }
152         try {
153             return jcrSession.hasPermission( path, action);
154         } catch (RepositoryException e) {
155             return false;
156         }
157     }
158 
159     /**
160      * Return String-representation of permissions convert from provided long-permission (old).
161      */
162     public static long convertPermissions(String newPermissions) {
163         String[] perms = StringUtils.split(newPermissions, ", ");
164         long oldPerms = 0;
165         for (String perm : perms) {
166             if (Session.ACTION_ADD_NODE.equals(perm)) {
167                 oldPerms += Permission.WRITE;
168             } else if (Session.ACTION_READ.equals(perm)) {
169                 oldPerms += Permission.READ;
170             } else if (Session.ACTION_REMOVE.equals(perm)) {
171                 oldPerms += Permission.REMOVE;
172             } else if (Session.ACTION_SET_PROPERTY.equals(perm)) {
173                 oldPerms += Permission.SET;
174             }
175         }
176         return oldPerms;
177     }
178 
179     /**
180      * Return String-representation of permissions convert from provided long-permission (old).
181      */
182     static String convertPermissions(long oldPermissions) {
183         StringBuilder permissions = new StringBuilder();
184         if ((oldPermissions & Permission.ALL) == Permission.ALL) {
185             permissions.append(Session.ACTION_ADD_NODE).append(",").append(Session.ACTION_READ).append(",").append(Session.ACTION_REMOVE + ",").append(Session.ACTION_SET_PROPERTY);
186             // skip the rest to be sure we don't introduce duplicates.
187         } else {
188             if ((oldPermissions & Permission.WRITE) == Permission.WRITE) {
189                 if (permissions.length() > 0) {
190                     permissions.append(",");
191                 }
192                 permissions.append(Session.ACTION_ADD_NODE);
193             }
194             if ((oldPermissions & Permission.READ) == Permission.READ) {
195                 if (permissions.length() > 0) {
196                     permissions.append(",");
197                 }
198                 permissions.append(Session.ACTION_READ);
199             }
200             if ((oldPermissions & Permission.REMOVE) == Permission.REMOVE) {
201                 if (permissions.length() > 0) {
202                     permissions.append(",");
203                 }
204                 permissions.append(Session.ACTION_REMOVE);
205             }
206             if ((oldPermissions & Permission.SET) == Permission.SET) {
207                 if (permissions.length() > 0) {
208                     permissions.append(",");
209                 }
210                 permissions.append(Session.ACTION_SET_PROPERTY);
211             }
212         }
213         final String result = permissions.toString();
214         if (StringUtils.isEmpty(result)) {
215             throw new IllegalArgumentException("Unknown permissions: " + oldPermissions);
216         }
217         return result;
218     }
219 
220     /**
221      * Checks whether given session has requested permission on provided path. Throws an exception if permission is not granted on given path.
222      * @throws AccessDeniedException when permission is not granted.
223      */
224     public static void verifyIsGrantedOrThrowException(Session jcrSession, String path, String action) throws AccessDeniedException {
225         try {
226             if (!jcrSession.hasPermission( path, action)) {
227                 throw new AccessDeniedException("Not allowed to access " + path + " with permission " + action);
228             }
229         } catch (RepositoryException e) {
230             throw new AccessDeniedException("Exception occurred while checking permissions for " + path + " with permission " + action, e);
231         }
232     }
233 
234 }