mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-22 13:34:04 +03:00
added communication between a token-actor and a token-service
This commit is contained in:
parent
d25f278230
commit
fd85e3a202
@ -33,6 +33,7 @@ import time
|
||||
import logging
|
||||
import typing
|
||||
import functools
|
||||
import enum
|
||||
|
||||
from uds.models import (
|
||||
ActorToken,
|
||||
@ -76,6 +77,16 @@ class BlockAccess(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NotifyActionType(enum.StrEnum):
|
||||
LOGIN = 'login'
|
||||
LOGOUT = 'logout'
|
||||
DATA = 'data'
|
||||
|
||||
@staticmethod
|
||||
def valid_names() -> typing.List[str]:
|
||||
return [e.value for e in NotifyActionType]
|
||||
|
||||
|
||||
# Helpers
|
||||
def fixIdsList(idsList: typing.List[str]) -> typing.List[str]:
|
||||
"""
|
||||
@ -174,6 +185,46 @@ class ActorV3Action(Handler):
|
||||
|
||||
raise AccessDenied('Access denied')
|
||||
|
||||
# Some helpers
|
||||
def notifyService(self, action: NotifyActionType) -> None:
|
||||
try:
|
||||
# If unmanaged, use Service locator
|
||||
service: 'services.Service' = Service.objects.get(token=self._params['token']).getInstance()
|
||||
|
||||
# We have a valid service, now we can make notifications
|
||||
|
||||
# Build the possible ids and make initial filter to match service
|
||||
idsList = [x['ip'] for x in self._params['id']] + [x['mac'] for x in self._params['id']][:10]
|
||||
|
||||
# ensure idsLists has upper and lower versions for case sensitive databases
|
||||
idsList = fixIdsList(idsList)
|
||||
|
||||
validId: typing.Optional[str] = service.getValidId(idsList)
|
||||
|
||||
is_remote = self._params.get('session_type', '')[:4] in ('xrdp', 'RDP-')
|
||||
|
||||
# Must be valid
|
||||
if action in (NotifyActionType.LOGIN, NotifyActionType.LOGOUT):
|
||||
if not validId: # For login/logout, we need a valid id
|
||||
raise Exception()
|
||||
# Notify Service that someone logged in/out
|
||||
|
||||
if action == NotifyActionType.LOGIN:
|
||||
# Try to guess if this is a remote session
|
||||
service.processLogin(validId, remote_login=is_remote)
|
||||
elif action == NotifyActionType.LOGOUT:
|
||||
service.processLogout(validId, remote_login=is_remote)
|
||||
elif action == NotifyActionType.DATA:
|
||||
service.notifyData(validId, self._params['data'])
|
||||
else:
|
||||
raise Exception('Invalid action')
|
||||
|
||||
# All right, service notified..
|
||||
except Exception as e:
|
||||
# Log error and continue
|
||||
logger.error('Error notifying service: %s (%s)', e, self._params)
|
||||
raise BlockAccess() from None
|
||||
|
||||
|
||||
class Test(ActorV3Action):
|
||||
"""
|
||||
@ -501,47 +552,7 @@ class Version(ActorV3Action):
|
||||
return ActorV3Action.actorResult()
|
||||
|
||||
|
||||
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) -> None:
|
||||
try:
|
||||
# If unmanaged, use Service locator
|
||||
service: 'services.Service' = Service.objects.get(token=self._params['token']).getInstance()
|
||||
|
||||
# We have a valid service, now we can make notifications
|
||||
|
||||
# Build the possible ids and make initial filter to match service
|
||||
idsList = [x['ip'] for x in self._params['id']] + [x['mac'] for x in self._params['id']][:10]
|
||||
|
||||
# ensure idsLists has upper and lower versions for case sensitive databases
|
||||
idsList = fixIdsList(idsList)
|
||||
|
||||
validId: typing.Optional[str] = service.getValidId(idsList)
|
||||
|
||||
# Must be valid
|
||||
if not validId:
|
||||
raise Exception()
|
||||
|
||||
# Recover Id Info from service and validId
|
||||
# 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
|
||||
service.processLogin(validId, remote_login=is_remote)
|
||||
else:
|
||||
service.processLogout(validId, remote_login=is_remote)
|
||||
|
||||
# All right, service notified..
|
||||
except Exception as e:
|
||||
# Log error and continue
|
||||
logger.error('Error notifying service: %s (%s)', e, self._params)
|
||||
raise BlockAccess() from None
|
||||
|
||||
|
||||
class Login(LoginLogout):
|
||||
class Login(ActorV3Action):
|
||||
"""
|
||||
Notifies user logged id
|
||||
"""
|
||||
@ -597,7 +608,7 @@ class Login(LoginLogout):
|
||||
): # If unamanaged host, lest do a bit more work looking for a service with the provided parameters...
|
||||
if isManaged:
|
||||
raise
|
||||
self.notifyService(isLogin=True)
|
||||
self.notifyService(action=NotifyActionType.LOGIN)
|
||||
|
||||
return ActorV3Action.actorResult(
|
||||
{
|
||||
@ -610,7 +621,7 @@ class Login(LoginLogout):
|
||||
)
|
||||
|
||||
|
||||
class Logout(LoginLogout):
|
||||
class Logout(ActorV3Action):
|
||||
"""
|
||||
Notifies user logged out
|
||||
"""
|
||||
@ -653,7 +664,7 @@ class Logout(LoginLogout):
|
||||
): # If unamanaged host, lest do a bit more work looking for a service with the provided parameters...
|
||||
if isManaged:
|
||||
raise
|
||||
self.notifyService(isLogin=False) # Logout notification
|
||||
self.notifyService(NotifyActionType.LOGOUT) # Logout notification
|
||||
return ActorV3Action.actorResult(
|
||||
'notified'
|
||||
) # Result is that we have not processed the logout in fact, but notified the service
|
||||
@ -761,7 +772,7 @@ class Unmanaged(ActorV3Action):
|
||||
# Try to infer the ip from the valid id (that could be an IP or a MAC)
|
||||
ip: str
|
||||
try:
|
||||
ip = next(x['ip'] for x in self._params['id'] if validId in (x['ip'], x['mac']))
|
||||
ip = next(x['ip'] for x in self._params['id'] if validId in (x['ip'], x['mac']))
|
||||
except StopIteration:
|
||||
ip = self._params['id'][0]['ip'] # Get first IP if no valid ip found
|
||||
|
||||
@ -802,21 +813,22 @@ class Notify(ActorV3Action):
|
||||
|
||||
def get(self) -> typing.MutableMapping[str, typing.Any]:
|
||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||
if (
|
||||
'action' not in self._params
|
||||
or 'token' not in self._params
|
||||
or self._params['action'] not in ('login', 'logout')
|
||||
):
|
||||
# Requested login or logout
|
||||
raise RequestError('Invalid parameters')
|
||||
try:
|
||||
action = NotifyActionType(self._params['action'])
|
||||
token = self._params['token'] # pylint: disable=unused-variable # Just to check it exists
|
||||
except Exception as e:
|
||||
# Requested login, logout or whatever
|
||||
raise RequestError('Invalid parameters') from e
|
||||
|
||||
try:
|
||||
# Check block manually
|
||||
checkBlockedIp(self._request) # pylint: disable=protected-access
|
||||
if self._params['action'] == 'login':
|
||||
if action == NotifyActionType.LOGIN:
|
||||
Login.action(typing.cast(Login, self))
|
||||
else:
|
||||
elif action == NotifyActionType.LOGOUT:
|
||||
Logout.action(typing.cast(Logout, self))
|
||||
elif action == NotifyActionType.DATA:
|
||||
self.notifyService(action)
|
||||
|
||||
return ActorV3Action.actorResult('ok')
|
||||
except UserService.DoesNotExist:
|
||||
|
@ -139,9 +139,7 @@ class Service(Module):
|
||||
usesCache = False
|
||||
|
||||
# : Tooltip to be used if services uses cache at administration interface, indicated by :py:attr:.usesCache
|
||||
cacheTooltip = _(
|
||||
'None'
|
||||
) # : Tooltip shown to user when this item is pointed at admin interface
|
||||
cacheTooltip = _('None') # : Tooltip shown to user when this item is pointed at admin interface
|
||||
|
||||
# : If user deployments can be cached (see :py:attr:.usesCache), may he also can provide a secondary cache,
|
||||
# : that is no more that user deployments that are "almost ready" to be used, but preperably consumes less
|
||||
@ -150,9 +148,7 @@ class Service(Module):
|
||||
usesCache_L2 = False # : If we need to generate a "Level 2" cache for this service (i.e., L1 could be running machines and L2 suspended machines)
|
||||
|
||||
# : Tooltip to be used if services uses L2 cache at administration interface, indicated by :py:attr:.usesCache_L2
|
||||
cacheTooltip_L2 = _(
|
||||
'None'
|
||||
) # : Tooltip shown to user when this item is pointed at admin interface
|
||||
cacheTooltip_L2 = _('None') # : Tooltip shown to user when this item is pointed at admin interface
|
||||
|
||||
# : If the service needs a o.s. manager (see os managers section)
|
||||
needsManager: bool = False
|
||||
@ -270,9 +266,7 @@ class Service(Module):
|
||||
|
||||
# Keep untouched if maxServices is not present
|
||||
|
||||
def requestServicesForAssignation(
|
||||
self, **kwargs
|
||||
) -> typing.Iterable[UserDeployment]:
|
||||
def requestServicesForAssignation(self, **kwargs) -> typing.Iterable[UserDeployment]:
|
||||
"""
|
||||
override this if mustAssignManualy is True
|
||||
@params kwargs: Named arguments
|
||||
@ -311,9 +305,7 @@ class Service(Module):
|
||||
return []
|
||||
|
||||
def assignFromAssignables(
|
||||
self, assignableId: str,
|
||||
user: 'models.User',
|
||||
userDeployment: UserDeployment
|
||||
self, assignableId: str, user: 'models.User', userDeployment: UserDeployment
|
||||
) -> str:
|
||||
"""
|
||||
Assigns from it internal assignable list to an user
|
||||
@ -384,6 +376,18 @@ class Service(Module):
|
||||
"""
|
||||
return
|
||||
|
||||
def notifyData(self, id: typing.Optional[str], data: str) -> None:
|
||||
"""
|
||||
Processes a custom data notification, that must be interpreted by the service itself.
|
||||
This allows "token actors" to communicate with service directly, what is needed for
|
||||
some kind of services (like LinuxApps)
|
||||
|
||||
Args:
|
||||
id (typing.Optional[str]): Id validated through "getValidId". May be None if not validated (or not provided)
|
||||
data (str): Data to process
|
||||
"""
|
||||
return
|
||||
|
||||
def storeIdInfo(self, id: str, data: typing.Any) -> None:
|
||||
self.storage.putPickle('__nfo_' + id, data)
|
||||
|
||||
@ -401,9 +405,7 @@ class Service(Module):
|
||||
from uds.models import Service as DBService # pylint: disable=import-outside-toplevel
|
||||
|
||||
if self.getUuid():
|
||||
log.doLog(
|
||||
DBService.objects.get(uuid=self.getUuid()), level, message, log.LogSource.SERVICE
|
||||
)
|
||||
log.doLog(DBService.objects.get(uuid=self.getUuid()), level, message, log.LogSource.SERVICE)
|
||||
|
||||
@classmethod
|
||||
def canAssign(cls) -> bool:
|
||||
|
Loading…
Reference in New Issue
Block a user