From df0686a1bd15ea158c0dc51e535306b1e9fe0de1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 11 Apr 2019 22:01:26 -0400 Subject: [PATCH] Implement custom auth backend and EXEMPT_VIEW_PERMISSIONS setting --- docs/configuration/optional-settings.md | 24 +++++++++++++++++++++ netbox/netbox/configuration.example.py | 8 +++++++ netbox/netbox/settings.py | 9 ++++++-- netbox/utilities/auth_backends.py | 28 +++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 netbox/utilities/auth_backends.py diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index f8bd70e88..f6725a92e 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -89,6 +89,30 @@ In order to send email, NetBox needs an email server configured. The following i --- +## EXEMPT_VIEW_PERMISSIONS + +Default: Empty list + +A list of models to exempt from the enforcement of view permissions. Models listed here will be viewable by all users and by anonymous users. + +List models in the form `.`. For example: + +``` +EXEMPT_VIEW_PERMISSIONS = [ + 'dcim.site', + 'dcim.region', + 'ipam.prefix', +] +``` + +To exempt _all_ models from view permission enforcement, set the following. (Note that `EXEMPT_VIEW_PERMISSIONS` must be an iterable.) + +``` +EXEMPT_VIEW_PERMISSIONS = ['*'] +``` + +--- + # ENFORCE_GLOBAL_UNIQUE Default: False diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index 145ebf0e6..6efecdf35 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -83,6 +83,14 @@ EMAIL = { # (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. ENFORCE_GLOBAL_UNIQUE = False +# Exempt certain models from the enforcement of view permissions. Models listed here will be viewable by all users and +# by anonymous users. List models in the form `.`. Add '*' to this list to exempt all models. +EXEMPT_VIEW_PERMISSIONS = [ + # 'dcim.site', + # 'dcim.region', + # 'ipam.prefix', +] + # Enable custom logging. Please see the Django documentation for detailed guidance on configuring custom logs: # https://docs.djangoproject.com/en/1.11/topics/logging/ LOGGING = {} diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 86c509e1e..69a9c13f7 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -51,8 +51,9 @@ CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', []) DATE_FORMAT = getattr(configuration, 'DATE_FORMAT', 'N j, Y') DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a') DEBUG = getattr(configuration, 'DEBUG', False) -ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False) EMAIL = getattr(configuration, 'EMAIL', {}) +ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False) +EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', []) LOGGING = getattr(configuration, 'LOGGING', {}) LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False) LOGIN_TIMEOUT = getattr(configuration, 'LOGIN_TIMEOUT', None) @@ -93,7 +94,7 @@ if LDAP_CONFIGURED: # Prepend LDAPBackend to the default ModelBackend AUTHENTICATION_BACKENDS = [ 'django_auth_ldap.backend.LDAPBackend', - 'django.contrib.auth.backends.ModelBackend', + 'utilities.auth_backends.ViewExemptModelBackend', ] # Optionally disable strict certificate checking if LDAP_IGNORE_CERT_ERRORS: @@ -107,6 +108,10 @@ if LDAP_CONFIGURED: "LDAP authentication has been configured, but django-auth-ldap is not installed. You can remove " "netbox/ldap_config.py to disable LDAP." ) +else: + AUTHENTICATION_BACKENDS = [ + 'utilities.auth_backends.ViewExemptModelBackend', + ] # Database configuration.DATABASE.update({'ENGINE': 'django.db.backends.postgresql'}) diff --git a/netbox/utilities/auth_backends.py b/netbox/utilities/auth_backends.py new file mode 100644 index 000000000..54541b0b5 --- /dev/null +++ b/netbox/utilities/auth_backends.py @@ -0,0 +1,28 @@ +from django.conf import settings +from django.contrib.auth.backends import ModelBackend + + +class ViewExemptModelBackend(ModelBackend): + """ + Custom implementation of Django's stock ModelBackend which allows for the exemption of arbitrary models from view + permission enforcement. + """ + def has_perm(self, user_obj, perm, obj=None): + + # If this is a view permission, check whether the model has been exempted from enforcement + try: + app, codename = perm.split('.') + action, model = codename.split('_') + if action == 'view': + if ( + # All models are exempt from view permission enforcement + '*' in settings.EXEMPT_VIEW_PERMISSIONS + ) or ( + # This specific model is exempt from view permission enforcement + '{}.{}'.format(app, model) in settings.EXEMPT_VIEW_PERMISSIONS + ): + return True + except ValueError: + pass + + return super().has_perm(user_obj, perm, obj)