import functools import inspect # WARNING: *MAGIC MODULE* # This is not a safe place, lot of magic is happening here class Permission(object): """ Base class used for defining class and instance permissions. Enabling an ''intuitive'' interface for checking permissions: # Define permissions class NodePermission(Permission): def change(self, obj, cls, user): return obj.user == user # Provide permissions Node.has_permission = NodePermission() # Check class permission by passing it as string Node.has_permission(user, 'change') # Check class permission by calling it Node.has_permission.change(user) # Check instance permissions node = Node() node.has_permission(user, 'change') node.has_permission.change(user) """ def __get__(self, obj, cls): """ Hacking object internals to provide means for the mentioned interface """ # call interface: has_permission(user, 'perm') def call(user, perm): return getattr(self, perm)(obj, cls, user) # has_permission.perm(user) for func in inspect.getmembers(type(self), predicate=inspect.ismethod): if not isinstance(self, func[1].__self__.__class__): # aggregated methods setattr(call, func[0], functools.partial(func[1], obj, cls)) else: # self methods setattr(call, func[0], functools.partial(func[1], self, obj, cls)) return call def _aggregate(self, obj, cls, perm): """ Aggregates cls methods to self class""" for method in inspect.getmembers(perm, predicate=inspect.ismethod): if not method[0].startswith('_'): setattr(type(self), method[0], method[1]) class ReadOnlyPermission(Permission): """ Read only permissions """ def view(self, obj, cls, user): return True class AllowAllPermission(object): """ All methods return True """ def __get__(self, obj, cls): return self.AllowAllWrapper() class AllowAllWrapper(object): """ Fake object that always returns True """ def __call__(self, *args): return True def __getattr__(self, name): return lambda n: True class RelatedPermission(Permission): """ Inherit permissions of a related object The following example will inherit permissions from sliver_iface.sliver.slice SliverIfaces.has_permission = RelatedPermission('sliver.slices') """ def __init__(self, relation): self.relation = relation def __get__(self, obj, cls): """ Hacking object internals to provide means for the mentioned interface """ # Walk through FK relations relations = self.relation.split('.') if obj is None: parent = cls for relation in relations: parent = getattr(parent, relation).field.rel.to else: parent = functools.reduce(getattr, relations, obj) # call interface: has_permission(user, 'perm') def call(user, perm): return parent.has_permission(user, perm) # method interface: has_permission.perm(user) for name, func in parent.has_permission.__dict__.items(): if not name.startswith('_'): setattr(call, name, func) return call