Note
TL;DR: Rather than
| from wsgiref.simple_server import make_server | |
| from pyramid.config import Configurator | |
| from pyramid.decorator import reify | |
| 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 | |
| @reify | |
| def user(self): | |
| 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 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() |
@wolever you could define a role 'group:friends' and the resource group_finder method would append 'group:friends' to principals if the authenticated user is a friend of the resource's owner.
Does it make sense?