diff --git a/.github/ISSUE_TEMPLATE/documentation_change.yaml b/.github/ISSUE_TEMPLATE/documentation_change.yaml index 0f87115fc..cb097d579 100644 --- a/.github/ISSUE_TEMPLATE/documentation_change.yaml +++ b/.github/ISSUE_TEMPLATE/documentation_change.yaml @@ -19,11 +19,15 @@ body: label: Area description: To what section of the documentation does this change primarily pertain? options: - - Installation instructions - - Configuration parameters - - Functionality/features - - REST API - - Administration/development + - Features + - Installation/upgrade + - Getting started + - Configuration + - Customization + - Integrations/API + - Plugins + - Administration + - Development - Other validations: required: true diff --git a/docs/installation/6-ldap.md b/docs/installation/6-ldap.md index 163ace70d..ffba6889b 100644 --- a/docs/installation/6-ldap.md +++ b/docs/installation/6-ldap.md @@ -46,7 +46,7 @@ Next, create a file in the same directory as `configuration.py` (typically `/opt ### General Server Configuration !!! info - When using Windows Server 2012 you may need to specify a port on `AUTH_LDAP_SERVER_URI`. Use `3269` for secure, or `3268` for non-secure. + When using Active Directory you may need to specify a port on `AUTH_LDAP_SERVER_URI` to authenticate users from all domains in the forest. Use `3269` for secure, or `3268` for non-secure access to the GC (Global Catalog). ```python import ldap @@ -67,6 +67,16 @@ AUTH_LDAP_BIND_PASSWORD = "demo" # Note that this is a NetBox-specific setting which sets: # ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) LDAP_IGNORE_CERT_ERRORS = True + +# Include this setting if you want to validate the LDAP server certificates against a CA certificate directory on your server +# Note that this is a NetBox-specific setting which sets: +# ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, LDAP_CA_CERT_DIR) +LDAP_CA_CERT_DIR = '/etc/ssl/certs' + +# Include this setting if you want to validate the LDAP server certificates against your own CA. +# Note that this is a NetBox-specific setting which sets: +# ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, LDAP_CA_CERT_FILE) +LDAP_CA_CERT_FILE = '/path/to/example-CA.crt' ``` STARTTLS can be configured by setting `AUTH_LDAP_START_TLS = True` and using the `ldap://` URI scheme. diff --git a/docs/plugins/development/forms.md b/docs/plugins/development/forms.md index e1eefa7a5..dee0d3796 100644 --- a/docs/plugins/development/forms.md +++ b/docs/plugins/development/forms.md @@ -144,73 +144,73 @@ class MyModelFilterForm(NetBoxModelFilterSetForm): In addition to the [form fields provided by Django](https://docs.djangoproject.com/en/stable/ref/forms/fields/), NetBox provides several field classes for use within forms to handle specific types of data. These can be imported from `utilities.forms.fields` and are documented below. ::: utilities.forms.ColorField - selection: + options: members: false ::: utilities.forms.CommentField - selection: + options: members: false ::: utilities.forms.JSONField - selection: + options: members: false ::: utilities.forms.MACAddressField - selection: + options: members: false ::: utilities.forms.SlugField - selection: + options: members: false ## Choice Fields ::: utilities.forms.ChoiceField - selection: + options: members: false ::: utilities.forms.MultipleChoiceField - selection: + options: members: false ## Dynamic Object Fields ::: utilities.forms.DynamicModelChoiceField - selection: + options: members: false ::: utilities.forms.DynamicModelMultipleChoiceField - selection: + options: members: false ## Content Type Fields ::: utilities.forms.ContentTypeChoiceField - selection: + options: members: false ::: utilities.forms.ContentTypeMultipleChoiceField - selection: + options: members: false ## CSV Import Fields ::: utilities.forms.CSVChoiceField - selection: + options: members: false ::: utilities.forms.CSVMultipleChoiceField - selection: + options: members: false ::: utilities.forms.CSVModelChoiceField - selection: + options: members: false ::: utilities.forms.CSVContentTypeField - selection: + options: members: false ::: utilities.forms.CSVMultipleContentTypeField - selection: + options: members: false diff --git a/docs/plugins/development/graphql-api.md b/docs/plugins/development/graphql-api.md index 0dadf021f..f802e8025 100644 --- a/docs/plugins/development/graphql-api.md +++ b/docs/plugins/development/graphql-api.md @@ -32,11 +32,11 @@ schema = MyQuery NetBox provides two object type classes for use by plugins. ::: netbox.graphql.types.BaseObjectType - selection: + options: members: false ::: netbox.graphql.types.NetBoxObjectType - selection: + options: members: false ## GraphQL Fields @@ -44,9 +44,9 @@ NetBox provides two object type classes for use by plugins. NetBox provides two field classes for use by plugins. ::: netbox.graphql.fields.ObjectField - selection: + options: members: false ::: netbox.graphql.fields.ObjectListField - selection: + options: members: false diff --git a/docs/plugins/development/tables.md b/docs/plugins/development/tables.md index 6dccb4ee2..f846139f0 100644 --- a/docs/plugins/development/tables.md +++ b/docs/plugins/development/tables.md @@ -52,38 +52,38 @@ This will automatically apply any user-specific preferences for the table. (If u The table column classes listed below are supported for use in plugins. These classes can be imported from `netbox.tables.columns`. ::: netbox.tables.BooleanColumn - selection: + options: members: false ::: netbox.tables.ChoiceFieldColumn - selection: + options: members: false ::: netbox.tables.ColorColumn - selection: + options: members: false ::: netbox.tables.ColoredLabelColumn - selection: + options: members: false ::: netbox.tables.ContentTypeColumn - selection: + options: members: false ::: netbox.tables.ContentTypesColumn - selection: + options: members: false ::: netbox.tables.MarkdownColumn - selection: + options: members: false ::: netbox.tables.TagColumn - selection: + options: members: false ::: netbox.tables.TemplateColumn - selection: + options: members: - __init__ diff --git a/docs/plugins/development/views.md b/docs/plugins/development/views.md index dfada7a42..e12f32bad 100644 --- a/docs/plugins/development/views.md +++ b/docs/plugins/development/views.md @@ -84,24 +84,24 @@ Below are the class definitions for NetBox's object views. These views handle CR ::: netbox.views.generic.base.BaseObjectView ::: netbox.views.generic.ObjectView - selection: + options: members: - get_object - get_template_name ::: netbox.views.generic.ObjectEditView - selection: + options: members: - get_object - alter_object ::: netbox.views.generic.ObjectDeleteView - selection: + options: members: - get_object ::: netbox.views.generic.ObjectChildrenView - selection: + options: members: - get_children - prep_table_data @@ -113,22 +113,22 @@ Below are the class definitions for NetBox's multi-object views. These views han ::: netbox.views.generic.base.BaseMultiObjectView ::: netbox.views.generic.ObjectListView - selection: + options: members: - get_table - export_table - export_template ::: netbox.views.generic.BulkImportView - selection: + options: members: false ::: netbox.views.generic.BulkEditView - selection: + options: members: false ::: netbox.views.generic.BulkDeleteView - selection: + options: members: - get_form @@ -137,12 +137,12 @@ Below are the class definitions for NetBox's multi-object views. These views han These views are provided to enable or enhance certain NetBox model features, such as change logging or journaling. These typically do not need to be subclassed: They can be used directly e.g. in a URL path. ::: netbox.views.generic.ObjectChangeLogView - selection: + options: members: - get_form ::: netbox.views.generic.ObjectJournalView - selection: + options: members: - get_form diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 2e25a9589..de0f1a40a 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -2,6 +2,21 @@ ## v3.3.6 (FUTURE) +### Enhancements + +* [#9722](https://github.com/netbox-community/netbox/issues/9722) - Add LDAP configuration parameters to specify certificates +* [#10685](https://github.com/netbox-community/netbox/issues/10685) - Position A/Z termination cards above the fold under circuit view + +### Bug Fixes + +* [#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 +* [#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+ + --- ## v3.3.5 (2022-10-05) diff --git a/mkdocs.yml b/mkdocs.yml index a7977d7ea..fc3a40632 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,7 +30,7 @@ plugins: - os.chdir('netbox/') - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "netbox.settings") - django.setup() - rendering: + options: heading_level: 3 members_order: source show_root_heading: true diff --git a/netbox/circuits/forms/models.py b/netbox/circuits/forms/models.py index 17c2e7480..03c473d62 100644 --- a/netbox/circuits/forms/models.py +++ b/netbox/circuits/forms/models.py @@ -64,6 +64,12 @@ class ProviderNetworkForm(NetBoxModelForm): class CircuitTypeForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Circuit Type', ( + 'name', 'slug', 'description', 'tags', + )), + ) + class Meta: model = CircuitType fields = [ diff --git a/netbox/circuits/tables/circuits.py b/netbox/circuits/tables/circuits.py index f9ab7e190..477f9c1ab 100644 --- a/netbox/circuits/tables/circuits.py +++ b/netbox/circuits/tables/circuits.py @@ -1,8 +1,9 @@ import django_tables2 as tables - from circuits.models import * +from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin + from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenancyColumnsMixin + from .columns import CommitRateColumn __all__ = ( @@ -39,7 +40,7 @@ class CircuitTypeTable(NetBoxTable): default_columns = ('pk', 'name', 'circuit_count', 'description', 'slug') -class CircuitTable(TenancyColumnsMixin, NetBoxTable): +class CircuitTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): cid = tables.Column( linkify=True, verbose_name='Circuit ID' @@ -58,9 +59,6 @@ class CircuitTable(TenancyColumnsMixin, NetBoxTable): ) commit_rate = CommitRateColumn() comments = columns.MarkdownColumn() - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='circuits:circuit_list' ) diff --git a/netbox/circuits/tables/providers.py b/netbox/circuits/tables/providers.py index 3e2fd1193..a117274ff 100644 --- a/netbox/circuits/tables/providers.py +++ b/netbox/circuits/tables/providers.py @@ -1,7 +1,8 @@ import django_tables2 as tables -from django_tables2.utils import Accessor - from circuits.models import * +from django_tables2.utils import Accessor +from tenancy.tables import ContactsColumnMixin + from netbox.tables import NetBoxTable, columns __all__ = ( @@ -10,7 +11,7 @@ __all__ = ( ) -class ProviderTable(NetBoxTable): +class ProviderTable(ContactsColumnMixin, NetBoxTable): name = tables.Column( linkify=True ) @@ -31,9 +32,6 @@ class ProviderTable(NetBoxTable): verbose_name='Circuits' ) comments = columns.MarkdownColumn() - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='circuits:provider_list' ) diff --git a/netbox/dcim/forms/connections.py b/netbox/dcim/forms/connections.py index cc5cf362f..5e3948baa 100644 --- a/netbox/dcim/forms/connections.py +++ b/netbox/dcim/forms/connections.py @@ -108,7 +108,7 @@ def get_cable_form(a_type, b_type): label='Power Feed', disabled_indicator='_occupied', query_params={ - 'powerpanel_id': f'$termination_{cable_end}_powerpanel', + 'power_panel_id': f'$termination_{cable_end}_powerpanel', } ) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 06d63af94..1df9e143c 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -78,6 +78,12 @@ class RegionForm(NetBoxModelForm): ) slug = SlugField() + fieldsets = ( + ('Region', ( + 'parent', 'name', 'slug', 'description', 'tags', + )), + ) + class Meta: model = Region fields = ( @@ -92,6 +98,12 @@ class SiteGroupForm(NetBoxModelForm): ) slug = SlugField() + fieldsets = ( + ('Site Group', ( + 'parent', 'name', 'slug', 'description', 'tags', + )), + ) + class Meta: model = SiteGroup fields = ( @@ -213,6 +225,12 @@ class LocationForm(TenancyForm, NetBoxModelForm): class RackRoleForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Rack Role', ( + 'name', 'slug', 'color', 'description', 'tags', + )), + ) + class Meta: model = RackRole fields = [ @@ -341,6 +359,12 @@ class RackReservationForm(TenancyForm, NetBoxModelForm): class ManufacturerForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Manufacturer', ( + 'name', 'slug', 'description', 'tags', + )), + ) + class Meta: model = Manufacturer fields = [ @@ -413,6 +437,12 @@ class ModuleTypeForm(NetBoxModelForm): class DeviceRoleForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Device Role', ( + 'name', 'slug', 'color', 'vm_role', 'description', 'tags', + )), + ) + class Meta: model = DeviceRole fields = [ @@ -429,6 +459,13 @@ class PlatformForm(NetBoxModelForm): max_length=64 ) + fieldsets = ( + ('Platform', ( + 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description', 'tags', + + )), + ) + class Meta: model = Platform fields = [ @@ -1584,6 +1621,12 @@ class InventoryItemForm(DeviceComponentForm): class InventoryItemRoleForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Inventory Item Role', ( + 'name', 'slug', 'color', 'description', 'tags', + )), + ) + class Meta: model = InventoryItemRole fields = [ diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 142c7ef67..3b129c963 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -1,12 +1,26 @@ import django_tables2 as tables -from django_tables2.utils import Accessor - from dcim.models import ( - ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, FrontPort, Interface, InventoryItem, - InventoryItemRole, ModuleBay, Platform, PowerOutlet, PowerPort, RearPort, VirtualChassis, + ConsolePort, + ConsoleServerPort, + Device, + DeviceBay, + DeviceRole, + FrontPort, + Interface, + InventoryItem, + InventoryItemRole, + ModuleBay, + Platform, + PowerOutlet, + PowerPort, + RearPort, + VirtualChassis, ) +from django_tables2.utils import Accessor +from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin + from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenancyColumnsMixin + from .template_code import * __all__ = ( @@ -137,7 +151,7 @@ class PlatformTable(NetBoxTable): # Devices # -class DeviceTable(TenancyColumnsMixin, NetBoxTable): +class DeviceTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = tables.TemplateColumn( order_by=('_name',), template_code=DEVICE_LINK @@ -201,9 +215,6 @@ class DeviceTable(TenancyColumnsMixin, NetBoxTable): verbose_name='VC Priority' ) comments = columns.MarkdownColumn() - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:device_list' ) diff --git a/netbox/dcim/tables/devicetypes.py b/netbox/dcim/tables/devicetypes.py index c48e93ca7..19b04c70d 100644 --- a/netbox/dcim/tables/devicetypes.py +++ b/netbox/dcim/tables/devicetypes.py @@ -1,10 +1,21 @@ import django_tables2 as tables from dcim.models import ( - ConsolePortTemplate, ConsoleServerPortTemplate, DeviceBayTemplate, DeviceType, FrontPortTemplate, InterfaceTemplate, - InventoryItemTemplate, Manufacturer, ModuleBayTemplate, PowerOutletTemplate, PowerPortTemplate, RearPortTemplate, + ConsolePortTemplate, + ConsoleServerPortTemplate, + DeviceBayTemplate, + DeviceType, + FrontPortTemplate, + InterfaceTemplate, + InventoryItemTemplate, + Manufacturer, + ModuleBayTemplate, + PowerOutletTemplate, + PowerPortTemplate, + RearPortTemplate, ) from netbox.tables import NetBoxTable, columns +from tenancy.tables import ContactsColumnMixin from .template_code import MODULAR_COMPONENT_TEMPLATE_BUTTONS, DEVICE_WEIGHT __all__ = ( @@ -27,7 +38,7 @@ __all__ = ( # Manufacturers # -class ManufacturerTable(NetBoxTable): +class ManufacturerTable(ContactsColumnMixin, NetBoxTable): name = tables.Column( linkify=True ) @@ -43,9 +54,6 @@ class ManufacturerTable(NetBoxTable): verbose_name='Platforms' ) slug = tables.Column() - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:manufacturer_list' ) diff --git a/netbox/dcim/tables/power.py b/netbox/dcim/tables/power.py index 6696d516a..04012ea4a 100644 --- a/netbox/dcim/tables/power.py +++ b/netbox/dcim/tables/power.py @@ -1,7 +1,9 @@ import django_tables2 as tables - from dcim.models import PowerFeed, PowerPanel +from tenancy.tables import ContactsColumnMixin + from netbox.tables import NetBoxTable, columns + from .devices import CableTerminationTable __all__ = ( @@ -14,7 +16,7 @@ __all__ = ( # Power panels # -class PowerPanelTable(NetBoxTable): +class PowerPanelTable(ContactsColumnMixin, NetBoxTable): name = tables.Column( linkify=True ) @@ -29,9 +31,6 @@ class PowerPanelTable(NetBoxTable): url_params={'power_panel_id': 'pk'}, verbose_name='Feeds' ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:powerpanel_list' ) diff --git a/netbox/dcim/tables/racks.py b/netbox/dcim/tables/racks.py index ffca07145..9c7b28983 100644 --- a/netbox/dcim/tables/racks.py +++ b/netbox/dcim/tables/racks.py @@ -3,7 +3,7 @@ from django_tables2.utils import Accessor from dcim.models import Rack, RackReservation, RackRole from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenancyColumnsMixin +from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin from .template_code import DEVICE_WEIGHT __all__ = ( @@ -38,7 +38,7 @@ class RackRoleTable(NetBoxTable): # Racks # -class RackTable(TenancyColumnsMixin, NetBoxTable): +class RackTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = tables.Column( order_by=('_name',), linkify=True @@ -69,9 +69,6 @@ class RackTable(TenancyColumnsMixin, NetBoxTable): orderable=False, verbose_name='Power' ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:rack_list' ) diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py index 5dc2aa611..f013025f7 100644 --- a/netbox/dcim/tables/sites.py +++ b/netbox/dcim/tables/sites.py @@ -1,8 +1,9 @@ import django_tables2 as tables - from dcim.models import Location, Region, Site, SiteGroup +from tenancy.tables import ContactsColumnMixin, TenancyColumnsMixin + from netbox.tables import NetBoxTable, columns -from tenancy.tables import TenancyColumnsMixin + from .template_code import LOCATION_BUTTONS __all__ = ( @@ -17,7 +18,7 @@ __all__ = ( # Regions # -class RegionTable(NetBoxTable): +class RegionTable(ContactsColumnMixin, NetBoxTable): name = columns.MPTTColumn( linkify=True ) @@ -26,9 +27,6 @@ class RegionTable(NetBoxTable): url_params={'region_id': 'pk'}, verbose_name='Sites' ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:region_list' ) @@ -46,7 +44,7 @@ class RegionTable(NetBoxTable): # Site groups # -class SiteGroupTable(NetBoxTable): +class SiteGroupTable(ContactsColumnMixin, NetBoxTable): name = columns.MPTTColumn( linkify=True ) @@ -55,9 +53,6 @@ class SiteGroupTable(NetBoxTable): url_params={'group_id': 'pk'}, verbose_name='Sites' ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:sitegroup_list' ) @@ -75,7 +70,7 @@ class SiteGroupTable(NetBoxTable): # Sites # -class SiteTable(TenancyColumnsMixin, NetBoxTable): +class SiteTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = tables.Column( linkify=True ) @@ -97,9 +92,6 @@ class SiteTable(TenancyColumnsMixin, NetBoxTable): verbose_name='ASN Count' ) comments = columns.MarkdownColumn() - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:site_list' ) @@ -118,7 +110,7 @@ class SiteTable(TenancyColumnsMixin, NetBoxTable): # Locations # -class LocationTable(TenancyColumnsMixin, NetBoxTable): +class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable): name = columns.MPTTColumn( linkify=True ) @@ -136,9 +128,6 @@ class LocationTable(TenancyColumnsMixin, NetBoxTable): url_params={'location_id': 'pk'}, verbose_name='Devices' ) - contacts = columns.ManyToManyColumn( - linkify_item=True - ) tags = columns.TagColumn( url_name='dcim:location_list' ) diff --git a/netbox/ipam/forms/models.py b/netbox/ipam/forms/models.py index dea42065c..86a083361 100644 --- a/netbox/ipam/forms/models.py +++ b/netbox/ipam/forms/models.py @@ -88,6 +88,12 @@ class RouteTargetForm(TenancyForm, NetBoxModelForm): class RIRForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('RIR', ( + 'name', 'slug', 'is_private', 'description', 'tags', + )), + ) + class Meta: model = RIR fields = [ @@ -164,6 +170,12 @@ class ASNForm(TenancyForm, NetBoxModelForm): class RoleForm(NetBoxModelForm): slug = SlugField() + fieldsets = ( + ('Role', ( + 'name', 'slug', 'weight', 'description', 'tags', + )), + ) + class Meta: model = Role fields = [ @@ -784,6 +796,12 @@ class ServiceTemplateForm(NetBoxModelForm): help_text="Comma-separated list of one or more port numbers. A range may be specified using a hyphen." ) + fieldsets = ( + ('Service Template', ( + 'name', 'protocol', 'ports', 'description', 'tags', + )), + ) + class Meta: model = ServiceTemplate fields = ('name', 'protocol', 'ports', 'description', 'tags') diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index 70ad38197..b566db375 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -92,6 +92,8 @@ class Service(ServiceBase, NetBoxModel): verbose_name='IP addresses' ) + clone_fields = ['protocol', 'ports', 'description', 'device', 'virtual_machine', 'ipaddresses', ] + class Meta: ordering = ('protocol', 'ports', 'pk') # (protocol, port) may be non-unique diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py index b1f4858c7..a7e56e279 100644 --- a/netbox/netbox/authentication.py +++ b/netbox/netbox/authentication.py @@ -351,6 +351,14 @@ class LDAPBackend: if getattr(ldap_config, 'LDAP_IGNORE_CERT_ERRORS', False): ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) + # Optionally set CA cert directory + if ca_cert_dir := getattr(ldap_config, 'LDAP_CA_CERT_DIR', None): + ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, ca_cert_dir) + + # Optionally set CA cert file + if ca_cert_file := getattr(ldap_config, 'LDAP_CA_CERT_FILE', None): + ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, ca_cert_file) + return obj diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index a693f4754..980d3dc7e 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -493,7 +493,7 @@ for param in dir(configuration): # Force usage of PostgreSQL's JSONB field for extra data SOCIAL_AUTH_JSONFIELD_ENABLED = True - +SOCIAL_AUTH_CLEAN_USERNAME_FUNCTION = 'netbox.users.utils.clean_username' # # Django Prometheus diff --git a/netbox/project-static/.eslintrc b/netbox/project-static/.eslintrc index 802fa7a3e..30b47308b 100644 --- a/netbox/project-static/.eslintrc +++ b/netbox/project-static/.eslintrc @@ -31,8 +31,7 @@ } }, "rules": { - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-unused-vars-experimental": "error", + "@typescript-eslint/no-unused-vars": "error", "no-unused-vars": "off", "no-inner-declarations": "off", "comma-dangle": ["error", "always-multiline"], diff --git a/netbox/project-static/dist/cable_trace.css b/netbox/project-static/dist/cable_trace.css index ff431f4ad..a5f5ff7e9 100644 --- a/netbox/project-static/dist/cable_trace.css +++ b/netbox/project-static/dist/cable_trace.css @@ -1 +1 @@ -:root{--nbx-trace-color: #000;--nbx-trace-node-bg: #e9ecef;--nbx-trace-termination-bg: #f8f9fa;--nbx-trace-cable-shadow: #343a40;--nbx-trace-attachment: #ced4da}:root[data-netbox-color-mode=dark]{--nbx-trace-color: #fff;--nbx-trace-node-bg: #212529;--nbx-trace-termination-bg: #343a40;--nbx-trace-cable-shadow: #e9ecef;--nbx-trace-attachment: #6c757d}*{font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:.875rem}text{text-anchor:middle;dominant-baseline:middle}text:not([fill]){fill:var(--nbx-trace-color)}text.bold{font-weight:700}svg rect{fill:var(--nbx-trace-node-bg);stroke:#606060;stroke-width:1}svg rect .termination{fill:var(--nbx-trace-termination-bg)}svg .connector text{text-anchor:start}svg line{stroke-width:5px}svg polyline{fill:none;stroke-width:5px}svg .cable-shadow{stroke:var(--nbx-trace-cable-shadow);stroke-width:7px}svg line.wireless-link{stroke:var(--nbx-trace-attachment);stroke-dasharray:4px 12px;stroke-linecap:round}svg line.attachment{stroke:var(--nbx-trace-attachment);stroke-dasharray:5px} +:root{--nbx-trace-color: #000;--nbx-trace-node-bg: #e9ecef;--nbx-trace-termination-bg: #f8f9fa;--nbx-trace-cable-shadow: #343a40;--nbx-trace-attachment: #ced4da}:root[data-netbox-color-mode=dark]{--nbx-trace-color: #fff;--nbx-trace-node-bg: #212529;--nbx-trace-termination-bg: #343a40;--nbx-trace-cable-shadow: #e9ecef;--nbx-trace-attachment: #6c757d}*{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-size:.875rem}text{text-anchor:middle;dominant-baseline:middle}text:not([fill]){fill:var(--nbx-trace-color)}text.bold{font-weight:700}svg rect{fill:var(--nbx-trace-node-bg);stroke:#606060;stroke-width:1}svg rect .termination{fill:var(--nbx-trace-termination-bg)}svg .connector text{text-anchor:start}svg line{stroke-width:5px}svg polyline{fill:none;stroke-width:5px}svg .cable-shadow{stroke:var(--nbx-trace-cable-shadow);stroke-width:7px}svg line.wireless-link{stroke:var(--nbx-trace-attachment);stroke-dasharray:4px 12px;stroke-linecap:round}svg line.attachment{stroke:var(--nbx-trace-attachment);stroke-dasharray:5px} diff --git a/netbox/project-static/dist/config.js b/netbox/project-static/dist/config.js index 2b360c5a2..cda30523c 100644 --- a/netbox/project-static/dist/config.js +++ b/netbox/project-static/dist/config.js @@ -1,5 +1,5 @@ -(()=>{var cr=Object.create;var le=Object.defineProperty,ur=Object.defineProperties,fr=Object.getOwnPropertyDescriptor,dr=Object.getOwnPropertyDescriptors,hr=Object.getOwnPropertyNames,hn=Object.getOwnPropertySymbols,pr=Object.getPrototypeOf,pn=Object.prototype.hasOwnProperty,mr=Object.prototype.propertyIsEnumerable;var mn=(i,t,e)=>t in i?le(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,O=(i,t)=>{for(var e in t||(t={}))pn.call(t,e)&&mn(i,e,t[e]);if(hn)for(var e of hn(t))mr.call(t,e)&&mn(i,e,t[e]);return i},Me=(i,t)=>ur(i,dr(t)),gn=i=>le(i,"__esModule",{value:!0});var Ot=(i,t)=>()=>(t||i((t={exports:{}}).exports,t),t.exports),gr=(i,t)=>{gn(i);for(var e in t)le(i,e,{get:t[e],enumerable:!0})},_r=(i,t,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of hr(t))!pn.call(i,n)&&n!=="default"&&le(i,n,{get:()=>t[n],enumerable:!(e=fr(t,n))||e.enumerable});return i},Er=i=>_r(gn(le(i!=null?cr(pr(i)):{},"default",i&&i.__esModule&&"default"in i?{get:()=>i.default,enumerable:!0}:{value:i,enumerable:!0})),i);var vi=(i,t,e)=>new Promise((n,o)=>{var r=u=>{try{l(e.next(u))}catch(p){o(p)}},s=u=>{try{l(e.throw(u))}catch(p){o(p)}},l=u=>u.done?n(u.value):Promise.resolve(u.value).then(r,s);l((e=e.apply(i,t)).next())});var tn=Ot((Jo,ai)=>{(function(i,t){typeof define=="function"&&define.amd?define(t):typeof ai=="object"&&ai.exports?ai.exports=t():i.EvEmitter=t()})(typeof window!="undefined"?window:Jo,function(){"use strict";function i(){}var t=i.prototype;return t.on=function(e,n){if(!(!e||!n)){var o=this._events=this._events||{},r=o[e]=o[e]||[];return r.indexOf(n)==-1&&r.push(n),this}},t.once=function(e,n){if(!(!e||!n)){this.on(e,n);var o=this._onceEvents=this._onceEvents||{},r=o[e]=o[e]||{};return r[n]=!0,this}},t.off=function(e,n){var o=this._events&&this._events[e];if(!(!o||!o.length)){var r=o.indexOf(n);return r!=-1&&o.splice(r,1),this}},t.emitEvent=function(e,n){var o=this._events&&this._events[e];if(!(!o||!o.length)){o=o.slice(0),n=n||[];for(var r=this._onceEvents&&this._onceEvents[e],s=0;s{(function(i,t){typeof define=="function"&&define.amd?define(t):typeof li=="object"&&li.exports?li.exports=t():i.getSize=t()})(window,function(){"use strict";function t(d){var y=parseFloat(d),E=d.indexOf("%")==-1&&!isNaN(y);return E&&y}function e(){}var n=typeof console=="undefined"?e:function(d){console.error(d)},o=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],r=o.length;function s(){for(var d={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},y=0;y