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.core;
35  
36  import info.magnolia.cms.security.Permission;
37  import info.magnolia.cms.security.PermissionImpl;
38  import info.magnolia.cms.security.PrincipalUtil;
39  import info.magnolia.cms.security.auth.ACL;
40  import info.magnolia.cms.util.SimpleUrlPattern;
41  import info.magnolia.objectfactory.Classes;
42  import info.magnolia.objectfactory.MgnlInstantiationException;
43  
44  import java.security.Principal;
45  import java.util.LinkedList;
46  import java.util.List;
47  import java.util.Map;
48  import java.util.Set;
49  
50  import javax.jcr.ItemNotFoundException;
51  import javax.jcr.RepositoryException;
52  import javax.jcr.Session;
53  import javax.jcr.security.AccessControlPolicy;
54  
55  import org.apache.jackrabbit.core.ItemImpl;
56  import org.apache.jackrabbit.core.security.authorization.AccessControlEditor;
57  import org.apache.jackrabbit.core.security.authorization.CompiledPermissions;
58  import org.apache.jackrabbit.core.security.authorization.combined.CombinedProvider;
59  import org.apache.jackrabbit.spi.Path;
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  
63  /**
64   * Magnolia specific ACL provider. This code depends on JR specific API rather then on JCR API. That's why it'll be moved out from core (SCRUM-636).
65   * 
66   * @version $Id$
67   */
68  // TODO: should extend just abstract control provider!!!
69  public class MagnoliaAccessProvider extends CombinedProvider {
70  
71      private static final Logger log = LoggerFactory.getLogger(MagnoliaAccessProvider.class);
72  
73      private CompiledPermissions RootOnlyPermission;
74  
75      private Map<?, ?> configuration;
76  
77      private final Class<? extends DefaultACLBasedPermissions> defaultPermissionsClass = DefaultACLBasedPermissions.class;
78  
79      private Class<? extends DefaultACLBasedPermissions> permissionsClass;
80  
81      private final String compiledPermissionsClass = null;
82  
83      @Override
84      public boolean canAccessRoot(Set<Principal> principals) throws RepositoryException {
85          checkInitialized();
86  
87          // old Magnolia security allowed access to root to every user
88          return true;
89      }
90  
91      @Override
92      public void close() {
93          log.debug("close()");
94          super.close();
95      }
96  
97      @Override
98      public CompiledPermissions compilePermissions(Set<Principal> principals) throws RepositoryException {
99          log.debug("compile permissions for {} at {}", printUserNames(principals), session == null ? null : session.getWorkspace().getName());
100         checkInitialized();
101 
102         // superuser is also admin user!
103         if (isAdminOrSystem(principals)) {
104             return getAdminPermissions();
105         }
106 
107         final String workspaceName = super.session.getWorkspace().getName();
108 
109         ACL acl = PrincipalUtil.findAccessControlList(principals, workspaceName);
110         if (acl != null) {
111             return getUserPermissions(addJcrSystemReadPermissions(acl.getList()));
112         }
113 
114         return RootOnlyPermission;
115     }
116 
117     private CompiledPermissions getUserPermissions(List<Permission> permissions) {
118         return Classes.getClassFactory().newInstance(permissionsClass, permissions, session, configuration);
119     }
120 
121     @Override
122     public AccessControlEditor getEditor(Session editingSession) {
123         log.debug("getEditor({})", editingSession);
124         return new MagnoliaACLEditor(super.getEditor(editingSession));
125     }
126 
127     @Override
128     public AccessControlPolicy[] getEffectivePolicies(Path absPath, CompiledPermissions permissions) throws ItemNotFoundException, RepositoryException {
129         log.debug("getEffectivePolicies({}, {})", absPath, permissions);
130         return super.getEffectivePolicies(absPath, permissions);
131     }
132 
133     @Override
134     public AccessControlPolicy[] getEffectivePolicies(Set<Principal> principals, CompiledPermissions permissions) throws RepositoryException {
135         log.debug("getEffectivePolicies({}, {})", principals, permissions);
136         return super.getEffectivePolicies(principals, permissions);
137     }
138 
139     private final String warnMessage = "Check settings of 'permissionsClass' parameter under Workspace>WorkspaceSecurity>AccessControlProvider>. " +
140             "Using default " + defaultPermissionsClass + " instead. Only classes extended from this default class can be used.";
141 
142     @Override
143     public void init(Session systemSession, Map configuration) throws RepositoryException {
144 
145         log.debug("init({}, {})", systemSession, configuration);
146         super.init(systemSession, configuration);
147         RootOnlyPermission = new RootOnlyPermissions(session);
148         this.configuration = configuration;
149 
150         Object compiledPermissionsClass = configuration.get("permissionsClass");
151         if (compiledPermissionsClass == null) {
152             permissionsClass = defaultPermissionsClass;
153             return;
154         }
155         try {
156             permissionsClass = Classes.getClassFactory().forName((String) compiledPermissionsClass);
157             if (!DefaultACLBasedPermissions.class.isAssignableFrom(permissionsClass)) {
158                 log.warn("The '{}' cannot be used as permissionClass. " + warnMessage, permissionsClass, defaultPermissionsClass);
159                 permissionsClass = defaultPermissionsClass;
160             } else { // try to instantiate
161                 Classes.getClassFactory().newInstance(permissionsClass, new LinkedList<Permission>(), session, configuration);
162                 log.info("Using {} for resolving permissions.", permissionsClass);
163             }
164         } catch (ClassNotFoundException e) {
165             log.warn("The class '{}' doesn't exist. " + warnMessage, compiledPermissionsClass, defaultPermissionsClass);
166             permissionsClass = defaultPermissionsClass;
167 
168         } catch (MgnlInstantiationException e) {
169             log.warn("Cannot instantiate '{}'. The permissionClass must have constructor with exact same arguments like '{}'. Using the default permission class '{}' instead.", permissionsClass, defaultPermissionsClass);
170             permissionsClass = defaultPermissionsClass;
171 
172         } catch (Exception e) { // use default permission class if any exception occurs
173             log.warn("Cannot instantiate permissionsClass '{}'. " + warnMessage, permissionsClass, e);
174             permissionsClass = defaultPermissionsClass;
175         }
176     }
177 
178     @Override
179     public boolean isAcItem(ItemImpl item) throws RepositoryException {
180         log.debug("isAcItem({})", item);
181         return super.isAcItem(item);
182     }
183 
184     @Override
185     public boolean isAcItem(Path absPath) throws RepositoryException {
186         log.debug("isAcItem({})", absPath);
187         return super.isAcItem(absPath);
188     }
189 
190     private String printUserNames(Set<Principal> principals) {
191         StringBuilder sb = new StringBuilder();
192         for (Principal p : principals) {
193             sb.append(" or ").append(p.getName()).append("[").append(p.getClass().getName()).append("]");
194         }
195         sb.delete(0, 4);
196         return sb.toString();
197     }
198 
199     private List<Permission> addJcrSystemReadPermissions(List<Permission> permissions) {
200         Permission permission = new PermissionImpl();
201         permission.setPattern(new SimpleUrlPattern("/jcr:system"));
202         permission.setPermissions(Permission.READ);
203         permissions.add(permission);
204         permission = new PermissionImpl();
205         permission.setPattern(new SimpleUrlPattern("/jcr:system/*"));
206         permission.setPermissions(Permission.READ);
207         permissions.add(permission);
208         return permissions;
209     }
210 }