diff options
Diffstat (limited to 'src/mailman/rest/root.py')
| -rw-r--r-- | src/mailman/rest/root.py | 100 |
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>] |
