diff --git a/server/src/uds/REST/methods/authenticators.py b/server/src/uds/REST/methods/authenticators.py index d2438959..e9ebbd26 100644 --- a/server/src/uds/REST/methods/authenticators.py +++ b/server/src/uds/REST/methods/authenticators.py @@ -115,7 +115,7 @@ class Authenticators(ModelHandler): 'tooltip': gettext( 'Access type for this transport. Disabled means not only hidden, but also not usable as login method.' ), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 107, 'tab': gettext('Display'), }, @@ -137,7 +137,7 @@ class Authenticators(ModelHandler): 'tooltip': gettext( 'MFA provider to use for this authenticator' ), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 108, 'tab': gui.Tab.MFA, }, diff --git a/server/src/uds/REST/methods/images.py b/server/src/uds/REST/methods/images.py index bce8cf5f..bea9ce1b 100644 --- a/server/src/uds/REST/methods/images.py +++ b/server/src/uds/REST/methods/images.py @@ -85,7 +85,7 @@ class Images(ModelHandler): 'value': '', 'label': gettext('Image'), 'tooltip': gettext('Image object'), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 100, # At end }, ) diff --git a/server/src/uds/REST/methods/meta_pools.py b/server/src/uds/REST/methods/meta_pools.py index 5e662e21..fd64863a 100644 --- a/server/src/uds/REST/methods/meta_pools.py +++ b/server/src/uds/REST/methods/meta_pools.py @@ -155,7 +155,7 @@ class MetaPools(ModelHandler): ], 'label': gettext('Policy'), 'tooltip': gettext('Service pool policy'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 100, }, { @@ -165,7 +165,7 @@ class MetaPools(ModelHandler): ], 'label': gettext('HA Policy'), 'tooltip': gettext('Service pool HA policy. Enable with care!'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 101, }, { @@ -179,7 +179,7 @@ class MetaPools(ModelHandler): ), 'label': gettext('Associated Image'), 'tooltip': gettext('Image assocciated with this service'), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 120, 'tab': gui.Tab.DISPLAY, }, @@ -196,7 +196,7 @@ class MetaPools(ModelHandler): 'tooltip': gettext( 'Pool group for this pool (for pool classify on display)' ), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 121, 'tab': gui.Tab.DISPLAY, }, @@ -205,7 +205,7 @@ class MetaPools(ModelHandler): 'value': True, 'label': gettext('Visible'), 'tooltip': gettext('If active, metapool will be visible for users'), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'order': 123, 'tab': gui.Tab.DISPLAY, }, @@ -216,7 +216,7 @@ class MetaPools(ModelHandler): 'tooltip': gettext( 'Custom message to be shown to users if access is limited by calendar rules.' ), - 'type': gui.InputField.TEXT_TYPE, + 'type': gui.InputField.Types.TEXT, 'order': 124, 'tab': gui.Tab.DISPLAY, }, @@ -228,7 +228,7 @@ class MetaPools(ModelHandler): ], 'label': gettext('Transport Selection'), 'tooltip': gettext('Transport selection policy'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 125, 'tab': gui.Tab.DISPLAY, }, diff --git a/server/src/uds/REST/methods/mfas.py b/server/src/uds/REST/methods/mfas.py index 5e8e5d07..0c286291 100644 --- a/server/src/uds/REST/methods/mfas.py +++ b/server/src/uds/REST/methods/mfas.py @@ -81,7 +81,7 @@ class MFA(ModelHandler): 'tooltip': gettext( 'Time in hours to cache device so MFA is not required again. User based.' ), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 111, }, ) @@ -95,7 +95,7 @@ class MFA(ModelHandler): 'tooltip': gettext( 'Time in minutes to allow MFA code to be used.' ), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 112, }, diff --git a/server/src/uds/REST/methods/networks.py b/server/src/uds/REST/methods/networks.py index 7a6b58b2..6317ba12 100644 --- a/server/src/uds/REST/methods/networks.py +++ b/server/src/uds/REST/methods/networks.py @@ -92,7 +92,7 @@ class Networks(ModelHandler): 'tooltip': gettext( 'Network range. Accepts most network definitions formats (range, subnet, host, etc...' ), - 'type': gui.InputField.TEXT_TYPE, + 'type': gui.InputField.Types.TEXT, 'order': 100, # At end }, ) diff --git a/server/src/uds/REST/methods/notifiers.py b/server/src/uds/REST/methods/notifiers.py index d143cc30..4911df66 100644 --- a/server/src/uds/REST/methods/notifiers.py +++ b/server/src/uds/REST/methods/notifiers.py @@ -86,7 +86,7 @@ class Notifiers(ModelHandler): 'values': [gui.choiceItem(i[0], i[1]) for i in NotificationLevel.all()], 'label': gettext('Level'), 'tooltip': gettext('Level of notifications'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'order': 102, } ]: diff --git a/server/src/uds/REST/methods/services.py b/server/src/uds/REST/methods/services.py index 37a0db3a..bc6f6a95 100644 --- a/server/src/uds/REST/methods/services.py +++ b/server/src/uds/REST/methods/services.py @@ -302,7 +302,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods 'tooltip': _( 'Kind of service counting for calculating if MAX is reached' ), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'rdonly': False, 'order': 101, }, diff --git a/server/src/uds/REST/methods/services_pool_groups.py b/server/src/uds/REST/methods/services_pool_groups.py index 131d3282..709f5911 100644 --- a/server/src/uds/REST/methods/services_pool_groups.py +++ b/server/src/uds/REST/methods/services_pool_groups.py @@ -99,7 +99,7 @@ class ServicesPoolGroups(ModelHandler): ), 'label': gettext('Associated Image'), 'tooltip': gettext('Image assocciated with this service'), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 102, } ]: diff --git a/server/src/uds/REST/methods/services_pools.py b/server/src/uds/REST/methods/services_pools.py index 663b421b..ceeab6bf 100644 --- a/server/src/uds/REST/methods/services_pools.py +++ b/server/src/uds/REST/methods/services_pools.py @@ -331,7 +331,7 @@ class ServicesPools(ModelHandler): ), 'label': gettext('Base service'), 'tooltip': gettext('Service used as base of this service pool'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'rdonly': True, 'order': 100, # Ensures is At end }, @@ -343,7 +343,7 @@ class ServicesPools(ModelHandler): ), 'label': gettext('OS Manager'), 'tooltip': gettext('OS Manager used as base of this service pool'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'rdonly': True, 'order': 101, }, @@ -354,7 +354,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'If active, the user will be allowed to remove the service "manually". Be careful with this, because the user will have the "power" to delete it\'s own service' ), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'order': 111, 'tab': gettext('Advanced'), }, @@ -365,7 +365,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'If active, the user will be allowed to reset the service' ), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'order': 112, 'tab': gettext('Advanced'), }, @@ -376,7 +376,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'If the option is enabled, UDS will not attempt to detect and remove the user services assigned but not in use.' ), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'order': 113, 'tab': gettext('Advanced'), }, @@ -385,7 +385,7 @@ class ServicesPools(ModelHandler): 'value': True, 'label': gettext('Visible'), 'tooltip': gettext('If active, transport will be visible for users'), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'order': 107, 'tab': gettext('Display'), }, @@ -400,7 +400,7 @@ class ServicesPools(ModelHandler): ), 'label': gettext('Associated Image'), 'tooltip': gettext('Image assocciated with this service'), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 120, 'tab': gettext('Display'), }, @@ -417,7 +417,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'Pool group for this pool (for pool classify on display)' ), - 'type': gui.InputField.IMAGECHOICE_TYPE, + 'type': gui.InputField.Types.IMAGECHOICE, 'order': 121, 'tab': gettext('Display'), }, @@ -428,7 +428,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'Custom message to be shown to users if access is limited by calendar rules.' ), - 'type': gui.InputField.TEXT_TYPE, + 'type': gui.InputField.Types.TEXT, 'order': 122, 'tab': gettext('Display'), }, @@ -438,7 +438,7 @@ class ServicesPools(ModelHandler): 'minValue': '0', 'label': gettext('Initial available services'), 'tooltip': gettext('Services created initially for this service pool'), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 130, 'tab': gettext('Availability'), }, @@ -450,7 +450,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'Services kept in cache for improved user service assignation' ), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 131, 'tab': gettext('Availability'), }, @@ -462,7 +462,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'Services kept in cache of level2 for improved service generation' ), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 132, 'tab': gettext('Availability'), }, @@ -474,7 +474,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'Maximum number of service (assigned and L1 cache) that can be created for this service' ), - 'type': gui.InputField.NUMERIC_TYPE, + 'type': gui.InputField.Types.NUMERIC, 'order': 133, 'tab': gettext('Availability'), }, @@ -485,7 +485,7 @@ class ServicesPools(ModelHandler): 'tooltip': gettext( 'If active, alternative transports for user will be shown' ), - 'type': gui.InputField.CHECKBOX_TYPE, + 'type': gui.InputField.Types.CHECKBOX, 'tab': gettext('Advanced'), 'order': 130, }, @@ -497,7 +497,7 @@ class ServicesPools(ModelHandler): ), 'label': gettext('Accounting'), 'tooltip': gettext('Account associated to this service pool'), - 'type': gui.InputField.CHOICE_TYPE, + 'type': gui.InputField.Types.CHOICE, 'tab': gettext('Advanced'), 'order': 131, }, @@ -624,8 +624,8 @@ class ServicesPools(ModelHandler): if self._params.get('publish_on_save', False) is True: try: item.publish() - except Exception: - pass + except Exception as e: + logger.error('Could not publish service pool %s: %s',item.name, e) def deleteItem(self, item: ServicePool) -> None: try: diff --git a/server/src/uds/REST/model.py b/server/src/uds/REST/model.py index da7e1bc7..40acc7b1 100644 --- a/server/src/uds/REST/model.py +++ b/server/src/uds/REST/model.py @@ -109,7 +109,7 @@ class BaseModelHandler(Handler): 'multiline': field.get('multiline', 0), 'tooltip': field.get('tooltip', ''), 'rdonly': field.get('rdonly', False), - 'type': field.get('type', uiGui.InputField.TEXT_TYPE), + 'type': str(field.get('type', uiGui.InputField.Types.TEXT)), 'order': field.get('order', 0), 'values': field.get('values', []), }, diff --git a/server/src/uds/core/ui/user_interface.py b/server/src/uds/core/ui/user_interface.py index 620d119b..12e54bd0 100644 --- a/server/src/uds/core/ui/user_interface.py +++ b/server/src/uds/core/ui/user_interface.py @@ -285,20 +285,24 @@ class gui: so if you use both, the used one will be "value". This is valid for all form fields. """ + class Types(enum.Enum): + TEXT = 'text' + TEXT_AUTOCOMPLETE = 'text-autocomplete' + # TEXTBOX = 'textbox' + NUMERIC = 'numeric' + PASSWORD = 'password' # nosec: this is not a password + HIDDEN = 'hidden' + CHOICE = 'choice' + MULTI_CHOICE = 'multichoice' + EDITABLE_LIST = 'editlist' + CHECKBOX = 'checkbox' + IMAGECHOICE = 'imgchoice' + DATE = 'date' + INFO = 'dummy' + + def __str__(self): + return self.value - TEXT_TYPE: typing.ClassVar[str] = 'text' - TEXT_AUTOCOMPLETE_TYPE: typing.ClassVar[str] = 'text-autocomplete' - TEXTBOX_TYPE: typing.ClassVar[str] = 'textbox' - NUMERIC_TYPE: typing.ClassVar[str] = 'numeric' - PASSWORD_TYPE: typing.ClassVar[str] = 'password' - HIDDEN_TYPE: typing.ClassVar[str] = 'hidden' - CHOICE_TYPE: typing.ClassVar[str] = 'choice' - MULTI_CHOICE_TYPE: typing.ClassVar[str] = 'multichoice' - EDITABLE_LIST: typing.ClassVar[str] = 'editlist' - CHECKBOX_TYPE: typing.ClassVar[str] = 'checkbox' - IMAGECHOICE_TYPE: typing.ClassVar[str] = 'imgchoice' - DATE_TYPE: typing.ClassVar[str] = 'date' - INFO_TYPE: typing.ClassVar[str] = 'dummy' # : If length of some fields are not especified, this value is used as default DEFAULT_LENTGH: typing.ClassVar[int] = 64 @@ -321,26 +325,26 @@ class gui: ), # This property only affects in "modify" operations 'order': options.get('order', 0), 'tooltip': options.get('tooltip', ''), - 'type': gui.InputField.TEXT_TYPE, + 'type': str(gui.InputField.Types.TEXT), 'value': options.get('value', ''), } if 'tab' in options: self._data['tab'] = str(options.get('tab')) # Ensure it's a string - def _type(self, type_: str) -> None: + def _type(self, type_: typing.Union[Types, str]) -> None: """ Sets the type of this field. Args: type: Type to set (from constants of this class) """ - self._data['type'] = type_ + self._data['type'] = str(type_) - def isType(self, type_: str) -> bool: + def isType(self, type_: typing.Union[Types, str]) -> bool: """ Returns true if this field is of specified type """ - return self._data['type'] == type_ + return self._data['type'] == str(type_) def isSerializable(self) -> bool: return True @@ -450,7 +454,7 @@ class gui: def __init__(self, **options) -> None: super().__init__(**options) - self._type(gui.InputField.TEXT_TYPE) + self._type(gui.InputField.Types.TEXT) multiline = int(options.get('multiline', 0)) if multiline > 8: multiline = 8 @@ -468,7 +472,7 @@ class gui: def __init__(self, **options) -> None: super().__init__(**options) # Change the type - self._type(gui.InputField.TEXT_AUTOCOMPLETE_TYPE) + self._type(gui.InputField.Types.TEXT_AUTOCOMPLETE) # And store values in a list self._data['values'] = gui.convertToChoices(options.get('values', [])) @@ -508,7 +512,7 @@ class gui: options.get('maxValue', options.get('maxvalue', '987654321')) ) - self._type(gui.InputField.NUMERIC_TYPE) + self._type(gui.InputField.Types.NUMERIC) def _setValue(self, value: typing.Any): # Internally stores an string @@ -568,7 +572,7 @@ class gui: self.processValue(v, options) super().__init__(**options) - self._type(gui.InputField.DATE_TYPE) + self._type(gui.InputField.Types.DATE) def date(self, min: bool = True) -> datetime.date: """ @@ -635,7 +639,7 @@ class gui: def __init__(self, **options): super().__init__(**options) - self._type(gui.InputField.PASSWORD_TYPE) + self._type(gui.InputField.Types.PASSWORD) def cleanStr(self): return str(self.value).strip() @@ -675,7 +679,7 @@ class gui: def __init__(self, **options): super().__init__(**options) self._isSerializable: bool = options.get('serializable', '') != '' - self._type(gui.InputField.HIDDEN_TYPE) + self._type(gui.InputField.Types.HIDDEN) def isSerializable(self) -> bool: return self._isSerializable @@ -701,7 +705,7 @@ class gui: def __init__(self, **options): super().__init__(**options) - self._type(gui.InputField.CHECKBOX_TYPE) + self._type(gui.InputField.Types.CHECKBOX) @staticmethod def _checkTrue(val: typing.Union[str, bytes, bool]) -> bool: @@ -832,7 +836,7 @@ class gui: fills.pop('function') self._data['fills'] = fills gui.callbacks[fills['callbackName']] = fnc - self._type(gui.InputField.CHOICE_TYPE) + self._type(gui.InputField.Types.CHOICE) def setValues(self, values: typing.List[typing.Dict[str, typing.Any]]): """ @@ -845,7 +849,7 @@ class gui: super().__init__(**options) self._data['values'] = options.get('values', []) - self._type(gui.InputField.IMAGECHOICE_TYPE) + self._type(gui.InputField.Types.IMAGECHOICE) def setValues(self, values: typing.List[typing.Any]): """ @@ -893,7 +897,7 @@ class gui: options['values'] = gui.convertToChoices(options['values']) self._data['values'] = options.get('values', []) self._data['rows'] = options.get('rows', -1) - self._type(gui.InputField.MULTI_CHOICE_TYPE) + self._type(gui.InputField.Types.MULTI_CHOICE) def setValues(self, values: typing.List[typing.Any]) -> None: """ @@ -934,7 +938,7 @@ class gui: def __init__(self, **options) -> None: super().__init__(**options) self._data['values'] = gui.convertToList(options.get('values', [])) - self._type(gui.InputField.EDITABLE_LIST) + self._type(gui.InputField.Types.EDITABLE_LIST) def _setValue(self, value): """ @@ -950,7 +954,7 @@ class gui: def __init__(self, **options) -> None: super().__init__(**options) - self._type(gui.InputField.TEXT_TYPE) + self._type(gui.InputField.Types.TEXT) class InfoField(InputField): """ @@ -959,7 +963,7 @@ class gui: def __init__(self, **options) -> None: super().__init__(**options) - self._type(gui.InputField.INFO_TYPE) + self._type(gui.InputField.Types.INFO) class UserInterfaceType(type): @@ -1082,9 +1086,9 @@ class UserInterface(metaclass=UserInterfaceType): """ dic: gui.ValuesDictType = {} for k, v in self._gui.items(): - if v.isType(gui.InputField.EDITABLE_LIST): + if v.isType(gui.InputField.Types.EDITABLE_LIST): dic[k] = gui.convertToList(v.value) - elif v.isType(gui.InputField.MULTI_CHOICE_TYPE): + elif v.isType(gui.InputField.Types.MULTI_CHOICE): dic[k] = gui.convertToChoices(v.value) else: dic[k] = v.value @@ -1110,24 +1114,24 @@ class UserInterface(metaclass=UserInterfaceType): val: typing.Any for k, v in self._gui.items(): logger.debug('serializing Key: %s/%s', k, v.value) - if v.isType(gui.InputField.HIDDEN_TYPE) and v.isSerializable() is False: + if v.isType(gui.InputField.Types.HIDDEN) and v.isSerializable() is False: # logger.debug('Field {0} is not serializable'.format(k)) continue - if v.isType(gui.InputField.INFO_TYPE): + if v.isType(gui.InputField.Types.INFO): # logger.debug('Field {} is a dummy field and will not be serialized') continue - if v.isType(gui.InputField.EDITABLE_LIST) or v.isType( - gui.InputField.MULTI_CHOICE_TYPE + if v.isType(gui.InputField.Types.EDITABLE_LIST) or v.isType( + gui.InputField.Types.MULTI_CHOICE ): # logger.debug('Serializing value {0}'.format(v.value)) val = MULTIVALUE_FIELD + pickle.dumps(v.value, protocol=0) - elif v.isType(gui.InfoField.PASSWORD_TYPE): + elif v.isType(gui.InfoField.Types.PASSWORD): val = PASSWORD_FIELD + cryptoManager().AESCrypt( v.value.encode('utf8'), settings.SECRET_KEY.encode(), True ) - elif v.isType(gui.InputField.NUMERIC_TYPE): + elif v.isType(gui.InputField.Types.NUMERIC): val = str(int(v.num())).encode('utf8') - elif v.isType(gui.InputField.CHECKBOX_TYPE): + elif v.isType(gui.InputField.Types.CHECKBOX): val = v.isTrue() else: val = v.value.encode('utf8') @@ -1154,7 +1158,7 @@ class UserInterface(metaclass=UserInterfaceType): # Set all values to defaults ones for k in self._gui: if ( - self._gui[k].isType(gui.InputField.HIDDEN_TYPE) + self._gui[k].isType(gui.InputField.Types.HIDDEN) and self._gui[k].isSerializable() is False ): # logger.debug('Field {0} is not unserializable'.format(k))