Skip to content
Merged
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ Version numbers should follow https://semver.org/spec/v2.0.0.html

## [Unreleased]

### Added

- Introduced `SCHEMA_GRAPH_VISIBLE` setting as a way to control access to the
`Schema` view. We will continue to default to using `DEBUG`.


### Changed

- We no longer use a decorator on the `Schema` view to override `dispatch`, and
now override it directly.
- It is now possible to control access to the `Schema` view by subclassing and
overriding the `access_permitted` function.


## [2.0.0] - 2022-08-01

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ urlpatterns += [

Browse to `/schema/` (assuming that's where you put it in your URLs).

Note: `DEBUG` mode is required, on the assumption that you don't want to leak
sensitive information about your website outside of local development.
You can control access to this page using the `SCHEMA_GRAPH_VISIBLE` setting,
or by subclassing `schema_graph.views.Schema` and overriding `access_permitted`.
By default the page is only visible when `DEBUG` is `True`,
because we assume that you don't want to leak sensitive information about your
website outside of local development.

## Support

Expand Down
26 changes: 16 additions & 10 deletions schema_graph/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,30 @@

from django.conf import settings
from django.http import Http404
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

from schema_graph.schema import get_schema


def debug_required(view_function):
def view_wrapper(request, *args, **kwargs):
if not settings.DEBUG:
raise Http404()
return view_function(request, *args, **kwargs)
class Schema(TemplateView):
template_name = "schema_graph/schema.html"

return view_wrapper
def access_permitted(self):
"""
When this returns True, the schema graph page is accessible.

We look for the setting `SCHEMA_GRAPH_VISIBLE`, and fall back to `DEBUG`.

@method_decorator(debug_required, name="dispatch")
class Schema(TemplateView):
template_name = "schema_graph/schema.html"
To control this on a per-request basis, override this function in a subclass.
The request will be accessible using `self.request`.
"""

return getattr(settings, "SCHEMA_GRAPH_VISIBLE", settings.DEBUG)

def dispatch(self, request):
if not self.access_permitted():
raise Http404()
return super().dispatch(request)

def get_context_data(self, **kwargs):
schema = get_schema()
Expand Down
28 changes: 22 additions & 6 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,35 @@ def test_content():
assert response.rendered_content.startswith("<!doctype html>")


def test_debug():
"""Schema should be accessible in DEBUG mode."""
@pytest.mark.parametrize(
"settings_dict",
[
# SCHEMA_GRAPH_VISIBLE takes priority over DEBUG.
{"DEBUG": True, "SCHEMA_GRAPH_VISIBLE": True},
{"DEBUG": False, "SCHEMA_GRAPH_VISIBLE": True},
{"DEBUG": True},
],
)
def test_accessible_settings(settings_dict):
view = Schema.as_view()
request = create_request()
with override_settings(DEBUG=True):
with override_settings(**settings_dict):
response = view(request)
assert response.status_code == 200


def test_no_debug():
"""Schema should be inaccessible outwith DEBUG mode."""
@pytest.mark.parametrize(
"settings_dict",
[
# SCHEMA_GRAPH_VISIBLE takes priority over DEBUG.
{"DEBUG": True, "SCHEMA_GRAPH_VISIBLE": False},
{"DEBUG": False, "SCHEMA_GRAPH_VISIBLE": False},
{"DEBUG": False},
],
)
def test_inaccessible_settings(settings_dict):
view = Schema.as_view()
request = create_request()
with override_settings(DEBUG=False):
with override_settings(**settings_dict):
with pytest.raises(Http404):
view(request)