Skip to content

Commit

Permalink
Merge branch 'feature.callable-acl'
Browse files Browse the repository at this point in the history
  • Loading branch information
mmerickel committed Mar 19, 2013
2 parents fc9dfba + 81e84fe commit aae62a0
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 5 deletions.
13 changes: 13 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
next release
============

Features
--------

- ``ACLAuthorizationPolicy`` supports ``__acl__`` as a callable. This
removes the ambiguity between the potential ``AttributeError`` that would
be raised on the ``context`` when the property was not defined and the
``AttributeError`` that could be raised from any user-defined code within
a dynamic property. It is recommended to define a dynamic ACL as a callable
to avoid this ambiguity. See https://github.com/Pylons/pyramid/issues/735.

1.4 (2012-12-18)
================

Expand Down
31 changes: 26 additions & 5 deletions docs/narr/security.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ class:
.. code-block:: python
:linenos:
from pyramid.security import Everyone
from pyramid.security import Allow
from pyramid.security import Everyone
class Blog(object):
__acl__ = [
Expand All @@ -250,8 +250,8 @@ Or, if your resources are persistent, an ACL might be specified via the
.. code-block:: python
:linenos:
from pyramid.security import Everyone
from pyramid.security import Allow
from pyramid.security import Everyone
class Blog(object):
pass
Expand All @@ -270,6 +270,27 @@ resource instances with an ACL (as opposed to just decorating their class) in
applications such as "CMS" systems where fine-grained access is required on
an object-by-object basis.

Dynamic ACLs are also possible by turning the ACL into a callable on the
resource. This may allow the ACL to dynamically generate rules based on
properties of the instance.

.. code-block:: python
:linenos:
from pyramid.security import Allow
from pyramid.security import Everyone
class Blog(object):
def __acl__(self):
return [
(Allow, Everyone, 'view'),
(Allow, self.owner, 'edit'),
(Allow, 'group:editors', 'edit'),
]
def __init__(self, owner):
self.owner = owner
.. index::
single: ACE
single: access control entry
Expand All @@ -282,8 +303,8 @@ Here's an example ACL:
.. code-block:: python
:linenos:
from pyramid.security import Everyone
from pyramid.security import Allow
from pyramid.security import Everyone
__acl__ = [
(Allow, Everyone, 'view'),
Expand Down Expand Up @@ -321,9 +342,9 @@ order dictated by the ACL*. So if you have an ACL like this:
.. code-block:: python
:linenos:
from pyramid.security import Everyone
from pyramid.security import Allow
from pyramid.security import Deny
from pyramid.security import Everyone
__acl__ = [
(Allow, Everyone, 'view'),
Expand Down Expand Up @@ -359,8 +380,8 @@ ACE, as below.
.. code-block:: python
:linenos:
from pyramid.security import Everyone
from pyramid.security import Allow
from pyramid.security import Everyone
__acl__ = [
(Allow, Everyone, 'view'),
Expand Down
3 changes: 3 additions & 0 deletions pyramid/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ def permits(self, context, principals, permission):
except AttributeError:
continue

if acl and callable(acl):
acl = acl()

for ace in acl:
ace_action, ace_principal, ace_permissions = ace
if ace_principal in principals:
Expand Down
9 changes: 9 additions & 0 deletions pyramid/tests/test_authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ def test_principals_allowed_by_permission_deny_permission_in_acl(self):
result = sorted(
policy.principals_allowed_by_permission(context, 'read'))
self.assertEqual(result, [])

def test_callable_acl(self):
from pyramid.security import Allow
context = DummyContext()
fn = lambda self: [(Allow, 'bob', 'read')]
context.__acl__ = fn.__get__(context, context.__class__)
policy = self._makeOne()
result = policy.permits(context, ['bob'], 'read')
self.assertTrue(result)


class DummyContext:
Expand Down

0 comments on commit aae62a0

Please sign in to comment.