diff --git a/integration/conftest.py b/integration/conftest.py index 04e3dcb52..802deb79d 100644 --- a/integration/conftest.py +++ b/integration/conftest.py @@ -8,6 +8,10 @@ from os.path import join, exists from tempfile import mkdtemp, mktemp from functools import partial +from foolscap.furl import ( + decode_furl, +) + from eliot import ( to_file, log_call, @@ -226,6 +230,16 @@ def introducer_furl(introducer, temp_dir): print("Don't see {} yet".format(furl_fname)) sleep(.1) furl = open(furl_fname, 'r').read() + tubID, location_hints, name = decode_furl(furl) + if not location_hints: + # If there are no location hints then nothing can ever possibly + # connect to it and the only thing that can happen next is something + # will hang or time out. So just give up right now. + raise ValueError( + "Introducer ({!r}) fURL has no location hints!".format( + introducer_furl, + ), + ) return furl diff --git a/integration/util.py b/integration/util.py index bbcf5efc6..a64bcbf8e 100644 --- a/integration/util.py +++ b/integration/util.py @@ -1,7 +1,7 @@ import sys import time import json -from os import mkdir +from os import mkdir, environ from os.path import exists, join from six.moves import StringIO from functools import partial @@ -145,6 +145,7 @@ def _tahoe_runner_optional_coverage(proto, reactor, request, other_args): proto, sys.executable, args, + env=environ, ) diff --git a/newsfragments/3481.minor b/newsfragments/3481.minor new file mode 100644 index 000000000..e69de29bb diff --git a/newsfragments/3482.minor b/newsfragments/3482.minor new file mode 100644 index 000000000..e69de29bb diff --git a/src/allmydata/test/web/test_token.py b/src/allmydata/test/web/test_token.py deleted file mode 100644 index a439702af..000000000 --- a/src/allmydata/test/web/test_token.py +++ /dev/null @@ -1,105 +0,0 @@ -from zope.interface import implementer -from twisted.trial import unittest -from twisted.web import server -from nevow.inevow import IRequest -from allmydata.web import common - -# XXX FIXME when we introduce "mock" as a dependency, these can -# probably just be Mock instances -@implementer(IRequest) -class FakeRequest(object): - def __init__(self): - self.method = "POST" - self.fields = dict() - self.args = dict() - - -class FakeField(object): - def __init__(self, *values): - if len(values) == 1: - self.value = values[0] - else: - self.value = list(values) - - -class FakeClientWithToken(object): - token = 'a' * 32 - - def get_auth_token(self): - return self.token - - -class TestTokenOnlyApi(unittest.TestCase): - - def setUp(self): - self.client = FakeClientWithToken() - self.page = common.TokenOnlyWebApi(self.client) - - def test_not_post(self): - req = FakeRequest() - req.method = "GET" - - self.assertRaises( - server.UnsupportedMethod, - self.page.render, req, - ) - - def test_missing_token(self): - req = FakeRequest() - - exc = self.assertRaises( - common.WebError, - self.page.render, req, - ) - self.assertEquals(exc.text, "Missing token") - self.assertEquals(exc.code, 401) - - def test_token_in_get_args(self): - req = FakeRequest() - req.args['token'] = 'z' * 32 - - exc = self.assertRaises( - common.WebError, - self.page.render, req, - ) - self.assertEquals(exc.text, "Do not pass 'token' as URL argument") - self.assertEquals(exc.code, 400) - - def test_invalid_token(self): - wrong_token = 'b' * 32 - req = FakeRequest() - req.fields['token'] = FakeField(wrong_token) - - exc = self.assertRaises( - common.WebError, - self.page.render, req, - ) - self.assertEquals(exc.text, "Invalid token") - self.assertEquals(exc.code, 401) - - def test_valid_token_no_t_arg(self): - req = FakeRequest() - req.fields['token'] = FakeField(self.client.token) - - with self.assertRaises(common.WebError) as exc: - self.page.render(req) - self.assertEquals(exc.exception.text, "Must provide 't=' argument") - self.assertEquals(exc.exception.code, 400) - - def test_valid_token_invalid_t_arg(self): - req = FakeRequest() - req.fields['token'] = FakeField(self.client.token) - req.args['t'] = 'not at all json' - - with self.assertRaises(common.WebError) as exc: - self.page.render(req) - self.assertTrue("invalid type" in exc.exception.text) - self.assertEquals(exc.exception.code, 400) - - def test_valid(self): - req = FakeRequest() - req.fields['token'] = FakeField(self.client.token) - req.args['t'] = ['json'] - - result = self.page.render(req) - self.assertTrue(result == NotImplemented) diff --git a/src/allmydata/web/common.py b/src/allmydata/web/common.py index 6c8b557fa..c9d59e7ae 100644 --- a/src/allmydata/web/common.py +++ b/src/allmydata/web/common.py @@ -72,7 +72,6 @@ from allmydata.interfaces import ( SDMF_VERSION, ) from allmydata.mutable.common import UnrecoverableFileError -from allmydata.util.hashutil import timing_safe_compare from allmydata.util.time_format import ( format_delta, format_time, @@ -423,59 +422,6 @@ class SlotsSequenceElement(template.Element): return tag -class TokenOnlyWebApi(resource.Resource, object): - """ - I provide a rend.Page implementation that only accepts POST calls, - and only if they have a 'token=' arg with the correct - authentication token (see - :meth:`allmydata.client.Client.get_auth_token`). Callers must also - provide the "t=" argument to indicate the return-value (the only - valid value for this is "json") - - Subclasses should override 'post_json' which should process the - API call and return a string which encodes a valid JSON - object. This will only be called if the correct token is present - and valid (during renderHTTP processing). - """ - - def __init__(self, client): - self.client = client - - def post_json(self, req): - return NotImplemented - - def render(self, req): - if req.method != 'POST': - raise UnsupportedMethod(('POST',)) - if req.args.get('token', False): - raise WebError("Do not pass 'token' as URL argument", http.BAD_REQUEST) - # not using get_arg() here because we *don't* want the token - # argument to work if you passed it as a GET-style argument - token = None - if req.fields and 'token' in req.fields: - token = req.fields['token'].value.strip() - if not token: - raise WebError("Missing token", http.UNAUTHORIZED) - if not timing_safe_compare(token, self.client.get_auth_token()): - raise WebError("Invalid token", http.UNAUTHORIZED) - - t = get_arg(req, "t", "").strip() - if not t: - raise WebError("Must provide 't=' argument") - if t == u'json': - try: - return self.post_json(req) - except WebError as e: - req.setResponseCode(e.code) - return json.dumps({"error": e.text}) - except Exception as e: - message, code = humanize_exception(e) - req.setResponseCode(500 if code is None else code) - return json.dumps({"error": message}) - else: - raise WebError("'%s' invalid type for 't' arg" % (t,), http.BAD_REQUEST) - - def exception_to_child(getChild): """ Decorate ``getChild`` method with exception handling behavior to render an