Skip to content

Commit

Permalink
Merge pull request #16132 from netbox-community/develop
Browse files Browse the repository at this point in the history
Release v4.0.2
  • Loading branch information
jeremystretch committed May 14, 2024
2 parents a3f7dc0 + 70c0aec commit cca1b0a
Show file tree
Hide file tree
Showing 53 changed files with 2,253 additions and 2,111 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ body:
attributes:
label: NetBox Version
description: What version of NetBox are you currently running?
placeholder: v4.0.1
placeholder: v4.0.2
validations:
required: true
- type: dropdown
Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ body:
attributes:
label: NetBox version
description: What version of NetBox are you currently running?
placeholder: v4.0.1
placeholder: v4.0.2
validations:
required: true
- type: dropdown
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/close-stale-issues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
workflow_dispatch:

permissions:
actions: write
issues: write
pull-requests: write

Expand Down
2 changes: 1 addition & 1 deletion base_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ social-auth-app-django
strawberry-graphql

# Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/blob/main/CHANGELOG.md
# https://github.com/strawberry-graphql/strawberry-django/releases
strawberry-graphql-django

# SVG image rendering (used for rack elevations)
Expand Down
7 changes: 5 additions & 2 deletions docs/configuration/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,12 @@ Note that enabling this setting causes NetBox to update a user's session in the

## LOGIN_REQUIRED

Default: False
Default: True

When enabled, only authenticated users are permitted to access any part of NetBox. Disabling this will allow unauthenticated users to access most areas of NetBox (but not make any changes).

Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users are permitted to access most data in NetBox but not make any changes.
!!! info "Changed in NetBox v4.0.2"
Prior to NetBox v4.0.2, this setting was disabled by default.

---

Expand Down
8 changes: 8 additions & 0 deletions docs/configuration/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,11 @@ If `STORAGE_BACKEND` is not defined, this setting will be ignored.
Default: UTC

The time zone NetBox will use when dealing with dates and times. It is recommended to use UTC time unless you have a specific need to use a local time zone. Please see the [list of available time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).

---

## TRANSLATION_ENABLED

Default: True

Enables language translation for the user interface. (This parameter maps to Django's [USE_I18N](https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-USE_I18N) setting.)
23 changes: 23 additions & 0 deletions docs/release-notes/version-4.0.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# NetBox v4.0

## v4.0.2 (2024-05-14)

!!! warning "Important"
This release includes an important security fix, and is a strongly recommended update for all users. More details will follow.

### Enhancements

* [#15119](https://github.com/netbox-community/netbox/issues/15119) - Add cluster & cluster group UI filter fields for VLAN groups
* [#16090](https://github.com/netbox-community/netbox/issues/16090) - Include current NetBox version when an unsupported plugin is detected
* [#16096](https://github.com/netbox-community/netbox/issues/16096) - Introduce the `ENABLE_TRANSLATION` configuration parameter
* [#16107](https://github.com/netbox-community/netbox/issues/16107) - Change the default value for `LOGIN_REQUIRED` to True
* [#16127](https://github.com/netbox-community/netbox/issues/16127) - Add integration point for unsupported settings

### Bug Fixes

* [#16077](https://github.com/netbox-community/netbox/issues/16077) - Fix display of parameter values when viewing configuration revisions
* [#16078](https://github.com/netbox-community/netbox/issues/16078) - Fix integer filters mistakenly marked as required for GraphQL API
* [#16101](https://github.com/netbox-community/netbox/issues/16101) - Fix initial loading of pagination widget for dynamic object tables
* [#16123](https://github.com/netbox-community/netbox/issues/16123) - Fix custom script execution via REST API
* [#16124](https://github.com/netbox-community/netbox/issues/16124) - Fix GraphQL API support for querying virtual machine interfaces

---

## v4.0.1 (2024-05-09)

### Enhancements
Expand Down
2 changes: 0 additions & 2 deletions netbox/dcim/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2093,7 +2093,6 @@ class DeviceVirtualMachinesView(generic.ObjectChildrenView):
child_model = VirtualMachine
table = VirtualMachineTable
filterset = VirtualMachineFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('Virtual Machines'),
badge=lambda obj: VirtualMachine.objects.filter(cluster=obj.cluster, device=obj).count(),
Expand Down Expand Up @@ -2986,7 +2985,6 @@ class InventoryItemChildrenView(generic.ObjectChildrenView):
child_model = InventoryItem
table = tables.InventoryItemTable
filterset = filtersets.InventoryItemFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('Children'),
badge=lambda obj: obj.child_items.count(),
Expand Down
4 changes: 2 additions & 2 deletions netbox/extras/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ def post(self, request, pk):
raise RQWorkerNotRunningException()

if input_serializer.is_valid():
script.result = Job.enqueue(
Job.enqueue(
run_script,
instance=script.module,
instance=script,
name=script.python_class.class_name,
user=request.user,
data=input_serializer.data['data'],
Expand Down
6 changes: 3 additions & 3 deletions netbox/extras/tables/tables.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import json

import django_tables2 as tables
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from extras.models import *
from netbox.constants import EMPTY_TABLE_TEXT
from netbox.tables import BaseTable, NetBoxTable, columns
from .template_code import *

Expand Down Expand Up @@ -550,7 +550,7 @@ class ScriptResultsTable(BaseTable):
)

class Meta(BaseTable.Meta):
empty_text = _('No results found')
empty_text = _(EMPTY_TABLE_TEXT)
fields = (
'index', 'time', 'status', 'message',
)
Expand Down Expand Up @@ -581,7 +581,7 @@ class ReportResultsTable(BaseTable):
)

class Meta(BaseTable.Meta):
empty_text = _('No results found')
empty_text = _(EMPTY_TABLE_TEXT)
fields = (
'index', 'method', 'time', 'status', 'object', 'url', 'message',
)
14 changes: 13 additions & 1 deletion netbox/ipam/forms/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, add_blank_choice
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.rendering import FieldSet
from virtualization.models import VirtualMachine
from virtualization.models import VirtualMachine, ClusterGroup, Cluster
from vpn.models import L2VPN

__all__ = (
Expand Down Expand Up @@ -405,6 +405,7 @@ class VLANGroupFilterForm(NetBoxModelFilterSetForm):
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('region', 'sitegroup', 'site', 'location', 'rack', name=_('Location')),
FieldSet('cluster_group', 'cluster', name=_('Cluster')),
FieldSet('min_vid', 'max_vid', name=_('VLAN ID')),
)
model = VLANGroup
Expand Down Expand Up @@ -445,6 +446,17 @@ class VLANGroupFilterForm(NetBoxModelFilterSetForm):
max_value=VLAN_VID_MAX,
label=_('Maximum VID')
)
cluster = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
label=_('Cluster')
)
cluster_group = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(),
required=False,
label=_('Cluster group')
)

tag = TagFilterField(model)


Expand Down
3 changes: 3 additions & 0 deletions netbox/ipam/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,9 @@ class IPAddressTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = {
'description': 'New description',
}
graphql_filter = {
'address': '192.168.0.1/24',
}

@classmethod
def setUpTestData(cls):
Expand Down
5 changes: 0 additions & 5 deletions netbox/ipam/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ class ASNRangeASNsView(generic.ObjectChildrenView):
child_model = ASN
table = tables.ASNTable
filterset = filtersets.ASNFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('ASNs'),
badge=lambda x: x.get_child_asns().count(),
Expand Down Expand Up @@ -883,7 +882,6 @@ class IPAddressRelatedIPsView(generic.ObjectChildrenView):
child_model = IPAddress
table = tables.IPAddressTable
filterset = filtersets.IPAddressFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('Related IPs'),
badge=lambda x: x.get_related_ips().count(),
Expand Down Expand Up @@ -955,7 +953,6 @@ class VLANGroupVLANsView(generic.ObjectChildrenView):
child_model = VLAN
table = tables.VLANTable
filterset = filtersets.VLANFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('VLANs'),
badge=lambda x: x.get_child_vlans().count(),
Expand Down Expand Up @@ -1111,7 +1108,6 @@ class VLANInterfacesView(generic.ObjectChildrenView):
child_model = Interface
table = tables.VLANDevicesTable
filterset = InterfaceFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('Device Interfaces'),
badge=lambda x: x.get_interfaces().count(),
Expand All @@ -1129,7 +1125,6 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
child_model = VMInterface
table = tables.VLANVirtualMachinesTable
filterset = VMInterfaceFilterSet
template_name = 'generic/object_children.html'
tab = ViewTab(
label=_('VM Interfaces'),
badge=lambda x: x.get_vminterfaces().count(),
Expand Down
5 changes: 2 additions & 3 deletions netbox/netbox/configuration_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,8 @@
# authenticated to NetBox indefinitely.
LOGIN_PERSISTENCE = False

# Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users
# are permitted to access most data in NetBox but not make any changes.
LOGIN_REQUIRED = False
# Setting this to False will permit unauthenticated users to access most areas of NetBox (but not make any changes).
LOGIN_REQUIRED = True

# The length of time (in seconds) for which a user will remain logged into the web UI before being prompted to
# re-authenticate. (Default: 1209600 [14 days])
Expand Down
3 changes: 3 additions & 0 deletions netbox/netbox/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@
# General-purpose tokens
CENSOR_TOKEN = '********'
CENSOR_TOKEN_CHANGED = '***CHANGED***'

# Placeholder text for empty tables
EMPTY_TABLE_TEXT = 'No results found'
2 changes: 1 addition & 1 deletion netbox/netbox/graphql/filter_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def map_strawberry_type(field):
pass
elif issubclass(type(field), django_filters.NumberFilter):
should_create_function = True
attr_type = int
attr_type = int | None
elif issubclass(type(field), django_filters.ModelMultipleChoiceFilter):
should_create_function = True
attr_type = List[str] | None
Expand Down
6 changes: 4 additions & 2 deletions netbox/netbox/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,15 @@ def validate(cls, user_config, netbox_version):
min_version = version.parse(cls.min_version)
if current_version < min_version:
raise ImproperlyConfigured(
f"Plugin {cls.__module__} requires NetBox minimum version {cls.min_version}."
f"Plugin {cls.__module__} requires NetBox minimum version {cls.min_version} (current: "
f"{netbox_version})."
)
if cls.max_version is not None:
max_version = version.parse(cls.max_version)
if current_version > max_version:
raise ImproperlyConfigured(
f"Plugin {cls.__module__} requires NetBox maximum version {cls.max_version}."
f"Plugin {cls.__module__} requires NetBox maximum version {cls.max_version} (current: "
f"{netbox_version})."
)

# Verify required configuration settings
Expand Down
9 changes: 7 additions & 2 deletions netbox/netbox/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ def get_page_lengths():
),
description=_('Enable dynamic UI navigation'),
default=False,
experimental=True
warning=_('Experimental feature')
),
'locale.language': UserPreference(
label=_('Language'),
choices=(
('', _('Auto')),
*settings.LANGUAGES,
),
description=_('Forces UI translation to the specified language.')
description=_('Forces UI translation to the specified language'),
warning=(
_("Support for translation has been disabled locally")
if not settings.TRANSLATION_ENABLED
else ''
)
),
'pagination.per_page': UserPreference(
label=_('Page length'),
Expand Down
15 changes: 13 additions & 2 deletions netbox/netbox/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# Environment setup
#

VERSION = '4.0.1'
VERSION = '4.0.2'
HOSTNAME = platform.node()
# Set the base directory two levels up
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Expand Down Expand Up @@ -105,7 +105,7 @@
LANGUAGE_COOKIE_PATH = CSRF_COOKIE_PATH
LOGGING = getattr(configuration, 'LOGGING', {})
LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False)
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', True)
LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None)
LOGOUT_REDIRECT_URL = getattr(configuration, 'LOGOUT_REDIRECT_URL', 'home')
MEDIA_ROOT = getattr(configuration, 'MEDIA_ROOT', os.path.join(BASE_DIR, 'media')).rstrip('/')
Expand Down Expand Up @@ -156,6 +156,7 @@
STORAGE_BACKEND = getattr(configuration, 'STORAGE_BACKEND', None)
STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', {})
TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC')
TRANSLATION_ENABLED = getattr(configuration, 'TRANSLATION_ENABLED', True)

# Load any dynamic configuration parameters which have been hard-coded in the configuration file
for param in CONFIG_PARAMS:
Expand Down Expand Up @@ -445,6 +446,9 @@ def _setting(name, default=None):
# Use timezone-aware datetime objects
USE_TZ = True

# Toggle language translation support
USE_I18N = TRANSLATION_ENABLED

# WSGI
WSGI_APPLICATION = 'netbox.wsgi.application'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Expand Down Expand Up @@ -801,3 +805,10 @@ def _setting(name, default=None):
RQ_QUEUES.update({
f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues
})

# UNSUPPORTED FUNCTIONALITY: Import any local overrides.
try:
from .local_settings import *
_UNSUPPORTED_SETTINGS = True
except ImportError:
pass
3 changes: 2 additions & 1 deletion netbox/netbox/tables/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from core.models import ObjectType
from extras.choices import *
from extras.models import CustomField, CustomLink
from netbox.constants import EMPTY_TABLE_TEXT
from netbox.registry import registry
from netbox.tables import columns
from utilities.paginator import EnhancedPaginator, get_paginate_count
Expand Down Expand Up @@ -258,7 +259,7 @@ class Meta:
attrs = {
'class': 'table table-hover object-list',
}
empty_text = _('No results found')
empty_text = _(EMPTY_TABLE_TEXT)

def __init__(self, data, highlight=None, **kwargs):
self.highlight = highlight
Expand Down
4 changes: 3 additions & 1 deletion netbox/netbox/tests/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def test_admin(self):
url = reverse('admin:dummy_plugin_dummymodel_add')
self.assertEqual(url, '/admin/dummy_plugin/dummymodel/add/')

@override_settings(LOGIN_REQUIRED=False)
def test_views(self):

# Test URL resolution
Expand All @@ -53,7 +54,7 @@ def test_views(self):
response = client.get(url)
self.assertEqual(response.status_code, 200)

@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], LOGIN_REQUIRED=False)
def test_api_views(self):

# Test URL resolution
Expand All @@ -65,6 +66,7 @@ def test_api_views(self):
response = client.get(url)
self.assertEqual(response.status_code, 200)

@override_settings(LOGIN_REQUIRED=False)
def test_registered_views(self):

# Test URL resolution
Expand Down

0 comments on commit cca1b0a

Please sign in to comment.