diff --git a/actor b/actor index 93e46e3d1..e6b59cf80 160000 --- a/actor +++ b/actor @@ -1 +1 @@ -Subproject commit 93e46e3d14a6e47ef53736573ae79abcd46fee31 +Subproject commit e6b59cf809202b0a9d26ee14abfd19fab13d4817 diff --git a/server/src/uds/REST/handlers.py b/server/src/uds/REST/handlers.py index 4cc817f1f..1941035d5 100644 --- a/server/src/uds/REST/handlers.py +++ b/server/src/uds/REST/handlers.py @@ -86,7 +86,7 @@ class Handler: # These are the "path" split by /, that is, the REST invocation arguments _args: list[str] _kwargs: dict[str, typing.Any] # This are the "path" split by /, that is, the REST invocation arguments - _headers: dict[str, str] + _headers: dict[str, str] # Note: These are "output" headers, not input headers (input headers can be retrieved from request) _session: typing.Optional[SessionStore] _auth_token: typing.Optional[str] _user: 'User' diff --git a/server/src/uds/REST/methods/actor_token.py b/server/src/uds/REST/methods/actor_token.py index 1e95168b1..181b3c96a 100644 --- a/server/src/uds/REST/methods/actor_token.py +++ b/server/src/uds/REST/methods/actor_token.py @@ -61,21 +61,22 @@ class ActorTokens(ModelHandler): {'stamp': {'title': _('Date'), 'type': 'datetime'}}, {'username': {'title': _('Issued by')}}, {'host': {'title': _('Origin')}}, + {'version': {'title': _('Version')}}, {'hostname': {'title': _('Hostname')}}, {'pre_command': {'title': _('Pre-connect')}}, {'post_command': {'title': _('Post-Configure')}}, - {'runonce_command': {'title': _('Run Once')}}, + {'run_once_command': {'title': _('Run Once')}}, {'log_level': {'title': _('Log level')}}, + {'os': {'title': _('OS')}}, ] def item_as_dict(self, item: 'Model') -> dict[str, typing.Any]: item = ensure.is_instance(item, Server) data: dict[str, typing.Any] = item.data or {} - log_level_int = data.get('log_level', 2) - if log_level_int < 10000: # Old log level, from actor, etc.. - log_level = LogLevel.from_actor_level(log_level_int).name + if item.log_level < 10000: # Old log level, from actor, etc.. + log_level = LogLevel.from_actor_level(item.log_level).name else: - log_level = LogLevel(log_level_int).name + log_level = LogLevel(item.log_level).name return { 'id': item.token, 'name': str(_('Token isued by {} from {}')).format(item.register_username, item.hostname or item.ip), @@ -84,10 +85,12 @@ class ActorTokens(ModelHandler): 'ip': item.ip, 'host': f'{item.ip} - {data.get("mac")}', 'hostname': item.hostname, + 'version': item.version, 'pre_command': data.get('pre_command', ''), 'post_command': data.get('post_command', ''), - 'runonce_command': data.get('runonce_command', ''), + 'run_once_command': data.get('run_once_command', ''), 'log_level': log_level, + 'os': item.os_type, } def delete(self) -> str: diff --git a/server/src/uds/REST/methods/actor_v3.py b/server/src/uds/REST/methods/actor_v3.py index 8781048af..bbf0fa41b 100644 --- a/server/src/uds/REST/methods/actor_v3.py +++ b/server/src/uds/REST/methods/actor_v3.py @@ -34,6 +34,7 @@ import logging import time import typing import collections.abc +import re from django.conf import settings @@ -277,48 +278,54 @@ class Register(ActorV3Action): actor_token: typing.Optional[Server] = Server.objects.filter( type=types.servers.ServerType.ACTOR, mac=self._params['mac'] ).first() + + # Try to get version from headers (USer-Agent), should be something like (UDS Actor v(.+)) + user_agent = self._request.headers.get('User-Agent', '') + match = re.search(r'UDS Actor v(.+)', user_agent) + if match: + self._params['version'] = self._params.get('version', match.group(1)) # override version if not provided # Actors does not support any SERVER API version in fact, they has their own interfaces on UserServices # This means that we can invoke its API from user_service, but not from server (The actor token is transformed as soon as initialized to a user service token) + data = { + 'pre_command': self._params['pre_command'], + 'post_command': self._params['post_command'], + 'run_once_command': self._params['run_once_command'], + 'custom': self._params.get('custom', ''), + } if actor_token: # Update parameters + # type is already set + actor_token.subtype = self._params.get('subtype', '') + actor_token.version = self._params.get('version', '') actor_token.register_username = self._user.pretty_name actor_token.register_ip = self._request.ip actor_token.ip = self._params['ip'] actor_token.hostname = self._params['hostname'] actor_token.log_level = self._params['log_level'] - actor_token.subtype = self._params.get('version', '') - actor_token.data = typing.cast(typing.Any, { # Cast due to mypy complains on this assignment - 'pre_command': self._params['pre_command'], - 'post_command': self._params['post_command'], - 'run_once_command': self._params['run_once_command'], - 'custom': self._params.get('custom', ''), - }) + actor_token.data = data actor_token.stamp = sql_now() + actor_token.os_type = self._params.get('os', types.os.KnownOS.UNKNOWN.os_name())[:31] + # Mac is already set, as type was used to locate it actor_token.save() logger.info('Registered actor %s', self._params) found = True if not found: kwargs = { + 'type': types.servers.ServerType.ACTOR, + 'subtype': self._params.get('subtype', ''), + 'version': self._params.get('version', ''), 'register_username': self._user.pretty_name, 'register_ip': self._request.ip, 'ip': self._params['ip'], 'hostname': self._params['hostname'], 'log_level': self._params['log_level'], - 'data': { - 'pre_command': self._params['pre_command'], - 'post_command': self._params['post_command'], - 'run_once_command': self._params['run_once_command'], - 'custom': self._params.get('custom', ''), - }, + 'data': data, # 'token': Server.create_token(), # Not needed, defaults to create_token - 'type': types.servers.ServerType.ACTOR, - 'subtype': self._params.get('version', ''), - 'version': '', + 'stamp': sql_now(), 'os_type': self._params.get('os', types.os.KnownOS.UNKNOWN.os_name()), 'mac': self._params['mac'], - 'stamp': sql_now(), } actor_token = Server.objects.create(**kwargs)