from decimal import Decimal import os import sys import django import dj_database_url import saml2 import saml2.saml from datetime import date, datetime, timedelta import pytz from dataclasses import dataclass PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) DJANGO_ROOT = os.path.abspath(os.path.dirname(django.__file__)) BASE_DIR = PACKAGE_ROOT sys.path.append(os.path.join(PROJECT_ROOT, 'vendor')) ### USER SETTINGS DEV_MODE = os.environ.get("SYMPOSION_DEV_MODE", None) DEBUG = os.environ.get('SYMPOSION_APP_DEBUG', '0') if isinstance(DEBUG, str): try: i = int(DEBUG) if not i in [0, 1]: raise ValueError("not 0 or 1") DEBUG = bool(i) except ValueError: sys.exit('DEBUG env var must be set to string value of a 0 or 1') else: sys.exit('DEBUG env var is in unexpected format. Should be a string' 'containing either a 0 or a 1 - Got type %s' % type(DEBUG)) DATABASES = {} DATABASES['default'] = dj_database_url.config(conn_max_age=600) if DATABASES['default']['ENGINE'] == 'django.db.backends.mysql': DATABASES['default']['OPTIONS'] = {'charset': 'utf8mb4'} EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = os.environ.get('EMAIL_HOST', None) EMAIL_PORT = os.environ.get('EMAIL_PORT', 25) EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', None) EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', None) DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'webmaster@localhost') EMAIL_USE_SSL = False EMAIL_USE_TLS = False _EMAIL_SSL_FLAVOR=os.environ.get('EMAIL_SSL_FLAVOR', None) if _EMAIL_SSL_FLAVOR == "TLS": EMAIL_USE_TLS = True elif _EMAIL_SSL_FLAVOR == "SSL": EMAIL_USE_SSL = True SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', None) PINAX_STRIPE_PUBLIC_KEY = os.environ.get('STRIPE_PUBLIC_KEY', None) PINAX_STRIPE_SECRET_KEY = os.environ.get('STRIPE_SECRET_KEY', None) PINAX_STRIPE_SEND_EMAIL_RECEIPTS = False ANALYTICS_KEY = os.environ.get('ANALYTICS_KEY', None) saml2_entityid = os.environ.get('SAML2_ENTITYID', None) saml2_sp_name = os.environ.get('SAML2_SP_NAME', None) saml2_sp_assertion_service = os.environ.get('SAML2_SP_ASSERTION_SERVICE', None) saml2_sp_slo_rdir = os.environ.get('SAML2_SP_SLO_RDIR', None) saml2_sp_slo_post = os.environ.get('SAML2_SP_SLO_POST', None) saml2_idp_metadata = { 'local': [os.environ.get('SAML2_IDP_METADATA_FILE', None)], } saml2_signing_key = os.environ.get('SAML2_SIGNING_KEY', None) saml2_signing_crt = os.environ.get('SAML2_SIGNING_CRT', None) saml2_encr_key = os.environ.get('SAML2_ENCRYPTION_KEY', None) saml2_encr_crt = os.environ.get('SAML2_ENCRYPTION_CRT', None) saml2_contact = { 'given_name': os.environ.get("META_GIVEN_NAME", 'Bastard'), 'sur_name': os.environ.get('META_FAM_NAME', 'Operator'), 'company': os.environ.get('META_COMPANY', 'Corp1'), 'email_address': os.environ.get('META_EMAIL', 'op@example.com'), 'contact_type': 'technical'}, fail = False BADGER_DEFAULT_SVG = 'registrasion/badge.svg' BADGER_DEFAULT_FORM = "registrasion/badge_form.html" if SECRET_KEY is None: print("FAILURE: You need to supply a DJANGO_SECRET_KEY " "environment variable") fail = True if PINAX_STRIPE_PUBLIC_KEY is None: print("FAILURE: You need to supply a STRIPE_PUBLIC_KEY " "environment variable") fail = True if PINAX_STRIPE_SECRET_KEY is None: print("FAILURE: You need to supply a STRIPE_SECRET_KEY " "environment variable") fail = True if fail: sys.exit('FAILURE: Missing environment variables.') ### Standard settings ADMIN_USERNAMES = [] if DEV_MODE and DEV_MODE == "LAPTOP": CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', } } else: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', } } ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*'] TIME_ZONE = "US/Pacific" DATE_FORMAT = "F j Y" LANGUAGE_CODE = "en-us" SITE_ID = int(os.environ.get("SITE_ID", 1)) USE_I18N = True USE_L10N = True USE_TZ = True MEDIA_ROOT = os.environ.get("MEDIA_ROOT", os.path.join(PACKAGE_ROOT, "site_media", "media")) MEDIA_URL = "/site_media/media/" STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static', 'build') STATIC_URL = '/static/build/' STATICFILES_DIRS = [ os.path.join(PROJECT_ROOT, 'static', 'src'), ] STATICFILES_FINDERS = [ "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", "sass_processor.finders.CssFinder", ] TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ os.path.join(PACKAGE_ROOT, "templates"), os.path.join(DJANGO_ROOT, 'forms/templates') ], "APP_DIRS": True, "OPTIONS": { "debug": DEBUG, "context_processors": [ "django.contrib.auth.context_processors.auth", "django.template.context_processors.debug", "django.template.context_processors.i18n", "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", "django.template.context_processors.request", "django.contrib.messages.context_processors.messages", "pinax_theme_bootstrap.context_processors.theme", "account.context_processors.account", "symposion.reviews.context_processors.reviews", "django_settings_export.settings_export", ], }, }, ] MIDDLEWARE = [ "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "account.middleware.LocaleMiddleware", "account.middleware.TimezoneMiddleware", "djangosaml2.middleware.SamlSessionMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "debug_toolbar.middleware.DebugToolbarMiddleware", "reversion.middleware.RevisionMiddleware", "waffle.middleware.WaffleMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware", 'pinaxcon.monkey_patch.MonkeyPatchMiddleware', ] if DEV_MODE and DEV_MODE == "LAPTOP": ROOT_URLCONF = "pinaxcon.devmode_urls" else: ROOT_URLCONF = "pinaxcon.urls" # Python dotted path to the WSGI application used by Django's runserver. WSGI_APPLICATION = "pinaxcon.wsgi.application" INSTALLED_APPS = [ "whitenoise.runserver_nostatic", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.flatpages", "django.contrib.messages", "django.contrib.sessions", "django.contrib.sites", "django.contrib.staticfiles", "django.contrib.humanize", "debug_toolbar", 'djangosaml2', # theme "bootstrapform", "pinax_theme_bootstrap", "sass_processor", "capture_tag", # external "easy_thumbnails", "taggit", "reversion", "sitetree", "django_jsonfield_backport", "pinax.eventlog", "timezone_field", # symposion "symposion", "symposion.conference", "symposion.proposals", "symposion.reviews", "symposion.schedule", "symposion.speakers", "symposion.teams", # Registrasion "registrasion", # Registrasion-stripe "pinax.stripe", "django_countries", "registripe", #registrasion-desk "regidesk", # admin - required by registrasion ?? "nested_admin", # project "pinaxcon", "pinaxcon.proposals", "pinaxcon.registrasion", "pinaxcon.raffle", "jquery", "djangoformsetjs", # testing and rollout "django_nose", "waffle", "crispy_forms", "account", ] CRISPY_TEMPLATE_PACK = "bootstrap4" DEBUG_TOOLBAR_PANELS = [ 'debug_toolbar.panels.versions.VersionsPanel', 'debug_toolbar.panels.timer.TimerPanel', 'debug_toolbar.panels.settings.SettingsPanel', 'debug_toolbar.panels.headers.HeadersPanel', 'debug_toolbar.panels.request.RequestPanel', 'debug_toolbar.panels.sql.SQLPanel', 'debug_toolbar.panels.staticfiles.StaticFilesPanel', 'debug_toolbar.panels.cache.CachePanel', 'debug_toolbar.panels.signals.SignalsPanel', 'debug_toolbar.panels.logging.LoggingPanel', 'debug_toolbar.panels.templates.TemplatesPanel', 'debug_toolbar.panels.redirects.RedirectsPanel', ] DEBUG_TOOLBAR_CONFIG = { 'INTERCEPT_REDIRECTS': False, } INTERNAL_IPS = [ '127.0.0.1', ] from debug_toolbar.panels.logging import collector LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' }, 'simple': { 'format': '%(asctime)s %(levelname)s $(module)s %(message)s' }, }, 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' } }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'mail_admins': { 'level': 'ERROR', 'filters': ['require_debug_false'], 'class': 'django.utils.log.AdminEmailHandler', 'include_html': True, }, 'djdt_log': { 'level': 'DEBUG', 'class': 'debug_toolbar.panels.logging.ThreadTrackingHandler', 'collector': collector, }, }, 'loggers': { 'django.request': { 'handlers': ['mail_admins'], 'level': 'DEBUG', 'propagate': True, }, 'symposion.request': { 'handlers': ['mail_admins'], 'level': 'DEBUG', 'propagate': True, }, }, 'root': { 'handlers': ['console', 'djdt_log'], 'level': 'DEBUG' }, } FIXTURE_DIRS = [ os.path.join(PROJECT_ROOT, "fixtures"), ] AUTHENTICATION_BACKENDS = [ 'symposion.teams.backends.TeamPermissionsBackend', 'django.contrib.auth.backends.ModelBackend', 'djangosaml2.backends.Saml2Backend', ] LOGIN_URL = '/account/login/' SESSION_EXPIRE_AT_BROWSER_CLOSE = True CONFERENCE_ID = 2 PROPOSAL_FORMS = { "talk": "pinaxcon.proposals.forms.TalkProposalForm", "tutorial": "pinaxcon.proposals.forms.TutorialProposalForm", "copyleft-compliance": "pinaxcon.proposals.forms.CopyleftComplianceProposalForm", "sfc-member-project": "pinaxcon.proposals.forms.MemberProjectProposalForm", } MAIN_CONFERENCE_PROPOSAL_KINDS = ("Talk",) # Registrasion bits: ATTENDEE_PROFILE_MODEL = "pinaxcon.registrasion.models.AttendeeProfile" ATTENDEE_PROFILE_FORM = "pinaxcon.registrasion.forms.ProfileForm" INVOICE_CURRENCY = "AUD" GST_RATE = Decimal('0.1') TICKET_PRODUCT_CATEGORY = 1 TERMS_PRODUCT_CATEGORY = 2 ATTENDEE_PROFILE_FORM = "pinaxcon.registrasion.forms.ProfileForm" #REGIDESK REGIDESK_BOARDING_GROUP = "Ready For Boarding" # CSRF custom error screen CSRF_FAILURE_VIEW = "pinaxcon.csrf_view.csrf_failure" # Use nose to run all tests TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' # Tell nose to measure coverage on the 'foo' and 'bar' apps NOSE_ARGS = [ '--with-coverage', '--cover-package=registrasion.controllers,registrasion.models', ] SASS_PROCESSOR_INCLUDE_DIRS = [ os.path.join(PROJECT_ROOT, 'static/src/bootstrap/scss'), os.path.join(PROJECT_ROOT, 'static/src/scss'), ] xmlsec_binary = '/usr/bin/xmlsec1' if not os.path.isfile(xmlsec_binary): sys.exit('ERROR: xmlsec1 binary missing, EXITING') SAML_ATTRIBUTE_MAPPING = { 'uid': ('username', ), 'mail': ('email', ), 'givenName': ('first_name', ), 'sn': ('last_name', ), } SAML_CONFIG = { 'xmlsec_binary': xmlsec_binary, 'entityid': saml2_entityid, 'attribute_map_dir': os.path.join(PACKAGE_ROOT, 'saml2/attribute-maps'), 'service': { 'sp': { 'name': saml2_sp_name, 'endpoints': { 'assertion_consumer_service': [ saml2_sp_assertion_service, ], 'single_logout_service': [ (saml2_sp_slo_rdir, saml2.BINDING_HTTP_REDIRECT), (saml2_sp_slo_post, saml2.BINDING_HTTP_POST), ], }, 'logout_requests_signed': True, 'required_attributes': ['uid', 'mail', 'givenName', 'sn'], 'allow_unsolicited': True, # Avoid issues with SameSite cookies for now. }, }, 'metadata': saml2_idp_metadata, 'debug': 0, 'key_file': saml2_signing_key, 'cert_file': saml2_signing_crt, 'encryption_keypairs': [{ 'key_file': saml2_encr_key, 'cert_file': saml2_encr_crt, }], 'contact_person': saml2_contact, 'valid_for': 10, } if 'SAML_CONFIG_LOADER' in os.environ: SAML_CONFIG_LOADER = os.environ.get('SAML_CONFIG_LOADER') DEFAULT_FILE_STORAGE = os.environ.get('DEFAULT_FILE_STORAGE', 'gapc_storage.storage.GoogleCloudStorage') GAPC_STORAGE = { 'num_retries': 2, } SETTINGS_EXPORT = [ 'DEBUG', 'ANALYTICS_KEY', 'TIME_ZONE', 'CONF_START', 'CONFERENCE_EMAIL', ] if DEV_MODE and DEV_MODE == "LAPTOP": print("ENABLING LAPTOP MODE") from .devmode_settings import * class Category(object): tickets = [] @classmethod def order(cls, ticket) -> int: return (cls.tickets.index(ticket) + 1) * 10 @dataclass(frozen=True) class Ticket: name: str regular_price: Decimal earlybird_price: Decimal def earlybird_discount(self): return self.regular_price - self.earlybird_price @dataclass(frozen=True) class DinnerTicket: name: str price: Decimal description: str reservation: timedelta cat: Category def order(self): return self.cat.order(self) class PenguinDinnerTicket(DinnerTicket): pass class SpeakersDinnerTicket(DinnerTicket): pass class SpeakersDinnerCat(Category): @classmethod def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> SpeakersDinnerTicket: t = SpeakersDinnerTicket(name, price, description, reservation, cls) cls.tickets.append(t) return t class PenguinDinnerCat(Category): @classmethod def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> PenguinDinnerTicket: t = PenguinDinnerTicket(name, price, description, reservation, cls) cls.tickets.append(t) return t CONFERENCE_NAME = os.environ.get('CONFERENCE_NAME', 'FOSSY') CONFERENCE_NAME_SHORT = os.environ.get('CONFERENCE_NAME_SHORT', 'FOSSY') CONFERENCE_EMAIL = os.environ.get('CONFERENCE_EMAIL', DEFAULT_FROM_EMAIL) CONF_TZINFO = pytz.timezone(TIME_ZONE) CONF_START = CONF_TZINFO.localize(datetime(2023, 7, 13)) CONF_END = CONF_TZINFO.localize(datetime(2023, 7, 16)) CONF_MINICONF_END = CONF_TZINFO.localize(datetime(2023, 3, 14, 23, 59)) EARLY_BIRD_DEADLINE = CONF_TZINFO.localize(datetime(2023, 1, 28)) PENGUIN_DINNER_TICKET_DATE = date(2023, 3, 15) SPEAKER_DINNER_TICKET_DATE = date(2023, 3, 14) PDNS_TICKET_DATE = date(2023, 3, 16) TSHIRT_PRICE = Decimal("25.00") CONTRIBUTOR = Ticket("Contributor", Decimal("300.00"), Decimal("250.00")) PROFESSIONAL = Ticket("Professional", Decimal("125.00"), Decimal("100.00")) HOBBYIST = Ticket("Hobbyist", Decimal("70.00"), None) STUDENT = Ticket("Student", Decimal("30.00"), None) MINICONF_ONLY = Ticket("Miniconf Only", Decimal("25.00"), None) MEDIA = Ticket("Media", Decimal("0.0"), None) SPEAKER = Ticket("Speaker", Decimal("0.0"), None) SPONSOR = Ticket("Sponsor", Decimal("0.0"), None) CONFERENCE_ORG = Ticket("Conference Organiser", Decimal("0.0"), None) CONFERENCE_VOL = Ticket("Conference Volunteer", Decimal("0.0"), None) PENGUIN_DINNER = PenguinDinnerCat PENGUIN_DINNER_ADULT = PenguinDinnerCat.create( "Adult", Decimal("95.00"), "Includes an adult's meal and full beverage service.", timedelta(hours=1)) PENGUIN_DINNER_CHILD = PenguinDinnerCat.create( "Child", Decimal("50.00"), "Children 14 and under. " "Includes a child's meal and soft drink service.", timedelta(hours=1)) PENGUIN_DINNER_INFANT = PenguinDinnerCat.create( "Infant", Decimal("0.0"), "Includes no food or beverage service.", timedelta(hours=1)) SPEAKERS_DINNER = SpeakersDinnerCat SPEAKERS_DINNER_ADULT = SpeakersDinnerCat.create( "Adult", Decimal("100.00"), "Includes an adult's meal and full beverage service.", timedelta(hours=1)) # SPEAKERS_DINNER_CHILD = SpeakersDinnerCat.create( # "Child", Decimal("60.00"), # "Children 14 and under. " # "Includes a child's meal and soft drink service.", # timedelta(hours=1)) # SPEAKERS_DINNER_INFANT = SpeakersDinnerCat.create( # "Infant", Decimal("00.00"), # "Infant must be seated in an adult's lap. " # "No food or beverage service.", # timedelta(hours=1)) # Venueless integration VENUELESS_URL = os.environ.get('VENUELESS_URL', None) VENUELESS_AUDIENCE = os.environ.get('VENUELESS_AUDIENCE', "venueless") VENUELESS_TOKEN_ISSUER = os.environ.get('VENUELESS_TOKEN_ISSUER', "any") VENUELESS_SECRET = os.environ.get('VENUELESS_SECRET', SECRET_KEY) ACCOUNT_SIGNUP_REDIRECT_URL = '/dashboard/' ACCOUNT_LOGIN_REDIRECT_URL = '/dashboard/'