summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2013-03-18 21:54:57 -0700
committerMichael Merickel <michael@merickel.org>2013-03-18 21:54:57 -0700
commitaae62a0ba0d4709b50fd5f2e0be86fd91080d014 (patch)
tree4335f960277684b87f99b085a07f7b7deec3fda0
parentfc9dfbae2a76732ee8d027825fac28dbfd57e581 (diff)
parent81e84fe86bee0c2753674fdaead001803936a2ba (diff)
downloadpyramid-aae62a0ba0d4709b50fd5f2e0be86fd91080d014.tar.gz
pyramid-aae62a0ba0d4709b50fd5f2e0be86fd91080d014.tar.bz2
pyramid-aae62a0ba0d4709b50fd5f2e0be86fd91080d014.zip
Merge branch 'feature.callable-acl'
-rw-r--r--CHANGES.txt13
-rw-r--r--docs/narr/security.rst31
-rw-r--r--pyramid/authorization.py3
-rw-r--r--pyramid/tests/test_authorization.py9
4 files changed, 51 insertions, 5 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 85dd3be2a..ae2cafba4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)
================
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index 5b79edd19..203aa2404 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -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__ = [
@@ -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
@@ -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
@@ -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'),
@@ -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'),
@@ -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'),
diff --git a/pyramid/authorization.py b/pyramid/authorization.py
index 943f8bd00..1fd05e244 100644
--- a/pyramid/authorization.py
+++ b/pyramid/authorization.py
@@ -80,6 +80,9 @@ class ACLAuthorizationPolicy(object):
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:
diff --git a/pyramid/tests/test_authorization.py b/pyramid/tests/test_authorization.py
index 27f2a18b4..60b1b0c8d 100644
--- a/pyramid/tests/test_authorization.py
+++ b/pyramid/tests/test_authorization.py
@@ -215,6 +215,15 @@ class TestACLAuthorizationPolicy(unittest.TestCase):
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: