From f60312febf06b39a5ee56f55491ee1ee145e4657 Mon Sep 17 00:00:00 2001 From: Patrick Rauscher Date: Thu, 20 Oct 2022 08:51:04 +0200 Subject: [PATCH 01/16] Set *_COOKIE_PATH according to BASE_PATH As discussed in #10639, all three `COOKIE_PATH`s should be set accordingly with the netbox-`BASE_PATH` to improve coexistance with other Django-projects probably hosted on the same Host --- netbox/netbox/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 0c3b1660b..14b66b2dd 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -85,6 +85,7 @@ CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False) CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', []) CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', []) CSRF_COOKIE_NAME = getattr(configuration, 'CSRF_COOKIE_NAME', 'csrftoken') +CSRF_COOKIE_PATH = BASE_PATH or '/' CSRF_TRUSTED_ORIGINS = getattr(configuration, 'CSRF_TRUSTED_ORIGINS', []) DATE_FORMAT = getattr(configuration, 'DATE_FORMAT', 'N j, Y') DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a') @@ -129,6 +130,8 @@ SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {}) SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None) SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid') +SESSION_COOKIE_PATH = BASE_PATH or '/' +LANGUAGE_COOKIE_PATH = BASE_PATH or '/' SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d') SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i') SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s') From 53c9c3cf8dbe96f6e8b6f40a14c71a52cb2247f6 Mon Sep 17 00:00:00 2001 From: Craig Pund Date: Thu, 20 Oct 2022 16:26:26 -0400 Subject: [PATCH 02/16] Fixes #10580 (#10687) * change IP address accessor to parent object * set IP assigned check to link to interface * Fix Assigned not being orderable Co-authored-by: Jeremy Stretch Co-authored-by: Craig Pund Co-authored-by: Jeremy Stretch --- netbox/ipam/tables/ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/tables/ip.py b/netbox/ipam/tables/ip.py index a820385ed..44f40b8a1 100644 --- a/netbox/ipam/tables/ip.py +++ b/netbox/ipam/tables/ip.py @@ -375,7 +375,7 @@ class IPAddressTable(TenancyColumnsMixin, NetBoxTable): ) assigned = columns.BooleanColumn( accessor='assigned_object_id', - linkify=True, + linkify=lambda record: record.assigned_object.get_absolute_url(), verbose_name='Assigned' ) tags = columns.TagColumn( From e7659a5f99801966124df765c799c8f26957f9d6 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 20 Oct 2022 13:27:51 -0700 Subject: [PATCH 03/16] 9584 add device type (slug) to filter list (#10630) * 9584 add device type (slug) to filter list * 9584 add test --- netbox/dcim/filtersets.py | 6 ++++++ netbox/dcim/tests/test_filtersets.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index 0a4439173..a999383c7 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -800,6 +800,12 @@ class DeviceFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilter to_field_name='slug', label='Manufacturer (slug)', ) + device_type = django_filters.ModelMultipleChoiceFilter( + field_name='device_type__slug', + queryset=DeviceType.objects.all(), + to_field_name='slug', + label='Device type (slug)', + ) device_type_id = django_filters.ModelMultipleChoiceFilter( queryset=DeviceType.objects.all(), label='Device type (ID)', diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py index feef4e90c..05bb64796 100644 --- a/netbox/dcim/tests/test_filtersets.py +++ b/netbox/dcim/tests/test_filtersets.py @@ -1643,6 +1643,8 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests): device_types = DeviceType.objects.all()[:2] params = {'device_type_id': [device_types[0].pk, device_types[1].pk]} self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) + params = {'device_type': [device_types[0].slug, device_types[1].slug]} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) def test_devicerole(self): device_roles = DeviceRole.objects.all()[:2] From 96c469641796b2b12ce643363d41d823670b68db Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 20 Oct 2022 16:31:52 -0400 Subject: [PATCH 04/16] Changelog for #9584, #10580, #10639 --- docs/release-notes/version-3.3.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index de0f1a40a..5f3d50098 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -4,7 +4,10 @@ ### Enhancements +* [#9584](https://github.com/netbox-community/netbox/issues/9584) - Enable filtering devices by device type slug * [#9722](https://github.com/netbox-community/netbox/issues/9722) - Add LDAP configuration parameters to specify certificates +* [#10580](https://github.com/netbox-community/netbox/issues/10580) - Link "assigned" checkbox in IP address table to assigned interface +* [#10639](https://github.com/netbox-community/netbox/issues/10639) - Set cookie paths according to configured `BASE_PATH` * [#10685](https://github.com/netbox-community/netbox/issues/10685) - Position A/Z termination cards above the fold under circuit view ### Bug Fixes From 3d687a6c2d71807cc25ad5d78f29b99d42243107 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 Oct 2022 12:39:03 -0400 Subject: [PATCH 05/16] Closes #10718: Optimize object-based permissions enforcement --- netbox/netbox/api/viewsets/mixins.py | 5 ++--- netbox/netbox/views/generic/object_views.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/netbox/netbox/api/viewsets/mixins.py b/netbox/netbox/api/viewsets/mixins.py index 7dc1111f3..b47c88a4e 100644 --- a/netbox/netbox/api/viewsets/mixins.py +++ b/netbox/netbox/api/viewsets/mixins.py @@ -108,6 +108,5 @@ class ObjectValidationMixin: conforming_count = self.queryset.filter(pk__in=[obj.pk for obj in instance]).count() if conforming_count != len(instance): raise ObjectDoesNotExist - else: - # Check that the instance is matched by the view's queryset - self.queryset.get(pk=instance.pk) + elif not self.queryset.filter(pk=instance.pk).exists(): + raise ObjectDoesNotExist diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index a56a832b6..3b0c77251 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -173,7 +173,7 @@ class ObjectImportView(GetReturnURLMixin, BaseObjectView): obj = model_form.save() # Enforce object-level permissions - if not self.queryset.filter(pk=obj.pk).first(): + if not self.queryset.filter(pk=obj.pk).exists(): raise PermissionsViolation() # Iterate through the related object forms (if any), validating and saving each instance. @@ -390,7 +390,7 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView): obj = form.save() # Check that the new object conforms with any assigned object-level permissions - if not self.queryset.filter(pk=obj.pk).first(): + if not self.queryset.filter(pk=obj.pk).exists(): raise PermissionsViolation() msg = '{} {}'.format( From 4c504870e029058d8e1cfeaaea84ac31b93a55e9 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Fri, 21 Oct 2022 12:47:19 -0400 Subject: [PATCH 06/16] Tweak PR template language --- .github/PULL_REQUEST_TEMPLATE.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 33134cb45..0bbbe90c7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,14 @@ ### Fixes: #1234 From 01654765e853e68290f1c2f22871657b0658ae87 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 Oct 2022 16:38:32 -0400 Subject: [PATCH 07/16] Fixes #10746: Add missing status attribute to cluster view --- docs/release-notes/version-3.3.md | 1 + netbox/templates/virtualization/cluster.html | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 5f3d50098..b05fa9074 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -19,6 +19,7 @@ * [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable * [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables * [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+ +* [#10746](https://github.com/netbox-community/netbox/issues/10746) - Add missing status attribute to cluster view --- diff --git a/netbox/templates/virtualization/cluster.html b/netbox/templates/virtualization/cluster.html index bf7c8a69a..bc02424cc 100644 --- a/netbox/templates/virtualization/cluster.html +++ b/netbox/templates/virtualization/cluster.html @@ -19,6 +19,10 @@ Type {{ object.type|linkify }} + + Status + {% badge object.get_status_display bg_color=object.get_status_color %} + Group {{ object.group|linkify|placeholder }} From eb91934d701cfc295969158fd8217d5c534a474f Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 25 Oct 2022 16:41:07 -0400 Subject: [PATCH 08/16] Fixes #10745: Correct display of status field in clusters list --- docs/release-notes/version-3.3.md | 1 + netbox/virtualization/tables/clusters.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index b05fa9074..e66ee1a28 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -19,6 +19,7 @@ * [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable * [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables * [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+ +* [#10745](https://github.com/netbox-community/netbox/issues/10745) - Correct display of status field in clusters list * [#10746](https://github.com/netbox-community/netbox/issues/10746) - Add missing status attribute to cluster view --- diff --git a/netbox/virtualization/tables/clusters.py b/netbox/virtualization/tables/clusters.py index fec539b8c..ae4c610d7 100644 --- a/netbox/virtualization/tables/clusters.py +++ b/netbox/virtualization/tables/clusters.py @@ -64,6 +64,7 @@ class ClusterTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): group = tables.Column( linkify=True ) + status = columns.ChoiceFieldColumn() site = tables.Column( linkify=True ) From 8d486c5838e0c882640e9a747cc1965e60e4f9e5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 26 Oct 2022 05:05:15 -0700 Subject: [PATCH 09/16] 10716 add left-right plugins to tags page (#10744) * 10716 add left-right plugins to tags page * 10716 add back plugin_full_width --- netbox/templates/extras/tag.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netbox/templates/extras/tag.html b/netbox/templates/extras/tag.html index b0b88b5af..6e4c5aee9 100644 --- a/netbox/templates/extras/tag.html +++ b/netbox/templates/extras/tag.html @@ -39,6 +39,7 @@ + {% plugin_left_page object %}
@@ -64,6 +65,7 @@
+ {% plugin_right_page object %}
From d8c07abd6817771b4f5491df19550d9f58261165 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 25 Oct 2022 09:07:39 -0700 Subject: [PATCH 10/16] 10610 interface_id query on lag return vc interfaces --- netbox/dcim/filtersets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index a999383c7..917f57923 100644 --- a/netbox/dcim/filtersets.py +++ b/netbox/dcim/filtersets.py @@ -1363,7 +1363,7 @@ class InterfaceFilterSet( try: devices = Device.objects.filter(pk__in=id_list) for device in devices: - vc_interface_ids += device.vc_interfaces().values_list('id', flat=True) + vc_interface_ids += device.vc_interfaces(if_master=False).values_list('id', flat=True) return queryset.filter(pk__in=vc_interface_ids) except Device.DoesNotExist: return queryset.none() From 2a62b628cf8c3648468e14bf67ec5a9b24278704 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 08:23:50 -0400 Subject: [PATCH 11/16] Fixes #10723: Distinguish between inside/outside NAT assignments for device/VM primary IPs --- docs/release-notes/version-3.3.md | 3 +++ netbox/templates/dcim/device.html | 4 ++-- netbox/templates/virtualization/virtualmachine.html | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index e66ee1a28..fe37cce3e 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -15,10 +15,13 @@ * [#9669](https://github.com/netbox-community/netbox/issues/9669) - Strip colons from usernames when using remote authentication * [#10575](https://github.com/netbox-community/netbox/issues/10575) - Include OIDC dependencies for python-social-auth * [#10584](https://github.com/netbox-community/netbox/issues/10584) - Fix service clone link +* [#10610](https://github.com/netbox-community/netbox/issues/10610) - Allow assignment of VC member to LAG on non-master peer * [#10643](https://github.com/netbox-community/netbox/issues/10643) - Ensure consistent display of custom fields for all model forms * [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable * [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables * [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+ +* [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view +* [#10723](https://github.com/netbox-community/netbox/issues/10723) - Distinguish between inside/outside NAT assignments for device/VM primary IPs * [#10745](https://github.com/netbox-community/netbox/issues/10745) - Correct display of status field in clusters list * [#10746](https://github.com/netbox-community/netbox/issues/10746) - Add missing status attribute to cluster view diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index d800658a5..b0cd76de4 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -178,7 +178,7 @@ {% if object.primary_ip4.nat_inside %} (NAT for {{ object.primary_ip4.nat_inside.address.ip }}) {% elif object.primary_ip4.nat_outside.exists %} - (NAT for {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) + (NAT: {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %} {% else %} {{ ''|placeholder }} @@ -193,7 +193,7 @@ {% if object.primary_ip6.nat_inside %} (NAT for {{ object.primary_ip6.nat_inside.address.ip }}) {% elif object.primary_ip6.nat_outside.exists %} - (NAT for {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) + (NAT: {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %} {% else %} {{ ''|placeholder }} diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index 5756d939a..c0e2ebd07 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -46,7 +46,7 @@ {% if object.primary_ip4.nat_inside %} (NAT for {{ object.primary_ip4.nat_inside.address.ip }}) {% elif object.primary_ip4.nat_outside.exists %} - (NAT for {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) + (NAT: {% for nat in object.primary_ip4.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %} {% else %} {{ ''|placeholder }} @@ -61,7 +61,7 @@ {% if object.primary_ip6.nat_inside %} (NAT for {{ object.primary_ip6.nat_inside.address.ip }}) {% elif object.primary_ip6.nat_outside.exists %} - (NAT for {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) + (NAT: {% for nat in object.primary_ip6.nat_outside.all %}{{ nat.address.ip }}{% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %} {% else %} {{ ''|placeholder }} From 7b3ef2ade5266ec0738c4e210cf1c6f9b85fbee7 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 08:44:20 -0400 Subject: [PATCH 12/16] Fixes #10719: Prevent user without sufficient permission from creating an IP address via FHRP group creation --- docs/release-notes/version-3.3.md | 1 + netbox/ipam/forms/models.py | 3 ++- netbox/ipam/views.py | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index fe37cce3e..0015dc2df 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -21,6 +21,7 @@ * [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables * [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+ * [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view +* [#10719](https://github.com/netbox-community/netbox/issues/10719) - Prevent user without sufficient permission from creating an IP address via FHRP group creation * [#10723](https://github.com/netbox-community/netbox/issues/10723) - Distinguish between inside/outside NAT assignments for device/VM primary IPs * [#10745](https://github.com/netbox-community/netbox/issues/10745) - Correct display of status field in clusters list * [#10746](https://github.com/netbox-community/netbox/issues/10746) - Add missing status attribute to cluster view diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index f66b7efba..1986b1590 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -552,6 +552,7 @@ class FHRPGroupForm(NetBoxModelForm): def save(self, *args, **kwargs): instance = super().save(*args, **kwargs) + user = getattr(instance, '_user', None) # Set under FHRPGroupEditView.alter_object() # Check if we need to create a new IPAddress for the group if self.cleaned_data.get('ip_address'): @@ -565,7 +566,7 @@ class FHRPGroupForm(NetBoxModelForm): ipaddress.save() # Check that the new IPAddress conforms with any assigned object-level permissions - if not IPAddress.objects.filter(pk=ipaddress.pk).first(): + if not IPAddress.objects.restrict(user, 'add').filter(pk=ipaddress.pk).first(): raise PermissionsViolation() return instance diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 04d07e356..72483d40f 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -930,6 +930,12 @@ class FHRPGroupEditView(generic.ObjectEditView): return return_url + def alter_object(self, obj, request, url_args, url_kwargs): + # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that + # we can evaluate permissions during the creation of a new IPAddress within the form. + obj._user = request.user + return obj + class FHRPGroupDeleteView(generic.ObjectDeleteView): queryset = FHRPGroup.objects.all() From 658c9347f39c7376866f02c917df8dcb0224e079 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 09:32:29 -0400 Subject: [PATCH 13/16] Fixes #10682: Correct home view links to connection lists --- docs/release-notes/version-3.3.md | 1 + netbox/netbox/views/__init__.py | 114 ++++++++++++++---------------- netbox/templates/home.html | 4 +- 3 files changed, 56 insertions(+), 63 deletions(-) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 0015dc2df..1e2c4a90e 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -19,6 +19,7 @@ * [#10643](https://github.com/netbox-community/netbox/issues/10643) - Ensure consistent display of custom fields for all model forms * [#10646](https://github.com/netbox-community/netbox/issues/10646) - Fix filtering of power feed by power panel when connecting a cable * [#10655](https://github.com/netbox-community/netbox/issues/10655) - Correct display of assigned contacts in object tables +* [#10682](https://github.com/netbox-community/netbox/issues/10682) - Correct home view links to connection lists * [#10712](https://github.com/netbox-community/netbox/issues/10712) - Fix ModuleNotFoundError exception when generating API schema under Python 3.9+ * [#10716](https://github.com/netbox-community/netbox/issues/10716) - Add left/right page plugin content embeds for tag view * [#10719](https://github.com/netbox-community/netbox/issues/10719) - Prevent user without sufficient permission from creating an IP address via FHRP group creation diff --git a/netbox/netbox/views/__init__.py b/netbox/netbox/views/__init__.py index bc1f0e2ca..18b64344f 100644 --- a/netbox/netbox/views/__init__.py +++ b/netbox/netbox/views/__init__.py @@ -1,5 +1,6 @@ import platform import sys +from collections import namedtuple from django.conf import settings from django.core.cache import cache @@ -8,6 +9,7 @@ from django.shortcuts import redirect, render from django.template import loader from django.template.exceptions import TemplateDoesNotExist from django.urls import reverse +from django.utils.translation import gettext as _ from django.views.decorators.csrf import requires_csrf_token from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found from django.views.generic import View @@ -24,100 +26,90 @@ from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF from netbox.constants import SEARCH_MAX_RESULTS from netbox.forms import SearchForm from netbox.search import SEARCH_TYPES -from tenancy.models import Tenant +from tenancy.models import Contact, Tenant from virtualization.models import Cluster, VirtualMachine from wireless.models import WirelessLAN, WirelessLink +Link = namedtuple('Link', ('label', 'viewname', 'permission', 'count')) + + class HomeView(View): template_name = 'home.html' def get(self, request): if settings.LOGIN_REQUIRED and not request.user.is_authenticated: - return redirect("login") + return redirect('login') - connected_consoleports = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter( + console_connections = ConsolePort.objects.restrict(request.user, 'view').prefetch_related('_path').filter( _path__is_complete=True - ) - connected_powerports = PowerPort.objects.restrict(request.user, 'view').prefetch_related('_path').filter( + ).count + power_connections = PowerPort.objects.restrict(request.user, 'view').prefetch_related('_path').filter( _path__is_complete=True - ) - connected_interfaces = Interface.objects.restrict(request.user, 'view').prefetch_related('_path').filter( + ).count + interface_connections = Interface.objects.restrict(request.user, 'view').prefetch_related('_path').filter( _path__is_complete=True - ) + ).count + + def get_count_queryset(model): + return model.objects.restrict(request.user, 'view').count def build_stats(): org = ( - ("dcim.view_site", "Sites", Site.objects.restrict(request.user, 'view').count), - ("tenancy.view_tenant", "Tenants", Tenant.objects.restrict(request.user, 'view').count), + Link(_('Sites'), 'dcim:site_list', 'dcim.view_site', get_count_queryset(Site)), + Link(_('Tenants'), 'tenancy:tenant_list', 'tenancy.view_tenant', get_count_queryset(Tenant)), + Link(_('Contacts'), 'tenancy:contact_list', 'tenancy.view_contact', get_count_queryset(Contact)), ) dcim = ( - ("dcim.view_rack", "Racks", Rack.objects.restrict(request.user, 'view').count), - ("dcim.view_devicetype", "Device Types", DeviceType.objects.restrict(request.user, 'view').count), - ("dcim.view_device", "Devices", Device.objects.restrict(request.user, 'view').count), + Link(_('Racks'), 'dcim:rack_list', 'dcim.view_rack', get_count_queryset(Rack)), + Link(_('Device Types'), 'dcim:devicetype_list', 'dcim.view_devicetype', get_count_queryset(DeviceType)), + Link(_('Devices'), 'dcim:device_list', 'dcim.view_device', get_count_queryset(Device)), ) ipam = ( - ("ipam.view_vrf", "VRFs", VRF.objects.restrict(request.user, 'view').count), - ("ipam.view_aggregate", "Aggregates", Aggregate.objects.restrict(request.user, 'view').count), - ("ipam.view_prefix", "Prefixes", Prefix.objects.restrict(request.user, 'view').count), - ("ipam.view_iprange", "IP Ranges", IPRange.objects.restrict(request.user, 'view').count), - ("ipam.view_ipaddress", "IP Addresses", IPAddress.objects.restrict(request.user, 'view').count), - ("ipam.view_vlan", "VLANs", VLAN.objects.restrict(request.user, 'view').count) - + Link(_('VRFs'), 'ipam:vrf_list', 'ipam.view_vrf', get_count_queryset(VRF)), + Link(_('Aggregates'), 'ipam:aggregate_list', 'ipam.view_aggregate', get_count_queryset(Aggregate)), + Link(_('Prefixes'), 'ipam:prefix_list', 'ipam.view_prefix', get_count_queryset(Prefix)), + Link(_('IP Ranges'), 'ipam:iprange_list', 'ipam.view_iprange', get_count_queryset(IPRange)), + Link(_('IP Addresses'), 'ipam:ipaddress_list', 'ipam.view_ipaddress', get_count_queryset(IPAddress)), + Link(_('VLANs'), 'ipam:vlan_list', 'ipam.view_vlan', get_count_queryset(VLAN)), ) circuits = ( - ("circuits.view_provider", "Providers", Provider.objects.restrict(request.user, 'view').count), - ("circuits.view_circuit", "Circuits", Circuit.objects.restrict(request.user, 'view').count), + Link(_('Providers'), 'circuits:provider_list', 'circuits.view_provider', get_count_queryset(Provider)), + Link(_('Circuits'), 'circuits:circuit_list', 'circuits.view_circuit', get_count_queryset(Circuit)) ) virtualization = ( - ("virtualization.view_cluster", "Clusters", Cluster.objects.restrict(request.user, 'view').count), - ("virtualization.view_virtualmachine", "Virtual Machines", VirtualMachine.objects.restrict(request.user, 'view').count), - + Link(_('Clusters'), 'virtualization:cluster_list', 'virtualization.view_cluster', + get_count_queryset(Cluster)), + Link(_('Virtual Machines'), 'virtualization:virtualmachine_list', 'virtualization.view_virtualmachine', + get_count_queryset(VirtualMachine)), ) connections = ( - ("dcim.view_cable", "Cables", Cable.objects.restrict(request.user, 'view').count), - ("dcim.view_consoleport", "Console", connected_consoleports.count), - ("dcim.view_interface", "Interfaces", connected_interfaces.count), - ("dcim.view_powerport", "Power Connections", connected_powerports.count), + Link(_('Cables'), 'dcim:cable_list', 'dcim.view_cable', get_count_queryset(Cable)), + Link(_('Interfaces'), 'dcim:interface_connections_list', 'dcim.view_interface', interface_connections), + Link(_('Console'), 'dcim:console_connections_list', 'dcim.view_consoleport', console_connections), + Link(_('Power'), 'dcim:power_connections_list', 'dcim.view_powerport', power_connections), ) power = ( - ("dcim.view_powerpanel", "Power Panels", PowerPanel.objects.restrict(request.user, 'view').count), - ("dcim.view_powerfeed", "Power Feeds", PowerFeed.objects.restrict(request.user, 'view').count), + Link(_('Power Panels'), 'dcim:powerpanel_list', 'dcim.view_powerpanel', get_count_queryset(PowerPanel)), + Link(_('Power Feeds'), 'dcim:powerfeed_list', 'dcim.view_powerfeed', get_count_queryset(PowerFeed)), ) wireless = ( - ("wireless.view_wirelesslan", "Wireless LANs", WirelessLAN.objects.restrict(request.user, 'view').count), - ("wireless.view_wirelesslink", "Wireless Links", WirelessLink.objects.restrict(request.user, 'view').count), + Link(_('Wireless LANs'), 'wireless:wirelesslan_list', 'wireless.view_wirelesslan', + get_count_queryset(WirelessLAN)), + Link(_('Wireless Links'), 'wireless:wirelesslink_list', 'wireless.view_wirelesslink', + get_count_queryset(WirelessLink)), ) - sections = ( - ("Organization", org, "domain"), - ("IPAM", ipam, "counter"), - ("Virtualization", virtualization, "monitor"), - ("Inventory", dcim, "server"), - ("Circuits", circuits, "transit-connection-variant"), - ("Connections", connections, "cable-data"), - ("Power", power, "flash"), - ("Wireless", wireless, "wifi"), + stats = ( + (_('Organization'), org, 'domain'), + (_('IPAM'), ipam, 'counter'), + (_('Virtualization'), virtualization, 'monitor'), + (_('Inventory'), dcim, 'server'), + (_('Circuits'), circuits, 'transit-connection-variant'), + (_('Connections'), connections, 'cable-data'), + (_('Power'), power, 'flash'), + (_('Wireless'), wireless, 'wifi'), ) - stats = [] - for section_label, section_items, icon_class in sections: - items = [] - for perm, item_label, get_count in section_items: - app, scope = perm.split(".") - url = ":".join((app, scope.replace("view_", "") + "_list")) - item = { - "label": item_label, - "count": None, - "url": url, - "disabled": True, - "icon": icon_class, - } - if request.user.has_perm(perm): - item["count"] = get_count() - item["disabled"] = False - items.append(item) - stats.append((section_label, items, icon_class)) - return stats # Compile changelog table diff --git a/netbox/templates/home.html b/netbox/templates/home.html index a12ec9277..f98d0ccf3 100644 --- a/netbox/templates/home.html +++ b/netbox/templates/home.html @@ -36,8 +36,8 @@
{% for item in items %} - {% if not item.disabled %} - + {% if item.permission in perms %} +
{{ item.label }}

{{ item.count }}

From 174ba6cf0f9d2c04f5d7ed533654f9c30bd3f75c Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Fri, 14 Oct 2022 17:00:20 +0200 Subject: [PATCH 14/16] Fix LDAP auth: user never updated if inactive --- netbox/netbox/api/authentication.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/netbox/netbox/api/authentication.py b/netbox/netbox/api/authentication.py index b8607a0bb..814ca1ed6 100644 --- a/netbox/netbox/api/authentication.py +++ b/netbox/netbox/api/authentication.py @@ -58,22 +58,24 @@ class TokenAuthentication(authentication.TokenAuthentication): if token.is_expired: raise exceptions.AuthenticationFailed("Token expired") - if not token.user.is_active: - raise exceptions.AuthenticationFailed("User inactive") - + user = token.user # When LDAP authentication is active try to load user data from LDAP directory if settings.REMOTE_AUTH_BACKEND == 'netbox.authentication.LDAPBackend': from netbox.authentication import LDAPBackend ldap_backend = LDAPBackend() # Load from LDAP if FIND_GROUP_PERMS is active - if ldap_backend.settings.FIND_GROUP_PERMS: - user = ldap_backend.populate_user(token.user.username) + # Always query LDAP when user is not active, otherwise it is never activated again + if ldap_backend.settings.FIND_GROUP_PERMS or not token.user.is_active: + ldap_user = ldap_backend.populate_user(token.user.username) # If the user is found in the LDAP directory use it, if not fallback to the local user - if user: - return user, token + if ldap_user: + user = ldap_user - return token.user, token + if not user.is_active: + raise exceptions.AuthenticationFailed("User inactive") + + return user, token class TokenPermissions(DjangoObjectPermissions): From eac2ace80b2ee333700c59dc1493cde42eb2e1b6 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 09:58:31 -0400 Subject: [PATCH 15/16] Release v3.3.6 --- .github/ISSUE_TEMPLATE/bug_report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yaml | 2 +- docs/release-notes/version-3.3.md | 2 +- netbox/netbox/settings.py | 2 +- requirements.txt | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 907ad6cf7..56c14e966 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.3.5 + placeholder: v3.3.6 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 3cd9bc4ee..bef1ce587 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.3.5 + placeholder: v3.3.6 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 1e2c4a90e..ffb831e9d 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -1,6 +1,6 @@ # NetBox v3.3 -## v3.3.6 (FUTURE) +## v3.3.6 (2022-10-26) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 14b66b2dd..cb26652b9 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -29,7 +29,7 @@ django.utils.encoding.force_text = force_str # Environment setup # -VERSION = '3.3.6-dev' +VERSION = '3.3.6' # Hostname HOSTNAME = platform.node() diff --git a/requirements.txt b/requirements.txt index 9afcaea03..bce015110 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,18 +19,18 @@ graphene-django==2.15.0 gunicorn==20.1.0 Jinja2==3.1.2 Markdown==3.3.7 -mkdocs-material==8.5.6 +mkdocs-material==8.5.7 mkdocstrings[python-legacy]==0.19.0 netaddr==0.8.0 Pillow==9.2.0 -psycopg2-binary==2.9.3 +psycopg2-binary==2.9.5 PyYAML==6.0 -sentry-sdk==1.9.10 +sentry-sdk==1.10.1 social-auth-app-django==5.0.0 social-auth-core[openidconnect]==4.3.0 svgwrite==1.4.3 tablib==3.2.1 -tzdata==2022.4 +tzdata==2022.5 # Workaround for #7401 jsonschema==3.2.0 From 18332bdbf10d8d79d2110a6a18cc80efbb8dc67d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Oct 2022 10:23:50 -0400 Subject: [PATCH 16/16] PRVB --- docs/release-notes/version-3.3.md | 4 ++++ netbox/netbox/settings.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index ffb831e9d..8b8bd0060 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -1,5 +1,9 @@ # NetBox v3.3 +## v3.3.7 (FUTURE) + +--- + ## v3.3.6 (2022-10-26) ### Enhancements diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index cb26652b9..02e80b6cd 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -29,7 +29,7 @@ django.utils.encoding.force_text = force_str # Environment setup # -VERSION = '3.3.6' +VERSION = '3.3.7-dev' # Hostname HOSTNAME = platform.node()