Santiago L
48ef1f21e3
Fix regression introduced by 7d975637d5
when there is a misunderstanding while replacing deprecated rel.to
107 lines
3.4 KiB
Python
107 lines
3.4 KiB
Python
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.related_model
|
|
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
|