Просмотр исходного кода

Fixes #4921: Render non-viewable devices as unavailable space in rack elevations

Jeremy Stretch 5 лет назад
Родитель
Сommit
73f9d615bc

+ 1 - 0
docs/release-notes/version-2.9.md

@@ -7,6 +7,7 @@
 * [#4905](https://github.com/netbox-community/netbox/issues/4905) - Fix front port count on device type view
 * [#4912](https://github.com/netbox-community/netbox/issues/4912) - Fix image attachment API endpoint
 * [#4914](https://github.com/netbox-community/netbox/issues/4914) - Fix toggling cable status under device view
+* [#4921](https://github.com/netbox-community/netbox/issues/4921) - Render non-viewable devices as unavailable space in rack elevations
 
 ---
 

+ 1 - 0
netbox/dcim/api/views.py

@@ -176,6 +176,7 @@ class RackViewSet(CustomFieldModelViewSet):
             # Render and return the elevation as an SVG drawing with the correct content type
             drawing = rack.get_elevation_svg(
                 face=data['face'],
+                user=request.user,
                 unit_width=data['unit_width'],
                 unit_height=data['unit_height'],
                 legend_width=data['legend_width'],

+ 15 - 4
netbox/dcim/elevations.py

@@ -14,10 +14,11 @@ class RackElevationSVG:
     Use this class to render a rack elevation as an SVG image.
 
     :param rack: A NetBox Rack instance
+    :param user: User instance. If specified, only devices viewable by this user will be fully displayed.
     :param include_images: If true, the SVG document will embed front/rear device face images, where available
     :param base_url: Base URL for links within the SVG document. If none, links will be relative.
     """
-    def __init__(self, rack, include_images=True, base_url=None):
+    def __init__(self, rack, user=None, include_images=True, base_url=None):
         self.rack = rack
         self.include_images = include_images
         if base_url is not None:
@@ -25,7 +26,14 @@ class RackElevationSVG:
         else:
             self.base_url = ''
 
-    def _get_device_description(self, device):
+        # Determine the subset of devices within this rack that are viewable by the user, if any
+        permitted_devices = self.rack.devices
+        if user is not None:
+            permitted_devices = permitted_devices.restrict(user, 'view')
+        self.permitted_device_ids = permitted_devices.values_list('pk', flat=True)
+
+    @staticmethod
+    def _get_device_description(device):
         return '{} ({}) — {} ({}U) {} {}'.format(
             device.name,
             device.device_role,
@@ -174,10 +182,13 @@ class RackElevationSVG:
             text_cordinates = (x_offset + (unit_width / 2), y_offset + end_y / 2)
 
             # Draw the device
-            if device and device.face == face:
+            if device and device.face == face and device.pk in self.permitted_device_ids:
                 self._draw_device_front(drawing, device, start_cordinates, end_cordinates, text_cordinates)
-            elif device and device.device_type.is_full_depth:
+            elif device and device.device_type.is_full_depth and device.pk in self.permitted_device_ids:
                 self._draw_device_rear(drawing, device, start_cordinates, end_cordinates, text_cordinates)
+            elif device:
+                # Devices which the user does not have permission to view are rendered only as unavailable space
+                drawing.add(drawing.rect(start_cordinates, end_cordinates, class_='blocked'))
             else:
                 # Draw shallow devices, reservations, or empty units
                 class_ = 'slot'

+ 4 - 1
netbox/dcim/models/__init__.py

@@ -750,6 +750,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
     def get_elevation_svg(
             self,
             face=DeviceFaceChoices.FACE_FRONT,
+            user=None,
             unit_width=settings.RACK_ELEVATION_DEFAULT_UNIT_WIDTH,
             unit_height=settings.RACK_ELEVATION_DEFAULT_UNIT_HEIGHT,
             legend_width=RACK_ELEVATION_LEGEND_WIDTH_DEFAULT,
@@ -760,6 +761,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
         Return an SVG of the rack elevation
 
         :param face: Enum of [front, rear] representing the desired side of the rack elevation to render
+        :param user: User instance to be used for evaluating device view permissions. If None, all devices
+            will be included.
         :param unit_width: Width in pixels for the rendered drawing
         :param unit_height: Height of each rack unit for the rendered drawing. Note this is not the total
             height of the elevation
@@ -767,7 +770,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
         :param include_images: Embed front/rear device images where available
         :param base_url: Base URL for links and images. If none, URLs will be relative.
         """
-        elevation = RackElevationSVG(self, include_images=include_images, base_url=base_url)
+        elevation = RackElevationSVG(self, user=user, include_images=include_images, base_url=base_url)
 
         return elevation.render(face, unit_width, unit_height, legend_width)