diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 24627abf78..2a65cc2e1d 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -215,9 +215,35 @@ class RegistratonMixin: return super().is_open_for_signup(request, *args, **kwargs) return False + def clean_email(self, email): + """Check if the mail is valid to the pattern in LOGIN_SIGNUP_MAIL_RESTRICTION (if enabled in settings).""" + mail_restriction = InvenTreeSetting.get_setting('LOGIN_SIGNUP_MAIL_RESTRICTION', None) + if not mail_restriction: + return super().clean_email(email) + + split_email = email.split('@') + if len(split_email) != 2: + logger.error(f'The user {email} has an invalid email address') + raise forms.ValidationError(_('The provided primary email address is not valid.')) + + mailoptions = mail_restriction.split(',') + for option in mailoptions: + if not option.startswith('@'): + log_error('LOGIN_SIGNUP_MAIL_RESTRICTION is not configured correctly') + raise forms.ValidationError(_('The provided primary email address is not valid.')) + else: + if split_email[1] == option[1:]: + return super().clean_email(email) + + logger.info(f'The provided email domain for {email} is not approved') + raise forms.ValidationError(_('The provided email domain is not approved.')) + def save_user(self, request, user, form, commit=True): """Check if a default group is set in settings.""" + # Create the user user = super().save_user(request, user, form) + + # Check if a default group is set in settings start_group = InvenTreeSetting.get_setting('SIGNUP_GROUP') if start_group: try: diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 4caa3dbfb6..c4e4b2b405 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -11,6 +11,7 @@ import json import logging import math import os +import re import uuid from datetime import datetime, timedelta from enum import Enum @@ -81,19 +82,33 @@ class BaseInvenTreeSetting(models.Model): self.clean(**kwargs) self.validate_unique(**kwargs) + # Execute before_save action + self._call_settings_function('before_save', args, kwargs) + # Update this setting in the cache if do_cache: self.save_to_cache() super().save() - # Get after_save action + # Execute after_save action + self._call_settings_function('after_save', args, kwargs) + + def _call_settings_function(self, reference: str, args, kwargs): + """Call a function associated with a particular setting. + + Args: + reference (str): The name of the function to call + args: Positional arguments to pass to the function + kwargs: Keyword arguments to pass to the function + """ + # Get action setting = self.get_setting_definition(self.key, *args, **kwargs) - after_save = setting.get('after_save', None) + settings_fnc = setting.get(reference, None) # Execute if callable - if callable(after_save): - after_save(self) + if callable(settings_fnc): + settings_fnc(self) @property def cache_key(self): @@ -771,6 +786,19 @@ def update_instance_name(setting): site_obj.save() +def validate_email_domains(setting): + """Validate the email domains setting.""" + if not setting.value: + return + + domains = setting.value.split(',') + for domain in domains: + if not domain: + raise ValidationError(_('An empty domain is not allowed.')) + if not re.match(r'^@[a-zA-Z0-9\.\-_]+$', domain): + raise ValidationError(_(f'Invalid domain name: {domain}')) + + class InvenTreeSetting(BaseInvenTreeSetting): """An InvenTreeSetting object is a key:value pair used for storing single values (e.g. one-off settings values). @@ -1375,6 +1403,13 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'validator': bool, }, + 'LOGIN_SIGNUP_MAIL_RESTRICTION': { + 'name': _('Allowed domains'), + 'description': _('Restrict signup to certain domains (comma-separated, strarting with @)'), + 'default': '', + 'before_save': validate_email_domains, + }, + 'SIGNUP_GROUP': { 'name': _('Group on signup'), 'description': _('Group to which new users are assigned on registration'), diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 2cf795de24..79c127706e 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -134,6 +134,7 @@ class SettingsTest(InvenTreeTestCase): 'units', 'requires_restart', 'after_save', + 'before_save', ] for k in setting.keys(): diff --git a/InvenTree/templates/InvenTree/settings/login.html b/InvenTree/templates/InvenTree/settings/login.html index edb77207e1..d9b11eb692 100644 --- a/InvenTree/templates/InvenTree/settings/login.html +++ b/InvenTree/templates/InvenTree/settings/login.html @@ -30,6 +30,7 @@ {% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_MAIL_TWICE" icon="fa-at" %} {% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_PWD_TWICE" icon="fa-user-lock" %} {% include "InvenTree/settings/setting.html" with key="SIGNUP_GROUP" icon="fa-users" %} + {% include "InvenTree/settings/setting.html" with key="LOGIN_SIGNUP_MAIL_RESTRICTION" icon="fa-sitemap" %}