summaryrefslogtreecommitdiff
path: root/src/mailman/rest/root.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/rest/root.py')
-rw-r--r--src/mailman/rest/root.py100
1 files changed, 66 insertions, 34 deletions
diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py
index ec0c9c93b..6b13d8246 100644
--- a/src/mailman/rest/root.py
+++ b/src/mailman/rest/root.py
@@ -25,8 +25,10 @@ __all__ = [
]
+import falcon
+
from base64 import b64decode
-from restish import guard, http, resource
+from restish import http
from zope.component import getUtility
from mailman.config import config
@@ -44,21 +46,25 @@ from mailman.rest.templates import TemplateFinder
from mailman.rest.users import AUser, AllUsers
-
-def webservice_auth_checker(request, obj):
- auth = request.environ.get('HTTP_AUTHORIZATION', '')
- if auth.startswith('Basic '):
- credentials = b64decode(auth[6:])
- username, password = credentials.split(':', 1)
- if (username != config.webservice.admin_user or
- password != config.webservice.admin_pass):
- # Not authorized.
- raise guard.GuardError(b'User is not authorized for the REST API')
- else:
- raise guard.GuardError(b'The REST API requires authentication')
+def child(matcher=None):
+ def decorator(func):
+ if matcher is None:
+ func.__matcher__ = func.__name__
+ else:
+ func.__matcher__ = matcher
+ return func
+ return decorator
+
+
+class BadRequest:
+ def on_get(self, request, response):
+ raise falcon.HTTPError(falcon.HTTP_400, None)
+
+STOP = []
-class Root(resource.Resource):
+
+class Root:
"""The RESTful root resource.
At the root of the tree are the API version numbers. Everything else
@@ -67,33 +73,59 @@ class Root(resource.Resource):
always be the case though.
"""
- @resource.child(config.webservice.api_version)
- @guard.guard(webservice_auth_checker)
+ @child(config.webservice.api_version)
def api_version(self, request, segments):
- return TopLevel()
+ # We have to do this here instead of in a @falcon.before() handler
+ # because those handlers are not compatible with our custom traversal
+ # logic. Specifically, falcon's before/after handlers will call the
+ # responder, but the method we're wrapping isn't a responder, it's a
+ # child traversal method. There's no way to cause the thing that
+ # calls the before hook to follow through with the child traversal in
+ # the case where no error is raised.
+ if request.auth is None:
+ raise falcon.HTTPUnauthorized(
+ b'401 Unauthorized',
+ b'The REST API requires authentication')
+ if request.auth.startswith('Basic '):
+ credentials = b64decode(request.auth[6:])
+ username, password = credentials.split(':', 1)
+ if (username != config.webservice.admin_user or
+ password != config.webservice.admin_pass):
+ # Not authorized.
+ raise falcon.HTTPUnauthorized(
+ b'401 Unauthorized',
+ b'User is not authorized for the REST API')
+ return TopLevel(), segments
+
+
+class System:
+ def on_get(self, request, response):
+ """/<api>/system"""
+ resource = dict(
+ mailman_version=system.mailman_version,
+ python_version=system.python_version,
+ self_link=path_to('system'),
+ )
+ response.status = falcon.HTTP_200
+ response.body = etag(resource)
-class TopLevel(resource.Resource):
+class TopLevel:
"""Top level collections and entries."""
- @resource.child()
+ @child()
def system(self, request, segments):
"""/<api>/system"""
if len(segments) == 0:
- resource = dict(
- mailman_version=system.mailman_version,
- python_version=system.python_version,
- self_link=path_to('system'),
- )
+ return System(), STOP
elif len(segments) > 1:
- return http.bad_request()
+ return BadRequest(), STOP
elif segments[0] == 'preferences':
- return ReadOnlyPreferences(system_preferences, 'system'), []
+ return ReadOnlyPreferences(system_preferences, 'system'), STOP
else:
- return http.bad_request()
- return http.ok([], etag(resource))
+ return BadRequest(), STOP
- @resource.child()
+ @child()
def addresses(self, request, segments):
"""/<api>/addresses
/<api>/addresses/<email>
@@ -104,7 +136,7 @@ class TopLevel(resource.Resource):
email = segments.pop(0)
return AnAddress(email), segments
- @resource.child()
+ @child()
def domains(self, request, segments):
"""/<api>/domains
/<api>/domains/<domain>
@@ -115,7 +147,7 @@ class TopLevel(resource.Resource):
domain = segments.pop(0)
return ADomain(domain), segments
- @resource.child()
+ @child()
def lists(self, request, segments):
"""/<api>/lists
/<api>/lists/<list>
@@ -135,7 +167,7 @@ class TopLevel(resource.Resource):
list_identifier = segments.pop(0)
return AList(list_identifier), segments
- @resource.child()
+ @child()
def members(self, request, segments):
"""/<api>/members"""
if len(segments) == 0:
@@ -148,7 +180,7 @@ class TopLevel(resource.Resource):
else:
return AMember(segment), segments
- @resource.child()
+ @child()
def users(self, request, segments):
"""/<api>/users"""
if len(segments) == 0:
@@ -157,7 +189,7 @@ class TopLevel(resource.Resource):
user_id = segments.pop(0)
return AUser(user_id), segments
- @resource.child()
+ @child()
def templates(self, request, segments):
"""/<api>/templates/<fqdn_listname>/<template>/[<language>]