diff --git a/docs/release-notes/version-3.5.md b/docs/release-notes/version-3.5.md index 1154d4fcd..816fe124c 100644 --- a/docs/release-notes/version-3.5.md +++ b/docs/release-notes/version-3.5.md @@ -4,6 +4,7 @@ ### Bug Fixes +* [#11934](https://github.com/netbox-community/netbox/issues/11934) - Prevent reassignment of an IP address designated as primary for its parent object * [#12694](https://github.com/netbox-community/netbox/issues/12694) - Strip leading & trailing whitespace from custom link URL & text --- diff --git a/netbox/extras/lookups.py b/netbox/extras/lookups.py index 77fe2301e..a8d89c943 100644 --- a/netbox/extras/lookups.py +++ b/netbox/extras/lookups.py @@ -7,12 +7,14 @@ class Empty(Lookup): Filter on whether a string is empty. """ lookup_name = 'empty' + prepare_rhs = False - def as_sql(self, qn, connection): - lhs, lhs_params = self.process_lhs(qn, connection) - rhs, rhs_params = self.process_rhs(qn, connection) - params = lhs_params + rhs_params - return 'CAST(LENGTH(%s) AS BOOLEAN) != %s' % (lhs, rhs), params + def as_sql(self, compiler, connection): + sql, params = compiler.compile(self.lhs) + if self.rhs: + return f"CAST(LENGTH({sql}) AS BOOLEAN) IS NOT TRUE", params + else: + return f"CAST(LENGTH({sql}) AS BOOLEAN) IS TRUE", params class NetContainsOrEquals(Lookup): diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index ac75e2cc3..eb6dbe598 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -328,6 +328,12 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): ): self.initial['primary_for_parent'] = True + # Disable object assignment fields if the IP address is designated as primary + if self.initial.get('primary_for_parent'): + self.fields['interface'].disabled = True + self.fields['vminterface'].disabled = True + self.fields['fhrpgroup'].disabled = True + def clean(self): super().clean() @@ -340,7 +346,12 @@ class IPAddressForm(TenancyForm, NetBoxModelForm): selected_objects[1]: "An IP address can only be assigned to a single object." }) elif selected_objects: - self.instance.assigned_object = self.cleaned_data[selected_objects[0]] + assigned_object = self.cleaned_data[selected_objects[0]] + if self.cleaned_data['primary_for_parent'] and assigned_object != self.instance.assigned_object: + raise ValidationError( + "Cannot reassign IP address while it is designated as the primary IP for the parent object" + ) + self.instance.assigned_object = assigned_object else: self.instance.assigned_object = None diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py index a0c1edee8..9a2385c45 100644 --- a/netbox/netbox/filtersets.py +++ b/netbox/netbox/filtersets.py @@ -177,7 +177,8 @@ class BaseFilterSet(django_filters.FilterSet): # create the new filter with the same type because there is no guarantee the defined type # is the same as the default type for the field resolve_field(field, lookup_expr) # Will raise FieldLookupError if the lookup is invalid - new_filter = type(existing_filter)( + filter_cls = django_filters.BooleanFilter if lookup_expr == 'empty' else type(existing_filter) + new_filter = filter_cls( field_name=field_name, lookup_expr=lookup_expr, label=existing_filter.label, @@ -224,6 +225,14 @@ class BaseFilterSet(django_filters.FilterSet): return filters + @classmethod + def filter_for_lookup(cls, field, lookup_type): + + if lookup_type == 'empty': + return django_filters.BooleanFilter, {} + + return super().filter_for_lookup(field, lookup_type) + class ChangeLoggedModelFilterSet(BaseFilterSet): """ diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 8caaaa9a0..bb83314f5 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 0201e7bf8..9b2017e3f 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/package.json b/netbox/project-static/package.json index f10b5b7ac..98e1a5c60 100644 --- a/netbox/project-static/package.json +++ b/netbox/project-static/package.json @@ -30,6 +30,7 @@ "dayjs": "^1.11.5", "flatpickr": "4.6.13", "gridstack": "^7.2.3", + "html-entities": "^2.3.3", "htmx.org": "^1.8.0", "just-debounce-it": "^3.1.1", "query-string": "^7.1.1", diff --git a/netbox/project-static/src/select/api/apiSelect.ts b/netbox/project-static/src/select/api/apiSelect.ts index f5b605d58..53996910e 100644 --- a/netbox/project-static/src/select/api/apiSelect.ts +++ b/netbox/project-static/src/select/api/apiSelect.ts @@ -1,5 +1,6 @@ import { readableColor } from 'color2k'; import debounce from 'just-debounce-it'; +import { encode } from 'html-entities'; import queryString from 'query-string'; import SlimSelect from 'slim-select'; import { createToast } from '../../bs'; @@ -446,7 +447,7 @@ export class APISelect { // Build SlimSelect options from all already-selected options. const preSelectedOptions = preSelected.map(option => ({ value: option.value, - text: option.innerText, + text: encode(option.innerText), selected: true, disabled: false, })) as Option[]; @@ -454,7 +455,7 @@ export class APISelect { let options = [] as Option[]; for (const result of data.results) { - let text = result.display; + let text = encode(result.display); if (typeof result._depth === 'number' && result._depth > 0) { // If the object has a `_depth` property, indent its display text. diff --git a/netbox/project-static/yarn.lock b/netbox/project-static/yarn.lock index c4bee7557..2adc50001 100644 --- a/netbox/project-static/yarn.lock +++ b/netbox/project-static/yarn.lock @@ -1818,6 +1818,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +html-entities@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + htmx.org@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/htmx.org/-/htmx.org-1.8.0.tgz#f3a2f681f3e2b6357b5a29bba24a2572a8e48fd3" diff --git a/netbox/templates/dcim/device/render_config.html b/netbox/templates/dcim/device/render_config.html index b6e16701f..dfda7cdf6 100644 --- a/netbox/templates/dcim/device/render_config.html +++ b/netbox/templates/dcim/device/render_config.html @@ -28,8 +28,22 @@
{{ context_data|pprint }}+
{{ context_data|pprint }}+