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