Merge remote-tracking branch 'origin/master' into 3428.dont-return-deferred-to-twistedweb.2

This commit is contained in:
Jean-Paul Calderone 2020-10-21 07:35:10 -04:00
commit 7ca8ede88c
6 changed files with 16 additions and 160 deletions

View File

@ -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

View File

@ -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,
)

0
newsfragments/3481.minor Normal file
View File

0
newsfragments/3482.minor Normal file
View File

View File

@ -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)

View File

@ -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