# Copyright 2011 Dustin C. Hatch
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
'''Request authorization
:Created: Apr 5, 2011
:Author: dustin
:Updated: $Date$
:Updater: $Author$
'''
[docs]class NotAuthorized(Exception):
'''Base class for unauthorized exceptions
This class is both an exception and a controller callable. If the
request validator raises an instance of this class, it will be
called and the resulting value will become the HTTP response. The
default implementation simply returns HTTP status 403 and a simple
body containing the exception message.
'''
def __call__(self, request, *args, **kwargs):
'''Return a response indicating the request is not authorized
:param request: WebOb Request instance for the current request
All other arguments and keywords are ignored.
'''
response = request.ResponseClass(str(self))
response.status_int = 403
return response
[docs]class RequestValidator(object):
'''Base class for request validators
A request validator is a class that exposes a ``validate`` method,
which accepts an instance of :py:class:`webob.Request` and an
optional ``requirement``. The ``validate`` method should return
``None`` on successful validation, or raise an instance of
:py:exc:`NotAuthorized` on failure. The base implementation will
raise an instance of the exception specified by
:py:attr:`exc_class`, which defaults to :py:class`NotAuthorized`.
To customize the response to unauthorized requests, it is
sufficient to subclass :py:class:`NotAuthorized`, override its
:py:meth:`~NotAuthorized.__call__` method, and specify the class
in :py:attr:`exc_class`.
'''
#: Exception class to raise if the request is unauthorized
exc_class = NotAuthorized
[docs] def validate(self, request, requirement=None):
'''Validates a request
:param request: The request to validate. Should be an instance
of :py:class:`webob.Request`.
:param requirement: (Optional) A requirement to check. Should be
an instance of :py:class:`~milla.auth.permissions.Permission`
or :py:class:`~milla.auth.permissions.PermissionRequirement`,
or some other class with a ``check`` method that accepts a
sequence of permissions.
The base implementation will perform authorization in the
following way:
1. Does the ``request`` have a ``user`` attribute? If not,
raise :py:exc:`NotAuthorized`.
2. Is the truth value of ``request.user`` true? If not, raise
:py:exc:`NotAuthorized`.
3. Does the ``request.user`` object have a ``permissions``
attribute? If not, raise :py:exc:`NotAuthorized`.
4. Do the user's permissions meet the requirements? If not,
raise :py:exc:`NotAuthorized`.
If none of the above steps raised an exception, the method will
return ``None``, indicating that the validation was successful.
.. note:: WebOb Request instances do not have a ``user``
attribute by default. You will need to supply this yourself,
i.e. in a WSGI middleware or in the ``__before__`` method of
your controller class.
'''
try:
user = request.user
except AttributeError:
# No user associated with the request at all
raise self.exc_class('Request has no user')
if not user:
raise self.exc_class('Anonymous not allowed')
if requirement:
try:
user_perms = user.permissions
except AttributeError:
raise self.exc_class('User has no permissions')
if not requirement.check(user_perms):
raise self.exc_class('User does not have required permissions')