/
settings.py
1324 lines (1127 loc) · 50.6 KB
/
settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# 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')
try:
import syslog
syslog.openlog(str("datatracker"), syslog.LOG_PID, syslog.LOG_USER)
except ImportError:
pass
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 don’t 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': ['debug_console', 'mail_admins'],
'level': 'INFO',
},
'django.request': {
'handlers': ['debug_console'],
'level': 'ERROR',
},
'django.server': {
'handlers': ['django.server'],
'level': 'INFO',
},
'django.security': {
'handlers': ['debug_console', ],
'level': 'INFO',
},
'oidc_provider': {
'handlers': ['debug_console', ],
'level': 'DEBUG',
},
'datatracker': {
'handlers': ['debug_console'],
'level': 'INFO',
},
'celery': {
'handlers': ['debug_console'],
'level': 'INFO',
},
},
#
# No logger filters
#
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'json',
},
'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' : {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter'
}
},
}
# 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',
]
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
'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',
'corsheaders',
'django_markup',
'oidc_provider',
'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.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',
)
# 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",
]
# 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
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
IPR_DOCUMENT_PATH = '/a/www/ietf-ftp/ietf/IPR/'
# 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'
DOCUMENT_FORMAT_ALLOWLIST = ["txt", "ps", "pdf", "xml", "html", ]
# Mailing list info URL for lists hosted on the IETF servers
MAILING_LIST_INFO_URL = "https://www.ietf.org/mailman/listinfo/%(list_addr)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 = "ietf-action@ietf.org"
SECRETARIAT_INFO_EMAIL = "ietf-info@ietf.org"
# 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_TEXT_RSYNC_SOURCE="ftp.rfc-editor.org::rfcs-text-only"
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_MANUAL_STAGING_DIR = '/tmp/'
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'
# ==============================================================================
RSYNC_BINARY = '/usr/bin/rsync'
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
HTPASSWD_COMMAND = "/usr/bin/htpasswd"
HTPASSWD_FILE = "/a/www/htpasswd"
# 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/'