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;
021
022import static java.nio.charset.StandardCharsets.UTF_8;
023import static org.nuxeo.wopi.Constants.ACCESS_TOKEN_PARAMETER;
024import static org.nuxeo.wopi.Constants.JWT_TOKEN_TTL;
025import static org.nuxeo.wopi.Constants.WOPI_SERVLET_PATH;
026
027import java.nio.charset.Charset;
028import java.util.List;
029import java.util.Map;
030
031import javax.servlet.http.HttpServletRequest;
032import javax.ws.rs.core.HttpHeaders;
033
034import org.apache.commons.lang3.StringUtils;
035import org.apache.logging.log4j.LogManager;
036import org.apache.logging.log4j.Logger;
037import org.nuxeo.ecm.core.api.Blob;
038import org.nuxeo.ecm.core.api.DocumentModel;
039import org.nuxeo.ecm.core.api.NuxeoPrincipal;
040import org.nuxeo.ecm.core.api.PropertyException;
041import org.nuxeo.ecm.core.blob.BlobManager;
042import org.nuxeo.ecm.core.blob.BlobProvider;
043import org.nuxeo.ecm.jwt.JWTClaims;
044import org.nuxeo.ecm.jwt.JWTService;
045import org.nuxeo.runtime.api.Framework;
046import org.nuxeo.wopi.exception.UnauthorizedException;
047
048/**
049 * @since 10.3
050 */
051public class Helpers {
052
053    private static final Logger log = LogManager.getLogger(Helpers.class);
054
055    public static final Charset UTF_7 = new com.beetstra.jutf7.CharsetProvider().charsetForName("UTF-7");
056
057    private Helpers() {
058        // helper class
059    }
060
061    public static String readUTF7String(String s) {
062        byte[] bytes = s.getBytes(UTF_8);
063        return new String(bytes, UTF_7);
064    }
065
066    public static String createJWTToken() {
067        return Framework.getService(JWTService.class).newBuilder().withTTL(JWT_TOKEN_TTL).build();
068    }
069
070    public static String getJWTToken(HttpServletRequest request) {
071        String token = request.getParameter(ACCESS_TOKEN_PARAMETER);
072        if (token == null || Framework.getService(JWTService.class).verifyToken(token) == null) {
073            throw new UnauthorizedException();
074        }
075        return token;
076    }
077
078    public static long getJWTTokenExp(String token) {
079        Map<String, Object> claims = Framework.getService(JWTService.class).verifyToken(token);
080        long expireAt = (long) claims.get(JWTClaims.CLAIM_EXPIRES_AT);
081        return expireAt * 1000; // milliseconds
082    }
083
084    // copied from org.nuxeo.ecm.platform.ui.web.tag.fn.Functions which lives in nuxeo-platform-ui-web
085    public static String principalFullName(NuxeoPrincipal principal) {
086        String first = principal.getFirstName();
087        String last = principal.getLastName();
088        return userDisplayName(principal.getName(), first, last);
089    }
090
091    public static Blob getEditableBlob(DocumentModel doc, String xpath) {
092        Blob blob = null;
093        try {
094            blob = (Blob) doc.getPropertyValue(xpath);
095        } catch (PropertyException e) {
096            // prevent server error
097        }
098        if (blob == null) {
099            log.debug("Blobs: repository={} docId={} xpath={} Cannot find blob", doc::getRepositoryName, doc::getId,
100                    () -> xpath);
101            return null;
102        }
103        // ignore blob providers that don't support sync
104        if (!supportsSync(blob)) {
105            log.debug(
106                    "Blobs: repository={} docId={} xpath={} Ignoring blob as it is backed by a BlobProvider preventing sync",
107                    doc::getRepositoryName, doc::getId, () -> xpath);
108            return null;
109        }
110        return blob;
111    }
112
113    /**
114     * @deprecated since 11.1, use {@link #supportsSync} (with opposite semantics) instead
115     */
116    @Deprecated
117    protected static boolean isExternalBlobProvider(Blob blob) {
118        BlobManager blobManager = Framework.getService(BlobManager.class);
119        BlobProvider blobProvider = blobManager.getBlobProvider(blob);
120        return blobProvider != null && (!blobProvider.supportsUserUpdate() || blobProvider.getBinaryManager() == null);
121    }
122
123    protected static boolean supportsSync(Blob blob) {
124        BlobManager blobManager = Framework.getService(BlobManager.class);
125        BlobProvider blobProvider = blobManager.getBlobProvider(blob);
126        return blobProvider != null && blobProvider.supportsSync();
127    }
128
129    public static String getWOPIURL(String baseURL, String action, DocumentModel doc, String xpath) {
130        return String.format("%s%s/%s/%s/%s/%s", baseURL, WOPI_SERVLET_PATH, action, doc.getRepositoryName(),
131                doc.getId(), xpath);
132    }
133
134    protected static String userDisplayName(String id, String first, String last) {
135        if (StringUtils.isEmpty(first)) {
136            if (StringUtils.isEmpty(last)) {
137                return id;
138            } else {
139                return last;
140            }
141        } else {
142            if (StringUtils.isEmpty(last)) {
143                return first;
144            } else {
145                return first + ' ' + last;
146            }
147        }
148    }
149
150    public static String getHeader(HttpHeaders httpHeaders, String headerName) {
151        List<String> headers = httpHeaders.getRequestHeader(headerName);
152        return headers == null || headers.isEmpty() ? null : headers.get(0);
153    }
154
155}