mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-02-22 02:16:42 +00:00
Merge pull request #867 from tahoe-lafs/3482.minus-tokenonlywebapi
Remove TokenOnlyWebApi Fixes: ticket:3482
This commit is contained in:
commit
7bff8396a2
0
newsfragments/3482.minor
Normal file
0
newsfragments/3482.minor
Normal 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)
|
|
@ -37,7 +37,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,
|
||||||
@ -426,59 +425,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 server.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(f):
|
def exception_to_child(f):
|
||||||
"""
|
"""
|
||||||
Decorate ``getChild`` method with exception handling behavior to render an
|
Decorate ``getChild`` method with exception handling behavior to render an
|
||||||
|
Loading…
x
Reference in New Issue
Block a user