From adaabf9d83bcb5f74a17ea35c716d7320a2c1fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Thu, 4 Aug 2022 21:37:33 +0200 Subject: [PATCH] Fixing up unmanaged actor --- actor/src/udsactor/client.py | 2 +- actor/src/udsactor/rest.py | 17 +++++++---- actor/src/udsactor/service.py | 29 ++++++++++++++----- server/src/uds/REST/methods/actor_v3.py | 7 +++-- server/src/uds/core/services/service.py | 2 +- .../PhysicalMachines/service_multi.py | 20 +++++++++---- 6 files changed, 54 insertions(+), 23 deletions(-) diff --git a/actor/src/udsactor/client.py b/actor/src/udsactor/client.py index 2678a57b..941fa008 100644 --- a/actor/src/udsactor/client.py +++ b/actor/src/udsactor/client.py @@ -198,7 +198,7 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att time.sleep(1.3) # Sleeps between loop iterations self._loginInfo = None - self.api.logout(platform.operations.getCurrentUser() + self._extraLogoff) + self.api.logout(platform.operations.getCurrentUser() + self._extraLogoff, platform.operations.getSessionType()) except Exception as e: logger.error('Error on client loop: %s', e) diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 061cb644..f058239b 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -131,7 +131,7 @@ class UDSApi: # pylint: disable=too-few-public-methods headers=headers, verify=self._validateCert, timeout=TIMEOUT, - proxies=NO_PROXY # type: ignore + proxies=NO_PROXY # type: ignore if disableProxy else None, # if not proxies wanted, enforce it ) @@ -351,19 +351,21 @@ class UDSServerApi(UDSApi): actor_type: typing.Optional[str], token: str, username: str, + sessionType: str, interfaces: typing.Iterable[types.InterfaceInfoType], secret: typing.Optional[str], - ) -> None: + ) -> typing.Optional[str]: if not token: - return + return None payload = { 'type': actor_type or types.MANAGED, 'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces], 'token': token, 'username': username, + 'session_type': sessionType, 'secret': secret or '', } - self._doPost('logout', payload) + return self._doPost('logout', payload) # Can be 'ok' or 'notified' def log(self, own_token: str, level: int, message: str) -> None: if not own_token: @@ -418,8 +420,11 @@ class UDSClientApi(UDSApi): max_idle=result['max_idle'], ) - def logout(self, username: str) -> None: - payLoad = {'username': username} + def logout(self, username: str, sessionType: typing.Optional[str]) -> None: + payLoad = { + 'username': username, + 'session_type': sessionType or UNKNOWN + } self.post('logout', payLoad) def ping(self) -> bool: diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index 92b5412f..7c94acd7 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -377,6 +377,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes self._cfg.actorType, self._cfg.own_token, '', + '', self._interfaces, self._secret, ) @@ -494,9 +495,12 @@ class CommonService: # pylint: disable=too-many-instance-attributes # If unmanaged, do initialization now, because we don't know before this # Also, even if not initialized, get a "login" notification token if not self.isManaged(): - self.initialize() - master_token = self._cfg.master_token - secret = self._secret + self._initialized = ( + self.initialize() + ) # Maybe it's a local login by an unmanaged host.... On real login, will execute initilize again + if self._initialized: + master_token = self._cfg.master_token + secret = self._secret # Own token will not be set if UDS did not assigned the initialized VM to an user # In that case, take master token (if machine is Unamanaged version) @@ -518,7 +522,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes return result - def logout(self, username: str) -> None: + def logout(self, username: str, sessionType: typing.Optional[str] = None) -> None: self._loggedIn = False master_token = self._cfg.master_token @@ -527,9 +531,20 @@ class CommonService: # pylint: disable=too-many-instance-attributes # In that case, take master token (if machine is Unamanaged version) token = self._cfg.own_token or master_token if token: - self._api.logout( - self._cfg.actorType, token, username, self._interfaces, self._secret - ) + # If logout is not processed (that is, not ok result), the logout has not been processed + if ( + self._api.logout( + self._cfg.actorType, + token, + username, + sessionType or '', + self._interfaces, + self._secret, + ) + != 'ok' + ): + logger.info('Logout from %s ignored as required by uds broker', username) + return self.onLogout(username) diff --git a/server/src/uds/REST/methods/actor_v3.py b/server/src/uds/REST/methods/actor_v3.py index f480a034..59f5fe4b 100644 --- a/server/src/uds/REST/methods/actor_v3.py +++ b/server/src/uds/REST/methods/actor_v3.py @@ -419,7 +419,7 @@ class Version(ActorV3Action): class LoginLogout(ActorV3Action): name = 'notused' # Not really important, this is not a "leaf" class and will not be directly available - def notifyService(self, isLogin: bool): + def notifyService(self, isLogin: bool) -> None: try: # If unmanaged, use Service locator service: 'services.Service' = Service.objects.get( @@ -446,12 +446,12 @@ class LoginLogout(ActorV3Action): # idInfo = service.recoverIdInfo(validId) # Notify Service that someone logged in/out + is_remote = self._params.get('session_type', '')[:4] in ('xrdp', 'RDP-') if isLogin: # Try to guess if this is a remote session - is_remote = self._params.get('session_type', '')[:4] in ('xrdp', 'RDP-') service.processLogin(validId, remote_login=is_remote) else: - service.processLogout(validId) + service.processLogout(validId, remote_login=is_remote) # All right, service notified... except Exception: @@ -562,6 +562,7 @@ class Logout(LoginLogout): if isManaged: raise self.notifyService(isLogin=False) # Logout notification + return ActorV3Action.actorResult('notified') # Result is that we have not processed the logout in fact, but notified the service return ActorV3Action.actorResult('ok') diff --git a/server/src/uds/core/services/service.py b/server/src/uds/core/services/service.py index 842629aa..381a735d 100644 --- a/server/src/uds/core/services/service.py +++ b/server/src/uds/core/services/service.py @@ -335,7 +335,7 @@ class Service(Module): """ return - def processLogout(self, id: str) -> None: + def processLogout(self, id: str, remote_login: bool) -> None: """ In the case that a logout is invoked directly on an actor controlled machine with an service token, this method will be called with provided info by uds actor (parameters) diff --git a/server/src/uds/services/PhysicalMachines/service_multi.py b/server/src/uds/services/PhysicalMachines/service_multi.py index 54f6190e..af5c83a8 100644 --- a/server/src/uds/services/PhysicalMachines/service_multi.py +++ b/server/src/uds/services/PhysicalMachines/service_multi.py @@ -231,10 +231,13 @@ class IPMachinesService(IPServiceBase): # Sets maximum services for this self.maxDeployed = len(self._ips) - def canBeUsed(self, locked: typing.Optional[int], now: int) -> int: + def canBeUsed(self, locked: typing.Optional[typing.Union[str, int]], now: int) -> int: # If _maxSessionForMachine is 0, it can be used only if not locked # (that is locked is None) locked = locked or 0 + if isinstance(locked, str) and not '.' in locked: # Convert to int and treat it as a "locked" element + locked = int(locked) + if self._maxSessionForMachine <= 0: return not bool(locked) # If locked is None, it can be used @@ -342,19 +345,26 @@ class IPMachinesService(IPServiceBase): Process login for a machine not assigned to any user. ''' logger.debug('Processing login for %s: %s', self, id) + # Locate the IP on the storage theIP = IPServiceBase.getIp(id) now = getSqlDatetimeAsUnix() - locked = self.storage.getPickle(theIP) + locked: typing.Union[None, str, int] = self.storage.getPickle(theIP) if self.canBeUsed(locked, now): - self.storage.putPickle(theIP, now) # Lock it + self.storage.putPickle(theIP, str(now)) # Lock it - def processLogout(self, id: str) -> None: + def processLogout(self, id: str, remote_login: bool) -> None: ''' Process logout for a machine not assigned to any user. ''' logger.debug('Processing logout for %s: %s', self, id) - self.unassignMachine(id) + # Locate the IP on the storage + theIP = IPServiceBase.getIp(id) + locked: typing.Union[None, str, int] = self.storage.getPickle(theIP) + # If locked is str, has been locked by processLogin so we can unlock it + if isinstance(locked, str): + self.unassignMachine(id) + # If not proccesed by login, we cannot release it def notifyInitialization(self, id: str) -> None: '''