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 tempfile import mkdtemp, mktemp
from functools import partial from functools import partial
from foolscap.furl import (
decode_furl,
)
from eliot import ( from eliot import (
to_file, to_file,
log_call, log_call,
@ -226,6 +230,16 @@ def introducer_furl(introducer, temp_dir):
print("Don't see {} yet".format(furl_fname)) print("Don't see {} yet".format(furl_fname))
sleep(.1) sleep(.1)
furl = open(furl_fname, 'r').read() 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 return furl

View File

@ -1,7 +1,7 @@
import sys import sys
import time import time
import json import json
from os import mkdir from os import mkdir, environ
from os.path import exists, join from os.path import exists, join
from six.moves import StringIO from six.moves import StringIO
from functools import partial from functools import partial
@ -145,6 +145,7 @@ def _tahoe_runner_optional_coverage(proto, reactor, request, other_args):
proto, proto,
sys.executable, sys.executable,
args, 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, SDMF_VERSION,
) )
from allmydata.mutable.common import UnrecoverableFileError from allmydata.mutable.common import UnrecoverableFileError
from allmydata.util.hashutil import timing_safe_compare
from allmydata.util.time_format import ( from allmydata.util.time_format import (
format_delta, format_delta,
format_time, format_time,
@ -423,59 +422,6 @@ class SlotsSequenceElement(template.Element):
return tag 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): def exception_to_child(getChild):
""" """
Decorate ``getChild`` method with exception handling behavior to render an Decorate ``getChild`` method with exception handling behavior to render an