Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove EXPOSE_HEADER option #97

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ Package settings are added in your ``settings.py``:
'GUID_HEADER_NAME': 'Correlation-ID',
'VALIDATE_GUID': True,
'RETURN_HEADER': True,
'EXPOSE_HEADER': True,
'INTEGRATIONS': [],
'IGNORE_URLS': [],
'UUID_LENGTH': 32,
Expand All @@ -158,13 +157,6 @@ Package settings are added in your ``settings.py``:

Default: True

* :code:`EXPOSE_HEADER`
Whether to return :code:`Access-Control-Expose-Headers` for the GUID header if
:code:`RETURN_HEADER` is :code:`True`, has no effect if :code:`RETURN_HEADER` is :code:`False`.
This is allows the JavaScript Fetch API to access the header when CORS is enabled.

Default: True

* :code:`INTEGRATIONS`
Whether to enable any custom or available integrations with :code:`django_guid`.
As an example, using :code:`SentryIntegration()` as an integration would set Sentry's :code:`transaction_id` to
Expand Down Expand Up @@ -284,3 +276,44 @@ Simply add django_guid to your loggers in the project, like in the example below
}

This is especially useful when implementing the package, if you plan to pass existing GUIDs to the middleware, as misconfigured GUIDs will not raise exceptions, but will generate warning logs.

******************
CORS Configuration
******************

When calling the API from a browser in JavaScript code, and using cross-origin resource sharing, you must configure your server to allow the Correlation-ID property in inbound requests, and to return a response which allows the browser to make use of the header in scripts via the `Access-Control-Allow-Headers` and `Access-Control-Expose-Headers` respectively. By making use of the popular `django-cors-headers <https://pypi.org/project/django-cors-headers/>`__ package, you can expose the Correlation-ID easily with configuration by adding the following to your Django ``settings.py`` file:

.. code-block:: python

INSTALLED_APPS = [
...,
'django_guid',
'corsheaders',
...,
]

MIDDLEWARE = [
...,
'django_guid.middleware.guid_middleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]

DJANGO_GUID = {
...,
'GUID_HEADER_NAME': 'Correlation-ID',
...,
}

CORS_ALLOWED_ORIGINS = [
"https://example.com",
]

CORS_ALLOW_HEADERS = list(default_headers) + [
DJANGO_GUID['GUID_HEADER_NAME'],
]

CORS_EXPOSE_HEADERS = [
DJANGO_GUID['GUID_HEADER_NAME'],
]
6 changes: 0 additions & 6 deletions django_guid/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ def guid_header_name(self) -> str:
def return_header(self) -> bool:
return self.settings.get('RETURN_HEADER', True)

@property
def expose_header(self) -> bool:
return self.settings.get('EXPOSE_HEADER', True)

@property
def ignore_urls(self) -> List[str]:
return list({url.strip('/') for url in self.settings.get('IGNORE_URLS', [])})
Expand Down Expand Up @@ -73,8 +69,6 @@ def validate(self) -> None:
raise ImproperlyConfigured('GUID_HEADER_NAME must be a string') # Note: Case insensitive
if not isinstance(self.return_header, bool):
raise ImproperlyConfigured('RETURN_HEADER must be a boolean')
if not isinstance(self.expose_header, bool):
raise ImproperlyConfigured('EXPOSE_HEADER must be a boolean')
if not isinstance(self.integrations, (list, tuple)):
raise ImproperlyConfigured('INTEGRATIONS must be an array')
if not isinstance(self.settings.get('IGNORE_URLS', []), (list, tuple)):
Expand Down
2 changes: 0 additions & 2 deletions django_guid/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ def process_outgoing_request(response: 'HttpResponse', request: 'HttpRequest') -
if not ignored_url(request=request):
if settings.return_header:
response[settings.guid_header_name] = guid.get() # Adds the GUID to the response header
if settings.expose_header:
response['Access-Control-Expose-Headers'] = settings.guid_header_name

# Run tear down for all the integrations
for integration in settings.integrations:
Expand Down
8 changes: 0 additions & 8 deletions docs/README_PYPI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ Package settings are added in your ``settings.py``:
'GUID_HEADER_NAME': 'Correlation-ID',
'VALIDATE_GUID': True,
'RETURN_HEADER': True,
'EXPOSE_HEADER': True,
'INTEGRATIONS': [],
'IGNORE_URLS': [],
'UUID_LENGTH': 32,
Expand All @@ -143,13 +142,6 @@ Package settings are added in your ``settings.py``:

Default: True

* :code:`EXPOSE_HEADER`
Whether to return :code:`Access-Control-Expose-Headers` for the GUID header if
:code:`RETURN_HEADER` is :code:`True`, has no effect if :code:`RETURN_HEADER` is :code:`False`.
This is allows the JavaScript Fetch API to access the header when CORS is enabled.

Default: True

* :code:`INTEGRATIONS`
Whether to enable any custom or available integrations with :code:`django_guid`.
As an example, using :code:`SentryIntegration()` as an integration would set Sentry's :code:`transaction_id` to
Expand Down
10 changes: 0 additions & 10 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Default settings are shown below:
'GUID_HEADER_NAME': 'Correlation-ID',
'VALIDATE_GUID': True,
'RETURN_HEADER': True,
'EXPOSE_HEADER': True,
'INTEGRATIONS': [],
'UUID_LENGTH': 32,
'UUID_FORMAT': 'hex',
Expand Down Expand Up @@ -49,15 +48,6 @@ Whether to return the GUID (Correlation-ID) as a header in the response or not.
It will have the same name as the :code:`GUID_HEADER_NAME` setting.


EXPOSE_HEADER
-------------
* **Default**: ``True``
* **Type**: ``boolean``

Whether to return :code:`Access-Control-Expose-Headers` for the GUID header if
:code:`RETURN_HEADER` is :code:`True`, has no effect if :code:`RETURN_HEADER` is :code:`False`.
This is allows the JavaScript Fetch API to access the header when CORS is enabled.

INTEGRATIONS
------------
* **Default**: ``[]``
Expand Down
67 changes: 0 additions & 67 deletions tests/functional/test_sync_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,73 +176,6 @@ def test_no_return_header_and_drf_url(client, caplog, mock_uuid, monkeypatch):
assert not response.get('Correlation-ID')


def test_no_expose_header_return_header_true(client, monkeypatch):
"""
Tests that it does not return the Access-Control-Allow-Origin when EXPOSE_HEADER is set to False
and RETURN_HEADER is True
"""
from django.conf import settings as django_settings

mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['EXPOSE_HEADER'] = False
mocked_settings['RETURN_HEADER'] = True
with override_settings(DJANGO_GUID=mocked_settings):
settings = Settings()
monkeypatch.setattr('django_guid.middleware.settings', settings)
response = client.get('/api')
assert not response.get('Access-Control-Expose-Headers')


def test_expose_header_return_header_true(client, monkeypatch):
"""
Tests that it does return the Access-Control-Allow-Origin when EXPOSE_HEADER is set to True
and RETURN_HEADER is True
"""
from django.conf import settings as django_settings

mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['EXPOSE_HEADER'] = True
with override_settings(DJANGO_GUID=mocked_settings):
settings = Settings()
monkeypatch.setattr('django_guid.middleware.settings', settings)
response = client.get('/api')
assert response.get('Access-Control-Expose-Headers')


def test_no_expose_header_return_header_false(client, monkeypatch):
"""
Tests that it does not return the Access-Control-Allow-Origin when EXPOSE_HEADER is set to False
and RETURN_HEADER is False
"""
from django.conf import settings as django_settings

mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['EXPOSE_HEADER'] = False
mocked_settings['RETURN_HEADER'] = False
with override_settings(DJANGO_GUID=mocked_settings):
settings = Settings()
monkeypatch.setattr('django_guid.middleware.settings', settings)
response = client.get('/api')
assert not response.get('Access-Control-Expose-Headers')


def test_expose_header_return_header_false(client, monkeypatch):
"""
Tests that it does not return the Access-Control-Allow-Origin when EXPOSE_HEADER is set to True
and RETURN_HEADER is False
"""
from django.conf import settings as django_settings

mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['EXPOSE_HEADER'] = True
mocked_settings['RETURN_HEADER'] = False
with override_settings(DJANGO_GUID=mocked_settings):
settings = Settings()
monkeypatch.setattr('django_guid.middleware.settings', settings)
response = client.get('/api')
assert not response.get('Access-Control-Expose-Headers')


def test_cleanup_signal(client, caplog, monkeypatch):
"""
Tests that a request cleans up a request after finishing.
Expand Down
9 changes: 0 additions & 9 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,11 @@ def test_invalid_return_header_setting():
Settings().validate()


def test_invalid_expose_header_setting():
mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['EXPOSE_HEADER'] = 'string'
with override_settings(DJANGO_GUID=mocked_settings):
with pytest.raises(ImproperlyConfigured, match='EXPOSE_HEADER must be a boolean'):
Settings().validate()


def test_valid_settings():
mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['VALIDATE_GUID'] = False
mocked_settings['GUID_HEADER_NAME'] = 'Correlation-ID-TEST'
mocked_settings['RETURN_HEADER'] = False
mocked_settings['EXPOSE_HEADER'] = False
with override_settings(DJANGO_GUID=mocked_settings):
assert not Settings().validate_guid
assert Settings().guid_header_name == 'Correlation-ID-TEST'
Expand Down