Whitespace cleanup
parent
6519cfbb9e
commit
86b19bb9e7
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -21,17 +21,18 @@ from webob.exc import *
|
||||||
import webob
|
import webob
|
||||||
try:
|
try:
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
except ImportError: #pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
import urllib
|
import urllib
|
||||||
import urlparse
|
import urlparse
|
||||||
urllib.parse = urlparse
|
urllib.parse = urlparse
|
||||||
urllib.parse.urlencode = urllib.urlencode #@UndefinedVariable
|
urllib.parse.urlencode = urllib.urlencode
|
||||||
|
|
||||||
|
|
||||||
def allow(*methods):
|
def allow(*methods):
|
||||||
'''Specify the allowed HTTP verbs for a controller callable
|
'''Specify the allowed HTTP verbs for a controller callable
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
@milla.allow('GET', 'POST')
|
@milla.allow('GET', 'POST')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'Hello, world!'
|
return 'Hello, world!'
|
||||||
|
@ -56,12 +57,12 @@ class Request(webob.Request):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def blank(cls, path, *args, **kwargs):
|
def blank(cls, path, *args, **kwargs):
|
||||||
'''Create a simple request for the specified path
|
'''Create a simple request for the specified path
|
||||||
|
|
||||||
See :py:meth:`webob.Request.blank <webob.request.BaseRequest.blank>`
|
See :py:meth:`webob.Request.blank <webob.request.BaseRequest.blank>`
|
||||||
for information on other arguments and keywords
|
for information on other arguments and keywords
|
||||||
'''
|
'''
|
||||||
|
|
||||||
req = super(Request, cls).blank(path, *args, **kwargs)
|
req = super(Request, cls).blank(path, *args, **kwargs)
|
||||||
req.config = {}
|
req.config = {}
|
||||||
return req
|
return req
|
||||||
|
@ -73,7 +74,7 @@ class Request(webob.Request):
|
||||||
|
|
||||||
Any other keyword arguments will be encoded and appended to the URL
|
Any other keyword arguments will be encoded and appended to the URL
|
||||||
as querystring arguments.
|
as querystring arguments.
|
||||||
|
|
||||||
The HREF returned will will be the absolute path on the same host
|
The HREF returned will will be the absolute path on the same host
|
||||||
and protocol as the request. To get the full URL including scheme
|
and protocol as the request. To get the full URL including scheme
|
||||||
and host information, use :py:meth:`create_href_full` instead.
|
and host information, use :py:meth:`create_href_full` instead.
|
||||||
|
@ -88,22 +89,22 @@ class Request(webob.Request):
|
||||||
|
|
||||||
def create_href_full(self, path, **keywords):
|
def create_href_full(self, path, **keywords):
|
||||||
'''Combine the application's full URL with a path to form a new URL
|
'''Combine the application's full URL with a path to form a new URL
|
||||||
|
|
||||||
:param path: relative path to join with the request URL
|
:param path: relative path to join with the request URL
|
||||||
|
|
||||||
Any other keyword arguments will be encoded and appended to the
|
Any other keyword arguments will be encoded and appended to the
|
||||||
URL as querystring arguments/
|
URL as querystring arguments/
|
||||||
|
|
||||||
The HREF returned will be the full URL, including scheme and host
|
The HREF returned will be the full URL, including scheme and host
|
||||||
information. To get the path only, use :py:meth:`create_href`
|
information. To get the path only, use :py:meth:`create_href`
|
||||||
instead.
|
instead.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
url = self._merge_url(self.application_url, path)
|
url = self._merge_url(self.application_url, path)
|
||||||
|
|
||||||
if keywords:
|
if keywords:
|
||||||
url += '?' + urllib.parse.urlencode(keywords)
|
url += '?' + urllib.parse.urlencode(keywords)
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def static_resource(self, path):
|
def static_resource(self, path):
|
||||||
|
|
|
@ -28,6 +28,7 @@ import milla.dispatch.traversal
|
||||||
|
|
||||||
__all__ = ['Application']
|
__all__ = ['Application']
|
||||||
|
|
||||||
|
|
||||||
class Application(object):
|
class Application(object):
|
||||||
'''Represents a Milla web application
|
'''Represents a Milla web application
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ class Application(object):
|
||||||
return response
|
return response
|
||||||
func = options_response
|
func = options_response
|
||||||
else:
|
else:
|
||||||
func = HTTPMethodNotAllowed(headers=allow_header)
|
func = HTTPMethodNotAllowed(headers=allow_header)
|
||||||
return func(environ, start_response)
|
return func(environ, start_response)
|
||||||
|
|
||||||
start_response_wrapper = StartResponseWrapper(start_response)
|
start_response_wrapper = StartResponseWrapper(start_response)
|
||||||
|
@ -118,7 +119,7 @@ class Application(object):
|
||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return response.app_iter
|
return response.app_iter
|
||||||
|
|
||||||
def _call_after(self, func):
|
def _call_after(self, func):
|
||||||
try:
|
try:
|
||||||
return self._find_attr(func, '__after__')
|
return self._find_attr(func, '__after__')
|
||||||
|
@ -130,7 +131,7 @@ class Application(object):
|
||||||
return self._find_attr(func, '__before__')
|
return self._find_attr(func, '__before__')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return lambda r: None
|
return lambda r: None
|
||||||
|
|
||||||
def _find_attr(self, obj, attr):
|
def _find_attr(self, obj, attr):
|
||||||
try:
|
try:
|
||||||
# Object has the specified attribute itself
|
# Object has the specified attribute itself
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
class NotAuthorized(Exception):
|
class NotAuthorized(Exception):
|
||||||
'''Base class for unauthorized exceptions
|
'''Base class for unauthorized exceptions
|
||||||
|
|
||||||
This class is both an exception and a controller callable. If the
|
This class is both an exception and a controller callable. If the
|
||||||
request validator raises an instance of this class, it will be
|
request validator raises an instance of this class, it will be
|
||||||
called and the resulting value will become the HTTP response. The
|
called and the resulting value will become the HTTP response. The
|
||||||
|
@ -33,17 +33,17 @@ class NotAuthorized(Exception):
|
||||||
'''Return a response indicating the request is not authorized
|
'''Return a response indicating the request is not authorized
|
||||||
|
|
||||||
:param request: WebOb Request instance for the current request
|
:param request: WebOb Request instance for the current request
|
||||||
|
|
||||||
All other arguments and keywords are ignored.
|
All other arguments and keywords are ignored.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
response = request.ResponseClass(str(self))
|
response = request.ResponseClass(str(self))
|
||||||
response.status_int = 403
|
response.status_int = 403
|
||||||
return response
|
return response
|
||||||
|
|
||||||
class RequestValidator(object):
|
class RequestValidator(object):
|
||||||
'''Base class for request validators
|
'''Base class for request validators
|
||||||
|
|
||||||
A request validator is a class that exposes a ``validate`` method,
|
A request validator is a class that exposes a ``validate`` method,
|
||||||
which accepts an instance of :py:class:`webob.Request` and an
|
which accepts an instance of :py:class:`webob.Request` and an
|
||||||
optional ``requirement``. The ``validate`` method should return
|
optional ``requirement``. The ``validate`` method should return
|
||||||
|
@ -51,7 +51,7 @@ class RequestValidator(object):
|
||||||
:py:exc:`NotAuthorized` on failure. The base implementation will
|
:py:exc:`NotAuthorized` on failure. The base implementation will
|
||||||
raise an instance of the exception specified by
|
raise an instance of the exception specified by
|
||||||
:py:attr:`exc_class`, which defaults to :py:class`NotAuthorized`.
|
:py:attr:`exc_class`, which defaults to :py:class`NotAuthorized`.
|
||||||
|
|
||||||
To customize the response to unauthorized requests, it is
|
To customize the response to unauthorized requests, it is
|
||||||
sufficient to subclass :py:class:`NotAuthorized`, override its
|
sufficient to subclass :py:class:`NotAuthorized`, override its
|
||||||
:py:meth:`~NotAuthorized.__call__` method, and specify the class
|
:py:meth:`~NotAuthorized.__call__` method, and specify the class
|
||||||
|
@ -63,7 +63,7 @@ class RequestValidator(object):
|
||||||
|
|
||||||
def validate(self, request, requirement=None):
|
def validate(self, request, requirement=None):
|
||||||
'''Validates a request
|
'''Validates a request
|
||||||
|
|
||||||
:param request: The request to validate. Should be an instance
|
:param request: The request to validate. Should be an instance
|
||||||
of :py:class:`webob.Request`.
|
of :py:class:`webob.Request`.
|
||||||
:param requirement: (Optional) A requirement to check. Should be
|
:param requirement: (Optional) A requirement to check. Should be
|
||||||
|
@ -71,10 +71,10 @@ class RequestValidator(object):
|
||||||
or :py:class:`~milla.auth.permissions.PermissionRequirement`,
|
or :py:class:`~milla.auth.permissions.PermissionRequirement`,
|
||||||
or some other class with a ``check`` method that accepts a
|
or some other class with a ``check`` method that accepts a
|
||||||
sequence of permissions.
|
sequence of permissions.
|
||||||
|
|
||||||
The base implementation will perform authorization in the
|
The base implementation will perform authorization in the
|
||||||
following way:
|
following way:
|
||||||
|
|
||||||
1. Does the ``request`` have a ``user`` attribute? If not,
|
1. Does the ``request`` have a ``user`` attribute? If not,
|
||||||
raise :py:exc:`NotAuthorized`.
|
raise :py:exc:`NotAuthorized`.
|
||||||
2. Is the truth value of ``request.user`` true? If not, raise
|
2. Is the truth value of ``request.user`` true? If not, raise
|
||||||
|
@ -83,16 +83,16 @@ class RequestValidator(object):
|
||||||
attribute? If not, raise :py:exc:`NotAuthorized`.
|
attribute? If not, raise :py:exc:`NotAuthorized`.
|
||||||
4. Do the user's permissions meet the requirements? If not,
|
4. Do the user's permissions meet the requirements? If not,
|
||||||
raise :py:exc:`NotAuthorized`.
|
raise :py:exc:`NotAuthorized`.
|
||||||
|
|
||||||
If none of the above steps raised an exception, the method will
|
If none of the above steps raised an exception, the method will
|
||||||
return ``None``, indicating that the validation was successful.
|
return ``None``, indicating that the validation was successful.
|
||||||
|
|
||||||
.. note:: WebOb Request instances do not have a ``user``
|
.. note:: WebOb Request instances do not have a ``user``
|
||||||
attribute by default. You will need to supply this yourself,
|
attribute by default. You will need to supply this yourself,
|
||||||
i.e. in a WSGI middleware or in the ``__before__`` method of
|
i.e. in a WSGI middleware or in the ``__before__`` method of
|
||||||
your controller class.
|
your controller class.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user = request.user
|
user = request.user
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -15,8 +15,6 @@
|
||||||
|
|
||||||
:Created: Mar 3, 2011
|
:Created: Mar 3, 2011
|
||||||
:Author: dustin
|
:Author: dustin
|
||||||
:Updated: $Date$
|
|
||||||
:Updater: $Author$
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -24,10 +22,13 @@ from milla.auth import RequestValidator, NotAuthorized, permissions
|
||||||
import milla
|
import milla
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['auth_required', 'require_perms']
|
__all__ = ['auth_required', 'require_perms']
|
||||||
|
|
||||||
|
|
||||||
VALIDATOR_EP_GROUP = 'milla.request_validator'
|
VALIDATOR_EP_GROUP = 'milla.request_validator'
|
||||||
|
|
||||||
|
|
||||||
def _find_request(*args, **kwargs):
|
def _find_request(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
return kwargs['request']
|
return kwargs['request']
|
||||||
|
@ -36,6 +37,7 @@ def _find_request(*args, **kwargs):
|
||||||
if isinstance(arg, milla.Request):
|
if isinstance(arg, milla.Request):
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
def _validate_request(func, requirement, *args, **kwargs):
|
def _validate_request(func, requirement, *args, **kwargs):
|
||||||
request = _find_request(*args, **kwargs)
|
request = _find_request(*args, **kwargs)
|
||||||
ep_name = request.config.get('request_validator', 'default')
|
ep_name = request.config.get('request_validator', 'default')
|
||||||
|
@ -55,20 +57,21 @@ def _validate_request(func, requirement, *args, **kwargs):
|
||||||
return e(request)
|
return e(request)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def auth_required(func):
|
def auth_required(func):
|
||||||
'''Simple decorator to enforce authentication for a controller
|
'''Simple decorator to enforce authentication for a controller
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
class SomeController(object):
|
class SomeController(object):
|
||||||
|
|
||||||
def __before__(request):
|
def __before__(request):
|
||||||
request.user = find_a_user_somehow(request)
|
request.user = find_a_user_somehow(request)
|
||||||
|
|
||||||
@milla.auth_required
|
@milla.auth_required
|
||||||
def __call__(request):
|
def __call__(request):
|
||||||
return 'Hello, world!'
|
return 'Hello, world!'
|
||||||
|
|
||||||
In this example, the ``SomeController`` controller class implements
|
In this example, the ``SomeController`` controller class implements
|
||||||
an ``__before__`` method that adds the ``user`` attribute to the
|
an ``__before__`` method that adds the ``user`` attribute to the
|
||||||
``request`` instance. This could be done by extracting user
|
``request`` instance. This could be done by extracting user
|
||||||
|
@ -76,7 +79,7 @@ def auth_required(func):
|
||||||
method is decorated with ``auth_required``, which will ensure that
|
method is decorated with ``auth_required``, which will ensure that
|
||||||
the user is successfully authenticated. This is handled by a
|
the user is successfully authenticated. This is handled by a
|
||||||
*request validator*.
|
*request validator*.
|
||||||
|
|
||||||
If the request is not authorized, the decorated method will never
|
If the request is not authorized, the decorated method will never
|
||||||
be called. Instead, the response is generated by calling the
|
be called. Instead, the response is generated by calling the
|
||||||
:py:exc:`~milla.auth.NotAuthorized` exception raised inside
|
:py:exc:`~milla.auth.NotAuthorized` exception raised inside
|
||||||
|
@ -88,20 +91,21 @@ def auth_required(func):
|
||||||
return _validate_request(func, None, *args, **kwargs)
|
return _validate_request(func, None, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class require_perms(object):
|
class require_perms(object):
|
||||||
'''Decorator that requires the user have certain permissions
|
'''Decorator that requires the user have certain permissions
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
class SomeController(object):
|
class SomeController(object):
|
||||||
|
|
||||||
def __before__(request):
|
def __before__(request):
|
||||||
request.user = find_a_user_somehow(request)
|
request.user = find_a_user_somehow(request)
|
||||||
|
|
||||||
@milla.require_perms('some_permission', 'and_this_permission')
|
@milla.require_perms('some_permission', 'and_this_permission')
|
||||||
def __call__(request):
|
def __call__(request):
|
||||||
return 'Hello, world!'
|
return 'Hello, world!'
|
||||||
|
|
||||||
In this example, the ``SomeController`` controller class implements
|
In this example, the ``SomeController`` controller class implements
|
||||||
an ``__before__`` method that adds the ``user`` attribute to the
|
an ``__before__`` method that adds the ``user`` attribute to the
|
||||||
``request`` instance. This could be done by extracting user
|
``request`` instance. This could be done by extracting user
|
||||||
|
@ -109,9 +113,9 @@ class require_perms(object):
|
||||||
method is decorated with ``require_perms``, which will ensure that
|
method is decorated with ``require_perms``, which will ensure that
|
||||||
the user is successfully authenticated and the the user has the
|
the user is successfully authenticated and the the user has the
|
||||||
specified permissions. This is handled by a *request validator*.
|
specified permissions. This is handled by a *request validator*.
|
||||||
|
|
||||||
There are two ways to specify the required permissions:
|
There are two ways to specify the required permissions:
|
||||||
|
|
||||||
* By passing the string name of all required permissions as
|
* By passing the string name of all required permissions as
|
||||||
positional arguments. A complex permission requirement will be
|
positional arguments. A complex permission requirement will be
|
||||||
constructed that requires *all* of the given permissions to be
|
constructed that requires *all* of the given permissions to be
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -18,11 +18,11 @@ Examples::
|
||||||
>>> req = Permission('foo') & Permission('bar')
|
>>> req = Permission('foo') & Permission('bar')
|
||||||
>>> req.check(PermissionContainer(['foo', 'baz'], ['bar']))
|
>>> req.check(PermissionContainer(['foo', 'baz'], ['bar']))
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> req = Permission('login')
|
>>> req = Permission('login')
|
||||||
>>> req.check(['login'])
|
>>> req.check(['login'])
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> req = Permission('login') | Permission('admin')
|
>>> req = Permission('login') | Permission('admin')
|
||||||
>>> req.check(['none'])
|
>>> req.check(['none'])
|
||||||
False
|
False
|
||||||
|
@ -30,62 +30,62 @@ Examples::
|
||||||
|
|
||||||
class PermissionContainer(object):
|
class PermissionContainer(object):
|
||||||
'''Container object for user and group permissions
|
'''Container object for user and group permissions
|
||||||
|
|
||||||
:param list user_perms: List of permissions held by the user itself
|
:param list user_perms: List of permissions held by the user itself
|
||||||
:param list group_perms: List of permissions held by the groups to
|
:param list group_perms: List of permissions held by the groups to
|
||||||
which the user belongs
|
which the user belongs
|
||||||
|
|
||||||
Iterating over :py:class:`PermissionContainer` objects results in
|
Iterating over :py:class:`PermissionContainer` objects results in
|
||||||
a flattened representation of all permissions.
|
a flattened representation of all permissions.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, user_perms=[], group_perms=[]):
|
def __init__(self, user_perms=[], group_perms=[]):
|
||||||
self._user_perms = user_perms
|
self._user_perms = user_perms
|
||||||
self._group_perms = group_perms
|
self._group_perms = group_perms
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for perm in self._user_perms:
|
for perm in self._user_perms:
|
||||||
yield perm
|
yield perm
|
||||||
for perm in self._group_perms:
|
for perm in self._group_perms:
|
||||||
yield perm
|
yield perm
|
||||||
|
|
||||||
def __contains__(self, perm):
|
def __contains__(self, perm):
|
||||||
return perm in self._user_perms or perm in self._group_perms
|
return perm in self._user_perms or perm in self._group_perms
|
||||||
|
|
||||||
|
|
||||||
class BasePermission(object):
|
class BasePermission(object):
|
||||||
'''Base class for permissions and requirements
|
'''Base class for permissions and requirements
|
||||||
|
|
||||||
Complex permission requirements can be created using the bitwise
|
Complex permission requirements can be created using the bitwise
|
||||||
``and`` and ``or`` operators::
|
``and`` and ``or`` operators::
|
||||||
|
|
||||||
login_and_view = Permission('login') & Permission('view')
|
login_and_view = Permission('login') & Permission('view')
|
||||||
admin_or_root = Permission('admin') | Permission('root')
|
admin_or_root = Permission('admin') | Permission('root')
|
||||||
|
|
||||||
complex = Permission('login') & Permission('view') | Permission('admin')
|
complex = Permission('login') & Permission('view') | Permission('admin')
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __and__(self, other):
|
def __and__(self, other):
|
||||||
assert isinstance(other, BasePermission)
|
assert isinstance(other, BasePermission)
|
||||||
return PermissionRequirementAll(self, other)
|
return PermissionRequirementAll(self, other)
|
||||||
|
|
||||||
def __or__(self, other):
|
def __or__(self, other):
|
||||||
assert isinstance(other, BasePermission)
|
assert isinstance(other, BasePermission)
|
||||||
return PermissionRequirementAny(self, other)
|
return PermissionRequirementAny(self, other)
|
||||||
|
|
||||||
class Permission(BasePermission):
|
class Permission(BasePermission):
|
||||||
'''Simple permission implementation
|
'''Simple permission implementation
|
||||||
|
|
||||||
:param str name: Name of the permission
|
:param str name: Name of the permission
|
||||||
|
|
||||||
Permissions must implement a ``check`` method that accepts an
|
Permissions must implement a ``check`` method that accepts an
|
||||||
iterable and returns ``True`` if the permission is present or
|
iterable and returns ``True`` if the permission is present or
|
||||||
``False`` otherwise.
|
``False`` otherwise.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
|
|
||||||
|
@ -94,27 +94,27 @@ class Permission(BasePermission):
|
||||||
|
|
||||||
def check(self, perms):
|
def check(self, perms):
|
||||||
'''Check if the permission is held
|
'''Check if the permission is held
|
||||||
|
|
||||||
This method can be overridden to provide more robust
|
This method can be overridden to provide more robust
|
||||||
support, but this implementation is simple::
|
support, but this implementation is simple::
|
||||||
|
|
||||||
return self in perms
|
return self in perms
|
||||||
'''
|
'''
|
||||||
|
|
||||||
return self in perms
|
return self in perms
|
||||||
|
|
||||||
class PermissionRequirement(BasePermission):
|
class PermissionRequirement(BasePermission):
|
||||||
'''Base class for complex permission requirements'''
|
'''Base class for complex permission requirements'''
|
||||||
|
|
||||||
def __init__(self, *requirements):
|
def __init__(self, *requirements):
|
||||||
self.requirements = requirements
|
self.requirements = requirements
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ', '.join(self.requirements)
|
return ', '.join(self.requirements)
|
||||||
|
|
||||||
class PermissionRequirementAll(PermissionRequirement):
|
class PermissionRequirementAll(PermissionRequirement):
|
||||||
'''Complex permission requirement needing all given permissions'''
|
'''Complex permission requirement needing all given permissions'''
|
||||||
|
|
||||||
def check(self, perms):
|
def check(self, perms):
|
||||||
for req in self.requirements:
|
for req in self.requirements:
|
||||||
if not req.check(perms):
|
if not req.check(perms):
|
||||||
|
@ -123,7 +123,7 @@ class PermissionRequirementAll(PermissionRequirement):
|
||||||
|
|
||||||
class PermissionRequirementAny(PermissionRequirement):
|
class PermissionRequirementAny(PermissionRequirement):
|
||||||
'''Complex permission requirement needing any given permissions'''
|
'''Complex permission requirement needing any given permissions'''
|
||||||
|
|
||||||
def check(self, perms):
|
def check(self, perms):
|
||||||
for req in self.requirements:
|
for req in self.requirements:
|
||||||
if req.check(perms):
|
if req.check(perms):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011, 2012, 2015 Dustin C. Hatch
|
# Copyright 2011, 2012, 2015 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -28,13 +28,13 @@ import warnings
|
||||||
|
|
||||||
class Router(object):
|
class Router(object):
|
||||||
'''A dispatcher that maps arbitrary paths to controller callables
|
'''A dispatcher that maps arbitrary paths to controller callables
|
||||||
|
|
||||||
Typical usage::
|
Typical usage::
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
|
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
|
||||||
app = milla.Application(dispatcher=router)
|
app = milla.Application(dispatcher=router)
|
||||||
|
|
||||||
In many cases, paths with trailing slashes need special handling.
|
In many cases, paths with trailing slashes need special handling.
|
||||||
The ``Router`` has two ways of dealing with requests that should
|
The ``Router`` has two ways of dealing with requests that should
|
||||||
have a trailing slash but do not. The default is to send the client
|
have a trailing slash but do not. The default is to send the client
|
||||||
|
@ -44,19 +44,19 @@ class Router(object):
|
||||||
return HTTP 404 Not Found for requests with missing trailing
|
return HTTP 404 Not Found for requests with missing trailing
|
||||||
slashes. To change the behavior, pass a different value to the
|
slashes. To change the behavior, pass a different value to the
|
||||||
constructor's ``trailing_slash`` keyword.
|
constructor's ``trailing_slash`` keyword.
|
||||||
|
|
||||||
Redirect the client to the proper path (the default)::
|
Redirect the client to the proper path (the default)::
|
||||||
|
|
||||||
router = Router(trailing_slash=Router.REDIRECT)
|
router = Router(trailing_slash=Router.REDIRECT)
|
||||||
router.add_route('/my_collection/', some_func)
|
router.add_route('/my_collection/', some_func)
|
||||||
|
|
||||||
Pretend the request had a trailing slash, even if it didn't::
|
Pretend the request had a trailing slash, even if it didn't::
|
||||||
|
|
||||||
router = Router(trailing_slash=Router.SILENT)
|
router = Router(trailing_slash=Router.SILENT)
|
||||||
router.add_route('/my_collection/', some_func)
|
router.add_route('/my_collection/', some_func)
|
||||||
|
|
||||||
Do nothing, let the client get a 404 error::
|
Do nothing, let the client get a 404 error::
|
||||||
|
|
||||||
router = Router(trailing_slash=None)
|
router = Router(trailing_slash=None)
|
||||||
router.add_route('/my_collection/', some_func)
|
router.add_route('/my_collection/', some_func)
|
||||||
'''
|
'''
|
||||||
|
@ -74,12 +74,12 @@ class Router(object):
|
||||||
|
|
||||||
def resolve(self, path_info):
|
def resolve(self, path_info):
|
||||||
'''Find a controller for a given path
|
'''Find a controller for a given path
|
||||||
|
|
||||||
:param path_info: Path for which to locate a controller
|
:param path_info: Path for which to locate a controller
|
||||||
:returns: A :py:class:`functools.partial` instance that sets
|
:returns: A :py:class:`functools.partial` instance that sets
|
||||||
the values collected from variable segments as keyword
|
the values collected from variable segments as keyword
|
||||||
arguments to the callable
|
arguments to the callable
|
||||||
|
|
||||||
This method walks through the routing table created with calls
|
This method walks through the routing table created with calls
|
||||||
to :py:meth:`add_route` and finds the first whose template
|
to :py:meth:`add_route` and finds the first whose template
|
||||||
matches the given path. Variable segments are added as keywords
|
matches the given path. Variable segments are added as keywords
|
||||||
|
@ -132,9 +132,9 @@ class Router(object):
|
||||||
|
|
||||||
def _compile_template(self, template):
|
def _compile_template(self, template):
|
||||||
'''Compiles a template into a real regular expression
|
'''Compiles a template into a real regular expression
|
||||||
|
|
||||||
:param template: A route template string
|
:param template: A route template string
|
||||||
|
|
||||||
Converts the ``{name}`` or ``{name:regexp}`` syntax into a full
|
Converts the ``{name}`` or ``{name:regexp}`` syntax into a full
|
||||||
regular expression for later parsing.
|
regular expression for later parsing.
|
||||||
'''
|
'''
|
||||||
|
@ -163,40 +163,40 @@ class Router(object):
|
||||||
|
|
||||||
def add_route(self, template, controller, **vars):
|
def add_route(self, template, controller, **vars):
|
||||||
'''Add a route to the routing table
|
'''Add a route to the routing table
|
||||||
|
|
||||||
:param template: Route template string
|
:param template: Route template string
|
||||||
:param controller: Controller callable or string Python path
|
:param controller: Controller callable or string Python path
|
||||||
|
|
||||||
Route template strings are path segments, beginning with ``/``.
|
Route template strings are path segments, beginning with ``/``.
|
||||||
Paths can also contain variable segments, delimited with curly
|
Paths can also contain variable segments, delimited with curly
|
||||||
braces.
|
braces.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
/some/other/{variable}/{path}
|
/some/other/{variable}/{path}
|
||||||
|
|
||||||
By default, variable segments will match any character except a
|
By default, variable segments will match any character except a
|
||||||
``/``. Alternate expressions can be passed by specifying them
|
``/``. Alternate expressions can be passed by specifying them
|
||||||
alongside the name, separated by a ``:``.
|
alongside the name, separated by a ``:``.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
/some/other/{alternate:[a-zA-Z]}
|
/some/other/{alternate:[a-zA-Z]}
|
||||||
|
|
||||||
Variable path segments will be passed as keywords to the
|
Variable path segments will be passed as keywords to the
|
||||||
controller. In the first example above, assuming ``controller``
|
controller. In the first example above, assuming ``controller``
|
||||||
is the name of the callable passed, and the request path was
|
is the name of the callable passed, and the request path was
|
||||||
``/some/other/great/place``::
|
``/some/other/great/place``::
|
||||||
|
|
||||||
controller(request, variable='great', path='place')
|
controller(request, variable='great', path='place')
|
||||||
|
|
||||||
The ``controller`` argument itself can be any callable that
|
The ``controller`` argument itself can be any callable that
|
||||||
accepts a *WebOb* request as its first argument, and any
|
accepts a *WebOb* request as its first argument, and any
|
||||||
keywords that may be passed from variable segments. It can
|
keywords that may be passed from variable segments. It can
|
||||||
also be a string Python path to such a callable. For example::
|
also be a string Python path to such a callable. For example::
|
||||||
|
|
||||||
`some.module:function`
|
`some.module:function`
|
||||||
|
|
||||||
This string will resolve to the function ``function`` in the
|
This string will resolve to the function ``function`` in the
|
||||||
module ``some.module``.
|
module ``some.module``.
|
||||||
'''
|
'''
|
||||||
|
@ -208,17 +208,17 @@ class Router(object):
|
||||||
|
|
||||||
class Generator(object):
|
class Generator(object):
|
||||||
'''URL generator
|
'''URL generator
|
||||||
|
|
||||||
Creates URL references based on a *WebOb* request.
|
Creates URL references based on a *WebOb* request.
|
||||||
|
|
||||||
Typical usage:
|
Typical usage:
|
||||||
|
|
||||||
>>> generator = Generator(request)
|
>>> generator = Generator(request)
|
||||||
>>> generator.generate('foo', 'bar')
|
>>> generator.generate('foo', 'bar')
|
||||||
'/foo/bar'
|
'/foo/bar'
|
||||||
|
|
||||||
A common pattern is to wrap this in a stub function::
|
A common pattern is to wrap this in a stub function::
|
||||||
|
|
||||||
url = Generator(request).generate
|
url = Generator(request).generate
|
||||||
|
|
||||||
.. deprecated:: 0.2
|
.. deprecated:: 0.2
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Copyright 2011 Dustin C. Hatch
|
# Copyright 2011 Dustin C. Hatch
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -23,35 +23,35 @@ from milla.dispatch import UnresolvedPath
|
||||||
|
|
||||||
class Traverser(object):
|
class Traverser(object):
|
||||||
'''Default URL dispatcher
|
'''Default URL dispatcher
|
||||||
|
|
||||||
:param root: The root object at which lookup will begin
|
:param root: The root object at which lookup will begin
|
||||||
|
|
||||||
The default URL dispatcher uses object attribute traversal to
|
The default URL dispatcher uses object attribute traversal to
|
||||||
locate a handler for a given path. For example, consider the
|
locate a handler for a given path. For example, consider the
|
||||||
following class::
|
following class::
|
||||||
|
|
||||||
class Root(object):
|
class Root(object):
|
||||||
|
|
||||||
def foo(self):
|
def foo(self):
|
||||||
return 'Hello, world!'
|
return 'Hello, world!'
|
||||||
|
|
||||||
The path ``/foo`` would resolve to the ``foo`` method of the
|
The path ``/foo`` would resolve to the ``foo`` method of the
|
||||||
``Root`` class.
|
``Root`` class.
|
||||||
|
|
||||||
If a path cannot be resolved, :py:exc:`UnresolvedPath` will be
|
If a path cannot be resolved, :py:exc:`UnresolvedPath` will be
|
||||||
raised.
|
raised.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self.root = root
|
self.root = root
|
||||||
|
|
||||||
def resolve(self, path_info):
|
def resolve(self, path_info):
|
||||||
'''Find a handler given a path
|
'''Find a handler given a path
|
||||||
|
|
||||||
:param path_info: Path for which to find a handler
|
:param path_info: Path for which to find a handler
|
||||||
:returns: A handler callable
|
:returns: A handler callable
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def walk_path(handler, parts):
|
def walk_path(handler, parts):
|
||||||
if not parts or not parts[0]:
|
if not parts or not parts[0]:
|
||||||
# No more parts, or the last part is blank, we're done
|
# No more parts, or the last part is blank, we're done
|
||||||
|
@ -66,9 +66,9 @@ class Traverser(object):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# No default either, can't resolve
|
# No default either, can't resolve
|
||||||
raise UnresolvedPath
|
raise UnresolvedPath
|
||||||
|
|
||||||
# Strip the leading slash and split the path
|
# Strip the leading slash and split the path
|
||||||
split_path = path_info.lstrip('/').split('/')
|
split_path = path_info.lstrip('/').split('/')
|
||||||
|
|
||||||
handler = walk_path(self.root, split_path)
|
handler = walk_path(self.root, split_path)
|
||||||
return handler
|
return handler
|
||||||
|
|
|
@ -10,21 +10,21 @@ import nose.tools
|
||||||
def test_permission_check():
|
def test_permission_check():
|
||||||
'''Ensure Permission.check returns True for lists of strings
|
'''Ensure Permission.check returns True for lists of strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
assert perm.check(['foo'])
|
assert perm.check(['foo'])
|
||||||
|
|
||||||
def test_permission_check_false():
|
def test_permission_check_false():
|
||||||
'''Ensure Permission.check returns False for lists of strings
|
'''Ensure Permission.check returns False for lists of strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
assert not perm.check(['bar'])
|
assert not perm.check(['bar'])
|
||||||
|
|
||||||
def test_permission_check_perm():
|
def test_permission_check_perm():
|
||||||
'''Ensure Permission.check returns True for lists of Permissions
|
'''Ensure Permission.check returns True for lists of Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
assert req.check([perm])
|
assert req.check([perm])
|
||||||
|
@ -32,7 +32,7 @@ def test_permission_check_perm():
|
||||||
def test_permission_check_perm_false():
|
def test_permission_check_perm_false():
|
||||||
'''Ensure Permission.check returns True for lists of Permissions
|
'''Ensure Permission.check returns True for lists of Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
perm = milla.auth.permissions.Permission('bar')
|
perm = milla.auth.permissions.Permission('bar')
|
||||||
assert not req.check([perm])
|
assert not req.check([perm])
|
||||||
|
@ -40,7 +40,7 @@ def test_permission_check_perm_false():
|
||||||
def test_permission_check_container():
|
def test_permission_check_container():
|
||||||
'''Ensure Permission.check returns True for PermissionContainers of strings
|
'''Ensure Permission.check returns True for PermissionContainers of strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
container = milla.auth.permissions.PermissionContainer(['foo'])
|
container = milla.auth.permissions.PermissionContainer(['foo'])
|
||||||
assert perm.check(container)
|
assert perm.check(container)
|
||||||
|
@ -48,7 +48,7 @@ def test_permission_check_container():
|
||||||
def test_permission_check_container_false():
|
def test_permission_check_container_false():
|
||||||
'''Ensure Permission.check returns True for PermissionContainers of strings
|
'''Ensure Permission.check returns True for PermissionContainers of strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
container = milla.auth.permissions.PermissionContainer(['bar'])
|
container = milla.auth.permissions.PermissionContainer(['bar'])
|
||||||
assert not perm.check(container)
|
assert not perm.check(container)
|
||||||
|
@ -56,7 +56,7 @@ def test_permission_check_container_false():
|
||||||
def test_permission_check_container_perm():
|
def test_permission_check_container_perm():
|
||||||
'''Ensure Permission.check returns True for PermissionContainers of Permissions
|
'''Ensure Permission.check returns True for PermissionContainers of Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
container = milla.auth.permissions.PermissionContainer([perm])
|
container = milla.auth.permissions.PermissionContainer([perm])
|
||||||
|
@ -65,7 +65,7 @@ def test_permission_check_container_perm():
|
||||||
def test_permission_check_container_perm_false():
|
def test_permission_check_container_perm_false():
|
||||||
'''Ensure Permission.check returns False for PermissionContainers of Permissions
|
'''Ensure Permission.check returns False for PermissionContainers of Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
req = milla.auth.permissions.Permission('bar')
|
req = milla.auth.permissions.Permission('bar')
|
||||||
container = milla.auth.permissions.PermissionContainer([perm])
|
container = milla.auth.permissions.PermissionContainer([perm])
|
||||||
|
@ -74,14 +74,14 @@ def test_permission_check_container_perm_false():
|
||||||
def test_permission_container_iter():
|
def test_permission_container_iter():
|
||||||
'''Ensure iterating a PermissionContainer yields all permissions
|
'''Ensure iterating a PermissionContainer yields all permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
container = milla.auth.permissions.PermissionContainer(['foo'], ['bar'])
|
container = milla.auth.permissions.PermissionContainer(['foo'], ['bar'])
|
||||||
assert list(container) == ['foo', 'bar']
|
assert list(container) == ['foo', 'bar']
|
||||||
|
|
||||||
def test_permission_and():
|
def test_permission_and():
|
||||||
'''Ensure AND-ing Permissions returns a PermissionRequirementAll
|
'''Ensure AND-ing Permissions returns a PermissionRequirementAll
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm1 = milla.auth.permissions.Permission('foo')
|
perm1 = milla.auth.permissions.Permission('foo')
|
||||||
perm2 = milla.auth.permissions.Permission('bar')
|
perm2 = milla.auth.permissions.Permission('bar')
|
||||||
req = perm1 & perm2
|
req = perm1 & perm2
|
||||||
|
@ -91,7 +91,7 @@ def test_permission_and():
|
||||||
def test_permission_or():
|
def test_permission_or():
|
||||||
'''Ensure OR-ing Permissions returns a PermissionRequirementAny
|
'''Ensure OR-ing Permissions returns a PermissionRequirementAny
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm1 = milla.auth.permissions.Permission('foo')
|
perm1 = milla.auth.permissions.Permission('foo')
|
||||||
perm2 = milla.auth.permissions.Permission('bar')
|
perm2 = milla.auth.permissions.Permission('bar')
|
||||||
req = perm1 | perm2
|
req = perm1 | perm2
|
||||||
|
@ -101,7 +101,7 @@ def test_permission_or():
|
||||||
def test_permission_str():
|
def test_permission_str():
|
||||||
'''Ensure calling str on a Permission returns its name
|
'''Ensure calling str on a Permission returns its name
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm_name = 'foo'
|
perm_name = 'foo'
|
||||||
perm = milla.auth.permissions.Permission(perm_name)
|
perm = milla.auth.permissions.Permission(perm_name)
|
||||||
assert str(perm) == perm_name
|
assert str(perm) == perm_name
|
||||||
|
@ -109,7 +109,7 @@ def test_permission_str():
|
||||||
def test_permission_eq():
|
def test_permission_eq():
|
||||||
'''Ensure two Permissions with the same name are equal but not identical
|
'''Ensure two Permissions with the same name are equal but not identical
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm_name = 'foo'
|
perm_name = 'foo'
|
||||||
perm1 = milla.auth.permissions.Permission(perm_name)
|
perm1 = milla.auth.permissions.Permission(perm_name)
|
||||||
perm2 = milla.auth.permissions.Permission(perm_name)
|
perm2 = milla.auth.permissions.Permission(perm_name)
|
||||||
|
@ -119,7 +119,7 @@ def test_permission_eq():
|
||||||
def test_permission_check_container_group():
|
def test_permission_check_container_group():
|
||||||
'''Test group permissions in PermissionContainer objects
|
'''Test group permissions in PermissionContainer objects
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm = milla.auth.permissions.Permission('foo')
|
perm = milla.auth.permissions.Permission('foo')
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
container = milla.auth.permissions.PermissionContainer([], [perm])
|
container = milla.auth.permissions.PermissionContainer([], [perm])
|
||||||
|
@ -128,7 +128,7 @@ def test_permission_check_container_group():
|
||||||
def test_permissionrequirement_all():
|
def test_permissionrequirement_all():
|
||||||
'''Ensure PermissionRequirementAll requires all listed permissions
|
'''Ensure PermissionRequirementAll requires all listed permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm1 = milla.auth.permissions.Permission('foo')
|
perm1 = milla.auth.permissions.Permission('foo')
|
||||||
perm2 = milla.auth.permissions.Permission('bar')
|
perm2 = milla.auth.permissions.Permission('bar')
|
||||||
req = milla.auth.permissions.PermissionRequirementAll(perm1, perm2)
|
req = milla.auth.permissions.PermissionRequirementAll(perm1, perm2)
|
||||||
|
@ -141,7 +141,7 @@ def test_permissionrequirement_all():
|
||||||
def test_permissionrequirement_any():
|
def test_permissionrequirement_any():
|
||||||
'''Ensure PermissionRequirementAll requires only one permission
|
'''Ensure PermissionRequirementAll requires only one permission
|
||||||
'''
|
'''
|
||||||
|
|
||||||
perm1 = milla.auth.permissions.Permission('foo')
|
perm1 = milla.auth.permissions.Permission('foo')
|
||||||
perm2 = milla.auth.permissions.Permission('bar')
|
perm2 = milla.auth.permissions.Permission('bar')
|
||||||
req = milla.auth.permissions.PermissionRequirementAny(perm1, perm2)
|
req = milla.auth.permissions.PermissionRequirementAny(perm1, perm2)
|
||||||
|
@ -154,7 +154,7 @@ def test_permissionrequirement_any():
|
||||||
def test_exception_callable():
|
def test_exception_callable():
|
||||||
'''Ensure that NotAuthorizedException is a valid controller callable
|
'''Ensure that NotAuthorizedException is a valid controller callable
|
||||||
'''
|
'''
|
||||||
|
|
||||||
exc = milla.auth.NotAuthorized()
|
exc = milla.auth.NotAuthorized()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
response = exc(request)
|
response = exc(request)
|
||||||
|
@ -165,7 +165,7 @@ def test_exception_callable():
|
||||||
def test_request_validator_nouser():
|
def test_request_validator_nouser():
|
||||||
'''Ensure ensure requests without a user attribute raise NotAuthorized
|
'''Ensure ensure requests without a user attribute raise NotAuthorized
|
||||||
'''
|
'''
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
validator.validate(request)
|
validator.validate(request)
|
||||||
|
@ -174,7 +174,7 @@ def test_request_validator_nouser():
|
||||||
def test_request_validator_emptyuser():
|
def test_request_validator_emptyuser():
|
||||||
'''Ensure requests with an empty user raise NotAuthorized
|
'''Ensure requests with an empty user raise NotAuthorized
|
||||||
'''
|
'''
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = None
|
request.user = None
|
||||||
|
@ -182,13 +182,13 @@ def test_request_validator_emptyuser():
|
||||||
|
|
||||||
def test_request_validator_user_noperms():
|
def test_request_validator_user_noperms():
|
||||||
'''Ensure user permissions are not checked if no requirement is given
|
'''Ensure user permissions are not checked if no requirement is given
|
||||||
|
|
||||||
If no ``requirement`` is given to
|
If no ``requirement`` is given to
|
||||||
:py:meth:`milla.auth.RequestValidator.validate`, then the fact that the
|
:py:meth:`milla.auth.RequestValidator.validate`, then the fact that the
|
||||||
request's ``user`` attribute doesn't have a ``permissions`` attribute
|
request's ``user`` attribute doesn't have a ``permissions`` attribute
|
||||||
shouldn't matter.
|
shouldn't matter.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -201,10 +201,10 @@ def test_request_validator_user_noperms():
|
||||||
def test_request_validator_missingperms():
|
def test_request_validator_missingperms():
|
||||||
'''Ensure requests whose user has no permissions attribute are invalid
|
'''Ensure requests whose user has no permissions attribute are invalid
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
|
@ -215,10 +215,10 @@ def test_request_validator_missingperms():
|
||||||
def test_request_validator_emptyperms():
|
def test_request_validator_emptyperms():
|
||||||
'''Ensure requests whose user has an empty set of permissions are invalid
|
'''Ensure requests whose user has an empty set of permissions are invalid
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
|
@ -230,10 +230,10 @@ def test_request_validator_emptyperms():
|
||||||
def test_request_validator_incorrectperms():
|
def test_request_validator_incorrectperms():
|
||||||
'''Ensure requests whose user has incorrect permissions raise NotAuthorized
|
'''Ensure requests whose user has incorrect permissions raise NotAuthorized
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
|
@ -244,10 +244,10 @@ def test_request_validator_incorrectperms():
|
||||||
def test_request_validator_correctperms():
|
def test_request_validator_correctperms():
|
||||||
'''Ensure requests from users with appropriate permissions are valid
|
'''Ensure requests from users with appropriate permissions are valid
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
validator = milla.auth.RequestValidator()
|
validator = milla.auth.RequestValidator()
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
|
@ -258,7 +258,7 @@ def test_request_validator_correctperms():
|
||||||
def test_find_request_kwarg():
|
def test_find_request_kwarg():
|
||||||
'''Ensure _find_request finds a request in keyword arguments
|
'''Ensure _find_request finds a request in keyword arguments
|
||||||
'''
|
'''
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
found = milla.auth.decorators._find_request('foo', request=request)
|
found = milla.auth.decorators._find_request('foo', request=request)
|
||||||
assert found is request
|
assert found is request
|
||||||
|
@ -266,7 +266,7 @@ def test_find_request_kwarg():
|
||||||
def test_find_request_arg1():
|
def test_find_request_arg1():
|
||||||
'''Ensure _find_request finds a request in position 1
|
'''Ensure _find_request finds a request in position 1
|
||||||
'''
|
'''
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
found = milla.auth.decorators._find_request(request)
|
found = milla.auth.decorators._find_request(request)
|
||||||
assert found is request
|
assert found is request
|
||||||
|
@ -274,7 +274,7 @@ def test_find_request_arg1():
|
||||||
def test_find_request_arg2():
|
def test_find_request_arg2():
|
||||||
'''Ensure _find_request finds a request in another position
|
'''Ensure _find_request finds a request in another position
|
||||||
'''
|
'''
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
found = milla.auth.decorators._find_request('foo', request)
|
found = milla.auth.decorators._find_request('foo', request)
|
||||||
assert found is request
|
assert found is request
|
||||||
|
@ -282,14 +282,14 @@ def test_find_request_arg2():
|
||||||
def test_auth_required_true():
|
def test_auth_required_true():
|
||||||
'''Test the auth_required decorator with a valid user
|
'''Test the auth_required decorator with a valid user
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.auth_required
|
@milla.auth.decorators.auth_required
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
response = controller(request)
|
response = controller(request)
|
||||||
|
@ -298,7 +298,7 @@ def test_auth_required_true():
|
||||||
def test_auth_required_false():
|
def test_auth_required_false():
|
||||||
'''Test the auth_required decorator with no user
|
'''Test the auth_required decorator with no user
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@milla.auth.decorators.auth_required
|
@milla.auth.decorators.auth_required
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
@ -312,10 +312,10 @@ def test_auth_required_false():
|
||||||
def test_require_perms_none():
|
def test_require_perms_none():
|
||||||
'''Test the require_perms decorator with no requirement
|
'''Test the require_perms decorator with no requirement
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms()
|
@milla.auth.decorators.require_perms()
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
@ -328,14 +328,14 @@ def test_require_perms_none():
|
||||||
def test_require_perms_valid_str():
|
def test_require_perms_valid_str():
|
||||||
'''Test the require_perms decorator with valid permissions as strings
|
'''Test the require_perms decorator with valid permissions as strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo')
|
@milla.auth.decorators.require_perms('foo')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['foo']
|
request.user.permissions = ['foo']
|
||||||
|
@ -345,15 +345,15 @@ def test_require_perms_valid_str():
|
||||||
def test_require_perms_valid_permission():
|
def test_require_perms_valid_permission():
|
||||||
'''Test the require_perms decorator with valid permissions as Permissions
|
'''Test the require_perms decorator with valid permissions as Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
@milla.auth.decorators.require_perms(req)
|
@milla.auth.decorators.require_perms(req)
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['foo']
|
request.user.permissions = ['foo']
|
||||||
|
@ -363,10 +363,10 @@ def test_require_perms_valid_permission():
|
||||||
def test_require_perms_multi_valid_string():
|
def test_require_perms_multi_valid_string():
|
||||||
'''Test the require_perms decorator with multiple requirements as strings
|
'''Test the require_perms decorator with multiple requirements as strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo', 'bar')
|
@milla.auth.decorators.require_perms('foo', 'bar')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
@ -380,10 +380,10 @@ def test_require_perms_multi_valid_string():
|
||||||
def test_require_perms_multi_valid_permission():
|
def test_require_perms_multi_valid_permission():
|
||||||
'''Test the require_perms decorator with multiple requirements as Permissions
|
'''Test the require_perms decorator with multiple requirements as Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
req1 = milla.auth.permissions.Permission('foo')
|
req1 = milla.auth.permissions.Permission('foo')
|
||||||
req2 = milla.auth.permissions.Permission('bar')
|
req2 = milla.auth.permissions.Permission('bar')
|
||||||
@milla.auth.decorators.require_perms(req1, req2)
|
@milla.auth.decorators.require_perms(req1, req2)
|
||||||
|
@ -399,14 +399,14 @@ def test_require_perms_multi_valid_permission():
|
||||||
def test_require_perms_invalid_none():
|
def test_require_perms_invalid_none():
|
||||||
'''Test the require_perms decorator with no permissions
|
'''Test the require_perms decorator with no permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo')
|
@milla.auth.decorators.require_perms('foo')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
response = controller(request)
|
response = controller(request)
|
||||||
|
@ -416,10 +416,10 @@ def test_require_perms_invalid_none():
|
||||||
def test_require_perms_invalid_empty():
|
def test_require_perms_invalid_empty():
|
||||||
'''Test the require_perms decorator with an empty permissions set
|
'''Test the require_perms decorator with an empty permissions set
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo')
|
@milla.auth.decorators.require_perms('foo')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
@ -434,14 +434,14 @@ def test_require_perms_invalid_empty():
|
||||||
def test_require_perms_invalid_string():
|
def test_require_perms_invalid_string():
|
||||||
'''Test the require_perms decorator with invalid permissions as strings
|
'''Test the require_perms decorator with invalid permissions as strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo')
|
@milla.auth.decorators.require_perms('foo')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['bar']
|
request.user.permissions = ['bar']
|
||||||
|
@ -452,15 +452,15 @@ def test_require_perms_invalid_string():
|
||||||
def test_require_perms_invalid_permission():
|
def test_require_perms_invalid_permission():
|
||||||
'''Test the require_perms decorator with invalid permissions as Permissions
|
'''Test the require_perms decorator with invalid permissions as Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
req = milla.auth.permissions.Permission('foo')
|
req = milla.auth.permissions.Permission('foo')
|
||||||
@milla.auth.decorators.require_perms(req)
|
@milla.auth.decorators.require_perms(req)
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['bar']
|
request.user.permissions = ['bar']
|
||||||
|
@ -471,14 +471,14 @@ def test_require_perms_invalid_permission():
|
||||||
def test_require_perms_multi_invalid_string():
|
def test_require_perms_multi_invalid_string():
|
||||||
'''Test the require_perms decorator with multiple invalid permissions as strings
|
'''Test the require_perms decorator with multiple invalid permissions as strings
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@milla.auth.decorators.require_perms('foo', 'bar')
|
@milla.auth.decorators.require_perms('foo', 'bar')
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['bar']
|
request.user.permissions = ['bar']
|
||||||
|
@ -489,16 +489,16 @@ def test_require_perms_multi_invalid_string():
|
||||||
def test_require_perms_multi_invalid_permission():
|
def test_require_perms_multi_invalid_permission():
|
||||||
'''Test the require_perms decorator with multiple invalid permissions as Permissions
|
'''Test the require_perms decorator with multiple invalid permissions as Permissions
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
req1 = milla.auth.permissions.Permission('foo')
|
req1 = milla.auth.permissions.Permission('foo')
|
||||||
req2 = milla.auth.permissions.Permission('foo')
|
req2 = milla.auth.permissions.Permission('foo')
|
||||||
@milla.auth.decorators.require_perms(req1, req2)
|
@milla.auth.decorators.require_perms(req1, req2)
|
||||||
def controller(request):
|
def controller(request):
|
||||||
return 'success'
|
return 'success'
|
||||||
|
|
||||||
request = milla.Request.blank('/')
|
request = milla.Request.blank('/')
|
||||||
request.user = User()
|
request.user = User()
|
||||||
request.user.permissions = ['bar']
|
request.user.permissions = ['bar']
|
||||||
|
|
|
@ -12,7 +12,7 @@ import nose.tools
|
||||||
|
|
||||||
def test_static():
|
def test_static():
|
||||||
'''Ensure the dispatcher can resolve a static path
|
'''Ensure the dispatcher can resolve a static path
|
||||||
|
|
||||||
Given the path ``/foo/bar/baz`` and a route for the exact same
|
Given the path ``/foo/bar/baz`` and a route for the exact same
|
||||||
path, the resolver should return the controller mapped to the
|
path, the resolver should return the controller mapped to the
|
||||||
route.
|
route.
|
||||||
|
@ -28,7 +28,7 @@ def test_static():
|
||||||
|
|
||||||
def test_urlvars():
|
def test_urlvars():
|
||||||
'''Ensure the dispatcher can resolve a path with variable segments
|
'''Ensure the dispatcher can resolve a path with variable segments
|
||||||
|
|
||||||
Given the path ``/foo/abc/def`` and a route ``/foo/{bar}/{baz}``,
|
Given the path ``/foo/abc/def`` and a route ``/foo/{bar}/{baz}``,
|
||||||
the resolver should return the controller mapped to the route with
|
the resolver should return the controller mapped to the route with
|
||||||
preset keywords ``bar='abc', baz='def'``.
|
preset keywords ``bar='abc', baz='def'``.
|
||||||
|
@ -47,7 +47,7 @@ def test_urlvars():
|
||||||
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
||||||
def test_regexp_urlvar():
|
def test_regexp_urlvar():
|
||||||
'''Ensure the dispatcher can resolve alternate regexps in urlvars
|
'''Ensure the dispatcher can resolve alternate regexps in urlvars
|
||||||
|
|
||||||
Given a route ``/test/{arg:[a-z]+}``, the resolver should return
|
Given a route ``/test/{arg:[a-z]+}``, the resolver should return
|
||||||
the mapped controller for the path ``/test/abcde``, but not the
|
the mapped controller for the path ``/test/abcde``, but not the
|
||||||
path ``/test/1234``.
|
path ``/test/1234``.
|
||||||
|
@ -67,7 +67,7 @@ def test_regexp_urlvar():
|
||||||
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
@nose.tools.raises(milla.dispatch.UnresolvedPath)
|
||||||
def test_unresolved():
|
def test_unresolved():
|
||||||
'''Ensure the resolver raises an exception for unresolved paths
|
'''Ensure the resolver raises an exception for unresolved paths
|
||||||
|
|
||||||
Given a route ``/test``, the resolver should raise
|
Given a route ``/test``, the resolver should raise
|
||||||
:py:exc:`~milla.dispatch.UnresolvedPath` for the path ``/tset``.
|
:py:exc:`~milla.dispatch.UnresolvedPath` for the path ``/tset``.
|
||||||
'''
|
'''
|
||||||
|
@ -81,7 +81,7 @@ def test_unresolved():
|
||||||
|
|
||||||
def test_unrelated():
|
def test_unrelated():
|
||||||
'''Ensure the dispatcher is not confused by unrelated paths
|
'''Ensure the dispatcher is not confused by unrelated paths
|
||||||
|
|
||||||
Given routes for ``/testA`` and ``/testB``, the resolver should
|
Given routes for ``/testA`` and ``/testB``, the resolver should
|
||||||
return the controller mapped to the former for the path ``/testA``,
|
return the controller mapped to the former for the path ``/testA``,
|
||||||
without regard for the latter.
|
without regard for the latter.
|
||||||
|
@ -101,7 +101,7 @@ def test_unrelated():
|
||||||
|
|
||||||
def test_string_controller():
|
def test_string_controller():
|
||||||
'''Ensure the dispatcher can find a controller given a string
|
'''Ensure the dispatcher can find a controller given a string
|
||||||
|
|
||||||
Given a string path to a controller function, the callable defined
|
Given a string path to a controller function, the callable defined
|
||||||
therein should be returned by the resolver for the corresponding
|
therein should be returned by the resolver for the corresponding
|
||||||
path.
|
path.
|
||||||
|
@ -134,10 +134,10 @@ def test_trailing_slash_redir():
|
||||||
def test_trailing_slash_none():
|
def test_trailing_slash_none():
|
||||||
'''Paths that match except the trailing slash are ignored
|
'''Paths that match except the trailing slash are ignored
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def controller():
|
def controller():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
router = milla.dispatch.routing.Router(None)
|
router = milla.dispatch.routing.Router(None)
|
||||||
router.add_route('/test/', controller)
|
router.add_route('/test/', controller)
|
||||||
router.resolve('/test')
|
router.resolve('/test')
|
||||||
|
@ -145,11 +145,11 @@ def test_trailing_slash_none():
|
||||||
def test_trailing_slash_silent():
|
def test_trailing_slash_silent():
|
||||||
'''Paths that match except the trailing slash are treated the same
|
'''Paths that match except the trailing slash are treated the same
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def controller():
|
def controller():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
router = milla.dispatch.routing.Router(milla.dispatch.routing.Router.SILENT)
|
router = milla.dispatch.routing.Router(milla.dispatch.routing.Router.SILENT)
|
||||||
router.add_route('/test/', controller)
|
router.add_route('/test/', controller)
|
||||||
func = router.resolve('/test')
|
func = router.resolve('/test')
|
||||||
assert func.func is controller
|
assert func.func is controller
|
||||||
|
|
|
@ -9,7 +9,7 @@ import milla.dispatch.traversal
|
||||||
|
|
||||||
def test_root():
|
def test_root():
|
||||||
'''Ensure the root path resolves to the root handler
|
'''Ensure the root path resolves to the root handler
|
||||||
|
|
||||||
Given the path ``/``, the resolver should return the root handler,
|
Given the path ``/``, the resolver should return the root handler,
|
||||||
which was given to it at initialization
|
which was given to it at initialization
|
||||||
'''
|
'''
|
||||||
|
@ -24,8 +24,8 @@ def test_root():
|
||||||
|
|
||||||
def test_unrelated():
|
def test_unrelated():
|
||||||
'''Ensure unrelated attributes do not confuse the dispatcher
|
'''Ensure unrelated attributes do not confuse the dispatcher
|
||||||
|
|
||||||
Given the path ``/`` and a root handler with attributes and
|
Given the path ``/`` and a root handler with attributes and
|
||||||
methods, the resolver should still return the root handler
|
methods, the resolver should still return the root handler
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ def test_unrelated():
|
||||||
|
|
||||||
def test_unresolved():
|
def test_unresolved():
|
||||||
'''Ensure that the resolver returns remaining parts
|
'''Ensure that the resolver returns remaining parts
|
||||||
|
|
||||||
Given the path ``/foo/bar/baz`` and a root handler with no
|
Given the path ``/foo/bar/baz`` and a root handler with no
|
||||||
children, the resolver should raise
|
children, the resolver should raise
|
||||||
:py:exc:`~milla.dispatch.UnresolvedPath`
|
:py:exc:`~milla.dispatch.UnresolvedPath`
|
||||||
|
@ -61,7 +61,7 @@ def test_unresolved():
|
||||||
|
|
||||||
def test_method():
|
def test_method():
|
||||||
'''Ensure the resolver finds an instance method handler
|
'''Ensure the resolver finds an instance method handler
|
||||||
|
|
||||||
Given the path ``/test`` and a root handler with an instance
|
Given the path ``/test`` and a root handler with an instance
|
||||||
method named ``test``, the resolver should return that method.
|
method named ``test``, the resolver should return that method.
|
||||||
'''
|
'''
|
||||||
|
@ -77,7 +77,7 @@ def test_method():
|
||||||
|
|
||||||
def test_nested_class():
|
def test_nested_class():
|
||||||
'''Ensure the resolver finds a nested class handler
|
'''Ensure the resolver finds a nested class handler
|
||||||
|
|
||||||
Given the path ``/test`` and a root handler with an inner class
|
Given the path ``/test`` and a root handler with an inner class
|
||||||
named ``test``, the resolver should return the inner class.
|
named ``test``, the resolver should return the inner class.
|
||||||
'''
|
'''
|
||||||
|
@ -93,7 +93,7 @@ def test_nested_class():
|
||||||
|
|
||||||
def test_nested_class_method():
|
def test_nested_class_method():
|
||||||
'''Ensure the resolver finds an instance method of a nested class
|
'''Ensure the resolver finds an instance method of a nested class
|
||||||
|
|
||||||
Given the path ``/test/test`` and a root handler with an inner
|
Given the path ``/test/test`` and a root handler with an inner
|
||||||
class named ``test``, which in turn has an instance method named
|
class named ``test``, which in turn has an instance method named
|
||||||
``test``, the resolver should return the ``test`` method of the
|
``test``, the resolver should return the ``test`` method of the
|
||||||
|
@ -112,7 +112,7 @@ def test_nested_class_method():
|
||||||
|
|
||||||
def test_attribute():
|
def test_attribute():
|
||||||
'''Ensure the resolver finds a handler in an instance attribute
|
'''Ensure the resolver finds a handler in an instance attribute
|
||||||
|
|
||||||
Given the path ``/test`` and a root handler with an attribute named
|
Given the path ``/test`` and a root handler with an attribute named
|
||||||
``test`` containing another class, the resolver should return that
|
``test`` containing another class, the resolver should return that
|
||||||
class.
|
class.
|
||||||
|
@ -130,10 +130,10 @@ def test_attribute():
|
||||||
|
|
||||||
def test_default():
|
def test_default():
|
||||||
'''Ensure the resolver finds the default handler
|
'''Ensure the resolver finds the default handler
|
||||||
|
|
||||||
Given the path ``/test`` and a root handler with a method named
|
Given the path ``/test`` and a root handler with a method named
|
||||||
``default``, but no method named ``test``, the resolver should
|
``default``, but no method named ``test``, the resolver should
|
||||||
return the ``default`` method.
|
return the ``default`` method.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
class Root(object):
|
class Root(object):
|
||||||
|
@ -147,7 +147,7 @@ def test_default():
|
||||||
|
|
||||||
def test_nested_default():
|
def test_nested_default():
|
||||||
'''Ensure the resolver finds a nested default handler
|
'''Ensure the resolver finds a nested default handler
|
||||||
|
|
||||||
Given the path ``/test/bar`` and a root handler with a ``test``
|
Given the path ``/test/bar`` and a root handler with a ``test``
|
||||||
attribute containing a class instance with a ``default`` method but
|
attribute containing a class instance with a ``default`` method but
|
||||||
no ``bar`` method, the resolver should return the ``default``
|
no ``bar`` method, the resolver should return the ``default``
|
||||||
|
|
Loading…
Reference in New Issue