diff --git a/server/src/uds/REST/methods/tunnel.py b/server/src/uds/REST/methods/tunnel.py index 304e8b425..2e25b6981 100644 --- a/server/src/uds/REST/methods/tunnel.py +++ b/server/src/uds/REST/methods/tunnel.py @@ -101,15 +101,14 @@ class TunnelTicket(Handler): # Try to log Close event try: - pool = models.ServicePool.objects.filter(uuid=extra['p']) # If pool does not exists, do not log anything events.addEvent( userService.deployed_service, events.ET_TUNNEL_CLOSE, - username=extra.get('u', 'unknown'), - srcip=extra.get('s', 'unkown'), - dstip=extra.get('d', 'unknown'), - uniqueid=extra.get('m', 'unknown'), + duration=totalTime, + sent=sent, + received=recv, + tunnel=extra.get('t', 'unknown'), ) except Exception: pass @@ -123,7 +122,7 @@ class TunnelTicket(Handler): username=user.pretty_name, srcip=self._args[1], dstip=host, - uniqueid=userService.unique_id, + tunnel=self._args[0], ) msg = f'User {user.name} started tunnel {self._args[0][:8]}... to {host}:{port} from {self._args[1]}.' log.doLog(user.manager, log.INFO, msg) @@ -137,11 +136,6 @@ class TunnelTicket(Handler): extra={ 't': self._args[0], # ticket 'b': models.getSqlDatetimeAsUnix(), # Begin time stamp - 'p': userService.deployed_service.uuid, # Pool - 'u': userService.user.pretty_name, # Username - 'm': userService.unique_id, # USerService unique id (MAC normally) - 's': self._args[1], - 'd': host + ':' + str(port) }, validity=MAX_SESSION_LENGTH, ) diff --git a/server/src/uds/core/auths/auth.py b/server/src/uds/core/auths/auth.py index cee257999..ef0bc51cd 100644 --- a/server/src/uds/core/auths/auth.py +++ b/server/src/uds/core/auths/auth.py @@ -224,14 +224,14 @@ def __registerUser( # And add an login event events.addEvent( authenticator, events.ET_LOGIN, username=username, srcip=request.ip - ) # pylint: disable=maybe-no-member + ) events.addEvent( authenticator, events.ET_PLATFORM, platform=request.os['OS'], browser=request.os['Browser'], version=request.os['Version'], - ) # pylint: disable=maybe-no-member + ) return usr return None diff --git a/server/src/uds/core/managers/stats.py b/server/src/uds/core/managers/stats.py index e6e8c8397..91098933b 100644 --- a/server/src/uds/core/managers/stats.py +++ b/server/src/uds/core/managers/stats.py @@ -42,10 +42,10 @@ from uds.models import StatsEvents logger = logging.getLogger(__name__) FLDS_EQUIV: typing.Mapping[str, typing.Iterable[str]] = { - 'fld1': ('username', 'platform'), - 'fld2': ('source', 'srcip', 'browser'), - 'fld3': ('destionation', 'dstip'), - 'fld4': ('uniqueid',), + 'fld1': ('username', 'platform', 'duration'), + 'fld2': ('source', 'srcip', 'browser', 'sent'), + 'fld3': ('destination', 'dstip', 'received'), + 'fld4': ('uniqueid', 'tunnel'), } REVERSE_FLDS_EQUIV: typing.Mapping[str, str] = { diff --git a/server/src/uds/core/osmanagers/osmanager.py b/server/src/uds/core/osmanagers/osmanager.py index 98afdeb94..05457330e 100644 --- a/server/src/uds/core/osmanagers/osmanager.py +++ b/server/src/uds/core/osmanagers/osmanager.py @@ -46,6 +46,7 @@ if typing.TYPE_CHECKING: from uds.models.user_service import UserService from uds.core.environment import Environment + class OSManager(Module): """ An OS Manager is responsible for communication the service the different actions to take (i.e. adding a windows machine to a domain) @@ -54,6 +55,7 @@ class OSManager(Module): Server will iterate thought them and look for an identifier associated with the service. This list is a comma separated values (i.e. AA:BB:CC:DD:EE:FF,00:11:22:...) Remember also that we inherit the test and check methods from BaseModule """ + # Service informational related data typeName = _('Base OS Manager') typeType = 'osmanager' @@ -95,7 +97,13 @@ class OSManager(Module): @return nothing """ - def process(self, userService: 'UserService', message: str, data: typing.Any, options: typing.Optional[typing.Dict[str, typing.Any]] = None) -> str: + def process( + self, + userService: 'UserService', + message: str, + data: typing.Any, + options: typing.Optional[typing.Dict[str, typing.Any]] = None, + ) -> str: """ @param userService: Service that sends the request (virtual machine or whatever) @param message: message to process (os manager dependent) @@ -108,7 +116,9 @@ class OSManager(Module): return '' # These methods must be overriden - def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]: + def actorData( + self, userService: 'UserService' + ) -> typing.MutableMapping[str, typing.Any]: """ This method provides information to actor, so actor can complete os configuration. Currently exists 3 types of os managers actions @@ -182,7 +192,9 @@ class OSManager(Module): """ return hash(cls.processUserPassword) != hash(OSManager.processUserPassword) - def processUserPassword(self, userService: 'UserService', username: str, password: str) -> typing.Tuple[str, str]: + def processUserPassword( + self, userService: 'UserService', username: str, password: str + ) -> typing.Tuple[str, str]: """ This will be invoked prior to passsing username/password to Transport. @@ -214,7 +226,9 @@ class OSManager(Module): userService.setProperty('loginsCounter', '0') @staticmethod - def loggedIn(userService: 'UserService', userName: typing.Optional[str] = None) -> None: + def loggedIn( + userService: 'UserService', userName: typing.Optional[str] = None + ) -> None: """ This method: - Add log in event to stats @@ -229,24 +243,49 @@ class OSManager(Module): serviceIp = userServiceInstance.getIp() - fullUserName = userService.user.manager.name + '\\' + userService.user.name if userService.user else 'unknown' + fullUserName = userService.user.pretty_name if userService.user else 'unknown' knownUserIP = userService.src_ip + ':' + userService.src_hostname knownUserIP = knownUserIP if knownUserIP != ':' else 'unknown' userName = userName or 'unknown' - addEvent(userService.deployed_service, ET_LOGIN, fld1=userName, fld2=knownUserIP, fld3=serviceIp, fld4=fullUserName) + addEvent( + userService.deployed_service, + ET_LOGIN, + fld1=userName, + fld2=knownUserIP, + fld3=serviceIp, + fld4=fullUserName, + ) - log.doLog(userService, log.INFO, "User {0} has logged in".format(userName), log.OSMANAGER) + log.doLog( + userService, + log.INFO, + "User {0} has logged in".format(userName), + log.OSMANAGER, + ) - log.useLog('login', uniqueId, serviceIp, userName, knownUserIP, fullUserName, userService.friendly_name, userService.deployed_service.name) + log.useLog( + 'login', + uniqueId, + serviceIp, + userName, + knownUserIP, + fullUserName, + userService.friendly_name, + userService.deployed_service.name, + ) - counter = int(typing.cast(str, userService.getProperty('loginsCounter', '0'))) + 1 + counter = ( + int(typing.cast(str, userService.getProperty('loginsCounter', '0'))) + 1 + ) userService.setProperty('loginsCounter', str(counter)) @staticmethod - def loggedOut(userService: 'UserService', userName: typing.Optional[str] = None) -> None: + def loggedOut( + userService: 'UserService', userName: typing.Optional[str] = None + ) -> None: """ This method: - Add log in event to stats @@ -269,25 +308,48 @@ class OSManager(Module): serviceIp = userServiceInstance.getIp() - fullUserName = 'unknown' - if userService.user: - fullUserName = userService.user.manager.name + '\\' + userService.user.name + fullUserName = userService.user.pretty_name if userService.user else 'unknown' knownUserIP = userService.src_ip + ':' + userService.src_hostname knownUserIP = knownUserIP if knownUserIP != ':' else 'unknown' userName = userName or 'unknown' - addEvent(userService.deployed_service, ET_LOGOUT, fld1=userName, fld2=knownUserIP, fld3=serviceIp, fld4=fullUserName) + addEvent( + userService.deployed_service, + ET_LOGOUT, + fld1=userName, + fld2=knownUserIP, + fld3=serviceIp, + fld4=fullUserName, + ) - log.doLog(userService, log.INFO, "User {0} has logged out".format(userName), log.OSMANAGER) + log.doLog( + userService, + log.INFO, + "User {0} has logged out".format(userName), + log.OSMANAGER, + ) - log.useLog('logout', uniqueId, serviceIp, userName, knownUserIP, fullUserName, userService.friendly_name, userService.deployed_service.name) + log.useLog( + 'logout', + uniqueId, + serviceIp, + userName, + knownUserIP, + fullUserName, + userService.friendly_name, + userService.deployed_service.name, + ) - def loginNotified(self, userService: 'UserService', userName: typing.Optional[str] = None) -> None: + def loginNotified( + self, userService: 'UserService', userName: typing.Optional[str] = None + ) -> None: OSManager.loggedIn(userService, userName) - def logoutNotified(self, userService: 'UserService', userName: typing.Optional[str] = None) -> None: + def logoutNotified( + self, userService: 'UserService', userName: typing.Optional[str] = None + ) -> None: OSManager.loggedOut(userService, userName) def readyNotified(self, userService: 'UserService') -> None: diff --git a/server/src/uds/core/util/stats/events.py b/server/src/uds/core/util/stats/events.py index c5595d973..2fb989d41 100644 --- a/server/src/uds/core/util/stats/events.py +++ b/server/src/uds/core/util/stats/events.py @@ -57,10 +57,9 @@ if typing.TYPE_CHECKING: ET_CACHE_MISS, # Platforms detected ET_PLATFORM, - # Plugin downloads - ET_PLUGIN_DOWNLOAD, + # Tunnel ET_TUNNEL_OPEN, - ET_TUNNEL_CLOSE + ET_TUNNEL_CLOSE, ) = range(9) ( @@ -77,6 +76,46 @@ __transDict: typing.Mapping[typing.Type['Model'], int] = { Authenticator: OT_AUTHENTICATOR, } +# Events data (fld1, fld2, fld3, fld4): +# ET_LOGIN --> on Authenticator +# (username, srcip) +# Note: Generated on user login on UDS web +# +# ET_PLATFORM --> on Authenticator +# (platform, browser, version) +# Note: Generated on user login on UDS web +# +# ET_LOGOUT --> on Authenticator +# (username, srcip) +# Note: Generated on user logout on UDS web. Not generated if browser is closed or session discards. +# +# ET_CACHE_HIT --> On UserService +# (usableServicesInCacheL1,) +# Note: Generated on assigning from cache to user +# +# ET_CACHE_MISS --> On UserService +# (preparingServicesInCacheL1,) +# Note: Generated on missed assigning from cache to user +# +# ET_ACCESS -> On ServicePool +# (username, srcpi, dstip, userService_uuid) +# Note: Generated on user access to service in UDS (that is, clicked on an service) +# +# ET_LOGIN -> On ServicePool +# (username, knownUserIp, serviceIp, fullUserName) +# Note: Generated by OsManager (that is, services without os manager will not trigger this) +# +# ET_LOGOUT -> On ServicePool +# (username, knownUserIp, serviceIp, fullUserName) +# Note: Generated by OsManager (that is, services without os manager will not trigger this) +# +# OT_TUNNEL_OPEN: -> On ServicePool +# (username, srcip, dstip, tunnel_id) +# Note: For HTML5, scrip = "source" string indicating tunnel type (HTML5-{RDP,RDP,VNC}) +# +# OT_TUNNEL_CLOSE: -> On ServicePool +# (duration, sent, received, tunnel_id) + def addEvent(obj: EventClass, eventType: int, **kwargs) -> bool: """ diff --git a/server/src/uds/dispatchers/guacamole/views.py b/server/src/uds/dispatchers/guacamole/views.py index b3fc0bb2b..ca21d903f 100644 --- a/server/src/uds/dispatchers/guacamole/views.py +++ b/server/src/uds/dispatchers/guacamole/views.py @@ -82,7 +82,7 @@ def guacamole(request: ExtendedHttpRequestWithUser, tunnelId: str) -> HttpRespon userService.deployed_service, events.ET_TUNNEL_OPEN, username=userService.user.pretty_name, - source='HTML5 ' + protocol, # On HTML5, currently src is not provided by Guacamole + source='HTML5-' + protocol, # On HTML5, currently src is not provided by Guacamole dstip=host, uniqueid=userService.unique_id, )