mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-02-01 08:48:01 +00:00
web: remove /vdrive/private, replace with a start.html file that points at the /uri/PRIVATE_URI, to prevent XSRF attacks
This commit is contained in:
parent
8717905e35
commit
4de5767c98
2
setup.py
2
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",
|
||||
)
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
'</td>\s+<td>DIR-RO</td>', 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):
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user