Java tutorial
/** * 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; } }