Skip to content

Instantly share code, notes, and snippets.

@hadrien
Last active September 16, 2016 17:58
Show Gist options
  • Select an option

  • Save hadrien/0efa49ca8837f948d63c to your computer and use it in GitHub Desktop.

Select an option

Save hadrien/0efa49ca8837f948d63c to your computer and use it in GitHub Desktop.

Revisions

  1. hadrien revised this gist May 9, 2014. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions doc.rst
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,14 @@
    ACL in pyramid
    ==============

    Dynamic ACLs
    ------------

    Here is a good example: http://stackoverflow.com/questions/5761617/pyramid-authorization-for-stored-items/5761901#5761901

    Dynamic group finding
    ---------------------

    This is the way I deal with ACL and reflect solely my opinion.

    It is an answer to `one conversation on twitter <https://twitter.com/merwok_/status/464472822744875010>`_
    @@ -17,3 +25,5 @@ Below example can be run directly and then:

    * ``curl -XPUT -H 'X-DUMMY-AUTH-USERID: bob' http://localhost:8080/users/bob/notes/456`` will succeed
    * ``curl -XPUT -H 'X-DUMMY-AUTH-USERID: not_bob' http://localhost:8080/users/bob/notes/456`` will return 403


  2. hadrien revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ ACL in pyramid

    This is the way I deal with ACL and reflect solely my opinion.

    It is an answer to https://twitter.com/merwok_/status/464472822744875010
    It is an answer to `one conversation on twitter <https://twitter.com/merwok_/status/464472822744875010>`_

    Rather than doing dynamic ACL which can be quite hard to read and maintain IMHO,
    I defined `roles <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L90>`_ which are allowed permissions on resource.
  3. hadrien revised this gist May 9, 2014. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions doc.rst
    Original file line number Diff line number Diff line change
    @@ -3,10 +3,10 @@ ACL in pyramid

    This is the way I deal with ACL and reflect solely my opinion.

    It is an more than 140 characters answer to https://twitter.com/merwok_/status/464472822744875010
    It is an answer to https://twitter.com/merwok_/status/464472822744875010

    Rather than doing dynamic ACL which can be quite hard to read and maintain IMHO,
    I defined a finite set of roles which are allowed permission on resource.
    I defined `roles <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L90>`_ which are allowed permissions on resource.

    For example, in a sticky notes as a service api, everyone can access Bob's notes.
    But only the **owner** must be able to change it.
  4. hadrien revised this gist May 9, 2014. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions doc.rst
    Original file line number Diff line number Diff line change
    @@ -13,3 +13,7 @@ But only the **owner** must be able to change it.

    In order to authenticate a user as an owner, resource define a `group_finder <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117>`_ method which the auth policy calls to extend principals.

    Below example can be run directly and then:

    * ``curl -XPUT -H 'X-DUMMY-AUTH-USERID: bob' http://localhost:8080/users/bob/notes/456`` will succeed
    * ``curl -XPUT -H 'X-DUMMY-AUTH-USERID: not_bob' http://localhost:8080/users/bob/notes/456`` will return 403
  5. hadrien revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ This is the way I deal with ACL and reflect solely my opinion.

    It is an more than 140 characters answer to https://twitter.com/merwok_/status/464472822744875010

    Rather than doing dynamic ACL which can be quite hard to read and maintain,
    Rather than doing dynamic ACL which can be quite hard to read and maintain IMHO,
    I defined a finite set of roles which are allowed permission on resource.

    For example, in a sticky notes as a service api, everyone can access Bob's notes.
  6. hadrien revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -11,5 +11,5 @@ I defined a finite set of roles which are allowed permission on resource.
    For example, in a sticky notes as a service api, everyone can access Bob's notes.
    But only the **owner** must be able to change it.

    In order to authenticate a user as an owner, resource define a `group_finder <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117>`_ method which the auth policy call to extend principals.
    In order to authenticate a user as an owner, resource define a `group_finder <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117>`_ method which the auth policy calls to extend principals.

  7. hadrien revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -11,5 +11,5 @@ I defined a finite set of roles which are allowed permission on resource.
    For example, in a sticky notes as a service api, everyone can access Bob's notes.
    But only the **owner** must be able to change it.

    In order to authenticate a user as an owner, resource define a `group_finder:https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117`_ method which the auth policy call to extend principals.
    In order to authenticate a user as an owner, resource define a `group_finder <https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117>`_ method which the auth policy call to extend principals.

  8. hadrien revised this gist May 9, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -11,5 +11,5 @@ I defined a finite set of roles which are allowed permission on resource.
    For example, in a sticky notes as a service api, everyone can access Bob's notes.
    But only the **owner** must be able to change it.

    In order to authenticate a user as an owner, resource define a ``group_finder`` method which the auth policy call to extend principals.
    In order to authenticate a user as an owner, resource define a `group_finder:https://gist.github.com/hadrien/0efa49ca8837f948d63c#file-example-py-L117`_ method which the auth policy call to extend principals.

  9. hadrien revised this gist May 9, 2014. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -48,6 +48,7 @@ def callback(self, userid, request):
    return principals


    # /
    class Root(object):

    def __init__(self, request):
    @@ -59,6 +60,7 @@ def __getitem__(self, key):
    return UsersCollection(key, self)


    # /users
    class UsersCollection(object):

    def __init__(self, name, parent):
    @@ -69,6 +71,7 @@ def __getitem__(self, key):
    return User(key, self)


    # /users/bob_marley/
    class User(object):

    def __init__(self, name, parent):
  10. hadrien revised this gist May 9, 2014. 2 changed files with 16 additions and 7 deletions.
    19 changes: 14 additions & 5 deletions doc.rst
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,15 @@
    ACL in pyramid with traversal
    =============================
    ACL in pyramid
    ==============

    This is the way I deal with ACL and reflect solely my opinion.

    It is an more than 140 characters answer to https://twitter.com/merwok_/status/464472822744875010

    Rather than doing dynamic ACL which can be quite hard to read and maintain,
    I defined a finite set of roles which are allowed permission on resource.

    For example, in a sticky notes as a service api, everyone can access Bob's notes.
    But only the **owner** must be able to change it.

    In order to authenticate a user as an owner, resource define a ``group_finder`` method which the auth policy call to extend principals.

    .. important::

    TL;DR: Rather than
    4 changes: 2 additions & 2 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -79,7 +79,7 @@ def __getitem__(self, key):
    return NotesCollection(key, self)


    # /users/123/notes/
    # /users/bob_marley/notes/
    class NotesCollection(object):

    __acl__ = [
    @@ -95,7 +95,7 @@ def __getitem__(self, key):
    return Note(key, self)


    # /users/123/notes/456
    # /users/bob_marley/notes/456
    class Note(object):

    def __init__(self, name, parent):
  11. hadrien revised this gist May 9, 2014. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    ACL in pyramid with traversal
    =============================

    .. note:: TL;DR: Rather than
    .. important::

    TL;DR: Rather than
  12. hadrien revised this gist May 9, 2014. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion doc.rst
    Original file line number Diff line number Diff line change
    @@ -1,2 +1,4 @@
    ACL in pyramid with traversal
    =============================
    =============================

    .. note:: TL;DR: Rather than
  13. hadrien revised this gist May 9, 2014. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions doc.rst
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    ACL in pyramid with traversal
    =============================
  14. hadrien revised this gist May 9, 2014. 1 changed file with 97 additions and 22 deletions.
    119 changes: 97 additions & 22 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -1,75 +1,150 @@
    from wsgiref.simple_server import make_server

    from pyramid.config import Configurator
    from pyramid.decorator import reify
    from pyramid.security import Allow, ALL_PERMISSIONS, Everyone, Deny, authenticated_userid
    from pyramid.authorization import ACLAuthorizationPolicy
    from pyramid.authentication import CallbackAuthenticationPolicy
    from pyramid.view import view_config, view_defaults
    from pyramid.security import (
    ALL_PERMISSIONS,
    Allow,
    unauthenticated_userid,
    Everyone,
    )
    from pyramid.traversal import find_interface


    def main(global_config, **settings):
    config = Configurator(settings=settings)
    config.include(__name__)
    return config.make_wsgi_app()


    def includeme(config):
    config.set_root_factory(Root)
    config.set_authentication_policy(AuthenticationPolicy())
    config.set_authorization_policy(ACLAuthorizationPolicy())
    config.scan()


    class AuthenticationPolicy(CallbackAuthenticationPolicy):

    def __init__(self):
    super(CallbackAuthenticationPolicy, self).__init__()

    def unauthenticated_userid(self, request):
    userid = None
    if 'X-DUMMY-AUTH-USERID' in request.headers:
    userid = request.headers.get('X-DUMMY-AUTH-USERID')
    return userid

    def callback(self, userid, request):
    principals = []
    context = request.context

    if hasattr(context, 'group_finder'):
    principals.extend(context.group_finder(request))

    return principals


    class Root(object):

    def __init__(self, request):
    self.request = request
    self.__name__ = ''
    self.__parent__ = None

    def __getitem__(self, key):
    return UsersCollection(key, self)


    class UsersCollection(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return User(key, self)


    class User(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return NotesCollection(key, self)


    # /users/123/notes/
    class NotesCollection(object):

    __acl__ = [
    (Allow, Everyone, 'show'),
    (Allow, 'group:owner', ALL_PERMISSIONS),
    ]

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return Note(key, self)


    # /users/123/notes/456
    class Note(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent
    @property

    @reify
    def user(self):
    # could be done via find_interface
    return self.parent.parent

    return find_interface(self, User)

    @property
    def userid(self):
    # /users/123/notes/456 will return 123
    return self.user.__name__

    def group_finder(self, request):
    principals = []
    if authenticated_userid(request) == self.user.__name__:
    if unauthenticated_userid(request) == self.user.__name__:
    principals.append('group:owner')
    return principals



    def replace(self, params):
    "save in db... Only owners can"
    return {'status': '200'}

    def show(self):
    "get from db"
    return {
    'id': self.__name__,
    'content': 'A dummy note',
    }


    @view_defaults(context=Note)
    class View(object):

    def __init__(self, context, request):
    self.request = request
    self.context = context

    @view_config(request_method='GET', permission='show', renderer='json')
    def show(self):
    return self.context.show()

    @view_config(request_method='PUT', permission='replace', renderer='json')
    def replace(self):
    return self.context.replace(self.request.POST)


    if __name__ == '__main__':
    server = make_server('0.0.0.0', 8080, main({}))
    server.serve_forever()
  15. hadrien revised this gist May 8, 2014. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    from pyramid.decorator import reify
    from pyramid.security import Allow, ALL_PERMISSIONS, Everyone, Deny, authenticated_userid


    class Root(object):

    def __init__(self, request):
    @@ -12,7 +13,7 @@ def __getitem__(self, key):
    return UsersCollection(key, self)


    class UserCollection(object):
    class UsersCollection(object):

    def __init__(self, name, parent):
    self.__name__ = name
    @@ -29,11 +30,11 @@ def __init__(self, name, parent):
    self.__parent__ = parent

    def __getitem__(self, key):
    return NoteCollection(key, self)
    return NotesCollection(key, self)


    # /users/123/notes/
    class NoteCollection(object):
    class NotesCollection(object):

    __acl__ = [
    (Allow, Everyone, 'show'),
  16. hadrien renamed this gist May 8, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  17. hadrien renamed this gist May 8, 2014. 1 changed file with 32 additions and 2 deletions.
    34 changes: 32 additions & 2 deletions example.py → gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,13 +1,43 @@
    from pyramid.decorator import reify
    from pyramid.security import Allow, ALL_PERMISSIONS, Everyone, Deny, authenticated_userid

    class Root(object):

    def __init__(self, request):
    self.request = request
    self.__name__ = ''
    self.__parent__ = None

    def __getitem__(self, key):
    return UsersCollection(key, self)


    class UserCollection(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return User(key, self)


    class User(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return NoteCollection(key, self)


    # /users/123/notes/
    class PhotoCollection(object):
    class NoteCollection(object):

    __acl__ = [
    (Allow, Everyone, 'show'),
    (Allow, 'group:owner',
    (Allow, 'group:owner', ALL_PERMISSIONS),
    ]

    def __init__(self, name, parent):
  18. hadrien revised this gist May 8, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion example.py
    Original file line number Diff line number Diff line change
    @@ -39,5 +39,6 @@ def group_finder(self, request):
    principals = []
    if authenticated_userid(request) == self.user.__name__:
    principals.append('group:owner')
    return principals


  19. hadrien revised this gist May 8, 2014. 1 changed file with 38 additions and 5 deletions.
    43 changes: 38 additions & 5 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,43 @@
    from pyramid.security import Allow, Everyone, Deny
    from pyramid.decorator import reify
    from pyramid.security import Allow, ALL_PERMISSIONS, Everyone, Deny, authenticated_userid


    # /users/123/photos

    class oto(object):
    # /users/123/notes/
    class PhotoCollection(object):
    __acl__ = [
    (Allow, Everyone, 'show'),
    (Allow, 'group:owner',
    ]


    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    def __getitem__(self, key):
    return Note(key, self)


    # /users/123/notes/456
    class Note(object):

    def __init__(self, name, parent):
    self.__name__ = name
    self.__parent__ = parent

    @property
    def user(self):
    # could be done via find_interface
    return self.parent.parent

    @property
    def userid(self):
    # /users/123/notes/456 will return 123
    return self.user.__name__

    def group_finder(self, request):
    principals = []
    if authenticated_userid(request) == self.user.__name__:
    principals.append('group:owner')


  20. hadrien created this gist May 8, 2014.
    10 changes: 10 additions & 0 deletions example.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    from pyramid.security import Allow, Everyone, Deny


    # /users/123/photos

    class oto(object):
    __acl__ = [
    (Allow, Everyone, 'show'),
    ]