io.personium.core.model.DavRsCmp.java Source code

Java tutorial

Introduction

Here is the source code for io.personium.core.model.DavRsCmp.java

Source

/**
 * personium.io
 * Copyright 2014 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.personium.core.model;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.codec.CharEncoding;
import org.apache.http.HttpStatus;
import org.apache.wink.webdav.model.Creationdate;
import org.apache.wink.webdav.model.Getcontentlength;
import org.apache.wink.webdav.model.Getcontenttype;
import org.apache.wink.webdav.model.Getlastmodified;
import org.apache.wink.webdav.model.Multistatus;
import org.apache.wink.webdav.model.ObjectFactory;
import org.apache.wink.webdav.model.Propertyupdate;
import org.apache.wink.webdav.model.Propfind;
import org.apache.wink.webdav.model.Resourcetype;
import org.apache.wink.webdav.model.WebDAVModelHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import io.personium.common.auth.token.Role;
import io.personium.common.utils.PersoniumCoreUtils;
import io.personium.core.PersoniumCoreAuthzException;
import io.personium.core.PersoniumCoreException;
import io.personium.core.auth.AccessContext;
import io.personium.core.auth.BoxPrivilege;
import io.personium.core.auth.OAuth2Helper;
import io.personium.core.auth.OAuth2Helper.AcceptableAuthScheme;
import io.personium.core.auth.Privilege;
import io.personium.core.model.jaxb.Acl;
import io.personium.core.model.jaxb.ObjectIo;
import io.personium.core.rs.box.DavCollectionResource;
import io.personium.core.rs.box.DavFileResource;
import io.personium.core.rs.box.NullResource;
import io.personium.core.rs.box.ODataSvcCollectionResource;
import io.personium.core.rs.box.PersoniumEngineSvcCollectionResource;
import io.personium.core.utils.ResourceUtils;

/**
 * JaxRS Resource ??????Dav?????.
 */
public class DavRsCmp {
    /**
     * Logger.
     */
    private static Logger log = LoggerFactory.getLogger(DavRsCmp.class);

    DavCmp davCmp;
    DavRsCmp parent;
    String pathName;
    ObjectFactory of;

    /**
     * constructor.
     * @param parent 
     * @param davCmp ??????????
     */
    public DavRsCmp(final DavRsCmp parent, final DavCmp davCmp) {
        this.parent = parent;
        this.davCmp = davCmp;
        this.of = new ObjectFactory();

        if (this.davCmp != null) {
            this.pathName = this.davCmp.getName();
        }
    }

    /**
     * ??????Jax-RS?.
     * @param nextPath ????
     * @param request 
     * @return ??Jax-RS
     */
    public Object nextPath(final String nextPath, final HttpServletRequest request) {

        // nextPath???????new ???
        if (this.davCmp == null) {
            return new NullResource(this, null, true);
        }
        DavCmp nextCmp = this.davCmp.getChild(nextPath);
        String type = nextCmp.getType();

        if (DavCmp.TYPE_NULL.equals(type)) {
            // ??
            if (DavCmp.TYPE_NULL.equals(this.davCmp.getType())) {
                // ???????????NullResorce
                return new NullResource(this, nextCmp, true);
            } else {
                return new NullResource(this, nextCmp, false);
            }
        } else if (DavCmp.TYPE_COL_WEBDAV.equals(type)) {
            return new DavCollectionResource(this, nextCmp);
        } else if (DavCmp.TYPE_DAV_FILE.equals(type)) {
            return new DavFileResource(this, nextCmp);
        } else if (DavCmp.TYPE_COL_ODATA.equals(type)) {
            return new ODataSvcCollectionResource(this, nextCmp);
        } else if (DavCmp.TYPE_COL_SVC.equals(type)) {
            return new PersoniumEngineSvcCollectionResource(this, nextCmp);
        }

        return null;
    }

    /**
     * returns the URL string of this resource.
     * @return URL String
     */
    public String getUrl() {
        // ????BoxResource??????BoxResource???????URL???
        return this.parent.getUrl() + "/" + this.pathName;
    }

    /**
     * returns the Cell which this resource belongs to.
     * @return Cell Object
     */
    public Cell getCell() {
        // ????BoxResource??????????Cell?????????BoxResource????????
        return this.parent.getCell();
    }

    /**
     * returns the Box which this resource belongs to.
     * @return Box Object
     */
    public Box getBox() {
        // ????BoxResource??????????Cell?????????BoxResource????????
        return this.parent.getBox();
    }

    /**
     * ???davCmp???.
     * @return davCmp
     */
    public DavCmp getDavCmp() {
        return this.davCmp;
    }

    /**
     * ???parent???.
     * @return DavRsCmp
     */
    public DavRsCmp getParent() {
        return this.parent;
    }

    /**
     * @return AccessContext
     */
    public AccessContext getAccessContext() {
        return this.parent.getAccessContext();
    }

    /**
     * @param etag string
     * @return true if given string matches  the stored Etag
     */
    public boolean matchesETag(String etag) {
        if (etag == null) {
            return false;
        }
        String storedEtag = this.davCmp.getEtag();
        String weakEtag = "W/" + storedEtag;
        return etag.equals(storedEtag) || etag.equals(weakEtag);
    }

    /**
     * Process a GET request.
     * @param ifNoneMatch ifNoneMatch header
     * @param rangeHeaderField range header
     * @return ResponseBuilder object
     */
    public final ResponseBuilder get(final String ifNoneMatch, final String rangeHeaderField) {
        // return "Not-Modified" if "If-None-Match" header matches.
        if (matchesETag(ifNoneMatch)) {
            return Response.notModified().header(HttpHeaders.ETAG, this.davCmp.getEtag());
        }
        return this.davCmp.get(rangeHeaderField);
    }

    /**
     * PROPFIND??. ???????.
     * @param requestBodyXml requestBody
     * @param depth Depth 
     * @param contentLength Content-Length
     * @param transferEncoding Transfer-Encoding
     * @param requiredForPropfind PROPFIND???Privilege
     * @param requiredForReadAcl ACL?????Privilege
     * @return Jax-RS 
     */
    public final Response doPropfind(final Reader requestBodyXml, final String depth, final Long contentLength,
            final String transferEncoding, final Privilege requiredForPropfind,
            final Privilege requiredForReadAcl) {

        // 
        this.checkAccessContext(this.getAccessContext(), requiredForPropfind);

        // ????ACL?Privilege????????ACL???
        boolean canAclRead = false;
        if (this.getAccessContext().isUnitUserToken()
                || this.hasPrivilege(this.getAccessContext(), requiredForReadAcl)) {
            canAclRead = true;
        }

        // ?? pf??
        Propfind propfind = null;
        if (ResourceUtils.hasApparentlyRequestBody(contentLength, transferEncoding)) {
            BufferedReader br = null;
            try {
                br = new BufferedReader(requestBodyXml);
                propfind = Propfind.unmarshal(br);
            } catch (Exception e1) {
                throw PersoniumCoreException.Dav.XML_ERROR.reason(e1);
            }
        } else {
            log.debug("Content-Length 0");
        }

        // Depth??? 0, 1
        // infinity?????????403??
        if ("infinity".equals(depth)) {
            throw PersoniumCoreException.Dav.PROPFIND_FINITE_DEPTH;
        } else if (depth == null) {
            throw PersoniumCoreException.Dav.INVALID_DEPTH_HEADER.params("null");
        } else if (!("0".equals(depth) || "1".equals(depth))) {
            throw PersoniumCoreException.Dav.INVALID_DEPTH_HEADER.params(depth);
        }

        String reqUri = this.getUrl();
        // ?/???????????
        if (reqUri.endsWith("/")) {
            reqUri = reqUri.substring(0, reqUri.length() - 1);
        }

        // ????????URL?
        int resourcePos = reqUri.lastIndexOf("/");
        if (resourcePos != -1) {
            String resourceName = reqUri.substring(resourcePos + 1);
            try {
                resourceName = URLEncoder.encode(resourceName, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.debug("UnsupportedEncodingException");
            }
            String collectionUrl = reqUri.substring(0, resourcePos);
            reqUri = collectionUrl + "/" + resourceName;
        }

        // ??
        final Multistatus ms = this.of.createMultistatus();
        List<org.apache.wink.webdav.model.Response> resList = ms.getResponse();
        resList.add(createDavResponse(pathName, reqUri, this.davCmp, propfind, canAclRead));

        // if Depth is not 0, then process children.
        if (!"0".equals(depth)) {
            Map<String, DavCmp> childrenMap = this.davCmp.getChildren();
            for (String childName : childrenMap.keySet()) {
                DavCmp child = childrenMap.get(childName);
                resList.add(createDavResponse(childName, reqUri + "/" + childName, child, propfind, canAclRead));
            }
        }

        // ??
        StreamingOutput str = new StreamingOutput() {
            @Override
            public void write(final OutputStream os) throws IOException {
                Multistatus.marshal(ms, os);
            }
        };
        return Response.status(HttpStatus.SC_MULTI_STATUS).header(HttpHeaders.ETAG, this.davCmp.getEtag())
                .header("Content-Type", "application/xml").entity(str).build();
    }

    /**
     * PROPPATCH??. ????????? ???????.
     * @param reqBodyXml requestBody
     * @return Jax-RS 
     */
    public final Response doProppatch(final Reader reqBodyXml) {

        // ?? pu ??
        BufferedReader br = null;
        Propertyupdate pu = null;
        try {
            br = new BufferedReader(reqBodyXml);
            pu = Propertyupdate.unmarshal(br);
        } catch (Exception e1) {
            throw PersoniumCoreException.Dav.XML_ERROR.reason(e1);
        }

        // ??
        final Multistatus ms = this.davCmp.proppatch(pu, this.getUrl());

        // ??
        StreamingOutput str = new StreamingOutput() {
            @Override
            public void write(final OutputStream os) throws IOException {
                Multistatus.marshal(ms, os);
            }
        };
        return Response.status(HttpStatus.SC_MULTI_STATUS).header(HttpHeaders.ETAG, this.davCmp.getEtag())
                .header(HttpHeaders.CONTENT_TYPE, "application/xml").entity(str).build();
    }

    /**
     * ACL??. ACL??. ????????? ??????????.
     * @param reader XML
     * @return JAX-RS Response
     */
    public final Response doAcl(final Reader reader) {

        return this.davCmp.acl(reader).build();
    }

    /**
     * @return ??
     */
    public String getConfidentialLevel() {
        String confidentialStringTmp = null;
        if (this.davCmp == null) {
            confidentialStringTmp = this.parent.getConfidentialLevel();
        } else {
            confidentialStringTmp = this.davCmp.getConfidentialLevel();
        }

        if (confidentialStringTmp == null || "".equals(confidentialStringTmp)) {
            if (this.parent == null) {
                // BOX????????????????????
                return OAuth2Helper.SchemaLevel.NONE;
            }
            confidentialStringTmp = this.parent.getConfidentialLevel();
        }
        return confidentialStringTmp;
    }

    /**
     * ?ACL??????.
     * @param ac 
     * @param privilege ACL?read??write
     * @return boolean
     */
    public boolean hasPrivilege(AccessContext ac, Privilege privilege) {

        // davCmp??????????????ACL???
        if (this.davCmp != null && this.getAccessContext().requirePrivilege(this.davCmp.getAcl(), privilege,
                this.getCell().getUrl())) {
            return true;
        }

        // ???
        if (this.parent != null && this.parent.hasPrivilege(ac, privilege)) {
            return true;
        }

        return false;
    }

    /**
     * OPTIONS.
     * @return JAX-RS Response
     */
    @OPTIONS
    public Response options() {
        // 
        this.checkAccessContext(this.getAccessContext(), BoxPrivilege.READ);

        return PersoniumCoreUtils.responseBuilderForOptions(HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE,
                io.personium.common.utils.PersoniumCoreUtils.HttpMethod.MKCOL,
                io.personium.common.utils.PersoniumCoreUtils.HttpMethod.PROPFIND,
                io.personium.common.utils.PersoniumCoreUtils.HttpMethod.PROPPATCH,
                io.personium.common.utils.PersoniumCoreUtils.HttpMethod.ACL).build();
    }

    /**
     * ?.
     * @param ac 
     * @param privilege ???
     */
    public void checkAccessContext(final AccessContext ac, Privilege privilege) {
        // ?
        if (ac.isUnitUserToken()) {
            return;
        }

        AcceptableAuthScheme allowedAuthScheme = getAcceptableAuthScheme();

        // ??
        ac.checkSchemaAccess(this.getConfidentialLevel(), this.getBox(), allowedAuthScheme);

        // Basic??????
        ac.updateBasicAuthenticationStateForResource(this.getBox());

        // ?
        if (!this.hasPrivilege(ac, privilege)) {
            // ??
            // ?INVALID?ACL?Privilege?all????????????????

            if (AccessContext.TYPE_INVALID.equals(ac.getType())) {
                ac.throwInvalidTokenException(allowedAuthScheme);
            } else if (AccessContext.TYPE_ANONYMOUS.equals(ac.getType())) {
                throw PersoniumCoreAuthzException.AUTHORIZATION_REQUIRED.realm(ac.getRealm(), allowedAuthScheme);
            }
            throw PersoniumCoreException.Auth.NECESSARY_PRIVILEGE_LACKING;
        }
    }

    /**
     * ?????Auth Scheme??.
     * @return ?????Auth Scheme
     */
    public AcceptableAuthScheme getAcceptableAuthScheme() {
        AcceptableAuthScheme allowedAuthScheme = AcceptableAuthScheme.ALL;
        // ?Box???????
        String boxSchema = this.getBox().getSchema();
        // ???????Basic?WWW-Authenticate????
        if (boxSchema != null && boxSchema.length() > 0 && !Role.DEFAULT_BOX_NAME.equals(this.getBox().getName())) {
            allowedAuthScheme = AcceptableAuthScheme.BEARER;
        }
        return allowedAuthScheme;
    }

    /**
     * ??.
     * @param account ??
     * @return ??
     */
    public boolean checkOwnerRepresentativeAccounts(final String account) {
        List<String> ownerRepresentativeAccountsSetting = this.davCmp.getOwnerRepresentativeAccounts();
        if (ownerRepresentativeAccountsSetting == null || account == null) {
            return false;
        }

        for (String ownerRepresentativeAccount : ownerRepresentativeAccountsSetting) {
            if (account.equals(ownerRepresentativeAccount)) {
                return true;
            }
        }
        return false;
    }

    static final org.apache.wink.webdav.model.Response createDavResponse(final String pathName, final String href,
            final DavCmp dCmp, final Propfind propfind, final boolean isAclRead) {
        ObjectFactory of = new ObjectFactory();
        org.apache.wink.webdav.model.Response ret = of.createResponse();
        ret.getHref().add(href);

        // TODO v1.1 PROPFIND???????
        if (propfind != null) {

            log.debug("isAllProp:" + propfind.isAllprop());
            log.debug("isPropName:" + propfind.isPropname());
        } else {
            log.debug("propfind is null");
        }

        /*
         * Displayname dn = of.createDisplayname(); dn.setValue(name); ret.setPropertyOk(dn);
         */

        Long updated = dCmp.getUpdated();
        if (updated != null) {
            Getlastmodified lm = of.createGetlastmodified();
            lm.setValue(new Date(updated));
            ret.setPropertyOk(lm);
        }
        Long published = dCmp.getPublished();
        if (published != null) {
            Creationdate cd = of.createCreationdate();
            cd.setValue(new Date(published));
            ret.setPropertyOk(cd);
        }
        String type = dCmp.getType();
        if (DavCmp.TYPE_DAV_FILE.equals(type)) {
            // Dav ?????
            Resourcetype rt1 = of.createResourcetype();
            ret.setPropertyOk(rt1);
            Getcontentlength gcl = new Getcontentlength();
            gcl.setValue(String.valueOf(dCmp.getContentLength()));
            ret.setPropertyOk(gcl);
            String contentType = dCmp.getContentType();
            Getcontenttype gct = new Getcontenttype();
            gct.setValue(contentType);
            ret.setPropertyOk(gct);
        } else if (DavCmp.TYPE_COL_ODATA.equals(type)) {
            // OData ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            List<Element> listElement = colRt.getAny();
            QName qname = new QName(PersoniumCoreUtils.XmlConst.NS_PERSONIUM, PersoniumCoreUtils.XmlConst.ODATA,
                    PersoniumCoreUtils.XmlConst.NS_PREFIX_PERSONIUM);
            Element element = WebDAVModelHelper.createElement(qname);
            listElement.add(element);
            ret.setPropertyOk(colRt);

        } else if (DavCmp.TYPE_COL_SVC.equals(type)) {
            // Service ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            List<Element> listElement = colRt.getAny();
            QName qname = new QName(PersoniumCoreUtils.XmlConst.NS_PERSONIUM, PersoniumCoreUtils.XmlConst.SERVICE,
                    PersoniumCoreUtils.XmlConst.NS_PREFIX_PERSONIUM);
            Element element = WebDAVModelHelper.createElement(qname);
            listElement.add(element);
            ret.setPropertyOk(colRt);

        } else {
            // Col ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            ret.setPropertyOk(colRt);

        }

        // ACL??
        Acl acl = dCmp.getAcl();
        if (isAclRead && acl != null) {

            Document aclDoc = null;

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            try {
                aclDoc = dbf.newDocumentBuilder().newDocument();
                ObjectIo.marshal(acl, aclDoc);
            } catch (Exception e) {
                throw new WebApplicationException(e);
            }
            if (aclDoc != null) {
                Element e = aclDoc.getDocumentElement();
                ret.setPropertyOk(e);
            }
        }

        Map<String, String> props = dCmp.getProperties();
        if (props != null) {
            List<String> nsList = new ArrayList<String>();
            for (Map.Entry<String, String> entry : props.entrySet()) {
                String key = entry.getKey();
                String val = entry.getValue();
                int idx = key.indexOf("@");
                String ns = key.substring(idx + 1, key.length());

                int nsIdx = nsList.indexOf(ns);
                if (nsIdx == -1) {
                    nsList.add(ns);
                }

                Element e = parseProp(val);

                ret.setPropertyOk(e);
            }

        }
        return ret;
    }

    private static Element parseProp(String value) {
        // valDOM?Element
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = null;
        Document doc = null;
        try {
            builder = factory.newDocumentBuilder();
            ByteArrayInputStream is = new ByteArrayInputStream(value.getBytes(CharEncoding.UTF_8));
            doc = builder.parse(is);
        } catch (Exception e1) {
            throw PersoniumCoreException.Dav.DAV_INCONSISTENCY_FOUND.reason(e1);
        }
        Element e = doc.getDocumentElement();
        return e;
    }

}