Merge remote-tracking branch 'origin/master' into 3428.dont-return-deferred-to-twistedweb.2
This commit is contained in:
commit
7ca8ede88c
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue