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