datatracker/ietf/settings.py
Matthew Holloway db7d3074da
feat: Add session recordings (#8218)
* feat: add session recordings

* feat: add session recordings

* feat: deleting recordings

* feat: deleting recordings and initial form values

* feat: use meeting date rather than today for initial title field. Fix delete recording

* feat: confirm delete recordings modal. fix server utils delete recording

* fix: removing debug console.log

* feat: change button name from 'Ok' to 'Delete' for confirm deletion to be clearer

* feat: UTC time in string and delete modal text

* fix: django html validation tests

* fix: django html validation tests

* fix: django html validation tests

* refactor: Work with SessionPresentations

* fix: better ordering

* chore: drop rev, hide table when empty

* test: test delete_recordings method

* fix: debug delete_recordings

* test: test add_session_recordings view

* fix: better permissions handling

* fix: only delete recordings for selected session

* refactor: inline script -> js module

* chore: remove accidental import

*shakes fist at pycharm*

* fix: consistent timestamp format

plus slight rephrase

* style: Black

* chore: remove comment

* test: update test to match

* fix: reversible url pattern for materials

Tests were perturbed in a way that led to a test
getting an interim instead of an IETF meeting.
This exposed a bug reversing the URL for the
materials_document() view. This splits it into
two patterns that are equivalent to the original.

---------

Co-authored-by: Jennifer Richards <jennifer@staff.ietf.org>
2025-01-31 10:28:39 -06:00

1403 lines
54 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright The IETF Trust 2007-2024, All Rights Reserved
# -*- coding: utf-8 -*-
# Django settings for ietf project.
# BASE_DIR and "settings_local" are from
# http://code.djangoproject.com/wiki/SplitSettings
import os
import sys
import datetime
import warnings
from hashlib import sha384
from typing import Any, Dict, List, Tuple # pyflakes:ignore
warnings.simplefilter("always", DeprecationWarning)
warnings.filterwarnings("ignore", message="pkg_resources is deprecated as an API")
warnings.filterwarnings("ignore", "Log out via GET requests is deprecated") # happens in oidc_provider
warnings.filterwarnings("ignore", module="tastypie", message="The django.utils.datetime_safe module is deprecated.")
warnings.filterwarnings("ignore", module="oidc_provider", message="The django.utils.timezone.utc alias is deprecated.")
warnings.filterwarnings("ignore", message="The USE_DEPRECATED_PYTZ setting,") # https://github.com/ietf-tools/datatracker/issues/5635
warnings.filterwarnings("ignore", message="The USE_L10N setting is deprecated.") # https://github.com/ietf-tools/datatracker/issues/5648
warnings.filterwarnings("ignore", message="django.contrib.auth.hashers.CryptPasswordHasher is deprecated.") # https://github.com/ietf-tools/datatracker/issues/5663
warnings.filterwarnings("ignore", message="'urllib3\\[secure\\]' extra is deprecated")
warnings.filterwarnings("ignore", message="The logout\\(\\) view is superseded by")
warnings.filterwarnings("ignore", message="Report.file_reporters will no longer be available in Coverage.py 4.2", module="coverage.report")
warnings.filterwarnings("ignore", message="Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated", module="bleach")
warnings.filterwarnings("ignore", message="HTTPResponse.getheader\\(\\) is deprecated", module='selenium.webdriver')
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
from ietf import __version__
import debug
DEBUG = True
debug.debug = DEBUG
DEBUG_AGENDA = False
# Valid values:
# 'production', 'test', 'development'
# Override this in settings_local.py if it's not the desired setting:
SERVER_MODE = 'development'
# Domain name of the IETF
IETF_DOMAIN = 'ietf.org'
# Overriden in settings_local
ADMINS = [
('Tools Help', 'tools-help@ietf.org'),
] # type: List[Tuple[str, str]]
BUG_REPORT_EMAIL = "tools-help@ietf.org"
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
]
ALLOWED_HOSTS = [".ietf.org", ".ietf.org.", "209.208.19.216", "4.31.198.44", "127.0.0.1", "localhost", ]
# Server name of the tools server
TOOLS_SERVER = 'tools.' + IETF_DOMAIN
# Override this in the settings_local.py file:
SERVER_EMAIL = 'Django Server <django-project@' + IETF_DOMAIN + '>'
DEFAULT_FROM_EMAIL = 'IETF Secretariat <ietf-secretariat-reply@' + IETF_DOMAIN + '>'
UTILS_ON_BEHALF_EMAIL = 'noreply@' + IETF_DOMAIN
UTILS_FROM_EMAIL_DOMAINS = [ 'ietf.org', 'iab.org', ]
MANAGERS = ADMINS
DATABASES = {
'default': {
'NAME': 'datatracker',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'ietf',
#'PASSWORD': 'somepassword',
},
}
# Local time zone for this installation. Choices can be found here:
# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
# although not all variations may be possible on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'PST8PDT'
# Language code for this installation. All choices can be found here:
# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
# http://blogs.law.harvard.edu/tech/stories/storyReader$15
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = False
# Django 4.0 changed the default setting of USE_L10N to True. The setting
# is deprecated and will be removed in Django 5.0.
USE_L10N = False
USE_TZ = True
USE_DEPRECATED_PYTZ = True # supported until Django 5
# The DjangoDivFormRenderer is a transitional class that opts in to defaulting to the div.html
# template for formsets. This will become the default behavior in Django 5.0. This configuration
# can be removed at that point.
# See https://docs.djangoproject.com/en/4.2/releases/4.1/#forms
FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
# Default primary key field type to use for models that dont have a field with primary_key=True.
# In the future (relative to 4.2), the default will become 'django.db.models.BigAutoField.'
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# OIDC configuration
_SITE_URL = os.environ.get("OIDC_SITE_URL", None)
if _SITE_URL is not None:
SITE_URL = _SITE_URL
if SERVER_MODE == 'production':
MEDIA_ROOT = '/a/www/www6s/lib/dt/media/'
MEDIA_URL = 'https://www.ietf.org/lib/dt/media/'
PHOTOS_DIRNAME = 'photo'
PHOTOS_DIR = os.path.join(MEDIA_ROOT, PHOTOS_DIRNAME)
else:
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')
MEDIA_URL = '/media/'
PHOTOS_DIRNAME = 'photo'
PHOTOS_DIR = os.path.join(MEDIA_ROOT, PHOTOS_DIRNAME)
OLD_PHOTO_DIRS = [
'/a/www/www6/wg/images',
'/a/www/www6/iesg/bio/photo',
'/a/www/iab/wp-content/IAB-uploads/2010/10/',
'/a/www/iab/wp-content/IAB-uploads/2011/05/',
'/a/www/iab/wp-content/IAB-uploads/2014/02/',
'/a/www/iab/wp-content/IAB-uploads/2015/02/',
'/a/www/iab/wp-content/IAB-uploads/2015/03/',
'/a/www/iab/wp-content/IAB-uploads/2015/06/',
'/a/www/iab/wp-content/IAB-uploads/2015/08/',
'/a/www/iab/wp-content/IAB-uploads/2016/03/',
]
IETF_HOST_URL = 'https://www.ietf.org/'
IETF_ID_URL = IETF_HOST_URL + 'id/' # currently unused
IETF_ID_ARCHIVE_URL = IETF_HOST_URL + 'archive/id/'
IETF_AUDIO_URL = IETF_HOST_URL + 'audio/'
IETF_NOTES_URL = 'https://notes.ietf.org/' # HedgeDoc base URL
# Absolute path to the directory static files should be collected to.
# Example: "/var/www/example.com/static/"
SERVE_CDN_PHOTOS = True
SERVE_CDN_FILES_LOCALLY_IN_DEV_MODE = True
# URL to use when referring to static files located in STATIC_ROOT.
if SERVER_MODE != 'production' and SERVE_CDN_FILES_LOCALLY_IN_DEV_MODE:
STATIC_URL = "/static/"
STATIC_ROOT = os.path.abspath(BASE_DIR + "/../static/")
else:
STATIC_URL = "https://static.ietf.org/dt/%s/"%__version__
STATIC_ROOT = "/a/www/www6s/lib/dt/%s/"%__version__
# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
# Client-side static.ietf.org URL
STATIC_IETF_ORG = "https://static.ietf.org"
# Server-side static.ietf.org URL (used in pdfized)
STATIC_IETF_ORG_INTERNAL = STATIC_IETF_ORG
WSGI_APPLICATION = "ietf.wsgi.application"
AUTHENTICATION_BACKENDS = ( 'ietf.ietfauth.backends.CaseInsensitiveModelBackend', )
FILE_UPLOAD_PERMISSIONS = 0o644
# ------------------------------------------------------------------------
# Django/Python Logging Framework Modifications
# Filter out "Invalid HTTP_HOST" emails
# Based on http://www.tiwoc.de/blog/2013/03/django-prevent-email-notification-on-suspiciousoperation/
from django.core.exceptions import SuspiciousOperation
def skip_suspicious_operations(record):
if record.exc_info:
exc_value = record.exc_info[1]
if isinstance(exc_value, SuspiciousOperation):
return False
return True
# Filter out UreadablePostError:
from django.http import UnreadablePostError
def skip_unreadable_post(record):
if record.exc_info:
exc_type, exc_value = record.exc_info[:2] # pylint: disable=unused-variable
if isinstance(exc_value, UnreadablePostError):
return False
return True
# Copied from DEFAULT_LOGGING as of Django 1.10.5 on 22 Feb 2017, and modified
# to incorporate html logging, invalid http_host filtering, and more.
# Changes from the default has comments.
# The Python logging flow is as follows:
# (see https://docs.python.org/2.7/howto/logging.html#logging-flow)
#
# Init: get a Logger: logger = logging.getLogger(name)
#
# Logging call, e.g. logger.error(level, msg, *args, exc_info=(...), extra={...})
# --> Logger (discard if level too low for this logger)
# (create log record from level, msg, args, exc_info, extra)
# --> Filters (discard if any filter attach to logger rejects record)
# --> Handlers (discard if level too low for handler)
# --> Filters (discard if any filter attached to handler rejects record)
# --> Formatter (format log record and emit)
#
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
#
'loggers': {
'django': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
},
'django.request': {
'handlers': ['console'],
'level': 'ERROR',
},
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
},
'django.security': {
'handlers': ['console', ],
'level': 'INFO',
},
'oidc_provider': {
'handlers': ['console', ],
'level': 'DEBUG',
},
'datatracker': {
'handlers': ['console'],
'level': 'INFO',
},
'celery': {
'handlers': ['console'],
'level': 'INFO',
},
},
#
# No logger filters
#
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'plain',
},
'debug_console': {
# Active only when DEBUG=True
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'plain',
},
'django.server': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'django.server',
},
'mail_admins': {
'level': 'ERROR',
'filters': [
'require_debug_false',
'skip_suspicious_operations', # custom
'skip_unreadable_posts', # custom
],
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True, # non-default
}
},
#
# All these are used by handlers
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
# custom filter, function defined above:
'skip_suspicious_operations': {
'()': 'django.utils.log.CallbackFilter',
'callback': skip_suspicious_operations,
},
# custom filter, function defined above:
'skip_unreadable_posts': {
'()': 'django.utils.log.CallbackFilter',
'callback': skip_unreadable_post,
},
},
# And finally the formatters
'formatters': {
'django.server': {
'()': 'django.utils.log.ServerFormatter',
'format': '[%(server_time)s] %(message)s',
},
'plain': {
'style': '{',
'format': '{levelname}: {name}:{lineno}: {message}',
},
'json' : {
"class": "ietf.utils.jsonlogger.DatatrackerJsonFormatter",
"style": "{",
"format": "{asctime}{levelname}{message}{name}{pathname}{lineno}{funcName}{process}",
}
},
}
# End logging
# ------------------------------------------------------------------------
X_FRAME_OPTIONS = 'SAMEORIGIN'
CSRF_TRUSTED_ORIGINS = [
"https://ietf.org",
"https://*.ietf.org",
'https://meetecho.com',
'https://*.meetecho.com',
]
CSRF_COOKIE_SAMESITE = 'None'
CSRF_COOKIE_SECURE = True
# SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds: 2 weeks (django default)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 4 # Age of cookie, in seconds: 4 weeks
SESSION_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SECURE = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_SAVE_EVERY_REQUEST = True
SESSION_CACHE_ALIAS = 'sessions'
PREFERENCES_COOKIE_AGE = 60 * 60 * 24 * 365 * 50 # Age of cookie, in seconds: 50 years
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR + "/templates",
BASE_DIR + "/secr/templates",
],
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug', # makes 'sql_queries' available in templates
'django.template.context_processors.i18n',
'django.template.context_processors.request',
'django.template.context_processors.media',
#'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'ietf.context_processors.server_mode',
# 'ietf.context_processors.debug_mark_queries_from_view',
# 'ietf.context_processors.sql_debug',
'ietf.context_processors.revision_info',
'ietf.context_processors.settings_info',
'ietf.secr.context_processors.secr_revision_info',
'ietf.context_processors.rfcdiff_base_url',
'ietf.context_processors.timezone_now',
],
'loaders': [
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
'ietf.dbtemplate.template.Loader',
]
},
},
] # type: List[Dict[str,Any]]
if DEBUG:
TEMPLATES[0]['OPTIONS']['string_if_invalid'] = "** No value found for '%s' **"
MIDDLEWARE = [
"django.middleware.csrf.CsrfViewMiddleware",
"corsheaders.middleware.CorsMiddleware", # see docs on CORS_REPLACE_HTTPS_REFERER before using it
"django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.http.ConditionalGetMiddleware",
"simple_history.middleware.HistoryRequestMiddleware",
# comment in this to get logging of SQL insert and update statements:
#"ietf.middleware.sql_log_middleware",
"ietf.middleware.SMTPExceptionMiddleware",
"ietf.middleware.Utf8ExceptionMiddleware",
"ietf.middleware.redirect_trailing_period_middleware",
"django_referrer_policy.middleware.ReferrerPolicyMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.security.SecurityMiddleware",
#"csp.middleware.CSPMiddleware",
"ietf.middleware.unicode_nfkc_normalization_middleware",
"ietf.middleware.is_authenticated_header_middleware",
]
ROOT_URLCONF = 'ietf.urls'
DJANGO_VITE_ASSETS_PATH = os.path.join(BASE_DIR, 'static/dist-neue')
if DEBUG:
DJANGO_VITE_MANIFEST_PATH = os.path.join(BASE_DIR, 'static/dist-neue/manifest.json')
# Additional locations of static files (in addition to each app's static/ dir)
STATICFILES_DIRS = (
DJANGO_VITE_ASSETS_PATH,
os.path.join(BASE_DIR, 'static/dist'),
os.path.join(BASE_DIR, 'secr/static/dist'),
)
INSTALLED_APPS = [
# Django apps
'ietf.admin', # replaces django.contrib.admin
'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.humanize',
'django.contrib.messages',
'django.contrib.sessions',
'django.contrib.sitemaps',
'django.contrib.sites',
'django.contrib.staticfiles',
# External apps
'analytical',
'django_vite',
'django_bootstrap5',
'django_celery_beat',
'django_celery_results',
'corsheaders',
'django_markup',
'oidc_provider',
'drf_spectacular',
'drf_standardized_errors',
'rest_framework',
'simple_history',
'tastypie',
'widget_tweaks',
# IETF apps
'ietf.api',
'ietf.community',
'ietf.dbtemplate',
'ietf.doc',
'ietf.group',
'ietf.idindex',
'ietf.iesg',
'ietf.ietfauth',
'ietf.ipr',
'ietf.liaisons',
'ietf.mailinglists',
'ietf.mailtrigger',
'ietf.meeting',
'ietf.message',
'ietf.name',
'ietf.nomcom',
'ietf.person',
'ietf.redirects',
'ietf.release',
'ietf.review',
'ietf.stats',
'ietf.status',
'ietf.submit',
'ietf.sync',
'ietf.utils',
# IETF Secretariat apps
'ietf.secr.announcement',
'ietf.secr.meetings',
'ietf.secr.rolodex',
'ietf.secr.sreq',
'ietf.secr.telechat',
]
try:
import django_extensions # pyflakes:ignore
INSTALLED_APPS.append('django_extensions')
except ImportError:
pass
# Settings for django-bootstrap5
# See https://django-bootstrap5.readthedocs.io/en/latest/settings.html
BOOTSTRAP5 = {
# Label class to use in horizontal forms
'horizontal_label_class': 'col-md-2 fw-bold',
# Field class to use in horiozntal forms
'horizontal_field_class': 'col-md-10',
# Field class used for horizontal fields withut a label.
'horizontal_field_offset_class': 'offset-md-2',
# Set placeholder attributes to label if no placeholder is provided
'set_placeholder': False,
'required_css_class': 'required',
'error_css_class': 'is-invalid',
'success_css_class': 'is-valid',
'field_renderers': {
'default': 'ietf.utils.bootstrap.SeparateErrorsFromHelpTextFieldRenderer',
},
}
# CORS settings
# See https://github.com/ottoyiu/django-cors-headers/
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = ( 'GET', 'OPTIONS', )
CORS_URLS_REGEX = r'^(/api/.*|.*\.json|.*/json/?)$'
# Setting for django_referrer_policy.middleware.ReferrerPolicyMiddleware
REFERRER_POLICY = 'strict-origin-when-cross-origin'
# django.middleware.security.SecurityMiddleware
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
#SECURE_HSTS_PRELOAD = True # Enable after testing
SECURE_HSTS_SECONDS = 3600
#SECURE_REDIRECT_EXEMPT
#SECURE_SSL_HOST
#SECURE_SSL_REDIRECT = True
# Relax the COOP policy to allow Meetecho authentication pop-up
SECURE_CROSS_ORIGIN_OPENER_POLICY = "unsafe-none"
# Override this in your settings_local with the IP addresses relevant for you:
INTERNAL_IPS = (
# local
'127.0.0.1',
'::1',
)
# django-rest-framework configuration
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"ietf.api.authentication.ApiKeyAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"ietf.api.permissions.HasApiKey",
],
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
"DEFAULT_PARSER_CLASSES": [
"rest_framework.parsers.JSONParser",
],
"DEFAULT_SCHEMA_CLASS": "drf_standardized_errors.openapi.AutoSchema",
"EXCEPTION_HANDLER": "drf_standardized_errors.handler.exception_handler",
}
# DRF OpenApi schema settings
SPECTACULAR_SETTINGS = {
"TITLE": "Datatracker API",
"DESCRIPTION": "Datatracker API",
"VERSION": "1.0.0",
"SCHEMA_PATH_PREFIX": "/api/",
"COMPONENT_SPLIT_REQUEST": True,
"COMPONENT_NO_READ_ONLY_REQUIRED": True,
"SERVERS": [
{"url": "http://localhost:8000", "description": "local dev server"},
{"url": "https://datatracker.ietf.org", "description": "production server"},
],
# The following settings are needed for drf-standardized-errors
"ENUM_NAME_OVERRIDES": {
"ValidationErrorEnum": "drf_standardized_errors.openapi_serializers.ValidationErrorEnum.choices",
"ClientErrorEnum": "drf_standardized_errors.openapi_serializers.ClientErrorEnum.choices",
"ServerErrorEnum": "drf_standardized_errors.openapi_serializers.ServerErrorEnum.choices",
"ErrorCode401Enum": "drf_standardized_errors.openapi_serializers.ErrorCode401Enum.choices",
"ErrorCode403Enum": "drf_standardized_errors.openapi_serializers.ErrorCode403Enum.choices",
"ErrorCode404Enum": "drf_standardized_errors.openapi_serializers.ErrorCode404Enum.choices",
"ErrorCode405Enum": "drf_standardized_errors.openapi_serializers.ErrorCode405Enum.choices",
"ErrorCode406Enum": "drf_standardized_errors.openapi_serializers.ErrorCode406Enum.choices",
"ErrorCode415Enum": "drf_standardized_errors.openapi_serializers.ErrorCode415Enum.choices",
"ErrorCode429Enum": "drf_standardized_errors.openapi_serializers.ErrorCode429Enum.choices",
"ErrorCode500Enum": "drf_standardized_errors.openapi_serializers.ErrorCode500Enum.choices",
},
"POSTPROCESSING_HOOKS": ["drf_standardized_errors.openapi_hooks.postprocess_schema_enums"],
}
# DRF Standardized Errors settings
DRF_STANDARDIZED_ERRORS = {
# enable the standardized errors when DEBUG=True for unhandled exceptions.
# By default, this is set to False so you're able to view the traceback in
# the terminal and get more information about the exception.
"ENABLE_IN_DEBUG_FOR_UNHANDLED_EXCEPTIONS": False,
# ONLY the responses that correspond to these status codes will appear
# in the API schema.
"ALLOWED_ERROR_STATUS_CODES": [
"400",
# "401",
# "403",
"404",
# "405",
# "406",
# "415",
# "429",
# "500",
],
}
# no slash at end
IDTRACKER_BASE_URL = "https://datatracker.ietf.org"
RFCDIFF_BASE_URL = "https://author-tools.ietf.org/iddiff"
IDNITS_BASE_URL = "https://author-tools.ietf.org/api/idnits"
IDNITS_SERVICE_URL = "https://author-tools.ietf.org/idnits"
# Content security policy configuration (django-csp)
# (In current production, the Content-Security-Policy header is completely set by nginx configuration, but
# we try to keep this in sync to avoid confusion)
CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", f"data: {IDTRACKER_BASE_URL} http://ietf.org/ https://www.ietf.org/ https://analytics.ietf.org/ https://static.ietf.org")
# The name of the method to use to invoke the test suite
TEST_RUNNER = 'ietf.utils.test_runner.IetfTestRunner'
# Fixtures which will be loaded before testing starts
GLOBAL_TEST_FIXTURES = [ 'names','ietf.utils.test_data.make_immutable_base_data',
'nomcom_templates','proceedings_templates' ]
TEST_DIFF_FAILURE_DIR = "/tmp/test/failure/"
# These are regexes
TEST_URL_COVERAGE_EXCLUDE = [
r"^\^admin/",
]
# These are filename globs. They are fed directly to the coverage code checker.
TEST_CODE_COVERAGE_EXCLUDE_FILES = [
"*/tests*",
"*/admin.py",
"*/factories.py",
"*/migrations/*",
"*/management/commands/*",
"docker/*",
"idindex/generate_all_id2_txt.py",
"idindex/generate_all_id_txt.py",
"idindex/generate_id_abstracts_txt.py",
"idindex/generate_id_index_txt.py",
"ietf/checks.py",
"ietf/manage.py",
"ietf/virtualenv-manage.py",
"ietf/meeting/timedeltafield.py", # Dead code, kept for a migration include
"ietf/settings*",
"ietf/utils/templatetags/debug_filters.py",
"ietf/utils/test_runner.py",
"ietf/name/generate_fixtures.py",
"ietf/review/import_from_review_tool.py",
"ietf/utils/patch.py",
"ietf/utils/test_data.py",
"ietf/utils/jstest.py",
]
# These are code line regex patterns
TEST_CODE_COVERAGE_EXCLUDE_LINES = [
"coverage: *ignore",
"debug",
r"unreachable\([^)]*\)",
"if settings.DEBUG",
"if settings.TEST_CODE_COVERAGE_CHECKER",
"if __name__ == .__main__.:",
]
# These are filename globs. They are used by test_parse_templates() and
# get_template_paths()
TEST_TEMPLATE_IGNORE = [
".*", # dot-files
"*~", # tilde temp-files
"#*", # files beginning with a hashmark
"500.html" # isn't loaded by regular loader, but checked by test_500_page()
]
TEST_COVERAGE_MAIN_FILE = os.path.join(BASE_DIR, "../release-coverage.json")
TEST_COVERAGE_LATEST_FILE = os.path.join(BASE_DIR, "../latest-coverage.json")
TEST_CODE_COVERAGE_CHECKER = None
if SERVER_MODE != 'production':
import coverage
TEST_CODE_COVERAGE_CHECKER = coverage.Coverage(source=[ BASE_DIR ], cover_pylib=False, omit=TEST_CODE_COVERAGE_EXCLUDE_FILES)
TEST_CODE_COVERAGE_REPORT_PATH = "coverage/"
TEST_CODE_COVERAGE_REPORT_URL = os.path.join(STATIC_URL, TEST_CODE_COVERAGE_REPORT_PATH, "index.html")
TEST_CODE_COVERAGE_REPORT_DIR = os.path.join(BASE_DIR, "static", TEST_CODE_COVERAGE_REPORT_PATH)
TEST_CODE_COVERAGE_REPORT_FILE = os.path.join(TEST_CODE_COVERAGE_REPORT_DIR, "index.html")
# WG Chair configuration
MAX_WG_DELEGATES = 3
# These states aren't available in forms with drop-down choices for new
# document state:
GROUP_STATES_WITH_EXTRA_PROCESSING = ["sub-pub", "rfc-edit", ]
# Review team related settings
GROUP_REVIEW_MAX_ITEMS_TO_SHOW_IN_REVIEWER_LIST = 10
GROUP_REVIEW_DAYS_TO_SHOW_IN_REVIEWER_LIST = 365
DATE_FORMAT = "Y-m-d"
DATETIME_FORMAT = "Y-m-d H:i T"
# Add reusable URL regexps here, for consistency. No need to do so if the
# regex can reasonably be expected to be a unique one-off.
URL_REGEXPS = {
"acronym": r"(?P<acronym>[-a-z0-9]+)",
"bofreq": r"(?P<name>bofreq-[-a-z0-9]+)",
"charter": r"(?P<name>charter-[-a-z0-9]+)",
"statement": r"(?P<name>statement-[-a-z0-9]+)",
"date": r"(?P<date>\d{4}-\d{2}-\d{2})",
"name": r"(?P<name>[A-Za-z0-9._+-]+?)",
"document": r"(?P<document>[a-z][-a-z0-9]+)", # regular document names
"rev": r"(?P<rev>[0-9]{1,2}(-[0-9]{2})?)",
"owner": r"(?P<owner>[-A-Za-z0-9\'+._]+@[A-Za-z0-9-._]+)",
"schedule_name": r"(?P<name>[A-Za-z0-9-:_]+)",
}
# Override this in settings_local.py if needed
# *_PATH variables ends with a slash/ .
DOCUMENT_PATH_PATTERN = '/a/ietfdata/doc/{doc.type_id}/'
INTERNET_DRAFT_PATH = '/a/ietfdata/doc/draft/repository'
INTERNET_DRAFT_PDF_PATH = '/a/www/ietf-datatracker/pdf/'
RFC_PATH = '/a/www/ietf-ftp/rfc/'
CHARTER_PATH = '/a/ietfdata/doc/charter/'
CHARTER_COPY_PATH = '/a/www/ietf-ftp/ietf' # copy 1wg-charters files here if set
CHARTER_COPY_OTHER_PATH = '/a/ftp/ietf'
CHARTER_COPY_THIRD_PATH = '/a/ftp/charter'
GROUP_SUMMARY_PATH = '/a/www/ietf-ftp/ietf'
BOFREQ_PATH = '/a/ietfdata/doc/bofreq/'
CONFLICT_REVIEW_PATH = '/a/ietfdata/doc/conflict-review'
STATUS_CHANGE_PATH = '/a/ietfdata/doc/status-change'
AGENDA_PATH = '/a/www/www6s/proceedings/'
MEETINGHOST_LOGO_PATH = AGENDA_PATH # put these in the same place as other proceedings files
# Move drafts to this directory when they expire
INTERNET_DRAFT_ARCHIVE_DIR = '/a/ietfdata/doc/draft/collection/draft-archive/'
# The following directory contains copies of all drafts - it used to be
# a set of hardlinks maintained by ghostlinkd, but is now explicitly written to
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/a/ietfdata/doc/draft/archive'
MEETING_RECORDINGS_DIR = '/a/www/audio'
DERIVED_DIR = '/a/ietfdata/derived'
FTP_DIR = '/a/ftp'
ALL_ID_DOWNLOAD_DIR = '/a/www/www6s/download'
NFS_METRICS_TMP_DIR = '/a/tmp'
DOCUMENT_FORMAT_ALLOWLIST = ["txt", "ps", "pdf", "xml", "html", ]
# Mailing list info URL for lists hosted on the IETF servers
MAILING_LIST_INFO_URL = "https://mailman3.%(domain)s/mailman3/lists/%(list_addr)s.%(domain)s"
MAILING_LIST_ARCHIVE_URL = "https://mailarchive.ietf.org"
# Liaison Statement Tool settings (one is used in DOC_HREFS below)
LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool <statements@' + IETF_DOMAIN + '>'
LIAISON_ATTACH_PATH = '/a/www/ietf-datatracker/documents/LIAISON/' # should end in a slash
LIAISON_ATTACH_URL = 'https://www.ietf.org/lib/dt/documents/LIAISON/' # should end in a slash, location should have a symlink to LIAISON_ATTACH_PATH
# Ideally, more of these would be local -- but since we don't support
# versions right now, we'll point to external websites
DOC_HREFS = {
"charter": "https://www.ietf.org/charter/{doc.name}-{doc.rev}.txt",
"draft": "https://www.ietf.org/archive/id/{doc.name}-{doc.rev}.txt",
"rfc": "https://www.rfc-editor.org/rfc/rfc{doc.rfc_number}.txt",
"slides": "https://www.ietf.org/slides/{doc.name}-{doc.rev}",
"procmaterials": "https://www.ietf.org/procmaterials/{doc.name}-{doc.rev}",
"conflrev": "https://www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
"statchg": "https://www.ietf.org/sc/{doc.name}-{doc.rev}.txt",
"liaison": "%s{doc.uploaded_filename}" % LIAISON_ATTACH_URL,
"liai-att": "%s{doc.uploaded_filename}" % LIAISON_ATTACH_URL,
}
# Valid MIME types for cases where text is uploaded and immediately extracted,
# e.g. a charter or a review. Must be a tuple, not a list.
DOC_TEXT_FILE_VALID_UPLOAD_MIME_TYPES = ('text/plain', 'text/markdown', 'text/x-rst', 'text/x-markdown', )
# Age limit before action holders are flagged in the document display
DOC_ACTION_HOLDER_AGE_LIMIT_DAYS = 20
# Override this in settings_local.py if needed
CACHE_MIDDLEWARE_SECONDS = 300
CACHE_MIDDLEWARE_KEY_PREFIX = ''
HTMLIZER_VERSION = 1
HTMLIZER_URL_PREFIX = "/doc/html"
HTMLIZER_CACHE_TIME = 60*60*24*14 # 14 days
PDFIZER_CACHE_TIME = HTMLIZER_CACHE_TIME
PDFIZER_URL_PREFIX = IDTRACKER_BASE_URL+"/doc/pdf"
# Email settings
IPR_EMAIL_FROM = 'ietf-ipr@ietf.org'
AUDIO_IMPORT_EMAIL = ['ietf@meetecho.com']
SESSION_REQUEST_FROM_EMAIL = 'IETF Meeting Session Request Tool <session-request@ietf.org>'
SECRETARIAT_SUPPORT_EMAIL = "support@ietf.org"
SECRETARIAT_ACTION_EMAIL = SECRETARIAT_SUPPORT_EMAIL
SECRETARIAT_INFO_EMAIL = SECRETARIAT_SUPPORT_EMAIL
# Put real password in settings_local.py
IANA_SYNC_PASSWORD = "secret"
IANA_SYNC_CHANGES_URL = "https://datatracker.iana.org:4443/data-tracker/changes"
IANA_SYNC_PROTOCOLS_URL = "https://www.iana.org/protocols/"
RFC_EDITOR_SYNC_PASSWORD="secret"
RFC_EDITOR_SYNC_NOTIFICATION_URL = "https://www.rfc-editor.org/parser/parser.php"
RFC_EDITOR_GROUP_NOTIFICATION_EMAIL = "webmaster@rfc-editor.org"
#RFC_EDITOR_GROUP_NOTIFICATION_URL = "https://www.rfc-editor.org/notification/group.php"
RFC_EDITOR_QUEUE_URL = "https://www.rfc-editor.org/queue2.xml"
RFC_EDITOR_INDEX_URL = "https://www.rfc-editor.org/rfc/rfc-index.xml"
RFC_EDITOR_ERRATA_JSON_URL = "https://www.rfc-editor.org/errata.json"
RFC_EDITOR_ERRATA_URL = "https://www.rfc-editor.org/errata_search.php?rfc={rfc_number}"
RFC_EDITOR_INLINE_ERRATA_URL = "https://www.rfc-editor.org/rfc/inline-errata/rfc{rfc_number}.html"
RFC_EDITOR_INFO_BASE_URL = "https://www.rfc-editor.org/info/"
# NomCom Tool settings
ROLODEX_URL = ""
NOMCOM_PUBLIC_KEYS_DIR = '/a/www/nomcom/public_keys/'
NOMCOM_FROM_EMAIL = 'nomcom-chair-{year}@ietf.org'
OPENSSL_COMMAND = '/usr/bin/openssl'
DAYS_TO_EXPIRE_NOMINATION_LINK = ''
NOMINEE_FEEDBACK_TYPES = ['comment', 'questio', 'nomina', 'obe']
# SlideSubmission settings
SLIDE_STAGING_PATH = '/a/www/www6s/staging/'
SLIDE_STAGING_URL = 'https://www.ietf.org/staging/'
# ID Submission Tool settings
IDSUBMIT_FROM_EMAIL = 'IETF I-D Submission Tool <idsubmission@ietf.org>'
IDSUBMIT_ANNOUNCE_FROM_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_LIST_EMAIL = 'i-d-announce@ietf.org'
# Interim Meeting Tool settings
INTERIM_ANNOUNCE_FROM_EMAIL_DEFAULT = 'IESG Secretary <iesg-secretary@ietf.org>'
INTERIM_ANNOUNCE_FROM_EMAIL_PROGRAM = 'IAB Executive Administrative Manager <execd@iab.org>'
VIRTUAL_INTERIMS_REQUIRE_APPROVAL = False
INTERIM_SESSION_MINIMUM_MINUTES = 30
INTERIM_SESSION_MAXIMUM_MINUTES = 300
# Days from meeting to day of cut off dates on submit -- cutoff_time_utc is added to this
IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_00 = 13
IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_01 = 13
IDSUBMIT_DEFAULT_CUTOFF_TIME_UTC = datetime.timedelta(hours=23, minutes=59, seconds=59)
IDSUBMIT_DEFAULT_CUTOFF_WARNING_DAYS = datetime.timedelta(days=21)
# 14 Jun 2017: New convention: prefix settings with the app name to which
# they (mainly) belong. So here, SUBMIT_, rather than IDSUBMIT_
SUBMIT_YANG_RFC_MODEL_DIR = '/a/www/ietf-ftp/yang/rfcmod/'
SUBMIT_YANG_DRAFT_MODEL_DIR = '/a/www/ietf-ftp/yang/draftmod/'
SUBMIT_YANG_IANA_MODEL_DIR = '/a/www/ietf-ftp/yang/ianamod/'
SUBMIT_YANG_CATALOG_MODEL_DIR = '/a/www/ietf-ftp/yang/catalogmod/'
IDSUBMIT_REPOSITORY_PATH = INTERNET_DRAFT_PATH
IDSUBMIT_STAGING_PATH = '/a/www/www6s/staging/'
IDSUBMIT_STAGING_URL = '//www.ietf.org/staging/'
IDSUBMIT_IDNITS_BINARY = '/a/www/ietf-datatracker/scripts/idnits'
SUBMIT_PYANG_COMMAND = 'pyang --verbose --ietf -p {libs} {model}'
SUBMIT_YANGLINT_COMMAND = 'yanglint --verbose -p {tmplib} -p {rfclib} -p {draftlib} -p {ianalib} -p {cataloglib} {model} -i'
SUBMIT_YANG_CATALOG_MODULEARG = "modules[]={module}"
SUBMIT_YANG_CATALOG_IMPACT_URL = "https://www.yangcatalog.org/yang-search/impact_analysis.php?{moduleargs}&recurse=0&rfcs=1&show_subm=1&show_dir=both"
SUBMIT_YANG_CATALOG_IMPACT_DESC = "Yang impact analysis for {draft}"
SUBMIT_YANG_CATALOG_MODULE_URL = "https://www.yangcatalog.org/yang-search/module_details.php?module={module}"
SUBMIT_YANG_CATALOG_MODULE_DESC = "Yang catalog entry for {module}"
SUBMIT_YANG_CATALOG_CHECKER_URL = "https://yangcatalog.org/yangvalidator/api/v1/datatracker/{type}"
IDSUBMIT_CHECKER_CLASSES = (
"ietf.submit.checkers.DraftIdnitsChecker",
"ietf.submit.checkers.DraftYangChecker",
# "ietf.submit.checkers.DraftYangvalidatorChecker",
)
# Max time to allow for validation before a submission is subject to cancellation
IDSUBMIT_MAX_VALIDATION_TIME = datetime.timedelta(minutes=20)
# Age at which a submission expires if not posted
IDSUBMIT_EXPIRATION_AGE = datetime.timedelta(days=14)
IDSUBMIT_FILE_TYPES = (
'txt',
'html',
'xml',
'pdf',
'ps',
)
RFC_FILE_TYPES = IDSUBMIT_FILE_TYPES
IDSUBMIT_MAX_DRAFT_SIZE = {
'txt': 2*1024*1024, # Max size of txt draft file in bytes
'xml': 3*1024*1024, # Max size of xml draft file in bytes
'html': 4*1024*1024,
'pdf': 6*1024*1024,
'ps' : 6*1024*1024,
}
IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME = 20
IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE = 50 # in MB
IDSUBMIT_MAX_DAILY_SAME_SUBMITTER = 50
IDSUBMIT_MAX_DAILY_SAME_SUBMITTER_SIZE = 150 # in MB
IDSUBMIT_MAX_DAILY_SAME_GROUP = 150
IDSUBMIT_MAX_DAILY_SAME_GROUP_SIZE = 450 # in MB
IDSUBMIT_MAX_DAILY_SUBMISSIONS = 1000
IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE = 2000 # in MB
# === Meeting Related Settings =================================================
MEETING_MATERIALS_SERVE_LOCALLY = True
# If you override MEETING_MATERIALS_SERVE_LOCALLY in your settings_local.conf, you will need to
# set the right value for MEETING_DOC_HREFS there as well. MEETING_DOC_LOCAL_HREFS and
# CDN_MEETING_DOC_HREFS are defined here to make that simpler.
MEETING_DOC_LOCAL_HREFS = {
"agenda": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"minutes": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"narrativeminutes": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"slides": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"chatlog": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"polls": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
"recording": "{doc.external_url}",
"bluesheets": "https://www.ietf.org/proceedings/{meeting.number}/bluesheets/{doc.uploaded_filename}",
"procmaterials": "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}",
}
MEETING_DOC_CDN_HREFS = {
"agenda": "https://www.ietf.org/proceedings/{meeting.number}/agenda/{doc.name}-{doc.rev}",
"minutes": "https://www.ietf.org/proceedings/{meeting.number}/minutes/{doc.name}-{doc.rev}",
"narrativeminutes": "https://www.ietf.org/proceedings/{meeting.number}/narrative-minutes/{doc.name}-{doc.rev}",
"slides": "https://www.ietf.org/proceedings/{meeting.number}/slides/{doc.name}-{doc.rev}",
"recording": "{doc.external_url}",
"bluesheets": "https://www.ietf.org/proceedings/{meeting.number}/bluesheets/{doc.uploaded_filename}",
"procmaterials": "https://www.ietf.org/proceedings/{meeting.number}/procmaterials/{doc.name}-{doc.rev}",
}
MEETING_DOC_HREFS = MEETING_DOC_LOCAL_HREFS if MEETING_MATERIALS_SERVE_LOCALLY else MEETING_DOC_CDN_HREFS
MEETING_DOC_OLD_HREFS = {
"agenda": "/meeting/{meeting.number}/materials/{doc.name}",
"minutes": "/meeting/{meeting.number}/materials/{doc.name}",
"narrativeminutes" : "/meeting/{meeting.number}/materials/{doc.name}",
"slides": "/meeting/{meeting.number}/materials/{doc.name}",
"recording": "{doc.external_url}",
"bluesheets": "https://www.ietf.org/proceedings/{meeting.number}/bluesheets/{doc.uploaded_filename}",
}
# For http references to documents without a version number (that is, to the current version at the time of reference)
MEETING_DOC_GREFS = {
"agenda": "/meeting/{meeting.number}/materials/{doc.name}",
"minutes": "/meeting/{meeting.number}/materials/{doc.name}",
"narrativeminutes": "/meeting/{meeting.number}/materials/{doc.name}",
"slides": "/meeting/{meeting.number}/materials/{doc.name}",
"recording": "{doc.external_url}",
"bluesheets": "https://www.ietf.org/proceedings/{meeting.number}/bluesheets/{doc.uploaded_filename}",
"procmaterials": "/meeting/{meeting.number}/materials/{doc.name}",
}
MEETING_MATERIALS_DEFAULT_SUBMISSION_START_DAYS = 90
MEETING_MATERIALS_DEFAULT_SUBMISSION_CUTOFF_DAYS = 26
MEETING_MATERIALS_DEFAULT_SUBMISSION_CORRECTION_DAYS = 50
MEETING_VALID_UPLOAD_EXTENSIONS = {
'agenda': ['.txt','.html','.htm', '.md', ],
'minutes': ['.txt','.html','.htm', '.md', '.pdf', ],
'narrativeminutes': ['.txt','.html','.htm', '.md', '.pdf', ],
'slides': ['.doc','.docx','.pdf','.ppt','.pptx','.txt', ], # Note the removal of .zip
'bluesheets': ['.pdf', '.txt', ],
'procmaterials':['.pdf', ],
'meetinghostlogo': ['.png', '.jpg', '.jpeg'],
}
MEETING_VALID_UPLOAD_MIME_TYPES = {
'agenda': ['text/plain', 'text/html', 'text/markdown', 'text/x-markdown', ],
'minutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
'narrativeminutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
'slides': [],
'bluesheets': ['application/pdf', 'text/plain', ],
'procmaterials':['application/pdf', ],
'meetinghostlogo': ['image/jpeg', 'image/png', ],
}
MEETING_VALID_MIME_TYPE_EXTENSIONS = {
'text/plain': ['.txt', '.md', ],
'text/markdown': ['.txt', '.md', ],
'text/x-markdown': ['.txt', '.md', ],
'text/html': ['.html', '.htm'],
'application/pdf': ['.pdf'],
}
# Files uploaded with Content-Type application/octet-stream and an extension in this map will
# be treated as if they had been uploaded with the mapped Content-Type value.
MEETING_APPLICATION_OCTET_STREAM_OVERRIDES = {
'.md': 'text/markdown',
}
MEETING_VALID_UPLOAD_MIME_FOR_OBSERVED_MIME = {
'text/plain': ['text/plain', 'text/markdown', 'text/x-markdown', ],
'text/html': ['text/html', ],
'application/pdf': ['application/pdf', ],
}
INTERNET_DRAFT_DAYS_TO_EXPIRE = 185
FLOORPLAN_MEDIA_DIR = 'floor'
FLOORPLAN_DIR = os.path.join(MEDIA_ROOT, FLOORPLAN_MEDIA_DIR)
MEETING_LEGACY_OFFICE_HOURS_END = 112 # last meeting to use legacy office hours representation
# Maximum dimensions to accept at all
MEETINGHOST_LOGO_MAX_UPLOAD_WIDTH = 400
MEETINGHOST_LOGO_MAX_UPLOAD_HEIGHT = 400
# Maximum dimensions to display
MEETINGHOST_LOGO_MAX_DISPLAY_WIDTH = 120
MEETINGHOST_LOGO_MAX_DISPLAY_HEIGHT = 120
# Session assignments on the official schedule lock this long before the timeslot starts
MEETING_SESSION_LOCK_TIME = datetime.timedelta(minutes=10)
# === OpenID Connect Provide Related Settings ==================================
# Used by django-oidc-provider
LOGIN_URL = '/accounts/login/'
OIDC_USERINFO = 'ietf.ietfauth.utils.openid_userinfo'
OIDC_EXTRA_SCOPE_CLAIMS = 'ietf.ietfauth.utils.OidcExtraScopeClaims'
# ==============================================================================
YANGLINT_BINARY = '/usr/bin/yanglint'
DE_GFM_BINARY = '/usr/bin/de-gfm.ruby2.5'
# Account settings
DAYS_TO_EXPIRE_REGISTRATION_LINK = 3
MINUTES_TO_EXPIRE_RESET_PASSWORD_LINK = 60
# Generation of pdf files
GHOSTSCRIPT_COMMAND = "/usr/bin/gs"
# Generation of bibxml files (currently only for Internet-Drafts)
BIBXML_BASE_PATH = '/a/ietfdata/derived/bibxml'
# Timezone files for iCalendar
TZDATA_ICS_PATH = BASE_DIR + '/../vzic/zoneinfo/'
DATATRACKER_MAX_UPLOAD_SIZE = 40960000
PPT2PDF_COMMAND = [
"/usr/bin/soffice", "--headless", "--convert-to", "pdf:writer_globaldocument_pdf_Export", "--outdir"
]
STATS_REGISTRATION_ATTENDEES_JSON_URL = 'https://registration.ietf.org/{number}/attendees/'
PROCEEDINGS_VERSION_CHANGES = [
0, # version 1
97, # version 2: meeting 97 and later (was number was NEW_PROCEEDINGS_START)
111, # version 3: meeting 111 and later
]
PROCEEDINGS_V1_BASE_URL = 'https://www.ietf.org/proceedings/{meeting.number}'
YOUTUBE_API_KEY = ''
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'
YOUTUBE_BASE_URL = 'https://www.youtube.com/watch'
YOUTUBE_IETF_CHANNEL_ID = 'UC8dtK9njBLdFnBahHFp0eZQ'
# If we need to revert to xmpp, change this to 'xmpp:{chat_room_name}@jabber.ietf.org?join'
CHAT_URL_PATTERN = 'https://zulip.ietf.org/#narrow/stream/{chat_room_name}'
# If we need to revert to xmpp
# CHAT_ARCHIVE_URL_PATTERN = 'https://www.ietf.org/jabber/logs/{chat_room_name}?C=M;O=D'
PYFLAKES_DEFAULT_ARGS= ["ietf", ]
# Automatic Scheduling
#
# how much to login while running, bigger numbers make it more verbose.
BADNESS_CALC_LOG = 0
#
# these penalties affect the calculation of how bad the assignments are.
BADNESS_UNPLACED = 1000000
# following four are used only during migrations to setup up ConstraintName
# and penalties are taken from the database afterwards.
BADNESS_BETHERE = 200000
BADNESS_CONFLICT_1 = 100000
BADNESS_CONFLICT_2 = 10000
BADNESS_CONFLICT_3 = 1000
BADNESS_TOOSMALL_50 = 5000
BADNESS_TOOSMALL_100 = 50000
BADNESS_TOOBIG = 100
BADNESS_MUCHTOOBIG = 500
# Set debug apps in settings_local.DEV_APPS
DEV_APPS = [] # type: List[str]
DEV_PRE_APPS = [] # type: List[str]
DEV_MIDDLEWARE = ()
PROD_PRE_APPS = [] # type: List[str]
# django-debug-toolbar and the debug listing of sql queries at the bottom of
# each page when in dev mode can overlap in functionality, and can slow down
# page loading. If you wish to use the sql_queries debug listing, put this in
# your settings_local and make sure your client IP address is in INTERNAL_IPS:
#
# DEV_TEMPLATE_CONTEXT_PROCESSORS = [
# 'ietf.context_processors.sql_debug',
# ]
#
DEV_TEMPLATE_CONTEXT_PROCESSORS = [] # type: List[str]
# Domain which hosts draft and wg alias lists
DRAFT_ALIAS_DOMAIN = IETF_DOMAIN
GROUP_ALIAS_DOMAIN = IETF_DOMAIN
TEST_DATA_DIR = os.path.abspath(BASE_DIR + "/../test/data")
USER_PREFERENCE_DEFAULTS = {
"expires_soon" : "14",
"new_enough" : "14",
"full_draft" : "on",
"left_menu" : "off",
}
# Email addresses people attempt to set for their account will be checked
# against the following list of regex expressions with re.search(pat, addr):
EXCLUDED_PERSONAL_EMAIL_REGEX_PATTERNS = [
"@ietf.org$",
]
# Configuration for django-markup
MARKUP_SETTINGS = {
'restructuredtext': {
'settings_overrides': {
'report_level': 3, # error (3) or severe (4) only
'initial_header_level': 3,
'doctitle_xform': False,
'footnote_references': 'superscript',
'trim_footnote_reference_space': True,
'default_reference_context': 'view',
'raw_enabled': False, # critical for security
'file_insertion_enabled': False, # critical for security
'link_base': ''
}
}
}
# This is the number of seconds required between subscribing to an ietf
# mailing list and datatracker account creation being accepted
LIST_ACCOUNT_DELAY = 60*60*25 # 25 hours
ACCOUNT_REQUEST_EMAIL = 'account-request@ietf.org'
SILENCED_SYSTEM_CHECKS = [
"fields.W342", # Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.
"fields.W905", # django.contrib.postgres.fields.CICharField is deprecated. (see https://github.com/ietf-tools/datatracker/issues/5660)
]
CHECKS_LIBRARY_PATCHES_TO_APPLY = [
'patch/change-oidc-provider-field-sizes-228.patch',
'patch/fix-oidc-access-token-post.patch',
'patch/fix-jwkest-jwt-logging.patch',
'patch/django-cookie-delete-with-all-settings.patch',
'patch/tastypie-django22-fielderror-response.patch',
]
if DEBUG:
try:
import django_cprofile_middleware # pyflakes:ignore
CHECKS_LIBRARY_PATCHES_TO_APPLY += [ 'patch/add-django-cprofile-filter.patch', ]
except ImportError:
pass
STATS_NAMES_LIMIT = 25
UTILS_MEETING_CONFERENCE_DOMAINS = ['webex.com', 'zoom.us', 'jitsi.org', 'meetecho.com', 'gather.town', ]
UTILS_TEST_RANDOM_STATE_FILE = '.factoryboy_random_state'
UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS = 30
API_KEY_TYPE="ES256" # EC / P=256
API_PUBLIC_KEY_PEM = b"""
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqVojsaofDJScuMJN+tshumyNM5ME
garzVPqkVovmF6yE7IJ/dv4FcV+QKCtJ/rOS8e36Y8ZAEVYuukhes0yZ1w==
-----END PUBLIC KEY-----
"""
API_PRIVATE_KEY_PEM = b"""
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoI6LJkopKq8XrHi9
QqGQvE4A83TFYjqLz+8gULYecsqhRANCAASpWiOxqh8MlJy4wk362yG6bI0zkwSB
qvNU+qRWi+YXrITsgn92/gVxX5AoK0n+s5Lx7fpjxkARVi66SF6zTJnX
-----END PRIVATE KEY-----
"""
# Default timeout for HTTP requests via the requests library
DEFAULT_REQUESTS_TIMEOUT = 20 # seconds
# Celery configuration
CELERY_TIMEZONE = 'UTC'
CELERY_BROKER_URL = 'amqp://mq/'
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'
CELERY_BEAT_SYNC_EVERY = 1 # update DB after every event
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True # the default, but setting it squelches a warning
# Use a result backend so we can chain tasks. This uses the rpc backend, see
# https://docs.celeryq.dev/en/stable/userguide/tasks.html#rpc-result-backend-rabbitmq-qpid
# Results can be retrieved only once and only by the caller of the task. Results will be
# lost if the message broker restarts.
CELERY_RESULT_BACKEND = 'django-cache' # use a Django cache for results
CELERY_CACHE_BACKEND = 'celery-results' # which Django cache to use
CELERY_RESULT_EXPIRES = datetime.timedelta(minutes=5) # how long are results valid? (Default is 1 day)
CELERY_TASK_IGNORE_RESULT = True # ignore results unless specifically enabled for a task
# Meetecho API setup: Uncomment this and provide real credentials to enable
# Meetecho conference creation for interim session requests
#
# MEETECHO_API_CONFIG = {
# 'api_base': 'https://meetings.conf.meetecho.com/api/v1/',
# 'client_id': 'datatracker',
# 'client_secret': 'some secret',
# 'request_timeout': 3.01, # python-requests doc recommend slightly > a multiple of 3 seconds
# # How many minutes before/after session to enable slide update API. Defaults to 15. Set to None to disable,
# # or < 0 to _always_ send updates (useful for debugging)
# 'slides_notify_time': 15,
# 'debug': False, # if True, API calls will be echoed as debug instead of sent (only works for slides for now)
# }
# Meetecho URLs - instantiate with url.format(session=some_session)
MEETECHO_ONSITE_TOOL_URL = "https://meetings.conf.meetecho.com/onsite{session.meeting.number}/?session={session.pk}"
MEETECHO_VIDEO_STREAM_URL = "https://meetings.conf.meetecho.com/ietf{session.meeting.number}/?session={session.pk}"
MEETECHO_AUDIO_STREAM_URL = "https://mp3.conf.meetecho.com/ietf{session.meeting.number}/{session.pk}.m3u"
MEETECHO_SESSION_RECORDING_URL = "https://meetecho-player.ietf.org/playout/?session={session_label}"
# Put the production SECRET_KEY in settings_local.py, and also any other
# sensitive or site-specific changes. DO NOT commit settings_local.py to svn.
from ietf.settings_local import * # pyflakes:ignore pylint: disable=wildcard-import
for app in INSTALLED_APPS:
if app.startswith('ietf'):
app_settings_file = os.path.join(BASE_DIR, '../', app.replace('.', os.sep), "settings.py")
if os.path.exists(app_settings_file):
exec("from %s import *" % (app+".settings"))
# Add APPS from settings_local to INSTALLED_APPS
if SERVER_MODE == 'production':
INSTALLED_APPS = PROD_PRE_APPS + INSTALLED_APPS
else:
INSTALLED_APPS += DEV_APPS
INSTALLED_APPS = DEV_PRE_APPS + INSTALLED_APPS
MIDDLEWARE += DEV_MIDDLEWARE
TEMPLATES[0]['OPTIONS']['context_processors'] += DEV_TEMPLATE_CONTEXT_PROCESSORS
if "CACHES" not in locals():
if SERVER_MODE == "production":
MEMCACHED_HOST = os.environ.get("MEMCACHED_SERVICE_HOST", "127.0.0.1")
MEMCACHED_PORT = os.environ.get("MEMCACHED_SERVICE_PORT", "11211")
CACHES = {
"default": {
"BACKEND": "ietf.utils.cache.LenientMemcacheCache",
"LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}",
"VERSION": __version__,
"KEY_PREFIX": "ietf:dt",
"KEY_FUNCTION": lambda key, key_prefix, version: (
f"{key_prefix}:{version}:{sha384(str(key).encode('utf8')).hexdigest()}"
),
},
"sessions": {
"BACKEND": "ietf.utils.cache.LenientMemcacheCache",
"LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}",
# No release-specific VERSION setting.
"KEY_PREFIX": "ietf:dt",
},
"htmlized": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/a/cache/datatracker/htmlized",
"OPTIONS": {
"MAX_ENTRIES": 100000, # 100,000
},
},
"pdfized": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/a/cache/datatracker/pdfized",
"OPTIONS": {
"MAX_ENTRIES": 100000, # 100,000
},
},
"slowpages": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/a/cache/datatracker/slowpages",
"OPTIONS": {
"MAX_ENTRIES": 5000,
},
},
"celery-results": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": f"{MEMCACHED_HOST}:{MEMCACHED_PORT}",
"KEY_PREFIX": "ietf:celery",
},
}
else:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
#'BACKEND': 'ietf.utils.cache.LenientMemcacheCache',
#'LOCATION': '127.0.0.1:11211',
#'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
"VERSION": __version__,
"KEY_PREFIX": "ietf:dt",
},
"sessions": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
},
"htmlized": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
#'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
"LOCATION": "/var/cache/datatracker/htmlized",
"OPTIONS": {
"MAX_ENTRIES": 1000,
},
},
"pdfized": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
#'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
"LOCATION": "/var/cache/datatracker/pdfized",
"OPTIONS": {
"MAX_ENTRIES": 1000,
},
},
"slowpages": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
#'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
"LOCATION": "/var/cache/datatracker/",
"OPTIONS": {
"MAX_ENTRIES": 5000,
},
},
"celery-results": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "app:11211",
"KEY_PREFIX": "ietf:celery",
},
}
PUBLISH_IPR_STATES = ['posted', 'removed', 'removed_objfalse']
ADVERTISE_VERSIONS = ["markdown", "pyang", "rfc2html", "xml2rfc"]
# We provide a secret key only for test and development modes. It's
# absolutely vital that django fails to start in production mode unless a
# secret key has been provided elsewhere, not in this file which is
# publicly available, for instance from the source repository.
if SERVER_MODE != 'production':
# stomp out the cached template loader, it's annoying
loaders = TEMPLATES[0]['OPTIONS']['loaders']
loaders = tuple(l for e in loaders for l in (e[1] if isinstance(e, tuple) and "cached.Loader" in e[0] else (e,)))
TEMPLATES[0]['OPTIONS']['loaders'] = loaders
SESSION_ENGINE = "django.contrib.sessions.backends.db"
if 'SECRET_KEY' not in locals():
SECRET_KEY = 'PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHka'
if 'NOMCOM_APP_SECRET' not in locals():
NOMCOM_APP_SECRET = b'\x9b\xdas1\xec\xd5\xa0SI~\xcb\xd4\xf5t\x99\xc4i\xd7\x9f\x0b\xa9\xe8\xfeY\x80$\x1e\x12tN:\x84'
ALLOWED_HOSTS = ['*',]
try:
# see https://github.com/omarish/django-cprofile-middleware
import django_cprofile_middleware # pyflakes:ignore
MIDDLEWARE = MIDDLEWARE + ['django_cprofile_middleware.middleware.ProfilerMiddleware', ]
except ImportError:
pass
# Cannot have this set to True if we're using http: from the dev-server:
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_SAMESITE = 'Lax'
CSRF_TRUSTED_ORIGINS += ['http://localhost:8000', 'http://127.0.0.1:8000', 'http://[::1]:8000']
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_SAMESITE = 'Lax'
YOUTUBE_DOMAINS = ['www.youtube.com', 'youtube.com', 'youtu.be', 'm.youtube.com', 'youtube-nocookie.com', 'www.youtube-nocookie.com']