Skip to content

Commit

Permalink
Make permissions in dynamic item lazy evaluated
Browse files Browse the repository at this point in the history
This fixes #302. By adding lazy evaluation for the permissions we prevent
manage.py breaking down when using dynamic items with permissions in an
AppConfig.ready().
  • Loading branch information
jgadelange committed Aug 3, 2021
1 parent a2d77e9 commit 725a42d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 32 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Kishor Kunal Raj <https://github.com/kishorkunal-raj>
Ben Finney <https://github.com/bignose-debian>
witwar <https://github.com/witwar>
Jon Kiparsky <https://github.com/jonkiparsky>
Jeffrey de Lange <https://github.com/jgadelange>


Translators
Expand Down
9 changes: 6 additions & 3 deletions sitetree/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,16 @@ def test_bad_string_permissions(self):
from sitetree.toolbox import item

with pytest.raises(ValueError):
item('root', 'url', access_by_perms='bad name')
# Force setup to ensure evaluation
item('root', 'url', access_by_perms='bad name').permissions._setup()

with pytest.raises(ValueError):
item('root', 'url', access_by_perms='unknown.name')
# Force setup to ensure evaluation
item('root', 'url', access_by_perms='unknown.name').permissions._setup()

with pytest.raises(ValueError):
item('root', 'url', access_by_perms=42.2)
# Force setup to ensure evaluation
item('root', 'url', access_by_perms=42.2).permissions._setup()

def test_access_restricted(self):
from sitetree.toolbox import item
Expand Down
65 changes: 36 additions & 29 deletions sitetree/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.apps import apps
from django.contrib.auth.models import Permission
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import SimpleLazyObject
from django.utils.module_loading import module_has_submodule

from . import settings
Expand Down Expand Up @@ -51,6 +52,39 @@ def traverse(items):
return tree_obj


def clean_permission(permission: TypePermission) -> Union[int, Permission]:
if isinstance(permission, str):
# Get permission object from string
try:
app, codename = permission.split('.')
except ValueError:
raise ValueError(
f'Wrong permission string format: supplied - `{permission}`; '
'expected - `<app_name>.<permission_name>`.')
try:
return Permission.objects.get(codename=codename, content_type__app_label=app)
except Permission.DoesNotExist:
raise ValueError(f'Permission `{app}.{codename}` does not exist.')
elif not isinstance(permission, (int, Permission)):
raise ValueError('Permissions must be given as strings, ints, or `Permission` instances.')

return permission


def clean_permissions(permissions: Union[TypePermission, List[TypePermission]]) -> List[Permission]:
if permissions is None:
return []

# Make permissions a list if currently a single object
if not isinstance(permissions, list):
permissions = [permissions]

return [
clean_permission(permission)
for permission in permissions
]


def item(
title: str,
url: str,
Expand Down Expand Up @@ -118,37 +152,10 @@ def item(
item_obj.is_dynamic = True
item_obj.dynamic_children = []

cleaned_permissions = []
if access_by_perms:
# Make permissions a list if currently a single object
if not isinstance(access_by_perms, list):
access_by_perms = [access_by_perms]

for perm in access_by_perms:
if isinstance(perm, str):
# Get permission object from string
try:
app, codename = perm.split('.')
except ValueError:
raise ValueError(
f'Wrong permission string format: supplied - `{perm}`; '
'expected - `<app_name>.<permission_name>`.')

try:
perm = Permission.objects.get(codename=codename, content_type__app_label=app)

except Permission.DoesNotExist:
raise ValueError(f'Permission `{app}.{codename}` does not exist.')

elif not isinstance(perm, (int, Permission)):
raise ValueError('Permissions must be given as strings, ints, or `Permission` instances.')

cleaned_permissions.append(perm)

item_obj.permissions = cleaned_permissions or []
item_obj.permissions = SimpleLazyObject(lambda: clean_permissions(access_by_perms))
item_obj.access_perm_type = item_obj.PERM_TYPE_ALL if perms_mode_all else item_obj.PERM_TYPE_ANY

if item_obj.permissions:
if access_by_perms:
item_obj.access_restricted = True

if children is not None:
Expand Down

0 comments on commit 725a42d

Please sign in to comment.