def test_DELETE(self): """ DELETE request """ def check_result(response, path): response = IResponse(response) if response.code != responsecode.NO_CONTENT: self.fail("DELETE response %s != %s" % (response.code, responsecode.NO_CONTENT)) if os.path.exists(path): self.fail("DELETE did not remove path %s" % (path,)) def work(): for filename in os.listdir(self.docroot): path = os.path.join(self.docroot, filename) uri = urllib.quote("/" + filename) if os.path.isdir(path): uri = uri + "/" def do_test(response, path=path): return check_result(response, path) request = SimpleRequest(self.site, "DELETE", uri) depth = random.choice(("infinity", None)) if depth is not None: request.headers.setHeader("depth", depth) yield (request, do_test) return serialize(self.send, work())
def test_MOVE_create(self): """ MOVE to new resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CREATED: self.fail("Incorrect response code for MOVE %s: %s != %s" % (uri, response.code, responsecode.CREATED)) if response.headers.getHeader("location") is None: self.fail( "Reponse to MOVE %s with CREATE status is missing location: header." % (uri, )) if isfile: if not os.path.isfile(dst_path): self.fail("MOVE %s produced no output file" % (uri, )) if sum != sumFile(dst_path): self.fail("MOVE %s produced different file" % (uri, )) else: if not os.path.isdir(dst_path): self.fail("MOVE %s produced no output directory" % (uri, )) if sum != sumFile(dst_path): self.fail("isdir %s produced different directory" % (uri, )) return serialize(self.send, work(self, test))
def test_COPY_MOVE_source(self): """ Verify source access controls during COPY and MOVE. """ def work(): dst_path = os.path.join(self.docroot, "copy_dst") dst_uri = "/" + os.path.basename(dst_path) for src, status in ( ("nobind", responsecode.FORBIDDEN), ("bind", responsecode.FORBIDDEN), ("unbind", responsecode.CREATED), ): src_path = os.path.join(self.docroot, "src_" + src) src_uri = "/" + os.path.basename(src_path) if not os.path.isdir(src_path): os.mkdir(src_path) src_resource = self.resource_class(src_path) src_resource.setAccessControlList({ "nobind": self.grant(), "bind" : self.grant(element.Bind()), "unbind": self.grant(element.Bind(), element.Unbind()) }[src]) for name, acl in ( ("none" , self.grant()), ("read" , self.grant(element.Read())), ("read-write" , self.grant(element.Read(), element.Write())), ("unlock" , self.grant(element.Unlock())), ("all" , self.grant(element.All())), ): filename = os.path.join(src_path, name) if not os.path.isfile(filename): file(filename, "w").close() self.resource_class(filename).setAccessControlList(acl) for method in ("COPY", "MOVE"): for name, code in ( ("none", {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]), ("read", {"COPY": responsecode.CREATED, "MOVE": status}[method]), ("read-write" , {"COPY": responsecode.CREATED, "MOVE": status}[method]), ("unlock", {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]), ("all", {"COPY": responsecode.CREATED, "MOVE": status}[method]), ): path = os.path.join(src_path, name) uri = src_uri + "/" + name request = SimpleRequest(self.site, method, uri) request.headers.setHeader("destination", dst_uri) _add_auth_header(request) def test(response, code=code, path=path): if os.path.isfile(dst_path): os.remove(dst_path) if response.code != code: return self.oops(request, response, code, method, name) yield (request, test) return serialize(self.send, work())
def test_COPY_MOVE_dest(self): """ Verify destination access controls during COPY and MOVE. """ def work(): src_path = os.path.join(self.docroot, "read") uri = "/" + os.path.basename(src_path) for method in ("COPY", "MOVE"): for name, code in ( ("nobind" , responsecode.FORBIDDEN), ("bind" , responsecode.CREATED), ("unbind" , responsecode.CREATED), ): dst_parent_path = os.path.join(self.docroot, name) dst_path = os.path.join(dst_parent_path, "dst") request = SimpleRequest(self.site, method, uri) request.headers.setHeader("destination", "/" + name + "/dst") _add_auth_header(request) def test(response, code=code, dst_path=dst_path): if os.path.isfile(dst_path): os.remove(dst_path) if response.code != code: return self.oops(request, response, code, method, name) yield (request, test) self.restore() return serialize(self.send, work())
def test_MOVE_create(self): """ MOVE to new resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CREATED: self.fail("Incorrect response code for MOVE %s: %s != %s" % (uri, response.code, responsecode.CREATED)) if response.headers.getHeader("location") is None: self.fail("Reponse to MOVE %s with CREATE status is missing location: header." % (uri,)) if isfile: if not os.path.isfile(dst_path): self.fail("MOVE %s produced no output file" % (uri,)) if sum != sumFile(dst_path): self.fail("MOVE %s produced different file" % (uri,)) else: if not os.path.isdir(dst_path): self.fail("MOVE %s produced no output directory" % (uri,)) if sum != sumFile(dst_path): self.fail("isdir %s produced different directory" % (uri,)) return serialize(self.send, work(self, test))
def test_MKCOL_PUT(self): """ Verify access controls during MKCOL. """ for method in ("MKCOL", "PUT"): def work(): for name, code in ( ("nobind" , responsecode.FORBIDDEN), ("bind" , responsecode.CREATED), ("unbind" , responsecode.CREATED), ): collection_path = os.path.join(self.docroot, name) path = os.path.join(collection_path, "dst") if os.path.isfile(path): os.remove(path) elif os.path.isdir(path): os.rmdir(path) request = SimpleRequest(self.site, method, "/" + name + "/dst") _add_auth_header(request) def test(response, code=code, path=path): if response.code != code: return self.oops(request, response, code, method, name) yield (request, test) return serialize(self.send, work())
def test_DELETE(self): """ Verify access controls during DELETE. """ def work(): for name, code in ( ("nobind" , responsecode.FORBIDDEN), ("bind" , responsecode.FORBIDDEN), ("unbind" , responsecode.NO_CONTENT), ): collection_path = os.path.join(self.docroot, name) path = os.path.join(collection_path, "dst") file(path, "w").close() request = SimpleRequest(self.site, "DELETE", "/" + name + "/dst") _add_auth_header(request) def test(response, code=code, path=path): if response.code != code: return self.oops(request, response, code, "DELETE", name) yield (request, test) return serialize(self.send, work())
def test_PROPPATCH(self): """ Verify access controls during PROPPATCH. """ def work(): for name, code in ( ("none" , responsecode.FORBIDDEN), ("read" , responsecode.FORBIDDEN), ("read-write" , responsecode.MULTI_STATUS), ("unlock" , responsecode.FORBIDDEN), ("all" , responsecode.MULTI_STATUS), ): path = os.path.join(self.docroot, name) request = SimpleRequest(self.site, "PROPPATCH", "/" + name) request.stream = MemoryStream( element.WebDAVDocument(element.PropertyUpdate()).toxml() ) _add_auth_header(request) def test(response, code=code, path=path): if response.code != code: return self.oops(request, response, code, "PROPPATCH", name) yield (request, test) return serialize(self.send, work())
def test_DELETE(self): """ DELETE request """ def check_result(response, path): response = IResponse(response) if response.code != responsecode.NO_CONTENT: self.fail("DELETE response %s != %s" % (response.code, responsecode.NO_CONTENT)) if os.path.exists(path): self.fail("DELETE did not remove path %s" % (path, )) def work(): for filename in os.listdir(self.docroot): path = os.path.join(self.docroot, filename) uri = urllib.quote("/" + filename) if os.path.isdir(path): uri = uri + "/" def do_test(response, path=path): return check_result(response, path) request = SimpleRequest(self.site, "DELETE", uri) depth = random.choice(("infinity", None)) if depth is not None: request.headers.setHeader("depth", depth) yield (request, do_test) return serialize(self.send, work())
def test_GET_REPORT(self): """ Verify access controls during GET and REPORT. """ def work(): for method in ("GET", "REPORT"): if method == "GET": ok = responsecode.OK elif method == "REPORT": ok = responsecode.MULTI_STATUS else: raise AssertionError("We shouldn't be here. (method = %r)" % (method,)) for name, code in ( ("none" , responsecode.FORBIDDEN), ("read" , ok), ("read-write" , ok), ("unlock" , responsecode.FORBIDDEN), ("all" , ok), ): path = os.path.join(self.docroot, name) request = SimpleRequest(self.site, method, "/" + name) if method == "REPORT": request.stream = MemoryStream(element.PrincipalPropertySearch().toxml()) _add_auth_header(request) def test(response, code=code, path=path): if response.code != code: return self.oops(request, response, code, method, name) yield (request, test) return serialize(self.send, work())
def test_COPY_create(self): """ COPY to new resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CREATED: self.fail( "Incorrect response code for COPY %s (depth=%r): %s != %s" % (uri, depth, response.code, responsecode.CREATED)) if response.headers.getHeader("location") is None: self.fail( "Reponse to COPY %s (depth=%r) with CREATE status is missing location: header." % (uri, depth)) if os.path.isfile(path): if not os.path.isfile(dst_path): self.fail("COPY %s (depth=%r) produced no output file" % (uri, depth)) if not cmp(path, dst_path): self.fail("COPY %s (depth=%r) produced different file" % (uri, depth)) os.remove(dst_path) elif os.path.isdir(path): if not os.path.isdir(dst_path): self.fail( "COPY %s (depth=%r) produced no output directory" % (uri, depth)) if depth in ("infinity", None): if dircmp(path, dst_path): self.fail( "COPY %s (depth=%r) produced different directory" % (uri, depth)) elif depth == "0": for filename in os.listdir(dst_path): self.fail( "COPY %s (depth=%r) shouldn't copy directory contents (eg. %s)" % (uri, depth, filename)) else: raise AssertionError("Unknown depth: %r" % (depth, )) rmdir(dst_path) else: self.fail("Source %s is neither a file nor a directory" % (path, )) return serialize(self.send, work(self, test))
def test_COPY_no_parent(self): """ COPY to resource with no parent. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CONFLICT: self.fail("Incorrect response code for COPY with no parent %s: %s != %s" % (uri, response.code, responsecode.CONFLICT)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
def test_MOVE_no_parent(self): """ MOVE to resource with no parent. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CONFLICT: self.fail("Incorrect response code for MOVE with no parent %s: %s != %s" % (uri, response.code, responsecode.CONFLICT)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
def test_MOVE_overwrite(self): """ MOVE to existing resource with overwrite header. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.NO_CONTENT: self.fail("Incorrect response code for MOVE with overwrite %s: %s != %s" % (uri, response.code, responsecode.NO_CONTENT)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, overwrite=True))
def test_MOVE_exists(self): """ MOVE to existing resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.PRECONDITION_FAILED: self.fail("Incorrect response code for MOVE without overwrite %s: %s != %s" % (uri, response.code, responsecode.PRECONDITION_FAILED)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, overwrite=False))
def test_COPY_exists(self): """ COPY to existing resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.PRECONDITION_FAILED: self.fail("Incorrect response code for COPY without overwrite %s: %s != %s" % (uri, response.code, responsecode.PRECONDITION_FAILED)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, overwrite=False))
def test_PUT_simple(self): """ PUT request """ dst_path = os.path.join(self.docroot, "dst") def checkResult(response, path): response = IResponse(response) if response.code not in ( responsecode.CREATED, responsecode.NO_CONTENT ): self.fail("PUT failed: %s" % (response.code,)) if not os.path.isfile(dst_path): self.fail("PUT failed to create file %s." % (dst_path,)) if not filecmp.cmp(path, dst_path): self.fail("PUT failed to preserve data for file %s in file %s." % (path, dst_path)) etag = response.headers.getHeader("etag") if not etag: self.fail("No etag header in PUT response %r." % (response,)) # # We need to serialize these request & test iterations because they can # interfere with each other. # def work(): dst_uri = "/dst" for name in os.listdir(self.docroot): if name == "dst": continue path = os.path.join(self.docroot, name) # Can't really PUT something you can't read if not os.path.isfile(path): continue def do_test(response): checkResult(response, path) request = SimpleRequest(self.site, "PUT", dst_uri) request.stream = FileStream(file(path, "rb")) yield (request, do_test) return serialize(self.send, work())
def test_PUT_simple(self): """ PUT request """ dst_path = os.path.join(self.docroot, "dst") def checkResult(response, path): response = IResponse(response) if response.code not in (responsecode.CREATED, responsecode.NO_CONTENT): self.fail("PUT failed: %s" % (response.code, )) if not os.path.isfile(dst_path): self.fail("PUT failed to create file %s." % (dst_path, )) if not filecmp.cmp(path, dst_path): self.fail( "PUT failed to preserve data for file %s in file %s." % (path, dst_path)) etag = response.headers.getHeader("etag") if not etag: self.fail("No etag header in PUT response %r." % (response, )) # # We need to serialize these request & test iterations because they can # interfere with each other. # def work(): dst_uri = "/dst" for name in os.listdir(self.docroot): if name == "dst": continue path = os.path.join(self.docroot, name) # Can't really PUT something you can't read if not os.path.isfile(path): continue def do_test(response): checkResult(response, path) request = SimpleRequest(self.site, "PUT", dst_uri) request.stream = FileStream(file(path, "rb")) yield (request, do_test) return serialize(self.send, work())
def test_MOVE_overwrite(self): """ MOVE to existing resource with overwrite header. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.NO_CONTENT: self.fail( "Incorrect response code for MOVE with overwrite %s: %s != %s" % (uri, response.code, responsecode.NO_CONTENT)) else: # FIXME: Check XML error code (2518bis) pass return serialize(self.send, work(self, test, overwrite=True))
def test_COPY_overwrite(self): """ COPY to existing resource with overwrite header. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.NO_CONTENT: self.fail("Incorrect response code for COPY with overwrite %s: %s != %s" % (uri, response.code, responsecode.NO_CONTENT)) else: # FIXME: Check XML error code (2518bis) pass self.failUnless(os.path.exists(dst_path), "COPY didn't produce file: %s" % (dst_path,)) return serialize(self.send, work(self, test, overwrite=True))
def test_COPY_create(self): """ COPY to new resource. """ def test(response, path, isfile, sum, uri, depth, dst_path): if response.code != responsecode.CREATED: self.fail( "Incorrect response code for COPY %s (depth=%r): %s != %s" % (uri, depth, response.code, responsecode.CREATED) ) if response.headers.getHeader("location") is None: self.fail( "Reponse to COPY %s (depth=%r) with CREATE status is missing location: header." % (uri, depth) ) if os.path.isfile(path): if not os.path.isfile(dst_path): self.fail("COPY %s (depth=%r) produced no output file" % (uri, depth)) if not cmp(path, dst_path): self.fail("COPY %s (depth=%r) produced different file" % (uri, depth)) os.remove(dst_path) elif os.path.isdir(path): if not os.path.isdir(dst_path): self.fail("COPY %s (depth=%r) produced no output directory" % (uri, depth)) if depth in ("infinity", None): if dircmp(path, dst_path): self.fail("COPY %s (depth=%r) produced different directory" % (uri, depth)) elif depth == "0": for filename in os.listdir(dst_path): self.fail( "COPY %s (depth=%r) shouldn't copy directory contents (eg. %s)" % (uri, depth, filename) ) else: raise AssertionError("Unknown depth: %r" % (depth,)) rmdir(dst_path) else: self.fail("Source %s is neither a file nor a directory" % (path,)) return serialize(self.send, work(self, test))
def test_PUT_again(self): """ PUT on existing resource with If-None-Match header """ dst_path = os.path.join(self.docroot, "dst") dst_uri = "/dst" def work(): for code in ( responsecode.CREATED, responsecode.PRECONDITION_FAILED, responsecode.NO_CONTENT, responsecode.PRECONDITION_FAILED, responsecode.NO_CONTENT, responsecode.CREATED, ): def checkResult(response, code=code): response = IResponse(response) if response.code != code: self.fail( "Incorrect response code for PUT (%s != %s)" % (response.code, code)) def onError(f): f.trap(HTTPError) return checkResult(f.value.response) request = SimpleRequest(self.site, "PUT", dst_uri) request.stream = FileStream(file(__file__, "rb")) if code == responsecode.CREATED: if os.path.isfile(dst_path): os.remove(dst_path) request.headers.setHeader("if-none-match", ("*", )) elif code == responsecode.PRECONDITION_FAILED: request.headers.setHeader("if-none-match", ("*", )) yield (request, (checkResult, onError)) return serialize(self.send, work())
def test_PUT_again(self): """ PUT on existing resource with If-None-Match header """ dst_path = os.path.join(self.docroot, "dst") dst_uri = "/dst" def work(): for code in ( responsecode.CREATED, responsecode.PRECONDITION_FAILED, responsecode.NO_CONTENT, responsecode.PRECONDITION_FAILED, responsecode.NO_CONTENT, responsecode.CREATED, ): def checkResult(response, code=code): response = IResponse(response) if response.code != code: self.fail("Incorrect response code for PUT (%s != %s)" % (response.code, code)) def onError(f): f.trap(HTTPError) return checkResult(f.value.response) request = SimpleRequest(self.site, "PUT", dst_uri) request.stream = FileStream(file(__file__, "rb")) if code == responsecode.CREATED: if os.path.isfile(dst_path): os.remove(dst_path) request.headers.setHeader("if-none-match", ("*",)) elif code == responsecode.PRECONDITION_FAILED: request.headers.setHeader("if-none-match", ("*",)) yield (request, (checkResult, onError)) return serialize(self.send, work())
def test_PUT_exists(self): """ Verify access controls during PUT of existing file. """ def work(): for name, code in ( ("none" , responsecode.FORBIDDEN), ("read" , responsecode.FORBIDDEN), ("read-write" , responsecode.NO_CONTENT), ("unlock" , responsecode.FORBIDDEN), ("all" , responsecode.NO_CONTENT), ): path = os.path.join(self.docroot, name) request = SimpleRequest(self.site, "PUT", "/" + name) _add_auth_header(request) def test(response, code=code, path=path): if response.code != code: return self.oops(request, response, code, "PUT", name) yield (request, test) return serialize(self.send, work())
def test_PROPFIND_list(self): """ PROPFIND with allprop, propname """ def check_result(which): def _check_result(response): response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail( "Incorrect response code for PROPFIND (%s != %s)" % (response.code, responsecode.MULTI_STATUS)) return davXMLFromStream(response.stream).addCallback( check_xml, which) return _check_result def check_xml(doc, which): response = doc.root_element.childOfType( davxml.PropertyStatusResponse) self.failUnless( response.childOfType(davxml.HRef) == "/", "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef), )) for propstat in response.childrenOfType(davxml.PropertyStatus): status = propstat.childOfType(davxml.Status) properties = propstat.childOfType( davxml.PropertyContainer).children if status.code != responsecode.OK: self.fail( "PROPFIND failed (status %s) to locate live properties: %s" % (status.code, properties)) if which.name == "allprop": properties_to_find = [ p.qname() for p in self.liveProperties() if not p.hidden ] else: properties_to_find = [ p.qname() for p in self.liveProperties() ] for property in properties: qname = property.qname() if qname in properties_to_find: properties_to_find.remove(qname) elif qname[0] != dav_namespace: pass else: self.fail( "PROPFIND with %s found property we didn't expect: %r" % (which.name, property)) if which.name == "propname": # Element should be empty self.failUnless(len(property.children) == 0) else: # Element should have a value, unless the property exists and is empty... # Verify that there is a value for live properties for which we know # that this should be the case. if property.namespace == dav_namespace and property.name in ( "getetag", "getcontenttype", "getlastmodified", "creationdate", "displayname", ): self.failIf( len(property.children) == 0, "Property has no children: %r" % (property.toxml(), )) if properties_to_find: self.fail( "PROPFIND with %s failed to find properties: %r" % (which.name, properties_to_find)) properties = propstat.childOfType( davxml.PropertyContainer).children def work(): for which in (davxml.AllProperties(), davxml.PropertyName()): query = davxml.PropertyFind(which) request = SimpleRequest(self.site, "PROPFIND", "/") request.headers.setHeader("depth", "0") request.stream = MemoryStream(query.toxml()) yield (request, check_result(which)) return serialize(self.send, work())
def test_PROPFIND_list(self): """ PROPFIND with allprop, propname """ def check_result(which): def _check_result(response): response = IResponse(response) if response.code != responsecode.MULTI_STATUS: self.fail("Incorrect response code for PROPFIND (%s != %s)" % (response.code, responsecode.MULTI_STATUS)) return davXMLFromStream(response.stream).addCallback(check_xml, which) return _check_result def check_xml(doc, which): response = doc.root_element.childOfType(davxml.PropertyStatusResponse) self.failUnless( response.childOfType(davxml.HRef) == "/", "Incorrect response URI: %s != /" % (response.childOfType(davxml.HRef),) ) for propstat in response.childrenOfType(davxml.PropertyStatus): status = propstat.childOfType(davxml.Status) properties = propstat.childOfType(davxml.PropertyContainer).children if status.code != responsecode.OK: self.fail("PROPFIND failed (status %s) to locate live properties: %s" % (status.code, properties)) if which.name == "allprop": properties_to_find = [p.qname() for p in self.liveProperties() if not p.hidden] else: properties_to_find = [p.qname() for p in self.liveProperties()] for property in properties: qname = property.qname() if qname in properties_to_find: properties_to_find.remove(qname) elif qname[0] != dav_namespace: pass else: self.fail("PROPFIND with %s found property we didn't expect: %r" % (which.name, property)) if which.name == "propname": # Element should be empty self.failUnless(len(property.children) == 0) else: # Element should have a value, unless the property exists and is empty... # Verify that there is a value for live properties for which we know # that this should be the case. if property.namespace == dav_namespace and property.name in ( "getetag", "getcontenttype", "getlastmodified", "creationdate", "displayname", ): self.failIf( len(property.children) == 0, "Property has no children: %r" % (property.toxml(),) ) if properties_to_find: self.fail("PROPFIND with %s failed to find properties: %r" % (which.name, properties_to_find)) properties = propstat.childOfType(davxml.PropertyContainer).children def work(): for which in (davxml.AllProperties(), davxml.PropertyName()): query = davxml.PropertyFind(which) request = SimpleRequest(self.site, "PROPFIND", "/") request.headers.setHeader("depth", "0") request.stream = MemoryStream(query.toxml()) yield (request, check_result(which)) return serialize(self.send, work())