001/* 002 * (C) Copyright 2018 Nuxeo (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 * Thomas Roger 018 */ 019 020package org.nuxeo.wopi.jaxrs; 021 022import static org.nuxeo.wopi.Constants.WOPI_BASE_URL_PROPERTY; 023import static org.nuxeo.wopi.Headers.PROOF; 024import static org.nuxeo.wopi.Headers.PROOF_OLD; 025import static org.nuxeo.wopi.Headers.TIMESTAMP; 026 027import javax.ws.rs.Path; 028import javax.ws.rs.PathParam; 029import javax.ws.rs.core.Context; 030import javax.ws.rs.core.HttpHeaders; 031 032import org.nuxeo.ecm.core.api.Blob; 033import org.nuxeo.ecm.core.api.CoreSession; 034import org.nuxeo.ecm.core.api.DocumentModel; 035import org.nuxeo.ecm.core.api.DocumentRef; 036import org.nuxeo.ecm.core.api.IdRef; 037import org.nuxeo.ecm.platform.web.common.RequestContext; 038import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper; 039import org.nuxeo.ecm.webengine.model.WebContext; 040import org.nuxeo.ecm.webengine.model.WebObject; 041import org.nuxeo.ecm.webengine.model.impl.ModuleRoot; 042import org.nuxeo.runtime.api.Framework; 043import org.nuxeo.wopi.Constants; 044import org.nuxeo.wopi.FileInfo; 045import org.nuxeo.wopi.Helpers; 046import org.nuxeo.wopi.WOPIService; 047import org.nuxeo.wopi.exception.InternalServerErrorException; 048import org.nuxeo.wopi.exception.NotFoundException; 049import org.nuxeo.wopi.lock.LockHelper; 050 051/** 052 * @since 10.3 053 */ 054@Path("/") 055@WebObject(type = "wopi") 056public class WOPIRoot extends ModuleRoot { 057 058 protected static final String THREAD_NAME_PREFIX = "WOPI_"; 059 060 @Context 061 protected HttpHeaders httpHeaders; 062 063 @Path("/files/{fileId}") 064 public Object filesResource(@PathParam("fileId") FileInfo fileInfo) { 065 // prefix thread name for logging purpose 066 prefixThreadName(); 067 068 // verify proof key 069 verifyProofKey(); 070 071 // flag the request as originating from a WOPI client for locking policy purpose 072 LockHelper.flagWOPIRequest(); 073 RequestContext.getActiveContext(request).addRequestCleanupHandler(req -> LockHelper.unflagWOPIRequest()); 074 075 WebContext context = getContext(); 076 context.setRepositoryName(fileInfo.repositoryName); 077 CoreSession session = context.getCoreSession(); 078 DocumentModel doc = getDocument(session, fileInfo.docId); 079 Blob blob = getBlob(doc, fileInfo.xpath); 080 return newObject("wopiFiles", session, doc, blob, fileInfo.xpath); 081 } 082 083 protected void prefixThreadName() { 084 Thread currentThread = Thread.currentThread(); 085 String threadName = currentThread.getName(); 086 if (!threadName.startsWith(THREAD_NAME_PREFIX)) { 087 currentThread.setName(THREAD_NAME_PREFIX + threadName); 088 } 089 } 090 091 protected void verifyProofKey() { 092 String proofKeyHeader = Helpers.getHeader(httpHeaders, PROOF); 093 String oldProofKeyHeader = Helpers.getHeader(httpHeaders, PROOF_OLD); 094 String timestampHeader = Helpers.getHeader(httpHeaders, TIMESTAMP); 095 String accessToken = request.getParameter(Constants.ACCESS_TOKEN_PARAMETER); 096 String wopiBaseURL = Framework.getProperty(WOPI_BASE_URL_PROPERTY, VirtualHostHelper.getBaseURL(request)); 097 // remove /nuxeo/ from the request URI 098 String requestURI = request.getRequestURI().substring(request.getContextPath().length() + 1); 099 String url = String.format("%s%s?%s", wopiBaseURL, requestURI, request.getQueryString()); 100 if (!Framework.getService(WOPIService.class) 101 .verifyProofKey(proofKeyHeader, oldProofKeyHeader, url, accessToken, timestampHeader)) { 102 throw new InternalServerErrorException(); 103 } 104 } 105 106 protected DocumentModel getDocument(CoreSession session, String fileId) { 107 DocumentRef ref = new IdRef(fileId); 108 if (!session.exists(ref)) { 109 throw new NotFoundException(); 110 } 111 return session.getDocument(ref); 112 } 113 114 protected Blob getBlob(DocumentModel doc, String xpath) { 115 Blob blob = Helpers.getEditableBlob(doc, xpath); 116 if (blob == null) { 117 throw new NotFoundException(); 118 } 119 return blob; 120 } 121 122}