From: Brian Warner Date: Wed, 22 Aug 2007 21:54:34 +0000 (-0700) Subject: web: remove /vdrive/private, replace with a start.html file that points at the /uri... X-Git-Url: https://git.rkrishnan.org/pf/content//%22%3C?a=commitdiff_plain;h=4de5767c98e548b79b967febf05d7ba6cc257d27;p=tahoe-lafs%2Ftahoe-lafs.git web: remove /vdrive/private, replace with a start.html file that points at the /uri/PRIVATE_URI, to prevent XSRF attacks --- diff --git a/setup.py b/setup.py index 726dd47b..2066bdde 100644 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ majority of the nodes are no longer available.""", "allmydata.scripts",], package_dir={ "allmydata": "src/allmydata",}, scripts = ["bin/allmydata-tahoe"], - package_data={ 'allmydata': ['web/*.xhtml', 'web/*.css'] }, + package_data={ 'allmydata': ['web/*.xhtml', 'web/*.html', 'web/*.css'] }, classifiers=trove_classifiers, test_suite="allmydata.test", ) diff --git a/src/allmydata/client.py b/src/allmydata/client.py index 5976a7b1..55e364ad 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -50,10 +50,7 @@ class Client(node.Node, Referenceable): except EnvironmentError: pass # absent or unreadable webport file else: - ws = WebishServer(webport) - ws.allow_local_access(os.path.exists(os.path.join(self.basedir, - self.WEB_ALLOW_LOCAL_ACCESS_FILE))) - self.add_service(ws) + self.init_web(webport) INTRODUCER_FURL_FILE = os.path.join(self.basedir, self.INTRODUCER_FURL_FILE) @@ -100,6 +97,18 @@ class Client(node.Node, Referenceable): if os.path.exists(filename): self.push_to_ourselves = True + def init_web(self, webport): + # this must be called after the VirtualDrive is attached + ws = WebishServer(webport) + ws.allow_local_access(os.path.exists(os.path.join(self.basedir, + self.WEB_ALLOW_LOCAL_ACCESS_FILE))) + self.add_service(ws) + vd = self.getServiceNamed("vdrive") + startfile = os.path.join(self.basedir, "start.html") + d = vd.when_private_root_available() + d.addCallback(ws.create_start_html, startfile) + + def _check_hotline(self, hotline_file): if os.path.exists(hotline_file): mtime = os.stat(hotline_file)[stat.ST_MTIME] diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py index 09c9e243..606839cd 100644 --- a/src/allmydata/test/test_system.py +++ b/src/allmydata/test/test_system.py @@ -285,6 +285,7 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase): d.addCallback(self._check_publish_private) d.addCallback(self.log, "did _check_publish_private") d.addCallback(self._test_web) + d.addCallback(self._test_web_start) d.addCallback(self._test_runner) return d test_vdrive.timeout = 1100 @@ -582,6 +583,7 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase): d.addCallback(lambda res: self.GET("vdrive/global/subdir3/new.txt")) d.addCallback(self.failUnlessEqual, "NEWER contents") + # TODO: mangle the second segment of a file, to test errors that # occur after we've already sent some good data, which uses a # different error path. @@ -594,6 +596,16 @@ class SystemTest(testutil.SignalMixin, unittest.TestCase): return d + def _test_web_start(self, res): + basedir = self.clients[0].basedir + startfile = os.path.join(basedir, "start.html") + self.failUnless(os.path.exists(startfile)) + start_html = open(startfile, "r").read() + self.failUnless(self.webish_url in start_html) + private_uri = self.clients[0].getServiceNamed("vdrive")._private_uri + private_url = self.webish_url + "uri/" + private_uri.replace("/","!") + self.failUnless(private_url in start_html) + def _test_runner(self, res): # exercise some of the diagnostic tools in runner.py diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py index b1edc1e1..74fcec6d 100644 --- a/src/allmydata/test/test_web.py +++ b/src/allmydata/test/test_web.py @@ -16,6 +16,7 @@ import itertools class MyClient(service.MultiService): nodeid = "fake_nodeid" + basedir = "fake_basedir" def get_versions(self): return {'allmydata': "fake", 'foolscap': "fake", @@ -378,10 +379,33 @@ class Web(WebMixin, unittest.TestCase): self.failUnless('Welcome To AllMyData' in res) self.failUnless('Tahoe' in res) self.failUnless('To view the global shared filestore' in res) - self.failUnless('To view your personal private non-shared' in res) + self.failUnless('personal vdrive not available.' in res) + + self.s.basedir = 'web/test_welcome' + fileutil.make_dirs("web/test_welcome") + self.ws.create_start_html("private_uri", + "web/test_welcome/start.html") + return self.GET("/") d.addCallback(_check) + def _check2(res): + self.failUnless('To view your personal private non-shared' in res) + self.failUnless('from your local filesystem:' in res) + self.failUnless(os.path.abspath('web/test_welcome/start.html') + in res) + d.addCallback(_check2) return d + def test_start_html(self): + fileutil.make_dirs("web") + startfile = "web/start.html" + self.ws.create_start_html("private_uri", startfile) + + self.failUnless(os.path.exists(startfile)) + start_html = open(startfile, "r").read() + self.failUnless(self.webish_url in start_html) + private_url = self.webish_url + "/uri/private_uri" + self.failUnless(private_url in start_html) + def test_GET_FILEURL(self): d = self.GET("/vdrive/global/foo/bar.txt") d.addCallback(self.failUnlessIsBarDotTxt) @@ -646,13 +670,6 @@ class Web(WebMixin, unittest.TestCase): '\s+DIR-RO', res)) d.addCallback(_check3) - # and take a quick peek at the private vdrive - d.addCallback(lambda res: - self.GET("/vdrive/private", followRedirect=True)) - def _check4(res): - pass - d.addCallback(_check4) - return d def test_GET_DIRURL_json(self): diff --git a/src/allmydata/vdrive.py b/src/allmydata/vdrive.py index 694fb020..7bafda49 100644 --- a/src/allmydata/vdrive.py +++ b/src/allmydata/vdrive.py @@ -3,6 +3,7 @@ import os from twisted.application import service from zope.interface import implements from allmydata.interfaces import IVirtualDrive, IDirnodeURI, IURI +from allmydata.util import observer from allmydata import dirnode from twisted.internet import defer @@ -24,6 +25,7 @@ class VirtualDrive(service.MultiService): service.MultiService.__init__(self) self._global_uri = None self._private_uri = None + self._private_root_observer = observer.OneShotObserverList() def log(self, msg): self.parent.log(msg) @@ -46,6 +48,7 @@ class VirtualDrive(service.MultiService): self._private_uri = f.read().strip() f.close() self.log("using private vdrive uri %s" % self._private_uri) + self._private_root_observer.fire(self._private_uri) furl_file = os.path.join(basedir, self.GLOBAL_VDRIVE_FURL_FILE) if os.path.exists(furl_file): @@ -94,6 +97,7 @@ class VirtualDrive(service.MultiService): f = open(private_uri_file, "w") f.write(self._private_uri + "\n") f.close() + self._private_root_observer.fire(self._private_uri) d.addCallback(_got_directory) @@ -104,6 +108,15 @@ class VirtualDrive(service.MultiService): return defer.fail(NoGlobalVirtualDriveError()) return self.get_node(self._global_uri) + def when_private_root_available(self): + """Return a Deferred that will fire with the URI of the private + vdrive root, when it is available. + + This might be right away if the private vdrive was already present. + The first time the node is started, this will take a bit longer. + """ + return self._private_root_observer.when_fired() + def have_private_root(self): return bool(self._private_uri) def get_private_root(self): diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py index 14ed4111..76bcf8d4 100644 --- a/src/allmydata/webish.py +++ b/src/allmydata/webish.py @@ -1,7 +1,7 @@ from base64 import b32encode import os.path -from twisted.application import service, strports +from twisted.application import service, strports, internet from twisted.web import static, resource, server, html, http from twisted.python import util, log from twisted.internet import defer @@ -1021,9 +1021,6 @@ class Root(rend.Page): if segments[1] == "global": d = vdrive.get_public_root() name = "public vdrive" - elif segments[1] == "private": - d = vdrive.get_private_root() - name = "private vdrive" else: return rend.NotFound d.addCallback(lambda dirnode: VDrive(dirnode, name)) @@ -1099,9 +1096,13 @@ class Root(rend.Page): "responding), no vdrive available."] def render_private_vdrive(self, ctx, data): - if IClient(ctx).getServiceNamed("vdrive").have_private_root(): + basedir = IClient(ctx).basedir + start_html = os.path.abspath(os.path.join(basedir, "start.html")) + if os.path.exists(start_html): return T.p["To view your personal private non-shared filestore, ", - T.a(href="vdrive/private")["Click Here!"], + "use this browser to open the following file from ", + "your local filesystem:", + T.pre[start_html], ] return T.p["personal vdrive not available."] @@ -1131,8 +1132,9 @@ class LocalAccess: class WebishServer(service.MultiService): name = "webish" - def __init__(self, webport, local_access=False): + def __init__(self, webport): service.MultiService.__init__(self) + self.webport = webport self.root = Root() self.site = site = appserver.NevowSite(self.root) self.site.requestFactory = MyRequest @@ -1155,3 +1157,23 @@ class WebishServer(service.MultiService): # I thought you could do the same with an existing interface, but # apparently 'ISite' does not exist #self.site._client = self.parent + + def create_start_html(self, private_uri, startfile): + f = open(startfile, "w") + os.chmod(startfile, 0600) + template = open(util.sibpath(__file__, "web/start.html"), "r").read() + # what is our webport? + s = self.listener + if isinstance(s, internet.TCPServer): + base_url = "http://localhost:%d" % s._port.getHost().port + elif isinstance(s, internet.SSLServer): + base_url = "https://localhost:%d" % s._port.getHost().port + else: + base_url = "UNKNOWN" # this will break the href + # TODO: emit a start.html that explains that we don't know + # how to create a suitable URL + fields = {"private_uri": private_uri.replace("/","!"), + "base_url": base_url, + } + f.write(template % fields) + f.close()