/usr/lib/python2.7/dist-packages/maasserver/api_support.py is in python-django-maas 1.5+bzr2252-0ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | # Copyright 2012 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Supporting infrastructure for Piston-based APIs in MAAS."""
from __future__ import (
absolute_import,
print_function,
unicode_literals,
)
str = None
__metaclass__ = type
__all__ = [
'admin_method',
'AnonymousOperationsHandler',
'operation',
'OperationsHandler',
]
from functools import wraps
from django.core.exceptions import PermissionDenied
from django.http import Http404
from maasserver.exceptions import MAASAPIBadRequest
from piston.handler import (
AnonymousBaseHandler,
BaseHandler,
HandlerMetaClass,
)
from piston.resource import Resource
from piston.utils import (
HttpStatusCode,
rc,
)
class OperationsResource(Resource):
"""A resource supporting operation dispatch.
All requests are passed onto the handler's `dispatch` method. See
:class:`OperationsHandler`.
"""
crudmap = Resource.callmap
callmap = dict.fromkeys(crudmap, "dispatch")
def error_handler(self, e, request, meth, em_format):
"""
Override piston's error_handler to fix bug #1228205 and generally
do not hide exceptions.
"""
if isinstance(e, Http404):
return rc.NOT_FOUND
elif isinstance(e, HttpStatusCode):
return e.response
else:
raise
class RestrictedResource(OperationsResource):
"""A resource that's restricted to active users."""
def authenticate(self, request, rm):
actor, anonymous = super(
RestrictedResource, self).authenticate(request, rm)
if not anonymous and not request.user.is_active:
raise PermissionDenied("User is not allowed access to this API.")
else:
return actor, anonymous
class AdminRestrictedResource(RestrictedResource):
"""A resource that's restricted to administrators."""
def authenticate(self, request, rm):
actor, anonymous = super(
AdminRestrictedResource, self).authenticate(request, rm)
if anonymous or not request.user.is_superuser:
raise PermissionDenied("User is not allowed access to this API.")
else:
return actor, anonymous
def operation(idempotent, exported_as=None):
"""Decorator to make a method available on the API.
:param idempotent: If this operation is idempotent. Idempotent operations
are made available via HTTP GET, non-idempotent operations via HTTP
POST.
:param exported_as: Optional operation name; defaults to the name of the
exported method.
"""
method = "GET" if idempotent else "POST"
def _decorator(func):
if exported_as is None:
func.export = method, func.__name__
else:
func.export = method, exported_as
return func
return _decorator
METHOD_RESERVED_ADMIN = "This method is reserved for admin users."
def admin_method(func):
"""Decorator to protect a method from non-admin users.
If a non-admin tries to call a methode decorated with this decorator,
they will get an HTTP "forbidden" error and a message saying the
operation is accessible only to administrators.
"""
@wraps(func)
def wrapper(self, request, *args, **kwargs):
if not request.user.is_superuser:
raise PermissionDenied(METHOD_RESERVED_ADMIN)
else:
return func(self, request, *args, **kwargs)
return wrapper
class OperationsHandlerType(HandlerMetaClass):
"""Type for handlers that dispatch operations.
Collects all the exported operations, CRUD and custom, into the class's
`exports` attribute. This is a signature:function mapping, where signature
is an (http-method, operation-name) tuple. If operation-name is None, it's
a CRUD method.
The `allowed_methods` attribute is calculated as the union of all HTTP
methods required for the exported CRUD and custom operations.
"""
def __new__(metaclass, name, bases, namespace):
cls = super(OperationsHandlerType, metaclass).__new__(
metaclass, name, bases, namespace)
# Create a signature:function mapping for CRUD operations.
crud = {
(http_method, None): getattr(cls, method)
for http_method, method in OperationsResource.crudmap.items()
if getattr(cls, method, None) is not None
}
# Create a signature:function mapping for non-CRUD operations.
operations = {
attribute.export: attribute
for attribute in vars(cls).values()
if getattr(attribute, "export", None) is not None
}
# Create the exports mapping.
exports = {}
exports.update(crud)
exports.update(operations)
# Update the class.
cls.exports = exports
cls.allowed_methods = frozenset(
http_method for http_method, name in exports)
return cls
class OperationsHandlerMixin:
"""Handler mixin for operations dispatch.
This enabled dispatch to custom functions that piggyback on HTTP methods
that ordinarily, in Piston, are used for CRUD operations.
This must be used in cooperation with :class:`OperationsResource` and
:class:`OperationsHandlerType`.
"""
def dispatch(self, request, *args, **kwargs):
signature = request.method.upper(), request.REQUEST.get("op")
function = self.exports.get(signature)
if function is None:
# Can't use MAASAPIBadRequest here because it derives from
# Exception, which gets Piston all confused for some reason.
raise MAASAPIBadRequest(
"Unrecognised signature: %s %s" % signature)
else:
return function(self, request, *args, **kwargs)
class OperationsHandler(
OperationsHandlerMixin, BaseHandler):
"""Base handler that supports operation dispatch."""
__metaclass__ = OperationsHandlerType
class AnonymousOperationsHandler(
OperationsHandlerMixin, AnonymousBaseHandler):
"""Anonymous base handler that supports operation dispatch."""
__metaclass__ = OperationsHandlerType
|