View Javadoc

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