From e4d2d2a84314cd15bc3e18b4e71f49b8d40037c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Thu, 11 Jan 2024 23:19:58 +0100 Subject: [PATCH] A lot of refactorization --- server/src/uds/REST/methods/actor_v3.py | 8 +- server/src/uds/REST/methods/config.py | 2 +- server/src/uds/REST/methods/connection.py | 4 +- server/src/uds/REST/methods/login_logout.py | 4 +- server/src/uds/REST/methods/servers.py | 10 +- server/src/uds/REST/methods/services_pools.py | 4 +- .../uds/REST/methods/tunnels_management.py | 2 +- server/src/uds/auths/Radius/authenticator.py | 6 +- server/src/uds/auths/SAML/saml.py | 2 +- server/src/uds/core/environment.py | 30 ++-- server/src/uds/core/jobs/delayed_task.py | 2 +- .../src/uds/core/jobs/delayed_task_runner.py | 2 +- server/src/uds/core/jobs/job.py | 2 +- server/src/uds/core/managers/publication.py | 18 +-- server/src/uds/core/managers/servers.py | 2 +- .../core/managers/servers_api/requester.py | 2 +- server/src/uds/core/managers/stats.py | 2 +- server/src/uds/core/managers/task.py | 18 +-- server/src/uds/core/managers/user_service.py | 22 +-- .../uds/core/managers/userservice/comms.py | 4 +- server/src/uds/core/messaging/config.py | 2 +- server/src/uds/core/messaging/processor.py | 2 +- .../src/uds/core/services/provider_factory.py | 21 ++- server/src/uds/core/services/service.py | 4 +- server/src/uds/core/services/user_service.py | 6 +- server/src/uds/core/types/log.py | 4 +- server/src/uds/core/types/ui.py | 1 + server/src/uds/core/ui/user_interface.py | 130 ++++++++++-------- server/src/uds/core/util/cache.py | 1 - server/src/uds/core/util/calendar/__init__.py | 4 +- server/src/uds/core/util/config.py | 40 +++--- server/src/uds/core/util/decorators.py | 2 +- server/src/uds/core/util/hash.py | 2 +- server/src/uds/core/util/security.py | 12 +- server/src/uds/core/util/storage.py | 128 ++++++++++------- server/src/uds/core/util/validators.py | 68 ++++----- .../src/uds/core/workers/assigned_unused.py | 2 +- .../workers/hanged_userservice_cleaner.py | 4 +- .../uds/core/workers/publication_cleaner.py | 2 +- .../uds/core/workers/service_pool_cleaner.py | 2 +- .../src/uds/core/workers/stats_collector.py | 2 +- .../uds/core/workers/userservice_cleaner.py | 4 +- server/src/uds/management/commands/config.py | 2 +- .../src/uds/management/commands/showconfig.py | 2 +- server/src/uds/mfas/Email/mfa.py | 6 +- server/src/uds/mfas/SMS/mfa.py | 2 +- server/src/uds/middleware/request.py | 4 +- .../uds/migrations/fixers/properties_v4.py | 4 +- .../fixers/providers_v4/_migrator.py | 2 +- server/src/uds/models/authenticator.py | 2 +- server/src/uds/models/managed_object_model.py | 2 +- server/src/uds/models/mfa.py | 2 +- server/src/uds/models/notifications.py | 2 +- server/src/uds/models/os_manager.py | 2 +- server/src/uds/models/provider.py | 2 +- server/src/uds/models/scheduler.py | 4 +- server/src/uds/models/service.py | 4 +- server/src/uds/models/service_pool.py | 16 +-- .../uds/models/service_pool_publication.py | 4 +- server/src/uds/models/transport.py | 2 +- server/src/uds/models/user_service.py | 4 +- server/src/uds/notifiers/email/notifier.py | 8 +- server/src/uds/services/OVirt/provider.py | 4 +- server/src/uds/services/OVirt/service.py | 2 +- .../src/uds/services/OpenGnsys/og/__init__.py | 6 +- server/src/uds/services/OpenGnsys/provider.py | 2 +- .../src/uds/services/OpenNebula/provider.py | 2 +- server/src/uds/services/OpenNebula/service.py | 2 +- .../OpenStack/openstack/openstack_client.py | 2 +- server/src/uds/services/OpenStack/provider.py | 2 +- .../uds/services/OpenStack/provider_legacy.py | 2 +- server/src/uds/services/OpenStack/service.py | 2 +- .../services/PhysicalMachines/service_base.py | 2 +- .../uds/services/Proxmox/client/__init__.py | 8 +- server/src/uds/services/Proxmox/provider.py | 2 +- server/src/uds/services/Proxmox/service.py | 2 +- server/src/uds/services/Xen/service.py | 2 +- server/src/uds/transports/RDP/rdptunnel.py | 2 +- .../src/uds/transports/SPICE/spicetunnel.py | 2 +- server/src/uds/transports/URL/url_custom.py | 14 +- server/src/uds/transports/X2GO/x2gotunnel.py | 2 +- server/src/uds/urls.py | 10 +- server/src/uds/web/util/authentication.py | 31 ++--- server/src/uds/web/util/configjs.py | 6 +- server/src/uds/web/util/errors.py | 8 +- server/src/uds/web/util/services.py | 10 +- server/src/uds/web/views/__init__.py | 12 +- server/src/uds/web/views/auth.py | 14 +- server/src/uds/web/views/main.py | 16 +-- server/src/uds/web/views/service.py | 14 +- .../ui/test_userinterface_serialization.py | 16 ++- server/tests/core/util/test_storage.py | 102 ++++++++++---- server/tests/fixtures/user_interface.py | 24 +++- server/tests/web/util/test_services.py | 6 +- 94 files changed, 563 insertions(+), 433 deletions(-) diff --git a/server/src/uds/REST/methods/actor_v3.py b/server/src/uds/REST/methods/actor_v3.py index 0bf0e7f4b..fda8022e2 100644 --- a/server/src/uds/REST/methods/actor_v3.py +++ b/server/src/uds/REST/methods/actor_v3.py @@ -96,7 +96,7 @@ def checkBlockedIp(request: 'ExtendedHttpRequest') -> None: logger.info( 'Access to actor from %s is blocked for %s seconds since last fail', request.ip, - GlobalConfig.LOGIN_BLOCK.getInt(), + GlobalConfig.LOGIN_BLOCK.as_int(), ) # Sleep a while to try to minimize brute force attacks somehow time.sleep(3) # 3 seconds should be enough @@ -105,7 +105,7 @@ def checkBlockedIp(request: 'ExtendedHttpRequest') -> None: def incFailedIp(request: 'ExtendedHttpRequest') -> None: fails = cache.get(request.ip, 0) + 1 - cache.put(request.ip, fails, GlobalConfig.LOGIN_BLOCK.getInt()) + cache.put(request.ip, fails, GlobalConfig.LOGIN_BLOCK.as_int()) # Decorator that clears failed counter for the IP if succeeds @@ -509,7 +509,7 @@ class BaseReadyChange(ActorV3Action): UserServiceManager().notify_ready_from_os_manager(userService, '') # Generates a certificate and send it to client. - privateKey, cert, password = security.selfSignedCert(self._params['ip']) + privateKey, cert, password = security.create_self_signed_cert(self._params['ip']) # Store certificate with userService userService.properties['cert'] = cert userService.properties['priv'] = privateKey @@ -800,7 +800,7 @@ class Unmanaged(ActorV3Action): ip = self._params['id'][0]['ip'] # Get first IP if no valid ip found # Generates a certificate and send it to client (actor). - privateKey, certificate, password = security.selfSignedCert(ip) + privateKey, certificate, password = security.create_self_signed_cert(ip) if validId: # If id is assigned to an user service, notify "logout" to it diff --git a/server/src/uds/REST/methods/config.py b/server/src/uds/REST/methods/config.py index b70885933..e6120e55e 100644 --- a/server/src/uds/REST/methods/config.py +++ b/server/src/uds/REST/methods/config.py @@ -45,7 +45,7 @@ class Config(Handler): needs_admin = True # By default, staff is lower level needed def get(self) -> typing.Any: - return CfgConfig.getConfigValues(self.is_admin()) + return CfgConfig.get_config_values(self.is_admin()) def put(self) -> typing.Any: diff --git a/server/src/uds/REST/methods/connection.py b/server/src/uds/REST/methods/connection.py index 3a9b81fc1..523271ced 100644 --- a/server/src/uds/REST/methods/connection.py +++ b/server/src/uds/REST/methods/connection.py @@ -93,7 +93,7 @@ class Connection(Handler): self._request.user = self._user return Connection.result( - result=services.getServicesData(typing.cast(ExtendedHttpRequestWithUser, self._request)) + result=services.get_services_data(typing.cast(ExtendedHttpRequestWithUser, self._request)) ) def connection(self, idService: str, idTransport: str, skip: str = '') -> dict[str, typing.Any]: @@ -183,7 +183,7 @@ class Connection(Handler): self._request.user = self._user # type: ignore setattr(self._request, '_cryptedpass', self._session['REST']['password']) # type: ignore # pylint: disable=protected-access setattr(self._request, '_scrambler', self._request.META['HTTP_SCRAMBLER']) # type: ignore # pylint: disable=protected-access - linkInfo = services.enableService(self._request, idService=idService, idTransport=idTransport) + linkInfo = services.enable_service(self._request, idService=idService, idTransport=idTransport) if linkInfo['error']: return Connection.result(error=linkInfo['error']) return Connection.result(result=linkInfo['url']) diff --git a/server/src/uds/REST/methods/login_logout.py b/server/src/uds/REST/methods/login_logout.py index 2dd084fb1..05cfd7e41 100644 --- a/server/src/uds/REST/methods/login_logout.py +++ b/server/src/uds/REST/methods/login_logout.py @@ -104,7 +104,7 @@ class Login(Handler): logger.info( 'Access to REST API %s is blocked for %s seconds since last fail', self._request.ip, - GlobalConfig.LOGIN_BLOCK.getInt(), + GlobalConfig.LOGIN_BLOCK.as_int(), ) raise exceptions.rest.AccessDenied('Too many fails') @@ -177,7 +177,7 @@ class Login(Handler): # Sleep a while here to "prottect" time.sleep(3) # Wait 3 seconds if credentials fails for "protection" # And store in cache for blocking for a while if fails - fail_cache.put(self._request.ip, fails + 1, GlobalConfig.LOGIN_BLOCK.getInt()) + fail_cache.put(self._request.ip, fails + 1, GlobalConfig.LOGIN_BLOCK.as_int()) return Login.result(error='Invalid credentials') return Login.result( diff --git a/server/src/uds/REST/methods/servers.py b/server/src/uds/REST/methods/servers.py index 6ad50f463..30ca69d90 100644 --- a/server/src/uds/REST/methods/servers.py +++ b/server/src/uds/REST/methods/servers.py @@ -80,11 +80,11 @@ class ServerRegisterBase(Handler): raise ValueError(_('Invalid data. Max length is 2048.')) if port < 1 or port > 65535: raise ValueError(_('Invalid port. Must be between 1 and 65535')) - validators.validateIpv4OrIpv6(ip) # Will raise "validation error" - validators.validateFqdn(hostname) - validators.validateMac(mac) - validators.validateJson(data) - validators.validateServerCertificate(certificate) + validators.validate_ip(ip) # Will raise "validation error" + validators.validate_fqdn(hostname) + validators.validate_mac(mac) + validators.validate_json(data) + validators.validate_server_certificate(certificate) except Exception as e: raise rest_exceptions.RequestError(str(e)) from e diff --git a/server/src/uds/REST/methods/services_pools.py b/server/src/uds/REST/methods/services_pools.py index d48cf548f..698215d24 100644 --- a/server/src/uds/REST/methods/services_pools.py +++ b/server/src/uds/REST/methods/services_pools.py @@ -138,7 +138,7 @@ class ServicesPools(ModelHandler): def get_items(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: # Optimized query, due that there is a lot of info needed for theee - d = sql_datetime() - datetime.timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt()) + d = sql_datetime() - datetime.timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int()) return super().get_items( overview=kwargs.get('overview', True), query=( @@ -240,7 +240,7 @@ class ServicesPools(ModelHandler): if hasattr(item, 'valid_count'): valid_count = item.valid_count # type: ignore preparing_count = item.preparing_count # type: ignore - restrained = item.error_count >= GlobalConfig.RESTRAINT_COUNT.getInt() # type: ignore + restrained = item.error_count >= GlobalConfig.RESTRAINT_COUNT.as_int() # type: ignore usage_count = item.usage_count # type: ignore else: valid_count = item.userServices.exclude(state__in=State.INFO_STATES).count() diff --git a/server/src/uds/REST/methods/tunnels_management.py b/server/src/uds/REST/methods/tunnels_management.py index 7df453503..350b52e57 100644 --- a/server/src/uds/REST/methods/tunnels_management.py +++ b/server/src/uds/REST/methods/tunnels_management.py @@ -207,7 +207,7 @@ class Tunnels(ModelHandler): fields['type'] = types.servers.ServerType.TUNNEL.value fields['port'] = int(fields['port']) # Ensure host is a valid IP(4 or 6) or hostname - validators.validateHost(fields['host']) + validators.validate_host(fields['host']) def assign(self, parent: 'Model') -> typing.Any: parent = ensure.is_instance(parent, models.ServerGroup) diff --git a/server/src/uds/auths/Radius/authenticator.py b/server/src/uds/auths/Radius/authenticator.py index 71b8060a5..16c5ba5ba 100644 --- a/server/src/uds/auths/Radius/authenticator.py +++ b/server/src/uds/auths/Radius/authenticator.py @@ -174,7 +174,7 @@ class RadiusAuth(auths.Authenticator): groups.append(self.globalGroup.value.strip()) # Cache groups for "getGroups", because radius will not send us those - with self.storage.map() as storage: + with self.storage.as_dict() as storage: storage[username] = groups # Validate groups @@ -183,14 +183,14 @@ class RadiusAuth(auths.Authenticator): return types.auth.SUCCESS_AUTH def get_groups(self, username: str, groupsManager: 'auths.GroupsManager') -> None: - with self.storage.map() as storage: + with self.storage.as_dict() as storage: groupsManager.validate(storage.get(username, [])) def create_user(self, usrData: dict[str, str]) -> None: pass def remove_user(self, username: str) -> None: - with self.storage.map() as storage: + with self.storage.as_dict() as storage: if username in storage: del storage[username] return super().remove_user(username) diff --git a/server/src/uds/auths/SAML/saml.py b/server/src/uds/auths/SAML/saml.py index e8cdf81a0..d8d693b13 100644 --- a/server/src/uds/auths/SAML/saml.py +++ b/server/src/uds/auths/SAML/saml.py @@ -370,7 +370,7 @@ class SAMLAuthenticator(auths.Authenticator): except Exception as e: raise exceptions.validation.ValidationError(gettext('Invalid private key. ') + str(e)) - if not security.checkCertificateMatchPrivateKey( + if not security.check_certificate_matches_private_key( cert=self.serverCertificate.value, key=self.privateKey.value ): raise exceptions.validation.ValidationError(gettext('Certificate and private key do not match')) diff --git a/server/src/uds/core/environment.py b/server/src/uds/core/environment.py index dba7a6a49..86b1e32d2 100644 --- a/server/src/uds/core/environment.py +++ b/server/src/uds/core/environment.py @@ -51,17 +51,17 @@ class Environment: not stored with main module data. The environment is composed of a "cache" and a "storage". First are volatile data, while second are persistent data. """ - __slots__ = ['_key', '_cache', '_storage', '_idGenerators'] + __slots__ = ['_key', '_cache', '_storage', '_id_generators'] _key: str _cache: 'Cache' _storage: 'Storage' - _idGenerators: dict[str, 'UniqueIDGenerator'] + _id_generators: dict[str, 'UniqueIDGenerator'] def __init__( self, uniqueKey: str, - idGenerators: typing.Optional[dict[str, 'UniqueIDGenerator']] = None, + id_generators: typing.Optional[dict[str, 'UniqueIDGenerator']] = None, ): """ Initialized the Environment for the specified id @@ -77,7 +77,7 @@ class Environment: self._key = uniqueKey self._cache = Cache(uniqueKey) self._storage = Storage(uniqueKey) - self._idGenerators = idGenerators or {} + self._id_generators = id_generators or {} @property def cache(self) -> 'Cache': @@ -95,7 +95,7 @@ class Environment: """ return self._storage - def idGenerators(self, generatorId: str) -> 'UniqueIDGenerator': + def id_generator(self, generator_id: str) -> 'UniqueIDGenerator': """ The idea of generator of id is to obtain at some moment Ids with a proper generator. If the environment do not contains generators of id, this method will return None. @@ -103,9 +103,9 @@ class Environment: @param generatorId: Id of the generator to obtain @return: Generator for that id, or None if no generator for that id is found """ - if not self._idGenerators or generatorId not in self._idGenerators: - raise Exception(f'No generator found for {generatorId}') - return self._idGenerators[generatorId] + if not self._id_generators or generator_id not in self._id_generators: + raise Exception(f'No generator found for {generator_id}') + return self._id_generators[generator_id] @property def key(self) -> str: @@ -114,17 +114,17 @@ class Environment: """ return self._key - def clearRelatedData(self): + def clean_related_data(self): """ Removes all related information from database for this environment. """ self._cache.clear() self._storage.clear() - for _, v in self._idGenerators.items(): + for _, v in self._id_generators.items(): v.release() @staticmethod - def getEnvForTableElement( + def get_environment_for_table( tblName, id_, idGeneratorsTypes: typing.Optional[dict[str, typing.Any]] = None, @@ -147,7 +147,7 @@ class Environment: return Environment(name, idGenerators) @staticmethod - def getEnvForType(type_) -> 'Environment': + def get_environment_for_type(type_) -> 'Environment': """ Obtains an environment associated with a type instead of a record @param type_: Type @@ -167,7 +167,7 @@ class Environment: return env @staticmethod - def getGlobalEnv() -> 'Environment': + def get_common_environment() -> 'Environment': """ Provides global environment """ @@ -236,7 +236,7 @@ class Environmentable: """ return self._env.storage - def id_generators(self, generatorId: str) -> 'UniqueIDGenerator': + def id_generator(self, generatorId: str) -> 'UniqueIDGenerator': """ Utility method to access the id generator of the environment containe by this object @@ -246,4 +246,4 @@ class Environmentable: Returns: Generator for the object and the id specified """ - return self._env.idGenerators(generatorId) + return self._env.id_generator(generatorId) diff --git a/server/src/uds/core/jobs/delayed_task.py b/server/src/uds/core/jobs/delayed_task.py index ade7b4607..95beb3c09 100644 --- a/server/src/uds/core/jobs/delayed_task.py +++ b/server/src/uds/core/jobs/delayed_task.py @@ -50,7 +50,7 @@ class DelayedTask(Environmentable): """ Remember to invoke parent init in derived clases using super(myClass,self).__init__() to let this initialize its own variables """ - super().__init__(environment or Environment.getEnvForType(self.__class__)) + super().__init__(environment or Environment.get_environment_for_type(self.__class__)) def execute(self) -> None: """ diff --git a/server/src/uds/core/jobs/delayed_task_runner.py b/server/src/uds/core/jobs/delayed_task_runner.py index 0434f0633..21a840544 100644 --- a/server/src/uds/core/jobs/delayed_task_runner.py +++ b/server/src/uds/core/jobs/delayed_task_runner.py @@ -137,7 +137,7 @@ class DelayedTaskRunner(metaclass=singleton.Singleton): if taskInstance: logger.debug('Executing delayedTask:>%s<', task) # Re-create environment data - taskInstance.env = Environment.getEnvForType(taskInstance.__class__) + taskInstance.env = Environment.get_environment_for_type(taskInstance.__class__) DelayedTaskThread(taskInstance).start() def _insert(self, instance: DelayedTask, delay: int, tag: str) -> None: diff --git a/server/src/uds/core/jobs/job.py b/server/src/uds/core/jobs/job.py index a7a025765..7fd23c83c 100644 --- a/server/src/uds/core/jobs/job.py +++ b/server/src/uds/core/jobs/job.py @@ -58,7 +58,7 @@ class Job(Environmentable): """ if cls.frecuency_cfg: try: - cls.frecuency = cls.frecuency_cfg.getInt(force=True) + cls.frecuency = cls.frecuency_cfg.as_int(force=True) logger.debug( 'Setting frequency from DB setting for %s to %s', cls, cls.frecuency ) diff --git a/server/src/uds/core/managers/publication.py b/server/src/uds/core/managers/publication.py index 59373ca54..d27910eff 100644 --- a/server/src/uds/core/managers/publication.py +++ b/server/src/uds/core/managers/publication.py @@ -124,7 +124,7 @@ class PublicationLauncher(DelayedTask): serialize( now + datetime.timedelta( - hours=GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) + hours=GlobalConfig.SESSION_EXPIRE_TIME.as_int(True) ) ), ) @@ -188,7 +188,7 @@ class PublicationFinishChecker(DelayedTask): if doPublicationCleanup: pc = PublicationOldMachinesCleaner(old.id) pc.register( - GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) * 3600, + GlobalConfig.SESSION_EXPIRE_TIME.as_int(True) * 3600, 'pclean-' + str(old.id), True, ) @@ -371,27 +371,27 @@ class PublicationManager(metaclass=singleton.Singleton): raise PublishException(str(e)) from e def unpublish( - self, servicePoolPub: ServicePoolPublication + self, servicepool_publication: ServicePoolPublication ): # pylint: disable=no-self-use """ Unpublishes an active (usable) or removable publication :param servicePoolPub: Publication to unpublish """ if ( - State.from_str(servicePoolPub.state).is_usable() is False - and State.from_str(servicePoolPub.state).is_removable() is False + State.from_str(servicepool_publication.state).is_usable() is False + and State.from_str(servicepool_publication.state).is_removable() is False ): raise PublishException(_('Can\'t unpublish non usable publication')) - if servicePoolPub.userServices.exclude(state__in=State.INFO_STATES).count() > 0: + if servicepool_publication.userServices.exclude(state__in=State.INFO_STATES).count() > 0: raise PublishException( _('Can\'t unpublish publications with services in process') ) try: - pubInstance = servicePoolPub.get_instance() + pubInstance = servicepool_publication.get_instance() state = pubInstance.destroy() - servicePoolPub.set_state(State.REMOVING) + servicepool_publication.set_state(State.REMOVING) PublicationFinishChecker.state_updater( - servicePoolPub, pubInstance, state + servicepool_publication, pubInstance, state ) except Exception as e: raise PublishException(str(e)) from e diff --git a/server/src/uds/core/managers/servers.py b/server/src/uds/core/managers/servers.py index 766ed5f48..4708b3a17 100644 --- a/server/src/uds/core/managers/servers.py +++ b/server/src/uds/core/managers/servers.py @@ -73,7 +73,7 @@ class ServerManager(metaclass=singleton.Singleton): # If counters are too old, restart them if datetime.datetime.now() - self.last_counters_clean > self.MAX_COUNTERS_AGE: self.clear_unmanaged_usage() - return Storage(self.STORAGE_NAME).map(atomic=True, group='counters') + return Storage(self.STORAGE_NAME).as_dict(atomic=True, group='counters') def property_name(self, user: typing.Optional[typing.Union[str, 'models.User']]) -> str: """Returns the property name for a user""" diff --git a/server/src/uds/core/managers/servers_api/requester.py b/server/src/uds/core/managers/servers_api/requester.py index a3b455757..7f4b7828b 100644 --- a/server/src/uds/core/managers/servers_api/requester.py +++ b/server/src/uds/core/managers/servers_api/requester.py @@ -98,7 +98,7 @@ class ServerApiRequester: with tempfile.NamedTemporaryFile('w', delete=False) as f: f.write(self.server.certificate) # Save cert verify = f.name - session = security.secureRequestsSession(verify=verify) + session = security.secure_requests_session(verify=verify) # Setup headers session.headers.update( { diff --git a/server/src/uds/core/managers/stats.py b/server/src/uds/core/managers/stats.py index e80cd5475..39e01ef44 100644 --- a/server/src/uds/core/managers/stats.py +++ b/server/src/uds/core/managers/stats.py @@ -89,7 +89,7 @@ class StatsManager(metaclass=singleton.Singleton): model: type[typing.Union['StatsCounters', 'StatsEvents', 'StatsCountersAccum']], ) -> None: minTime = time.mktime( - (sql_datetime() - datetime.timedelta(days=GlobalConfig.STATS_DURATION.getInt())).timetuple() + (sql_datetime() - datetime.timedelta(days=GlobalConfig.STATS_DURATION.as_int())).timetuple() ) model.objects.filter(stamp__lt=minTime).delete() diff --git a/server/src/uds/core/managers/task.py b/server/src/uds/core/managers/task.py index fdb29dde8..ad33f6635 100644 --- a/server/src/uds/core/managers/task.py +++ b/server/src/uds/core/managers/task.py @@ -68,13 +68,13 @@ class DelayedTaskThread(BaseThread): class TaskManager(metaclass=singleton.Singleton): - __slots__ = ('threads', 'keepRunning') + __slots__ = ('threads', 'keep_running') - keepRunning: bool + keep_running: bool threads: list[BaseThread] def __init__(self): - self.keepRunning = True + self.keep_running = True self.threads = [] @staticmethod @@ -82,7 +82,7 @@ class TaskManager(metaclass=singleton.Singleton): return TaskManager() @staticmethod - def sig_term(sigNum, frame): # pylint: disable=unused-argument + def sig_term(sigNum: int, frame: typing.Any) -> None: """ This method will ensure that we finish correctly current running task before exiting. If we need to stop cause something went wrong (that should not happen), we must send sigterm, wait a while (10-20 secs) and after that send sigkill @@ -92,7 +92,7 @@ class TaskManager(metaclass=singleton.Singleton): Take a look at killTaskManager.sh :-) """ logger.info("Caught term signal, finishing task manager") - TaskManager.manager().keepRunning = False + TaskManager.manager().keep_running = False def register_job(self, jobType: type[jobs.Job]) -> None: jobName = jobType.friendly_name @@ -114,7 +114,7 @@ class TaskManager(metaclass=singleton.Singleton): self.threads.append(thread) def run(self) -> None: - self.keepRunning = True + self.keep_running = True # Don't know why, but with django 1.8, must "reset" connections so them do not fail on first access... # Is simmilar to https://code.djangoproject.com/ticket/21597#comment:29 connection.close() @@ -124,8 +124,8 @@ class TaskManager(metaclass=singleton.Singleton): self.register_scheduled_tasks() - noSchedulers: int = GlobalConfig.SCHEDULER_THREADS.getInt() - noDelayedTasks: int = GlobalConfig.DELAYED_TASKS_THREADS.getInt() + noSchedulers: int = GlobalConfig.SCHEDULER_THREADS.as_int() + noDelayedTasks: int = GlobalConfig.DELAYED_TASKS_THREADS.as_int() logger.info( 'Starting %s schedulers and %s task executors', noSchedulers, noDelayedTasks @@ -156,7 +156,7 @@ class TaskManager(metaclass=singleton.Singleton): # Remote.on() # gc.set_debug(gc.DEBUG_LEAK) - while self.keepRunning: + while self.keep_running: time.sleep(1) for thread in self.threads: diff --git a/server/src/uds/core/managers/user_service.py b/server/src/uds/core/managers/user_service.py index 17e65e047..9039a2bfc 100644 --- a/server/src/uds/core/managers/user_service.py +++ b/server/src/uds/core/managers/user_service.py @@ -327,27 +327,27 @@ class UserServiceManager(metaclass=singleton.Singleton): return user_service - def remove(self, user_service: UserService) -> UserService: + def remove(self, userservice: UserService) -> UserService: """ Removes a uService element """ with transaction.atomic(): - user_service = UserService.objects.select_for_update().get(id=user_service.id) - operationsLogger.info('Removing userService %a', user_service.name) - if user_service.is_usable() is False and State.from_str(user_service.state).is_removable() is False: + userservice = UserService.objects.select_for_update().get(id=userservice.id) + operationsLogger.info('Removing userService %a', userservice.name) + if userservice.is_usable() is False and State.from_str(userservice.state).is_removable() is False: raise OperationException(_('Can\'t remove a non active element')) - user_service.set_state(State.REMOVING) - logger.debug("***** The state now is %s *****", State.from_str(user_service.state).literal) - user_service.setInUse(False) # For accounting, ensure that it is not in use right now - user_service.save() + userservice.set_state(State.REMOVING) + logger.debug("***** The state now is %s *****", State.from_str(userservice.state).literal) + userservice.setInUse(False) # For accounting, ensure that it is not in use right now + userservice.save() - userServiceInstance = user_service.get_instance() + userServiceInstance = userservice.get_instance() state = userServiceInstance.destroy() # Data will be serialized on makeUnique process - UserServiceOpChecker.make_unique(user_service, userServiceInstance, state) + UserServiceOpChecker.make_unique(userservice, userServiceInstance, state) - return user_service + return userservice def remove_or_cancel(self, user_service: UserService): if user_service.is_usable() or State.from_str(user_service.state).is_removable(): diff --git a/server/src/uds/core/managers/userservice/comms.py b/server/src/uds/core/managers/userservice/comms.py index 84c908b38..09383f9b5 100644 --- a/server/src/uds/core/managers/userservice/comms.py +++ b/server/src/uds/core/managers/userservice/comms.py @@ -37,7 +37,7 @@ import typing import collections.abc from uds.core import exceptions, types -from uds.core.util.security import secureRequestsSession +from uds.core.util.security import secure_requests_session if typing.TYPE_CHECKING: from uds.models import UserService @@ -86,7 +86,7 @@ def _execute_actor_request( verify = f.name else: verify = False - session = secureRequestsSession(verify=cert) + session = secure_requests_session(verify=cert) if data is None: r = session.get(url, verify=verify, timeout=TIMEOUT) else: diff --git a/server/src/uds/core/messaging/config.py b/server/src/uds/core/messaging/config.py index 719916e0b..4ae0a8895 100644 --- a/server/src/uds/core/messaging/config.py +++ b/server/src/uds/core/messaging/config.py @@ -45,4 +45,4 @@ DO_NOT_REPEAT: typing.Final[cfg.Config.Value] = cfg.Config.section(cfg.Config.Se ) # Ensure that we have a default value for this on startup -DO_NOT_REPEAT.getInt() +DO_NOT_REPEAT.as_int() diff --git a/server/src/uds/core/messaging/processor.py b/server/src/uds/core/messaging/processor.py index 7124db46f..eb864b5b0 100644 --- a/server/src/uds/core/messaging/processor.py +++ b/server/src/uds/core/messaging/processor.py @@ -78,7 +78,7 @@ class MessageProcessorThread(BaseThread): # Locate all notifications from "persistent" and try to process them # If no notification can be fully resolved, it will be kept in the database not_before = sql_datetime() - datetime.timedelta( - seconds=DO_NOT_REPEAT.getInt() + seconds=DO_NOT_REPEAT.as_int() ) for n in Notification.get_persistent_queryset().all(): # If there are any other notification simmilar to this on default db, skip it diff --git a/server/src/uds/core/services/provider_factory.py b/server/src/uds/core/services/provider_factory.py index 54c03c800..c061b5524 100644 --- a/server/src/uds/core/services/provider_factory.py +++ b/server/src/uds/core/services/provider_factory.py @@ -71,7 +71,7 @@ class ServiceProviderFactory(factory.ModuleFactory[ServiceProvider]): offers = [] for s in type_.offers: if s.uses_cache_l2: - s.uses_cache = True + s.uses_cache = True # Ensures uses cache is true if s.publication_type is None: logger.error( 'Provider %s offers %s, but %s needs cache and do not have publication_type defined', @@ -93,9 +93,16 @@ class ServiceProviderFactory(factory.ModuleFactory[ServiceProvider]): Returns a list of all service providers registered that do not need to be published """ - res = [] - for p in self.providers().values(): - for s in p.offers: - if s.publication_type is None and s.must_assign_manually is False: - res.append(s) - return res + return [ + s + for p in self.providers().values() + for s in p.offers + if s.publication_type is None and s.must_assign_manually is False + ] + # old code :-) + # res = [] + # for p in self.providers().values(): + # for s in p.offers: + # if s.publication_type is None and s.must_assign_manually is False: + # res.append(s) + # return res diff --git a/server/src/uds/core/services/service.py b/server/src/uds/core/services/service.py index f5379eb1c..f40efc970 100644 --- a/server/src/uds/core/services/service.py +++ b/server/src/uds/core/services/service.py @@ -318,7 +318,7 @@ class Service(Module): Returns the environment unique mac addresses generator """ - return typing.cast('UniqueMacGenerator', self.id_generators('mac')) + return typing.cast('UniqueMacGenerator', self.id_generator('mac')) def name_generator(self) -> typing.Optional['UniqueNameGenerator']: """ @@ -326,7 +326,7 @@ class Service(Module): Returns the environment unique name generator """ - return typing.cast('UniqueNameGenerator', self.id_generators('name')) + return typing.cast('UniqueNameGenerator', self.id_generator('name')) def enumerate_assignables(self) -> collections.abc.Iterable[tuple[str, str]]: """ diff --git a/server/src/uds/core/services/user_service.py b/server/src/uds/core/services/user_service.py index 6e9f14b9d..8f0fb0ab1 100644 --- a/server/src/uds/core/services/user_service.py +++ b/server/src/uds/core/services/user_service.py @@ -238,7 +238,7 @@ class UserService(Environmentable, Serializable): Returns the environment unique mac addresses generator """ - return typing.cast('UniqueMacGenerator', self.id_generators('mac')) + return typing.cast('UniqueMacGenerator', self.id_generator('mac')) def name_generator(self) -> 'UniqueNameGenerator': """ @@ -246,7 +246,7 @@ class UserService(Environmentable, Serializable): Returns the environment unique name generator """ - return typing.cast('UniqueNameGenerator', self.id_generators('name')) + return typing.cast('UniqueNameGenerator', self.id_generator('name')) def gid_generator(self) -> 'UniqueGIDGenerator': """ @@ -254,7 +254,7 @@ class UserService(Environmentable, Serializable): Returns the environment unique global id generator """ - return typing.cast('UniqueGIDGenerator', self.id_generators('id')) + return typing.cast('UniqueGIDGenerator', self.id_generator('id')) def get_unique_id(self) -> str: """ diff --git a/server/src/uds/core/types/log.py b/server/src/uds/core/types/log.py index bcc6eec8d..3e5cbfcea 100644 --- a/server/src/uds/core/types/log.py +++ b/server/src/uds/core/types/log.py @@ -32,8 +32,8 @@ class LogObjectType(enum.IntEnum): from uds.core.util.config import GlobalConfig # pylint: disable=import-outside-toplevel if self == LogObjectType.SYSLOG: - return GlobalConfig.GENERAL_LOG_MAX_ELEMENTS.getInt() - return GlobalConfig.INDIVIDIAL_LOG_MAX_ELEMENTS.getInt() + return GlobalConfig.GENERAL_LOG_MAX_ELEMENTS.as_int() + return GlobalConfig.INDIVIDIAL_LOG_MAX_ELEMENTS.as_int() @staticmethod def get_type_from_model(model: 'Model') -> 'LogObjectType|None': diff --git a/server/src/uds/core/types/ui.py b/server/src/uds/core/types/ui.py index c6da4e9ed..49ce3a810 100644 --- a/server/src/uds/core/types/ui.py +++ b/server/src/uds/core/types/ui.py @@ -125,6 +125,7 @@ class FieldInfo: tooltip: str order: int type: FieldType + stored_field_name: typing.Optional[str] = None readonly: typing.Optional[bool] = None value: typing.Union[collections.abc.Callable[[], typing.Any], typing.Any] = None default: typing.Optional[typing.Union[collections.abc.Callable[[], str], str]] = None diff --git a/server/src/uds/core/ui/user_interface.py b/server/src/uds/core/ui/user_interface.py index c5c9fdee8..cf8345871 100644 --- a/server/src/uds/core/ui/user_interface.py +++ b/server/src/uds/core/ui/user_interface.py @@ -255,35 +255,14 @@ class gui: _fields_info: types.ui.FieldInfo - def __init__(self, label: str, type: types.ui.FieldType, **kwargs) -> None: - # if defvalue or defaultValue or defValue in kwargs, emit a warning - # with the new name (that is "default"), but use the old one - for new_name, old_names in ( - ('default', ('defvalue', 'defaultValue', 'defValue')), - ('readonly', ('rdonly, readOnly')), - ): - for i in old_names: - if i in kwargs: - try: - caller = inspect.stack()[ - 2 - ] # bypass this method and the caller (that is a derived class) - except IndexError: - caller = inspect.stack()[1] # bypass only this method - logger.warning( - 'Field %s: %s parameter is deprecated, use "%s" instead. Called from %s:%s', - label, - i, - new_name, - caller.filename, - caller.lineno, - ) - kwargs[new_name] = kwargs[i] - break + def __init__( + self, label: str, type: types.ui.FieldType, stored_field_name: typing.Optional[str], **kwargs + ) -> None: default = kwargs.get('default') # Length is not used on some kinds of fields, but present in all anyway # This property only affects in "modify" operations self._fields_info = types.ui.FieldInfo( + stored_field_name=stored_field_name, order=kwargs.get('order') or 0, label=label, tooltip=kwargs.get('tooltip') or '', @@ -318,6 +297,12 @@ class gui: def is_serializable(self) -> bool: return True + + def stored_field_name(self) -> typing.Optional[str]: + """ + Returns the name of the field + """ + return self._fields_info.stored_field_name def num(self) -> int: try: @@ -469,8 +454,10 @@ class gui: value: typing.Optional[str] = None, pattern: typing.Union[str, types.ui.FieldPatternType] = types.ui.FieldPatternType.NONE, lines: int = 0, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, length=length, readonly=readonly, @@ -515,28 +502,28 @@ class gui: if isinstance(pattern, types.ui.FieldPatternType): try: if pattern == types.ui.FieldPatternType.IPV4: - validators.validateIpv4(self.value) + validators.validate_ipv4(self.value) elif pattern == types.ui.FieldPatternType.IPV6: - validators.validateIpv6(self.value) + validators.validate_ipv6(self.value) elif pattern == types.ui.FieldPatternType.IP: - validators.validateIpv4OrIpv6(self.value) + validators.validate_ip(self.value) elif pattern == types.ui.FieldPatternType.MAC: - validators.validateMac(self.value) + validators.validate_mac(self.value) elif pattern == types.ui.FieldPatternType.URL: validators.validateUrl(self.value) elif pattern == types.ui.FieldPatternType.EMAIL: - validators.validateEmail(self.value) + validators.validate_email(self.value) elif pattern == types.ui.FieldPatternType.FQDN: - validators.validateFqdn(self.value) + validators.validate_fqdn(self.value) elif pattern == types.ui.FieldPatternType.HOSTNAME: - validators.validateHostname(self.value) + validators.validate_hostname(self.value) elif pattern == types.ui.FieldPatternType.HOST: try: - validators.validateHostname(self.value, allowDomain=True) + validators.validate_hostname(self.value, allowDomain=True) except exceptions.validation.ValidationError: - validators.validateIpv4OrIpv6(self.value) + validators.validate_ip(self.value) elif pattern == types.ui.FieldPatternType.PATH: - validators.validatePath(self.value) + validators.validate_path(self.value) return True except exceptions.validation.ValidationError: return False @@ -628,8 +615,10 @@ class gui: value: typing.Optional[int] = None, min_value: typing.Optional[int] = None, max_value: typing.Optional[int] = None, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, length=length, readonly=readonly, @@ -681,8 +670,10 @@ class gui: typing.Union[collections.abc.Callable[[], datetime.date], datetime.date] ] = None, value: typing.Optional[typing.Union[str, datetime.date]] = None, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, length=length, readonly=readonly, @@ -766,8 +757,10 @@ class gui: tab: typing.Optional[typing.Union[str, types.ui.Tab]] = None, default: typing.Union[collections.abc.Callable[[], str], str] = '', value: typing.Optional[str] = None, + stored_field_name: typing.Optional[str] = None, ): super().__init__( + stored_field_name=stored_field_name, label=label, length=length, readonly=readonly, @@ -828,8 +821,10 @@ class gui: default: typing.Any = None, # May be also callable value: typing.Any = None, serializable: bool = False, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, order=order, default=default, @@ -880,8 +875,10 @@ class gui: tab: typing.Optional[typing.Union[str, types.ui.Tab]] = None, default: typing.Union[collections.abc.Callable[[], bool], bool] = False, value: typing.Optional[bool] = None, + stored_field_name: typing.Optional[str] = None, ): super().__init__( + stored_field_name=stored_field_name, label=label, readonly=readonly, order=order, @@ -1019,8 +1016,10 @@ class gui: tab: typing.Optional[typing.Union[str, types.ui.Tab]] = None, default: typing.Union[collections.abc.Callable[[], str], str, None] = None, value: typing.Optional[str] = None, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, readonly=readonly, order=order, @@ -1067,8 +1066,10 @@ class gui: tab: typing.Optional[typing.Union[str, types.ui.Tab]] = None, default: typing.Union[collections.abc.Callable[[], str], str, None] = None, value: typing.Optional[str] = None, + stored_field_name: typing.Optional[str] = None, ): super().__init__( + stored_field_name=stored_field_name, label=label, readonly=readonly, order=order, @@ -1141,8 +1142,10 @@ class gui: collections.abc.Callable[[], str], collections.abc.Callable[[], list[str]], list[str], str, None ] = None, value: typing.Optional[collections.abc.Iterable[str]] = None, + stored_field_name: typing.Optional[str] = None, ): super().__init__( + stored_field_name=stored_field_name, label=label, readonly=readonly, order=order, @@ -1202,8 +1205,10 @@ class gui: collections.abc.Callable[[], str], collections.abc.Callable[[], list[str]], list[str], str, None ] = None, value: typing.Optional[collections.abc.Iterable[str]] = None, + stored_field_name: typing.Optional[str] = None, ) -> None: super().__init__( + stored_field_name=stored_field_name, label=label, readonly=readonly, order=order, @@ -1230,8 +1235,15 @@ class gui: """ - def __init__(self, label: str, default: str) -> None: - super().__init__(label=label, default=default, type=types.ui.FieldType.INFO) + def __init__( + self, + label: str, + default: str, + stored_field_name: typing.Optional[str] = None, + ) -> None: + super().__init__( + label=label, default=default, type=types.ui.FieldType.INFO, stored_field_name=stored_field_name + ) class UserInterfaceType(type): @@ -1259,7 +1271,7 @@ class UserInterfaceType(type): _gui[attrName] = attr new_class_dict[attrName] = attr - new_class_dict['_base_gui'] = _gui + new_class_dict['_gui_fields_template'] = _gui return typing.cast('UserInterfaceType', type.__new__(mcs, classname, bases, new_class_dict)) @@ -1285,7 +1297,7 @@ class UserInterface(metaclass=UserInterfaceAbstract): error: str # Class variable that will hold the gui fields description - _base_gui: typing.ClassVar[dict[str, gui.InputField]] + _gui_fields_template: typing.ClassVar[dict[str, gui.InputField]] # instance variable that will hold the gui fields description # this allows us to modify the gui fields values at runtime without affecting other instances @@ -1303,7 +1315,7 @@ class UserInterface(metaclass=UserInterfaceAbstract): # Ensure "gui" points to a copy of original gui, not the original one # this is done to avoid modifying the original gui description - self._gui = copy.deepcopy(self._base_gui) + self._gui = copy.deepcopy(self._gui_fields_template) # If a field has a callable on defined attributes(value, default, choices) # update the reference to the new copy @@ -1416,9 +1428,9 @@ class UserInterface(metaclass=UserInterfaceAbstract): } # Any unexpected type will raise an exception arr = [ - (k, v.type.name, fw_converters[v.type](v)) - for k, v in self._gui.items() - if fw_converters[v.type](v) is not None + (field_name, field.stored_field_name() or field.type.name, fw_converters[field.type](field)) + for field_name, field in self._gui.items() + if fw_converters[field.type](field) is not None ] return SERIALIZATION_HEADER + SERIALIZATION_VERSION + serialize(arr) @@ -1461,13 +1473,21 @@ class UserInterface(metaclass=UserInterfaceAbstract): return arr = _unserialize(values) + + # Dict of translations from stored_field_name to field_name + field_names_translations: dict[str, str] = {} + for fld_name, fld in self._gui.items(): + fld_stored_field_name = fld.stored_field_name() + if fld_stored_field_name and fld_stored_field_name != fld_name: + field_names_translations[fld_stored_field_name] = fld_name # Set all values to defaults ones - for k in self._gui: - if self._gui[k].is_type(types.ui.FieldType.HIDDEN) and self._gui[k].is_serializable() is False: + for fld_name in self._gui: + fld = self._gui[fld_name] + if self._gui[fld_name].is_type(types.ui.FieldType.HIDDEN) and self._gui[fld_name].is_serializable() is False: # logger.debug('Field {0} is not unserializable'.format(k)) continue - self._gui[k].value = self._gui[k].default + self._gui[fld_name].value = self._gui[fld_name].default converters: collections.abc.Mapping[types.ui.FieldType, collections.abc.Callable[[str], typing.Any]] = { types.ui.FieldType.TEXT: lambda x: x, @@ -1486,18 +1506,20 @@ class UserInterface(metaclass=UserInterfaceAbstract): types.ui.FieldType.INFO: lambda x: None, } - for k, t, v in arr: - if k not in self._gui: - logger.warning('Field %s not found in form', k) + for fld_name, fld_type, fld_value in arr: + if fld_name in field_names_translations: + fld_name = field_names_translations[fld_name] # Convert stored_field_name to field_name if needed + if fld_name not in self._gui: + logger.warning('Field %s not found in form', fld_name) continue - field_type = self._gui[k].type + field_type = self._gui[fld_name].type if field_type not in converters: - logger.warning('Field %s has no converter', k) + logger.warning('Field %s has no converter', fld_name) continue - if t != field_type.name: - logger.warning('Field %s has different type than expected', k) + if fld_type != field_type.name: + logger.warning('Field %s has different type than expected', fld_name) continue - self._gui[k].value = converters[field_type](v) + self._gui[fld_name].value = converters[field_type](fld_value) def deserialize_old_fields(self, values: bytes) -> None: """ diff --git a/server/src/uds/core/util/cache.py b/server/src/uds/core/util/cache.py index 335a0dfd4..55aaeb7ac 100644 --- a/server/src/uds/core/util/cache.py +++ b/server/src/uds/core/util/cache.py @@ -37,7 +37,6 @@ import logging from django.db import transaction -from django.db.utils import OperationalError from uds.models.cache import Cache as DBCache from uds.core.util.model import sql_datetime from uds.core.util import serializer diff --git a/server/src/uds/core/util/calendar/__init__.py b/server/src/uds/core/util/calendar/__init__.py index 07fafbe8b..3bde8da86 100644 --- a/server/src/uds/core/util/calendar/__init__.py +++ b/server/src/uds/core/util/calendar/__init__.py @@ -215,6 +215,4 @@ class CalendarChecker: @staticmethod def _gen_cache_key(key: str) -> str: - # Returns a valid cache key for all caching backends (memcached, redis, or whatever) - # Simple, fastest algorihm is to use md5 - return hashlib.md5(key.encode('utf-8')).hexdigest() # nosec simple fast algorithm for cache keys + return hashlib.sha256(key.encode('utf-8'), usedforsecurity=False).hexdigest() diff --git a/server/src/uds/core/util/config.py b/server/src/uds/core/util/config.py index 1fe63a3b3..102e34be2 100644 --- a/server/src/uds/core/util/config.py +++ b/server/src/uds/core/util/config.py @@ -98,25 +98,25 @@ class Config: return Config.SectionType class Section: - _sectionName: 'Config.SectionType' + _section_name: 'Config.SectionType' def __init__(self, sectionName: 'Config.SectionType') -> None: - self._sectionName = sectionName + self._section_name = sectionName def value(self, key, default='', **kwargs) -> 'Config.Value': return Config.value(self, key, default, **kwargs) - def valueCrypt(self, key, default='', **kwargs) -> 'Config.Value': + def value_encrypted(self, key, default='', **kwargs) -> 'Config.Value': return Config.value(self, key, default, True, **kwargs) - def valueLong(self, key, default='', **kwargs) -> 'Config.Value': + def value_longtext(self, key, default='', **kwargs) -> 'Config.Value': return Config.value(self, key, default, False, True, **kwargs) def name(self) -> str: - return self._sectionName + return self._section_name def __str__(self) -> str: - return self._sectionName + return self._section_name class Value: _section: 'Config.Section' # type: ignore # mypy complains?? @@ -195,10 +195,10 @@ class Config: return CryptoManager().decrypt(typing.cast(str, self._data)) return typing.cast(str, self._data) - def setParams(self, params: typing.Any) -> None: + def set_params(self, params: typing.Any) -> None: _configParams[self._section.name() + self._key] = params - def getInt(self, force: bool = False) -> int: + def as_int(self, force: bool = False) -> int: try: return int(self.get(force)) except Exception: @@ -228,19 +228,19 @@ class Config: def section(self) -> str: return self._section.name() - def isCrypted(self) -> bool: + def is_encrypted(self) -> bool: return self._crypt - def isLongText(self) -> bool: + def is_long_text(self) -> bool: return self._longText def get_type(self) -> int: return self._type - def getParams(self) -> typing.Any: + def get_params(self) -> typing.Any: return _configParams.get(self._section.name() + self._key, None) - def getHelp(self) -> str: + def get_help(self) -> str: return gettext(self._help) def set(self, value: typing.Union[str, bool, int]) -> None: @@ -309,7 +309,7 @@ class Config: continue logger.debug('%s.%s:%s,%s', cfg.section, cfg.key, cfg.value, cfg.field_type) if cfg.crypt: - val = Config.section(Config.SectionType.from_str(cfg.section)).valueCrypt(cfg.key) + val = Config.section(Config.SectionType.from_str(cfg.section)).value_encrypted(cfg.key) else: val = Config.section(Config.SectionType.from_str(cfg.section)).value(cfg.key) yield val @@ -342,7 +342,7 @@ class Config: return False @staticmethod - def getConfigValues( + def get_config_values( addCrypt: bool = False, ) -> collections.abc.Mapping[str, collections.abc.Mapping[str, collections.abc.Mapping[str, typing.Any]]]: """ @@ -355,7 +355,7 @@ class Config: if cfg.key() in REMOVED_CONFIG_ELEMENTS.get(cfg.section(), ()): continue - if cfg.isCrypted() is True and addCrypt is False: + if cfg.is_encrypted() is True and addCrypt is False: continue if cfg.get_type() == Config.FieldType.PASSWORD and addCrypt is False: @@ -366,11 +366,11 @@ class Config: res[cfg.section()] = {} res[cfg.section()][cfg.key()] = { 'value': cfg.get(), - 'crypt': cfg.isCrypted(), - 'longText': cfg.isLongText(), + 'crypt': cfg.is_encrypted(), + 'longText': cfg.is_long_text(), 'type': cfg.get_type(), - 'params': cfg.getParams(), - 'help': cfg.getHelp(), + 'params': cfg.get_params(), + 'help': cfg.get_help(), } logger.debug('Configuration: %s', res) return res @@ -448,7 +448,7 @@ class GlobalConfig: 'superUser', 'root', type=Config.FieldType.TEXT, help=_('Superuser username') ) # Superuser password (do not need to be at database!!!) - SUPER_USER_PASS: Config.Value = Config.section(Config.SectionType.SECURITY).valueCrypt( + SUPER_USER_PASS: Config.Value = Config.section(Config.SectionType.SECURITY).value_encrypted( 'rootPass', 'udsmam0', type=Config.FieldType.PASSWORD, help=_('Superuser password') ) SUPER_USER_ALLOW_WEBACCESS: Config.Value = Config.section(Config.SectionType.SECURITY).value( diff --git a/server/src/uds/core/util/decorators.py b/server/src/uds/core/util/decorators.py index aa67bd13e..0c07d28bb 100644 --- a/server/src/uds/core/util/decorators.py +++ b/server/src/uds/core/util/decorators.py @@ -393,7 +393,7 @@ def blocker( result = f(*args, **kwargs) except uds.core.exceptions.rest.BlockAccess: # Increment - blockCache.put(ip, failuresCount + 1, GlobalConfig.LOGIN_BLOCK.getInt()) + blockCache.put(ip, failuresCount + 1, GlobalConfig.LOGIN_BLOCK.as_int()) raise exceptions.rest.AccessDenied # Any other exception will be raised except Exception: diff --git a/server/src/uds/core/util/hash.py b/server/src/uds/core/util/hash.py index 1fefab977..02b30b816 100644 --- a/server/src/uds/core/util/hash.py +++ b/server/src/uds/core/util/hash.py @@ -43,7 +43,7 @@ try: except ImportError: import hashlib - hasher = hashlib.md5 # nosec: just a hashm not for crypto + hasher = hashlib.sha256 # nosec: just a hashm not for crypto diff --git a/server/src/uds/core/util/security.py b/server/src/uds/core/util/security.py index b1caa0bcf..1a25806bf 100644 --- a/server/src/uds/core/util/security.py +++ b/server/src/uds/core/util/security.py @@ -35,7 +35,7 @@ except Exception: # nosec: simple check for disabling warnings, pass -def selfSignedCert(ip: str) -> tuple[str, str, str]: +def create_self_signed_cert(ip: str) -> tuple[str, str, str]: """ Generates a self signed certificate for the given ip. This method is mainly intended to be used for generating/saving Actor certificates. @@ -80,7 +80,7 @@ def selfSignedCert(ip: str) -> tuple[str, str, str]: ) -def createClientSslContext(verify: bool = True) -> ssl.SSLContext: +def create_client_sslcontext(verify: bool = True) -> ssl.SSLContext: """ Creates a SSLContext for client connections. @@ -115,7 +115,7 @@ def createClientSslContext(verify: bool = True) -> ssl.SSLContext: return sslContext -def checkCertificateMatchPrivateKey(*, cert: str, key: str) -> bool: +def check_certificate_matches_private_key(*, cert: str, key: str) -> bool: """ Checks if a certificate and a private key match. All parameters must be keyword arguments. @@ -147,7 +147,7 @@ def checkCertificateMatchPrivateKey(*, cert: str, key: str) -> bool: return False -def secureRequestsSession( +def secure_requests_session( *, verify: typing.Union[str, bool] = True ) -> 'requests.Session': ''' @@ -167,7 +167,7 @@ def secureRequestsSession( class UDSHTTPAdapter(requests.adapters.HTTPAdapter): def init_poolmanager(self, *args, **kwargs) -> None: - kwargs["ssl_context"] = createClientSslContext(verify=verify is True) + kwargs["ssl_context"] = create_client_sslcontext(verify=verify is True) # See urllib3.poolmanager.SSL_KEYWORDS for all available keys. return super().init_poolmanager(*args, **kwargs) @@ -196,7 +196,7 @@ def secureRequestsSession( return session -def checkServerCertificateIsValid(cert: str) -> bool: +def is_server_certificate_valid(cert: str) -> bool: """ Checks if a certificate is valid. All parameters must be keyword arguments. diff --git a/server/src/uds/core/util/storage.py b/server/src/uds/core/util/storage.py index b8c4ff02a..6cdd3a94d 100644 --- a/server/src/uds/core/util/storage.py +++ b/server/src/uds/core/util/storage.py @@ -29,6 +29,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +from email.mime import base import pickle # nosec: This is e controled pickle use import base64 import hashlib @@ -46,20 +47,22 @@ logger = logging.getLogger(__name__) MARK = '_mgb_' -def _calculate_key(owner: bytes, key: bytes, extra: typing.Optional[bytes] = None) -> str: +def _old_calculate_key(owner: bytes, key: bytes) -> str: h = hashlib.md5(usedforsecurity=False) h.update(owner) h.update(key) - if extra: - h.update(extra) return h.hexdigest() -def _encode_value(key: str, value: typing.Any, compat: bool = False) -> str: - if not compat: - return base64.b64encode(pickle.dumps((MARK, key, value))).decode() - # Compatibility save - return base64.b64encode(pickle.dumps(value)).decode() +def _calculate_key(owner: bytes, key: bytes) -> str: + h = hashlib.sha256(usedforsecurity=False) + h.update(owner) + h.update(key) + return h.hexdigest() + + +def _encode_value(key: str, value: typing.Any) -> str: + return base64.b64encode(pickle.dumps((MARK, key, value))).decode() def _decode_value(dbk: str, value: typing.Optional[str]) -> tuple[str, typing.Any]: @@ -83,12 +86,15 @@ class StorageAsDict(MutableMapping): Accesses storage as dictionary. Much more convenient that old method """ + _group: str + _owner: str + _atomic: bool + def __init__( self, owner: str, group: typing.Optional[str], atomic: bool = False, - compat: bool = False, ) -> None: """Initializes an storage as dict accesor @@ -102,7 +108,6 @@ class StorageAsDict(MutableMapping): self._group = group or '' self._owner = owner self._atomic = atomic # Not used right now, maybe removed - self._compat = compat @property def _db(self) -> typing.Union[models.QuerySet, models.Manager]: @@ -117,33 +122,43 @@ class StorageAsDict(MutableMapping): fltr_params['attr1'] = self._group return typing.cast('models.QuerySet[DBStorage]', self._db.filter(**fltr_params)) - def _key(self, key: str) -> str: + def _key(self, key: str, old_method: bool = False) -> str: if key[0] == '#': # Compat with old db key return key[1:] - return _calculate_key(self._owner.encode(), key.encode()) + if not old_method: + return _calculate_key(self._owner.encode(), key.encode()) + return _old_calculate_key(self._owner.encode(), key.encode()) def __getitem__(self, key: str) -> typing.Any: if not isinstance(key, str): raise TypeError(f'Key must be str, {type(key)} found') - dbk = self._key(key) - try: - c: DBStorage = typing.cast(DBStorage, self._db.get(pk=dbk)) - if c.owner != self._owner: # Maybe a key collision, - logger.error('Key collision detected for key %s', key) - return None - okey, value = _decode_value(dbk, c.data) - return _decode_value(dbk, c.data)[1] # Ignores original key - except DBStorage.DoesNotExist: - return None + # First, try new key, and, if needed, old key + # If old key is found, it will be updated to new key + for use_old_method in (False, True): + db_key = self._key(key, old_method=use_old_method) + try: + c: DBStorage = typing.cast(DBStorage, self._db.get(pk=db_key)) + if c.owner != self._owner: # Maybe a key collision, + logger.error('Key collision detected for key %s', key) + return None + okey, value = _decode_value(db_key, c.data) + if use_old_method: + # Update key on db + c.delete() + DBStorage.objects.create(key=self._key(key), owner=self._owner, data=c.data, attr1=c.attr1) + return value + except DBStorage.DoesNotExist: + pass + return None def __setitem__(self, key: str, value: typing.Any) -> None: if not isinstance(key, str): raise TypeError(f'Key must be str type, {type(key)} found') dbk = self._key(key) - data = _encode_value(key, value, self._compat) + data = _encode_value(key, value) # ignores return value, we don't care if it was created or updated DBStorage.objects.update_or_create( key=dbk, defaults={'data': data, 'attr1': self._group, 'owner': self._owner} @@ -195,22 +210,19 @@ class StorageAccess: Allows the access to the storage as a dict, with atomic transaction if requested """ - owner: str - group: typing.Optional[str] - atomic: bool - compat: bool + _owner: str + _group: typing.Optional[str] + _atomic: typing.Optional[transaction.Atomic] def __init__( self, owner: str, group: typing.Optional[str] = None, atomic: bool = False, - compat: bool = False, ): self._owner = owner self._group = group self._atomic = transaction.atomic() if atomic else None - self._compat = compat def __enter__(self): if self._atomic: @@ -219,7 +231,6 @@ class StorageAccess: owner=self._owner, group=self._group, atomic=bool(self._atomic), - compat=self._compat, ) def __exit__(self, exc_type, exc_value, traceback): @@ -235,8 +246,11 @@ class Storage: self._owner = typing.cast(str, owner.decode('utf-8') if isinstance(owner, bytes) else owner) self._bowner = self._owner.encode('utf8') - def get_key(self, key: typing.Union[str, bytes]) -> str: - return _calculate_key(self._bowner, key.encode('utf8') if isinstance(key, str) else key) + def get_key(self, key: typing.Union[str, bytes], old_method: bool = False) -> str: + bkey: bytes = key.encode('utf8') if isinstance(key, str) else key + if not old_method: + return _calculate_key(self._bowner, bkey) + return _old_calculate_key(self._bowner, bkey) def save_to_db( self, @@ -252,14 +266,14 @@ class Storage: key = self.get_key(skey) if isinstance(data, str): data = data.encode('utf-8') - data_string = codecs.encode(data, 'base64').decode() + data_encoded = base64.b64encode(data).decode() attr1 = attr1 or '' try: - DBStorage.objects.create(owner=self._owner, key=key, data=data_string, attr1=attr1) + DBStorage.objects.create(owner=self._owner, key=key, data=data_encoded, attr1=attr1) except Exception: with transaction.atomic(): DBStorage.objects.filter(key=key).select_for_update().update( - owner=self._owner, data=data_string, attr1=attr1 + owner=self._owner, data=data_encoded, attr1=attr1 ) # @UndefinedVariable def put(self, skey: typing.Union[str, bytes], data: typing.Any) -> None: @@ -286,20 +300,29 @@ class Storage: def read_from_db( self, skey: typing.Union[str, bytes], fromPickle: bool = False ) -> typing.Optional[typing.Union[str, bytes]]: - try: - key = self.get_key(skey) - c: DBStorage = DBStorage.objects.get(pk=key) # @UndefinedVariable - val = codecs.decode(c.data.encode(), 'base64') - - if fromPickle: - return val - + for use_old_method in (False, True): try: - return val.decode('utf-8') # Tries to encode in utf-8 - except Exception: - return val - except DBStorage.DoesNotExist: # @UndefinedVariable - return None + key = self.get_key(skey, old_method=use_old_method) + c: DBStorage = DBStorage.objects.get(pk=key) # @UndefinedVariable + val = base64.b64decode(c.data.encode()) + # if old key is used, update it to new key + if use_old_method: + # Remove and re-create with new key + c.delete() + DBStorage.objects.create( + key=self.get_key(skey), owner=self._owner, data=c.data, attr1=c.attr1 + ) + + if fromPickle: + return val + + try: + return val.decode('utf-8') # Tries to encode in utf-8 + except Exception: + return val + except DBStorage.DoesNotExist: + pass + return None def get(self, skey: typing.Union[str, bytes]) -> typing.Optional[typing.Union[str, bytes]]: return self.read_from_db(skey) @@ -348,13 +371,12 @@ class Storage: """ # dbStorage.objects.unlock() # @UndefinedVariable - def map( + def as_dict( self, group: typing.Optional[str] = None, atomic: bool = False, - compat: bool = False, ) -> StorageAccess: - return StorageAccess(self._owner, group=group, atomic=atomic, compat=compat) + return StorageAccess(self._owner, group=group, atomic=atomic) def search_by_attr1( self, attr1: typing.Union[collections.abc.Iterable[str], str] @@ -368,14 +390,14 @@ class Storage: yield codecs.decode(v.data.encode(), 'base64') def filter( - self, attr1: typing.Optional[str] = None, forUpdate: bool = False + self, attr1: typing.Optional[str] = None, for_update: bool = False ) -> collections.abc.Iterable[tuple[str, bytes, 'str|None']]: if attr1 is None: query = DBStorage.objects.filter(owner=self._owner) # @UndefinedVariable else: query = DBStorage.objects.filter(owner=self._owner, attr1=attr1) # @UndefinedVariable - if forUpdate: + if for_update: query = query.select_for_update() for v in query: # @UndefinedVariable diff --git a/server/src/uds/core/util/validators.py b/server/src/uds/core/util/validators.py index 7c25821ba..d177a1c78 100644 --- a/server/src/uds/core/util/validators.py +++ b/server/src/uds/core/util/validators.py @@ -49,7 +49,7 @@ logger = logging.getLogger(__name__) url_validator = dj_validators.URLValidator(['http', 'https']) -def validateNumeric( +def validate_numeric( value: typing.Union[str, int], min_value: typing.Optional[int] = None, max_value: typing.Optional[int] = None, @@ -87,7 +87,7 @@ def validateNumeric( return int(value) -def validateHostname(hostname: str, maxLength: int = 64, allowDomain=False) -> str: +def validate_hostname(hostname: str, maxLength: int = 64, allowDomain=False) -> str: if len(hostname) > maxLength: raise exceptions.validation.ValidationError( _('{} is not a valid hostname: maximum host name length exceeded.').format(hostname) @@ -107,8 +107,8 @@ def validateHostname(hostname: str, maxLength: int = 64, allowDomain=False) -> s return hostname -def validateFqdn(fqdn: str, maxLength: int = 255) -> str: - return validateHostname(fqdn, maxLength, allowDomain=True) +def validate_fqdn(fqdn: str, maxLength: int = 255) -> str: + return validate_hostname(fqdn, maxLength, allowDomain=True) def validateUrl(url: str, maxLength: int = 1024) -> str: @@ -123,7 +123,7 @@ def validateUrl(url: str, maxLength: int = 1024) -> str: return url -def validateIpv4(ipv4: str) -> str: +def validate_ipv4(ipv4: str) -> str: """ Validates that a ipv4 address is valid :param ipv4: ipv4 address to validate @@ -137,7 +137,7 @@ def validateIpv4(ipv4: str) -> str: return ipv4 -def validateIpv6(ipv6: str) -> str: +def validate_ipv6(ipv6: str) -> str: """ Validates that a ipv6 address is valid :param ipv6: ipv6 address to validate @@ -151,7 +151,7 @@ def validateIpv6(ipv6: str) -> str: return ipv6 -def validateIpv4OrIpv6(ipv4OrIpv6: str) -> str: +def validate_ip(ipv4_or_ipv6: str) -> str: """ Validates that a ipv4 or ipv6 address is valid :param ipv4OrIpv6: ipv4 or ipv6 address to validate @@ -159,15 +159,15 @@ def validateIpv4OrIpv6(ipv4OrIpv6: str) -> str: :return: Raises exceptions.Validation exception if is invalid, else return the value "fixed" """ try: - dj_validators.validate_ipv46_address(ipv4OrIpv6) + dj_validators.validate_ipv46_address(ipv4_or_ipv6) except Exception: raise exceptions.validation.ValidationError( - _('{} is not a valid IPv4 or IPv6 address').format(ipv4OrIpv6) + _('{} is not a valid IPv4 or IPv6 address').format(ipv4_or_ipv6) ) from None - return ipv4OrIpv6 + return ipv4_or_ipv6 -def validatePath( +def validate_path( path: str, maxLength: int = 1024, mustBeWindows: bool = False, @@ -207,7 +207,7 @@ def validatePath( return path -def validatePort(port: typing.Union[str, int]) -> int: +def validate_port(port: typing.Union[str, int]) -> int: """ Validates that a port number is valid @@ -220,10 +220,10 @@ def validatePort(port: typing.Union[str, int]) -> int: Raises: exceptions.ValidationException: if port is not valid """ - return validateNumeric(port, min_value=1, max_value=65535, fieldName='Port') + return validate_numeric(port, min_value=1, max_value=65535, fieldName='Port') -def validateHost(host: str) -> str: +def validate_host(host: str) -> str: """ Validates that a host is valid :param host: host to validate @@ -233,10 +233,10 @@ def validateHost(host: str) -> str: dj_validators.validate_ipv46_address(host) return host except Exception: - return validateFqdn(host) + return validate_fqdn(host) -def validateHostPortPair(hostPortPair: str) -> tuple[str, int]: +def validate_host_port(host_port_pair: str) -> tuple[str, int]: """ Validates that a host:port pair is valid :param hostPortPair: host:port pair to validate @@ -244,32 +244,32 @@ def validateHostPortPair(hostPortPair: str) -> tuple[str, int]: :return: Raises exceptions.Validation exception if is invalid, else return the value "fixed" """ try: - if '[' in hostPortPair and ']' in hostPortPair: # IPv6 - host, port = hostPortPair.split(']:') + if '[' in host_port_pair and ']' in host_port_pair: # IPv6 + host, port = host_port_pair.split(']:') host = host[1:] else: - host, port = hostPortPair.split(':') + host, port = host_port_pair.split(':') # if an ip address is used, it must be valid try: dj_validators.validate_ipv46_address(host) - return host, validatePort(port) + return host, validate_port(port) except Exception: - return validateHostname(host, 255, False), validatePort(port) + return validate_hostname(host, 255, False), validate_port(port) except Exception: - raise exceptions.validation.ValidationError(_('{} is not a valid host:port pair').format(hostPortPair)) from None + raise exceptions.validation.ValidationError(_('{} is not a valid host:port pair').format(host_port_pair)) from None -def validateTimeout(timeOutStr: str) -> int: +def validate_timeout(timeOutStr: str) -> int: """ Validates that a timeout value is valid :param timeOutStr: timeout to validate :param returnAsInteger: if True, returns value as integer, if not, as string :return: Raises exceptions.Validation exception if is invalid, else return the value "fixed" """ - return validateNumeric(timeOutStr, min_value=0, fieldName='Timeout') + return validate_numeric(timeOutStr, min_value=0, fieldName='Timeout') -def validateMac(mac: str) -> str: +def validate_mac(mac: str) -> str: """ Validates that a mac address is valid :param mac: mac address to validate @@ -288,7 +288,7 @@ def validateMac(mac: str) -> str: return mac -def validateMacRange(macRange: str) -> str: +def validate_mac_range(macRange: str) -> str: """ Corrects mac range (uppercase, without spaces), and checks that is range is valid :param macRange: Range to fix @@ -296,15 +296,15 @@ def validateMacRange(macRange: str) -> str: """ try: macRangeStart, macRangeEnd = macRange.split('-') - validateMac(macRangeStart) - validateMac(macRangeEnd) + validate_mac(macRangeStart) + validate_mac(macRangeEnd) except Exception: raise exceptions.validation.ValidationError(_('{} is not a valid MAC range').format(macRange)) from None return macRange -def validateEmail(email: str) -> str: +def validate_email(email: str) -> str: """ Validates that an email is valid :param email: email to validate @@ -319,7 +319,7 @@ def validateEmail(email: str) -> str: return email -def validateBasename(baseName: str, length: int = -1) -> str: +def validate_basename(baseName: str, length: int = -1) -> str: """ "Checks if the basename + length is valid for services. Raises an exception if not valid" Arguments: @@ -348,7 +348,7 @@ def validateBasename(baseName: str, length: int = -1) -> str: return baseName -def validateJson(jsonData: typing.Optional[str]) -> typing.Any: +def validate_json(jsonData: typing.Optional[str]) -> typing.Any: """ Validates that a json data is valid (or empty) @@ -369,7 +369,7 @@ def validateJson(jsonData: typing.Optional[str]) -> typing.Any: raise exceptions.validation.ValidationError(_('Invalid JSON data')) from None -def validateServerCertificate(cert: typing.Optional[str]) -> str: +def validate_server_certificate(cert: typing.Optional[str]) -> str: """ Validates that a certificate is valid @@ -385,13 +385,13 @@ def validateServerCertificate(cert: typing.Optional[str]) -> str: if not cert: return '' try: - security.checkServerCertificateIsValid(cert) + security.is_server_certificate_valid(cert) except Exception as e: raise exceptions.validation.ValidationError(_('Invalid certificate') + f' :{e}') from e return cert -def validateServerCertificateMulti(value: typing.Optional[str]) -> str: +def validate_server_certificate_multiple(value: typing.Optional[str]) -> str: """ Validates the multi line fields refering to attributes """ diff --git a/server/src/uds/core/workers/assigned_unused.py b/server/src/uds/core/workers/assigned_unused.py index d138a0d80..ef3f36d74 100644 --- a/server/src/uds/core/workers/assigned_unused.py +++ b/server/src/uds/core/workers/assigned_unused.py @@ -50,7 +50,7 @@ class AssignedAndUnused(Job): def run(self) -> None: since_state = sql_datetime() - timedelta( - seconds=GlobalConfig.CHECK_UNUSED_TIME.getInt() + seconds=GlobalConfig.CHECK_UNUSED_TIME.as_int() ) # Locate service pools with pending assigned service in use outdatedServicePools = ServicePool.objects.annotate( diff --git a/server/src/uds/core/workers/hanged_userservice_cleaner.py b/server/src/uds/core/workers/hanged_userservice_cleaner.py index 172b77f37..9a79aa697 100644 --- a/server/src/uds/core/workers/hanged_userservice_cleaner.py +++ b/server/src/uds/core/workers/hanged_userservice_cleaner.py @@ -51,9 +51,9 @@ class HangedCleaner(Job): def run(self) -> None: now = sql_datetime() since_state = now - timedelta( - seconds=GlobalConfig.MAX_INITIALIZING_TIME.getInt() + seconds=GlobalConfig.MAX_INITIALIZING_TIME.as_int() ) - since_removing = now - timedelta(seconds=GlobalConfig.MAX_REMOVAL_TIME.getInt()) + since_removing = now - timedelta(seconds=GlobalConfig.MAX_REMOVAL_TIME.as_int()) # Filter for locating machine not ready flt = Q(state_date__lt=since_state, state=State.PREPARING) | Q( state_date__lt=since_state, state=State.USABLE, os_state=State.PREPARING diff --git a/server/src/uds/core/workers/publication_cleaner.py b/server/src/uds/core/workers/publication_cleaner.py index 05d935765..bee6f42b1 100644 --- a/server/src/uds/core/workers/publication_cleaner.py +++ b/server/src/uds/core/workers/publication_cleaner.py @@ -54,7 +54,7 @@ class PublicationInfoItemsCleaner(Job): def run(self) -> None: removeFrom = sql_datetime() - timedelta( - seconds=GlobalConfig.KEEP_INFO_TIME.getInt(True) + seconds=GlobalConfig.KEEP_INFO_TIME.as_int(True) ) ServicePoolPublication.objects.filter( state__in=State.INFO_STATES, state_date__lt=removeFrom diff --git a/server/src/uds/core/workers/service_pool_cleaner.py b/server/src/uds/core/workers/service_pool_cleaner.py index 513be7f75..933d831be 100644 --- a/server/src/uds/core/workers/service_pool_cleaner.py +++ b/server/src/uds/core/workers/service_pool_cleaner.py @@ -55,7 +55,7 @@ class DeployedServiceInfoItemsCleaner(Job): def run(self) -> None: removeFrom = sql_datetime() - timedelta( - seconds=GlobalConfig.KEEP_INFO_TIME.getInt() + seconds=GlobalConfig.KEEP_INFO_TIME.as_int() ) ServicePool.objects.filter( state__in=State.INFO_STATES, state_date__lt=removeFrom diff --git a/server/src/uds/core/workers/stats_collector.py b/server/src/uds/core/workers/stats_collector.py index 6f6440e43..b0b2f07cb 100644 --- a/server/src/uds/core/workers/stats_collector.py +++ b/server/src/uds/core/workers/stats_collector.py @@ -171,7 +171,7 @@ class StatsAccumulator(Job): def run(self): try: - StatsManager.manager().acummulate(config.GlobalConfig.STATS_ACCUM_MAX_CHUNK_TIME.getInt()) + StatsManager.manager().acummulate(config.GlobalConfig.STATS_ACCUM_MAX_CHUNK_TIME.as_int()) except Exception: logger.exception('Compressing counters') diff --git a/server/src/uds/core/workers/userservice_cleaner.py b/server/src/uds/core/workers/userservice_cleaner.py index b28c0da97..d752ff910 100644 --- a/server/src/uds/core/workers/userservice_cleaner.py +++ b/server/src/uds/core/workers/userservice_cleaner.py @@ -59,7 +59,7 @@ class UserServiceInfoItemsCleaner(Job): def run(self) -> None: removeFrom = sql_datetime() - timedelta( - seconds=GlobalConfig.KEEP_INFO_TIME.getInt(True) + seconds=GlobalConfig.KEEP_INFO_TIME.as_int(True) ) logger.debug('Removing information user services from %s', removeFrom) with transaction.atomic(): @@ -78,7 +78,7 @@ class UserServiceRemover(Job): def run(self) -> None: # USER_SERVICE_REMOVAL_LIMIT is the maximum number of items to remove at once # This configuration value is cached at startup, so it is not updated until next reload - removeAtOnce: int = GlobalConfig.USER_SERVICE_CLEAN_NUMBER.getInt() + removeAtOnce: int = GlobalConfig.USER_SERVICE_CLEAN_NUMBER.as_int() manager = UserServiceManager() with transaction.atomic(): diff --git a/server/src/uds/management/commands/config.py b/server/src/uds/management/commands/config.py index 3464722fd..80646fcc0 100644 --- a/server/src/uds/management/commands/config.py +++ b/server/src/uds/management/commands/config.py @@ -70,7 +70,7 @@ class Command(BaseCommand): if options['password']: kwargs['type'] = Config.FieldType.PASSWORD if options['force_crypt']: - value = Config.section(mod).valueCrypt(name, value).get() + value = Config.section(mod).value_encrypted(name, value).get() else: Config.section(mod).value(name, value).get() except Exception as e: diff --git a/server/src/uds/management/commands/showconfig.py b/server/src/uds/management/commands/showconfig.py index df37747ea..911f2a522 100644 --- a/server/src/uds/management/commands/showconfig.py +++ b/server/src/uds/management/commands/showconfig.py @@ -73,7 +73,7 @@ class Command(BaseCommand): elif options['yaml']: writer = {} # Create a dict to store data, and write at the end # Get sections, key, value as a list of tuples - for section, data in config.Config.getConfigValues().items(): + for section, data in config.Config.get_config_values().items(): for key, value in data.items(): # value is a dict, get 'value' key if options['csv']: diff --git a/server/src/uds/mfas/Email/mfa.py b/server/src/uds/mfas/Email/mfa.py index 1123918d5..a81c8c4a3 100644 --- a/server/src/uds/mfas/Email/mfa.py +++ b/server/src/uds/mfas/Email/mfa.py @@ -193,14 +193,14 @@ class EmailMFA(mfas.MFA): # Now check is valid format if ':' in hostname: - host, port = validators.validateHostPortPair(hostname) + host, port = validators.validate_host_port(hostname) self.hostname.value = f'{host}:{port}' else: host = self.hostname.as_clean_str() - self.hostname.value = validators.validateFqdn(host) + self.hostname.value = validators.validate_fqdn(host) # now check from email and to email - self.fromEmail.value = validators.validateEmail(self.fromEmail.value) + self.fromEmail.value = validators.validate_email(self.fromEmail.value) def html(self, request: 'ExtendedHttpRequest', userId: str, username: str) -> str: return gettext('Check your mail. You will receive an email with the verification code') diff --git a/server/src/uds/mfas/SMS/mfa.py b/server/src/uds/mfas/SMS/mfa.py index 2af705ffd..47d718945 100644 --- a/server/src/uds/mfas/SMS/mfa.py +++ b/server/src/uds/mfas/SMS/mfa.py @@ -282,7 +282,7 @@ class SMSMFA(mfas.MFA): return url def getSession(self) -> requests.Session: - session = security.secureRequestsSession(verify=self.ignoreCertificateErrors.as_bool()) + session = security.secure_requests_session(verify=self.ignoreCertificateErrors.as_bool()) # 0 means no authentication if self.authenticationMethod.value == '1': session.auth = requests.auth.HTTPBasicAuth( diff --git a/server/src/uds/middleware/request.py b/server/src/uds/middleware/request.py index fe78df561..bb92508a7 100644 --- a/server/src/uds/middleware/request.py +++ b/server/src/uds/middleware/request.py @@ -159,9 +159,9 @@ def _process_request(request: 'ExtendedHttpRequest') -> typing.Optional['HttpRes request.session[EXPIRY_KEY] = ( now + datetime.timedelta( - seconds=GlobalConfig.SESSION_DURATION_ADMIN.getInt() + seconds=GlobalConfig.SESSION_DURATION_ADMIN.as_int() if request.user.is_staff() - else GlobalConfig.SESSION_DURATION_USER.getInt() + else GlobalConfig.SESSION_DURATION_USER.as_int() ) ).isoformat() # store as ISO format, str, json serilizable diff --git a/server/src/uds/migrations/fixers/properties_v4.py b/server/src/uds/migrations/fixers/properties_v4.py index d336443ad..358974554 100644 --- a/server/src/uds/migrations/fixers/properties_v4.py +++ b/server/src/uds/migrations/fixers/properties_v4.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) def migrate(apps: typing.Any, schema_editor: typing.Any) -> None: """ - Migrates an old tunnel transport to a new one (with tunnelServer) + Migrates old properties to new ones """ try: UserServiceProperty = apps.get_model('uds', 'UserServiceProperty') @@ -28,7 +28,7 @@ def migrate(apps: typing.Any, schema_editor: typing.Any) -> None: def rollback(apps: typing.Any, schema_editor: typing.Any) -> None: """ - Migrates an old tunnel transport to a new one (with tunnelServer) + rollback migration """ try: UserServiceProperty = apps.get_model('uds', 'UserServiceProperty') diff --git a/server/src/uds/migrations/fixers/providers_v4/_migrator.py b/server/src/uds/migrations/fixers/providers_v4/_migrator.py index 798909f86..6936b9889 100644 --- a/server/src/uds/migrations/fixers/providers_v4/_migrator.py +++ b/server/src/uds/migrations/fixers/providers_v4/_migrator.py @@ -72,7 +72,7 @@ def migrate( server_ip_hostname: list[tuple[str, str]] = [] for server in servers: try: - validators.validateIpv4OrIpv6(server) + validators.validate_ip(server) # Is Pure IP, try to get hostname try: answers = dns.resolver.resolve(dns.reversename.from_address(server), 'PTR') diff --git a/server/src/uds/models/authenticator.py b/server/src/uds/models/authenticator.py index 281d51f3c..3217d5d25 100644 --- a/server/src/uds/models/authenticator.py +++ b/server/src/uds/models/authenticator.py @@ -292,7 +292,7 @@ class Authenticator(ManagedObjectModel, TaggingMixin): if toDelete.data != '': s = toDelete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() # Clears related logs log.clear_logs(toDelete) diff --git a/server/src/uds/models/managed_object_model.py b/server/src/uds/models/managed_object_model.py index f7893434d..7eaee1440 100644 --- a/server/src/uds/models/managed_object_model.py +++ b/server/src/uds/models/managed_object_model.py @@ -69,7 +69,7 @@ class ManagedObjectModel(UUIDModel): """ Returns an environment valid for the record this object represents """ - return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore # pylint: disable=no-member + return Environment.get_environment_for_table(self._meta.verbose_name, self.id) # type: ignore # pylint: disable=no-member def deserialize( self, obj: Module, values: typing.Optional[collections.abc.Mapping[str, str]] diff --git a/server/src/uds/models/mfa.py b/server/src/uds/models/mfa.py index 4390e06e7..0ed194e30 100644 --- a/server/src/uds/models/mfa.py +++ b/server/src/uds/models/mfa.py @@ -101,7 +101,7 @@ class MFA(ManagedObjectModel, TaggingMixin): # type: ignore try: s = to_delete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() except Exception as e: logger.error( 'Error processing deletion of notifier %s: %s (forced deletion)', diff --git a/server/src/uds/models/notifications.py b/server/src/uds/models/notifications.py index a80944331..91c6fb050 100644 --- a/server/src/uds/models/notifications.py +++ b/server/src/uds/models/notifications.py @@ -140,7 +140,7 @@ class Notifier(ManagedObjectModel, TaggingMixin): try: s = to_delete.get_instance() s.destroy() # Invokes the destruction of "related own data" - s.env.clearRelatedData() # Clears related data, such as storage, cache, etc... + s.env.clean_related_data() # Clears related data, such as storage, cache, etc... except Exception as e: logger.error( 'Error processing deletion of notifier %s: %s (forced deletion)', diff --git a/server/src/uds/models/os_manager.py b/server/src/uds/models/os_manager.py index 28bb30c28..dfd9fb4c6 100644 --- a/server/src/uds/models/os_manager.py +++ b/server/src/uds/models/os_manager.py @@ -116,7 +116,7 @@ class OSManager(ManagedObjectModel, TaggingMixin): if to_delete.data != '': s = to_delete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() logger.debug('Before delete os manager %s', to_delete) diff --git a/server/src/uds/models/provider.py b/server/src/uds/models/provider.py index 23645bb92..b58688c67 100644 --- a/server/src/uds/models/provider.py +++ b/server/src/uds/models/provider.py @@ -121,7 +121,7 @@ class Provider(ManagedObjectModel, TaggingMixin): # type: ignore if to_delete.data != '': s = to_delete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() # Clears related logs log.clear_logs(to_delete) diff --git a/server/src/uds/models/scheduler.py b/server/src/uds/models/scheduler.py index f7f493343..2ecdff537 100644 --- a/server/src/uds/models/scheduler.py +++ b/server/src/uds/models/scheduler.py @@ -87,7 +87,7 @@ class Scheduler(models.Model): """ Returns an environment valid for the record this object represents """ - return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore # pylint: disable=no-member + return Environment.get_environment_for_table(self._meta.verbose_name, self.id) # type: ignore # pylint: disable=no-member def get_instance(self) -> typing.Optional[jobs.Job]: """ @@ -108,7 +108,7 @@ class Scheduler(models.Model): """ toDelete: 'Scheduler' = kwargs['instance'] logger.debug('Deleting sheduled task %s', toDelete) - toDelete.get_environment().clearRelatedData() + toDelete.get_environment().clean_related_data() def __str__(self) -> str: return f'Scheduled task {self.name}, every {self.frecuency}, last execution at {self.last_execution}, state = {self.state}' diff --git a/server/src/uds/models/service.py b/server/src/uds/models/service.py index d782a9c5e..b9acc6b9a 100644 --- a/server/src/uds/models/service.py +++ b/server/src/uds/models/service.py @@ -103,7 +103,7 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore """ Returns an environment valid for the record this object represents """ - return Environment.getEnvForTableElement( + return Environment.get_environment_for_table( self._meta.verbose_name, # type: ignore self.id, { @@ -220,7 +220,7 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore if to_delete.data != '': s = to_delete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() # Clears related logs log.clear_logs(to_delete) diff --git a/server/src/uds/models/service_pool.py b/server/src/uds/models/service_pool.py index cca18f928..5a0db2279 100644 --- a/server/src/uds/models/service_pool.py +++ b/server/src/uds/models/service_pool.py @@ -165,7 +165,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore """ Returns an environment valid for the record this object represents """ - return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore + return Environment.get_environment_for_table(self._meta.verbose_name, self.id) # type: ignore def active_publication(self) -> typing.Optional['ServicePoolPublication']: """ @@ -207,13 +207,13 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore from uds.models.user_service import \ UserService # pylint: disable=import-outside-toplevel - if GlobalConfig.RESTRAINT_TIME.getInt() <= 0: + if GlobalConfig.RESTRAINT_TIME.as_int() <= 0: return ( ServicePool.objects.none() ) # Do not perform any restraint check if we set the globalconfig to 0 (or less) - date = sql_datetime() - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt()) - min_ = GlobalConfig.RESTRAINT_COUNT.getInt() + date = sql_datetime() - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int()) + min_ = GlobalConfig.RESTRAINT_COUNT.as_int() res = [] for v in ( @@ -258,13 +258,13 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore from uds.core.util.config import \ GlobalConfig # pylint: disable=import-outside-toplevel - if GlobalConfig.RESTRAINT_TIME.getInt() <= 0: + if GlobalConfig.RESTRAINT_TIME.as_int() <= 0: return False # Do not perform any restraint check if we set the globalconfig to 0 (or less) - date = typing.cast(datetime, sql_datetime()) - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.getInt()) + date = typing.cast(datetime, sql_datetime()) - timedelta(seconds=GlobalConfig.RESTRAINT_TIME.as_int()) if ( self.userServices.filter(state=types.states.State.ERROR, state_date__gt=date).count() - >= GlobalConfig.RESTRAINT_COUNT.getInt() + >= GlobalConfig.RESTRAINT_COUNT.as_int() ): return True @@ -677,7 +677,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore toDelete: 'ServicePool' = kwargs['instance'] logger.debug('Deleting Service Pool %s', toDelete) - toDelete.get_environment().clearRelatedData() + toDelete.get_environment().clean_related_data() # Clears related logs log.clear_logs(toDelete) diff --git a/server/src/uds/models/service_pool_publication.py b/server/src/uds/models/service_pool_publication.py index 74272a822..dbb726812 100644 --- a/server/src/uds/models/service_pool_publication.py +++ b/server/src/uds/models/service_pool_publication.py @@ -113,7 +113,7 @@ class ServicePoolPublication(UUIDModel): """ Returns an environment valid for the record this object represents """ - return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore + return Environment.get_environment_for_table(self._meta.verbose_name, self.id) # type: ignore def get_instance(self) -> 'services.Publication': """ @@ -210,7 +210,7 @@ class ServicePoolPublication(UUIDModel): :note: If destroy raises an exception, the deletion is not taken. """ to_delete: ServicePoolPublication = kwargs['instance'] - to_delete.get_environment().clearRelatedData() + to_delete.get_environment().clean_related_data() # Delete method is invoked directly by PublicationManager, # Destroying a publication is not obligatory an 1 step action. diff --git a/server/src/uds/models/transport.py b/server/src/uds/models/transport.py index 62d5cd63d..8f06c6a1b 100644 --- a/server/src/uds/models/transport.py +++ b/server/src/uds/models/transport.py @@ -170,7 +170,7 @@ class Transport(ManagedObjectModel, TaggingMixin): if toDelete.data != '': s = toDelete.get_instance() s.destroy() - s.env.clearRelatedData() + s.env.clean_related_data() # Clears related permissions clean(toDelete) diff --git a/server/src/uds/models/user_service.py b/server/src/uds/models/user_service.py index e7aa37e53..275cc53fb 100644 --- a/server/src/uds/models/user_service.py +++ b/server/src/uds/models/user_service.py @@ -174,7 +174,7 @@ class UserService(UUIDModel, properties.PropertiesMixin): (see related classes uds.core.util.unique_name_generator and uds.core.util.unique_mac_generator) """ - return Environment.getEnvForTableElement( + return Environment.get_environment_for_table( self._meta.verbose_name, # type: ignore # pylint: disable=no-member self.id, { @@ -636,7 +636,7 @@ class UserService(UUIDModel, properties.PropertiesMixin): """ to_delete: 'UserService' = kwargs['instance'] # Clear environment - to_delete.get_environment().clearRelatedData() + to_delete.get_environment().clean_related_data() # Ensure all sessions are closed (invoke with '' to close all sessions) # In fact, sessions are going to be deleted also, but we give then # the oportunity to execute some code before deleting them diff --git a/server/src/uds/notifiers/email/notifier.py b/server/src/uds/notifiers/email/notifier.py index 7c1353615..b6c400179 100644 --- a/server/src/uds/notifiers/email/notifier.py +++ b/server/src/uds/notifiers/email/notifier.py @@ -153,15 +153,15 @@ class EmailNotifier(messaging.Notifier): # Now check is valid format if ':' in hostname: - host, port = validators.validateHostPortPair(hostname) + host, port = validators.validate_host_port(hostname) self.hostname.value = f'{host}:{port}' else: host = self.hostname.as_clean_str() - self.hostname.value = validators.validateFqdn(host) + self.hostname.value = validators.validate_fqdn(host) # now check from email and to email - self.fromEmail.value = validators.validateEmail(self.fromEmail.value) - self.toEmail.value = validators.validateEmail(self.toEmail.value) + self.fromEmail.value = validators.validate_email(self.fromEmail.value) + self.toEmail.value = validators.validate_email(self.toEmail.value) # Done diff --git a/server/src/uds/services/OVirt/provider.py b/server/src/uds/services/OVirt/provider.py index 91c3795e3..00bdc4ef2 100644 --- a/server/src/uds/services/OVirt/provider.py +++ b/server/src/uds/services/OVirt/provider.py @@ -209,8 +209,8 @@ class OVirtProvider( self._api = None if values is not None: - self.macsRange.value = validators.validateMacRange(self.macsRange.value) - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.macsRange.value = validators.validate_mac_range(self.macsRange.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) logger.debug(self.host.value) def testConnection(self) -> bool: diff --git a/server/src/uds/services/OVirt/service.py b/server/src/uds/services/OVirt/service.py index 92c003dc2..9a8c59498 100644 --- a/server/src/uds/services/OVirt/service.py +++ b/server/src/uds/services/OVirt/service.py @@ -223,7 +223,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m initialized by __init__ method of base class, before invoking this. """ if values: - validators.validateBasename(self.baseName.value, self.lenName.num()) + validators.validate_basename(self.baseName.value, self.lenName.num()) if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256: raise exceptions.validation.ValidationError( _('The minimum allowed memory is 256 Mb') diff --git a/server/src/uds/services/OpenGnsys/og/__init__.py b/server/src/uds/services/OpenGnsys/og/__init__.py index cfe3de1c6..81cfd4f9f 100644 --- a/server/src/uds/services/OpenGnsys/og/__init__.py +++ b/server/src/uds/services/OpenGnsys/og/__init__.py @@ -135,7 +135,7 @@ class OpenGnsysClient: ) -> typing.Any: if not FAKE: return ensureResponseIsValid( - security.secureRequestsSession(verify=self.verifyCert).post( + security.secure_requests_session(verify=self.verifyCert).post( self._ogUrl(path), data=json.dumps(data), headers=self.headers, @@ -149,7 +149,7 @@ class OpenGnsysClient: def _get(self, path: str, errMsg: typing.Optional[str] = None) -> typing.Any: if not FAKE: return ensureResponseIsValid( - security.secureRequestsSession(verify=self.verifyCert).get( + security.secure_requests_session(verify=self.verifyCert).get( self._ogUrl(path), headers=self.headers, verify=self.verifyCert, timeout=TIMEOUT, ), @@ -161,7 +161,7 @@ class OpenGnsysClient: def _delete(self, path: str, errMsg: typing.Optional[str] = None) -> typing.Any: if not FAKE: return ensureResponseIsValid( - security.secureRequestsSession(verify=self.verifyCert).delete( + security.secure_requests_session(verify=self.verifyCert).delete( self._ogUrl(path), headers=self.headers, timeout=TIMEOUT, diff --git a/server/src/uds/services/OpenGnsys/provider.py b/server/src/uds/services/OpenGnsys/provider.py index f761fb664..171a9261d 100644 --- a/server/src/uds/services/OpenGnsys/provider.py +++ b/server/src/uds/services/OpenGnsys/provider.py @@ -186,7 +186,7 @@ class OGProvider(ServiceProvider): self._api = None if values: - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) logger.debug('Endpoint: %s', self.endpoint) try: diff --git a/server/src/uds/services/OpenNebula/provider.py b/server/src/uds/services/OpenNebula/provider.py index 64aa38b2c..4c9cd60a6 100644 --- a/server/src/uds/services/OpenNebula/provider.py +++ b/server/src/uds/services/OpenNebula/provider.py @@ -158,7 +158,7 @@ class OpenNebulaProvider(ServiceProvider): # pylint: disable=too-many-public-me self._api = None if values: - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) logger.debug('Endpoint: %s', self.endpoint) @property diff --git a/server/src/uds/services/OpenNebula/service.py b/server/src/uds/services/OpenNebula/service.py index 06f50b5a6..2eb93cfcc 100644 --- a/server/src/uds/services/OpenNebula/service.py +++ b/server/src/uds/services/OpenNebula/service.py @@ -149,7 +149,7 @@ class LiveService(services.Service): if not values: return - self.baseName.value = validators.validateBasename( + self.baseName.value = validators.validate_basename( self.baseName.value, length=self.lenName.num() ) diff --git a/server/src/uds/services/OpenStack/openstack/openstack_client.py b/server/src/uds/services/OpenStack/openstack/openstack_client.py index b73b2aa02..b1733bee1 100644 --- a/server/src/uds/services/OpenStack/openstack/openstack_client.py +++ b/server/src/uds/services/OpenStack/openstack/openstack_client.py @@ -172,7 +172,7 @@ class Client: # pylint: disable=too-many-public-methods access: typing.Optional[str] = None, proxies: typing.Optional[collections.abc.MutableMapping[str, str]] = None, ): - self._session = security.secureRequestsSession(verify=VERIFY_SSL) + self._session = security.secure_requests_session(verify=VERIFY_SSL) if proxies: self._session.proxies = proxies diff --git a/server/src/uds/services/OpenStack/provider.py b/server/src/uds/services/OpenStack/provider.py index 99c1de105..f5339388d 100644 --- a/server/src/uds/services/OpenStack/provider.py +++ b/server/src/uds/services/OpenStack/provider.py @@ -232,7 +232,7 @@ class OpenStackProvider(ServiceProvider): self._api = None if values is not None: - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) def api(self, projectId=None, region=None) -> openstack.Client: projectId = projectId or self.tenant.value or None diff --git a/server/src/uds/services/OpenStack/provider_legacy.py b/server/src/uds/services/OpenStack/provider_legacy.py index cfd2a3b50..2248f811f 100644 --- a/server/src/uds/services/OpenStack/provider_legacy.py +++ b/server/src/uds/services/OpenStack/provider_legacy.py @@ -220,7 +220,7 @@ class ProviderLegacy(ServiceProvider): # Just reset _api connection variable if values is not None: - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) def api(self, projectId=None, region=None) -> openstack.Client: proxies = None diff --git a/server/src/uds/services/OpenStack/service.py b/server/src/uds/services/OpenStack/service.py index b5443038d..b07bcd696 100644 --- a/server/src/uds/services/OpenStack/service.py +++ b/server/src/uds/services/OpenStack/service.py @@ -210,7 +210,7 @@ class LiveService(services.Service): initialized by __init__ method of base class, before invoking this. """ if values: - validators.validateBasename(self.baseName.value, self.lenName.num()) + validators.validate_basename(self.baseName.value, self.lenName.num()) # self.ov.value = self.parent().serialize() # self.ev.value = self.parent().env.key diff --git a/server/src/uds/services/PhysicalMachines/service_base.py b/server/src/uds/services/PhysicalMachines/service_base.py index dbe7b2e4d..0b8076876 100644 --- a/server/src/uds/services/PhysicalMachines/service_base.py +++ b/server/src/uds/services/PhysicalMachines/service_base.py @@ -79,7 +79,7 @@ class IPServiceBase(services.Service): if wolurl: logger.info('Launching WOL: %s', wolurl) try: - security.secureRequestsSession(verify=verify_ssl).get(wolurl) + security.secure_requests_session(verify=verify_ssl).get(wolurl) # logger.debug('Result: %s', result) except Exception as e: logger.error('Error on WOL: %s', e) diff --git a/server/src/uds/services/Proxmox/client/__init__.py b/server/src/uds/services/Proxmox/client/__init__.py index 8cd832bf3..c222a978f 100644 --- a/server/src/uds/services/Proxmox/client/__init__.py +++ b/server/src/uds/services/Proxmox/client/__init__.py @@ -160,7 +160,7 @@ class ProxmoxClient: def _get(self, path: str) -> typing.Any: try: - result = security.secureRequestsSession(verify=self._validateCert).get( + result = security.secure_requests_session(verify=self._validateCert).get( self._getPath(path), headers=self.headers, cookies={'PVEAuthCookie': self._ticket}, @@ -179,7 +179,7 @@ class ProxmoxClient: data: typing.Optional[collections.abc.Iterable[tuple[str, str]]] = None, ) -> typing.Any: try: - result = security.secureRequestsSession(verify=self._validateCert).post( + result = security.secure_requests_session(verify=self._validateCert).post( self._getPath(path), data=data, # type: ignore headers=self.headers, @@ -199,7 +199,7 @@ class ProxmoxClient: data: typing.Optional[collections.abc.Iterable[tuple[str, str]]] = None, ) -> typing.Any: try: - result = security.secureRequestsSession(verify=self._validateCert).delete( + result = security.secure_requests_session(verify=self._validateCert).delete( self._getPath(path), data=data, # type: ignore headers=self.headers, @@ -231,7 +231,7 @@ class ProxmoxClient: return try: - result = security.secureRequestsSession(verify=self._validateCert).post( + result = security.secure_requests_session(verify=self._validateCert).post( url=self._getPath('access/ticket'), data=self._credentials, headers=self.headers, diff --git a/server/src/uds/services/Proxmox/provider.py b/server/src/uds/services/Proxmox/provider.py index 5f2b5263c..d836028cd 100644 --- a/server/src/uds/services/Proxmox/provider.py +++ b/server/src/uds/services/Proxmox/provider.py @@ -190,7 +190,7 @@ class ProxmoxProvider( self._api = None if values is not None: - self.timeout.value = validators.validateTimeout(self.timeout.value) + self.timeout.value = validators.validate_timeout(self.timeout.value) logger.debug(self.host.value) # All proxmox use same UniqueId generator diff --git a/server/src/uds/services/Proxmox/service.py b/server/src/uds/services/Proxmox/service.py index dace7df03..bcaea355d 100644 --- a/server/src/uds/services/Proxmox/service.py +++ b/server/src/uds/services/Proxmox/service.py @@ -194,7 +194,7 @@ class ProxmoxLinkedService(services.Service): # pylint: disable=too-many-public def initialize(self, values: 'Module.ValuesType') -> None: if values: - self.baseName.value = validators.validateBasename( + self.baseName.value = validators.validate_basename( self.baseName.value, length=self.lenName.num() ) # if int(self.memory.value) < 128: diff --git a/server/src/uds/services/Xen/service.py b/server/src/uds/services/Xen/service.py index 6d5c95db5..727281af7 100644 --- a/server/src/uds/services/Xen/service.py +++ b/server/src/uds/services/Xen/service.py @@ -188,7 +188,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met initialized by __init__ method of base class, before invoking this. """ if values: - validators.validateBasename(self.baseName.value, self.lenName.num()) + validators.validate_basename(self.baseName.value, self.lenName.num()) if int(self.memory.value) < 256: raise exceptions.validation.ValidationError( diff --git a/server/src/uds/transports/RDP/rdptunnel.py b/server/src/uds/transports/RDP/rdptunnel.py index 0ba45cdd0..b4727e685 100644 --- a/server/src/uds/transports/RDP/rdptunnel.py +++ b/server/src/uds/transports/RDP/rdptunnel.py @@ -118,7 +118,7 @@ class TRDPTransport(BaseRDPTransport): def initialize(self, values: 'Module.ValuesType'): if values: - validators.validateHostPortPair(values.get('tunnelServer', '')) + validators.validate_host_port(values.get('tunnelServer', '')) def get_transport_script( # pylint: disable=too-many-locals self, diff --git a/server/src/uds/transports/SPICE/spicetunnel.py b/server/src/uds/transports/SPICE/spicetunnel.py index 8ded6c5e7..9d958f7be 100644 --- a/server/src/uds/transports/SPICE/spicetunnel.py +++ b/server/src/uds/transports/SPICE/spicetunnel.py @@ -87,7 +87,7 @@ class TSPICETransport(BaseSpiceTransport): def initialize(self, values: 'Module.ValuesType'): if values: - validators.validateHostPortPair(values.get('tunnelServer', '')) + validators.validate_host_port(values.get('tunnelServer', '')) def get_transport_script( # pylint: disable=too-many-locals self, diff --git a/server/src/uds/transports/URL/url_custom.py b/server/src/uds/transports/URL/url_custom.py index 5d2a138f7..5adb65b5c 100644 --- a/server/src/uds/transports/URL/url_custom.py +++ b/server/src/uds/transports/URL/url_custom.py @@ -64,16 +64,17 @@ class URLCustomTransport(transports.Transport): protocol = types.transports.Protocol.OTHER group = types.transports.Grouping.DIRECT - urlPattern = gui.TextField( + url_pattern = gui.TextField( label=_('URL Pattern'), order=1, tooltip=_('URL Pattern to open (i.e. https://_IP_/test?user=_USER_'), default='https://www.udsenterprise.com', length=256, required=True, + stored_field_name='urlPattern', # Allows compat with old versions ) - forceNewWindow = gui.CheckBoxField( + force_new_window = gui.CheckBoxField( label=_('Force new HTML Window'), order=91, tooltip=_( @@ -81,6 +82,7 @@ class URLCustomTransport(transports.Transport): ), default=False, tab=types.ui.Tab.ADVANCED, + stored_field_name='forceNewWindow', # Allows compat with old versions ) def initialize(self, values: 'Module.ValuesType'): @@ -88,8 +90,8 @@ class URLCustomTransport(transports.Transport): return # Strip spaces if not ( - self.urlPattern.value.startswith('http://') - or self.urlPattern.value.startswith('https://') + self.url_pattern.value.startswith('http://') + or self.url_pattern.value.startswith('https://') ): raise exceptions.validation.ValidationError( _('The url must be http or https') @@ -115,11 +117,11 @@ class URLCustomTransport(transports.Transport): username: str = user.get_username_for_auth() username, password = userService.process_user_password(username, password) - url = self.urlPattern.value.replace('_IP_', ip).replace('_USER_', username) + url = self.url_pattern.value.replace('_IP_', ip).replace('_USER_', username) onw = ( '&o_n_w={}'.format(hash(transport.name)) - if self.forceNewWindow.as_bool() + if self.force_new_window.as_bool() else '' ) return str("{}{}".format(url, onw)) diff --git a/server/src/uds/transports/X2GO/x2gotunnel.py b/server/src/uds/transports/X2GO/x2gotunnel.py index bb51d09b1..650bcc741 100644 --- a/server/src/uds/transports/X2GO/x2gotunnel.py +++ b/server/src/uds/transports/X2GO/x2gotunnel.py @@ -93,7 +93,7 @@ class TX2GOTransport(BaseX2GOTransport): def initialize(self, values: 'Module.ValuesType'): if values: - validators.validateHostPortPair(values.get('tunnelServer', '')) + validators.validate_host_port(values.get('tunnelServer', '')) def get_transport_script( self, diff --git a/server/src/uds/urls.py b/server/src/uds/urls.py index d1660ae51..8140ce7a3 100644 --- a/server/src/uds/urls.py +++ b/server/src/uds/urls.py @@ -156,7 +156,7 @@ urlpatterns = [ # WEB API path (not REST api, frontend) re_path( r'^uds/webapi/img/transport/(?P[a-zA-Z0-9:-]+)$', - uds.web.views.transportIcon, + uds.web.views.transport_icon, name='webapi.transportIcon', ), re_path( @@ -167,12 +167,12 @@ urlpatterns = [ # Enabler and Status action are first processed, and if not match, execute the generic "action" handler re_path( r'^uds/webapi/action/(?P[a-zA-Z0-9:-]+)/enable/(?P[a-zA-Z0-9:-]+)$', - uds.web.views.userServiceEnabler, + uds.web.views.user_service_enabler, name='webapi.enabler', ), re_path( r'^uds/webapi/action/(?P[a-zA-Z0-9:-]+)/status/(?P[a-zA-Z0-9:-]+)$', - uds.web.views.userServiceStatus, + uds.web.views.user_service_status, name='webapi.status', ), re_path( @@ -189,7 +189,7 @@ urlpatterns = [ # Transport own link processor re_path( r'^uds/webapi/trans/(?P[a-zA-Z0-9:-]+)/(?P[a-zA-Z0-9:-]+)$', - uds.web.views.transportOwnLink, + uds.web.views.transport_own_link, name='TransportOwnLink', ), # Transport ticket update (for username/password on html5) @@ -207,7 +207,7 @@ urlpatterns = [ # Error message re_path( r'^uds/webapi/error/(?P[0-9]+)$', - uds.web.views.errorMessage, + uds.web.views.error_message, name='webapi.error', ), # END WEB API diff --git a/server/src/uds/web/util/authentication.py b/server/src/uds/web/util/authentication.py index 9abdc23bb..7d5cf60ab 100644 --- a/server/src/uds/web/util/authentication.py +++ b/server/src/uds/web/util/authentication.py @@ -32,7 +32,6 @@ import collections.abc import logging import typing -from django.http import HttpResponseRedirect from django.utils.translation import gettext as _ from uds.core import types @@ -44,8 +43,6 @@ from uds.models import Authenticator # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: - from django.http import HttpRequest # pylint: disable=ungrouped-imports - from uds.core.types.requests import ExtendedHttpRequest from uds.web.forms.LoginForm import LoginForm @@ -83,9 +80,7 @@ def check_login( # pylint: disable=too-many-branches, too-many-statements if form.is_valid(): os = request.os try: - authenticator = Authenticator.objects.get( - uuid=process_uuid(form.cleaned_data['authenticator']) - ) + authenticator = Authenticator.objects.get(uuid=process_uuid(form.cleaned_data['authenticator'])) except Exception: authenticator = Authenticator.null() userName = form.cleaned_data['user'] @@ -95,22 +90,14 @@ def check_login( # pylint: disable=too-many-branches, too-many-statements cache = Cache('auth') cacheKey = str(authenticator.id) + userName tries = cache.get(cacheKey) or 0 - triesByIp = ( - (cache.get(request.ip) or 0) if GlobalConfig.LOGIN_BLOCK_IP.as_bool() else 0 - ) - maxTries = GlobalConfig.MAX_LOGIN_TRIES.getInt() + triesByIp = (cache.get(request.ip) or 0) if GlobalConfig.LOGIN_BLOCK_IP.as_bool() else 0 + maxTries = GlobalConfig.MAX_LOGIN_TRIES.as_int() # Get instance.. authInstance = authenticator.get_instance() # Check if user is locked - if ( - authInstance.block_user_on_failures is True - and (tries >= maxTries) - or triesByIp >= maxTries - ): + if authInstance.block_user_on_failures is True and (tries >= maxTries) or triesByIp >= maxTries: log_login(request, authenticator, userName, 'Temporarily blocked') - return types.auth.LoginResult( - errstr=_('Too many authentication errrors. User temporarily blocked') - ) + return types.auth.LoginResult(errstr=_('Too many authentication errrors. User temporarily blocked')) # check if authenticator is visible for this requests if authInstance.is_ip_allowed(request=request) is False: log_login( @@ -121,14 +108,16 @@ def check_login( # pylint: disable=too-many-branches, too-many-statements ) return types.auth.LoginResult(errstr=_('Access tried from an unallowed source')) - password = form.cleaned_data['password'] or 'axd56adhg466jasd6q8sadñ€sáé--v' # Random string, in fact, just a placeholder that will not be used :) + password = ( + form.cleaned_data['password'] or 'axd56adhg466jasd6q8sadñ€sáé--v' + ) # Random string, in fact, just a placeholder that will not be used :) authResult = authenticate(userName, password, authenticator, request=request) logger.debug('User: %s', authResult.user) if authResult.user is None: logger.debug("Invalid user %s (access denied)", userName) - cache.put(cacheKey, tries + 1, GlobalConfig.LOGIN_BLOCK.getInt()) - cache.put(request.ip, triesByIp + 1, GlobalConfig.LOGIN_BLOCK.getInt()) + cache.put(cacheKey, tries + 1, GlobalConfig.LOGIN_BLOCK.as_int()) + cache.put(request.ip, triesByIp + 1, GlobalConfig.LOGIN_BLOCK.as_int()) log_login( request, authenticator, diff --git a/server/src/uds/web/util/configjs.py b/server/src/uds/web/util/configjs.py index 9ba9524ac..cc85304a3 100644 --- a/server/src/uds/web/util/configjs.py +++ b/server/src/uds/web/util/configjs.py @@ -164,7 +164,7 @@ def uds_js(request: 'ExtendedHttpRequest') -> str: 'os': request.os.os.name, 'image_size': Image.MAX_IMAGE_SIZE, 'experimental_features': GlobalConfig.EXPERIMENTAL_FEATURES.as_bool(), - 'reload_time': GlobalConfig.RELOAD_TIME.getInt(True), + 'reload_time': GlobalConfig.RELOAD_TIME.as_int(True), 'site_name': GlobalConfig.SITE_NAME.get(), 'site_copyright_info': GlobalConfig.SITE_COPYRIGHT.get(), 'site_copyright_link': GlobalConfig.SITE_COPYRIGHT_LINK.get(), @@ -210,7 +210,7 @@ def uds_js(request: 'ExtendedHttpRequest') -> str: 'launch': request.session.get('launch', ''), 'brand': settings.UDSBRAND if hasattr(settings, 'UDSBRAND') else '' }, - 'min_for_filter': GlobalConfig.SITE_FILTER_MIN.getInt(True), + 'min_for_filter': GlobalConfig.SITE_FILTER_MIN.as_int(True), } info: typing.Optional[collections.abc.MutableMapping] = None @@ -308,7 +308,7 @@ def uds_js(request: 'ExtendedHttpRequest') -> str: config['urls']['admin'] = reverse('uds.admin.views.index') config['urls']['rest'] = reverse('REST', kwargs={'arguments': ''}) # Admin config - page_size = GlobalConfig.ADMIN_PAGESIZE.getInt(True) + page_size = GlobalConfig.ADMIN_PAGESIZE.as_int(True) vnc_userservices = GlobalConfig.ADMIN_ENABLE_USERSERVICES_VNC.as_bool(True) # Fix page size to razonable usable values page_size = 10 if page_size < 10 else 100 if page_size > 100 else page_size diff --git a/server/src/uds/web/util/errors.py b/server/src/uds/web/util/errors.py index 1eb0fcced..248fded4a 100644 --- a/server/src/uds/web/util/errors.py +++ b/server/src/uds/web/util/errors.py @@ -55,7 +55,7 @@ if typing.TYPE_CHECKING: logger = logging.getLogger(__name__) -def errorView(request: 'HttpRequest', errorCode: int) -> HttpResponseRedirect: +def error_view(request: 'HttpRequest', errorCode: int) -> HttpResponseRedirect: return HttpResponseRedirect(reverse('page.error', kwargs={'err': errorCode})) @@ -66,15 +66,15 @@ def error(request: 'HttpRequest', err: str) -> 'HttpResponse': return render(request, 'uds/modern/index.html', {}) -def exceptionView(request: 'HttpRequest', exception: Exception) -> HttpResponseRedirect: +def exception_view(request: 'HttpRequest', exception: Exception) -> HttpResponseRedirect: """ Tries to render an error page with error information """ logger.debug(traceback.format_exc()) - return errorView(request, types.errors.Error.from_exception(exception)) + return error_view(request, types.errors.Error.from_exception(exception)) -def errorMessage(request: 'HttpRequest', err: int) -> 'HttpResponse': +def error_message(request: 'HttpRequest', err: int) -> 'HttpResponse': """ Error view, responsible of error display """ diff --git a/server/src/uds/web/util/services.py b/server/src/uds/web/util/services.py index 502e299a5..d27d943c6 100644 --- a/server/src/uds/web/util/services.py +++ b/server/src/uds/web/util/services.py @@ -60,7 +60,7 @@ logger = logging.getLogger(__name__) # pylint: disable=too-many-arguments -def _serviceInfo( +def _service_info( uuid: str, is_meta: bool, name: str, @@ -101,7 +101,7 @@ def _serviceInfo( # pylint: disable=too-many-locals, too-many-branches, too-many-statements -def getServicesData( +def get_services_data( request: 'ExtendedHttpRequestWithUser', ) -> dict[str, typing.Any]: # pylint: disable=too-many-locals, too-many-branches, too-many-statements """Obtains the service data dictionary will all available services for this request @@ -285,7 +285,7 @@ def getServicesData( ) services.append( - _serviceInfo( + _service_info( uuid=meta.uuid, is_meta=True, name=macro_info(meta.name), @@ -386,7 +386,7 @@ def getServicesData( toBeReplacedTxt = '' services.append( - _serviceInfo( + _service_info( uuid=sPool.uuid, is_meta=False, name=macro_info(sPool.name), @@ -432,7 +432,7 @@ def getServicesData( } -def enableService( +def enable_service( request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str ) -> collections.abc.Mapping[str, typing.Any]: # Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..) diff --git a/server/src/uds/web/views/__init__.py b/server/src/uds/web/views/__init__.py index c6ff35da7..bdbbe2636 100644 --- a/server/src/uds/web/views/__init__.py +++ b/server/src/uds/web/views/__init__.py @@ -32,13 +32,13 @@ import logging # from .login import login, logout -from uds.web.util.errors import error, errorMessage +from uds.web.util.errors import error, error_message from .service import ( - transportOwnLink, - transportIcon, - userServiceEnabler, - userServiceStatus, - serviceImage, + transport_own_link, + transport_icon, + user_service_enabler, + user_service_status, + service_image, action, ) from .auth import auth_callback, auth_callback_stage2, auth_info, ticket_auth, custom_auth diff --git a/server/src/uds/web/views/auth.py b/server/src/uds/web/views/auth.py index 24f139ff8..a6db1fcc1 100644 --- a/server/src/uds/web/views/auth.py +++ b/server/src/uds/web/views/auth.py @@ -95,7 +95,7 @@ def auth_callback(request: HttpRequest, authName: str) -> HttpResponse: return HttpResponseRedirect(reverse('page.auth.callback_stage2', args=[ticket])) except Exception as e: # No authenticator found... - return errors.exceptionView(request, e) + return errors.exception_view(request, e) def auth_callback_stage2(request: 'ExtendedHttpRequestWithUser', ticketId: str) -> HttpResponse: @@ -138,7 +138,7 @@ def auth_callback_stage2(request: 'ExtendedHttpRequestWithUser', ticketId: str) ) except Exception as e: logger.exception('authCallback') - return errors.exceptionView(request, e) + return errors.exception_view(request, e) @csrf_exempt @@ -278,15 +278,15 @@ def ticket_auth( uds_cookie(request, response, True) return response except ServiceNotReadyError: - return errors.errorView(request, types.errors.Error.SERVICE_NOT_READY) + return errors.error_view(request, types.errors.Error.SERVICE_NOT_READY) except TicketStore.InvalidTicket: - return errors.errorView(request, types.errors.Error.RELOAD_NOT_SUPPORTED) + return errors.error_view(request, types.errors.Error.RELOAD_NOT_SUPPORTED) except Authenticator.DoesNotExist: logger.error('Ticket has an non existing authenticator') - return errors.errorView(request, types.errors.Error.ACCESS_DENIED) + return errors.error_view(request, types.errors.Error.ACCESS_DENIED) except ServicePool.DoesNotExist: # type: ignore # DoesNotExist is different for each model logger.error('Ticket has an invalid Service Pool') - return errors.errorView(request, types.errors.Error.SERVICE_NOT_FOUND) + return errors.error_view(request, types.errors.Error.SERVICE_NOT_FOUND) except Exception as e: logger.exception('Exception') - return errors.exceptionView(request, e) + return errors.exception_view(request, e) diff --git a/server/src/uds/web/views/main.py b/server/src/uds/web/views/main.py index 82b028521..7d51c9854 100644 --- a/server/src/uds/web/views/main.py +++ b/server/src/uds/web/views/main.py @@ -56,7 +56,7 @@ from uds.web.forms.LoginForm import LoginForm from uds.web.forms.MFAForm import MFAForm from uds.web.util import configjs, errors from uds.web.util.authentication import check_login -from uds.web.util.services import getServicesData +from uds.web.util.services import get_services_data logger = logging.getLogger(__name__) @@ -132,7 +132,7 @@ def login(request: types.requests.ExtendedHttpRequest, tag: typing.Optional[str] ) # On failure, wait a bit if not localhost (random wait) # If error is numeric, redirect... if loginResult.errid: - return errors.errorView(request, loginResult.errid) + return errors.error_view(request, loginResult.errid) # Error, set error on session for process for js request.session['errors'] = [loginResult.errstr] @@ -162,7 +162,7 @@ def js(request: types.requests.ExtendedHttpRequest) -> HttpResponse: @never_cache @auth.deny_non_authenticated # web_login_required not used here because this is not a web page, but js def services_data_json(request: types.requests.ExtendedHttpRequestWithUser) -> HttpResponse: - return JsonResponse(getServicesData(request)) + return JsonResponse(get_services_data(request)) # The MFA page does not needs CSRF token, so we disable it @@ -232,7 +232,7 @@ def mfa( request.user.manager.name, mfa_provider.name, ) - return errors.errorView(request, types.errors.Error.ACCESS_DENIED) + return errors.error_view(request, types.errors.Error.ACCESS_DENIED) # None, the authenticator will decide what to do if mfa_identifier is empty tries = request.session.get('mfa_tries', 0) @@ -277,12 +277,12 @@ def mfa( logger.error('MFA error: %s', e) tries += 1 request.session['mfa_tries'] = tries - if tries >= config.GlobalConfig.MAX_LOGIN_TRIES.getInt(): + if tries >= config.GlobalConfig.MAX_LOGIN_TRIES.as_int(): # Clean session request.session.flush() # Too many tries, redirect to login error page - return errors.errorView(request, types.errors.Error.ACCESS_DENIED) - return errors.errorView(request, types.errors.Error.INVALID_MFA_CODE) + return errors.error_view(request, types.errors.Error.ACCESS_DENIED) + return errors.error_view(request, types.errors.Error.INVALID_MFA_CODE) else: pass # Will render again the page else: @@ -306,7 +306,7 @@ def mfa( request.session['mfa_start_time'] = now except Exception as e: logger.error('Error processing MFA: %s', e) - return errors.errorView(request, types.errors.Error.UNKNOWN_ERROR) + return errors.error_view(request, types.errors.Error.UNKNOWN_ERROR) # Compose a nice "XX years, XX months, XX days, XX hours, XX minutes" string from mfaProvider.remember_device remember_device = '' diff --git a/server/src/uds/web/views/service.py b/server/src/uds/web/views/service.py index 7e61db629..4ee69d4ef 100644 --- a/server/src/uds/web/views/service.py +++ b/server/src/uds/web/views/service.py @@ -56,7 +56,7 @@ logger = logging.getLogger(__name__) @web_login_required(admin=False) -def transportOwnLink(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str): +def transport_own_link(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str): response: collections.abc.MutableMapping[str, typing.Any] = {} # If userService is not owned by user, will raise an exception @@ -89,7 +89,7 @@ def transportOwnLink(request: 'ExtendedHttpRequestWithUser', idService: str, idT # pylint: disable=unused-argument @cache_page(3600, key_prefix='img', cache='memory') -def transportIcon(request: 'ExtendedHttpRequest', idTrans: str) -> HttpResponse: +def transport_icon(request: 'ExtendedHttpRequest', idTrans: str) -> HttpResponse: try: transport: Transport if idTrans[:6] == 'LABEL:': @@ -105,7 +105,7 @@ def transportIcon(request: 'ExtendedHttpRequest', idTrans: str) -> HttpResponse: @cache_page(3600, key_prefix='img', cache='memory') -def serviceImage(request: 'ExtendedHttpRequest', idImage: str) -> HttpResponse: +def service_image(request: 'ExtendedHttpRequest', idImage: str) -> HttpResponse: try: icon = Image.objects.get(uuid=process_uuid(idImage)) return icon.image_as_response() @@ -121,11 +121,11 @@ def serviceImage(request: 'ExtendedHttpRequest', idImage: str) -> HttpResponse: @web_login_required(admin=False) @never_cache -def userServiceEnabler( +def user_service_enabler( request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str ) -> HttpResponse: return HttpResponse( - json.dumps(services.enableService(request, idService=idService, idTransport=idTransport)), + json.dumps(services.enable_service(request, idService=idService, idTransport=idTransport)), content_type='application/json', ) @@ -141,7 +141,7 @@ def closer(request: 'ExtendedHttpRequest') -> HttpResponse: @web_login_required(admin=False) @never_cache -def userServiceStatus(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str) -> HttpResponse: +def user_service_status(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str) -> HttpResponse: ''' Returns; 'running' if not ready @@ -222,7 +222,7 @@ def action(request: 'ExtendedHttpRequestWithUser', idService: str, actionString: if rebuild: # Rebuild services data, but return only "this" service - for v in services.getServicesData(request)['services']: + for v in services.get_services_data(request)['services']: if v['id'] == idService: response = v break diff --git a/server/tests/core/ui/test_userinterface_serialization.py b/server/tests/core/ui/test_userinterface_serialization.py index ff6098c0a..437faa4b0 100644 --- a/server/tests/core/ui/test_userinterface_serialization.py +++ b/server/tests/core/ui/test_userinterface_serialization.py @@ -41,7 +41,12 @@ from ...utils.test import UDSTestCase from uds.core import types, consts from uds.core.ui.user_interface import gui -from ...fixtures.user_interface import TestingUserInterface, DEFAULTS +from ...fixtures.user_interface import ( + TestingUserInterface, + DEFAULTS, + TestingUserInterfaceFieldName, + TestingUserInterfaceFieldNameOrig, +) logger = logging.getLogger(__name__) @@ -168,3 +173,12 @@ class UserinterfaceTest(UDSTestCase): self.assertEqual(ui, ui2) self.ensure_values_fine(ui2) + + def test_stored_field_name(self): + # This test is to ensure that new serialized data can be loaded + ui = TestingUserInterfaceFieldNameOrig() + data = ui.serialize_fields() + ui2 = TestingUserInterfaceFieldName() + ui2.unserialize_fields(data) + + self.assertEqual(ui.strField.value, ui2.str_field.value) diff --git a/server/tests/core/util/test_storage.py b/server/tests/core/util/test_storage.py index 4eb37f265..e4724794a 100644 --- a/server/tests/core/util/test_storage.py +++ b/server/tests/core/util/test_storage.py @@ -30,8 +30,10 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import base64 from ...utils.test import UDSTestCase -from uds.core.util.storage import Storage +from uds.core.util import storage +from uds import models UNICODE_CHARS = 'ñöçóá^(pípè)' UNICODE_CHARS_2 = 'ñöçóá^(€íöè)' @@ -40,32 +42,84 @@ VALUE_1 = ['unicode', b'string', {'a': 1, 'b': 2.0}] class StorageTest(UDSTestCase): def test_storage(self): - storage = Storage(UNICODE_CHARS) + strg = storage.Storage(UNICODE_CHARS) - storage.put(UNICODE_CHARS, b'chars') - storage.save_to_db('saveData', UNICODE_CHARS, UNICODE_CHARS) - storage.save_to_db('saveData2', UNICODE_CHARS_2, UNICODE_CHARS) - storage.save_to_db('saveData3', UNICODE_CHARS, 'attribute') - storage.save_to_db('saveData4', UNICODE_CHARS_2, 'attribute') - storage.put(b'key', UNICODE_CHARS) - storage.put(UNICODE_CHARS_2, UNICODE_CHARS) + strg.put(UNICODE_CHARS, b'chars') + strg.save_to_db('saveData', UNICODE_CHARS, UNICODE_CHARS) + strg.save_to_db('saveData2', UNICODE_CHARS_2, UNICODE_CHARS) + strg.save_to_db('saveData3', UNICODE_CHARS, 'attribute') + strg.save_to_db('saveData4', UNICODE_CHARS_2, 'attribute') + strg.put(b'key', UNICODE_CHARS) + strg.put(UNICODE_CHARS_2, UNICODE_CHARS) - storage.put_pickle('pickle', VALUE_1) + strg.put_pickle('pickle', VALUE_1) - self.assertEqual(storage.get(UNICODE_CHARS), u'chars') # Always returns unicod - self.assertEqual(storage.read_from_db('saveData'), UNICODE_CHARS) - self.assertEqual(storage.read_from_db('saveData2'), UNICODE_CHARS_2) - self.assertEqual(storage.get(b'key'), UNICODE_CHARS) - self.assertEqual(storage.get(UNICODE_CHARS_2), UNICODE_CHARS) - self.assertEqual(storage.get_unpickle('pickle'), VALUE_1) + self.assertEqual(strg.get(UNICODE_CHARS), u'chars') # Always returns unicod + self.assertEqual(strg.read_from_db('saveData'), UNICODE_CHARS) + self.assertEqual(strg.read_from_db('saveData2'), UNICODE_CHARS_2) + self.assertEqual(strg.get(b'key'), UNICODE_CHARS) + self.assertEqual(strg.get(UNICODE_CHARS_2), UNICODE_CHARS) + self.assertEqual(strg.get_unpickle('pickle'), VALUE_1) - self.assertEqual(len(list(storage.search_by_attr1(UNICODE_CHARS))), 2) - self.assertEqual(len(list(storage.search_by_attr1('attribute'))), 2) + self.assertEqual(len(list(strg.search_by_attr1(UNICODE_CHARS))), 2) + self.assertEqual(len(list(strg.search_by_attr1('attribute'))), 2) - storage.remove(UNICODE_CHARS) - storage.remove(b'key') - storage.remove('pickle') + strg.remove(UNICODE_CHARS) + strg.remove(b'key') + strg.remove('pickle') - self.assertIsNone(storage.get(UNICODE_CHARS)) - self.assertIsNone(storage.get(b'key')) - self.assertIsNone(storage.get_unpickle('pickle')) + self.assertIsNone(strg.get(UNICODE_CHARS)) + self.assertIsNone(strg.get(b'key')) + self.assertIsNone(strg.get_unpickle('pickle')) + + def test_storage_as_dict(self): + strg = storage.Storage(UNICODE_CHARS) + + strg.put(UNICODE_CHARS, 'chars') + + with strg.as_dict() as d: + d['test_key'] = UNICODE_CHARS_2 + + # Assert that UNICODE_CHARS is in the dict + self.assertEqual(d[UNICODE_CHARS], 'chars') + + self.assertEqual(d['test_key'], UNICODE_CHARS_2) + + # The values set inside the "with" are not available "outside" + # because the format is not compatible (with the dict, the values are stored as a tuple, with the original key stored + # and with old format, only the value is stored + + def test_old_storage_compat(self): + models.Storage.objects.create( + owner=UNICODE_CHARS, + key=storage._old_calculate_key(UNICODE_CHARS.encode(), UNICODE_CHARS.encode()), + data=base64.b64encode((UNICODE_CHARS * 5).encode()).decode(), + ) + strg = storage.Storage(UNICODE_CHARS) + # Ensure that key is found + self.assertEqual(strg.get(UNICODE_CHARS), UNICODE_CHARS * 5) + # And that now, the key is stored in the new format + # If not exists, will raise an exception + models.Storage.objects.get( + owner=UNICODE_CHARS, + key=storage._calculate_key(UNICODE_CHARS.encode(), UNICODE_CHARS.encode()), + ) + + def test_storage_as_dict_old(self): + models.Storage.objects.create( + owner=UNICODE_CHARS, + key=storage._old_calculate_key(UNICODE_CHARS.encode(), UNICODE_CHARS.encode()), + data=base64.b64encode((UNICODE_CHARS * 5).encode()).decode(), + ) + strg = storage.Storage(UNICODE_CHARS) + + with strg.as_dict() as d: + # Assert that UNICODE_CHARS is in the dict (stored with old format) + self.assertEqual(d[UNICODE_CHARS], UNICODE_CHARS * 5) + + # And that now, the key is stored in the new format + # If not exists, will raise an exception + models.Storage.objects.get( + owner=UNICODE_CHARS, + key=storage._calculate_key(UNICODE_CHARS.encode(), UNICODE_CHARS.encode()), + ) diff --git a/server/tests/fixtures/user_interface.py b/server/tests/fixtures/user_interface.py index 965d6c389..6ca6f59c2 100644 --- a/server/tests/fixtures/user_interface.py +++ b/server/tests/fixtures/user_interface.py @@ -50,6 +50,7 @@ DEFAULTS: dict[str, typing.Any] = { 'info_field': 'Default value info', } + class TestingUserInterface(UserInterface): str_field = gui.TextField( label='Text Field', @@ -57,6 +58,7 @@ class TestingUserInterface(UserInterface): tooltip='This is a text field', required=True, default=typing.cast(str, DEFAULTS['str_field']), + stored_field_name='strField', ) str_auto_field = gui.TextAutocompleteField( label='Text Autocomplete Field', @@ -136,7 +138,6 @@ class TestingUserInterface(UserInterface): default=typing.cast(str, DEFAULTS['info_field']), ) - # Equals operator, to speed up tests writing def __eq__(self, other: typing.Any) -> bool: if not isinstance(other, TestingUserInterface): @@ -155,3 +156,24 @@ class TestingUserInterface(UserInterface): and self.date_field.value == other.date_field.value # Info field is not compared, because it is not serialized ) + + +class TestingUserInterfaceFieldNameOrig(UserInterface): + strField = gui.TextField( + label='Text Field', + order=0, + tooltip='This is a text field', + required=True, + default=typing.cast(str, DEFAULTS['str_field']), + ) + + +class TestingUserInterfaceFieldName(UserInterface): + str_field = gui.TextField( + label='Text Field', + order=0, + tooltip='This is a text field', + required=True, + default='', # Will be loaded from orig + stored_field_name='strField', + ) diff --git a/server/tests/web/util/test_services.py b/server/tests/web/util/test_services.py index 1eff45cd0..fcda3ccba 100644 --- a/server/tests/web/util/test_services.py +++ b/server/tests/web/util/test_services.py @@ -83,7 +83,7 @@ class TestGetServicesData(UDSTransactionTestCase): )[0].deployed_service ) - data = services.getServicesData(self.request) + data = services.get_services_data(self.request) now = datetime.datetime.now() # Will return this: # return { @@ -184,7 +184,7 @@ class TestGetServicesData(UDSTransactionTestCase): ) - data = services.getServicesData(self.request) + data = services.get_services_data(self.request) now = datetime.datetime.now() result_services: typing.Final[ @@ -239,7 +239,7 @@ class TestGetServicesData(UDSTransactionTestCase): ) - data = services.getServicesData(self.request) + data = services.get_services_data(self.request) now = datetime.datetime.now() result_services: typing.Final[