From 7ba6e320e744bff0c5dca452dfc10a8f37b1707b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 21 Mar 2017 13:53:07 -0400 Subject: [PATCH] Fixes #843: Implemented CORS headers for API --- docs/configuration/optional-settings.md | 16 +++++++++ netbox/netbox/configuration.example.py | 47 +++++++++++++++---------- netbox/netbox/settings.py | 22 ++++++++---- requirements.txt | 1 + 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/docs/configuration/optional-settings.md b/docs/configuration/optional-settings.md index 9e466ddc1..ed5d2c03c 100644 --- a/docs/configuration/optional-settings.md +++ b/docs/configuration/optional-settings.md @@ -38,6 +38,22 @@ BASE_PATH = 'netbox/' --- +## CORS_ORIGIN_ALLOW_ALL + +Default: False + +If True, cross-origin resource sharing (CORS) requests will be accepted from all origins. If False, a whitelist will be used (see below). + +--- + +## CORS_ORIGIN_WHITELIST + +## CORS_ORIGIN_REGEX_WHITELIST + +These settings specify a list of origins that are authorized to make cross-site API requests. Use `CORS_ORIGIN_WHITELIST` to define a list of exact hostnames, or `CORS_ORIGIN_REGEX_WHITELIST` to define a set of regular expressions. (These settings have no effect if `CORS_ORIGIN_ALLOW_ALL` is True.) + +--- + ## DEBUG Default: False diff --git a/netbox/netbox/configuration.example.py b/netbox/netbox/configuration.example.py index b85fcafbb..f185a68c7 100644 --- a/netbox/netbox/configuration.example.py +++ b/netbox/netbox/configuration.example.py @@ -38,6 +38,26 @@ ADMINS = [ # ['John Doe', 'jdoe@example.com'], ] +# Optionally display a persistent banner at the top and/or bottom of every page. To display the same content in both +# banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. +BANNER_TOP = '' +BANNER_BOTTOM = '' + +# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: +# BASE_PATH = 'netbox/' +BASE_PATH = '' + +# API Cross-Origin Resource Sharing (CORS) settings. If CORS_ORIGIN_ALLOW_ALL is set to True, all origins will be +# allowed. Otherwise, define a list of allowed origins using either CORS_ORIGIN_WHITELIST or +# CORS_ORIGIN_REGEX_WHITELIST. For more information, see https://github.com/ottoyiu/django-cors-headers +CORS_ORIGIN_ALLOW_ALL = False +CORS_ORIGIN_WHITELIST = [ + # 'hostname.example.com', +] +CORS_ORIGIN_REGEX_WHITELIST = [ + # r'^(https?://)?(\w+\.)?example\.com$', +] + # Email settings EMAIL = { 'SERVER': 'localhost', @@ -48,24 +68,28 @@ EMAIL = { 'FROM_EMAIL': '', } +# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table +# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. +ENFORCE_GLOBAL_UNIQUE = False + # Setting this to True will permit only authenticated users to access any part of NetBox. By default, anonymous users # are permitted to access most data in NetBox (excluding secrets) but not make any changes. LOGIN_REQUIRED = False -# Base URL path if accessing NetBox within a directory. For example, if installed at http://example.com/netbox/, set: -# BASE_PATH = 'netbox/' -BASE_PATH = '' - # Setting this to True will display a "maintenance mode" banner at the top of every page. MAINTENANCE_MODE = False -# Credentials that NetBox will use to access live devices. +# Credentials that NetBox will use to access live devices (future use). NETBOX_USERNAME = '' NETBOX_PASSWORD = '' # Determine how many objects to display per page within a list. (Default: 50) PAGINATE_COUNT = 50 +# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to +# prefer IPv4 instead. +PREFER_IPV4 = False + # Time zone (default: UTC) TIME_ZONE = 'UTC' @@ -77,16 +101,3 @@ TIME_FORMAT = 'g:i a' SHORT_TIME_FORMAT = 'H:i:s' DATETIME_FORMAT = 'N j, Y g:i a' SHORT_DATETIME_FORMAT = 'Y-m-d H:i' - -# Optionally display a persistent banner at the top and/or bottom of every page. To display the same content in both -# banners, define BANNER_TOP and set BANNER_BOTTOM = BANNER_TOP. -BANNER_TOP = '' -BANNER_BOTTOM = '' - -# When determining the primary IP address for a device, IPv6 is preferred over IPv4 by default. Set this to True to -# prefer IPv4 instead. -PREFER_IPV4 = False - -# Enforcement of unique IP space can be toggled on a per-VRF basis. To enforce unique IP space within the global table -# (all prefixes and IP addresses not assigned to a VRF), set ENFORCE_GLOBAL_UNIQUE to True. -ENFORCE_GLOBAL_UNIQUE = False diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index f62570d2b..aeec93f06 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -8,19 +8,22 @@ from django.core.exceptions import ImproperlyConfigured try: from netbox import configuration except ImportError: - raise ImproperlyConfigured("Configuration file is not present. Please define netbox/netbox/configuration.py per " - "the documentation.") + raise ImproperlyConfigured( + "Configuration file is not present. Please define netbox/netbox/configuration.py per the documentation." + ) VERSION = '2.0.0-dev' # Import local configuration +ALLOWED_HOSTS = DATABASE = SECRET_KEY = None for setting in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY']: try: globals()[setting] = getattr(configuration, setting) except AttributeError: - raise ImproperlyConfigured("Mandatory setting {} is missing from configuration.py. Please define it per the " - "documentation.".format(setting)) + raise ImproperlyConfigured( + "Mandatory setting {} is missing from configuration.py.".format(setting) + ) # Default configurations ADMINS = getattr(configuration, 'ADMINS', []) @@ -45,6 +48,9 @@ BANNER_TOP = getattr(configuration, 'BANNER_TOP', False) BANNER_BOTTOM = getattr(configuration, 'BANNER_BOTTOM', False) PREFER_IPV4 = getattr(configuration, 'PREFER_IPV4', False) ENFORCE_GLOBAL_UNIQUE = getattr(configuration, 'ENFORCE_GLOBAL_UNIQUE', False) +CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False) +CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', []) +CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', []) CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS # Attempt to import LDAP configuration if it has been defined @@ -73,8 +79,10 @@ if LDAP_CONFIGURED: logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.DEBUG) except ImportError: - raise ImproperlyConfigured("LDAP authentication has been configured, but django-auth-ldap is not installed. " - "You can remove netbox/ldap_config.py to disable LDAP.") + raise ImproperlyConfigured( + "LDAP authentication has been configured, but django-auth-ldap is not installed. You can remove " + "netbox/ldap_config.py to disable LDAP." + ) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -102,6 +110,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', + 'corsheaders', 'debug_toolbar', 'django_tables2', 'mptt', @@ -120,6 +129,7 @@ INSTALLED_APPS = ( # Middleware MIDDLEWARE = ( 'debug_toolbar.middleware.DebugToolbarMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', diff --git a/requirements.txt b/requirements.txt index 2c6044f73..b732ab1b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ cffi>=1.8 cryptography>=1.4 Django>=1.10 +django-cors-headers>=2.0 django-debug-toolbar>=1.6 django-filter>=1.0.1 django-mptt==0.8.7