# authentic2 - versatile identity manager # Copyright (C) 2010-2019 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import logging import logging.config import os # Load default from Django from django.conf import global_settings from django.utils.translation import gettext_lazy as _ from . import logger, plugins # debian/debian_config.py::extract_settings_from_environ expects CACHES to be in its NAMESPACE CACHES = global_settings.CACHES BASE_DIR = os.path.dirname(__file__) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'please-change-me-with-a-very-long-random-string' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False DEBUG_DB = False MEDIA = 'media' MEDIA_ROOT = 'media' MEDIA_URL = '/media/' # See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = [] INTERNAL_IPS = ('127.0.0.1',) DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'authentic2', } } # Hey Entr'ouvert is in France !! TIME_ZONE = 'Europe/Paris' LANGUAGE_CODE = 'fr' USE_L10N = True USE_TZ = True # Static files STATIC_URL = '/static/' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', 'authentic2.a2_rbac.context_processors.auth', 'django.template.context_processors.debug', 'django.template.context_processors.i18n', 'django.template.context_processors.media', 'django.template.context_processors.request', 'django.contrib.messages.context_processors.messages', 'django.template.context_processors.static', 'authentic2.context_processors.a2_processor', 'authentic2.context_processors.home', ], }, }, ] MIDDLEWARE = ( 'authentic2.middleware.null_character_middleware', 'authentic2.middleware.StoreRequestMiddleware', 'authentic2.middleware.RequestIdMiddleware', 'authentic2.middleware.ServiceAccessControlMiddleware', 'authentic2.middleware.CookieTestMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.http.ConditionalGetMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'authentic2.middleware.journal_middleware', ) MIDDLEWARE += ( 'authentic2.middleware.DisplayMessageBeforeRedirectMiddleware', 'authentic2.middleware.CollectIPMiddleware', 'authentic2.middleware.ViewRestrictionMiddleware', 'authentic2.middleware.OpenedSessionCookieMiddleware', ) MIDDLEWARE = plugins.register_plugins_middleware(MIDDLEWARE) ROOT_URLCONF = 'authentic2.urls' STATICFILES_FINDERS = list(global_settings.STATICFILES_FINDERS) + ['gadjo.finders.XStaticFinder'] LOCALE_PATHS = (os.path.join(BASE_DIR, 'locale'),) INSTALLED_APPS = ( 'django.contrib.staticfiles', 'django.contrib.contenttypes', 'authentic2.custom_user', 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.admin', 'django.contrib.humanize', 'django.contrib.postgres', 'django_select2', 'django_tables2', 'mellon', 'authentic2_auth_fc', 'authentic2_auth_saml', 'authentic2_auth_oidc', 'authentic2_idp_cas', 'authentic2_idp_oidc', 'authentic2.nonce', 'authentic2.saml', 'authentic2.idp', 'authentic2.idp.saml', 'authentic2.attribute_aggregator', 'authentic2.disco_service', 'authentic2.manager', 'authentic2.apps.authenticators', 'authentic2.apps.journal', 'authentic2.backends', 'authentic2', 'django_rbac', 'authentic2.a2_rbac', 'gadjo', 'rest_framework', 'xstatic.pkg.jquery', 'xstatic.pkg.jquery_ui', 'xstatic.pkg.select2', 'django.forms', ) INSTALLED_APPS = tuple(plugins.register_plugins_installed_apps(INSTALLED_APPS)) # authentication AUTHENTICATION_BACKENDS = ( 'authentic2.backends.models_backend.ModelBackend', 'authentic2.backends.ldap_backend.LDAPBackend', 'authentic2.backends.ldap_backend.LDAPBackendPasswordLost', 'authentic2.backends.models_backend.DummyModelBackend', 'authentic2.custom_user.backends.DjangoRBACBackend', 'authentic2_auth_saml.backends.SAMLBackend', 'authentic2_auth_oidc.backends.OIDCBackend', 'authentic2_auth_fc.backends.FcBackend', ) AUTHENTICATION_BACKENDS = plugins.register_plugins_authentication_backends(AUTHENTICATION_BACKENDS) CSRF_FAILURE_VIEW = 'authentic2.views.csrf_failure_view' LOGIN_REDIRECT_URL = '/' LOGIN_URL = '/login/' LOGOUT_URL = '/logout/' # Registration ACCOUNT_ACTIVATION_DAYS = 2 # Authentic2 settings ########################### # Authentication settings ########################### AUTH_USER_MODEL = 'custom_user.User' ########################### # RBAC settings ########################### RBAC_OU_MODEL = 'a2_rbac.OrganizationalUnit' RBAC_PERMISSION_MODEL = 'a2_rbac.Permission' RBAC_ROLE_MODEL = 'a2_rbac.Role' RBAC_ROLE_PARENTING_MODEL = 'a2_rbac.RoleParenting' ############################# # Identity Provider settings ############################# # List of IdP backends, mainly used to show available services in the homepage # of user, and to handle SLO for each protocols IDP_BACKENDS = ('authentic2.idp.saml.backend.SamlBackend',) + plugins.register_plugins_idp_backends(()) # Whether to autoload SAML 2.0 identity providers and services metadata # Only https URLS are accepted. # Can be none, sp, idp or both PASSWORD_HASHERS = list(global_settings.PASSWORD_HASHERS) + [ 'authentic2.hashers.Drupal7PasswordHasher', 'authentic2.hashers.SHA256PasswordHasher', 'authentic2.hashers.SSHA1PasswordHasher', 'authentic2.hashers.SMD5PasswordHasher', 'authentic2.hashers.SHA1OLDAPPasswordHasher', 'authentic2.hashers.MD5OLDAPPasswordHasher', 'authentic2.hashers.PloneSHA1PasswordHasher', ] # Serialization module to support natural keys in generic foreign keys SERIALIZATION_MODULES = { 'json': 'authentic2.serializers', } LOGGING_CONFIG = None LOGGING = { 'version': 1, 'disable_existing_loggers': True, 'filters': { 'cleaning': { '()': 'authentic2.utils.misc.CleanLogMessage', }, 'request_context': { '()': 'authentic2.log_filters.RequestContextFilter', }, 'force_debug': { '()': 'authentic2.log_filters.ForceDebugFilter', }, }, 'formatters': { 'verbose': { 'format': ( '[%(asctime)s] %(ip)s %(user)s %(request_id)s %(levelname)s %(name)s.%(funcName)s:' ' %(message)s' ), 'datefmt': '%Y-%m-%d %a %H:%M:%S', }, 'verbose_db': { 'format': '[%(asctime)s] - - - %(levelname)s %(name)s.%(funcName)s: %(message)s', 'datefmt': '%Y-%m-%d %a %H:%M:%S', }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose', 'filters': ['cleaning', 'request_context'], }, # remove request_context filter for db log to prevent infinite loop # when logging sql query to retrieve the session user 'console_db': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose_db', 'filters': ['cleaning'], }, }, 'loggers': { # even when debugging seeing SQL queries is too much, activate it # explicitly using DEBUG_DB 'django.db': { 'handlers': ['console_db'], 'level': logger.SettingsLogLevel('INFO', debug_setting='DEBUG_DB'), 'propagate': False, }, 'django': { 'level': 'INFO', }, # django_select2 outputs debug message at level INFO 'django_select2': { 'level': 'WARNING', }, # lasso has the bad habit of logging everything as errors 'Lasso': { 'filters': ['force_debug'], }, 'libxml2': { 'filters': ['force_debug'], }, 'libxmlsec': { 'filters': ['force_debug'], }, '': { 'handlers': ['console'], 'level': logger.SettingsLogLevel('INFO'), }, }, } MIGRATION_MODULES = { 'auth': 'authentic2.auth_migrations_18', } # Django REST Framework REST_FRAMEWORK = { 'NON_FIELD_ERRORS_KEY': '__all__', 'DEFAULT_PARSER_CLASSES': [ 'authentic2.utils.rest_framework.UnflattenJSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', ], 'DEFAULT_AUTHENTICATION_CLASSES': ( 'authentic2.authentication.Authentic2Authentication', 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',), 'DEFAULT_FILTER_BACKENDS': ( 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter', ), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100, } # Authentic2 Auth SAML MELLON_ADAPTER = ('authentic2_auth_saml.adapters.AuthenticAdapter',) MELLON_LOOKUP_BY_ATTRIBUTES = [ {"saml_attribute": "email", "user_field": "email", "ignore-case": True}, {"saml_attribute": "username", "user_field": "username"}, ] # timeout used in python-requests call, in seconds # we use 28s by default: timeout just before web server, which is usually 30s REQUESTS_TIMEOUT = 28 # Permissions DJANGO_RBAC_PERMISSIONS_HIERARCHY = { 'view': ['search'], 'change_password': ['view', 'search'], 'change_email': ['view', 'search'], 'reset_password': ['view', 'search'], 'activate': ['view', 'search'], 'admin': [ 'change', 'delete', 'add', 'view', 'change_password', 'reset_password', 'activate', 'search', 'change_email', 'manage_members', 'manage_authorizations', ], 'change': ['view', 'search', 'manage_members'], 'delete': ['view', 'search'], 'add': ['view', 'search'], 'manage_members': ['view', 'search'], 'manage_authorizations': ['view', 'search'], } FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' SILENCED_SYSTEM_CHECKS = ["auth.W004"] # Get select2 from local copy. SELECT2_JS = '/static/xstatic/select2.min.js' SELECT2_CSS = '/static/xstatic/select2.min.css' # Phone prefixes by country for phone number as authentication identifier PHONE_COUNTRY_CODES = { '32': {'lang': 'BE', 'area': _('Belgium')}, '33': {'lang': 'FR', 'area': _('Metropolitan France')}, '262': {'lang': 'FR', 'area': _('RĂ©union')}, '508': {'lang': 'FR', 'area': _('Saint Pierre and Miquelon')}, '590': {'lang': 'FR', 'area': _('Guadeloupe')}, '594': {'lang': 'FR', 'area': _('French Guiana')}, '596': {'lang': 'FR', 'area': _('Martinique')}, } DEFAULT_COUNTRY_CODE = '33' # # Load configuration file # if 'AUTHENTIC2_SETTINGS_FILE' in os.environ: with open(os.environ['AUTHENTIC2_SETTINGS_FILE']) as fd: exec(fd.read()) logging.config.dictConfig(LOGGING)