"""
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 cfenv import AppEnv
from pathlib import Path

# # #                          ###
#      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_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")

secret_key = secret("DJANGO_SECRET_KEY")

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

# Build paths inside the project like this: BASE_DIR / "subdir".
BASE_DIR = path.resolve().parent.parent

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env_debug


# 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 = [
    # 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",
    # 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",
    # let's be sure to install our own application!
    "registrar",
]

# 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",
    # 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",
    # 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",
]

# 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 / "static"

# TODO: decide on template engine and document in ADR
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "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",
            ],
        },
    },
]

# 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"

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

# email address to use for various automated correspondence
# TODO: pick something sensible here
DEFAULT_FROM_EMAIL = "registrar@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-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

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

# No file logger is configured, because containerized apps
# do not log to the file system.
# TODO: Configure better logging options
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "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",
        },
    },
    "handlers": {
        "console": {
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "verbose",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "propagate": True,
            "level": env_log_level,
        },
        "django.template": {
            "handlers": ["console"],
            "propagate": True,
            "level": "INFO",
        },
        "registrar": {
            "handlers": ["console"],
            "propagate": True,
            "level": "INFO",
        },
    },
}

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

# TODO: FAC example for login.gov
# SIMPLE_JWT = {
#     "ALGORITHM": "RS256",
#     "AUDIENCE": None,
#     "ISSUER": "https://idp.int.identitysandbox.gov/",
#     "JWK_URL": "https://idp.int.identitysandbox.gov/api/openid_connect/certs",
#     "LEEWAY": 0,
#     "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.UntypedToken",),
#     "USER_ID_CLAIM": "sub",
# }
# TOKEN_AUTH = {"TOKEN_TTL": 3600}

# endregion
# region: Rest Framework/API------------------------------------------------###

# Enable CORS if api is served at subdomain
#     https://github.com/adamchainz/django-cors-headers
# TODO: FAC example for REST framework
# API_VERSION = "0"
# REST_FRAMEWORK = {
#     "DEFAULT_AUTHENTICATION_CLASSES": [
#         "rest_framework.authentication.BasicAuthentication",
#         "users.auth.ExpiringTokenAuthentication",
#     ],
#     "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
#     "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
#     "PAGE_SIZE": 10,
#     "TEST_REQUEST_RENDERER_CLASSES": [
#         "rest_framework.renderers.MultiPartRenderer",
#         "rest_framework.renderers.JSONRenderer",
#         "rest_framework.renderers.TemplateHTMLRenderer",
#         "rest_framework.renderers.BrowsableAPIRenderer",
#     ],
#     "TEST_REQUEST_DEFAULT_FORMAT": "api",
# }

# 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/"

# 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-unstable.app.cloud.gov",
    "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"

# Prevents session cookie from being sent if the user
# is coming to our site from an external page.
SESSION_COOKIE_SAMESITE = "Strict"

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

# ~ 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 to connect
    ALLOWED_HOSTS += ("localhost",)
    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",)
    NPLUSONE_RAISE = True

    # 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 cfenv import AppEnv
from pathlib import Path

# # #                          ###
#      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_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")

secret_key = secret("DJANGO_SECRET_KEY")

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

# Build paths inside the project like this: BASE_DIR / "subdir".
BASE_DIR = path.resolve().parent.parent

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env_debug


# 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 = [
    # 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",
    # 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",
    # let's be sure to install our own application!
    "registrar",
]

# 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",
    # 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",
    # 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",
]

# 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 / "static"

# TODO: decide on template engine and document in ADR
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "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",
            ],
        },
    },
]

# 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"

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

# email address to use for various automated correspondence
# TODO: pick something sensible here
DEFAULT_FROM_EMAIL = "registrar@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-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

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

# No file logger is configured, because containerized apps
# do not log to the file system.
# TODO: Configure better logging options
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "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",
        },
    },
    "handlers": {
        "console": {
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "verbose",
        },
    },
    "loggers": {
        "django": {
            "handlers": ["console"],
            "propagate": True,
            "level": env_log_level,
        },
        "django.template": {
            "handlers": ["console"],
            "propagate": True,
            "level": "INFO",
        },
        "registrar": {
            "handlers": ["console"],
            "propagate": True,
            "level": "INFO",
        },
    },
}

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

# TODO: FAC example for login.gov
# SIMPLE_JWT = {
#     "ALGORITHM": "RS256",
#     "AUDIENCE": None,
#     "ISSUER": "https://idp.int.identitysandbox.gov/",
#     "JWK_URL": "https://idp.int.identitysandbox.gov/api/openid_connect/certs",
#     "LEEWAY": 0,
#     "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.UntypedToken",),
#     "USER_ID_CLAIM": "sub",
# }
# TOKEN_AUTH = {"TOKEN_TTL": 3600}

# endregion
# region: Rest Framework/API------------------------------------------------###

# Enable CORS if api is served at subdomain
#     https://github.com/adamchainz/django-cors-headers
# TODO: FAC example for REST framework
# API_VERSION = "0"
# REST_FRAMEWORK = {
#     "DEFAULT_AUTHENTICATION_CLASSES": [
#         "rest_framework.authentication.BasicAuthentication",
#         "users.auth.ExpiringTokenAuthentication",
#     ],
#     "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
#     "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
#     "PAGE_SIZE": 10,
#     "TEST_REQUEST_RENDERER_CLASSES": [
#         "rest_framework.renderers.MultiPartRenderer",
#         "rest_framework.renderers.JSONRenderer",
#         "rest_framework.renderers.TemplateHTMLRenderer",
#         "rest_framework.renderers.BrowsableAPIRenderer",
#     ],
#     "TEST_REQUEST_DEFAULT_FORMAT": "api",
# }

# 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/"

# 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-unstable.app.cloud.gov",
    "getgov-staging.app.cloud.gov",
    "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"

# Prevents session cookie from being sent if the user
# is coming to our site from an external page.
SESSION_COOKIE_SAMESITE = "Strict"

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

# ~ 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 to connect
    ALLOWED_HOSTS += ("localhost",)
    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",)
    NPLUSONE_RAISE = True

    # 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/ISSUE_TEMPLATE/developer-onboarding.md b/.github/ISSUE_TEMPLATE/developer-onboarding.md
index 5dbcdb822..82c8c2c63 100644
--- a/.github/ISSUE_TEMPLATE/developer-onboarding.md
+++ b/.github/ISSUE_TEMPLATE/developer-onboarding.md
@@ -35,10 +35,10 @@ cf login -a api.fr.cloud.gov  --sso
 - [ ] Setup [commit signing in Github](#setting-up-commit-signing) and with git locally.
 
 ### Steps for the onboarder
-- [ ] Add the onboardee to cloud.gov org and relevant spaces as a SpaceDeveloper
+- [ ] Add the onboardee to cloud.gov org (cisa-getgov-prototyping) and relevant spaces (unstable) as a SpaceDeveloper
 
  ```bash
-cf set-space-role <cloud.account@email.gov> sandbox-gsa dotgov-poc SpaceDeveloper
+cf set-space-role <cloud.account@email.gov> cisa-getgov-prototyping unstable SpaceDeveloper
 ```
 - [ ] Add the onboardee to our login.gov sandbox team (`.gov registrar poc`) via the [dashboard](https://dashboard.int.identitysandbox.gov/)
 
diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index 1f5620cd3..5c1a68ac6 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -3,8 +3,7 @@ name: Build and deploy
 # This workflow runs on pushes to main (typically,
 # a merged pull request) and on pushes of tagged commits.
 
-# Pushes to main will deploy to Unstable; tagged commits
-# will deploy to Staging
+# Pushes to main will deploy to Staging
 
 on:
   push:
@@ -17,9 +16,9 @@ on:
   workflow_dispatch:
 
 jobs:
-  deploy-unstable:
+  deploy-staging:
     # if this job runs on a branch, we deduce that code
-    # has been pushed to main and should be deployed to unstable
+    # has been pushed to main and should be deployed to staging
     if: ${{ github.ref_type == 'branch' }}
     runs-on: ubuntu-latest
     steps:
@@ -30,13 +29,8 @@ jobs:
         env:
           DEPLOY_NOW: thanks
         with:
-          cf_username: ${{ secrets.CF_USERNAME }}
-          cf_password: ${{ secrets.CF_PASSWORD }}
-          cf_org: sandbox-gsa
-          cf_space: dotgov-poc
-          push_arguments: "-f ops/manifests/manifest-unstable.yaml"
-
-  # deploy-staging:
-  #   # if this job runs on a tag, we deduce that code
-  #   # has been tagged for release and should be deployed to staging
-  #   if: ${{ github.ref_type == 'tag' }}
+          cf_username: ${{ secrets.CF_STAGING_USERNAME }}
+          cf_password: ${{ secrets.CF_STAGING_PASSWORD }}
+          cf_org: cisa-getgov-prototyping
+          cf_space: staging
+          push_arguments: "-f ops/manifests/manifest-staging.yaml"
diff --git a/.github/workflows/migrate.yaml b/.github/workflows/migrate.yaml
index 004352e48..a58bd30d4 100644
--- a/.github/workflows/migrate.yaml
+++ b/.github/workflows/migrate.yaml
@@ -3,7 +3,7 @@ name: Run Migrations
 # This workflow can be run from the CLI
 #     gh workflow run migrate.yaml -f environment=sandbox
 # OR
-#     cf run-task getgov-unstable --wait \
+#     cf run-task getgov-staging --wait \
 #        --command 'python manage.py migrate' --name migrate
 
 on:
@@ -13,22 +13,19 @@ on:
         type: choice
         description: Where should we run migrations
         options:
-        - unstable
         - staging
 
 jobs:
-  migrate-unstable:
-    if: ${{ github.event.inputs.environment == 'unstable' }}
+  migrate-staging:
+    if: ${{ github.event.inputs.environment == 'staging' }}
     runs-on: ubuntu-latest
     steps:
-      - name: Run Django migrations for unstable
+      - name: Run Django migrations for staging
         uses: 18f/cg-deploy-action@main
         with:
           cf_username: ${{ secrets.CF_USERNAME }}
           cf_password: ${{ secrets.CF_PASSWORD }}
-          cf_org: sandbox-gsa
-          cf_space: dotgov-poc
-          full_command: "cf run-task getgov-unstable --wait --command 'python manage.py migrate' --name migrate"
+          cf_org: cisa-getgov-prototyping
+          cf_space: staging
+          full_command: "cf run-task getgov-staging --wait --command 'python manage.py migrate' --name migrate"
 
-  # migrate:
-  #   if: ${{ github.event.inputs.environment == 'staging' }}
\ No newline at end of file
diff --git a/docs/operations/README.md b/docs/operations/README.md
index 3e3d32481..a45d4dd8a 100644
--- a/docs/operations/README.md
+++ b/docs/operations/README.md
@@ -28,8 +28,18 @@ cf target -o <ORG> -s <SPACE>
 
 ## Database
 
-In sandbox, created with `cf create-service aws-rds micro-psql getgov-database`.
+In sandbox, created with `cf create-service aws-rds micro-psql getgov-ENV-database`.
 
 Binding the database in `manifest-<ENVIRONMENT>.json` automatically inserts the connection string into the environment as `DATABASE_URL`.
 
-[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).
\ No newline at end of file
+[Cloud.gov RDS documentation](https://cloud.gov/docs/services/relational-database/).
+
+# Deploy
+
+We have two environments: `unstable` and `staging`. Developers can deploy locally to unstable whenever they want. However, only our CD service can deploy to `staging`, and it does so on every commit to `main`. This is to ensure that we have a "golden" environment to point to, and can still test things out in an unstable space. To deploy locally to `unstable`: 
+
+```bash
+cf target -o cisa-getgov-prototyping -s unstable
+cf push getgov-unstable -f ops/manifests/manifest-unstable.yaml
+cf run-task getgov-unstable --command 'python manage.py migrate' --name migrate
+```
diff --git a/docs/operations/runbooks/rotate_application_secrets.md b/docs/operations/runbooks/rotate_application_secrets.md
index a82453d44..0c2045ebe 100644
--- a/docs/operations/runbooks/rotate_application_secrets.md
+++ b/docs/operations/runbooks/rotate_application_secrets.md
@@ -27,7 +27,7 @@ To rotate secrets, create a new `credentials-<ENVIRONMENT>.json` file, upload it
 Example:
 
 ```bash
-cf uups getgov-credentials -p credentials-unstable.json
+cf cups getgov-credentials -p credentials-unstable.json
 cf restage getgov-unstable --strategy rolling
 ```
 
diff --git a/ops/manifests/manifest-staging.yaml b/ops/manifests/manifest-staging.yaml
new file mode 100644
index 000000000..e474f8ba2
--- /dev/null
+++ b/ops/manifests/manifest-staging.yaml
@@ -0,0 +1,23 @@
+---
+applications:
+- name: getgov-staging
+  buildpacks:
+    - python_buildpack
+  path: ../../src
+  instances: 1
+  memory: 512M
+  stack: cflinuxfs3
+  timeout: 180
+  command: gunicorn registrar.config.wsgi -t 60
+  health-check-type: http
+  health-check-http-endpoint: /health
+  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
+  routes:
+    - route: getgov-staging.app.cloud.gov
+  services:
+  - getgov-credentials
+  - getgov-staging-database
\ No newline at end of file
diff --git a/ops/manifests/manifest-unstable.yaml b/ops/manifests/manifest-unstable.yaml
index 976bd4425..037af2288 100644
--- a/ops/manifests/manifest-unstable.yaml
+++ b/ops/manifests/manifest-unstable.yaml
@@ -20,4 +20,4 @@ applications:
     - route: getgov-unstable.app.cloud.gov
   services:
   - getgov-credentials
-  - getgov-database
\ No newline at end of file
+  - getgov-unstable-database
\ No newline at end of file
diff --git a/ops/scripts/rotate_cloud_secrets.sh b/ops/scripts/rotate_cloud_secrets.sh
index 68f5371dc..ff1d05e27 100755
--- a/ops/scripts/rotate_cloud_secrets.sh
+++ b/ops/scripts/rotate_cloud_secrets.sh
@@ -1,11 +1,16 @@
 # NOTE: This script does not work with cf v8. We recommend using cf v7 for all cloud.gov commands.
 if [ ! $(command -v gh) ] || [ ! $(command -v jq) ] || [ ! $(command -v cf) ]; then
-  echo "jq, cf, and gh packages must be installed. Please install via your preferred manager."
-  exit 1
+    echo "jq, cf, and gh packages must be installed. Please install via your preferred manager."
+    exit 1
+fi
+
+if [ -z "$1" ]; then
+    echo 'Please specify a space to target (i.e. unstable, staging)' >&2
+    exit 1
 fi
 
-cf spaces
-read -p "Are you logged in to the dotgov-poc CF space above? (y/n) " -n 1 -r
+cf target -o cisa-getgov-prototyping -s $1
+read -p "Are you logged in to the cisa-getgov-prototyping CF org above and targeting the correct space? (y/n) " -n 1 -r
 echo
 if [[ ! $REPLY =~ ^[Yy]$ ]]
 then
@@ -13,7 +18,7 @@ then
 fi
 
 gh auth status
-read -p "Are you logged into a Github account with access to cisagov/dotgov? (y/n) " -n 1 -r
+read -p "Are you logged into a Github account with access to cisagov/getgov? (y/n) " -n 1 -r
 echo
 if [[ ! $REPLY =~ ^[Yy]$ ]]
 then
@@ -21,6 +26,7 @@ then
 fi
 
 echo "Great, removing and replacing Github CD account..."
+cf target -s $1
 cf delete-service-key github-cd-account github-cd-key
 cf create-service-key github-cd-account github-cd-key
 cf service-key github-cd-account github-cd-key
@@ -31,8 +37,9 @@ then
     exit 1
 fi
 
+upcase_space=$(printf "%s" "$1" | tr '[:lower:]' '[:upper:]')
 cf service-key github-cd-account github-cd-key | sed 1,2d  | jq -r '[.username, .password]|@tsv' | 
 while read -r username password; do
-    gh secret --repo cisagov/dotgov set CF_USERNAME --body $username
-    gh secret --repo cisagov/dotgov set CF_PASSWORD --body $password
+    gh secret --repo cisagov/getgov set CF_${upcase_space}_USERNAME --body $username
+    gh secret --repo cisagov/getgov set CF_${upcase_space}_PASSWORD --body $password
 done
diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py
index b765688bb..04d4664ba 100644
--- a/src/registrar/config/settings.py
+++ b/src/registrar/config/settings.py
@@ -388,6 +388,7 @@
 # web server configurations.
 ALLOWED_HOSTS = [
     "getgov-unstable.app.cloud.gov",
+    "getgov-staging.app.cloud.gov",
     "get.gov",
 ]
 

Setup new cloud.gov prototyping org
We recently signed an agreement for a more robust prototyping org with cloud.gov that will give us the ability to have multiple spaces and invite members from DHS. 

- [ ] Setup org and make sure everyone who needs access has the appropriate access
- [ ] Setup spaces `unstable` and `staging` according to rules
- [ ] Change Github actions access to CD
- [ ] Replace org and space name in settings (actions, documentation, scripts, etc.)
