001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Antoine Taillefer <ataillefer@nuxeo.com>
016 */
017package org.nuxeo.drive.hierarchy.permission.factory;
018
019import java.security.Principal;
020import java.util.Map;
021
022import org.apache.commons.lang.StringUtils;
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.drive.adapter.FileSystemItem;
026import org.nuxeo.drive.adapter.FolderItem;
027import org.nuxeo.drive.adapter.impl.DefaultSyncRootFolderItem;
028import org.nuxeo.drive.service.FileSystemItemAdapterService;
029import org.nuxeo.drive.service.FileSystemItemFactory;
030import org.nuxeo.drive.service.impl.AbstractSyncRootFolderItemFactory;
031import org.nuxeo.ecm.core.api.CoreSession;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.NuxeoException;
034import org.nuxeo.ecm.core.api.security.SecurityConstants;
035import org.nuxeo.runtime.api.Framework;
036
037/**
038 * Permission based implementation of {@link FileSystemItemFactory} for a synchronization root {@link FolderItem}.
039 *
040 * @author Antoine Taillefer
041 */
042public class PermissionSyncRootFactory extends AbstractSyncRootFolderItemFactory {
043
044    private static final Log log = LogFactory.getLog(PermissionSyncRootFactory.class);
045
046    protected static final String REQUIRED_PERMISSION_PARAM = "requiredPermission";
047
048    protected static final String USER_SYNC_ROOT_PARENT_FACTORY_PARAM = "userSyncRootParentFactory";
049
050    protected static final String SHARED_SYNC_ROOT_PARENT_FACTORY_PARAM = "sharedSyncRootParentFactory";
051
052    // Required permission to include a folder as a synchronization root,
053    // default is ReadWrite
054    protected String requiredPermission = SecurityConstants.READ_WRITE;
055
056    protected String userSyncRootParentFactoryName;
057
058    protected String sharedSyncRootParentFactoryName;
059
060    /*------------------- AbstractFileSystemItemFactory ---------------------*/
061    @Override
062    public void handleParameters(Map<String, String> parameters) {
063        String requiredPermissionParam = parameters.get(REQUIRED_PERMISSION_PARAM);
064        if (!StringUtils.isEmpty(requiredPermissionParam)) {
065            requiredPermission = requiredPermissionParam;
066        }
067
068        String userSyncRootParentFactoryParam = parameters.get(USER_SYNC_ROOT_PARENT_FACTORY_PARAM);
069        if (StringUtils.isEmpty(userSyncRootParentFactoryParam)) {
070            throw new NuxeoException(
071                    String.format(
072                            "Factory %s has no %s parameter, please provide it in the factory contribution to set the name of the factory for the parent folder of the user's synchronization roots.",
073                            getName(), USER_SYNC_ROOT_PARENT_FACTORY_PARAM));
074        }
075        userSyncRootParentFactoryName = userSyncRootParentFactoryParam;
076
077        String sharedSyncRootParentFactoryParam = parameters.get(SHARED_SYNC_ROOT_PARENT_FACTORY_PARAM);
078        if (StringUtils.isEmpty(sharedSyncRootParentFactoryParam)) {
079            throw new NuxeoException(
080                    String.format(
081                            "Factory %s has no %s parameter, please provide it in the factory contribution to set the name of the factory for the parent folder of the user's shared synchronization roots.",
082                            getName(), SHARED_SYNC_ROOT_PARENT_FACTORY_PARAM));
083        }
084        sharedSyncRootParentFactoryName = sharedSyncRootParentFactoryParam;
085    }
086
087    /**
088     * Checks if the given {@link DocumentModel} is adaptable as a {@link FileSystemItem}. The permission
089     * synchronization root factory considers that a {@link DocumentModel} is adaptable as a {@link FileSystemItem} if:
090     * <ul>
091     * <li>It is Folderish</li>
092     * <li>AND it is not a version nor a proxy</li>
093     * <li>AND it is not HiddenInNavigation</li>
094     * <li>AND it is not in the "deleted" life cycle state, unless {@code includeDeleted} is true</li>
095     * <li>AND it is a synchronization root registered for the current user, unless {@code relaxSyncRootConstraint} is
096     * true</li>
097     * <li>AND the current user has the {@link #getRequiredPermission()} permission on the document</li>
098     * </ul>
099     */
100    @Override
101    public boolean isFileSystemItem(DocumentModel doc, boolean includeDeleted, boolean relaxSyncRootConstraint) {
102        // Check required permission
103        CoreSession session = doc.getCoreSession();
104        boolean hasRequiredPermission = session.hasPermission(doc.getRef(), requiredPermission);
105        if (!hasRequiredPermission) {
106            if (log.isDebugEnabled()) {
107                log.debug(String.format(
108                        "Required permission %s is not granted on document %s to user %s, it cannot be adapted as a FileSystemItem.",
109                        requiredPermission, doc.getId(), session.getPrincipal().getName()));
110            }
111            return false;
112        }
113        return super.isFileSystemItem(doc, includeDeleted, relaxSyncRootConstraint) && hasRequiredPermission;
114    }
115
116    @Override
117    protected FileSystemItem adaptDocument(DocumentModel doc, boolean forceParentId, FolderItem parentItem,
118            boolean relaxSyncRootConstraint) {
119        return new DefaultSyncRootFolderItem(name, parentItem, doc, relaxSyncRootConstraint);
120    }
121
122    /*------------------ AbstractSyncRootFolderItemFactory ------------------*/
123    @Override
124    protected FolderItem getParentItem(DocumentModel doc) {
125        Principal principal = doc.getCoreSession().getPrincipal();
126        String docCreator = (String) doc.getPropertyValue("dc:creator");
127        if (principal.getName().equals(docCreator)) {
128            FolderItem parent = getFileSystemAdapterService().getVirtualFolderItemFactory(userSyncRootParentFactoryName).getVirtualFolderItem(
129                    principal);
130            if (parent == null) {
131                throw new NuxeoException(String.format(
132                        "Cannot find the parent of document %s: virtual folder from factory %s.", doc.getId(),
133                        userSyncRootParentFactoryName));
134            }
135            return parent;
136        } else {
137            FolderItem parent = getFileSystemAdapterService().getVirtualFolderItemFactory(
138                    sharedSyncRootParentFactoryName).getVirtualFolderItem(principal);
139            if (parent == null) {
140                throw new NuxeoException(String.format(
141                        "Cannot find the parent of document %s: virtual folder from factory %s.", doc.getId(),
142                        sharedSyncRootParentFactoryName));
143            }
144            return parent;
145        }
146    }
147
148    protected FileSystemItemAdapterService getFileSystemAdapterService() {
149        return Framework.getLocalService(FileSystemItemAdapterService.class);
150    }
151
152}