# 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 sys from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext_lazy as _ class Setting: SENTINEL = object() def __init__(self, default=SENTINEL, definition='', names=None): self.names = names or [] if isinstance(self.names, str): self.names = [self.names] self.names = set(self.names) self.default = default self.definition = definition def has_default(self): return self.default != self.SENTINEL class AppSettings: def __init__(self, defaults): self.defaults = defaults @property def settings(self): if not hasattr(self, '_settings'): from django.conf import settings self._settings = settings return self._settings @property def REALMS(self): realms = {} if self.A2_REGISTRATION_REALM: realms[self.A2_REGISTRATION_REALM] = self.A2_REGISTRATION_REALM def add_realms(new_realms): for realm in new_realms: if not isinstance(realm, (tuple, list)): realms[realm] = realm else: realms[realm[0]] = realm[1] from django.contrib.auth import get_backends for backend in get_backends(): if hasattr(backend, 'get_realms'): add_realms(backend.get_realms()) if self.A2_REALMS: add_realms(self.A2_REALMS) return realms.items() @property def A2_USER_CAN_RESET_PASSWORD(self): if hasattr(self.settings, 'A2_USER_CAN_RESET_PASSWORD'): return self.settings.A2_USER_CAN_RESET_PASSWORD if hasattr(self.settings, 'A2_CAN_RESET_PASSWORD'): return self.settings.A2_CAN_RESET_PASSWORD return self.defaults['A2_USER_CAN_RESET_PASSWORD'].default def __getattr__(self, key): if key not in self.defaults: raise AttributeError('unknown key %s' % key) if hasattr(self.settings, key): return getattr(self.settings, key) if self.defaults[key].names: for other_key in self.defaults[key].names: if hasattr(self.settings, other_key): return getattr(self.settings, other_key) if self.defaults[key].has_default(): return self.defaults[key].default raise ImproperlyConfigured( 'missing setting %s(%s) is mandatory' % (key, self.defaults[key].description) ) default_settings = dict( ATTRIBUTE_BACKENDS=Setting( names=('A2_ATTRIBUTE_BACKENDS',), default=( 'authentic2.attributes_ng.sources.format', 'authentic2.attributes_ng.sources.function', 'authentic2.attributes_ng.sources.django_user', 'authentic2.attributes_ng.sources.ldap', 'authentic2.attributes_ng.sources.service_roles', ), definition='List of attribute backend classes or modules', ), CAFILE=Setting( names=('AUTHENTIC2_CAFILE', 'CAFILE'), default=None, definition='File containing certificate chains as PEM certificates', ), A2_REGISTRATION_CAN_DELETE_ACCOUNT=Setting( default=True, definition='Can user self delete their account and all their data' ), A2_REGISTRATION_CAN_CHANGE_PASSWORD=Setting( default=True, definition='Allow user to change its own password' ), A2_REGISTRATION_EMAIL_BLACKLIST=Setting( default=[], definition='List of forbidden email wildcards, ex.: ^.*@ville.fr$' ), A2_REGISTRATION_REDIRECT=Setting( default=None, definition=( 'Forced redirection after each redirect, NEXT_URL substring is replaced by the original next_url' ' passed to /accounts/register/' ), ), A2_PROFILE_CAN_CHANGE_EMAIL=Setting(default=True, definition='Can user self change their email'), A2_PROFILE_CAN_EDIT_PROFILE=Setting(default=True, definition='Can user self edit their profile'), A2_PROFILE_CAN_MANAGE_FEDERATION=Setting(default=True, definition='Can user manage its federations'), A2_PROFILE_CAN_MANAGE_SERVICE_AUTHORIZATIONS=Setting( default=True, definition='Allow user to revoke granted services access to its account profile data' ), A2_PROFILE_DISPLAY_EMPTY_FIELDS=Setting(default=False, definition='Include empty fields in profile view'), A2_HOMEPAGE_URL=Setting(default=None, definition='IdP has no homepage, redirect to this one.'), A2_USER_CAN_RESET_PASSWORD=Setting(default=None, definition='Allow online reset of passwords'), A2_USER_CAN_RESET_PASSWORD_BY_USERNAME=Setting( default=False, definition='Allow password reset request by username' ), A2_EMAIL_IS_UNIQUE=Setting(default=False, definition='Email of users must be unique'), A2_REGISTRATION_EMAIL_IS_UNIQUE=Setting( default=False, definition='Email of registered accounts must be unique' ), A2_REGISTRATION_FORM_USERNAME_REGEX=Setting( default=r'^[\w.@+-]+$', definition='Regex to validate usernames' ), A2_REGISTRATION_FORM_USERNAME_HELP_TEXT=Setting( default=_('Required. At most 30 characters. Letters, digits, and @/./+/-/_ only.') ), A2_REGISTRATION_FORM_USERNAME_LABEL=Setting(default=_('Username')), A2_REGISTRATION_REALM=Setting( default=None, definition='Default realm to assign to self-registrated users' ), A2_REGISTRATION_GROUPS=Setting(default=(), definition='Default groups for self-registered users'), A2_PROFILE_FIELDS=Setting(default=(), definition='Fields to show to the user in the profile page'), A2_REGISTRATION_FIELDS=Setting( default=(), definition='Fields from the user model that must appear on the registration form' ), A2_REQUIRED_FIELDS=Setting(default=(), definition='User fields that are required'), A2_REGISTRATION_REQUIRED_FIELDS=Setting( default=(), definition='Fields from the registration form that must be required' ), A2_PRE_REGISTRATION_FIELDS=Setting(default=(), definition='User fields to ask with email'), A2_REALMS=Setting(default=(), definition='List of realms to search user accounts'), A2_USERNAME_REGEX=Setting(default=None, definition='Regex that username must validate'), A2_USERNAME_LABEL=Setting(default=None, definition='Alternate username label for the login form'), A2_USERNAME_HELP_TEXT=Setting( default=None, definition='Help text to explain validation rules of usernames' ), A2_USERNAME_IS_UNIQUE=Setting(default=True, definition='Check username uniqueness'), A2_LOGIN_FORM_OU_SELECTOR_LABEL=Setting(default=None, definition='Label of OU field on login page'), A2_REGISTRATION_USERNAME_IS_UNIQUE=Setting( default=True, definition='Check username uniqueness on registration' ), IDP_BACKENDS=(), VALID_REFERERS=Setting(default=(), definition='List of prefix to match referers'), A2_OPENED_SESSION_COOKIE_NAME=Setting(default='A2_OPENED_SESSION', definition='Authentic session open'), A2_OPENED_SESSION_COOKIE_DOMAIN=Setting(default=None), A2_OPENED_SESSION_COOKIE_SECURE=Setting(default=False), A2_ATTRIBUTE_KINDS=Setting(default=(), definition='List of other attribute kinds'), A2_ATTRIBUTE_KIND_PROFILE_IMAGE_SIZE=Setting( default=200, definition='Width and height for a profile image' ), A2_VALIDATE_EMAIL=Setting( default=False, definition='Validate user email server by doing an RCPT command' ), A2_VALIDATE_EMAIL_DOMAIN=Setting(default=True, definition='Validate user email domain'), A2_PASSWORD_POLICY_MIN_CLASSES=Setting( default=3, definition='Minimum number of characters classes to be present in passwords' ), A2_PASSWORD_POLICY_MIN_LENGTH=Setting(default=8, definition='Minimum number of characters in a password'), A2_PASSWORD_POLICY_REGEX=Setting(default=None, definition='Regular expression for validating passwords'), A2_PASSWORD_POLICY_REGEX_ERROR_MSG=Setting( default=None, definition='Error message to show when the password do not validate the regular expression', ), A2_PASSWORD_POLICY_CLASS=Setting( default='authentic2.passwords.DefaultPasswordChecker', definition='path of a class to validate passwords', ), A2_PASSWORD_POLICY_SHOW_LAST_CHAR=Setting( default=False, definition='Show last character in password fields' ), A2_PASSWORD_POLICY_MIN_STRENGTH=Setting( default=None, definition='Minimun password strength on a 0-4 scale' ), A2_SUGGESTED_EMAIL_DOMAINS=Setting( default=[ 'gmail.com', 'msn.com', 'hotmail.com', 'hotmail.fr', 'wanadoo.fr', 'yahoo.fr', 'yahoo.com', 'laposte.net', 'free.fr', 'orange.fr', 'numericable.fr', ], definition='List of suggested email domains', ), A2_LOGIN_FAILURE_COUNT_BEFORE_WARNING=Setting( default=0, definition=( 'Failure count before logging a warning to authentic2.user_login_failure. No warning will be send' ' if value is 0.' ), ), PUSH_PROFILE_UPDATES=Setting(default=False, definition='Push profile update to linked services'), TEMPLATE_VARS=Setting(default={}, definition='Variable to pass to templates'), A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_FACTOR=Setting( default=1.8, definition='exponential backoff factor duration as seconds until next try after a login failure', ), A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_DURATION=Setting( default=1, definition='exponential backoff base factor duration as seconds until next try after a login failure', ), A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_MAX_DURATION=Setting( default=3600, definition=( 'maximum exponential backoff maximum duration as seconds until next try after a login failure' ), ), A2_LOGIN_EXPONENTIAL_RETRY_TIMEOUT_MIN_DURATION=Setting( default=10, definition=( 'minimum exponential backoff maximum duration as seconds until next try after a login failure' ), ), A2_VERIFY_SSL=Setting(default=True, definition='Verify SSL certificate in HTTP requests'), A2_ATTRIBUTE_KIND_TITLE_CHOICES=Setting(default=(), definition='Choices for the title attribute kind'), A2_CORS_WHITELIST=Setting( default=(), definition='List of origin URL to whitelist, must be scheme://netloc[:port]' ), A2_EMAIL_CHANGE_TOKEN_LIFETIME=Setting( default=7200, definition='Lifetime in seconds of the token sent to verify email adresses' ), A2_DELETION_REQUEST_LIFETIME=Setting( default=48 * 3600, definition='Lifetime in seconds of the user account deletion request' ), A2_REDIRECT_WHITELIST=Setting( default=(), definition='List of origins which are authorized to ask for redirection.' ), A2_API_USERS_REQUIRED_FIELDS=Setting( default=(), definition='List of fields to require on user\'s API, override other settings' ), A2_USER_FILTER=Setting( default={}, definition='Filters (as in QuerySet.filter() to apply to User queryset before authentication', ), A2_USER_EXCLUDE=Setting( default={}, definition=( 'Exclusion filter (as in QuerySet.exclude() to apply to User queryset before authentication' ), ), A2_LOGIN_REDIRECT_AUTHENTICATED_USERS_TO_HOMEPAGE=Setting( default=False, definition='Redirect authenticated users to homepage' ), A2_LOGIN_DISPLAY_A_CANCEL_BUTTON=Setting( default=False, definition='Display a cancel button.This is only applicable for Liberty single sign on requests', ), A2_SET_RANDOM_PASSWORD_ON_RESET=Setting( default=True, definition='Set a random password on request to reset the password from the front-office', ), A2_ACCOUNTS_URL=Setting(default=None, definition='IdP has no account page, redirect to this one.'), A2_CACHE_ENABLED=Setting(default=True, definition='Disable all cache decorators for testing purpose.'), A2_ACCEPT_EMAIL_AUTHENTICATION=Setting(default=True, definition='Enable authentication by email'), A2_EMAILS_IP_RATELIMIT=Setting( default='10/h', definition='Maximum rate of email sendings triggered by the same IP address.' ), A2_EMAILS_ADDRESS_RATELIMIT=Setting( default='3/d', definition='Maximum rate of emails sent to the same email address.' ), A2_USER_DELETED_KEEP_DATA=Setting( default=['email', 'uuid', 'phone'], definition='User data to keep after deletion' ), A2_USER_DELETED_KEEP_DATA_DAYS=Setting( default=365, definition='Number of days to keep data on deleted users' ), A2_TOKEN_EXISTS_WARNING=Setting( default=True, definition='If an active token exists, warn user before generating a new one.' ), A2_DUPLICATES_THRESHOLD=Setting( default=0.7, definition='Trigram similarity threshold for considering user as duplicate.' ), A2_FTS_THRESHOLD=Setting(default=0.2, definition='Trigram similarity threshold for free text search.'), A2_DUPLICATES_BIRTHDATE_BONUS=Setting( default=0.3, definition='Bonus in case of birthdate match (no bonus is 0, max is 1).' ), A2_EMAIL_FORMAT=Setting( default='multipart/alternative', definition='Send email as "multiplart/alternative" or limit to "text/plain" or "text/html".', ), A2_CLEAN_UNUSED_ACCOUNTS_MAX_MAIL_PER_PERIOD=Setting( default=250, definition='Maximum number of mails to send per period', ), ) app_settings = AppSettings(default_settings) app_settings.__name__ = __name__ sys.modules[__name__] = app_settings