diff --git a/server/src/uds/REST/methods/config.py b/server/src/uds/REST/methods/config.py index d61f8fff4..a37b687c4 100644 --- a/server/src/uds/REST/methods/config.py +++ b/server/src/uds/REST/methods/config.py @@ -48,12 +48,17 @@ class Config(Handler): return CfgConfig.get_config_values(self.is_admin()) def put(self) -> typing.Any: - for section, section_dict in typing.cast( - dict[str, dict[str, dict[str, str]]], self._params - ).items(): + for section, section_dict in typing.cast(dict[str, dict[str, dict[str, str]]], self._params).items(): for key, vals in section_dict.items(): - logger.info( - 'Updating config value %s.%s to %s by %s', section, key, vals['value'], self._user.name - ) - CfgConfig.update(CfgConfig.SectionType.from_str(section), key, vals['value']) + config = CfgConfig.update(CfgConfig.SectionType.from_str(section), key, vals['value']) + if config is not None: + logger.info( + 'Updating config value %s.%s to %s by %s', + section, + key, + vals['value'] if not config.is_password else '********', + self._user.name, + ) + else: + logger.error('Non existing config value %s.%s to %s by %s', section, key, vals['value'], self._user.name) return 'done' diff --git a/server/src/uds/core/util/config.py b/server/src/uds/core/util/config.py index fa4515e26..dc5168ef8 100644 --- a/server/src/uds/core/util/config.py +++ b/server/src/uds/core/util/config.py @@ -233,6 +233,10 @@ class Config: def get_type(self) -> int: return self._type + + @property + def is_password(self) -> bool: + return self._type == Config.FieldType.PASSWORD def get_params(self) -> typing.Any: return Config._config_params.get(self._section.name() + self._key, None) @@ -322,7 +326,7 @@ class Config: yield val @staticmethod - def update(section: 'Config.SectionType', key: str, value: str, check_type: bool = False) -> bool: + def update(section: 'Config.SectionType', key: str, value: str, check_type: bool = False) -> 'None|Config.Value': # If cfg value does not exists, simply ignore request try: cfg: DBConfig = DBConfig.objects.get(section=section, key=key) @@ -330,17 +334,17 @@ class Config: Config.FieldType.READ, Config.FieldType.HIDDEN, ): - return False # Skip non writable elements + return None # Skip non writable elements if cfg.field_type == Config.FieldType.PASSWORD.value: - value = CryptoManager().hash(value) + value = CryptoManager.manager().hash(value) cfg.value = value - cfg.save() + cfg.save(update_fields=['value']) logger.debug('Updated value for %s.%s to %s', section, key, value) - return True + return Config.section(section).value(key, value, type=Config.FieldType.from_int(cfg.field_type)) except Exception: - return False + return None @staticmethod def removed(section: 'Config.SectionType', key: str) -> None: diff --git a/server/src/uds/management/commands/config.py b/server/src/uds/management/commands/config.py index 78808224f..5043ed806 100644 --- a/server/src/uds/management/commands/config.py +++ b/server/src/uds/management/commands/config.py @@ -61,9 +61,7 @@ class Command(BaseCommand): mod, name = Config.SectionType.from_str(first[0]), first[1] else: mod, name = Config.SectionType.GLOBAL, first[0] - if ( - Config.update(mod, name, value) is False - ): # If not exists, try to store value without any special parameters + if Config.update(mod, name, value) is None: kwargs = {} if options['password']: kwargs['type'] = Config.FieldType.PASSWORD diff --git a/server/src/uds/mfas/Radius/mfa.py b/server/src/uds/mfas/Radius/mfa.py index 9efde30b3..c1f2db166 100644 --- a/server/src/uds/mfas/Radius/mfa.py +++ b/server/src/uds/mfas/Radius/mfa.py @@ -35,7 +35,7 @@ import logging from django.utils.translation import gettext_noop as _, gettext -from uds.core import mfas, exceptions +from uds.core import mfas, exceptions, types from uds.core.ui import gui from uds.auths.Radius import client @@ -111,6 +111,18 @@ class RadiusOTP(mfas.MFA): login_without_mfa_policy_networks = fields.login_without_mfa_policy_networks_field() allow_skip_mfa_from_networks = fields.allow_skip_mfa_from_networks_field() + send_just_username = gui.CheckBoxField( + label=_('Send only username (without domain) to radius server'), + order=55, + default=False, + tooltip=_( + 'If unchecked, username will be sent as is to radius server. \n' + 'If checked, domain part will be removed from username before sending it to radius server.' + ), + required=False, + tab=types.ui.Tab.CONFIG, + ) + def radius_client(self) -> client.RadiusClient: """Return a new radius client .""" return client.RadiusClient( @@ -158,9 +170,14 @@ class RadiusOTP(mfas.MFA): # if we are in a "all-users-otp" policy, avoid this step and go directly to ask for OTP if self.all_users_otp.value: return mfas.MFA.RESULT.OK - + + # The identifier has preference over username, but normally will be empty username = identifier or username + # Remove domain part from username if needed + if self.send_just_username.value: + username = username.strip().split('@')[0].split('\\')[-1] + web_pwd = web_password(request) try: connection = self.radius_client() @@ -220,8 +237,14 @@ class RadiusOTP(mfas.MFA): regenerate a new State after a wrong sent OTP code slightly less efficient but a lot simpler ''' + # The identifier has preference over username, but normally will be empty + # This allows derived class to "alter" the username if needed username = identifier or username + # Remove domain part from username if needed + if self.send_just_username.value: + username = username.strip().split('@')[0].split('\\')[-1] + try: err = _('Invalid OTP code') diff --git a/server/src/uds/mfas/SMS/mfa.py b/server/src/uds/mfas/SMS/mfa.py index 4a65ae22c..614e42ed2 100644 --- a/server/src/uds/mfas/SMS/mfa.py +++ b/server/src/uds/mfas/SMS/mfa.py @@ -420,13 +420,15 @@ class SMSMFA(mfas.MFA): phone: str, ) -> mfas.MFA.RESULT: url = self.build_sms_url(userid, username, code, phone) - if self.http_method.value == 'GET': - return self._send_sms_using_get(request, userid, username, url) - if self.http_method.value == 'POST': - return self._send_sms_using_post(request, userid, username, url, code, phone) - if self.http_method.value == 'PUT': - return self._send_sms_using_put(request, userid, username, url, code, phone) - raise Exception('Unknown SMS sending method') + match self.http_method.value: + case 'GET': + return self._send_sms_using_get(request, userid, username, url) + case 'POST': + return self._send_sms_using_post(request, userid, username, url, code, phone) + case 'PUT': + return self._send_sms_using_put(request, userid, username, url, code, phone) + case _: + raise Exception('Unknown SMS sending method') def label(self) -> str: return gettext('MFA Code')