mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-13 15:22:16 -06:00
Compare commits
7 Commits
20911-drop
...
21050-devi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4892caa51 | ||
|
|
e14934e5a5 | ||
|
|
ae03723e43 | ||
|
|
c0f79df91f | ||
|
|
edbfd0bae6 | ||
|
|
c11f4b3716 | ||
|
|
3624b88c3f |
16
.github/ISSUE_TEMPLATE/06-deprecation.yaml
vendored
16
.github/ISSUE_TEMPLATE/06-deprecation.yaml
vendored
@@ -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
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/07-feature_removal.yaml
vendored
Normal file
20
.github/ISSUE_TEMPLATE/07-feature_removal.yaml
vendored
Normal 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
3
.gitignore
vendored
@@ -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/*
|
||||
|
||||
@@ -733,10 +733,9 @@ class ModuleForm(ModuleCommonForm, PrimaryModelForm):
|
||||
)
|
||||
module_bay = DynamicModelChoiceField(
|
||||
label=_('Module bay'),
|
||||
queryset=ModuleBay.objects.order_by('name'),
|
||||
queryset=ModuleBay.objects.all(),
|
||||
query_params={
|
||||
'device_id': '$device',
|
||||
'ordering': 'name',
|
||||
'device_id': '$device'
|
||||
},
|
||||
context={
|
||||
'disabled': 'installed_module',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
0
netbox/media/.gitkeep
Normal 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(
|
||||
|
||||
10
netbox/project-static/dist/netbox.js
vendored
10
netbox/project-static/dist/netbox.js
vendored
File diff suppressed because one or more lines are too long
6
netbox/project-static/dist/netbox.js.map
vendored
6
netbox/project-static/dist/netbox.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -75,15 +75,11 @@ export class DynamicTomSelect extends TomSelect {
|
||||
load(value: string) {
|
||||
const self = this;
|
||||
|
||||
const currentValue = self.getValue();
|
||||
|
||||
// Automatically clear any cached options. (Only options included
|
||||
// in the API response should be present.)
|
||||
self.clearOptions();
|
||||
|
||||
// Clear user_options to prevent the pre-selected option from being treated specially
|
||||
(self as any).user_options = {};
|
||||
|
||||
// Populate the null option (if any) if not searching
|
||||
if (self.nullOption && !value) {
|
||||
self.addOption(self.nullOption);
|
||||
}
|
||||
@@ -97,33 +93,21 @@ export class DynamicTomSelect extends TomSelect {
|
||||
addClasses(self.wrapper, self.settings.loadingClass);
|
||||
self.loading++;
|
||||
|
||||
// Make the API request
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(apiData => {
|
||||
const results: Dict[] = apiData.results;
|
||||
|
||||
// Add options and set $order to preserve API response order
|
||||
results.forEach((result, index) => {
|
||||
const options: Dict[] = [];
|
||||
for (const result of results) {
|
||||
const option = self.getOptionFromData(result);
|
||||
self.addOption(option);
|
||||
const key = option[self.settings.valueField as string] as string;
|
||||
if (self.options[key]) {
|
||||
(self.options[key] as any).$order = index;
|
||||
}
|
||||
});
|
||||
|
||||
if (self.loading > 0) {
|
||||
self.loading--;
|
||||
if (self.loading === 0) {
|
||||
self.wrapper.classList.remove(self.settings.loadingClass as string);
|
||||
}
|
||||
options.push(option);
|
||||
}
|
||||
|
||||
if (currentValue && !self.items.includes(currentValue as string)) {
|
||||
self.items.push(currentValue as string);
|
||||
}
|
||||
|
||||
self.refreshOptions(false);
|
||||
return options;
|
||||
})
|
||||
// Pass the options to the callback function
|
||||
.then(options => {
|
||||
self.loadCallback(options, []);
|
||||
})
|
||||
.catch(() => {
|
||||
self.loadCallback([], []);
|
||||
|
||||
@@ -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} "
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(_(
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user