Skip to content

Instantly share code, notes, and snippets.

@gilangvperdana
Last active June 11, 2023 04:07
Show Gist options
  • Save gilangvperdana/5709c523b24a18bf574f4bb09f8d20ce to your computer and use it in GitHub Desktop.
Save gilangvperdana/5709c523b24a18bf574f4bb09f8d20ce to your computer and use it in GitHub Desktop.
Defectdojo Disable CSRF Login

Disable Defectdojo CSRF Login

nano settings.dist.py
# Django settings for DefectDojo
import os
from datetime import timedelta
from celery.schedules import crontab
from dojo import __version__
import environ

# See https://defectdojo.github.io/django-DefectDojo/getting_started/configuration/ for options
# how to tune the configuration to your needs.

root = environ.Path(__file__) - 3  # Three folders back

# reference: https://pypi.org/project/django-environ/
env = environ.Env(
    # Set casting and default values
    DD_SITE_URL=(str, 'https://localhost:8080'),
    DD_DEBUG=(bool, False),
    DD_TEMPLATE_DEBUG=(bool, False),
    DD_LOG_LEVEL=(str, ''),
    DD_DJANGO_METRICS_ENABLED=(bool, False),
    DD_LOGIN_REDIRECT_URL=(str, '/'),
    DD_LOGIN_URL=(str, '/login'),
    DD_DJANGO_ADMIN_ENABLED=(bool, True),
    DD_SESSION_COOKIE_HTTPONLY=(bool, False),
    DD_CSRF_COOKIE_HTTPONLY=(bool, False),
    DD_SECURE_SSL_REDIRECT=(bool, False),
    DD_SECURE_HSTS_INCLUDE_SUBDOMAINS=(bool, False),
    DD_SECURE_HSTS_SECONDS=(int, 31536000),  # One year expiration
    DD_SESSION_COOKIE_SECURE=(bool, False),
    DD_SESSION_EXPIRE_AT_BROWSER_CLOSE=(bool, False),
    DD_SESSION_COOKIE_AGE=(int, 1209600),  # 14 days
    DD_CSRF_COOKIE_SECURE=(bool, False),
    DD_SECURE_CONTENT_TYPE_NOSNIFF=(bool, True),
    DD_TIME_ZONE=(str, 'UTC'),
    DD_LANG=(str, 'en-us'),
    DD_TEAM_NAME=(str, 'Security Team'),
    DD_ADMINS=(str, 'DefectDojo:dojo@localhost,Admin:admin@localhost'),
    DD_WHITENOISE=(bool, False),
    DD_TRACK_MIGRATIONS=(bool, True),
    DD_SECURE_PROXY_SSL_HEADER=(bool, False),
    DD_TEST_RUNNER=(str, 'django.test.runner.DiscoverRunner'),
    DD_URL_PREFIX=(str, ''),
    DD_ROOT=(str, root('dojo')),
    DD_LANGUAGE_CODE=(str, 'en-us'),
    DD_SITE_ID=(int, 1),
    DD_USE_I18N=(bool, True),
    DD_USE_L10N=(bool, True),
    DD_USE_TZ=(bool, True),
    DD_MEDIA_URL=(str, '/media/'),
    DD_MEDIA_ROOT=(str, root('media')),
    DD_STATIC_URL=(str, '/static/'),
    DD_STATIC_ROOT=(str, root('static')),
    DD_CELERY_BROKER_URL=(str, ''),
    DD_CELERY_BROKER_SCHEME=(str, 'sqla+sqlite'),
    DD_CELERY_BROKER_USER=(str, ''),
    DD_CELERY_BROKER_PASSWORD=(str, ''),
    DD_CELERY_BROKER_HOST=(str, ''),
    DD_CELERY_BROKER_PORT=(int, -1),
    DD_CELERY_BROKER_PATH=(str, '/dojo.celerydb.sqlite'),
    DD_CELERY_BROKER_PARAMS=(str, ''),
    DD_CELERY_TASK_IGNORE_RESULT=(bool, True),
    DD_CELERY_RESULT_BACKEND=(str, 'django-db'),
    DD_CELERY_RESULT_EXPIRES=(int, 86400),
    DD_CELERY_BEAT_SCHEDULE_FILENAME=(str, root('dojo.celery.beat.db')),
    DD_CELERY_TASK_SERIALIZER=(str, 'pickle'),
    DD_CELERY_PASS_MODEL_BY_ID=(str, True),
    DD_FOOTER_VERSION=(str, ''),
    # models should be passed to celery by ID, default is False (for now)
    DD_FORCE_LOWERCASE_TAGS=(bool, True),
    DD_MAX_TAG_LENGTH=(int, 25),
    DD_DATABASE_ENGINE=(str, 'django.db.backends.mysql'),
    DD_DATABASE_HOST=(str, 'mysql'),
    DD_DATABASE_NAME=(str, 'defectdojo'),
    # default django database name for testing is test_<dbname>
    DD_TEST_DATABASE_NAME=(str, 'test_defectdojo'),
    DD_DATABASE_PASSWORD=(str, 'defectdojo'),
    DD_DATABASE_PORT=(int, 3306),
    DD_DATABASE_USER=(str, 'defectdojo'),
    DD_SECRET_KEY=(str, ''),
    DD_CREDENTIAL_AES_256_KEY=(str, '.'),
    DD_DATA_UPLOAD_MAX_MEMORY_SIZE=(int, 8388608),  # Max post size set to 8mb
    DD_FORGOT_PASSWORD=(bool, True),  # do we show link "I forgot my password" on login screen
    DD_SOCIAL_AUTH_SHOW_LOGIN_FORM=(bool, True),  # do we show user/pass input
    DD_SOCIAL_AUTH_CREATE_USER=(bool, True),  # if True creates user at first login
    DD_SOCIAL_LOGIN_AUTO_REDIRECT=(bool, False),  # auto-redirect if there is only one social login method
    DD_SOCIAL_AUTH_TRAILING_SLASH=(bool, True),
    DD_SOCIAL_AUTH_AUTH0_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_AUTH0_KEY=(str, ''),
    DD_SOCIAL_AUTH_AUTH0_SECRET=(str, ''),
    DD_SOCIAL_AUTH_AUTH0_DOMAIN=(str, ''),
    DD_SOCIAL_AUTH_AUTH0_SCOPE=(list, ['openid', 'profile', 'email']),
    DD_SOCIAL_AUTH_GOOGLE_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_GOOGLE_OAUTH2_KEY=(str, ''),
    DD_SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET=(str, ''),
    DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS=(list, ['']),
    DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_EMAILS=(list, ['']),
    DD_SOCIAL_AUTH_OKTA_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_OKTA_OAUTH2_KEY=(str, ''),
    DD_SOCIAL_AUTH_OKTA_OAUTH2_SECRET=(str, ''),
    DD_SOCIAL_AUTH_OKTA_OAUTH2_API_URL=(str, 'https://{your-org-url}/oauth2/default'),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY=(str, ''),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET=(str, ''),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID=(str, ''),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_RESOURCE=(str, 'https://graph.microsoft.com/'),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS=(bool, False),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER=(str, ''),
    DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS=(bool, True),
    DD_SOCIAL_AUTH_GITLAB_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_GITLAB_PROJECT_AUTO_IMPORT=(bool, False),
    DD_SOCIAL_AUTH_GITLAB_PROJECT_IMPORT_TAGS=(bool, False),
    DD_SOCIAL_AUTH_GITLAB_PROJECT_IMPORT_URL=(bool, False),
    DD_SOCIAL_AUTH_GITLAB_PROJECT_MIN_ACCESS_LEVEL=(int, 20),
    DD_SOCIAL_AUTH_GITLAB_KEY=(str, ''),
    DD_SOCIAL_AUTH_GITLAB_SECRET=(str, ''),
    DD_SOCIAL_AUTH_GITLAB_API_URL=(str, 'https://gitlab.com'),
    DD_SOCIAL_AUTH_GITLAB_SCOPE=(list, ['api', 'read_user', 'openid', 'profile', 'email']),
    DD_SOCIAL_AUTH_KEYCLOAK_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_KEYCLOAK_KEY=(str, ''),
    DD_SOCIAL_AUTH_KEYCLOAK_SECRET=(str, ''),
    DD_SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY=(str, ''),
    DD_SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL=(str, ''),
    DD_SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL=(str, ''),
    DD_SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT=(str, 'Login with Keycloak'),
    DD_SOCIAL_AUTH_GITHUB_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_GITHUB_KEY=(str, ''),
    DD_SOCIAL_AUTH_GITHUB_SECRET=(str, ''),
    DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_OAUTH2_ENABLED=(bool, False),
    DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_URL=(str, ''),
    DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL=(str, ''),
    DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY=(str, ''),
    DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET=(str, ''),
    DD_SAML2_ENABLED=(bool, False),
    # Force Authentication to make SSO possible with SAML2
    DD_SAML2_FORCE_AUTH=(bool, True),
    DD_SAML2_LOGIN_BUTTON_TEXT=(str, 'Login with SAML'),
    # Optional: display the idp SAML Logout URL in DefectDojo
    DD_SAML2_LOGOUT_URL=(str, ''),
    # Metadata is required for SAML, choose either remote url or local file path
    DD_SAML2_METADATA_AUTO_CONF_URL=(str, ''),
    DD_SAML2_METADATA_LOCAL_FILE_PATH=(str, ''),  # ex. '/public/share/idp_metadata.xml'
    # Optional, default is SITE_URL + /saml2/metadata/
    DD_SAML2_ENTITY_ID=(str, ''),
    # Allow to create user that are not already in the Django database
    DD_SAML2_CREATE_USER=(bool, False),
    DD_SAML2_ATTRIBUTES_MAP=(dict, {
        # Change Email/UserName/FirstName/LastName to corresponding SAML2 userprofile attributes.
        # format: SAML attrib:django_user_model
        'Email': 'email',
        'UserName': 'username',
        'Firstname': 'first_name',
        'Lastname': 'last_name'
    }),
    DD_SAML2_ALLOW_UNKNOWN_ATTRIBUTE=(bool, False),
    # if somebody is using own documentation how to use DefectDojo in his own company
    DD_DOCUMENTATION_URL=(str, 'https://defectdojo.github.io/django-DefectDojo'),
    # merging findings doesn't always work well with dedupe and reimport etc.
    # disable it if you see any issues (and report them on github)
    DD_DISABLE_FINDING_MERGE=(bool, False),
    # SLA Notifications via alerts and JIRA comments
    # enable either DD_SLA_NOTIFY_ACTIVE or DD_SLA_NOTIFY_ACTIVE_VERIFIED_ONLY to enable the feature
    DD_SLA_NOTIFY_ACTIVE=(bool, False),
    DD_SLA_NOTIFY_ACTIVE_VERIFIED_ONLY=(bool, False),
    # finetuning settings for when enabled
    DD_SLA_NOTIFY_WITH_JIRA_ONLY=(bool, False),
    DD_SLA_NOTIFY_PRE_BREACH=(int, 3),
    DD_SLA_NOTIFY_POST_BREACH=(int, 7),
    # Use business day's to calculate SLA's and age instead of calendar days
    DD_SLA_BUSINESS_DAYS=(bool, False),
    # maximum number of result in search as search can be an expensive operation
    DD_SEARCH_MAX_RESULTS=(int, 100),
    DD_SIMILAR_FINDINGS_MAX_RESULTS=(int, 25),
    DD_MAX_AUTOCOMPLETE_WORDS=(int, 20000),
    DD_JIRA_SSL_VERIFY=(bool, True),
    # if you want to keep logging to the console but in json format, change this here to 'json_console'
    DD_LOGGING_HANDLER=(str, 'console'),
    DD_ALERT_REFRESH=(bool, True),
    DD_DISABLE_ALERT_COUNTER=(bool, False),
    # to disable deleting alerts per user set value to -1
    DD_MAX_ALERTS_PER_USER=(int, 999),
    DD_TAG_PREFETCHING=(bool, True),
    DD_QUALYS_WAS_WEAKNESS_IS_VULN=(bool, False),
    # regular expression to exclude one or more parsers
    # could be usefull to limit parser allowed
    # AWS Scout2 Scan Parser is deprecated (see https://github.com/DefectDojo/django-DefectDojo/pull/5268)
    DD_PARSER_EXCLUDE=(str, 'AWS Scout2 Scan'),
    # when enabled in sytem settings,  every minute a job run to delete excess duplicates
    # we limit the amount of duplicates that can be deleted in a single run of that job
    # to prevent overlapping runs of that job from occurrring
    DD_DUPE_DELETE_MAX_PER_RUN=(int, 200),
    # when enabled 'mitigated date' and 'mitigated by' of a finding become editable
    DD_EDITABLE_MITIGATED_DATA=(bool, False),
    # new feature that tracks history across multiple reimports for the same test
    DD_TRACK_IMPORT_HISTORY=(bool, True),

    # When enabled, staff users have full access to all product types and products
    DD_AUTHORIZATION_STAFF_OVERRIDE=(bool, False),

    # Allow grouping of findings in the same test, for example to group findings per dependency
    # DD_FEATURE_FINDING_GROUPS feature is moved to system_settings, will be removed from settings file
    DD_FEATURE_FINDING_GROUPS=(bool, True),
    DD_JIRA_TEMPLATE_ROOT=(str, 'dojo/templates/issue-trackers'),
    DD_TEMPLATE_DIR_PREFIX=(str, 'dojo/templates/'),

    # Initial behaviour in Defect Dojo was to delete all duplicates when an original was deleted
    # New behaviour is to leave the duplicates in place, but set the oldest of duplicates as new original
    # Set to True to revert to the old behaviour where all duplicates are deleted
    DD_DUPLICATE_CLUSTER_CASCADE_DELETE=(str, False),
    # Enable Rate Limiting for the login page
    DD_RATE_LIMITER_ENABLED=(bool, False),
    # Examples include 5/m 100/h and more https://django-ratelimit.readthedocs.io/en/stable/rates.html#simple-rates
    DD_RATE_LIMITER_RATE=(str, '5/m'),
    # Block the requests after rate limit is exceeded
    DD_RATE_LIMITER_BLOCK=(bool, False),
    # Forces the user to change password on next login.
    DD_RATE_LIMITER_ACCOUNT_LOCKOUT=(bool, False),
    # when enabled SonarQube API parser will download the security hotspots
    DD_SONARQUBE_API_PARSER_HOTSPOTS=(bool, True),
    # when enabled, finding importing will occur asynchronously, default False
    DD_ASYNC_FINDING_IMPORT=(bool, False),
    # The number of findings to be processed per celeryworker
    DD_ASYNC_FINDING_IMPORT_CHUNK_SIZE=(int, 100),
    # When enabled, deleting objects will be occur from the bottom up. In the example of deleting an engagement
    # The objects will be deleted as follows Endpoints -> Findings -> Tests -> Engagement
    DD_ASYNC_OBJECT_DELETE=(bool, False),
    # The number of objects to be deleted per celeryworker
    DD_ASYNC_OBEJECT_DELETE_CHUNK_SIZE=(int, 100),
    # When enabled, display the preview of objects to be deleted. This can take a long time to render
    # for very large objects
    DD_DELETE_PREVIEW=(bool, True),
    # Feature toggle for new authorization for configurations
    DD_FEATURE_CONFIGURATION_AUTHORIZATION=(bool, True),
)


def generate_url(scheme, double_slashes, user, password, host, port, path, params):
    result_list = []
    result_list.append(scheme)
    result_list.append(':')
    if double_slashes:
        result_list.append('//')
    result_list.append(user)
    if len(password) > 0:
        result_list.append(':')
        result_list.append(password)
    if len(user) > 0 or len(password) > 0:
        result_list.append('@')
    result_list.append(host)
    if port >= 0:
        result_list.append(':')
        result_list.append(str(port))
    if len(path) > 0 and path[0] != '/':
        result_list.append('/')
    result_list.append(path)
    if len(params) > 0 and params[0] != '?':
        result_list.append('?')
    result_list.append(params)
    return ''.join(result_list)


# Read .env file as default or from the command line, DD_ENV_PATH
if os.path.isfile(root('dojo/settings/.env.prod')) or 'DD_ENV_PATH' in os.environ:
    env.read_env(root('dojo/settings/' + env.str('DD_ENV_PATH', '.env.prod')))

# ------------------------------------------------------------------------------
# GENERAL
# ------------------------------------------------------------------------------

# False if not in os.environ
DEBUG = env('DD_DEBUG')
TEMPLATE_DEBUG = env('DD_TEMPLATE_DEBUG')

# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/2.0/ref/settings/#allowed-hosts
SITE_URL = env('DD_SITE_URL')
ALLOWED_HOSTS = tuple(env.list('DD_ALLOWED_HOSTS', default=['localhost', '127.0.0.1', 'https://defectdojo.bignetlab.com']))

# Raises django's ImproperlyConfigured exception if SECRET_KEY not in os.environ
SECRET_KEY = env('DD_SECRET_KEY')

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = env('DD_TIME_ZONE')

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = env('DD_LANGUAGE_CODE')

SITE_ID = env('DD_SITE_ID')

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = env('DD_USE_I18N')

# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale.
USE_L10N = env('DD_USE_L10N')

# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = env('DD_USE_TZ')

TEST_RUNNER = env('DD_TEST_RUNNER')

ALERT_REFRESH = env('DD_ALERT_REFRESH')
DISABLE_ALERT_COUNTER = env("DD_DISABLE_ALERT_COUNTER")
MAX_ALERTS_PER_USER = env("DD_MAX_ALERTS_PER_USER")

TAG_PREFETCHING = env('DD_TAG_PREFETCHING')

# ------------------------------------------------------------------------------
# DATABASE
# ------------------------------------------------------------------------------

# Parse database connection url strings like psql://user:pass@127.0.0.1:8458/db
if os.getenv('DD_DATABASE_URL') is not None:
    DATABASES = {
        'default': env.db('DD_DATABASE_URL')
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': env('DD_DATABASE_ENGINE'),
            'NAME': env('DD_DATABASE_NAME'),
            'TEST': {
                'NAME': env('DD_TEST_DATABASE_NAME'),
            },
            'USER': env('DD_DATABASE_USER'),
            'PASSWORD': env('DD_DATABASE_PASSWORD'),
            'HOST': env('DD_DATABASE_HOST'),
            'PORT': env('DD_DATABASE_PORT'),
        }
    }

# Track migrations through source control rather than making migrations locally
if env('DD_TRACK_MIGRATIONS'):
    MIGRATION_MODULES = {'dojo': 'dojo.db_migrations'}

# Default for automatically created id fields,
# see https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

# ------------------------------------------------------------------------------
# MEDIA
# ------------------------------------------------------------------------------

DOJO_ROOT = env('DD_ROOT')

# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/var/www/example.com/media/"
MEDIA_ROOT = env('DD_MEDIA_ROOT')

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = env('DD_MEDIA_URL')

# ------------------------------------------------------------------------------
# STATIC
# ------------------------------------------------------------------------------

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
STATIC_ROOT = env('DD_STATIC_ROOT')

# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = env('DD_STATIC_URL')

# Additional locations of static files
STATICFILES_DIRS = (
    # Put strings here, like "/home/html/static" or "C:/www/django/static".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    os.path.join(os.path.dirname(DOJO_ROOT), 'components', 'node_modules'),
)

# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

FILE_UPLOAD_HANDLERS = (
    "django.core.files.uploadhandler.TemporaryFileUploadHandler",
)

DATA_UPLOAD_MAX_MEMORY_SIZE = env('DD_DATA_UPLOAD_MAX_MEMORY_SIZE')

# ------------------------------------------------------------------------------
# URLS
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf

# AUTHENTICATION_BACKENDS = [
# 'axes.backends.AxesModelBackend',
# ]

ROOT_URLCONF = 'dojo.urls'

# Python dotted path to the WSGI application used by Django's runserver.
# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
WSGI_APPLICATION = 'dojo.wsgi.application'

URL_PREFIX = env('DD_URL_PREFIX')

# ------------------------------------------------------------------------------
# AUTHENTICATION
# ------------------------------------------------------------------------------

LOGIN_REDIRECT_URL = env('DD_LOGIN_REDIRECT_URL')
LOGIN_URL = env('DD_LOGIN_URL')

# These are the individidual modules supported by social-auth
AUTHENTICATION_BACKENDS = (
    'social_core.backends.auth0.Auth0OAuth2',
    'social_core.backends.google.GoogleOAuth2',
    'dojo.okta.OktaOAuth2',
    'social_core.backends.azuread_tenant.AzureADTenantOAuth2',
    'social_core.backends.gitlab.GitLabOAuth2',
    'social_core.backends.keycloak.KeycloakOAuth2',
    'social_core.backends.github.GithubOAuth2',
    'social_core.backends.github_enterprise.GithubEnterpriseOAuth2',
    'django.contrib.auth.backends.RemoteUserBackend',
    'django.contrib.auth.backends.ModelBackend',
)

# Make Argon2 the default password hasher by listing it first
# Unfortunately Django doesn't provide the default built-in
# PASSWORD_HASHERS list here as a variable which we could modify,
# so we have to list all the hashers present in Django :-(
PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
    'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
    'django.contrib.auth.hashers.CryptPasswordHasher',
]

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'dojo.pipeline.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.social_auth.associate_by_email',
    'dojo.pipeline.create_user',
    'dojo.pipeline.modify_permissions',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
    'dojo.pipeline.update_azure_groups',
    'dojo.pipeline.update_product_access',
)

CLASSIC_AUTH_ENABLED = True
FORGOT_PASSWORD = env('DD_FORGOT_PASSWORD')
# Showing login form (form is not needed for external auth: OKTA, Google Auth, etc.)
SHOW_LOGIN_FORM = env('DD_SOCIAL_AUTH_SHOW_LOGIN_FORM')
SOCIAL_LOGIN_AUTO_REDIRECT = env('DD_SOCIAL_LOGIN_AUTO_REDIRECT')
SOCIAL_AUTH_CREATE_USER = env('DD_SOCIAL_AUTH_CREATE_USER')

SOCIAL_AUTH_STRATEGY = 'social_django.strategy.DjangoStrategy'
SOCIAL_AUTH_STORAGE = 'social_django.models.DjangoStorage'
SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ['username', 'first_name', 'last_name', 'email']
SOCIAL_AUTH_USERNAME_IS_FULL_EMAIL = True

GOOGLE_OAUTH_ENABLED = env('DD_SOCIAL_AUTH_GOOGLE_OAUTH2_ENABLED')
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = env('DD_SOCIAL_AUTH_GOOGLE_OAUTH2_KEY')
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = env('DD_SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET')
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = env('DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS')
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_EMAILS = env('DD_SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_EMAILS')
SOCIAL_AUTH_LOGIN_ERROR_URL = '/login'
SOCIAL_AUTH_BACKEND_ERROR_URL = '/login'

OKTA_OAUTH_ENABLED = env('DD_SOCIAL_AUTH_OKTA_OAUTH2_ENABLED')
SOCIAL_AUTH_OKTA_OAUTH2_KEY = env('DD_SOCIAL_AUTH_OKTA_OAUTH2_KEY')
SOCIAL_AUTH_OKTA_OAUTH2_SECRET = env('DD_SOCIAL_AUTH_OKTA_OAUTH2_SECRET')
SOCIAL_AUTH_OKTA_OAUTH2_API_URL = env('DD_SOCIAL_AUTH_OKTA_OAUTH2_API_URL')

AZUREAD_TENANT_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_ENABLED')
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY')
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET')
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID')
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_RESOURCE = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_RESOURCE')
AZUREAD_TENANT_OAUTH2_GET_GROUPS = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GET_GROUPS')
AZUREAD_TENANT_OAUTH2_GROUPS_FILTER = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_GROUPS_FILTER')
AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS = env('DD_SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_CLEANUP_GROUPS')

GITLAB_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_GITLAB_OAUTH2_ENABLED')
GITLAB_PROJECT_AUTO_IMPORT = env('DD_SOCIAL_AUTH_GITLAB_PROJECT_AUTO_IMPORT')
GITLAB_PROJECT_IMPORT_TAGS = env('DD_SOCIAL_AUTH_GITLAB_PROJECT_IMPORT_TAGS')
GITLAB_PROJECT_IMPORT_URL = env('DD_SOCIAL_AUTH_GITLAB_PROJECT_IMPORT_URL')
GITLAB_PROJECT_MIN_ACCESS_LEVEL = env('DD_SOCIAL_AUTH_GITLAB_PROJECT_MIN_ACCESS_LEVEL')
SOCIAL_AUTH_GITLAB_KEY = env('DD_SOCIAL_AUTH_GITLAB_KEY')
SOCIAL_AUTH_GITLAB_SECRET = env('DD_SOCIAL_AUTH_GITLAB_SECRET')
SOCIAL_AUTH_GITLAB_API_URL = env('DD_SOCIAL_AUTH_GITLAB_API_URL')
SOCIAL_AUTH_GITLAB_SCOPE = env('DD_SOCIAL_AUTH_GITLAB_SCOPE')

AUTH0_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_AUTH0_OAUTH2_ENABLED')
SOCIAL_AUTH_AUTH0_KEY = env('DD_SOCIAL_AUTH_AUTH0_KEY')
SOCIAL_AUTH_AUTH0_SECRET = env('DD_SOCIAL_AUTH_AUTH0_SECRET')
SOCIAL_AUTH_AUTH0_DOMAIN = env('DD_SOCIAL_AUTH_AUTH0_DOMAIN')
SOCIAL_AUTH_AUTH0_SCOPE = env('DD_SOCIAL_AUTH_AUTH0_SCOPE')
SOCIAL_AUTH_TRAILING_SLASH = env('DD_SOCIAL_AUTH_TRAILING_SLASH')

KEYCLOAK_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_KEYCLOAK_OAUTH2_ENABLED')
SOCIAL_AUTH_KEYCLOAK_KEY = env('DD_SOCIAL_AUTH_KEYCLOAK_KEY')
SOCIAL_AUTH_KEYCLOAK_SECRET = env('DD_SOCIAL_AUTH_KEYCLOAK_SECRET')
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = env('DD_SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY')
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = env('DD_SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL')
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = env('DD_SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL')
SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT = env('DD_SOCIAL_AUTH_KEYCLOAK_LOGIN_BUTTON_TEXT')

GITHUB_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_GITHUB_OAUTH2_ENABLED')
SOCIAL_AUTH_GITHUB_KEY = env('DD_SOCIAL_AUTH_GITHUB_KEY')
SOCIAL_AUTH_GITHUB_SECRET = env('DD_SOCIAL_AUTH_GITHUB_SECRET')

GITHUB_ENTERPRISE_OAUTH2_ENABLED = env('DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_OAUTH2_ENABLED')
SOCIAL_AUTH_GITHUB_ENTERPRISE_URL = env('DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_URL')
SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL = env('DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_API_URL')
SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY = env('DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY')
SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET = env('DD_SOCIAL_AUTH_GITHUB_ENTERPRISE_SECRET')

DOCUMENTATION_URL = env('DD_DOCUMENTATION_URL')

# Setting SLA_NOTIFY_ACTIVE and SLA_NOTIFY_ACTIVE_VERIFIED to False will disable the feature
# If you import thousands of Active findings through your pipeline everyday,
# and make the choice of enabling SLA notifications for non-verified findings,
# be mindful of performance.
SLA_NOTIFY_ACTIVE = env('DD_SLA_NOTIFY_ACTIVE')  # this will include 'verified' findings as well as non-verified.
SLA_NOTIFY_ACTIVE_VERIFIED_ONLY = env('DD_SLA_NOTIFY_ACTIVE_VERIFIED_ONLY')
SLA_NOTIFY_WITH_JIRA_ONLY = env('DD_SLA_NOTIFY_WITH_JIRA_ONLY')  # Based on the 2 above, but only with a JIRA link
SLA_NOTIFY_PRE_BREACH = env('DD_SLA_NOTIFY_PRE_BREACH')  # in days, notify between dayofbreach minus this number until dayofbreach
SLA_NOTIFY_POST_BREACH = env('DD_SLA_NOTIFY_POST_BREACH')  # in days, skip notifications for findings that go past dayofbreach plus this number
SLA_BUSINESS_DAYS = env('DD_SLA_BUSINESS_DAYS')  # Use business days to calculate SLA's and age of a finding instead of calendar days


SEARCH_MAX_RESULTS = env('DD_SEARCH_MAX_RESULTS')
SIMILAR_FINDINGS_MAX_RESULTS = env('DD_SIMILAR_FINDINGS_MAX_RESULTS')
MAX_AUTOCOMPLETE_WORDS = env('DD_MAX_AUTOCOMPLETE_WORDS')

LOGIN_EXEMPT_URLS = (
    r'^%sstatic/' % URL_PREFIX,
    r'^%swebhook/([\w-]+)$' % URL_PREFIX,
    r'^%swebhook/' % URL_PREFIX,
    r'^%sjira/webhook/([\w-]+)$' % URL_PREFIX,
    r'^%sjira/webhook/' % URL_PREFIX,
    r'^%sreports/cover$' % URL_PREFIX,
    r'^%sfinding/image/(?P<token>[^/]+)$' % URL_PREFIX,
    r'^%sapi/v2/' % URL_PREFIX,
    r'complete/',
    r'empty_questionnaire/([\d]+)/answer',
    r'^%spassword_reset/' % URL_PREFIX,
    r'^%sreset/' % URL_PREFIX,
)

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 9,
        }
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'dojo.user.validators.NumberValidator'
    },
    {
        'NAME': 'dojo.user.validators.UppercaseValidator'
    },
    {
        'NAME': 'dojo.user.validators.LowercaseValidator'
    },
    {
        'NAME': 'dojo.user.validators.SymbolValidator'
    }
]

# https://django-ratelimit.readthedocs.io/en/stable/index.html
RATE_LIMITER_ENABLED = env('DD_RATE_LIMITER_ENABLED')
RATE_LIMITER_RATE = env('DD_RATE_LIMITER_RATE')  # Examples include 5/m 100/h and more https://django-ratelimit.readthedocs.io/en/stable/rates.html#simple-rates
RATE_LIMITER_BLOCK = env('DD_RATE_LIMITER_BLOCK')  # Block the requests after rate limit is exceeded
RATE_LIMITER_ACCOUNT_LOCKOUT = env('DD_RATE_LIMITER_ACCOUNT_LOCKOUT')  # Forces the user to change password on next login.

# ------------------------------------------------------------------------------
# SECURITY DIRECTIVES
# ------------------------------------------------------------------------------

# If True, the SecurityMiddleware redirects all non-HTTPS requests to HTTPS
# (except for those URLs matching a regular expression listed in SECURE_REDIRECT_EXEMPT).
SECURE_SSL_REDIRECT = env('DD_SECURE_SSL_REDIRECT')

# If True, the SecurityMiddleware sets the X-Content-Type-Options: nosniff;
SECURE_CONTENT_TYPE_NOSNIFF = env('DD_SECURE_CONTENT_TYPE_NOSNIFF')

# Whether to use HTTPOnly flag on the session cookie.
# If this is set to True, client-side JavaScript will not to be able to access the session cookie.
SESSION_COOKIE_HTTPONLY = env('DD_SESSION_COOKIE_HTTPONLY')

# Whether to use HttpOnly flag on the CSRF cookie. If this is set to True,
# client-side JavaScript will not to be able to access the CSRF cookie.
CSRF_COOKIE_HTTPONLY = env('DD_CSRF_COOKIE_HTTPONLY')

# Whether to use a secure cookie for the session cookie. If this is set to True,
# the cookie will be marked as secure, which means browsers may ensure that the
# cookie is only sent with an HTTPS connection.
SESSION_COOKIE_SECURE = env('DD_SESSION_COOKIE_SECURE')

# Whether to use a secure cookie for the CSRF cookie.
CSRF_COOKIE_SECURE = env('DD_CSRF_COOKIE_SECURE')

if env('DD_SECURE_PROXY_SSL_HEADER'):
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

if env('DD_SECURE_HSTS_INCLUDE_SUBDOMAINS'):
    SECURE_HSTS_SECONDS = env('DD_SECURE_HSTS_SECONDS')
    SECURE_HSTS_INCLUDE_SUBDOMAINS = env('DD_SECURE_HSTS_INCLUDE_SUBDOMAINS')

SESSION_EXPIRE_AT_BROWSER_CLOSE = env('DD_SESSION_EXPIRE_AT_BROWSER_CLOSE')
SESSION_COOKIE_AGE = env('DD_SESSION_COOKIE_AGE')

# ------------------------------------------------------------------------------
# DEFECTDOJO SPECIFIC
# ------------------------------------------------------------------------------

# Credential Key
CREDENTIAL_AES_256_KEY = env('DD_CREDENTIAL_AES_256_KEY')
DB_KEY = env('DD_CREDENTIAL_AES_256_KEY')

# Used in a few places to prefix page headings and in email salutations
TEAM_NAME = env('DD_TEAM_NAME')

# Used to configure a custom version in the footer of the base.html template.
FOOTER_VERSION = env('DD_FOOTER_VERSION')

# Django-tagging settings
FORCE_LOWERCASE_TAGS = env('DD_FORCE_LOWERCASE_TAGS')
MAX_TAG_LENGTH = env('DD_MAX_TAG_LENGTH')


# ------------------------------------------------------------------------------
# ADMIN
# ------------------------------------------------------------------------------
from email.utils import getaddresses
ADMINS = getaddresses([env('DD_ADMINS')])

# https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS

# Django admin enabled
DJANGO_ADMIN_ENABLED = env('DD_DJANGO_ADMIN_ENABLED')

# ------------------------------------------------------------------------------
# API V2
# ------------------------------------------------------------------------------

REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.DjangoModelPermissions',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 25,
    'EXCEPTION_HANDLER': 'dojo.api_v2.exception_handler.custom_exception_handler'
}

SWAGGER_SETTINGS = {
    'SECURITY_DEFINITIONS': {
        'api_key': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Authorization'
        }
    },
    'DOC_EXPANSION': "none",
    'JSON_EDITOR': True,
    'SHOW_REQUEST_HEADERS': True,
}

SPECTACULAR_SETTINGS = {
    'TITLE': 'Defect Dojo API v2',
    'DESCRIPTION': 'Defect Dojo - Open Source vulnerability Management made easy. Prefetch related parameters/responses not yet in the schema.',
    'VERSION': __version__,
    # OTHER SETTINGS
    # the following set to False could help some client generators
    # 'ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE': False,
    'POSTPROCESSING_HOOKS': ['dojo.api_v2.prefetch.schema.prefetch_postprocessing_hook'],
    # show file selection dialogue, see https://github.com/tfranzel/drf-spectacular/issues/455
    "COMPONENT_SPLIT_REQUEST": True,
}

# ------------------------------------------------------------------------------
# TEMPLATES
# ------------------------------------------------------------------------------

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'APP_DIRS': True,
        'OPTIONS': {
            'debug': env('DD_DEBUG'),
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'social_django.context_processors.backends',
                'social_django.context_processors.login_redirect',
                'dojo.context_processors.globalize_vars',
                'dojo.context_processors.bind_system_settings',
                'dojo.context_processors.bind_alert_count',
            ],
        },
    },
]

# ------------------------------------------------------------------------------
# APPS
# ------------------------------------------------------------------------------

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polymorphic',  # provides admin templates
    'django.contrib.admin',
    'django.contrib.humanize',
    'gunicorn',
    'auditlog',
    'dojo',
    'watson',
    'tagging',  # not used, but still needed for migration 0065_django_tagulous.py (v1.10.0)
    'imagekit',
    'multiselectfield',
    'rest_framework',
    'rest_framework.authtoken',
    'dbbackup',
    'django_celery_results',
    'social_django',
    'drf_yasg',
    'drf_spectacular',
    'tagulous'
)

# ------------------------------------------------------------------------------
# MIDDLEWARE
# ------------------------------------------------------------------------------
DJANGO_MIDDLEWARE_CLASSES = [
    'django.middleware.common.CommonMiddleware',
    'dojo.middleware.DojoSytemSettingsMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'dojo.middleware.LoginRequiredMiddleware',
    'social_django.middleware.SocialAuthExceptionMiddleware',
    'watson.middleware.SearchContextMiddleware',
    'auditlog.middleware.AuditlogMiddleware',
    'crum.CurrentRequestUserMiddleware',
    'dojo.request_cache.middleware.RequestCacheMiddleware',
]

MIDDLEWARE = DJANGO_MIDDLEWARE_CLASSES

# WhiteNoise allows your web app to serve its own static files,
# making it a self-contained unit that can be deployed anywhere without relying on nginx
if env('DD_WHITENOISE'):
    WHITE_NOISE = [
        # Simplified static file serving.
        # https://warehouse.python.org/project/whitenoise/
        'whitenoise.middleware.WhiteNoiseMiddleware',
    ]
    MIDDLEWARE = MIDDLEWARE + WHITE_NOISE

EMAIL_CONFIG = env.email_url(
    'DD_EMAIL_URL', default='smtp://user@:password@localhost:25')

vars().update(EMAIL_CONFIG)

# ------------------------------------------------------------------------------
# SAML
# ------------------------------------------------------------------------------
# For more configuration and customization options, see djangosaml2 documentation
# https://djangosaml2.readthedocs.io/contents/setup.html#configuration
# To override not configurable settings, you can use local_settings.py
# function that helps convert env var into the djangosaml2 attribute mapping format
# https://djangosaml2.readthedocs.io/contents/setup.html#users-attributes-and-account-linking


def saml2_attrib_map_format(dict):
    dout = {}
    for i in dict:
        dout[i] = (dict[i],)
    return dout


SAML2_ENABLED = env('DD_SAML2_ENABLED')
SAML2_LOGIN_BUTTON_TEXT = env('DD_SAML2_LOGIN_BUTTON_TEXT')
SAML2_LOGOUT_URL = env('DD_SAML2_LOGOUT_URL')
if SAML2_ENABLED:
    import saml2
    import saml2.saml
    from os import path
    # SSO_URL = env('DD_SSO_URL')
    SAML_METADATA = {}
    if len(env('DD_SAML2_METADATA_AUTO_CONF_URL')) > 0:
        SAML_METADATA['remote'] = [{"url": env('DD_SAML2_METADATA_AUTO_CONF_URL')}]
    if len(env('DD_SAML2_METADATA_LOCAL_FILE_PATH')) > 0:
        SAML_METADATA['local'] = [env('DD_SAML2_METADATA_LOCAL_FILE_PATH')]
    INSTALLED_APPS += ('djangosaml2',)
    MIDDLEWARE.append('djangosaml2.middleware.SamlSessionMiddleware')
    AUTHENTICATION_BACKENDS += ('djangosaml2.backends.Saml2Backend',)
    LOGIN_EXEMPT_URLS += (r'^%ssaml2/' % URL_PREFIX,)
    SAML_LOGOUT_REQUEST_PREFERRED_BINDING = saml2.BINDING_HTTP_POST
    SAML_IGNORE_LOGOUT_ERRORS = True
    SAML_DJANGO_USER_MAIN_ATTRIBUTE = 'username'
#    SAML_DJANGO_USER_MAIN_ATTRIBUTE_LOOKUP = '__iexact'
    SAML_USE_NAME_ID_AS_USERNAME = True
    SAML_CREATE_UNKNOWN_USER = env('DD_SAML2_CREATE_USER')
    SAML_ATTRIBUTE_MAPPING = saml2_attrib_map_format(env('DD_SAML2_ATTRIBUTES_MAP'))
    SAML_FORCE_AUTH = env('DD_SAML2_FORCE_AUTH')
    SAML_ALLOW_UNKNOWN_ATTRIBUTES = env('DD_SAML2_ALLOW_UNKNOWN_ATTRIBUTE')
    BASEDIR = path.dirname(path.abspath(__file__))
    if len(env('DD_SAML2_ENTITY_ID')) == 0:
        SAML2_ENTITY_ID = '%s/saml2/metadata/' % SITE_URL
    else:
        SAML2_ENTITY_ID = env('DD_SAML2_ENTITY_ID')

    SAML_CONFIG = {
        # full path to the xmlsec1 binary programm
        'xmlsec_binary': '/usr/bin/xmlsec1',

        # your entity id, usually your subdomain plus the url to the metadata view
        'entityid': '%s' % SAML2_ENTITY_ID,

        # directory with attribute mapping
        'attribute_map_dir': path.join(BASEDIR, 'attribute-maps'),
        # do now discard attributes not specified in attribute-maps
        'allow_unknown_attributes': SAML_ALLOW_UNKNOWN_ATTRIBUTES,
        # this block states what services we provide
        'service': {
            # we are just a lonely SP
            'sp': {
                'name': 'Defect_Dojo',
                'name_id_format': saml2.saml.NAMEID_FORMAT_TRANSIENT,
                'want_response_signed': False,
                'want_assertions_signed': True,
                'force_authn': SAML_FORCE_AUTH,
                'allow_unsolicited': True,

                # For Okta add signed logout requets. Enable this:
                # "logout_requests_signed": True,

                'endpoints': {
                    # url and binding to the assetion consumer service view
                    # do not change the binding or service name
                    'assertion_consumer_service': [
                        ('%s/saml2/acs/' % SITE_URL,
                        saml2.BINDING_HTTP_POST),
                    ],
                    # url and binding to the single logout service view
                    # do not change the binding or service name
                    'single_logout_service': [
                        # Disable next two lines for HTTP_REDIRECT for IDP's that only support HTTP_POST. Ex. Okta:
                        ('%s/saml2/ls/' % SITE_URL,
                        saml2.BINDING_HTTP_REDIRECT),
                        ('%s/saml2/ls/post' % SITE_URL,
                        saml2.BINDING_HTTP_POST),
                    ],
                },

                # attributes that this project need to identify a user
                'required_attributes': ['Email', 'UserName'],

                # attributes that may be useful to have but not required
                'optional_attributes': ['Firstname', 'Lastname'],

                # in this section the list of IdPs we talk to are defined
                # This is not mandatory! All the IdP available in the metadata will be considered.
                # 'idp': {
                #     # we do not need a WAYF service since there is
                #     # only an IdP defined here. This IdP should be
                #     # present in our metadata

                #     # the keys of this dictionary are entity ids
                #     'https://localhost/simplesaml/saml2/idp/metadata.php': {
                #         'single_sign_on_service': {
                #             saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SSOService.php',
                #         },
                #         'single_logout_service': {
                #             saml2.BINDING_HTTP_REDIRECT: 'https://localhost/simplesaml/saml2/idp/SingleLogoutService.php',
                #         },
                #     },
                # },
            },
        },

        # where the remote metadata is stored, local, remote or mdq server.
        # One metadatastore or many ...
        'metadata': SAML_METADATA,

        # set to 1 to output debugging information
        'debug': 0,

        # Signing
        # 'key_file': path.join(BASEDIR, 'private.key'),  # private part
        # 'cert_file': path.join(BASEDIR, 'public.pem'),  # public part

        # Encryption
        # 'encryption_keypairs': [{
        #     'key_file': path.join(BASEDIR, 'private.key'),  # private part
        #     'cert_file': path.join(BASEDIR, 'public.pem'),  # public part
        # }],

        # own metadata settings
        'contact_person': [
            {'given_name': 'Lorenzo',
            'sur_name': 'Gil',
            'company': 'Yaco Sistemas',
            'email_address': 'lgs@yaco.es',
            'contact_type': 'technical'},
            {'given_name': 'Angel',
            'sur_name': 'Fernandez',
            'company': 'Yaco Sistemas',
            'email_address': 'angel@yaco.es',
            'contact_type': 'administrative'},
        ],
        # you can set multilanguage information here
        'organization': {
            'name': [('Yaco Sistemas', 'es'), ('Yaco Systems', 'en')],
            'display_name': [('Yaco', 'es'), ('Yaco', 'en')],
            'url': [('http://www.yaco.es', 'es'), ('http://www.yaco.com', 'en')],
        },
        'valid_for': 24,  # how long is our metadata valid
    }

# ------------------------------------------------------------------------------
# CELERY
# ------------------------------------------------------------------------------

# Celery settings
CELERY_BROKER_URL = env('DD_CELERY_BROKER_URL') \
    if len(env('DD_CELERY_BROKER_URL')) > 0 else generate_url(
    env('DD_CELERY_BROKER_SCHEME'),
    True,
    env('DD_CELERY_BROKER_USER'),
    env('DD_CELERY_BROKER_PASSWORD'),
    env('DD_CELERY_BROKER_HOST'),
    env('DD_CELERY_BROKER_PORT'),
    env('DD_CELERY_BROKER_PATH'),
    env('DD_CELERY_BROKER_PARAMS')
)
CELERY_TASK_IGNORE_RESULT = env('DD_CELERY_TASK_IGNORE_RESULT')
CELERY_RESULT_BACKEND = env('DD_CELERY_RESULT_BACKEND')
CELERY_TIMEZONE = TIME_ZONE
CELERY_RESULT_EXPIRES = env('DD_CELERY_RESULT_EXPIRES')
CELERY_BEAT_SCHEDULE_FILENAME = env('DD_CELERY_BEAT_SCHEDULE_FILENAME')
CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml']
CELERY_TASK_SERIALIZER = env('DD_CELERY_TASK_SERIALIZER')
CELERY_PASS_MODEL_BY_ID = env('DD_CELERY_PASS_MODEL_BY_ID')

CELERY_IMPORTS = ('dojo.tools.tool_issue_updater', )

# Celery beat scheduled tasks
CELERY_BEAT_SCHEDULE = {
    'add-alerts': {
        'task': 'dojo.tasks.add_alerts',
        'schedule': timedelta(hours=1),
        'args': [timedelta(hours=1)]
    },
    'cleanup-alerts': {
        'task': 'dojo.tasks.cleanup_alerts',
        'schedule': timedelta(hours=8),
    },
    'dedupe-delete': {
        'task': 'dojo.tasks.async_dupe_delete',
        'schedule': timedelta(minutes=1),
        'args': [timedelta(minutes=1)]
    },
    'update-findings-from-source-issues': {
        'task': 'dojo.tools.tool_issue_updater.update_findings_from_source_issues',
        'schedule': timedelta(hours=3),
    },
    'compute-sla-age-and-notify': {
        'task': 'dojo.tasks.async_sla_compute_and_notify_task',
        'schedule': crontab(hour=7, minute=30),
    },
    'risk_acceptance_expiration_handler': {
        'task': 'dojo.risk_acceptance.helper.expiration_handler',
        'schedule': crontab(minute=0, hour='*/3'),  # every 3 hours
    },
    # 'jira_status_reconciliation': {
    #     'task': 'dojo.tasks.jira_status_reconciliation_task',
    #     'schedule': timedelta(hours=12),
    #     'kwargs': {'mode': 'reconcile', 'dryrun': True, 'daysback': 10, 'product': None, 'engagement': None}
    # },
    # 'fix_loop_duplicates': {
    #     'task': 'dojo.tasks.fix_loop_duplicates_task',
    #     'schedule': timedelta(hours=12)
    # },


}

# ------------------------------------
# Monitoring Metrics
# ------------------------------------
# address issue when running ./manage.py collectstatic
# reference: https://github.com/korfuri/django-prometheus/issues/34
PROMETHEUS_EXPORT_MIGRATIONS = False
# django metrics for monitoring
if env('DD_DJANGO_METRICS_ENABLED'):
    DJANGO_METRICS_ENABLED = env('DD_DJANGO_METRICS_ENABLED')
    INSTALLED_APPS = INSTALLED_APPS + ('django_prometheus',)
    MIDDLEWARE = ['django_prometheus.middleware.PrometheusBeforeMiddleware', ] + \
        MIDDLEWARE + \
        ['django_prometheus.middleware.PrometheusAfterMiddleware', ]
    database_engine = DATABASES.get('default').get('ENGINE')
    DATABASES['default']['ENGINE'] = database_engine.replace('django.', 'django_prometheus.', 1)
    # CELERY_RESULT_BACKEND.replace('django.core','django_prometheus.', 1)
    LOGIN_EXEMPT_URLS += (r'^%sdjango_metrics/' % URL_PREFIX,)


# ------------------------------------
# Hashcode configuration
# ------------------------------------
# List of fields used to compute the hash_code
# The fields must be one of HASHCODE_ALLOWED_FIELDS
# If not present, default is the legacy behavior: see models.py, compute_hash_code_legacy function
# legacy is:
#   static scanner:  ['title', 'cwe', 'line', 'file_path', 'description']
#   dynamic scanner: ['title', 'cwe', 'line', 'file_path', 'description']
HASHCODE_FIELDS_PER_SCANNER = {
    # In checkmarx, same CWE may appear with different severities: example "sql injection" (high) and "blind sql injection" (low).
    # Including the severity in the hash_code keeps those findings not duplicate
    'Anchore Engine Scan': ['title', 'severity', 'component_name', 'component_version', 'file_path'],
    'Anchore Grype': ['title', 'severity', 'component_name', 'component_version'],
    'Aqua Scan': ['severity', 'vulnerability_ids', 'component_name', 'component_version'],
    'Bandit Scan': ['file_path', 'line', 'vuln_id_from_tool'],
    'CargoAudit Scan': ['vulnerability_ids', 'severity', 'component_name', 'component_version', 'vuln_id_from_tool'],
    'Checkmarx Scan': ['cwe', 'severity', 'file_path'],
    'Checkmarx OSA': ['vulnerability_ids', 'component_name'],
    'Cloudsploit Scan': ['title', 'description'],
    'SonarQube Scan': ['cwe', 'severity', 'file_path'],
    'SonarQube API Import': ['title', 'file_path', 'line'],
    'Dependency Check Scan': ['vulnerability_ids', 'cwe', 'file_path'],
    'Dockle Scan': ['title', 'description', 'vuln_id_from_tool'],
    'Dependency Track Finding Packaging Format (FPF) Export': ['component_name', 'component_version', 'cwe', 'vulnerability_ids'],
    'Mobsfscan Scan': ['title', 'severity', 'cwe'],
    'Nessus Scan': ['title', 'severity', 'vulnerability_ids', 'cwe'],
    'Nexpose Scan': ['title', 'severity', 'vulnerability_ids', 'cwe'],
    # possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity
    'NPM Audit Scan': ['title', 'severity', 'file_path', 'vulnerability_ids', 'cwe'],
    # possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity
    'Yarn Audit Scan': ['title', 'severity', 'file_path', 'vulnerability_ids', 'cwe'],
    # possible improvement: in the scanner put the library name into file_path, then dedup on vulnerability_ids + file_path + severity
    'Whitesource Scan': ['title', 'severity', 'description'],
    'ZAP Scan': ['title', 'cwe', 'severity'],
    'Qualys Scan': ['title', 'severity'],
    # 'Qualys Webapp Scan': ['title', 'unique_id_from_tool'],
    'PHP Symfony Security Check': ['title', 'vulnerability_ids'],
    'Clair Scan': ['title', 'vulnerability_ids', 'description', 'severity'],
    'Clair Klar Scan': ['title', 'description', 'severity'],
    # for backwards compatibility because someone decided to rename this scanner:
    'Symfony Security Check': ['title', 'vulnerability_ids'],
    'DSOP Scan': ['vulnerability_ids'],
    'Acunetix Scan': ['title', 'description'],
    'Terrascan Scan': ['vuln_id_from_tool', 'title', 'severity', 'file_path', 'line', 'component_name'],
    'Trivy Scan': ['title', 'severity', 'vulnerability_ids', 'cwe'],
    'TFSec Scan': ['severity', 'vuln_id_from_tool', 'file_path', 'line'],
    'Snyk Scan': ['vuln_id_from_tool', 'file_path', 'component_name', 'component_version'],
    'GitLab Dependency Scanning Report': ['title', 'vulnerability_ids', 'file_path', 'component_name', 'component_version'],
    'SpotBugs Scan': ['cwe', 'severity', 'file_path', 'line'],
    'JFrog Xray Unified Scan': ['vulnerability_ids', 'file_path', 'component_name', 'component_version'],
    'Scout Suite Scan': ['file_path', 'vuln_id_from_tool'],  # for now we use file_path as there is no attribute for "service"
    'AWS Security Hub Scan': ['unique_id_from_tool'],
    'Meterian Scan': ['cwe', 'component_name', 'component_version', 'description', 'severity'],
    'Github Vulnerability Scan': ['unique_id_from_tool'],
    'Azure Security Center Recommendations Scan': ['unique_id_from_tool'],
    'Solar Appscreener Scan': ['title', 'file_path', 'line', 'severity'],
    'pip-audit Scan': ['vuln_id_from_tool', 'component_name', 'component_version'],
    'Edgescan Scan': ['unique_id_from_tool'],
    'Rubocop Scan': ['vuln_id_from_tool', 'file_path', 'line'],
    'JFrog Xray Scan': ['title', 'description', 'component_name', 'component_version'],
    'CycloneDX Scan': ['vuln_id_from_tool', 'component_name', 'component_version'],
    'SSLyze Scan (JSON)': ['title', 'description'],
    'Harbor Vulnerability Scan': ['title'],
    'Rusty Hog Scan': ['title', 'description'],
    'StackHawk HawkScan': ['vuln_id_from_tool', 'component_name', 'component_version'],
    'Hydra Scan': ['title', 'description'],
    'DrHeader JSON Importer': ['title', 'description'],
}

# This tells if we should accept cwe=0 when computing hash_code with a configurable list of fields from HASHCODE_FIELDS_PER_SCANNER (this setting doesn't apply to legacy algorithm)
# If False and cwe = 0, then the hash_code computation will fallback to legacy algorithm for the concerned finding
# Default is True (if scanner is not configured here but is configured in HASHCODE_FIELDS_PER_SCANNER, it allows null cwe)
HASHCODE_ALLOWS_NULL_CWE = {
    'Anchore Engine Scan': True,
    'Anchore Grype': True,
    'AWS Prowler Scan': True,
    'Checkmarx Scan': False,
    'Checkmarx OSA': True,
    'Cloudsploit Scan': True,
    'SonarQube Scan': False,
    'Dependency Check Scan': True,
    'Mobsfscan Scan': False,
    'Nessus Scan': True,
    'Nexpose Scan': True,
    'NPM Audit Scan': True,
    'Yarn Audit Scan': True,
    'Whitesource Scan': True,
    'ZAP Scan': False,
    'Qualys Scan': True,
    'DSOP Scan': True,
    'Acunetix Scan': True,
    'Trivy Scan': True,
    'SpotBugs Scan': False,
    'Scout Suite Scan': True,
    'AWS Security Hub Scan': True,
    'Meterian Scan': True,
    'SARIF': True,
    'Hadolint Dockerfile check': True,
    'Semgrep JSON Report': True,
    'Generic Findings Import': True,
    'Edgescan Scan': True,
}

# List of fields that are known to be usable in hash_code computation)
# 'endpoints' is a pseudo field that uses the endpoints (for dynamic scanners)
# 'unique_id_from_tool' is often not needed here as it can be used directly in the dedupe algorithm, but it's also possible to use it for hashing
HASHCODE_ALLOWED_FIELDS = ['title', 'cwe', 'vulnerability_ids', 'line', 'file_path', 'component_name', 'component_version', 'description', 'endpoints', 'unique_id_from_tool', 'severity', 'vuln_id_from_tool']

# Adding fields to the hash_code calculation regardless of the previous settings
HASH_CODE_FIELDS_ALWAYS = ['service']

# ------------------------------------
# Deduplication configuration
# ------------------------------------
# List of algorithms
# legacy one with multiple conditions (default mode)
DEDUPE_ALGO_LEGACY = 'legacy'
# based on dojo_finding.unique_id_from_tool only (for checkmarx detailed, or sonarQube detailed for example)
DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL = 'unique_id_from_tool'
# based on dojo_finding.hash_code only
DEDUPE_ALGO_HASH_CODE = 'hash_code'
# unique_id_from_tool or hash_code
# Makes it possible to deduplicate on a technical id (same parser) and also on some functional fields (cross-parsers deduplication)
DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE = 'unique_id_from_tool_or_hash_code'

# Allows to deduplicate with endpoints if endpoints is not included in the hashcode.
# Possible values are: scheme, host, port, path, query, fragment, userinfo, and user. For a details description see https://hyperlink.readthedocs.io/en/latest/api.html#attributes.
# Example:
# Finding A and B have the same hashcode. Finding A has endpoint http://defectdojo.com and finding B has endpoint https://defectdojo.com/finding.
# - An empyt list ([]) means, no fields are used. B is marked as duplicated of A.
# - Host (['host']) means: B is marked as duplicate of A because the host (defectdojo.com) is the same.
# - Host and path (['host', 'path']) means: A and B stay untouched because the path is different.
#
# If a finding has more than one endpoint, only one endpoint pair must match to mark the finding as duplicate.
DEDUPE_ALGO_ENDPOINT_FIELDS = ['host', 'path']

# Choice of deduplication algorithm per parser
# Key = the scan_type from factory.py (= the test_type)
# Default is DEDUPE_ALGO_LEGACY
DEDUPLICATION_ALGORITHM_PER_PARSER = {
    'Anchore Engine Scan': DEDUPE_ALGO_HASH_CODE,
    'Anchore Grype': DEDUPE_ALGO_HASH_CODE,
    'Aqua Scan': DEDUPE_ALGO_HASH_CODE,
    'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE,
    'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Bandit Scan': DEDUPE_ALGO_HASH_CODE,
    'CargoAudit Scan': DEDUPE_ALGO_HASH_CODE,
    'Checkmarx Scan detailed': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Checkmarx Scan': DEDUPE_ALGO_HASH_CODE,
    'Checkmarx OSA': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE,
    'Coverity API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Cobalt.io API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Dependency Track Finding Packaging Format (FPF) Export': DEDUPE_ALGO_HASH_CODE,
    'Mobsfscan Scan': DEDUPE_ALGO_HASH_CODE,
    'SonarQube Scan detailed': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'SonarQube Scan': DEDUPE_ALGO_HASH_CODE,
    'SonarQube API Import': DEDUPE_ALGO_HASH_CODE,
    'Dependency Check Scan': DEDUPE_ALGO_HASH_CODE,
    'Dockle Scan': DEDUPE_ALGO_HASH_CODE,
    'Nessus Scan': DEDUPE_ALGO_HASH_CODE,
    'Nexpose Scan': DEDUPE_ALGO_HASH_CODE,
    'NPM Audit Scan': DEDUPE_ALGO_HASH_CODE,
    'Yarn Audit Scan': DEDUPE_ALGO_HASH_CODE,
    'Whitesource Scan': DEDUPE_ALGO_HASH_CODE,
    'ZAP Scan': DEDUPE_ALGO_HASH_CODE,
    'Qualys Scan': DEDUPE_ALGO_HASH_CODE,
    'PHP Symfony Security Check': DEDUPE_ALGO_HASH_CODE,
    'Acunetix Scan': DEDUPE_ALGO_HASH_CODE,
    'Clair Scan': DEDUPE_ALGO_HASH_CODE,
    'Clair Klar Scan': DEDUPE_ALGO_HASH_CODE,
    # 'Qualys Webapp Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,  # Must also uncomment qualys webapp line in hashcode fields per scanner
    'Veracode Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE,
    # for backwards compatibility because someone decided to rename this scanner:
    'Symfony Security Check': DEDUPE_ALGO_HASH_CODE,
    'DSOP Scan': DEDUPE_ALGO_HASH_CODE,
    'Terrascan Scan': DEDUPE_ALGO_HASH_CODE,
    'Trivy Scan': DEDUPE_ALGO_HASH_CODE,
    'TFSec Scan': DEDUPE_ALGO_HASH_CODE,
    'HackerOne Cases': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE,
    'Snyk Scan': DEDUPE_ALGO_HASH_CODE,
    'GitLab Dependency Scanning Report': DEDUPE_ALGO_HASH_CODE,
    'GitLab SAST Report': DEDUPE_ALGO_HASH_CODE,
    'Checkov Scan': DEDUPE_ALGO_HASH_CODE,
    'SpotBugs Scan': DEDUPE_ALGO_HASH_CODE,
    'JFrog Xray Unified Scan': DEDUPE_ALGO_HASH_CODE,
    'Scout Suite Scan': DEDUPE_ALGO_HASH_CODE,
    'AWS Security Hub Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Meterian Scan': DEDUPE_ALGO_HASH_CODE,
    'Github Vulnerability Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Cloudsploit Scan': DEDUPE_ALGO_HASH_CODE,
    'KICS Scan': DEDUPE_ALGO_HASH_CODE,
    'SARIF': DEDUPE_ALGO_HASH_CODE,
    'Azure Security Center Recommendations Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
    'Hadolint Dockerfile check': DEDUPE_ALGO_HASH_CODE,
    'Semgrep JSON Report': DEDUPE_ALGO_HASH_CODE,
    'Generic Findings Import': DEDUPE_ALGO_HASH_CODE,
    'Trufflehog3 Scan': DEDUPE_ALGO_HASH_CODE,
    'Detect-secrets Scan': DEDUPE_ALGO_HASH_CODE,
    'Solar Appscreener Scan': DEDUPE_ALGO_HASH_CODE,
    'Gitleaks Scan': DEDUPE_ALGO_HASH_CODE,
    'pip-audit Scan': DEDUPE_ALGO_HASH_CODE,
    'Edgescan Scan': DEDUPE_ALGO_HASH_CODE,
    'Rubocop Scan': DEDUPE_ALGO_HASH_CODE,
    'JFrog Xray Scan': DEDUPE_ALGO_HASH_CODE,
    'CycloneDX Scan': DEDUPE_ALGO_HASH_CODE,
    'SSLyze Scan (JSON)': DEDUPE_ALGO_HASH_CODE,
    'Harbor Vulnerability Scan': DEDUPE_ALGO_HASH_CODE,
    'Rusty Hog Scan': DEDUPE_ALGO_HASH_CODE,
    'Hydra Scan': DEDUPE_ALGO_HASH_CODE,
    'DrHeader JSON Importer': DEDUPE_ALGO_HASH_CODE,
}

DUPE_DELETE_MAX_PER_RUN = env('DD_DUPE_DELETE_MAX_PER_RUN')

DISABLE_FINDING_MERGE = env('DD_DISABLE_FINDING_MERGE')

TRACK_IMPORT_HISTORY = env('DD_TRACK_IMPORT_HISTORY')

# ------------------------------------------------------------------------------
# JIRA
# ------------------------------------------------------------------------------
# The 'Bug' issue type is mandatory, as it is used as the default choice.
JIRA_ISSUE_TYPE_CHOICES_CONFIG = (
    ('Task', 'Task'),
    ('Story', 'Story'),
    ('Epic', 'Epic'),
    ('Spike', 'Spike'),
    ('Bug', 'Bug'),
    ('Security', 'Security')
)

JIRA_SSL_VERIFY = env('DD_JIRA_SSL_VERIFY')

# ------------------------------------------------------------------------------
# LOGGING
# ------------------------------------------------------------------------------
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING_HANDLER = env('DD_LOGGING_HANDLER')

LOG_LEVEL = env('DD_LOG_LEVEL')
if not LOG_LEVEL:
    LOG_LEVEL = 'DEBUG' if DEBUG else 'INFO'

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)d] %(message)s',
            'datefmt': '%d/%b/%Y %H:%M:%S',
        },
        'simple': {
            'format': '%(levelname)s %(funcName)s %(lineno)d %(message)s'
        },
        'json': {
            '()': 'json_log_formatter.JSONFormatter',
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue'
        },
    },
    'handlers': {
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        },
        'json_console': {
            'class': 'logging.StreamHandler',
            'formatter': 'json'
        },
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins', 'console'],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'django.security': {
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'celery': {
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
            # workaround some celery logging known issue
            'worker_hijack_root_logger': False,
        },
        'dojo': {
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'dojo.specific-loggers.deduplication': {
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'saml2': {
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'MARKDOWN': {
            # The markdown library is too verbose in it's logging, reducing the verbosity in our logs.
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
        'titlecase': {
            # The titlecase library is too verbose in it's logging, reducing the verbosity in our logs.
            'handlers': [r'%s' % LOGGING_HANDLER],
            'level': '%s' % LOG_LEVEL,
            'propagate': False,
        },
    }
}

# override filter to ensure sensitive variables are also hidden when DEBUG = True
DEFAULT_EXCEPTION_REPORTER_FILTER = 'dojo.settings.exception_filter.CustomExceptionReporterFilter'

# As we require `innodb_large_prefix = ON` for MySQL, we can silence the
# warning about large varchar with unique indices.
SILENCED_SYSTEM_CHECKS = ['mysql.E001']

# Issue on benchmark : "The number of GET/POST parameters exceeded settings.DATA_UPLOAD_MAX_NUMBER_FIELD S"
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240

# Maximum size of a scan file in MB
SCAN_FILE_MAX_SIZE = 100

# Apply a severity level to "Security Weaknesses" in Qualys WAS
QUALYS_WAS_WEAKNESS_IS_VULN = env("DD_QUALYS_WAS_WEAKNESS_IS_VULN")

# Create a unique finding for all findings in qualys WAS parser
# If using this, lines for Qualys WAS deduplication functions must be un-commented
QUALYS_WAS_UNIQUE_ID = False

# exclusion list for parsers
PARSER_EXCLUDE = env("DD_PARSER_EXCLUDE")

SERIALIZATION_MODULES = {
    'xml': 'tagulous.serializers.xml_serializer',
    'json': 'tagulous.serializers.json',
    'python': 'tagulous.serializers.python',
    'yaml': 'tagulous.serializers.pyyaml',
}

# There seems to be no way just use the default and just leave out jquery, so we have to copy...
# ... and keep it up-to-date.
TAGULOUS_AUTOCOMPLETE_JS = (
    # 'tagulous/lib/jquery.js',
    'tagulous/lib/select2-4/js/select2.full.min.js',
    'tagulous/tagulous.js',
    'tagulous/adaptor/select2-4.js',
)

# using 'element' for width should take width from css defined in template, but it doesn't. So set to 70% here.
TAGULOUS_AUTOCOMPLETE_SETTINGS = {'placeholder': "Enter some tags (comma separated, use enter to select / create a new tag)", 'width': '70%'}

# When enabled, staff users have full access to all product types and products
AUTHORIZATION_STAFF_OVERRIDE = env('DD_AUTHORIZATION_STAFF_OVERRIDE')

EDITABLE_MITIGATED_DATA = env('DD_EDITABLE_MITIGATED_DATA')

USE_L10N = True

# FEATURE_FINDING_GROUPS feature is moved to system_settings, will be removed from settings file
FEATURE_FINDING_GROUPS = env('DD_FEATURE_FINDING_GROUPS')
JIRA_TEMPLATE_ROOT = env('DD_JIRA_TEMPLATE_ROOT')
TEMPLATE_DIR_PREFIX = env('DD_TEMPLATE_DIR_PREFIX')

DUPLICATE_CLUSTER_CASCADE_DELETE = env('DD_DUPLICATE_CLUSTER_CASCADE_DELETE')

# Deside if SonarQube API parser should download the security hotspots
SONARQUBE_API_PARSER_HOTSPOTS = env("DD_SONARQUBE_API_PARSER_HOTSPOTS")

# when enabled, finding importing will occur asynchronously, default False
ASYNC_FINDING_IMPORT = env("DD_ASYNC_FINDING_IMPORT")
# The number of findings to be processed per celeryworker
ASYNC_FINDING_IMPORT_CHUNK_SIZE = env("DD_ASYNC_FINDING_IMPORT_CHUNK_SIZE")
# When enabled, deleting objects will be occur from the bottom up. In the example of deleting an engagement
# The objects will be deleted as follows Endpoints -> Findings -> Tests -> Engagement
ASYNC_OBJECT_DELETE = env("DD_ASYNC_OBJECT_DELETE")
# The number of objects to be deleted per celeryworker
ASYNC_OBEJECT_DELETE_CHUNK_SIZE = env("DD_ASYNC_OBEJECT_DELETE_CHUNK_SIZE")
# When enabled, display the preview of objects to be deleted. This can take a long time to render
# for very large objects
DELETE_PREVIEW = env("DD_DELETE_PREVIEW")
# Feature toggle for new authorization for configurations
FEATURE_CONFIGURATION_AUTHORIZATION = env("DD_FEATURE_CONFIGURATION_AUTHORIZATION")

# django-auditlog imports django-jsonfield-backport raises a warning that can be ignored,
# see https://github.com/laymonage/django-jsonfield-backport
SILENCED_SYSTEM_CHECKS = ["django_jsonfield_backport.W001"]

VULNERABILITY_URLS = {
    'CVE': 'https://nvd.nist.gov/vuln/detail/',
    'GHSA': 'https://github.com/advisories/',
    'OSV': 'https://osv.dev/vulnerability/',
    'PYSEC': 'https://osv.dev/vulnerability/',
    'SNYK': 'https://snyk.io/vuln/',
    'RUSTSEC': 'https://rustsec.org/advisories/',
}

# your URL
CSRF_TRUSTED_ORIGINS = ['defectdojo.bignetlab.com']
docker cp settings.dist.py django-defectdojo-uwsgi-1:/app/dojo/settings/
docker restart $(docker ps -a -q)

Defectdojo New Version

nano docker-compose.yml
  • Add this on uwsgi environment container.
DD_CSRF_TRUSTED_ORIGINS: `https://domain.com'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment