Source code for tests.integration.01_system_wide_security

import re
import subprocess
from time import sleep

import requests

from tests.test_manager import CobaltTestManagerIntegration

# URLs that do not require authentication
NON_AUTH_URLS = [
    "/accounts/loggedout",
    "/accounts/login/",
    "/accounts/activate/dummy/dummy/",
    "/accounts/password-reset-request",
    "/accounts/password_reset/",
    "/accounts/password_reset/done/",
    "/accounts/register/1/dummy",
    "/accounts/register",
    "/accounts/reset/<uidb64>/<token>/",
    "/accounts/reset/done/",
    "/accounts/signin",
    "/accounts/test_email_send",
    "/admin/login/",
    "/dashboard/help",
    "/dashboard/logged-out",
    "/events/",
    "/events/congress-listing/dummy",
    "/events/congress/get_all_congresses",
    "/events/congress/view/1",
    "/events/congress/view/1/1",
    "/events/congress/event/view-event-entries/1/1",
    "/organisations/public-profile/1",
    "/summernote/upload_attachment/",
    "/support/acceptable-use",
    "/support/contact",
    "/support/contact-logged-out",
    "/support/cookies",
    "/support/guidelines",
    "/support/acceptable-use-logged-out",
    "/support/cookies-logged-out",
    "/view",
    "/summernote/editor/<id>/",  # TODO: Double check this one
    "/api/cobalt/keycheck/v1.0",
    "/api/cobalt/system-number-lookup/v1.0",
    "/api/docs/",
    "/api/openapi.json",
    "/accounts/unregistered-preferences/dummy",
    "/masterpoints/abf-registration-card",
    "/masterpoints/abf-registration-card-htmx",
    "/404",
    "/500",
]

# URLs that we do not test
DO_NOT_TEST_URLS = [
    "/masterpoints/system_number_lookup",
    "/accounts/create-pdf-system-card/",
    "/xero/",
    "/xero/callback",
    "/xero/config",
    "/xero/initialise",
    "/xero/refresh",
    "/xero/run-xero-api",
    "/payments/statement-org-summary",
]


[docs] class TestURLsRequireLogin: """Tests all available URLs require the user to be authenticated unless specifically not required. """ def __init__(self, manager: CobaltTestManagerIntegration): self.manager = manager self.client = self.manager.client
[docs] def a1_test_all_urls(self): """It is easiest to use manage.py show_urls to get the URLs. We filter out the Django admin commands as we don't need to test Django here""" # Health should probably be removed altogether from Cobalt # We don't test API as they are mostly Posts process = subprocess.Popen( [ r"./manage.py show_urls | awk '{print $1}' | grep -v '^\/admin' | grep -v '^\/health' | grep -v '^\/api'" ], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) urls = [] # Go through the list of URLs and format them to work for line in process.stdout.readlines(): url = line.decode("utf-8").strip() if url in DO_NOT_TEST_URLS: continue # If we have a parameter, then change it to a value # ? makes the expression not greedy so it can handle multiple parameters url = re.sub("<int(.*?)>", "1", url) url = re.sub("<str(.*?)>", "dummy", url) urls.append(url) # Start with empty list of errors errors = [] # Go through URLs and test them for url in urls: if url in NON_AUTH_URLS: continue # get response. We expect to get 302 - redirect to login page, but 40x are okay too response = requests.get(f"{self.manager.base_url}{url}") if not ( # We are okay if we get a redirect or not found code response.status_code in {302, 400, 403, 404, 405} # or if the url is the login page (since upgrade to Django 5) or response.url.find("/accounts/login/") != -1 # or if the url is the logged out page or response.url.find("http://127.0.0.1:8088/view") != -1 ): errors.append(f"{url} - {response.status_code}") self.manager.save_results( status=not errors, test_name="Check URLs require authentication", test_description="We go through all URLs (except Django Admin) and check that we cannot access " "them if not logged in. We allow a specific set of exceptions. ", output=f"URLs found: {len(urls)}. URLs ignored {len(DO_NOT_TEST_URLS)}. " f"URLs expected to allow unauthorised access {len(NON_AUTH_URLS)}. " f"Errors {errors}", )