Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pyramid: Authentication and Authorization Policy

Rach Belaid
September 03, 2013

Pyramid: Authentication and Authorization Policy

Attempt to bring some lights in this catacomb

Rach Belaid

September 03, 2013
Tweet

More Decks by Rach Belaid

Other Decks in Programming

Transcript

  1. Hello, Bonjour Rach Belaid Python Developer, Postgres Fan, Git Lover,

    Belgian Erlang Hobbyist, rach @rachbelaid rachbelaid.com [email protected] Wednesday, 1 January 14
  2. Why? User Users Resources Repo Issue Wiki Org Billing Anonymous

    User User ACL ACL ACL A Wednesday, 1 January 14
  3. Authentication Policies Simply to handle the Login / Logout and

    you use it by calling : from pyramid.security import * remember(request, userid) -> headers forget(request) -> headers .... Wednesday, 1 January 14
  4. Authorization policy Pyramid provide: - ACLAuthorizationPolicy This policy consults an

    ACL attached to a context/resource and determine authorization information. Wednesday, 1 January 14
  5. Example - Simple App from wsgiref.simple_server import make_server from pyramid.config

    import Configurator from pyramid.response import Response def hello_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() config.add_route('home', '/') config.add_view(hello_world, route_name='home') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever() Wednesday, 1 January 14
  6. Auth’s setup from wsgiref.simple_server import make_server from pyramid.config import Configurator

    from pyramid.response import Response from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy def hello_world(request): return Response('Hello world!') if __name__ == '__main__': config = Configurator() authn_policy = AuthTktAuthenticationPolicy('seekrit') authz_policy = ACLAuthorizationPolicy() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.add_route('home', '/') config.add_view(hello_world, route_name='home') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever() Wednesday, 1 January 14
  7. Login and Logout Views @view_config(route_name='login') def login(request): username = request.matchdict['username']

    #check valids headers = remember(request, username) return HTTPFound(location='/' , headers=headers) @view_config(route_name='logout') def logout(request): headers = forget(request) return HTTPFound(location='/' , headers=headers) Wednesday, 1 January 14
  8. Terminology - ACL - ACE - Resource and Resource Tree

    - Lineage - Principal - Root Factory Wednesday, 1 January 14
  9. ACL and ACE - ACL: Access control list. An ACL

    is a sequence of ACE tuples - ACE : Access control entry a three-tuple that describes three things: an action (Allow or Deny), a principal (a string describing a user or group), and a permission __acl__ = [ (Allow, 'fred', 'view'), (Allow, 'henry', 'view') ] (Allow, 'fred', 'view') Wednesday, 1 January 14
  10. principal A principal is a string object representing a user

    id or a group id Wednesday, 1 January 14
  11. Resource & Resources Tree Representing object to access in the

    resource tree of an application. Tree : A nested set of dictionary-like objects, each of which is a resource Wednesday, 1 January 14
  12. Lineage The lineage of a resource is composed of itself,

    its parent, its parent’s parent, and so on. If part of a lineage, the context’s parents are consulted for the ACL information too. Wednesday, 1 January 14
  13. Root Factory This object will usually be used as the

    context resource config.add_route('idea', 'ideas/{idea}', factory=Idea) config.add_view('myproject.views.idea_view', route_name='idea') ... class Idea(object): def __init__(self, request): matchdict = request.matchdict idea = matchdict.get('idea', None) if idea == 'mine': self.__acl__ = [ (Allow, 'editor', 'view') Wednesday, 1 January 14
  14. Type & instance permission __acl__ attribute can be defined on

    the resource instance if you need instance- level security, or it can be defined on the resource class if you just need type- level security. Wednesday, 1 January 14
  15. Assigning ACLs to your Resource Objects class Repo(Base): __acl__ =

    [ (Allow, 'jack', 'view')] class Billing(object): def __init__(self, request): matchdict = request.matchdict self.id = matchdict.get('id', None) if self.id == '1': self.__acl__ = [ (Allow, 'henry', 'view'), (Allow, 'jack', 'view') ] On the class On the instance Wednesday, 1 January 14
  16. ACL Inheritance if a resource object does not have an

    ACL when it is the context, its parent is consulted for an ACL. class Repo(Base): __acl__ = [ (Allow, 'jack', 'view') ] class Wiki(Base): __name__ = 'wiki' __parent__ = Repo Wednesday, 1 January 14
  17. Example: Imports from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy

    from pyramid.authorization import ACLAuthorizationPolicy from pyramid.view import view_config from pyramid.security import Allow, remember, forget from pyramid.security import authenticated_userid from pyramid.httpexceptions import HTTPFound Wednesday, 1 January 14
  18. Example: Resources class Base(object): def __init__(self, request): matchdict = request.matchdict

    self.id = matchdict.get('id', None) class Repo(Base): __acl__ = [ (Allow, 'jack', 'view')] class Wiki(Base): __name__ = 'wiki' __parent__ = Repo class Issue(Base): __name__ = 'issue' __parent__ = Repo class Org(Base): __acl__ = [ (Allow, 'fred', 'view'), (Allow, 'henry', 'view') ] class Billing(Base): __name__ = 'billing' def __init__(self, request): super(Billing,self).__init__(request) if self.id == '1': self.__acl__ = [ (Allow, 'henry', 'view'), (Allow, 'jack', 'view') ] self.__parent__ = Org Wednesday, 1 January 14
  19. Example: App def main(global_config, **settings): settings = dict(settings) authn_policy =

    AuthTktAuthenticationPolicy('secret') authz_policy = ACLAuthorizationPolicy() config = Configurator( settings=settings) config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) #route config.add_route('login', '/login/{username:\w+}') config.add_route('logout', '/logout') config.add_route('hello', '/') config.add_route('repo', '/repo/{id:\d+}', factory=Repo) config.add_route('issue', '/issue/{id:\d+}', factory=Issue) config.add_route('wiki', '/wiki/{id:\d+}', factory=Wiki) config.add_route('org', '/org/{id:\d+}', factory=Org) config.add_route('billing', '/billing/{id:\d+}',factory=Billing) #add views config.scan() return config.make_wsgi_app() Wednesday, 1 January 14
  20. Example: View @view_config(route_name='login') def login(request): headers = remember(request, request.matchdict['username']) return

    HTTPFound(location='/' , headers=headers) @view_config(route_name='logout') def logout(request): headers = forget(request) return HTTPFound(location='/' , headers=headers) @view_config(route_name='hello', renderer='string') def hello(request): userid = authenticated_userid(request) return 'Hello %s' % userid @view_config(route_name='repo', renderer='string', permission='view') @view_config(route_name='wiki', renderer='string', permission='view') @view_config(route_name='issue', renderer='string', permission='view') @view_config(route_name='org', renderer='string', permission='view') @view_config(route_name='billing', renderer='string', permission='view') def perm(request): return 'ok' Wednesday, 1 January 14
  21. Example Results - henry, fred has access to all billings

    - jack has access to billing 1 - jack has access to Repo, Issue, Wiki Wednesday, 1 January 14
  22. Let’s clean that ... with a bit of traversal ...

    no request handling in your resources Url dispatch + Traversal = AWESOME Wednesday, 1 January 14
  23. Example: Imports from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy

    from pyramid.authorization import ACLAuthorizationPolicy from pyramid.view import view_config from pyramid.security import Allow, remember, forget from pyramid.security import authenticated_userid from pyramid.httpexceptions import HTTPFound Wednesday, 1 January 14
  24. Example: Resources class Repo(object): __acl__ = [ (Allow, 'jack', 'view')]

    class Wiki(object): __name__ = 'wiki' __parent__ = Repo class Issue(object): __name__ = 'issue' __parent__ = Repo class Org(object): __acl__ = [ (Allow, 'fred', 'view'), (Allow, 'henry', 'view') ] class Billing(object): __name__ = 'billing' def __getitem__(self, key): if key == '1': self.__acl__ = [ (Allow, 'henry', 'view'), (Allow, 'jack', 'view') ] self.__parent__ = Org Wednesday, 1 January 14
  25. Example: App def main(global_config, **settings): settings = dict(settings) .... same

    as before .... #route config.add_route('login', '/login/{username:\w+}') config.add_route('logout', '/logout') config.add_route('hello', '/') config.add_route('repo', '/repo/{id:\d+}', factory=lambda x: Repo(), traverse= '/{id:\d+}') config.add_route('issue', '/issue/{id:\d+}', factory=lambda x: Issue(), traverse= '/{id:\d+}') config.add_route('wiki', '/wiki/{id:\d+}', factory=lambda x: Wiki(), traverse= '/{id:\d+}') config.add_route('org', '/org/{id:\d+}', factory=lambda x: Org(), traverse= '/{id:\d+}') config.add_route('billing', '/billing/{id:\d+}',factory=lambda x: Billing() traverse= '/{id:\d+}') #add views config.scan() return config.make_wsgi_app() Wednesday, 1 January 14
  26. Example: View @view_config(route_name='login') def login(request): headers = remember(request, request.matchdict['username']) return

    HTTPFound(location='/' , headers=headers) @view_config(route_name='logout') def logout(request): headers = forget(request) return HTTPFound(location='/' , headers=headers) @view_config(route_name='hello', renderer='string') def hello(request): userid = authenticated_userid(request) return 'Hello %s' % userid @view_config(route_name='repo', renderer='string', permission='view') @view_config(route_name='wiki', renderer='string', permission='view') @view_config(route_name='issue', renderer='string', permission='view') @view_config(route_name='org', renderer='string', permission='view') @view_config(route_name='billing', renderer='string', permission='view') def perm(request): return 'ok' Wednesday, 1 January 14
  27. Example Results - henry, fred has access to all billings

    - jack has access to billing 1 - jack has access to Repo, Issue, Wiki Wednesday, 1 January 14
  28. Callback def groupfinder(userid, request): if userid in USERS: return GROUPS.get(userid,

    []) def main(global_config, **settings): settings = dict(settings) authn_policy = AuthTktAuthenticationPolicy('secret',callback) authz_policy = ACLAuthorizationPolicy() config = Configurator( settings=settings) config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) #route ..... #add views config.scan() return config.make_wsgi_app() expected to return None if the userid doesn’t exist Wednesday, 1 January 14
  29. References - Pyramid docs - Let’s ride bikes - Pycon

    2013 from Chris McDonough https://github.com/mcdonc/bikes Wednesday, 1 January 14