1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Implement ObjectPermissionManager

This commit is contained in:
Jeremy Stretch
2020-05-11 14:32:10 -04:00
parent 06aca2e1d5
commit 63f842c7db
5 changed files with 85 additions and 54 deletions

View File

@@ -56,21 +56,12 @@ class ObjectPermissionBackend(ModelBackend):
if model._meta.model_name != model_name:
raise ValueError(f"Invalid permission {perm} for model {model}")
# Retrieve user's permissions for this model
# This can probably be cached
obj_permissions = ObjectPermission.objects.filter(
Q(users=user_obj) | Q(groups__user=user_obj),
model=ContentType.objects.get_for_model(obj),
**{f'can_{action}': True}
)
for perm in obj_permissions:
# Attempt to retrieve the model from the database using the
# attributes defined in the ObjectPermission. If we have a
# match, assert that the user has permission.
if model.objects.filter(pk=obj.pk, **perm.attrs).exists():
return True
# Attempt to retrieve the model from the database using the
# attributes defined in the ObjectPermission. If we have a
# match, assert that the user has permission.
attrs = ObjectPermission.objects.get_attr_constraints(user_obj, obj, action)
if model.objects.filter(pk=obj.pk, **attrs).exists():
return True
class RemoteUserBackend(ViewExemptModelBackend, RemoteUserBackend_):

View File

@@ -4,7 +4,7 @@ from copy import deepcopy
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldDoesNotExist, ValidationError
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
from django.db import transaction, IntegrityError
from django.db.models import ManyToManyField, ProtectedError
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
@@ -23,6 +23,7 @@ from django_tables2 import RequestConfig
from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset
from users.models import ObjectPermission
from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
from utilities.utils import csv_format, prepare_cloned_fields
@@ -262,32 +263,43 @@ class ObjectEditView(GetReturnURLMixin, View):
if form.is_valid():
logger.debug("Form validation was successful")
obj = form.save()
msg = '{} {}'.format(
'Created' if not form.instance.pk else 'Modified',
self.queryset.model._meta.verbose_name
)
logger.info(f"{msg} {obj} (PK: {obj.pk})")
if hasattr(obj, 'get_absolute_url'):
msg = '{} <a href="{}">{}</a>'.format(msg, obj.get_absolute_url(), escape(obj))
else:
msg = '{} {}'.format(msg, escape(obj))
messages.success(request, mark_safe(msg))
try:
with transaction.atomic():
obj = form.save()
if '_addanother' in request.POST:
# Check that the new object conforms with any assigned object-level permissions
self.queryset.get(pk=obj.pk)
# If the object has clone_fields, pre-populate a new instance of the form
if hasattr(obj, 'clone_fields'):
url = '{}?{}'.format(request.path, prepare_cloned_fields(obj))
return redirect(url)
msg = '{} {}'.format(
'Created' if not form.instance.pk else 'Modified',
self.queryset.model._meta.verbose_name
)
logger.info(f"{msg} {obj} (PK: {obj.pk})")
if hasattr(obj, 'get_absolute_url'):
msg = '{} <a href="{}">{}</a>'.format(msg, obj.get_absolute_url(), escape(obj))
else:
msg = '{} {}'.format(msg, escape(obj))
messages.success(request, mark_safe(msg))
return redirect(request.get_full_path())
if '_addanother' in request.POST:
return_url = form.cleaned_data.get('return_url')
if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()):
return redirect(return_url)
else:
return redirect(self.get_return_url(request, obj))
# If the object has clone_fields, pre-populate a new instance of the form
if hasattr(obj, 'clone_fields'):
url = '{}?{}'.format(request.path, prepare_cloned_fields(obj))
return redirect(url)
return redirect(request.get_full_path())
return_url = form.cleaned_data.get('return_url')
if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()):
return redirect(return_url)
else:
return redirect(self.get_return_url(request, obj))
except ObjectDoesNotExist:
logger.debug("Object save failed due to object-level permissions violation")
# TODO: Link user to personal permissions view
form.add_error(None, "Object save failed due to object-level permissions violation")
else:
logger.debug("Form validation failed")