diff --git a/README.md b/README.md index 5caa9e3..2334fba 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,353 @@ PLUGINS = [ ] ``` +### Validator installation +By default, the migration will install the validator for all models. If you're using a statically configured NetBox +instance you can use this `CUSTOM_VALIDATORS` block to install it for all models: +
+Show configuration snippet + +```python +CUSTOM_VALIDATORS = { + "circuits.circuit": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "circuits.circuittermination": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "circuits.circuittype": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "circuits.provider": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "circuits.provideraccount": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "circuits.providernetwork": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.cable": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.cablepath": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.cabletermination": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.consoleport": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.consoleporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.consoleserverport": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.consoleserverporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.device": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.devicebay": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.devicebaytemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.devicerole": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.devicetype": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.frontport": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.frontporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.interface": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.interfacetemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.inventoryitem": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.inventoryitemrole": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.inventoryitemtemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.location": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.manufacturer": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.module": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.modulebay": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.modulebaytemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.moduletype": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.platform": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.powerfeed": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.poweroutlet": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.poweroutlettemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.powerpanel": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.powerport": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.powerporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.rack": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.rackreservation": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.rackrole": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.rearport": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.rearporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.region": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.site": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.sitegroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.virtualchassis": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "dcim.virtualdevicecontext": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.bookmark": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.branch": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.cachedvalue": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.configcontext": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.configtemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.customfield": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.customfieldchoiceset": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.customlink": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.dashboard": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.eventrule": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.exporttemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.imageattachment": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.journalentry": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.objectchange": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.reportmodule": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.savedfilter": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.script": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.scriptmodule": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.stagedchange": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.tag": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.taggeditem": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "extras.webhook": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.aggregate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.asn": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.asnrange": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.fhrpgroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.fhrpgroupassignment": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.ipaddress": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.iprange": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.prefix": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.rir": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.role": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.routetarget": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.service": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.servicetemplate": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.vlan": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.vlangroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "ipam.vrf": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.contact": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.contactassignment": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.contactgroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.contactrole": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.tenant": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "tenancy.tenantgroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.cluster": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.clustergroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.clustertype": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.virtualdisk": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.virtualmachine": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "virtualization.vminterface": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.ikepolicy": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.ikeproposal": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.ipsecpolicy": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.ipsecprofile": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.ipsecproposal": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.l2vpn": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.l2vpntermination": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.tunnel": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.tunnelgroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "vpn.tunneltermination": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "wireless.wirelesslan": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "wireless.wirelesslangroup": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ], + "wireless.wirelesslink": [ + "netbox_field_permissions.validators.FieldPermissionValidator" + ] +} +``` +
+ +You can also view updated configuration on the `Plugins -> Field Permissions -> Manage Validator` page. + ## Features ### Disallow changes to any user accessible field diff --git a/netbox_field_permissions/migrations/0001_initial.py b/netbox_field_permissions/migrations/0001_initial.py index 8764be9..09d01fa 100644 --- a/netbox_field_permissions/migrations/0001_initial.py +++ b/netbox_field_permissions/migrations/0001_initial.py @@ -34,7 +34,8 @@ def get_current_config(apps): def write_static_configuration_messsage(): sys.stdout.write( colorize( - "\n Static NetBox configuration detected. Please install customer validator manually!!", + "\n Static NetBox configuration detected. Please install plugin validator manually by " + "adding 'netbox_field_permissions.validators.FieldPermissionValidator' for each model.", fg="yellow", opts=("bold",), ) diff --git a/netbox_field_permissions/navigation.py b/netbox_field_permissions/navigation.py index bd2a47f..6021f9d 100644 --- a/netbox_field_permissions/navigation.py +++ b/netbox_field_permissions/navigation.py @@ -10,7 +10,7 @@ title="Add", icon_class="mdi mdi-plus-thick", permissions=("netbox_field_permissions.add_fieldpermission",), - ) + ), ], ), ) diff --git a/netbox_field_permissions/templates/netbox_field_permissions/fieldpermission_list.html b/netbox_field_permissions/templates/netbox_field_permissions/fieldpermission_list.html index 444cbae..1962b0b 100644 --- a/netbox_field_permissions/templates/netbox_field_permissions/fieldpermission_list.html +++ b/netbox_field_permissions/templates/netbox_field_permissions/fieldpermission_list.html @@ -1,7 +1,7 @@ {% extends "generic/object_list.html" %} {% block extra_controls %} -{% if user.is_superuser %} +{% if user.is_staff %} Manage Validators {% endif %} diff --git a/netbox_field_permissions/templates/netbox_field_permissions/manage.html b/netbox_field_permissions/templates/netbox_field_permissions/manage.html index 6e4b795..5e16aeb 100644 --- a/netbox_field_permissions/templates/netbox_field_permissions/manage.html +++ b/netbox_field_permissions/templates/netbox_field_permissions/manage.html @@ -1,4 +1,5 @@ {% extends 'generic/_base.html' %} +{% load helpers %} {% load render_table from django_tables2 %} {% block title %}Manage Field Permission Plugin{% endblock title %} {% block subtitle %} @@ -6,17 +7,70 @@ {% endblock subtitle %} {% block control-buttons %} +{% if not validators_statically_configured %}
{% csrf_token %} + Install validators + + Remove validators +
+{% endif %} {% endblock %} + +{% block tabs %} +{% if validators_statically_configured %} + +{% endif %} +{% endblock tabs %} + {% block content %}
+ {% if validators_statically_configured %} + + {% endif %} + {% if show_manual_install %} +
+
+

Install custom validator

+
+ {% spaceless %} +
+                    CUSTOM_VALIDATORS = {{ install_validators|json }}
+                
+ {% endspaceless %} +
+
+
+
+
+

Remove custom validator

+
+ {% spaceless %} +
+                    CUSTOM_VALIDATORS = {{ uninstall_validators|json }}
+                
+ {% endspaceless %} +
+
+
+ {% else %} {% for app, table in tables.items %}
@@ -27,5 +81,6 @@
{% endfor %} + {% endif %}
{% endblock content %} diff --git a/netbox_field_permissions/utilities.py b/netbox_field_permissions/utilities.py index d68bd10..ea21917 100644 --- a/netbox_field_permissions/utilities.py +++ b/netbox_field_permissions/utilities.py @@ -3,6 +3,7 @@ from collections import defaultdict from core.models import ConfigRevision, ObjectType +from django.conf import settings from django.db import models from netbox.config import get_config @@ -42,6 +43,10 @@ def validator_status_by_content_type(): # Use the active config so no database has to be hit. config = get_config().config.get("CUSTOM_VALIDATORS", {}) + # If NetBox is statically configured use the validators from the settings. + if hasattr(settings, "CUSTOM_VALIDATORS"): + config = settings.CUSTOM_VALIDATORS + data = defaultdict(list) for ct in content_types: @@ -59,13 +64,15 @@ def validator_status_by_content_type(): return data -# Sorry Jeremy, I'm sure you're not going to like the following. -def install_validator(): +def get_content_types_for_validator(): content_types = ObjectType.objects.filter(FIELDPERMISSION_OBJECT_TYPES).order_by( "app_label" ) - install_for_content_types = [f"{ct.app_label}.{ct.model}" for ct in content_types] + return [f"{ct.app_label}.{ct.model}" for ct in content_types] + +# Sorry Jeremy, I'm sure you're not going to like the following. +def install_validator(): config_version = get_config().version config_qs = ConfigRevision.objects.filter(pk=config_version) @@ -74,7 +81,7 @@ def install_validator(): validators = initial.get("CUSTOM_VALIDATORS", {}).copy() needs_update = False - for ct in install_for_content_types: + for ct in get_content_types_for_validator(): if ct not in validators: needs_update = True validators[ct] = (FIELDPERMISSION_VALIDATOR,) diff --git a/netbox_field_permissions/views.py b/netbox_field_permissions/views.py index ac65dbe..db8ca2a 100644 --- a/netbox_field_permissions/views.py +++ b/netbox_field_permissions/views.py @@ -1,5 +1,6 @@ from collections import defaultdict +from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import UserPassesTestMixin from django.shortcuts import redirect @@ -11,6 +12,7 @@ ObjectView, ) +from netbox_field_permissions.constants import FIELDPERMISSION_VALIDATOR from netbox_field_permissions.forms import FieldPermissionEditForm, FieldPermissionForm from netbox_field_permissions.models import FieldPermission from netbox_field_permissions.tables import ( @@ -19,6 +21,7 @@ FieldPermissionValidatorTable, ) from netbox_field_permissions.utilities import ( + get_content_types_for_validator, install_validator, uninstall_validator, validator_status_by_content_type, @@ -86,12 +89,45 @@ class FieldPermissionManageView(UserPassesTestMixin, TemplateView): template_name = "netbox_field_permissions/manage.html" def test_func(self): - return self.request.user.is_superuser + return self.request.user.is_staff def get_context_data(self, **kwargs): status = validator_status_by_content_type() + # Create install/uninstall JSON for ease of use. + current_validators = getattr(settings, "CUSTOM_VALIDATORS", {}) + + # Dict for installing validators + install_validators = current_validators.copy() + for ct in get_content_types_for_validator(): + if ct not in install_validators: + install_validators[ct] = (FIELDPERMISSION_VALIDATOR,) + else: + if FIELDPERMISSION_VALIDATOR not in install_validators[ct]: + install_validators[ct] = tuple(install_validators[ct]) + ( + FIELDPERMISSION_VALIDATOR, + ) + + # Dict for uninstalling validators + uninstall_validators = current_validators.copy() + for model, validator in uninstall_validators.copy().items(): + if FIELDPERMISSION_VALIDATOR not in validator: + continue + + # If there's only 1 validator defined just remove the key from the config + # so we don't end up with empty values. + if len(validator) == 1: + del uninstall_validators[model] + else: + uninstall_validators[model] = ( + x for x in validator if x != FIELDPERMISSION_VALIDATOR + ) + return { + "validators_statically_configured": hasattr(settings, "CUSTOM_VALIDATORS"), + "show_manual_install": "manual_install" in self.request.GET, + "install_validators": install_validators, + "uninstall_validators": uninstall_validators, "tables": { k: FieldPermissionValidatorTable(data=v, empty_text="No models in app") for k, v in status.items()