Implements #2006 - run reports and scripts in the background

This commit is contained in:
John Anderson
2020-06-29 03:50:05 -04:00
parent eb8c0539c5
commit 3777fbccc3
29 changed files with 953 additions and 192 deletions

View File

@@ -42,3 +42,25 @@ ADVISORY_LOCK_KEYS = {
'available-prefixes': 100100,
'available-ips': 100200,
}
#
# HTTP Request META safe copy
#
HTTP_REQUEST_META_SAFE_COPY = [
'CONTENT_LENGTH',
'CONTENT_TYPE',
'HTTP_ACCEPT',
'HTTP_ACCEPT_ENCODING',
'HTTP_ACCEPT_LANGUAGE',
'HTTP_HOST',
'HTTP_REFERER',
'HTTP_USER_AGENT',
'QUERY_STRING',
'REMOTE_ADDR',
'REMOTE_HOST',
'REMOTE_USER',
'REQUEST_METHOD',
'SERVER_NAME',
'SERVER_PORT',
]

View File

@@ -5,11 +5,12 @@ from collections import OrderedDict
from django.core.serializers import serialize
from django.db.models import Count, OuterRef, Subquery
from django.http import QueryDict
from django.http.request import HttpRequest
from jinja2 import Environment
from dcim.choices import CableLengthUnitChoices
from extras.utils import is_taggable
from utilities.constants import HTTP_REQUEST_META_SAFE_COPY
def csv_format(data):
"""
@@ -257,3 +258,37 @@ def flatten_dict(d, prefix='', separator='.'):
else:
ret[key] = v
return ret
#
# Fake request object
#
class NetBoxFakeRequest:
"""
A fake request object which is explicitly defined at the module level so it is able to be pickled. It simply
takes what is passed to it as kwargs on init and sets them as instance variables.
"""
def __init__(self, *args, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def copy_safe_request(request):
"""
Copy selected attributes from a request object into a new fake request object. This is needed in places where
thread safe pickling of the useful request data is needed.
"""
meta = {
k: request.META[k]
for k in HTTP_REQUEST_META_SAFE_COPY
if k in request.META and isinstance(request.META[k], str)
}
return NetBoxFakeRequest(**{
'META': meta,
'POST': request.POST,
'GET': request.GET,
'FILES': request.FILES,
'user': request.user,
'path': request.path
})

View File

@@ -39,6 +39,40 @@ from .paginator import EnhancedPaginator, get_paginate_count
# Mixins
#
class ContentTypePermissionRequiredMixin(AccessMixin):
"""
Similar to Django's built-in PermissionRequiredMixin, but extended to check model-level permission assignments.
This is related to ObjectPermissionRequiredMixin, except that is does not enforce object-level permissions,
and fits within NetBox's custom permission enforcement system.
additional_permissions: An optional iterable of statically declared permissions to evaluate in addition to those
derived from the object type
"""
additional_permissions = list()
def get_required_permission(self):
"""
Return the specific permission necessary to perform the requested action on an object.
"""
raise NotImplementedError(f"{self.__class__.__name__} must implement get_required_permission()")
def has_permission(self):
user = self.request.user
permission_required = self.get_required_permission()
# Check that the user has been granted the required permission(s).
if user.has_perms((permission_required, *self.additional_permissions)):
return True
return False
def dispatch(self, request, *args, **kwargs):
if not self.has_permission():
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
class ObjectPermissionRequiredMixin(AccessMixin):
"""
Similar to Django's built-in PermissionRequiredMixin, but extended to check for both model-level and object-level