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