Compare commits

...

7 Commits

Author SHA1 Message Date
Martin Hauser
f4892caa51 fix(ipam): Prevent reassignment of OOB IPs
Disable reassignment of IP addresses designated as primary or OOB for
parent objects. Adds validation to block changes when an IP is marked as
the OOB IP.

Fixes #21050
2026-01-13 18:13:31 +01:00
github-actions
e14934e5a5 Update source translation strings
Some checks failed
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
2026-01-13 05:05:43 +00:00
Adam
ae03723e43 Fixes #21105: Update help text for token field on API page. (#21106)
Some checks failed
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CI / build (20.x, 3.14) (push) Has been cancelled
Co-authored-by: Jason Novinger <jnovinger@gmail.com>
2026-01-12 19:17:35 -06:00
Jeremy Stretch
c0f79df91f Introduce a new issue type for feature removals (#21092)
Co-authored-by: Jason Novinger <jnovinger@gmail.com>
2026-01-12 15:41:25 -06:00
Jeremy Stretch
edbfd0bae6 Fixes #21117: Avoid exception when attempting to create v2 token without API_TOKEN_PEPPERS defined (#21132) 2026-01-12 15:40:42 -06:00
Mario
c11f4b3716 21075-rename-l2vpn-terminations-menu-entry
Some checks failed
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CI / build (20.x, 3.14) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
2026-01-12 10:40:45 -05:00
Martin Hauser
3624b88c3f Closes #21035: Add .gitkeep to track the media directory (#21074)
Some checks failed
Close stale issues/PRs / stale (push) Has been cancelled
Close incomplete issues / stale (push) Has been cancelled
Update translation strings / makemessages (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CI / build (20.x, 3.14) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Lock threads / lock (push) Has been cancelled
2026-01-08 14:33:06 -06:00
11 changed files with 72 additions and 18 deletions

View File

@@ -1,20 +1,26 @@
---
name: 🗑 Deprecation
name: Deprecation
type: Deprecation
description: The removal of an existing feature or resource
description: Designation of a feature or behavior that will be removed in a future release
labels: ["netbox", "type: deprecation"]
body:
- type: textarea
attributes:
label: Proposed Changes
label: Deprecated Functionality
description: >
Describe in detail the proposed changes. What is being removed?
Describe the feature(s) and/or behavior that is being flagged for deprecation.
validations:
required: true
- type: input
attributes:
label: Scheduled removal
description: In what future release will the deprecated functionality be removed?
validations:
required: true
- type: textarea
attributes:
label: Justification
description: Please provide justification for the proposed change(s).
description: Please provide justification for the deprecation.
validations:
required: true
- type: textarea

View File

@@ -0,0 +1,20 @@
---
name: 🗑️ Feature Removal
type: Removal
description: The removal of a deprecated feature or resource
labels: ["netbox", "type: removal"]
body:
- type: input
attributes:
label: Deprecation Issue
description: Specify the issue in which this deprecation was announced.
placeholder: "#1234"
validations:
required: true
- type: textarea
attributes:
label: Summary of Changes
description: >
List all changes necessary to remove the deprecated feature or resource.
validations:
required: true

3
.gitignore vendored
View File

@@ -9,7 +9,8 @@ yarn-error.log*
/netbox/netbox/configuration.py
/netbox/netbox/ldap_config.py
/netbox/local/*
/netbox/media
/netbox/media/*
!/netbox/media/.gitkeep
/netbox/reports/*
!/netbox/reports/__init__.py
/netbox/scripts/*

View File

@@ -372,8 +372,8 @@ class IPAddressForm(TenancyForm, PrimaryModelForm):
'virtual_machine_id': instance.assigned_object.virtual_machine.pk,
})
# Disable object assignment fields if the IP address is designated as primary
if self.initial.get('primary_for_parent'):
# Disable object assignment fields if the IP address is designated as primary or OOB
if self.initial.get('primary_for_parent') or self.initial.get('oob_for_parent'):
self.fields['interface'].disabled = True
self.fields['vminterface'].disabled = True
self.fields['fhrpgroup'].disabled = True

View File

@@ -940,6 +940,13 @@ class IPAddress(ContactsMixin, PrimaryModel):
_("Cannot reassign IP address while it is designated as the primary IP for the parent object")
)
# can't use is_oob_ip as self.assigned_object might be changed
if hasattr(original_parent, 'oob_ip') and original_parent.oob_ip_id == self.pk:
if parent != original_parent:
raise ValidationError(
_("Cannot reassign IP address while it is designated as the OOB IP for the parent object")
)
# Validate IP status selection
if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
raise ValidationError({

0
netbox/media/.gitkeep Normal file
View File

View File

@@ -232,7 +232,7 @@ VPN_MENU = Menu(
label=_('L2VPNs'),
items=(
get_model_item('vpn', 'l2vpn', _('L2VPNs')),
get_model_item('vpn', 'l2vpntermination', _('Terminations')),
get_model_item('vpn', 'l2vpntermination', _('L2VPN Terminations')),
),
),
MenuGroup(

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-01-08 05:04+0000\n"
"POT-Creation-Date: 2026-01-13 05:05+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -1822,7 +1822,6 @@ msgid "ASN Count"
msgstr ""
#: netbox/circuits/tables/virtual_circuits.py:64
#: netbox/netbox/navigation/menu.py:235
#: netbox/templates/circuits/virtualcircuit.html:87
#: netbox/templates/vpn/l2vpn.html:60 netbox/templates/vpn/tunnel.html:72
#: netbox/vpn/tables/tunnels.py:59
@@ -12190,6 +12189,10 @@ msgstr ""
msgid "L2VPNs"
msgstr ""
#: netbox/netbox/navigation/menu.py:235
msgid "L2VPN Terminations"
msgstr ""
#: netbox/netbox/navigation/menu.py:241
msgid "IKE Proposals"
msgstr ""
@@ -15938,7 +15941,7 @@ msgstr ""
#: netbox/users/forms/model_forms.py:126
msgid ""
"Tokens must be at least 40 characters in length. <strong>Be sure to record "
"your key</strong> prior to submitting this form, as it will no longer be "
"your token</strong> prior to submitting this form, as it will no longer be "
"accessible once the token has been created."
msgstr ""
@@ -16077,7 +16080,7 @@ msgid "write enabled"
msgstr ""
#: netbox/users/models/tokens.py:72
msgid "Permit create/update/delete operations using this key"
msgid "Permit create/update/delete operations using this token"
msgstr ""
#: netbox/users/models/tokens.py:76
@@ -16126,12 +16129,16 @@ msgstr ""
msgid "tokens"
msgstr ""
#: netbox/users/models/tokens.py:219
#: netbox/users/models/tokens.py:217
msgid "Unable to save v2 tokens: API_TOKEN_PEPPERS is not defined."
msgstr ""
#: netbox/users/models/tokens.py:222
#, python-brace-format
msgid "Invalid pepper ID: {id}. Check configured API_TOKEN_PEPPERS."
msgstr ""
#: netbox/users/models/tokens.py:232
#: netbox/users/models/tokens.py:235
#, python-brace-format
msgid ""
"Expiration time must be in the future. Current server time is {current_time} "

View File

@@ -123,7 +123,7 @@ class UserTokenForm(forms.ModelForm):
token = forms.CharField(
label=_('Token'),
help_text=_(
'Tokens must be at least 40 characters in length. <strong>Be sure to record your key</strong> prior to '
'Tokens must be at least 40 characters in length. <strong>Be sure to record your token</strong> prior to '
'submitting this form, as it will no longer be accessible once the token has been created.'
),
widget=forms.TextInput(

View File

@@ -69,7 +69,7 @@ class Token(models.Model):
write_enabled = models.BooleanField(
verbose_name=_('write enabled'),
default=True,
help_text=_('Permit create/update/delete operations using this key')
help_text=_('Permit create/update/delete operations using this token')
)
# For legacy v1 tokens, this field stores the plaintext 40-char token value. Not used for v2.
plaintext = models.CharField(
@@ -213,6 +213,9 @@ class Token(models.Model):
def clean(self):
super().clean()
if self.version == TokenVersionChoices.V2 and not settings.API_TOKEN_PEPPERS:
raise ValidationError(_("Unable to save v2 tokens: API_TOKEN_PEPPERS is not defined."))
if self._state.adding:
if self.pepper_id is not None and self.pepper_id not in settings.API_TOKEN_PEPPERS:
raise ValidationError(_(

View File

@@ -1,9 +1,10 @@
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.test import TestCase
from django.test import TestCase, override_settings
from django.utils import timezone
from users.choices import TokenVersionChoices
from users.models import User, Token
from utilities.testing import create_test_user
@@ -94,6 +95,15 @@ class TokenTest(TestCase):
token.refresh_from_db()
self.assertEqual(token.description, 'New Description')
@override_settings(API_TOKEN_PEPPERS={})
def test_v2_without_peppers_configured(self):
"""
Attempting to save a v2 token without API_TOKEN_PEPPERS defined should raise a ValidationError.
"""
token = Token(version=TokenVersionChoices.V2)
with self.assertRaises(ValidationError):
token.clean()
class UserConfigTest(TestCase):