diff --git a/actor/src/UDSActorConfig.py b/actor/src/UDSActorConfig.py index 87bc8bbd..6b7a250f 100644 --- a/actor/src/UDSActorConfig.py +++ b/actor/src/UDSActorConfig.py @@ -88,9 +88,11 @@ class UDSConfigDialog(QDialog): auth: udsactor.types.AuthenticatorType for auth in self.api.enumerateAuthenticators(): self.ui.authenticators.addItem(auth.auth, userData=auth) + # Last, add "admin" authenticator (for uds root user) + self.ui.authenticators.addItem('Administration', userData=udsactor.types.AuthenticatorType('admin', 'admin', 'admin', 'admin', 1, False)) def textChanged(self): - enableButtons = self.ui.host.text() != '' and self.ui.username.text() != '' and self.ui.password.text() != '' + enableButtons = bool(self.ui.host.text() and self.ui.username.text() and self.ui.password.text() and self.ui.authenticators.currentText()) self.ui.registerButton.setEnabled(enableButtons) def finish(self): @@ -100,18 +102,24 @@ class UDSConfigDialog(QDialog): # Get network card. Will fail if no network card is available, but don't mind (not contempled) data: udsactor.types.InterfaceInfo = next(udsactor.operations.getNetworkInfo()) - key = self.api.register( - self.ui.authenticators.currentData().auth, - self.ui.username.text(), - self.ui.password.text(), - data.ip or '', # IP - data.mac or '', # MAC - self.ui.preCommand.text(), - self.ui.runonceCommand.text(), - self.ui.postConfigCommand.text() - ) + try: + key = self.api.register( + self.ui.authenticators.currentData().auth, + self.ui.username.text(), + self.ui.password.text(), + data.ip or '', # IP + data.mac or '', # MAC + self.ui.preCommand.text(), + self.ui.runonceCommand.text(), + self.ui.postConfigCommand.text(), + (self.ui.logLevelComboBox.currentIndex() + 1) * 10000 # Loglevel + ) + # Store parameters on register for later use, notify user of registration - print(key) + # Inform the user + QMessageBox.information(self, 'UDS Registration', 'Registration with UDS completed.', QMessageBox.Ok) + except udsactor.rest.RESTError as e: + QMessageBox.critical(self, 'UDS Registration', 'UDS Registration error: {}'.format(e), QMessageBox.Ok) if __name__ == "__main__": diff --git a/actor/src/designer/setup-dialog.ui b/actor/src/designer/setup-dialog.ui index 687be286..e9ce3664 100644 --- a/actor/src/designer/setup-dialog.ui +++ b/actor/src/designer/setup-dialog.ui @@ -579,6 +579,22 @@ + + authenticators + currentTextChanged(QString) + UdsActorSetupDialog + textChanged() + + + 343 + 137 + + + 294 + 153 + + + textChanged() diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 7105771e..d2ff9b32 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -125,7 +125,7 @@ class REST: pass - def register(self, auth: str, username: str, password: str, ip: str, mac: str, preCommand: str, runOnceCommand: str, postCommand: str) -> str: + def register(self, auth: str, username: str, password: str, ip: str, mac: str, preCommand: str, runOnceCommand: str, postCommand: str, logLevel: int) -> str: """ Raises an exception if could not register, or registers and returns the "authorization token" """ @@ -133,20 +133,28 @@ class REST: 'username': username, 'ip': ip, 'mac': mac, - 'preCommand': preCommand, - 'runOnceCommand': runOnceCommand, - 'postCommand': postCommand + 'pre_command': preCommand, + 'run_once_command': runOnceCommand, + 'post_command': postCommand, + 'log_level': logLevel } try: # First, try to login authInfo = {'auth': auth, 'username': username, 'password': password } headers = self.headers result = requests.post(self.url + 'auth/login', data=json.dumps(authInfo), headers=headers, verify=self.validateCert) + if not result.ok or result.json()['result'] == 'error': + raise Exception() # Invalid credentials + except (requests.ConnectionError, requests.ConnectTimeout) as e: + raise RESTConnectionError(str(e)) + except Exception as e: + raise RESTError('Invalid credentials') + + try: + headers['X-Auth-Token'] = result.json()['token'] + result = requests.post(self.url + 'actor/v2/register', data=json.dumps(data), headers=headers, verify=self.validateCert) if result.ok: - headers['X-Auth-Token'] = result.json()['token'] - result = requests.post(self.url + 'actor/v2/register', data=json.dumps(data), headers=headers, verify=self.validateCert) - if result.ok: - return result.json()['result'] + return result.json()['result'] except (requests.ConnectionError, requests.ConnectTimeout) as e: raise RESTConnectionError(str(e)) except Exception as e: diff --git a/actor/src/ui/setup_dialog_ui.py b/actor/src/ui/setup_dialog_ui.py index a8192360..29c56494 100644 --- a/actor/src/ui/setup_dialog_ui.py +++ b/actor/src/ui/setup_dialog_ui.py @@ -193,6 +193,7 @@ class Ui_UdsActorSetupDialog(object): self.browsePostConfigButton.clicked.connect(UdsActorSetupDialog.browsePostConfig) self.browseRunOnceButton.clicked.connect(UdsActorSetupDialog.browseRunOnce) self.host.editingFinished.connect(UdsActorSetupDialog.updateAuthenticators) + self.authenticators.currentTextChanged['QString'].connect(UdsActorSetupDialog.textChanged) QtCore.QMetaObject.connectSlotsByName(UdsActorSetupDialog) def retranslateUi(self, UdsActorSetupDialog): diff --git a/server/src/uds/REST/methods/actor_v2.py b/server/src/uds/REST/methods/actor_v2.py index d4618eb2..513e1da6 100644 --- a/server/src/uds/REST/methods/actor_v2.py +++ b/server/src/uds/REST/methods/actor_v2.py @@ -30,12 +30,12 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import secrets import logging import typing -from django.utils.translation import ugettext as _ +from uds.models import getSqlDatetimeAsUnix, getSqlDatetime, ActorToken -from uds.models.util import getSqlDatetimeAsUnix from uds.core import VERSION from ..handlers import Handler @@ -72,8 +72,6 @@ class ActorV2Action(Handler): def get(self): return actorResult('') - - class ActorV2Register(ActorV2Action): """ Registers an actor @@ -81,9 +79,34 @@ class ActorV2Register(ActorV2Action): authenticated = True name = 'register' - def post(self): - logger.debug('Args: %s, Params: %s', self._args, self._params) - return actorResult('ok') + def post(self) -> typing.MutableMapping[str, typing.Any]: + actorToken: ActorToken + try: + # If already exists a token for this MAC, return it instead of creating a new one, and update the information... + actorToken = ActorToken.objects.get(mac=self._params['mac']) + # Update parameters + actorToken.ip_from = self._request.ip + actorToken.ip = self._params['ip'] + actorToken.pre_command = self._params['pre_command'] + actorToken.post_command = self._params['post_command'] + actorToken.runonce_command = self._params['run_once_command'] + actorToken.log_level = self._params['log_level'] + actorToken.stamp = getSqlDatetime() + actorToken.save() + except Exception: + actorToken = ActorToken.objects.create( + username=self._params['username'], + ip_from=self._request.ip, + ip=self._params['ip'], + mac=self._params['mac'], + pre_command=self._params['pre_command'], + post_command=self._params['post_command'], + runonce_command=self._params['run_once_command'], + log_level=self._params['log_level'], + token=secrets.token_urlsafe(36), + stamp=getSqlDatetime() + ) + return actorResult(actorToken.token) class ActorV2Initiialize(ActorV2Action): """ diff --git a/server/src/uds/models/__init__.py b/server/src/uds/models/__init__.py index 054741b3..106fa703 100644 --- a/server/src/uds/models/__init__.py +++ b/server/src/uds/models/__init__.py @@ -112,4 +112,7 @@ from .tag import Tag # Utility from .dbfile import DBFile +# Actor tokens +from .actor_token import ActorToken + logger = logging.getLogger(__name__) diff --git a/server/src/uds/web/forms/LoginForm.py b/server/src/uds/web/forms/LoginForm.py index 2ee133dd..4825a48f 100644 --- a/server/src/uds/web/forms/LoginForm.py +++ b/server/src/uds/web/forms/LoginForm.py @@ -42,6 +42,7 @@ class LoginForm(forms.Form): user = forms.CharField(label=_('Username'), max_length=64, widget=forms.TextInput()) password = forms.CharField(label=_('Password'), widget=forms.PasswordInput(attrs={'title': _('Password')}), required=False) authenticator = forms.ChoiceField(label=_('Authenticator'), choices=(), required=False) + logouturl = forms.CharField(widget=forms.HiddenInput(), required=False) def __init__(self, *args, **kwargs): # If an specified login is passed in, retrieve it & remove it from kwargs dict