diff --git a/allianceauth/checks.py b/allianceauth/checks.py index 7235ad8a..7544bc11 100644 --- a/allianceauth/checks.py +++ b/allianceauth/checks.py @@ -1,3 +1,7 @@ +""" +Django system checks for Alliance Auth +""" + from typing import List from django import db from django.core.checks import CheckMessage, Error, register, Warning @@ -16,48 +20,172 @@ B = Configuration @register() def django_settings(app_configs, **kwargs) -> List[CheckMessage]: - errors: List[CheckMessage] = [] - if hasattr(settings, "SITE_URL"): - if settings.SITE_URL[-1] == "/": - errors.append(Warning("'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth.", hint="", id="allianceauth.checks.B005")) - else: - errors.append(Error("No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", hint="", id="allianceauth.checks.B006")) + """ + Check that Django settings are correctly configured - if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"): - if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: - errors.append(Warning("'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", hint="", id="allianceauth.checks.B007")) + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + + errors: List[CheckMessage] = [] + + # Check for SITE_URL + if hasattr(settings, "SITE_URL"): + # Check if SITE_URL is empty + if settings.SITE_URL == "": + errors.append( + Error( + msg="'SITE_URL' is empty.", + hint="Make sure to set 'SITE_URL' to the URL of your Auth instance. (Without trailing slash)", + id="allianceauth.checks.B011", + ) + ) + # Check if SITE_URL has a trailing slash + elif settings.SITE_URL[-1] == "/": + errors.append( + Warning( + msg="'SITE_URL' has a trailing slash. This may lead to incorrect links being generated by Auth.", + hint="", + id="allianceauth.checks.B005", + ) + ) + # SITE_URL not found else: - errors.append(Error("No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", hint="", id="allianceauth.checks.B008")) + errors.append( + Error( + msg="No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.", + hint="", + id="allianceauth.checks.B006", + ) + ) + + # Check for CSRF_TRUSTED_ORIGINS + if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"): + # Check if SITE_URL is not in CSRF_TRUSTED_ORIGINS + if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS: + errors.append( + Warning( + msg="'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.", + hint="", + id="allianceauth.checks.B007", + ) + ) + # CSRF_TRUSTED_ORIGINS not found + else: + errors.append( + Error( + msg="No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified", + hint="", + id="allianceauth.checks.B008", + ) + ) + + # Check for ESI_USER_CONTACT_EMAIL + if hasattr(settings, "ESI_USER_CONTACT_EMAIL"): + # Check if ESI_USER_CONTACT_EMAIL is empty + if settings.ESI_USER_CONTACT_EMAIL == "": + errors.append( + Error( + msg="'ESI_USER_CONTACT_EMAIL' is empty. A valid email is required as maintainer contact for CCP.", + hint="", + id="allianceauth.checks.B009", + ) + ) + # ESI_USER_CONTACT_EMAIL not found + else: + errors.append( + Error( + msg="No 'ESI_USER_CONTACT_EMAIL' found is settings. A valid email is required as maintainer contact for CCP.", + hint="", + id="allianceauth.checks.B010", + ) + ) return errors @register() def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that Redis is a supported version + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools" errors: List[CheckMessage] = [] try: - redis_version = Pep440Version(get_redis_client().info()['redis_version']) + redis_version = Pep440Version(get_redis_client().info()["redis_version"]) except InvalidVersion: errors.append(Warning("Unable to confirm Redis Version")) + return errors - if redis_version.major == 7 and redis_version.minor == 2 and timezone.now() > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc): - errors.append(Error(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A001")) + if ( + redis_version.major == 7 + and redis_version.minor == 2 + and timezone.now() + > timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc) + ): + errors.append( + Error( + msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested", + hint=allianceauth_redis_install_link, + id="allianceauth.checks.A001", + ) + ) elif redis_version.major == 7 and redis_version.minor == 0: - errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A002")) + errors.append( + Warning( + msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested", + hint=allianceauth_redis_install_link, + id="allianceauth.checks.A002", + ) + ) elif redis_version.major == 6 and redis_version.minor == 2: - errors.append(Warning(f"Redis {redis_version.public} in Security Support only, Updating Suggested", hint=allianceauth_redis_install_link, id="allianceauth.checks.A018")) + errors.append( + Warning( + msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested", + hint=allianceauth_redis_install_link, + id="allianceauth.checks.A018", + ) + ) elif redis_version.major in [6, 5]: - errors.append(Error(f"Redis {redis_version.public} EOL", hint=allianceauth_redis_install_link, id="allianceauth.checks.A003")) + errors.append( + Error( + msg=f"Redis {redis_version.public} EOL", + hint=allianceauth_redis_install_link, + id="allianceauth.checks.A003", + ) + ) return errors @register() def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that MySQL is a supported version + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/" errors: List[CheckMessage] = [] @@ -65,108 +193,302 @@ def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]: for connection in db.connections.all(): if connection.vendor == "mysql": try: - mysql_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) + mysql_version = Pep440Version( + ".".join(str(i) for i in connection.mysql_version) + ) except InvalidVersion: errors.append(Warning("Unable to confirm MySQL Version")) + return errors # MySQL 8 if mysql_version.major == 8: - if mysql_version.minor == 4 and timezone.now() > timezone.datetime(year=2032, month=4, day=30, tzinfo=timezone.utc): - errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A004")) + if mysql_version.minor == 4 and timezone.now() > timezone.datetime( + year=2032, month=4, day=30, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MySQL {mysql_version.public} EOL", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A004", + ) + ) elif mysql_version.minor == 3: - errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A005")) + errors.append( + Warning( + msg=f"MySQL {mysql_version.public} Non LTS", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A005", + ) + ) elif mysql_version.minor == 2: - errors.append(Warning(f"MySQL {mysql_version.public} Non LTS", hint=mysql_quick_guide_link, id="allianceauth.checks.A006")) + errors.append( + Warning( + msg=f"MySQL {mysql_version.public} Non LTS", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A006", + ) + ) elif mysql_version.minor == 1: - errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A007")) - elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(year=2026, month=4, day=30, tzinfo=timezone.utc): - errors.append(Error(f"MySQL {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A008")) + errors.append( + Error( + msg=f"MySQL {mysql_version.public} EOL", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A007", + ) + ) + elif mysql_version.minor == 0 and timezone.now() > timezone.datetime( + year=2026, month=4, day=30, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MySQL {mysql_version.public} EOL", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A008", + ) + ) # MySQL below 8 # This will also catch Mariadb 5.x elif mysql_version.major < 8: - errors.append(Error(f"MySQL or MariaDB {mysql_version.public} EOL", hint=mysql_quick_guide_link, id="allianceauth.checks.A009")) + errors.append( + Error( + msg=f"MySQL or MariaDB {mysql_version.public} EOL", + hint=mysql_quick_guide_link, + id="allianceauth.checks.A009", + ) + ) + return errors @register() def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that MariaDB is a supported version + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + mariadb_download_link = "https://mariadb.org/download/?t=repo-config" errors: List[CheckMessage] = [] for connection in db.connections.all(): - if connection.vendor == "mysql": # Still to find a way to determine MySQL vs MariaDB + # TODO: Find a way to determine MySQL vs. MariaDB + if connection.vendor == "mysql": try: - mariadb_version = Pep440Version(".".join(str(i) for i in connection.mysql_version)) + mariadb_version = Pep440Version( + ".".join(str(i) for i in connection.mysql_version) + ) except InvalidVersion: errors.append(Warning("Unable to confirm MariaDB Version")) + return errors # MariaDB 11 if mariadb_version.major == 11: - if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(year=2029, month=5, day=19, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A010")) + if mariadb_version.minor == 4 and timezone.now() > timezone.datetime( + year=2029, month=5, day=19, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A010", + ) + ) elif mariadb_version.minor == 2: - errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A018")) + errors.append( + Warning( + msg=f"MariaDB {mariadb_version.public} Non LTS", + hint=mariadb_download_link, + id="allianceauth.checks.A018", + ) + ) - if timezone.now() > timezone.datetime(year=2024, month=11, day=21, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A011")) + if timezone.now() > timezone.datetime( + year=2024, month=11, day=21, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A011", + ) + ) elif mariadb_version.minor == 1: - errors.append(Warning(f"MariaDB {mariadb_version.public} Non LTS", hint=mariadb_download_link, id="allianceauth.checks.A019")) + errors.append( + Warning( + msg=f"MariaDB {mariadb_version.public} Non LTS", + hint=mariadb_download_link, + id="allianceauth.checks.A019", + ) + ) - if timezone.now() > timezone.datetime(year=2024, month=8, day=21, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A012")) - elif mariadb_version.minor in [0, 3]: # Demote versions down here once EOL - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A013")) + if timezone.now() > timezone.datetime( + year=2024, month=8, day=21, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A012", + ) + ) + # Demote versions down here once EOL + elif mariadb_version.minor in [0, 3]: + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A013", + ) + ) # MariaDB 10 elif mariadb_version.major == 10: - if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(year=2028, month=2, day=10, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A014")) - elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(year=2026, month=7, day=6, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A0015")) - elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(year=2025, month=6, day=24, tzinfo=timezone.utc): - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A016")) - elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: # Demote versions down here once EOL - errors.append(Error(f"MariaDB {mariadb_version.public} EOL", hint=mariadb_download_link, id="allianceauth.checks.A017")) + if mariadb_version.minor == 11 and timezone.now() > timezone.datetime( + year=2028, month=2, day=10, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A014", + ) + ) + elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime( + year=2026, month=7, day=6, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A0015", + ) + ) + elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime( + year=2025, month=6, day=24, tzinfo=timezone.utc + ): + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A016", + ) + ) + # Demote versions down here once EOL + elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]: + errors.append( + Error( + msg=f"MariaDB {mariadb_version.public} EOL", + hint=mariadb_download_link, + id="allianceauth.checks.A017", + ) + ) return errors @register() def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that SQLite is a supported version + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + errors: List[CheckMessage] = [] + for connection in db.connections.all(): if connection.vendor == "sqlite": try: - sqlite_version = Pep440Version(".".join(str(i) for i in sqlite_version_info)) + sqlite_version = Pep440Version( + ".".join(str(i) for i in sqlite_version_info) + ) except InvalidVersion: errors.append(Warning("Unable to confirm SQLite Version")) + return errors if sqlite_version.major == 3 and sqlite_version.minor < 27: - errors.append(Error(f"SQLite {sqlite_version.public} Unsupported by Django", hint="https://pkgs.org/download/sqlite3", id="allianceauth.checks.A020")) + errors.append( + Error( + msg=f"SQLite {sqlite_version.public} Unsupported by Django", + hint="https://pkgs.org/download/sqlite3", + id="allianceauth.checks.A020", + ) + ) + return errors @register() def sql_settings(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that SQL settings are correctly configured + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + errors: List[CheckMessage] = [] + for connection in db.connections.all(): if connection.vendor == "mysql": try: if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4": - errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) + errors.append( + Error( + msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", + id="allianceauth.checks.B001", + ) + ) except KeyError: - errors.append(Error(f"SQL Charset is not set to utf8mb4 DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) + errors.append( + Error( + msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", + id="allianceauth.checks.B001", + ) + ) # This hasn't actually been set on AA yet # try: - # if connection.settings_dict["OPTIONS"]["collation"] != "utf8mb4_unicode_ci": - # errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) + # if ( + # connection.settings_dict["OPTIONS"]["collation"] + # != "utf8mb4_unicode_ci" + # ): + # errors.append( + # Error( + # msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", + # hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", + # id="allianceauth.checks.B001", + # ) + # ) # except KeyError: - # errors.append(Error(f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", id="allianceauth.checks.B001")) + # errors.append( + # Error( + # msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}", + # hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8", + # id="allianceauth.checks.B001", + # ) + # ) # if connection.vendor == "sqlite": @@ -175,19 +497,57 @@ def sql_settings(app_configs, **kwargs) -> List[CheckMessage]: @register() def celery_settings(app_configs, **kwargs) -> List[CheckMessage]: + """ + Check that Celery settings are correctly configured + + :param app_configs: + :type app_configs: + :param kwargs: + :type kwargs: + :return: + :rtype: + """ + errors: List[CheckMessage] = [] try: - if current_app.conf.broker_transport_options != {'priority_steps': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'queue_order_strategy': 'priority'}: - errors.append(Error("Celery Priorities are not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) + if current_app.conf.broker_transport_options != { + "priority_steps": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + "queue_order_strategy": "priority", + }: + errors.append( + Error( + msg="Celery Priorities are not set correctly", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", + id="allianceauth.checks.B003", + ) + ) except KeyError: - errors.append(Error("Celery Priorities are not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", id="allianceauth.checks.B003")) + errors.append( + Error( + msg="Celery Priorities are not set", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc", + id="allianceauth.checks.B003", + ) + ) try: - if current_app.conf.broker_connection_retry_on_startup != True: - errors.append(Error("Celery broker_connection_retry_on_startup not set correctly", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) + if not current_app.conf.broker_connection_retry_on_startup: + errors.append( + Error( + msg="Celery broker_connection_retry_on_startup not set correctly", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", + id="allianceauth.checks.B004", + ) + ) except KeyError: - errors.append(Error("Celery broker_connection_retry_on_startup not set", hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", id="allianceauth.checks.B004")) + errors.append( + Error( + msg="Celery broker_connection_retry_on_startup not set", + hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280", + id="allianceauth.checks.B004", + ) + ) return errors diff --git a/allianceauth/eveonline/tests/test_providers.py b/allianceauth/eveonline/tests/test_providers.py index 95f4f069..e90b727b 100644 --- a/allianceauth/eveonline/tests/test_providers.py +++ b/allianceauth/eveonline/tests/test_providers.py @@ -723,5 +723,5 @@ class TestEveSwaggerProvider(TestCase): my_client = my_provider.client operation = my_client.Universe.get_universe_factions() self.assertEqual( - operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0' + operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0 dummy@example.net' ) diff --git a/tests/settings_all.py b/tests/settings_all.py index 5bddee70..309f9260 100644 --- a/tests/settings_all.py +++ b/tests/settings_all.py @@ -47,6 +47,7 @@ CACHES = { ESI_SSO_CLIENT_ID = "dummy" ESI_SSO_CLIENT_SECRET = "dummy" ESI_SSO_CALLBACK_URL = f"{SITE_URL}/sso/callback" +ESI_USER_CONTACT_EMAIL = "dummy@example.net" ######################## # XenForo Configuration diff --git a/tests/settings_core.py b/tests/settings_core.py index 6403cfa8..3222f5df 100644 --- a/tests/settings_core.py +++ b/tests/settings_core.py @@ -37,3 +37,4 @@ ALLIANCEAUTH_DASHBOARD_TASK_STATISTICS_DISABLED = True # disable for tests ESI_SSO_CLIENT_ID = "dummy" ESI_SSO_CLIENT_SECRET = "dummy" ESI_SSO_CALLBACK_URL = f"{SITE_URL}/sso/callback" +ESI_USER_CONTACT_EMAIL = "dummy@example.net"