"""
Django settings for .gov registrar project.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/

IF you'd like to see all of these settings in the running app:

```shell
$ docker-compose exec app python manage.py shell
>>> from django.conf import settings
>>> dir(settings)
```

"""
import environs
from base64 import b64decode
from cfenv import AppEnv  # type: ignore
from pathlib import Path
from typing import Final

from botocore.config import Config

# # #                          ###
#      Setup code goes here      #
# # #                          ###

env = environs.Env()

# Get secrets from Cloud.gov user provided service, if exists
# If not, get secrets from environment variables
key_service = AppEnv().get_service(name="getgov-credentials")
if key_service and key_service.credentials:
    secret = key_service.credentials.get
else:
    secret = env

# # #                          ###
#   Values obtained externally   #
# # #                          ###

path = Path(__file__)

env_db_url = env.dj_db_url("DATABASE_URL")
env_debug = env.bool("DJANGO_DEBUG", default=False)
env_is_production = env.bool("IS_PRODUCTION", default=False)
env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")
env_base_url = env.str("DJANGO_BASE_URL")
env_getgov_public_site_url = env.str("GETGOV_PUBLIC_SITE_URL", "")
env_oidc_active_provider = env.str("OIDC_ACTIVE_PROVIDER", "identity sandbox")

secret_login_key = b64decode(secret("DJANGO_SECRET_LOGIN_KEY", ""))
secret_key = secret("DJANGO_SECRET_KEY")

secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None)
secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None)

secret_registry_cl_id = secret("REGISTRY_CL_ID")
secret_registry_password = secret("REGISTRY_PASSWORD")
secret_registry_cert = b64decode(secret("REGISTRY_CERT", ""))
secret_registry_key = b64decode(secret("REGISTRY_KEY", ""))
secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "")
secret_registry_hostname = secret("REGISTRY_HOSTNAME")

# region: Basic Django Config-----------------------------------------------###

# Build paths inside the project like this: BASE_DIR / "subdir".
# (settings.py is in `src/registrar/config/`: BASE_DIR is `src/`)
BASE_DIR = path.resolve().parent.parent.parent

# SECURITY WARNING: don't run with debug turned on in production!
# TODO - Investigate the behaviour of this flag. Does not appear
# to function for the IS_PRODUCTION flag.
DEBUG = env_debug

# Controls production specific feature toggles
IS_PRODUCTION = env_is_production

# Applications are modular pieces of code.
# They are provided by Django, by third-parties, or by yourself.
# Installing them here makes them available for execution.
# Do not access INSTALLED_APPS directly. Use `django.apps.apps` instead.
INSTALLED_APPS = [
    # let's be sure to install our own application!
    # it needs to be listed before django.contrib.admin
    # otherwise Django would find the default template
    # provided by django.contrib.admin first and use
    # that instead of our custom templates.
    "registrar",
    # Django automatic admin interface reads metadata
    # from database models to provide a quick, model-centric
    # interface where trusted users can manage content
    "django.contrib.admin",
    # vv Required by django.contrib.admin vv
    # the "user" model! *\o/*
    "django.contrib.auth",
    # audit logging of changes to models
    # it needs to be listed before django.contrib.contenttypes
    # for a ContentType query in fixtures.py
    "auditlog",
    # generic interface for Django models
    "django.contrib.contenttypes",
    # required for CSRF protection and many other things
    "django.contrib.sessions",
    # framework for displaying messages to the user
    "django.contrib.messages",
    # ^^ Required by django.contrib.admin ^^
    # collects static files from each of your applications
    # (and any other places you specify) into a single location
    # that can easily be served in production
    "django.contrib.staticfiles",
    # application used for integrating with Login.gov
    "djangooidc",
    # library to simplify form templating
    "widget_tweaks",
    # library for Finite State Machine statuses
    "django_fsm",
    # library for phone numbers
    "phonenumber_field",
    # Our internal API application
    "api",
    # Only for generating documentation, uncomment to run manage.py generate_puml
    # "puml_generator",
    # supports necessary headers for Django cross origin
    "corsheaders",
]

# Middleware are routines for processing web requests.
# Adding them here turns them "on"; Django will perform the
# specified routines on each incoming request and outgoing response.
MIDDLEWARE = [
    # django-allow-cidr: enable use of CIDR IP ranges in ALLOWED_HOSTS
    "allow_cidr.middleware.AllowCIDRMiddleware",
    # django-cors-headers: listen to cors responses
    "corsheaders.middleware.CorsMiddleware",
    # custom middleware to stop caching from CloudFront
    "registrar.no_cache_middleware.NoCacheMiddleware",
    # serve static assets in production
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # provide security enhancements to the request/response cycle
    "django.middleware.security.SecurityMiddleware",
    # store and retrieve arbitrary data on a per-site-visitor basis
    "django.contrib.sessions.middleware.SessionMiddleware",
    # add a few conveniences for perfectionists, see documentation
    "django.middleware.common.CommonMiddleware",
    # add protection against Cross Site Request Forgeries by adding
    # hidden form fields to POST forms and checking requests for the correct value
    "django.middleware.csrf.CsrfViewMiddleware",
    # add `user` (the currently-logged-in user) to incoming HttpRequest objects
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    # Require login for every single request by default
    "login_required.middleware.LoginRequiredMiddleware",
    # provide framework for displaying messages to the user, see documentation
    "django.contrib.messages.middleware.MessageMiddleware",
    # provide clickjacking protection via the X-Frame-Options header
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    # django-csp: enable use of Content-Security-Policy header
    "csp.middleware.CSPMiddleware",
    # django-auditlog: obtain the request User for use in logging
    "auditlog.middleware.AuditlogMiddleware",
]

# application object used by Django’s built-in servers (e.g. `runserver`)
WSGI_APPLICATION = "registrar.config.wsgi.application"

# endregion
# region: Assets and HTML and Caching---------------------------------------###

# https://docs.djangoproject.com/en/4.0/howto/static-files/


# Caching is disabled by default.
# For a low to medium traffic site, caching causes more
# problems than it solves. Should caching be desired,
# a reasonable start might be:
# CACHES = {
#     "default": {
#         "BACKEND": "django.core.cache.backends.db.DatabaseCache",
#     }
# }

# Absolute path to the directory where `collectstatic`
# will place static files for deployment.
# Do not use this directory for permanent storage -
# it is for Django!
STATIC_ROOT = BASE_DIR / "registrar" / "public"

STATICFILES_DIRS = [
    BASE_DIR / "registrar" / "assets",
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "registrar" / "templates"],
        # look for templates inside installed apps
        #     required by django-debug-toolbar
        "APP_DIRS": True,
        "OPTIONS": {
            # IMPORTANT security setting: escapes HTMLEntities,
            #     helping to prevent XSS attacks
            "autoescape": True,
            # context processors are callables which return
            #     dicts - Django merges them into the context
            #     dictionary used to render the templates
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                "registrar.context_processors.language_code",
                "registrar.context_processors.canonical_path",
                "registrar.context_processors.is_demo_site",
                "registrar.context_processors.is_production",
            ],
        },
    },
]

# Stop using table-based default form renderer which is deprecated
FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"

MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"

# IS_DEMO_SITE controls whether or not we show our big red "TEST SITE" banner
# underneath the "this is a real government website" banner.
IS_DEMO_SITE = True

# endregion
# region: Database----------------------------------------------------------###

# Wrap each view in a transaction on the database
# A decorator can be used for views which have no database activity:
#     from django.db import transaction
#     @transaction.non_atomic_requests
env_db_url["ATOMIC_REQUESTS"] = True

DATABASES = {
    # dj-database-url package takes the supplied Postgres connection string
    # and converts it into a dictionary with the correct USER, HOST, etc
    "default": env_db_url,
}

# Specify default field type to use for primary keys
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Use our user model instead of the default
AUTH_USER_MODEL = "registrar.User"

# endregion
# region: Email-------------------------------------------------------------###

# Configuration for accessing AWS SES
AWS_ACCESS_KEY_ID = secret_aws_ses_key_id
AWS_SECRET_ACCESS_KEY = secret_aws_ses_key
AWS_REGION = "us-gov-west-1"
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode
AWS_RETRY_MODE: Final = "standard"
# base 2 exponential backoff with max of 20 seconds:
AWS_MAX_ATTEMPTS = 3
BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS})

# email address to use for various automated correspondence
DEFAULT_FROM_EMAIL = "help@get.gov <help@get.gov>"

# connect to an (external) SMTP server for sending email
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

# TODO: configure these when the values are known
# EMAIL_HOST = ""
# EMAIL_HOST_PASSWORD = ""
# EMAIL_HOST_USER = ""
# EMAIL_PORT = 587

# for mail sent with mail_admins or mail_managers
EMAIL_SUBJECT_PREFIX = "[Attn: .gov admin] "

# use a TLS (secure) connection when talking to the SMTP server
# TLS generally uses port 587
EMAIL_USE_TLS = True

# mutually exclusive with EMAIL_USE_TLS = True
# SSL generally uses port 465
EMAIL_USE_SSL = False

# timeout in seconds for blocking operations, like the connection attempt
EMAIL_TIMEOUT = 30

# email address to use for sending error reports
SERVER_EMAIL = "root@get.gov"

# endregion
# region: Headers-----------------------------------------------------------###

# Content-Security-Policy configuration
# this can be restrictive because we have few external scripts
allowed_sources = ("'self'",)
CSP_DEFAULT_SRC = allowed_sources
# Most things fall back to default-src, but these two do not and should be
# explicitly set
CSP_FRAME_ANCESTORS = allowed_sources
CSP_FORM_ACTION = allowed_sources

# Cross-Origin Resource Sharing (CORS) configuration
# Sets clients that allow access control to manage.get.gov
# TODO: remove :8080 to see if we can have all localhost access
CORS_ALLOWED_ORIGINS = ["http://localhost:8080", "https://beta.get.gov"]
CORS_ALLOWED_ORIGIN_REGEXES = [r"https://[\w-]+\.sites\.pages\.cloud\.gov"]

# Content-Length header is set by django.middleware.common.CommonMiddleware

# X-Frame-Options header is set by
#     django.middleware.clickjacking.XFrameOptionsMiddleware
#     and configured in the Security and Privacy section of this file.
# Strict-Transport-Security is set by django.middleware.security.SecurityMiddleware
#     and configured in the Security and Privacy section of this file.

# prefer contents of X-Forwarded-Host header to Host header
# as Host header may contain a proxy rather than the actual client
USE_X_FORWARDED_HOST = True

# endregion
# region: Internationalisation----------------------------------------------###

# https://docs.djangoproject.com/en/4.0/topics/i18n/

# Charset to use for HttpResponse objects; used in Content-Type header
DEFAULT_CHARSET = "utf-8"

# provide fallback language if translation file is missing or
# user's locale is not supported - requires USE_I18N = True
LANGUAGE_CODE = "en-us"

# allows language cookie to be sent if the user
# is coming to our site from an external page.
LANGUAGE_COOKIE_SAMESITE = None

# only send via HTTPS connection
LANGUAGE_COOKIE_SECURE = True

# to display datetimes in templates
# and to interpret datetimes entered in forms
TIME_ZONE = "UTC"

# enable Django’s translation system
USE_I18N = True

# enable localized formatting of numbers and dates
USE_L10N = True

# make datetimes timezone-aware by default
USE_TZ = True

# setting for phonenumber library
PHONENUMBER_DEFAULT_REGION = "US"

# endregion
# region: Logging-----------------------------------------------------------###

# A Python logging configuration consists of four parts:
#   Loggers
#   Handlers
#   Filters
#   Formatters
# https://docs.djangoproject.com/en/4.1/topics/logging/

# Log a message by doing this:
#
#   import logging
#   logger = logging.getLogger(__name__)
#
# Then:
#
#   logger.debug("We're about to execute function xyz. Wish us luck!")
#   logger.info("Oh! Here's something you might want to know.")
#   logger.warning("Something kinda bad happened.")
#   logger.error("Can't do this important task. Something is very wrong.")
#   logger.critical("Going to crash now.")

LOGGING = {
    "version": 1,
    # Don't import Django's existing loggers
    "disable_existing_loggers": True,
    # define how to convert log messages into text;
    # each handler has its choice of format
    "formatters": {
        "verbose": {
            "format": "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            "datefmt": "%d/%b/%Y %H:%M:%S",
        },
        "simple": {
            "format": "%(levelname)s %(message)s",
        },
        "django.server": {
            "()": "django.utils.log.ServerFormatter",
            "format": "[{server_time}] {message}",
            "style": "{",
        },
    },
    # define where log messages will be sent;
    # each logger can have one or more handlers
    "handlers": {
        "console": {
            "level": env_log_level,
            "class": "logging.StreamHandler",
            "formatter": "verbose",
        },
        "django.server": {
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "django.server",
        },
        # No file logger is configured,
        # because containerized apps
        # do not log to the file system.
    },
    # define loggers: these are "sinks" into which
    # messages are sent for processing
    "loggers": {
        # Django's generic logger
        "django": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's template processor
        "django.template": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's runserver
        "django.server": {
            "handlers": ["django.server"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's runserver requests
        "django.request": {
            "handlers": ["django.server"],
            "level": "INFO",
            "propagate": False,
        },
        # OpenID Connect logger
        "oic": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django wrapper for OpenID Connect
        "djangooidc": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Our app!
        "registrar": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": False,
        },
    },
    # root logger catches anything, unless
    # defined by a more specific logger
    "root": {
        "handlers": ["console"],
        "level": "INFO",
    },
}

# endregion
# region: Login-------------------------------------------------------------###

# list of Python classes used when trying to authenticate a user
AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "djangooidc.backends.OpenIdConnectBackend",
]

# this is where unauthenticated requests are redirected when using
# the login_required() decorator, LoginRequiredMixin, or AccessMixin
LOGIN_URL = "/openid/login"

# We don't want the OIDC app to be login-required because then it can't handle
# the initial login requests without erroring.
LOGIN_REQUIRED_IGNORE_PATHS = [
    r"/openid/(.+)$",
]

# where to go after logging out
LOGOUT_REDIRECT_URL = "home"

# disable dynamic client registration,
# only the OP inside OIDC_PROVIDERS will be available
OIDC_ALLOW_DYNAMIC_OP = False

# which provider to use if multiple are available
# (code does not currently support user selection)
# See above for the default value if the env variable is missing
OIDC_ACTIVE_PROVIDER = env_oidc_active_provider


OIDC_PROVIDERS = {
    "identity sandbox": {
        "srv_discovery_url": "https://idp.int.identitysandbox.gov",
        "behaviour": {
            # the 'code' workflow requires direct connectivity from us to Login.gov
            "response_type": "code",
            "scope": ["email", "profile:name", "phone"],
            "user_info_request": ["email", "first_name", "last_name", "phone"],
            "acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
        },
        "client_registration": {
            "client_id": "cisa_dotgov_registrar",
            "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
            "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
            "token_endpoint_auth_method": ["private_key_jwt"],
            "sp_private_key": secret_login_key,
        },
    },
    "login.gov production": {
        "srv_discovery_url": "https://secure.login.gov",
        "behaviour": {
            # the 'code' workflow requires direct connectivity from us to Login.gov
            "response_type": "code",
            "scope": ["email", "profile:name", "phone"],
            "user_info_request": ["email", "first_name", "last_name", "phone"],
            "acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
        },
        "client_registration": {
            "client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
            "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
            "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
            "token_endpoint_auth_method": ["private_key_jwt"],
            "sp_private_key": secret_login_key,
        },
    },
}

# endregion
# region: Routing-----------------------------------------------------------###

# ~ Set by django.middleware.common.CommonMiddleware
# APPEND_SLASH = True
# PREPEND_WWW = False

# full Python import path to the root URLconf
ROOT_URLCONF = "registrar.config.urls"

# URL to use when referring to static files located in STATIC_ROOT
# Must be relative and end with "/"
STATIC_URL = "public/"

# Base URL of our separate static public website. Used by the
# {% public_site_url subdir/path %} template tag
GETGOV_PUBLIC_SITE_URL = env_getgov_public_site_url

# endregion
# region: Registry----------------------------------------------------------###

# SECURITY WARNING: keep all registry variables in production secret!
SECRET_REGISTRY_CL_ID = secret_registry_cl_id
SECRET_REGISTRY_PASSWORD = secret_registry_password
SECRET_REGISTRY_CERT = secret_registry_cert
SECRET_REGISTRY_KEY = secret_registry_key
SECRET_REGISTRY_KEY_PASSPHRASE = secret_registry_key_passphrase
SECRET_REGISTRY_HOSTNAME = secret_registry_hostname

# Use this variable to set the size of our connection pool in client.py
# WARNING: Setting this value too high could cause frequent app crashes!
# Having too many connections open could cause the sandbox to timeout,
# as the spinup time could exceed the timeout time.
EPP_CONNECTION_POOL_SIZE = 1

# Determines the interval in which we ping open connections in seconds
# Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE
POOL_KEEP_ALIVE = 60

# Determines how long we try to keep a pool alive for,
# before restarting it.
POOL_TIMEOUT = 60

# endregion
# region: Security and Privacy----------------------------------------------###

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = secret_key

# Use this variable for doing SECRET_KEY rotation, see documentation
SECRET_KEY_FALLBACKS: "list[str]" = []

# ~ Set by django.middleware.security.SecurityMiddleware
# SECURE_CONTENT_TYPE_NOSNIFF = True
# SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
# SECURE_REDIRECT_EXEMPT = []
# SECURE_REFERRER_POLICY = "same-origin"
# SECURE_SSL_HOST = None

# ~ Overridden from django.middleware.security.SecurityMiddleware
# adds the includeSubDomains directive to the HTTP Strict Transport Security header
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# adds the preload directive to the HTTP Strict Transport Security header
SECURE_HSTS_PRELOAD = True
# TODO: set this value to 31536000 (1 year) for production
SECURE_HSTS_SECONDS = 300
# redirect all non-HTTPS requests to HTTPS
SECURE_SSL_REDIRECT = True

# ~ Set by django.middleware.common.CommonMiddleware
# DISALLOWED_USER_AGENTS = []

# The host/domain names that Django can serve.
# This is a security measure to prevent HTTP Host header attacks,
# which are possible even under many seemingly-safe
# web server configurations.
ALLOWED_HOSTS = [
    "getgov-stable.app.cloud.gov",
    "getgov-staging.app.cloud.gov",
    "getgov-es.app.cloud.gov",
    "getgov-nl.app.cloud.gov",
    "getgov-rh.app.cloud.gov",
    "getgov-za.app.cloud.gov",
    "getgov-gd.app.cloud.gov",
    "getgov-rb.app.cloud.gov",
    "getgov-ko.app.cloud.gov",
    "getgov-ab.app.cloud.gov",
    "getgov-bl.app.cloud.gov",
    "getgov-rjm.app.cloud.gov",
    "getgov-dk.app.cloud.gov",
    "manage.get.gov",
]

# Extend ALLOWED_HOSTS.
# IP addresses can also be hosts, which are used by internal
# load balancers for health checks, etc.
ALLOWED_CIDR_NETS = ["10.0.0.0/8"]

# ~ Below are some protections from cross-site request forgery.
# This is canonically done by including a nonce value
# in pages sent to the user, which the user is expected
# to send back. The specifics of implementation are
# intricate and varied.

# Store the token server-side, do not send it
# to the user via a cookie. This means each page
# which requires protection must place the token
# in the HTML explicitly, otherwise the user will
# get a 403 error when they submit.
CSRF_USE_SESSIONS = True

# Expiry of CSRF cookie, in seconds.
# None means "use session-based CSRF cookies".
CSRF_COOKIE_AGE = None

# Prevent JavaScript from reading the CSRF cookie.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_HTTPONLY = True

# Only send the cookie via HTTPS connections.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_SECURE = True

# Protect from non-targeted attacks by obscuring
# the CSRF cookie name from the default.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_NAME = "CrSiReFo"

# Prevents CSRF cookie from being sent if the user
# is coming to our site from an external page.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_SAMESITE = "Strict"

# Change header name to match cookie name.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_HEADER_NAME = "HTTP_X_CRSIREFO"

# Max parameters that may be received via GET or POST
# TODO: 1000 is the default, may need to tune upward for
# large DNS zone files, if records are represented by
# individual form fields.
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000

# age of session cookies, in seconds (28800 = 8 hours)
SESSION_COOKIE_AGE = 28800

# instruct the browser to forbid client-side JavaScript
# from accessing the cookie
SESSION_COOKIE_HTTPONLY = True

# are we a spring boot application? who knows!
SESSION_COOKIE_NAME = "JSESSIONID"

# Allows session cookie to be sent if the user
# is coming to our site from an external page
# unless it is via "risky" paths, i.e. POST requests
SESSION_COOKIE_SAMESITE = "Lax"

# instruct browser to only send cookie via HTTPS
SESSION_COOKIE_SECURE = True

# session engine to cache session information
SESSION_ENGINE = "django.contrib.sessions.backends.cache"

# ~ Set by django.middleware.clickjacking.XFrameOptionsMiddleware
# prevent clickjacking by instructing the browser not to load
# our site within an iframe
# X_FRAME_OPTIONS = "Deny"

# endregion
# region: Testing-----------------------------------------------------------###

# Additional directories searched for fixture files.
# The fixtures directory of each application is searched by default.
# Must use unix style "/" path separators.
FIXTURE_DIRS: "list[str]" = []

# endregion


# # #                          ###
#      Development settings      #
# # #                          ###

if DEBUG:
    # used by debug() context processor
    INTERNAL_IPS = [
        "127.0.0.1",
        "::1",
    ]

    # allow dev laptop and docker-compose network to connect
    ALLOWED_HOSTS += ("localhost", "app")
    SECURE_SSL_REDIRECT = False
    SECURE_HSTS_PRELOAD = False

    # discover potentially inefficient database queries
    # TODO: use settings overrides to ensure this always is True during tests
    INSTALLED_APPS += ("nplusone.ext.django",)
    MIDDLEWARE += ("nplusone.ext.django.NPlusOneMiddleware",)
    # turned off for now, because django-auditlog has some issues
    NPLUSONE_RAISE = False
    NPLUSONE_WHITELIST = [
        {"model": "admin.LogEntry", "field": "user"},
    ]

    # insert the amazing django-debug-toolbar
    INSTALLED_APPS += ("debug_toolbar",)
    MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")

    DEBUG_TOOLBAR_CONFIG = {
        # due to Docker, bypass Debug Toolbar's check on INTERNAL_IPS
        "SHOW_TOOLBAR_CALLBACK": lambda _: True,
    }

src/registrar/config/settings.py
"""
Django settings for .gov registrar project.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/

IF you'd like to see all of these settings in the running app:

```shell
$ docker-compose exec app python manage.py shell
>>> from django.conf import settings
>>> dir(settings)
```

"""
import environs
from base64 import b64decode
from cfenv import AppEnv  # type: ignore
from pathlib import Path
from typing import Final

from botocore.config import Config

# # #                          ###
#      Setup code goes here      #
# # #                          ###

env = environs.Env()

# Get secrets from Cloud.gov user provided service, if exists
# If not, get secrets from environment variables
key_service = AppEnv().get_service(name="getgov-credentials")
if key_service and key_service.credentials:
    secret = key_service.credentials.get
else:
    secret = env

# # #                          ###
#   Values obtained externally   #
# # #                          ###

path = Path(__file__)

env_db_url = env.dj_db_url("DATABASE_URL")
env_debug = env.bool("DJANGO_DEBUG", default=False)
env_is_production = env.bool("IS_PRODUCTION", default=False)
env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")
env_base_url = env.str("DJANGO_BASE_URL")
env_getgov_public_site_url = env.str("GETGOV_PUBLIC_SITE_URL", "")
env_oidc_active_provider = env.str("OIDC_ACTIVE_PROVIDER", "identity sandbox")

secret_login_key = b64decode(secret("DJANGO_SECRET_LOGIN_KEY", ""))
secret_key = secret("DJANGO_SECRET_KEY")

secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None)
secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None)

secret_registry_cl_id = secret("REGISTRY_CL_ID")
secret_registry_password = secret("REGISTRY_PASSWORD")
secret_registry_cert = b64decode(secret("REGISTRY_CERT", ""))
secret_registry_key = b64decode(secret("REGISTRY_KEY", ""))
secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "")
secret_registry_hostname = secret("REGISTRY_HOSTNAME")

# region: Basic Django Config-----------------------------------------------###

# Build paths inside the project like this: BASE_DIR / "subdir".
# (settings.py is in `src/registrar/config/`: BASE_DIR is `src/`)
BASE_DIR = path.resolve().parent.parent.parent

# SECURITY WARNING: don't run with debug turned on in production!
# TODO - Investigate the behaviour of this flag. Does not appear
# to function for the IS_PRODUCTION flag.
DEBUG = env_debug

# Controls production specific feature toggles
IS_PRODUCTION = env_is_production

# Applications are modular pieces of code.
# They are provided by Django, by third-parties, or by yourself.
# Installing them here makes them available for execution.
# Do not access INSTALLED_APPS directly. Use `django.apps.apps` instead.
INSTALLED_APPS = [
    # let's be sure to install our own application!
    # it needs to be listed before django.contrib.admin
    # otherwise Django would find the default template
    # provided by django.contrib.admin first and use
    # that instead of our custom templates.
    "registrar",
    # Django automatic admin interface reads metadata
    # from database models to provide a quick, model-centric
    # interface where trusted users can manage content
    "django.contrib.admin",
    # vv Required by django.contrib.admin vv
    # the "user" model! *\o/*
    "django.contrib.auth",
    # audit logging of changes to models
    # it needs to be listed before django.contrib.contenttypes
    # for a ContentType query in fixtures.py
    "auditlog",
    # generic interface for Django models
    "django.contrib.contenttypes",
    # required for CSRF protection and many other things
    "django.contrib.sessions",
    # framework for displaying messages to the user
    "django.contrib.messages",
    # ^^ Required by django.contrib.admin ^^
    # collects static files from each of your applications
    # (and any other places you specify) into a single location
    # that can easily be served in production
    "django.contrib.staticfiles",
    # application used for integrating with Login.gov
    "djangooidc",
    # library to simplify form templating
    "widget_tweaks",
    # library for Finite State Machine statuses
    "django_fsm",
    # library for phone numbers
    "phonenumber_field",
    # Our internal API application
    "api",
    # Only for generating documentation, uncomment to run manage.py generate_puml
    # "puml_generator",
    # supports necessary headers for Django cross origin
    "corsheaders",
]

# Middleware are routines for processing web requests.
# Adding them here turns them "on"; Django will perform the
# specified routines on each incoming request and outgoing response.
MIDDLEWARE = [
    # django-allow-cidr: enable use of CIDR IP ranges in ALLOWED_HOSTS
    "allow_cidr.middleware.AllowCIDRMiddleware",
    # django-cors-headers: listen to cors responses
    "corsheaders.middleware.CorsMiddleware",
    # custom middleware to stop caching from CloudFront
    "registrar.no_cache_middleware.NoCacheMiddleware",
    # serve static assets in production
    "whitenoise.middleware.WhiteNoiseMiddleware",
    # provide security enhancements to the request/response cycle
    "django.middleware.security.SecurityMiddleware",
    # store and retrieve arbitrary data on a per-site-visitor basis
    "django.contrib.sessions.middleware.SessionMiddleware",
    # add a few conveniences for perfectionists, see documentation
    "django.middleware.common.CommonMiddleware",
    # add protection against Cross Site Request Forgeries by adding
    # hidden form fields to POST forms and checking requests for the correct value
    "django.middleware.csrf.CsrfViewMiddleware",
    # add `user` (the currently-logged-in user) to incoming HttpRequest objects
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    # Require login for every single request by default
    "login_required.middleware.LoginRequiredMiddleware",
    # provide framework for displaying messages to the user, see documentation
    "django.contrib.messages.middleware.MessageMiddleware",
    # provide clickjacking protection via the X-Frame-Options header
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    # django-csp: enable use of Content-Security-Policy header
    "csp.middleware.CSPMiddleware",
    # django-auditlog: obtain the request User for use in logging
    "auditlog.middleware.AuditlogMiddleware",
]

# application object used by Django’s built-in servers (e.g. `runserver`)
WSGI_APPLICATION = "registrar.config.wsgi.application"

# endregion
# region: Assets and HTML and Caching---------------------------------------###

# https://docs.djangoproject.com/en/4.0/howto/static-files/


# Caching is disabled by default.
# For a low to medium traffic site, caching causes more
# problems than it solves. Should caching be desired,
# a reasonable start might be:
# CACHES = {
#     "default": {
#         "BACKEND": "django.core.cache.backends.db.DatabaseCache",
#     }
# }

# Absolute path to the directory where `collectstatic`
# will place static files for deployment.
# Do not use this directory for permanent storage -
# it is for Django!
STATIC_ROOT = BASE_DIR / "registrar" / "public"

STATICFILES_DIRS = [
    BASE_DIR / "registrar" / "assets",
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "registrar" / "templates"],
        # look for templates inside installed apps
        #     required by django-debug-toolbar
        "APP_DIRS": True,
        "OPTIONS": {
            # IMPORTANT security setting: escapes HTMLEntities,
            #     helping to prevent XSS attacks
            "autoescape": True,
            # context processors are callables which return
            #     dicts - Django merges them into the context
            #     dictionary used to render the templates
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                "registrar.context_processors.language_code",
                "registrar.context_processors.canonical_path",
                "registrar.context_processors.is_demo_site",
                "registrar.context_processors.is_production",
            ],
        },
    },
]

# Stop using table-based default form renderer which is deprecated
FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"

MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"

# IS_DEMO_SITE controls whether or not we show our big red "TEST SITE" banner
# underneath the "this is a real government website" banner.
IS_DEMO_SITE = True

# endregion
# region: Database----------------------------------------------------------###

# Wrap each view in a transaction on the database
# A decorator can be used for views which have no database activity:
#     from django.db import transaction
#     @transaction.non_atomic_requests
env_db_url["ATOMIC_REQUESTS"] = True

DATABASES = {
    # dj-database-url package takes the supplied Postgres connection string
    # and converts it into a dictionary with the correct USER, HOST, etc
    "default": env_db_url,
}

# Specify default field type to use for primary keys
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Use our user model instead of the default
AUTH_USER_MODEL = "registrar.User"

# endregion
# region: Email-------------------------------------------------------------###

# Configuration for accessing AWS SES
AWS_ACCESS_KEY_ID = secret_aws_ses_key_id
AWS_SECRET_ACCESS_KEY = secret_aws_ses_key
AWS_REGION = "us-gov-west-1"
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode
AWS_RETRY_MODE: Final = "standard"
# base 2 exponential backoff with max of 20 seconds:
AWS_MAX_ATTEMPTS = 3
BOTO_CONFIG = Config(retries={"mode": AWS_RETRY_MODE, "max_attempts": AWS_MAX_ATTEMPTS})

# email address to use for various automated correspondence
DEFAULT_FROM_EMAIL = "help@get.gov <help@get.gov>"

# connect to an (external) SMTP server for sending email
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

# TODO: configure these when the values are known
# EMAIL_HOST = ""
# EMAIL_HOST_PASSWORD = ""
# EMAIL_HOST_USER = ""
# EMAIL_PORT = 587

# for mail sent with mail_admins or mail_managers
EMAIL_SUBJECT_PREFIX = "[Attn: .gov admin] "

# use a TLS (secure) connection when talking to the SMTP server
# TLS generally uses port 587
EMAIL_USE_TLS = True

# mutually exclusive with EMAIL_USE_TLS = True
# SSL generally uses port 465
EMAIL_USE_SSL = False

# timeout in seconds for blocking operations, like the connection attempt
EMAIL_TIMEOUT = 30

# email address to use for sending error reports
SERVER_EMAIL = "root@get.gov"

# endregion
# region: Headers-----------------------------------------------------------###

# Content-Security-Policy configuration
# this can be restrictive because we have few external scripts
allowed_sources = ("'self'",)
CSP_DEFAULT_SRC = allowed_sources
# Most things fall back to default-src, but these two do not and should be
# explicitly set
CSP_FRAME_ANCESTORS = allowed_sources
CSP_FORM_ACTION = allowed_sources

# Cross-Origin Resource Sharing (CORS) configuration
# Sets clients that allow access control to manage.get.gov
# TODO: remove :8080 to see if we can have all localhost access
CORS_ALLOWED_ORIGINS = ["http://localhost:8080", "https://beta.get.gov"]
CORS_ALLOWED_ORIGIN_REGEXES = [r"https://[\w-]+\.sites\.pages\.cloud\.gov"]

# Content-Length header is set by django.middleware.common.CommonMiddleware

# X-Frame-Options header is set by
#     django.middleware.clickjacking.XFrameOptionsMiddleware
#     and configured in the Security and Privacy section of this file.
# Strict-Transport-Security is set by django.middleware.security.SecurityMiddleware
#     and configured in the Security and Privacy section of this file.

# prefer contents of X-Forwarded-Host header to Host header
# as Host header may contain a proxy rather than the actual client
USE_X_FORWARDED_HOST = True

# endregion
# region: Internationalisation----------------------------------------------###

# https://docs.djangoproject.com/en/4.0/topics/i18n/

# Charset to use for HttpResponse objects; used in Content-Type header
DEFAULT_CHARSET = "utf-8"

# provide fallback language if translation file is missing or
# user's locale is not supported - requires USE_I18N = True
LANGUAGE_CODE = "en-us"

# allows language cookie to be sent if the user
# is coming to our site from an external page.
LANGUAGE_COOKIE_SAMESITE = None

# only send via HTTPS connection
LANGUAGE_COOKIE_SECURE = True

# to display datetimes in templates
# and to interpret datetimes entered in forms
TIME_ZONE = "UTC"

# enable Django’s translation system
USE_I18N = True

# enable localized formatting of numbers and dates
USE_L10N = True

# make datetimes timezone-aware by default
USE_TZ = True

# setting for phonenumber library
PHONENUMBER_DEFAULT_REGION = "US"

# endregion
# region: Logging-----------------------------------------------------------###

# A Python logging configuration consists of four parts:
#   Loggers
#   Handlers
#   Filters
#   Formatters
# https://docs.djangoproject.com/en/4.1/topics/logging/

# Log a message by doing this:
#
#   import logging
#   logger = logging.getLogger(__name__)
#
# Then:
#
#   logger.debug("We're about to execute function xyz. Wish us luck!")
#   logger.info("Oh! Here's something you might want to know.")
#   logger.warning("Something kinda bad happened.")
#   logger.error("Can't do this important task. Something is very wrong.")
#   logger.critical("Going to crash now.")

LOGGING = {
    "version": 1,
    # Don't import Django's existing loggers
    "disable_existing_loggers": True,
    # define how to convert log messages into text;
    # each handler has its choice of format
    "formatters": {
        "verbose": {
            "format": "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            "datefmt": "%d/%b/%Y %H:%M:%S",
        },
        "simple": {
            "format": "%(levelname)s %(message)s",
        },
        "django.server": {
            "()": "django.utils.log.ServerFormatter",
            "format": "[{server_time}] {message}",
            "style": "{",
        },
    },
    # define where log messages will be sent;
    # each logger can have one or more handlers
    "handlers": {
        "console": {
            "level": env_log_level,
            "class": "logging.StreamHandler",
            "formatter": "verbose",
        },
        "django.server": {
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "django.server",
        },
        # No file logger is configured,
        # because containerized apps
        # do not log to the file system.
    },
    # define loggers: these are "sinks" into which
    # messages are sent for processing
    "loggers": {
        # Django's generic logger
        "django": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's template processor
        "django.template": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's runserver
        "django.server": {
            "handlers": ["django.server"],
            "level": "INFO",
            "propagate": False,
        },
        # Django's runserver requests
        "django.request": {
            "handlers": ["django.server"],
            "level": "INFO",
            "propagate": False,
        },
        # OpenID Connect logger
        "oic": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Django wrapper for OpenID Connect
        "djangooidc": {
            "handlers": ["console"],
            "level": "INFO",
            "propagate": False,
        },
        # Our app!
        "registrar": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": False,
        },
    },
    # root logger catches anything, unless
    # defined by a more specific logger
    "root": {
        "handlers": ["console"],
        "level": "INFO",
    },
}

# endregion
# region: Login-------------------------------------------------------------###

# list of Python classes used when trying to authenticate a user
AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "djangooidc.backends.OpenIdConnectBackend",
]

# this is where unauthenticated requests are redirected when using
# the login_required() decorator, LoginRequiredMixin, or AccessMixin
LOGIN_URL = "/openid/login"

# We don't want the OIDC app to be login-required because then it can't handle
# the initial login requests without erroring.
LOGIN_REQUIRED_IGNORE_PATHS = [
    r"/openid/(.+)$",
]

# where to go after logging out
LOGOUT_REDIRECT_URL = "home"

# disable dynamic client registration,
# only the OP inside OIDC_PROVIDERS will be available
OIDC_ALLOW_DYNAMIC_OP = False

# which provider to use if multiple are available
# (code does not currently support user selection)
# See above for the default value if the env variable is missing
OIDC_ACTIVE_PROVIDER = env_oidc_active_provider


OIDC_PROVIDERS = {
    "identity sandbox": {
        "srv_discovery_url": "https://idp.int.identitysandbox.gov",
        "behaviour": {
            # the 'code' workflow requires direct connectivity from us to Login.gov
            "response_type": "code",
            "scope": ["email", "profile:name", "phone"],
            "user_info_request": ["email", "first_name", "last_name", "phone"],
            "acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
        },
        "client_registration": {
            "client_id": "cisa_dotgov_registrar",
            "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
            "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
            "token_endpoint_auth_method": ["private_key_jwt"],
            "sp_private_key": secret_login_key,
        },
    },
    "login.gov production": {
        "srv_discovery_url": "https://secure.login.gov",
        "behaviour": {
            # the 'code' workflow requires direct connectivity from us to Login.gov
            "response_type": "code",
            "scope": ["email", "profile:name", "phone"],
            "user_info_request": ["email", "first_name", "last_name", "phone"],
            "acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
        },
        "client_registration": {
            "client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
            "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
            "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
            "token_endpoint_auth_method": ["private_key_jwt"],
            "sp_private_key": secret_login_key,
        },
    },
}

# endregion
# region: Routing-----------------------------------------------------------###

# ~ Set by django.middleware.common.CommonMiddleware
# APPEND_SLASH = True
# PREPEND_WWW = False

# full Python import path to the root URLconf
ROOT_URLCONF = "registrar.config.urls"

# URL to use when referring to static files located in STATIC_ROOT
# Must be relative and end with "/"
STATIC_URL = "public/"

# Base URL of our separate static public website. Used by the
# {% public_site_url subdir/path %} template tag
GETGOV_PUBLIC_SITE_URL = env_getgov_public_site_url

# endregion
# region: Registry----------------------------------------------------------###

# SECURITY WARNING: keep all registry variables in production secret!
SECRET_REGISTRY_CL_ID = secret_registry_cl_id
SECRET_REGISTRY_PASSWORD = secret_registry_password
SECRET_REGISTRY_CERT = secret_registry_cert
SECRET_REGISTRY_KEY = secret_registry_key
SECRET_REGISTRY_KEY_PASSPHRASE = secret_registry_key_passphrase
SECRET_REGISTRY_HOSTNAME = secret_registry_hostname

# Use this variable to set the size of our connection pool in client.py
# WARNING: Setting this value too high could cause frequent app crashes!
# Having too many connections open could cause the sandbox to timeout,
# as the spinup time could exceed the timeout time.
EPP_CONNECTION_POOL_SIZE = 1

# Determines the interval in which we ping open connections in seconds
# Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE
POOL_KEEP_ALIVE = 60

# Determines how long we try to keep a pool alive for,
# before restarting it.
POOL_TIMEOUT = 60

# endregion
# region: Security and Privacy----------------------------------------------###

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = secret_key

# Use this variable for doing SECRET_KEY rotation, see documentation
SECRET_KEY_FALLBACKS: "list[str]" = []

# ~ Set by django.middleware.security.SecurityMiddleware
# SECURE_CONTENT_TYPE_NOSNIFF = True
# SECURE_CROSS_ORIGIN_OPENER_POLICY = "same-origin"
# SECURE_REDIRECT_EXEMPT = []
# SECURE_REFERRER_POLICY = "same-origin"
# SECURE_SSL_HOST = None

# ~ Overridden from django.middleware.security.SecurityMiddleware
# adds the includeSubDomains directive to the HTTP Strict Transport Security header
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# adds the preload directive to the HTTP Strict Transport Security header
SECURE_HSTS_PRELOAD = True
# TODO: set this value to 31536000 (1 year) for production
SECURE_HSTS_SECONDS = 300
# redirect all non-HTTPS requests to HTTPS
SECURE_SSL_REDIRECT = True

# ~ Set by django.middleware.common.CommonMiddleware
# DISALLOWED_USER_AGENTS = []

# The host/domain names that Django can serve.
# This is a security measure to prevent HTTP Host header attacks,
# which are possible even under many seemingly-safe
# web server configurations.
ALLOWED_HOSTS = [
    "getgov-stable.app.cloud.gov",
    "getgov-staging.app.cloud.gov",
    "getgov-ky.app.cloud.gov",
    "getgov-es.app.cloud.gov",
    "getgov-nl.app.cloud.gov",
    "getgov-rh.app.cloud.gov",
    "getgov-za.app.cloud.gov",
    "getgov-gd.app.cloud.gov",
    "getgov-rb.app.cloud.gov",
    "getgov-ko.app.cloud.gov",
    "getgov-ab.app.cloud.gov",
    "getgov-bl.app.cloud.gov",
    "getgov-rjm.app.cloud.gov",
    "getgov-dk.app.cloud.gov",
    "manage.get.gov",
]

# Extend ALLOWED_HOSTS.
# IP addresses can also be hosts, which are used by internal
# load balancers for health checks, etc.
ALLOWED_CIDR_NETS = ["10.0.0.0/8"]

# ~ Below are some protections from cross-site request forgery.
# This is canonically done by including a nonce value
# in pages sent to the user, which the user is expected
# to send back. The specifics of implementation are
# intricate and varied.

# Store the token server-side, do not send it
# to the user via a cookie. This means each page
# which requires protection must place the token
# in the HTML explicitly, otherwise the user will
# get a 403 error when they submit.
CSRF_USE_SESSIONS = True

# Expiry of CSRF cookie, in seconds.
# None means "use session-based CSRF cookies".
CSRF_COOKIE_AGE = None

# Prevent JavaScript from reading the CSRF cookie.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_HTTPONLY = True

# Only send the cookie via HTTPS connections.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_SECURE = True

# Protect from non-targeted attacks by obscuring
# the CSRF cookie name from the default.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_NAME = "CrSiReFo"

# Prevents CSRF cookie from being sent if the user
# is coming to our site from an external page.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_COOKIE_SAMESITE = "Strict"

# Change header name to match cookie name.
# Has no effect with CSRF_USE_SESSIONS = True.
CSRF_HEADER_NAME = "HTTP_X_CRSIREFO"

# Max parameters that may be received via GET or POST
# TODO: 1000 is the default, may need to tune upward for
# large DNS zone files, if records are represented by
# individual form fields.
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000

# age of session cookies, in seconds (28800 = 8 hours)
SESSION_COOKIE_AGE = 28800

# instruct the browser to forbid client-side JavaScript
# from accessing the cookie
SESSION_COOKIE_HTTPONLY = True

# are we a spring boot application? who knows!
SESSION_COOKIE_NAME = "JSESSIONID"

# Allows session cookie to be sent if the user
# is coming to our site from an external page
# unless it is via "risky" paths, i.e. POST requests
SESSION_COOKIE_SAMESITE = "Lax"

# instruct browser to only send cookie via HTTPS
SESSION_COOKIE_SECURE = True

# session engine to cache session information
SESSION_ENGINE = "django.contrib.sessions.backends.cache"

# ~ Set by django.middleware.clickjacking.XFrameOptionsMiddleware
# prevent clickjacking by instructing the browser not to load
# our site within an iframe
# X_FRAME_OPTIONS = "Deny"

# endregion
# region: Testing-----------------------------------------------------------###

# Additional directories searched for fixture files.
# The fixtures directory of each application is searched by default.
# Must use unix style "/" path separators.
FIXTURE_DIRS: "list[str]" = []

# endregion


# # #                          ###
#      Development settings      #
# # #                          ###

if DEBUG:
    # used by debug() context processor
    INTERNAL_IPS = [
        "127.0.0.1",
        "::1",
    ]

    # allow dev laptop and docker-compose network to connect
    ALLOWED_HOSTS += ("localhost", "app")
    SECURE_SSL_REDIRECT = False
    SECURE_HSTS_PRELOAD = False

    # discover potentially inefficient database queries
    # TODO: use settings overrides to ensure this always is True during tests
    INSTALLED_APPS += ("nplusone.ext.django",)
    MIDDLEWARE += ("nplusone.ext.django.NPlusOneMiddleware",)
    # turned off for now, because django-auditlog has some issues
    NPLUSONE_RAISE = False
    NPLUSONE_WHITELIST = [
        {"model": "admin.LogEntry", "field": "user"},
    ]

    # insert the amazing django-debug-toolbar
    INSTALLED_APPS += ("debug_toolbar",)
    MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")

    DEBUG_TOOLBAR_CONFIG = {
        # due to Docker, bypass Debug Toolbar's check on INTERNAL_IPS
        "SHOW_TOOLBAR_CALLBACK": lambda _: True,
    }

src/registrar/config/settings.py
diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml
index 01079a670..583791fc1 100644
--- a/.github/workflows/deploy-sandbox.yaml
+++ b/.github/workflows/deploy-sandbox.yaml
@@ -20,6 +20,7 @@ jobs:
         || startsWith(github.head_ref, 'nl/')
         || startsWith(github.head_ref, 'dk/')
         || startsWith(github.head_ref, 'es/')
+        || startsWith(github.head_ref, 'ky/')
     outputs:
       environment: ${{ steps.var.outputs.environment}}
     runs-on: "ubuntu-latest"
diff --git a/.github/workflows/migrate.yaml b/.github/workflows/migrate.yaml
index 3b1035657..2423c95ad 100644
--- a/.github/workflows/migrate.yaml
+++ b/.github/workflows/migrate.yaml
@@ -15,6 +15,7 @@ on:
         options:
           - stable
           - staging
+          - ky
           - es
           - nl
           - rh
diff --git a/.github/workflows/reset-db.yaml b/.github/workflows/reset-db.yaml
index 654fa27b5..ffc416604 100644
--- a/.github/workflows/reset-db.yaml
+++ b/.github/workflows/reset-db.yaml
@@ -16,6 +16,7 @@ on:
         options:
           - stable
           - staging
+          - ky
           - es
           - nl
           - rh
diff --git a/ops/manifests/manifest-ky.yaml b/ops/manifests/manifest-ky.yaml
new file mode 100644
index 000000000..31d67cfb3
--- /dev/null
+++ b/ops/manifests/manifest-ky.yaml
@@ -0,0 +1,32 @@
+---
+applications:
+- name: getgov-ky
+  buildpacks:
+    - python_buildpack
+  path: ../../src
+  instances: 1
+  memory: 512M
+  stack: cflinuxfs4
+  timeout: 180
+  command: ./run.sh
+  health-check-type: http
+  health-check-http-endpoint: /health
+  health-check-invocation-timeout: 40
+  env:
+    # Send stdout and stderr straight to the terminal without buffering
+    PYTHONUNBUFFERED: yup
+    # Tell Django where to find its configuration
+    DJANGO_SETTINGS_MODULE: registrar.config.settings
+    # Tell Django where it is being hosted
+    DJANGO_BASE_URL: https://getgov-ky.app.cloud.gov
+    # Tell Django how much stuff to log
+    DJANGO_LOG_LEVEL: INFO
+    # default public site location
+    GETGOV_PUBLIC_SITE_URL: https://beta.get.gov
+    # Flag to disable/enable features in prod environments
+    IS_PRODUCTION: False
+  routes:
+    - route: getgov-ky.app.cloud.gov
+  services:
+  - getgov-credentials
+  - getgov-ky-database
diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py
index 68a07da11..5f16f7e3b 100644
--- a/src/registrar/config/settings.py
+++ b/src/registrar/config/settings.py
@@ -619,6 +619,7 @@
 ALLOWED_HOSTS = [
     "getgov-stable.app.cloud.gov",
     "getgov-staging.app.cloud.gov",
+    "getgov-ky.app.cloud.gov",
     "getgov-es.app.cloud.gov",
     "getgov-nl.app.cloud.gov",
     "getgov-rh.app.cloud.gov",

Create sandbox for Kristina
### Issue description

create a dev sandbox for Kristina so she can have branches that deploy to it specifically

### Acceptance criteria

-sandbox and infra is created

### Additional context

_No response_

### Links to other issues

_No response_
