Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ venv/

# Some temp files
.DS_Store

.idea/
12 changes: 12 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
djangorestframework = "==3.8.*"

[dev-packages]

[requires]
python_version = "*"
29 changes: 29 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 17 additions & 6 deletions rest_condition/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def _is_permission_factory(obj):


class ConditionalPermission(permissions.BasePermission):
'''
"""
Example of usage:
>>> class MyView(GinericView):
>>> permission_classes = (ConditionalPermission, )
Expand All @@ -24,7 +24,7 @@ class ConditionalPermission(permissions.BasePermission):
Or you can just:
>>> class MyView(GinericView):
>>> permission_classes = (C(Perm1) | ~C(Perm2), )
'''
"""
permission_condition_field = 'permission_condition'

def get_permission_condition(self, view):
Expand All @@ -47,7 +47,7 @@ def has_permission(self, request, view):


class Condition(object):
'''
"""
Provides a simple way to define complex and multi-depth
(with logic operators) permissions tree.

Expand All @@ -61,7 +61,7 @@ class Condition(object):
permission will be evaluated to `True`:
>>> cond1 = C(Perm1, Perm2, Perm3, Perm4,
>>> reduce_op=operator.add, lazy_until=3, negated=True)
'''
"""
@classmethod
def And(cls, *perms_or_conds):
return cls(reduce_op=operator.and_, lazy_until=False, *perms_or_conds)
Expand All @@ -79,13 +79,17 @@ def __init__(self, *perms_or_conds, **kwargs):
self.reduce_op = kwargs.get('reduce_op', operator.and_)
self.lazy_until = kwargs.get('lazy_until', False)
self.negated = kwargs.get('negated')
self.message = None

def evaluate_permissions(self, permission_name, *args, **kwargs):

reduced_result = _NONE
last_condition = None

for condition in self.perms_or_conds:
if hasattr(condition, 'evaluate_permissions'):
result = condition.evaluate_permissions(permission_name, *args, **kwargs)
result = condition.evaluate_permissions(
permission_name, *args, **kwargs)
else:
if _is_permission_factory(condition):
condition = condition()
Expand All @@ -103,9 +107,16 @@ def evaluate_permissions(self, permission_name, *args, **kwargs):
else:
reduced_result = self.reduce_op(reduced_result, result)

if self.lazy_until is not None and self.lazy_until is reduced_result:
last_condition = condition

if self.lazy_until is not None and \
self.lazy_until is reduced_result:
break

message = getattr(last_condition, 'message', None)
if message:
self.message = message

if reduced_result is not _NONE:
return not reduced_result if self.negated else reduced_result

Expand Down
53 changes: 52 additions & 1 deletion tests/tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,34 @@ def test_permission(self, request):

return True

def test_message(self, request):
from rest_framework.request import Request

request = Request(request)

self.request = request

last_permission = None

for permission in self.get_permissions():
last_permission = permission
if not permission.has_permission(request, self):
return permission.message
return last_permission.message


class TruePermission(BasePermission):

message = "YES"

def has_permission(self, request, view):
return True


class FalsePermission(BasePermission):

message = "NO"

def has_permission(self, request, view):
return False

Expand All @@ -50,7 +69,13 @@ def assertViewPermission(self, view_class, granted=True):
else:
self.assertFalse(result)

def test_conditional_permissions_with_assigment(self):
def assertViewPermissionMessage(self, view_class, message):
view = view_class()
request = self.requests.get('/')
permission_message = view.test_message(request)
self.assertEqual(message, permission_message)

def test_conditional_permissions_with_assignment(self):

perm1 = C(TruePermission)
perm1 |= ~C(TruePermission)
Expand All @@ -70,6 +95,32 @@ class View2(TestView):

self.assertViewPermission(View2, True)

def test_conditional_permissions_with_message(self):

perm1 = And(TruePermission, FalsePermission)

class View1(TestView):
permission_classes = [perm1]

self.assertViewPermissionMessage(View1, FalsePermission.message)

perm2 = Or(TruePermission, FalsePermission)

class View2(TestView):
permission_classes = [perm2]

self.assertViewPermissionMessage(View2, TruePermission.message)

perm3 = And(
Or(Not(FalsePermission), FalsePermission),
Or(Not(TruePermission), FalsePermission)
)

class View3(TestView):
permission_classes = [perm3]

self.assertViewPermissionMessage(View3, FalsePermission.message)

def test_single_conditional_permission_true(self):

class View1(TestView):
Expand Down
10 changes: 8 additions & 2 deletions tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from rest_framework.views import APIView
try:
from django.conf.urls import include, patterns, url
from django.conf.urls import patterns
except ImportError:
from django.conf.urls.defaults import include, patterns, url
try:
from django.conf.urls.defaults import patterns
except ImportError:
from django.urls import path
patterns = lambda x: [path('', APIView.as_view())]


urlpatterns = patterns('', )