001/*
002 * (C) Copyright 2006-2017 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 *     Mincong Huang
019 */
020package org.nuxeo.ecm.core.opencmis.impl.server;
021
022import static org.apache.chemistry.opencmis.commons.BasicPermissions.ALL;
023import static org.apache.chemistry.opencmis.commons.BasicPermissions.READ;
024import static org.apache.chemistry.opencmis.commons.BasicPermissions.WRITE;
025import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_POLICY_OBJECT;
026import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_POLICY_POLICY;
027import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_TO_FOLDER_FOLDER;
028import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_ADD_TO_FOLDER_OBJECT;
029import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_APPLY_ACL_OBJECT;
030import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT;
031import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CHECKIN_DOCUMENT;
032import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CHECKOUT_DOCUMENT;
033import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER;
034import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_FOLDER_FOLDER;
035import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_RELATIONSHIP_SOURCE;
036import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_CREATE_RELATIONSHIP_TARGET;
037import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT;
038import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_OBJECT;
039import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_DELETE_TREE_FOLDER;
040import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_ACL_OBJECT;
041import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES;
042import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_APPLIED_POLICIES_OBJECT;
043import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_CHILDREN_FOLDER;
044import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_DESCENDENTS_FOLDER;
045import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT;
046import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT;
047import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_PARENTS_FOLDER;
048import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_GET_PROPERTIES_OBJECT;
049import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_OBJECT;
050import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_SOURCE;
051import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_MOVE_TARGET;
052import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_FROM_FOLDER_FOLDER;
053import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_FROM_FOLDER_OBJECT;
054import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_POLICY_OBJECT;
055import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_REMOVE_POLICY_POLICY;
056import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_SET_CONTENT_DOCUMENT;
057import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT;
058import static org.apache.chemistry.opencmis.commons.data.PermissionMapping.CAN_VIEW_CONTENT_OBJECT;
059
060import java.util.ArrayList;
061import java.util.Arrays;
062import java.util.Collections;
063import java.util.HashMap;
064import java.util.HashSet;
065import java.util.LinkedList;
066import java.util.List;
067import java.util.Map;
068import java.util.Map.Entry;
069import java.util.Set;
070
071import javax.servlet.http.HttpServletRequest;
072
073import org.apache.chemistry.opencmis.commons.data.ExtensionFeature;
074import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
075import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
076import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
077import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
078import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
079import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
080import org.apache.chemistry.opencmis.commons.enums.CapabilityAcl;
081import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
082import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
083import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
084import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
085import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
086import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
087import org.apache.chemistry.opencmis.commons.enums.SupportedPermissions;
088import org.apache.chemistry.opencmis.commons.impl.dataobjects.AclCapabilitiesDataImpl;
089import org.apache.chemistry.opencmis.commons.impl.dataobjects.CreatablePropertyTypesImpl;
090import org.apache.chemistry.opencmis.commons.impl.dataobjects.NewTypeSettableAttributesImpl;
091import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionDefinitionDataImpl;
092import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionMappingDataImpl;
093import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl;
094import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl;
095import org.apache.chemistry.opencmis.commons.server.CallContext;
096
097import org.nuxeo.common.Environment;
098import org.nuxeo.ecm.core.api.repository.FulltextConfiguration;
099import org.nuxeo.ecm.core.api.security.PermissionProvider;
100import org.nuxeo.ecm.core.api.security.SecurityConstants;
101import org.nuxeo.ecm.core.opencmis.impl.util.TypeManagerImpl;
102import org.nuxeo.ecm.core.repository.RepositoryService;
103import org.nuxeo.ecm.core.schema.DocumentType;
104import org.nuxeo.ecm.core.schema.SchemaManager;
105import org.nuxeo.ecm.core.schema.types.CompositeType;
106import org.nuxeo.ecm.core.security.DefaultPermissionProvider;
107import org.nuxeo.ecm.core.security.PermissionVisibilityDescriptor;
108import org.nuxeo.runtime.api.Framework;
109
110/**
111 * Information about a Nuxeo repository.
112 */
113public class NuxeoRepository {
114
115    /**
116     * @deprecated Since 7.10. Use {@link Environment#DISTRIBUTION_VERSION}
117     */
118    @Deprecated
119    public static final String NUXEO_VERSION_PROP = Environment.DISTRIBUTION_VERSION;
120
121    public static final String NUXEO_URL_PROP = "nuxeo.url";
122
123    public static final String SUPPORTS_JOINS_PROP = "org.nuxeo.cmis.joins";
124
125    public static final String SUPPORTS_PROXIES_PROP = "org.nuxeo.cmis.proxies";
126
127    public static final String ELASTICSEARCH_PROP = "org.nuxeo.cmis.elasticsearch";
128
129    /**
130     * Key of the configuration property {@code "org.nuxeo.cmis.relaxSpec"}, default to {@code false}. Setting this
131     * property to {@code true} allows users to relax the CMIS specification 1.1 and use customized CMISQL. Please be
132     * aware the risk of doing so. It will potentially cause query parsing error.
133     * <p>
134     * The relax mode does not follow the CMIS specification 1.1, section 2.1.14.2.4.4, where at most one
135     * {@code CONTAINS()} function MUST be included in a single query statement. Currently, such mode only works for
136     * CMIS query having no {@code JOIN} predicate.
137     *
138     * @see <a href="https://jira.nuxeo.com/browse/NXP-19858">NXP-19858</a>
139     */
140    public static final String RELAX_CMIS_SPEC = "org.nuxeo.cmis.relaxSpec";
141
142    private static final String NUXEO_CONTEXT_PATH_PROP = "org.nuxeo.ecm.contextPath";
143
144    private static final String NUXEO_CONTEXT_PATH_DEFAULT = "/nuxeo";
145
146    private static final String X_FORWARDED_HOST = "x-forwarded-host";
147
148    private static final String NUXEO_VH_HEADER = "nuxeo-virtual-host";
149
150    private static final String VH_PARAM = "nuxeo.virtual.host";
151
152    public static final String NUXEO_READ_REMOVE = "ReadRemove";
153
154    protected final String repositoryId;
155
156    protected final String rootFolderId;
157
158    protected boolean supportsJoins;
159
160    protected boolean supportsProxies = true;
161
162    protected boolean useElasticsearch;
163
164    protected boolean repositoryFulltextSearchDisabled;
165
166    protected Map<CmisVersion, TypeManagerImpl> typeManagerByCmisVersion = new HashMap<>();
167
168    public NuxeoRepository(String repositoryId, String rootFolderId) {
169        this.repositoryId = repositoryId;
170        this.rootFolderId = rootFolderId;
171        if (Framework.isBooleanPropertyTrue(SUPPORTS_JOINS_PROP)) {
172            setSupportsJoins(true);
173        }
174        if (Framework.isBooleanPropertyFalse(SUPPORTS_PROXIES_PROP)) {
175            setSupportsProxies(false);
176        }
177        if (Framework.isBooleanPropertyTrue(ELASTICSEARCH_PROP)) {
178            setUseElasticsearch(true);
179        }
180        FulltextConfiguration fulltextConfiguration = Framework.getService(RepositoryService.class)
181                                                    .getRepository(repositoryId)
182                                                    .getFulltextConfiguration();
183        repositoryFulltextSearchDisabled = fulltextConfiguration == null
184                || fulltextConfiguration.fulltextSearchDisabled;
185    }
186
187    public void setSupportsJoins(boolean supportsJoins) {
188        this.supportsJoins = supportsJoins;
189    }
190
191    public boolean supportsJoins() {
192        return supportsJoins;
193    }
194
195    public void setSupportsProxies(boolean supportsProxies) {
196        this.supportsProxies = supportsProxies;
197    }
198
199    public boolean supportsProxies() {
200        return supportsProxies;
201    }
202
203    public void setUseElasticsearch(boolean useElasticsearch) {
204        this.useElasticsearch = useElasticsearch;
205    }
206
207    public boolean useElasticsearch() {
208        return useElasticsearch;
209    }
210
211    public boolean hasRepositoryFulltextSearchDisabled() {
212        return repositoryFulltextSearchDisabled;
213    }
214
215    public String getId() {
216        return repositoryId;
217    }
218
219    // no need to have it synchronized
220    public TypeManagerImpl getTypeManager(CmisVersion cmisVersion) {
221        TypeManagerImpl typeManager = typeManagerByCmisVersion.get(cmisVersion);
222        if (typeManager == null) {
223            typeManager = initializeTypes(cmisVersion);
224            typeManagerByCmisVersion.put(cmisVersion, typeManager);
225        }
226        return typeManager;
227    }
228
229    protected TypeManagerImpl initializeTypes(CmisVersion cmisVersion) {
230        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
231        // scan the types to find super/inherited relationships
232        Map<String, List<String>> typesChildren = new HashMap<>();
233        for (DocumentType dt : schemaManager.getDocumentTypes()) {
234            org.nuxeo.ecm.core.schema.types.Type st = dt.getSuperType();
235            if (st == null) {
236                continue;
237            }
238            String name = st.getName();
239            List<String> siblings = typesChildren.get(name);
240            if (siblings == null) {
241                siblings = new LinkedList<>();
242                typesChildren.put(name, siblings);
243            }
244            siblings.add(dt.getName());
245        }
246        // convert the transitive closure for Folder and Document subtypes
247        Set<String> done = new HashSet<>();
248        TypeManagerImpl typeManager = new TypeManagerImpl();
249        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_DOCUMENT, schemaManager, cmisVersion));
250        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_FOLDER, schemaManager, cmisVersion));
251        typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_RELATIONSHIP, schemaManager, cmisVersion));
252        if (cmisVersion != CmisVersion.CMIS_1_0) {
253            typeManager.addTypeDefinition(NuxeoTypeHelper.constructCmisBase(BaseTypeId.CMIS_SECONDARY, schemaManager, cmisVersion));
254        }
255        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_DOCUMENT, typesChildren, done, schemaManager, cmisVersion);
256        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_FOLDER, typesChildren, done, schemaManager, cmisVersion);
257        addTypesRecursively(typeManager, NuxeoTypeHelper.NUXEO_RELATION, typesChildren, done, schemaManager, cmisVersion);
258        if (cmisVersion != CmisVersion.CMIS_1_0) {
259            addSecondaryTypes(typeManager, schemaManager, cmisVersion);
260        }
261        return typeManager;
262    }
263
264    protected void addTypesRecursively(TypeManagerImpl typeManager, String name,
265            Map<String, List<String>> typesChildren, Set<String> done, SchemaManager schemaManager,
266            CmisVersion cmisVersion) {
267        if (done.contains(name)) {
268            return;
269        }
270        done.add(name);
271        DocumentType dt = schemaManager.getDocumentType(name);
272        String parentTypeId = NuxeoTypeHelper.getParentTypeId(dt);
273        if (parentTypeId != null) {
274            TypeDefinitionContainer parentType = typeManager.getTypeById(parentTypeId);
275            if (parentType == null) {
276                // if parent was ignored, reparent under cmis:document
277                parentTypeId = BaseTypeId.CMIS_DOCUMENT.value();
278            } else {
279                if (parentType.getTypeDefinition().getBaseTypeId() != BaseTypeId.CMIS_FOLDER && dt.isFolder()) {
280                    // reparent Folderish but child of Document under
281                    // cmis:folder
282                    parentTypeId = BaseTypeId.CMIS_FOLDER.value();
283                }
284            }
285            typeManager.addTypeDefinition(NuxeoTypeHelper.constructDocumentType(dt, parentTypeId, cmisVersion));
286        }
287        // recurse in children
288        List<String> children = typesChildren.get(name);
289        if (children == null) {
290            return;
291        }
292        for (String sub : children) {
293            addTypesRecursively(typeManager, sub, typesChildren, done, schemaManager, cmisVersion);
294        }
295    }
296
297    protected void addSecondaryTypes(TypeManagerImpl typeManager, SchemaManager schemaManager,
298            CmisVersion cmisVersion) {
299        for (CompositeType type : schemaManager.getFacets()) {
300            typeManager.addTypeDefinition(NuxeoTypeHelper.constructSecondaryType(type, cmisVersion), false);
301        }
302    }
303
304    public String getRootFolderId() {
305        return rootFolderId;
306    }
307
308    public RepositoryInfo getRepositoryInfo(String latestChangeLogToken, CallContext callContext) {
309        CmisVersion cmisVersion = callContext.getCmisVersion();
310
311        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
312        repositoryInfo.setId(repositoryId);
313        repositoryInfo.setName("Nuxeo Repository " + repositoryId);
314        repositoryInfo.setDescription("Nuxeo Repository " + repositoryId);
315        repositoryInfo.setCmisVersionSupported(cmisVersion.value());
316        repositoryInfo.setPrincipalAnonymous("Guest"); // TODO
317        repositoryInfo.setPrincipalAnyone(SecurityConstants.EVERYONE);
318        repositoryInfo.setThinClientUri(getBaseURL(callContext));
319        repositoryInfo.setChangesIncomplete(Boolean.FALSE);
320        repositoryInfo.setChangesOnType(Arrays.asList(BaseTypeId.CMIS_DOCUMENT, BaseTypeId.CMIS_FOLDER));
321        repositoryInfo.setLatestChangeLogToken(latestChangeLogToken);
322        repositoryInfo.setVendorName("Nuxeo");
323        repositoryInfo.setProductName("Nuxeo OpenCMIS Connector");
324        String version = Framework.getProperty(Environment.DISTRIBUTION_VERSION, "5.5 dev");
325        repositoryInfo.setProductVersion(version);
326        repositoryInfo.setRootFolder(rootFolderId);
327        repositoryInfo.setExtensionFeature(Collections.<ExtensionFeature> emptyList());
328
329        // capabilities
330
331        RepositoryCapabilitiesImpl caps = new RepositoryCapabilitiesImpl();
332        caps.setAllVersionsSearchable(Boolean.TRUE);
333        caps.setCapabilityAcl(CapabilityAcl.MANAGE);
334        caps.setCapabilityChanges(CapabilityChanges.OBJECTIDSONLY);
335        caps.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.PWCONLY);
336        caps.setCapabilityJoin(supportsJoins ? CapabilityJoin.INNERANDOUTER : CapabilityJoin.NONE);
337        caps.setCapabilityQuery(CapabilityQuery.BOTHCOMBINED);
338        caps.setCapabilityRendition(CapabilityRenditions.READ);
339        caps.setIsPwcSearchable(Boolean.TRUE);
340        caps.setIsPwcUpdatable(Boolean.TRUE);
341        caps.setSupportsGetDescendants(Boolean.TRUE);
342        caps.setSupportsGetFolderTree(Boolean.TRUE);
343        caps.setSupportsMultifiling(Boolean.FALSE);
344        caps.setSupportsUnfiling(Boolean.FALSE);
345        caps.setSupportsVersionSpecificFiling(Boolean.FALSE);
346        caps.setNewTypeSettableAttributes(new NewTypeSettableAttributesImpl());
347        caps.setCreatablePropertyTypes(new CreatablePropertyTypesImpl());
348        repositoryInfo.setCapabilities(caps);
349
350        // ACL capabilities
351
352        AclCapabilitiesDataImpl aclCaps = new AclCapabilitiesDataImpl();
353        aclCaps.setAclPropagation(AclPropagation.PROPAGATE);
354        aclCaps.setSupportedPermissions(SupportedPermissions.REPOSITORY);
355
356        List<PermissionDefinition> permDefs = new ArrayList<>();
357        addPermissionDefinitions(permDefs);
358        aclCaps.setPermissionDefinitionData(permDefs);
359
360        Map<String, PermissionMapping> permMap = new HashMap<>();
361        addPermissionMapping(permMap, CAN_GET_DESCENDENTS_FOLDER, READ);
362        addPermissionMapping(permMap, CAN_GET_CHILDREN_FOLDER, READ);
363        addPermissionMapping(permMap, CAN_GET_PARENTS_FOLDER, READ);
364        addPermissionMapping(permMap, CAN_GET_FOLDER_PARENT_OBJECT, READ);
365        addPermissionMapping(permMap, CAN_CREATE_DOCUMENT_FOLDER, WRITE);
366        addPermissionMapping(permMap, CAN_CREATE_FOLDER_FOLDER, WRITE);
367        // no CAN_CREATE_POLICY_FOLDER due to spec bug
368        addPermissionMapping(permMap, CAN_CREATE_RELATIONSHIP_SOURCE, READ);
369        addPermissionMapping(permMap, CAN_CREATE_RELATIONSHIP_TARGET, READ);
370        addPermissionMapping(permMap, CAN_GET_PROPERTIES_OBJECT, READ);
371        addPermissionMapping(permMap, CAN_VIEW_CONTENT_OBJECT, READ);
372        addPermissionMapping(permMap, CAN_UPDATE_PROPERTIES_OBJECT, WRITE);
373        addPermissionMapping(permMap, CAN_MOVE_OBJECT, WRITE);
374        addPermissionMapping(permMap, CAN_MOVE_TARGET, WRITE);
375        addPermissionMapping(permMap, CAN_MOVE_SOURCE, WRITE);
376        addPermissionMapping(permMap, CAN_DELETE_OBJECT, WRITE);
377        addPermissionMapping(permMap, CAN_DELETE_TREE_FOLDER, WRITE);
378        addPermissionMapping(permMap, CAN_SET_CONTENT_DOCUMENT, WRITE);
379        addPermissionMapping(permMap, CAN_DELETE_CONTENT_DOCUMENT, WRITE);
380        addPermissionMapping(permMap, CAN_ADD_TO_FOLDER_OBJECT, WRITE);
381        addPermissionMapping(permMap, CAN_ADD_TO_FOLDER_FOLDER, WRITE);
382        addPermissionMapping(permMap, CAN_REMOVE_FROM_FOLDER_OBJECT, WRITE);
383        addPermissionMapping(permMap, CAN_REMOVE_FROM_FOLDER_FOLDER, WRITE);
384        addPermissionMapping(permMap, CAN_CHECKOUT_DOCUMENT, WRITE);
385        addPermissionMapping(permMap, CAN_CANCEL_CHECKOUT_DOCUMENT, WRITE);
386        addPermissionMapping(permMap, CAN_CHECKIN_DOCUMENT, WRITE);
387        addPermissionMapping(permMap, CAN_GET_ALL_VERSIONS_VERSION_SERIES, READ);
388        addPermissionMapping(permMap, CAN_GET_OBJECT_RELATIONSHIPS_OBJECT, READ);
389        addPermissionMapping(permMap, CAN_ADD_POLICY_OBJECT, WRITE);
390        addPermissionMapping(permMap, CAN_ADD_POLICY_POLICY, WRITE);
391        addPermissionMapping(permMap, CAN_REMOVE_POLICY_OBJECT, WRITE);
392        addPermissionMapping(permMap, CAN_REMOVE_POLICY_POLICY, WRITE);
393        addPermissionMapping(permMap, CAN_GET_APPLIED_POLICIES_OBJECT, READ);
394        addPermissionMapping(permMap, CAN_GET_ACL_OBJECT, READ);
395        addPermissionMapping(permMap, CAN_APPLY_ACL_OBJECT, ALL);
396        aclCaps.setPermissionMappingData(permMap);
397
398        repositoryInfo.setAclCapabilities(aclCaps);
399
400        return repositoryInfo;
401    }
402
403    @SuppressWarnings("unchecked")
404    protected static void addPermissionDefinitions(List<PermissionDefinition> permDefs) {
405        addPermissionDefinition(permDefs, READ, "Read"); // = Nuxeo Read
406        addPermissionDefinition(permDefs, WRITE, "Write"); // = Nuxeo ReadWrite
407        addPermissionDefinition(permDefs, ALL, "All"); // = Nuxeo Everything
408        addPermissionDefinition(permDefs, NUXEO_READ_REMOVE, "Remove");
409
410        Set<String> done = new HashSet<>();
411        done.add(SecurityConstants.READ);
412        done.add(SecurityConstants.READ_WRITE);
413        done.add(SecurityConstants.EVERYTHING);
414        done.add(NUXEO_READ_REMOVE);
415
416        /*
417         * Add Nuxeo-specific permissions registered through the permissionsVisibility extension point.
418         */
419
420        DefaultPermissionProvider permissionProvider = (DefaultPermissionProvider) Framework.getService(PermissionProvider.class);
421        permissionProvider.getUserVisiblePermissionDescriptors(); // init var
422        Map<String, PermissionVisibilityDescriptor> map = permissionProvider.mergedPermissionsVisibility;
423        // iterate for all types regisited, not just the default ""
424        for (Entry<String, PermissionVisibilityDescriptor> en : map.entrySet()) {
425            for (String permission : en.getValue().getSortedItems()) {
426                if (!done.add(permission)) {
427                    continue;
428                }
429                addPermissionDefinition(permDefs, permission, permission);
430            }
431        }
432    }
433
434    protected static void addPermissionDefinition(List<PermissionDefinition> permDefs, String permission,
435            String description) {
436        PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
437        pd.setId(permission);
438        pd.setDescription(description);
439        permDefs.add(pd);
440    }
441
442    protected static void addPermissionMapping(Map<String, PermissionMapping> permMap, String key, String permission) {
443        PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
444        pm.setKey(key);
445        pm.setPermissions(Collections.singletonList(permission));
446        permMap.put(key, pm);
447    }
448
449    /** Returns the server base URL (including context). */
450    private static String getBaseURL(CallContext callContext) {
451        HttpServletRequest request = (HttpServletRequest) callContext.get(CallContext.HTTP_SERVLET_REQUEST);
452        if (request != null) {
453            String baseURL = getServerURL(request);
454            String contextPath = request.getContextPath();
455            if (contextPath == null) {
456                contextPath = Framework.getProperty(NUXEO_CONTEXT_PATH_PROP, NUXEO_CONTEXT_PATH_DEFAULT);
457            }
458            // add context path
459            return baseURL + contextPath + '/';
460        } else {
461            return Framework.getProperty(NUXEO_URL_PROP);
462        }
463    }
464
465    /**
466     * Returns the server URL according to virtual hosting headers (without trailing slash).
467     */
468    private static String getServerURL(HttpServletRequest request) {
469        String url = null;
470        // Detect Nuxeo specific header for VH
471        String nuxeoVH = request.getHeader(NUXEO_VH_HEADER);
472        if (nuxeoVH == null) {
473            nuxeoVH = Framework.getProperty(VH_PARAM);
474        }
475        if (nuxeoVH != null && nuxeoVH.startsWith("http")) {
476            url = nuxeoVH;
477        } else {
478            // default values
479            String scheme = request.getScheme();
480            String serverName = request.getServerName();
481            int serverPort = request.getServerPort();
482            // Detect virtual hosting based in standard header
483            String forwardedHost = request.getHeader(X_FORWARDED_HOST);
484            if (forwardedHost != null) {
485                if (forwardedHost.contains(":")) {
486                    String[] split = forwardedHost.split(":");
487                    serverName = split[0];
488                    serverPort = Integer.parseInt(split[1]);
489                } else {
490                    serverName = forwardedHost;
491                    serverPort = 80; // fallback
492                }
493            }
494            url = buildURL(scheme, serverName, serverPort);
495        }
496        // strip trailing slash
497        if (url.endsWith("/")) {
498            url = url.substring(0, url.length() - 1);
499        }
500        return url;
501    }
502
503    /** Builds an URL (without trailing slash). */
504    private static String buildURL(String scheme, String serverName, int serverPort) {
505        StringBuilder sb = new StringBuilder();
506        sb.append(scheme);
507        sb.append("://");
508        sb.append(serverName);
509        if (serverPort != 0) {
510            if ("http".equals(scheme) && serverPort != 80 || "https".equals(scheme) && serverPort != 443) {
511                sb.append(':');
512                sb.append(serverPort);
513            }
514        }
515        return sb.toString();
516    }
517
518}