mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-31 16:36:20 +00:00
don't allow trailing slashes
This makes /uri/xxx URIs invalid if they have a trailing slash. It seems that the former Nevow implementation would allow this, and some tests (and, notable, "tahoe backup") did rely on using URIs of this style.
This commit is contained in:
parent
882c63dab9
commit
018e161f19
@ -51,8 +51,8 @@ def mkdir(contents, options):
|
||||
return dircap
|
||||
|
||||
def put_child(dirurl, childname, childcap):
|
||||
assert dirurl[-1] == "/"
|
||||
url = dirurl + urllib.quote(unicode_to_url(childname)) + "?t=uri"
|
||||
assert dirurl[-1] != "/"
|
||||
url = dirurl + "/" + urllib.quote(unicode_to_url(childname)) + "?t=uri"
|
||||
resp = do_http("PUT", url, childcap)
|
||||
if resp.status not in (200, 201):
|
||||
raise HTTPError("Error during put_child", resp)
|
||||
@ -105,6 +105,9 @@ class BackerUpper(object):
|
||||
|
||||
archives_url = to_url + "Archives/"
|
||||
|
||||
archives_url = archives_url.rstrip("/")
|
||||
to_url = to_url.rstrip("/")
|
||||
|
||||
# first step: make sure the target directory exists, as well as the
|
||||
# Archives/ subdirectory.
|
||||
resp = do_http("GET", archives_url + "?t=json")
|
||||
|
@ -328,8 +328,8 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
|
||||
def _stash_root_and_create_file(n):
|
||||
self.rootnode = n
|
||||
self.rooturl = "uri/" + urllib.quote(n.get_uri()) + "/"
|
||||
self.rourl = "uri/" + urllib.quote(n.get_readonly_uri()) + "/"
|
||||
self.rooturl = "uri/" + urllib.quote(n.get_uri())
|
||||
self.rourl = "uri/" + urllib.quote(n.get_readonly_uri())
|
||||
if not immutable:
|
||||
return self.rootnode.set_node(name, future_node)
|
||||
d.addCallback(_stash_root_and_create_file)
|
||||
@ -389,7 +389,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
|
||||
d.addCallback(lambda ign: self.GET(expected_info_url))
|
||||
d.addCallback(_check_info, expect_rw_uri=False, expect_ro_uri=False)
|
||||
d.addCallback(lambda ign: self.GET("%s%s?t=info" % (self.rooturl, str(name))))
|
||||
d.addCallback(lambda ign: self.GET("%s/%s?t=info" % (self.rooturl, str(name))))
|
||||
d.addCallback(_check_info, expect_rw_uri=False, expect_ro_uri=True)
|
||||
|
||||
def _check_json(res, expect_rw_uri):
|
||||
@ -413,7 +413,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
# TODO: check metadata contents
|
||||
self.failUnlessIn("metadata", data[1])
|
||||
|
||||
d.addCallback(lambda ign: self.GET("%s%s?t=json" % (self.rooturl, str(name))))
|
||||
d.addCallback(lambda ign: self.GET("%s/%s?t=json" % (self.rooturl, str(name))))
|
||||
d.addCallback(_check_json, expect_rw_uri=not immutable)
|
||||
|
||||
# and make sure that a read-only version of the directory can be
|
||||
@ -428,7 +428,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
d.addCallback(lambda ign: self.GET(self.rourl+"?t=json"))
|
||||
d.addCallback(_check_directory_json, expect_rw_uri=False)
|
||||
|
||||
d.addCallback(lambda ign: self.GET("%s%s?t=json" % (self.rourl, str(name))))
|
||||
d.addCallback(lambda ign: self.GET("%s/%s?t=json" % (self.rourl, str(name))))
|
||||
d.addCallback(_check_json, expect_rw_uri=False)
|
||||
|
||||
# TODO: check that getting t=info from the Info link in the ro directory
|
||||
@ -495,7 +495,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
self.failUnlessIn("CHK", cap.to_string())
|
||||
self.cap = cap
|
||||
self.rootnode = dn
|
||||
self.rooturl = "uri/" + urllib.quote(dn.get_uri()) + "/"
|
||||
self.rooturl = "uri/" + urllib.quote(dn.get_uri())
|
||||
return download_to_data(dn._node)
|
||||
d.addCallback(_created)
|
||||
|
||||
@ -585,7 +585,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
d = c0.create_dirnode()
|
||||
def _stash_root_and_create_file(n):
|
||||
self.rootnode = n
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri()) + "/"
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri())
|
||||
return n.add_file(u"good", upload.Data(DATA, convergence=""))
|
||||
d.addCallback(_stash_root_and_create_file)
|
||||
def _stash_uri(fn, which):
|
||||
@ -759,7 +759,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
d = c0.create_dirnode()
|
||||
def _stash_root_and_create_file(n):
|
||||
self.rootnode = n
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri()) + "/"
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri())
|
||||
return n.add_file(u"good", upload.Data(DATA, convergence=""))
|
||||
d.addCallback(_stash_root_and_create_file)
|
||||
def _stash_uri(fn, which):
|
||||
@ -972,7 +972,7 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
def _stash_root_and_create_file(n):
|
||||
self.rootnode = n
|
||||
self.uris["root"] = n.get_uri()
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri()) + "/"
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri())
|
||||
return n.add_file(u"one", upload.Data(DATA, convergence=""))
|
||||
d.addCallback(_stash_root_and_create_file)
|
||||
def _stash_uri(fn, which):
|
||||
@ -1039,8 +1039,8 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
DATA = "data" * 100
|
||||
d = c0.create_dirnode()
|
||||
def _stash_root(n):
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri()) + "/"
|
||||
self.fileurls["imaginary"] = self.fileurls["root"] + "imaginary"
|
||||
self.fileurls["root"] = "uri/" + urllib.quote(n.get_uri())
|
||||
self.fileurls["imaginary"] = self.fileurls["root"] + "/imaginary"
|
||||
return n
|
||||
d.addCallback(_stash_root)
|
||||
d.addCallback(lambda ign: c0.upload(upload.Data(DATA, convergence="")))
|
||||
@ -1056,14 +1056,14 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
d.addCallback(lambda ign: c0.create_dirnode())
|
||||
def _mangle_dirnode_1share(n):
|
||||
u = n.get_uri()
|
||||
url = self.fileurls["dir-1share"] = "uri/" + urllib.quote(u) + "/"
|
||||
url = self.fileurls["dir-1share"] = "uri/" + urllib.quote(u)
|
||||
self.fileurls["dir-1share-json"] = url + "?t=json"
|
||||
self.delete_shares_numbered(u, range(1,10))
|
||||
d.addCallback(_mangle_dirnode_1share)
|
||||
d.addCallback(lambda ign: c0.create_dirnode())
|
||||
def _mangle_dirnode_0share(n):
|
||||
u = n.get_uri()
|
||||
url = self.fileurls["dir-0share"] = "uri/" + urllib.quote(u) + "/"
|
||||
url = self.fileurls["dir-0share"] = "uri/" + urllib.quote(u)
|
||||
self.fileurls["dir-0share-json"] = url + "?t=json"
|
||||
self.delete_shares_numbered(u, range(0,10))
|
||||
d.addCallback(_mangle_dirnode_0share)
|
||||
@ -1342,8 +1342,8 @@ class Grid(GridTestMixin, WebErrorMixin, ShouldFailMixin, testutil.ReallyEqualMi
|
||||
self.dir_si_b32 = base32.b2a(dn.get_storage_index())
|
||||
self.dir_url_base = "uri/"+dn.get_write_uri()
|
||||
self.dir_url_json1 = "uri/"+dn.get_write_uri()+"?t=json"
|
||||
self.dir_url_json2 = "uri/"+dn.get_write_uri()+"/?t=json"
|
||||
self.dir_url_json_ro = "uri/"+dn.get_readonly_uri()+"/?t=json"
|
||||
self.dir_url_json2 = "uri/"+dn.get_write_uri()+"?t=json"
|
||||
self.dir_url_json_ro = "uri/"+dn.get_readonly_uri()+"?t=json"
|
||||
self.child_url = "uri/"+dn.get_readonly_uri()+"/child"
|
||||
d.addCallback(_get_dircap)
|
||||
d.addCallback(lambda ign: self.GET(self.dir_url_base, followRedirect=True))
|
||||
|
@ -658,8 +658,10 @@ class WebMixin(testutil.TimezoneMixin):
|
||||
(response_substring, res.value.response,
|
||||
which))
|
||||
else:
|
||||
self.fail("%s was supposed to raise %s, not get '%s'" %
|
||||
(which, expected_failure, res))
|
||||
self.fail(
|
||||
RuntimeError("%s was supposed to raise %s, not get '%s'" %
|
||||
(which, expected_failure, res))
|
||||
)
|
||||
d.addBoth(done)
|
||||
return d
|
||||
|
||||
@ -1940,7 +1942,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
@inlineCallbacks
|
||||
def test_GET_DIRURL_empty(self):
|
||||
# look at an empty directory
|
||||
data = yield self.GET(self.public_url + "/foo/empty/")
|
||||
data = yield self.GET(self.public_url + "/foo/empty")
|
||||
soup = BeautifulSoup(data, 'html5lib')
|
||||
self.failUnlessIn("directory is empty", data)
|
||||
mkdir_inputs = soup.find_all(u"input", {u"type": u"hidden", u"name": u"t", u"value": u"mkdir"})
|
||||
@ -1954,7 +1956,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
def test_GET_DIRURL_literal(self):
|
||||
# look at a literal directory
|
||||
tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT
|
||||
data = yield self.GET("/uri/" + tiny_litdir_uri + "/", followRedirect=True)
|
||||
data = yield self.GET("/uri/" + tiny_litdir_uri, followRedirect=True)
|
||||
soup = BeautifulSoup(data, 'html5lib')
|
||||
self.failUnlessIn('(immutable)', data)
|
||||
file_links = list(
|
||||
@ -2017,7 +2019,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
def test_POST_DIRURL_manifest(self):
|
||||
d = defer.succeed(None)
|
||||
def getman(ignored, output):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-manifest&ophandle=125"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-manifest&ophandle=125"
|
||||
d = do_http("post", url, allow_redirects=True,
|
||||
browser_like_redirects=True)
|
||||
d.addCallback(self.wait_for_operation, "125")
|
||||
@ -2069,7 +2071,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
return d
|
||||
|
||||
def test_POST_DIRURL_deepsize(self):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-deep-size&ophandle=126"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-deep-size&ophandle=126"
|
||||
d = do_http("post", url, allow_redirects=True,
|
||||
browser_like_redirects=True)
|
||||
d.addCallback(self.wait_for_operation, "126")
|
||||
@ -2098,7 +2100,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
return d
|
||||
|
||||
def test_POST_DIRURL_deepstats(self):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-deep-stats&ophandle=127"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-deep-stats&ophandle=127"
|
||||
d = do_http("post", url,
|
||||
allow_redirects=True, browser_like_redirects=True)
|
||||
d.addCallback(self.wait_for_operation, "127")
|
||||
@ -2127,7 +2129,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
return d
|
||||
|
||||
def test_POST_DIRURL_stream_manifest(self):
|
||||
d = self.POST(self.public_url + "/foo/?t=stream-manifest")
|
||||
d = self.POST(self.public_url + "/foo?t=stream-manifest")
|
||||
def _check(res):
|
||||
self.failUnless(res.endswith("\n"))
|
||||
units = [json.loads(t) for t in res[:-1].split("\n")]
|
||||
@ -2789,7 +2791,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
# slightly differently
|
||||
|
||||
d.addCallback(lambda res:
|
||||
self.GET(self.public_url + "/foo/",
|
||||
self.GET(self.public_url + "/foo",
|
||||
followRedirect=True))
|
||||
def _check_page(res):
|
||||
# TODO: assert more about the contents
|
||||
@ -2807,7 +2809,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
# look at the JSON form of the enclosing directory
|
||||
d.addCallback(lambda res:
|
||||
self.GET(self.public_url + "/foo/?t=json",
|
||||
self.GET(self.public_url + "/foo?t=json",
|
||||
followRedirect=True))
|
||||
def _check_page_json(res):
|
||||
parsed = json.loads(res)
|
||||
@ -3021,7 +3023,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
@inlineCallbacks
|
||||
def test_POST_DIRURL_check(self):
|
||||
foo_url = self.public_url + "/foo/"
|
||||
foo_url = self.public_url + "/foo"
|
||||
res = yield self.POST(foo_url, t="check")
|
||||
self.failUnlessIn("Healthy :", res)
|
||||
|
||||
@ -3043,7 +3045,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
@inlineCallbacks
|
||||
def test_POST_DIRURL_check_and_repair(self):
|
||||
foo_url = self.public_url + "/foo/"
|
||||
foo_url = self.public_url + "/foo"
|
||||
res = yield self.POST(foo_url, t="check", repair="true")
|
||||
self.failUnlessIn("Healthy :", res)
|
||||
|
||||
@ -4520,7 +4522,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
@inlineCallbacks
|
||||
def test_ophandle_cancel(self):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-manifest&ophandle=128"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-manifest&ophandle=128"
|
||||
yield do_http("post", url,
|
||||
allow_redirects=True, browser_like_redirects=True)
|
||||
res = yield self.GET("/operations/128?t=status&output=JSON")
|
||||
@ -4539,7 +4541,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
@inlineCallbacks
|
||||
def test_ophandle_retainfor(self):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-manifest&ophandle=129&retain-for=60"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-manifest&ophandle=129&retain-for=60"
|
||||
yield do_http("post", url,
|
||||
allow_redirects=True, browser_like_redirects=True)
|
||||
res = yield self.GET("/operations/129?t=status&output=JSON&retain-for=0")
|
||||
@ -4553,7 +4555,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
|
||||
@inlineCallbacks
|
||||
def test_ophandle_release_after_complete(self):
|
||||
url = self.webish_url + self.public_url + "/foo/?t=start-manifest&ophandle=130"
|
||||
url = self.webish_url + self.public_url + "/foo?t=start-manifest&ophandle=130"
|
||||
yield do_http("post", url,
|
||||
allow_redirects=True, browser_like_redirects=True)
|
||||
yield self.wait_for_operation(None, "130")
|
||||
@ -4567,7 +4569,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
# uncollected ophandles should expire after 4 days
|
||||
def _make_uncollected_ophandle(ophandle):
|
||||
url = (self.webish_url + self.public_url +
|
||||
"/foo/?t=start-manifest&ophandle=%d" % ophandle)
|
||||
"/foo?t=start-manifest&ophandle=%d" % ophandle)
|
||||
# When we start the operation, the webapi server will want to
|
||||
# redirect us to the page for the ophandle, so we get
|
||||
# confirmation that the operation has started. If the manifest
|
||||
@ -4605,7 +4607,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
||||
# collected ophandles should expire after 1 day
|
||||
def _make_collected_ophandle(ophandle):
|
||||
url = (self.webish_url + self.public_url +
|
||||
"/foo/?t=start-manifest&ophandle=%d" % ophandle)
|
||||
"/foo?t=start-manifest&ophandle=%d" % ophandle)
|
||||
# By following the initial redirect, we collect the ophandle
|
||||
# we've just created.
|
||||
return do_http("post", url,
|
||||
|
@ -105,15 +105,10 @@ class DirectoryNodeHandler(ReplaceMeMixin, Resource, object):
|
||||
# or no further children) renders "this" page
|
||||
name = name.decode('utf8')
|
||||
if not name:
|
||||
# replicating Nevow behavior that complains about "empty
|
||||
# path segments" .. but twisted.web sends in "name=''"
|
||||
# for a URL like "/foo/bar/" as well as "/foo//bar"
|
||||
# (i.e. a trailing slash means "name=''" as well)
|
||||
if b'//' in req.path:
|
||||
raise EmptyPathnameComponentError(
|
||||
u"The webapi does not allow empty pathname components, i.e. a double slash",
|
||||
)
|
||||
return self
|
||||
raise EmptyPathnameComponentError(
|
||||
u"The webapi does not allow empty pathname components",
|
||||
)
|
||||
|
||||
d = self.node.get(name)
|
||||
d.addBoth(self._got_child, req, name)
|
||||
return d
|
||||
|
Loading…
x
Reference in New Issue
Block a user