Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ jobs:
export $(grep -v '^#' tests/.env | xargs -d '\n')
cd dashboard_viewer
python manage.py test --exclude-tag third-party-app
#SINGLE_APPLICATION_MODE=n MAIN_APPLICATION_HOST=mainapp.host.com python manage.py test --tag third-party-app
SINGLE_APPLICATION_MODE=n MAIN_APPLICATION_HOST=mainapp.host.com python manage.py test --tag third-party-app
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# django local settings
local_settings.py

# documentation temporary files
docs/src/_book

Expand Down
18 changes: 14 additions & 4 deletions dashboard_viewer/dashboard_viewer/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,16 @@ def constance_updated(key, old_value, **_):
TEST_RUNNER = "dashboard_viewer.runners.CeleryTestSuiteRunner"


# required since django 3.2
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"


try:
import local_settings # noqa
from local_settings import * # noqa
except ImportError:
pass

# Variables that allow restricting the access to the uploader app if this Django
# app is being used as a third-party tool and is being iframed.
SINGLE_APPLICATION_MODE = strtobool(os.environ.get("SINGLE_APPLICATION_MODE", "y")) == 1
Expand All @@ -337,7 +347,7 @@ def constance_updated(key, old_value, **_):
"Only include the hostname part of the URL."
)

X_FRAME_OPTIONS = f"ALLOW-FROM https://{MAIN_APPLICATION_HOST}/"

# required since django 3.2
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
if (
len(ALLOWED_HOSTS) > 1 or ALLOWED_HOSTS[0] != "*"
) and MAIN_APPLICATION_HOST not in ALLOWED_HOSTS:
ALLOWED_HOSTS.append(MAIN_APPLICATION_HOST)
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.core.cache import cache
from django.core.management.base import BaseCommand

from materialized_queries_manager.utils import refresh

logger = logging.getLogger(__name__)
Expand Down
1 change: 1 addition & 0 deletions dashboard_viewer/materialized_queries_manager/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.core import serializers
from django.core.cache import cache
from django.db import connections, ProgrammingError, router, transaction

from materialized_queries_manager.models import MaterializedQuery
from materialized_queries_manager.utils import refresh

Expand Down
3 changes: 2 additions & 1 deletion dashboard_viewer/materialized_queries_manager/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.core.cache import cache
from django.db import connections
from materialized_queries_manager.models import MaterializedQuery
from redis_rw_lock import RWLock

from materialized_queries_manager.models import MaterializedQuery


def refresh(logger, db_id=None, query_set=None):
# Only one worker can update the materialized views at the same time -> same as -> only one thread
Expand Down
8 changes: 4 additions & 4 deletions dashboard_viewer/uploader/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from django.conf import settings
from django.http import HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.clickjacking import xframe_options_exempt


def uploader_decorator(view_func):
Expand All @@ -12,14 +12,14 @@ def uploader_decorator(view_func):
If not response with 403
Else don't do any verification
"""
wrapped_view = csrf_exempt(view_func)
if not settings.SINGLE_APPLICATION_MODE:
wrapped_view = xframe_options_exempt(view_func)

def check_host(request, *args, **kwargs):
if request.get_host() != settings.MAIN_APPLICATION_HOST:
return HttpResponseForbidden()
return view_func(request, *args, **kwargs)

wrapped_view = wraps(wrapped_view)(check_host)
return wraps(wrapped_view)(check_host)

return wrapped_view
return view_func
1 change: 1 addition & 0 deletions dashboard_viewer/uploader/file_handler/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.conf import settings
from django.db import connections
from sqlalchemy import create_engine

from uploader.models import (
AchillesResults,
AchillesResultsArchive,
Expand Down
2 changes: 1 addition & 1 deletion dashboard_viewer/uploader/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from django.core import serializers
from django.core.cache import cache
from django.db import router, transaction
from materialized_queries_manager.utils import refresh
from redis_rw_lock import RWLock

from materialized_queries_manager.utils import refresh
from .file_handler.checks import extract_data_from_uploaded_file
from .file_handler.updates import update_achilles_results_data
from .models import AchillesResults, PendingUpload, UploadHistory
Expand Down
10 changes: 0 additions & 10 deletions dashboard_viewer/uploader/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,11 @@ def test_not_block_if_correct_host(self):
response = self.client.get("/uploader/test/", HTTP_HOST="mainapp.host.com")

self.assertEqual(200, response.status_code)
self.assertTrue(response.has_header("X-Frame-Options"))
self.assertEqual(
"ALLOW-FROM HTTPS://MAINAPP.HOST.COM/", response["X-Frame-Options"]
)

def test_not_block_other_urls(self):
response = self.client.get("/admin/login/", HTTP_HOST="thisapp.host.com")

self.assertEqual(200, response.status_code)
self.assertTrue(response.has_header("X-Frame-Options"))
self.assertEqual(
"ALLOW-FROM HTTPS://MAINAPP.HOST.COM/", response["X-Frame-Options"]
)


class UploaderNonRestrictedAccess(TestCase):
Expand All @@ -74,8 +66,6 @@ def test_not_block_if_single_application(self):
response = self.client.get("/uploader/test/", HTTP_HOST="some.domain.com")

self.assertEqual(200, response.status_code)
if response.has_header("X-Frame-Options"):
self.assertNotIn("ALLOW-FROM ", response.get("X-Frame-Options"))


class DataSourceCreator:
Expand Down
10 changes: 4 additions & 6 deletions dashboard_viewer/uploader/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.html import format_html, mark_safe
from django.views.decorators.clickjacking import xframe_options_exempt
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

from materialized_queries_manager.tasks import refresh_materialized_views_task
from materialized_queries_manager.models import MaterializedQuery
from materialized_queries_manager.tasks import refresh_materialized_views_task
from .decorators import uploader_decorator
from .forms import AchillesResultsForm, EditSourceForm, SourceForm
from .models import Country, DataSource, PendingUpload, UploadHistory
Expand All @@ -22,7 +21,6 @@


@uploader_decorator
@xframe_options_exempt
def upload_achilles_results(request, *args, **kwargs):
data_source = kwargs.get("data_source")
try:
Expand Down Expand Up @@ -157,7 +155,6 @@ def _leave_valid_fields_values_only(request, initial, aux_form):


@uploader_decorator
@xframe_options_exempt
def create_data_source(request, *_, **kwargs):
data_source = kwargs.get("data_source")
if request.method == "GET":
Expand Down Expand Up @@ -239,7 +236,6 @@ def create_data_source(request, *_, **kwargs):


@uploader_decorator
@xframe_options_exempt
def edit_data_source(request, *_, **kwargs):
data_source = kwargs.get("data_source")
try:
Expand Down Expand Up @@ -307,7 +303,9 @@ def partial_update(self, request, *_, **__):
serializer.is_valid(raise_exception=True)
serializer.save()

refresh_materialized_views_task.delay([obj.matviewname for obj in MaterializedQuery.objects.all()])
refresh_materialized_views_task.delay(
[obj.matviewname for obj in MaterializedQuery.objects.all()]
)

if getattr(instance, "_prefetched_objects_cache", None):
# If 'prefetch_related' has been applied to a queryset, we need to
Expand Down
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
combine_as_imports = true
include_trailing_comma = true
known_third_party = bootstrap_datepicker_plus,celery,django,model_utils,rest_framework,uploader
known_first_party = dashboard_viewer,materialized_queries_manager,tabsManager,uploader
no_lines_before = LOCALFOLDER
multi_line_output = 3
order_by_type = false
extend_skip = migrations