diff --git a/actor/src/UDSActorConfig.py b/actor/src/UDSActorConfig.py index 4f83b63f..2c014388 100644 --- a/actor/src/UDSActorConfig.py +++ b/actor/src/UDSActorConfig.py @@ -29,52 +29,76 @@ ''' @author: Adolfo Gómez, dkmaster at dkmon dot com ''' +# pylint: disable=invalid-name import sys import os import logging +import typing import PyQt5 # pylint: disable=unused-import -from PyQt5.QtWidgets import QApplication, QDialog +from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog + +import udsactor from ui.setup_dialog_ui import Ui_UdsActorSetupDialog -# pylint: disable=invalid-name +# Not imported at runtime, just for type checking +if typing.TYPE_CHECKING: + from PyQt5.QtWidgets import QLineEdit # pylint: disable=ungrouped-imports logger = logging.getLogger('actor') class UDSConfigDialog(QDialog): - def __init__(self, data=None, parent=None): - QDialog.__init__(self, parent) + def __init__(self): + QDialog.__init__(self, None) self.ui = Ui_UdsActorSetupDialog() self.ui.setupUi(self) - if data is not None: - pass + self.ui.host.setText('172.27.0.1:8443') + self.ui.username.setText('admin') + self.ui.password.setText('temporal') + self.ui.postConfigCommand.setText(r'c:\windows\post-uds.bat') + self.ui.preCommand.setText(r'c:\windows\pre-uds.bat') + self.ui.runonceCommand.setText(r'c:\windows\runonce.bat') - def _getCfg(self): - return { - 'host': self.ui.host.text(), - 'username': self.ui.username.text(), - 'password': self.ui.password.text(), - 'validateCertificate': self.ui.validateCertificate.currentIndex() == 1, - 'logLevel': (self.ui.logLevelComboBox.currentIndex() + 1) * 10000 - } + def browse(self, lineEdit: 'QLineEdit', caption: str) -> None: + name = QFileDialog.getOpenFileName(parent=self, caption='')[0] # Returns tuple (filename, filter) + if name: + lineEdit.setText(name) + + def browsePreconnect(self) -> None: + self.browse(self.ui.preCommand, 'Select Preconnect command') + + def browseRunOnce(self) -> None: + self.browse(self.ui.runonceCommand, 'Select Runonce command') + + def browsePostConfig(self) -> None: + self.browse(self.ui.postConfigCommand, 'Select Postconfig command') def textChanged(self): enableButtons = self.ui.host.text() != '' and self.ui.username.text() != '' and self.ui.password.text() != '' - self.ui.testButton.setEnabled(enableButtons) - self.ui.saveButton.setEnabled(enableButtons) + self.ui.registerButton.setEnabled(enableButtons) - def cancelAndDiscard(self): - logger.debug('Cancelling changes') + def finish(self): self.close() - def testParameters(self): - pass + def registerWithUDS(self): + api = udsactor.rest.REST(self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1) - def acceptAndSave(self): - pass + data: udsactor.types.InterfaceInfo = next(udsactor.operations.getNetworkInfo()) + + key = api.register( + self.ui.username.text(), + self.ui.password.text(), + data.ip or '', # IP + data.mac or '', # first mac + self.ui.preCommand.text(), + self.ui.runonceCommand.text(), + self.ui.postConfigCommand.text() + ) + + print(key) if __name__ == "__main__": diff --git a/actor/src/designer/setup-dialog.ui b/actor/src/designer/setup-dialog.ui index 0a002feb..d1663765 100644 --- a/actor/src/designer/setup-dialog.ui +++ b/actor/src/designer/setup-dialog.ui @@ -10,8 +10,8 @@ 0 0 - 400 - 293 + 590 + 253 @@ -26,8 +26,11 @@ 9 + + Qt::DefaultContextMenu + - UDS Actor Setup + UDS Actor Configuration Tool @@ -45,18 +48,24 @@ true - + false - 20 + 10 220 - 361 + 181 23 + + + 181 + 0 + + Click to test the selecter parameters @@ -64,18 +73,15 @@ <html><head/><body><p>Click on this button to test the server host and master key parameters.</p><p>A window will be displayed with results after the test is executed.</p><p><br/></p><p>This button will only be active if all parameters are filled.</p></body></html> - Test parameters + Register with UDS - - - false - + - 20 - 250 - 101 + 410 + 220 + 171 23 @@ -85,30 +91,11 @@ 0 - - Accepts changes and saves them - - - Clicking on this button will accept all changes and save them, closing the configuration window - - - Accept && Save - - - - - - 260 - 250 - 121 - 23 - - - - - 0 - 0 - + + + 171 + 0 + Cancel all changes and discard them @@ -117,147 +104,296 @@ Discards all changes and closes the configuration window - Cancel && Discard + Close - + - 20 - 20 - 361 - 191 + 10 + 10 + 571 + 201 - - - QFormLayout::AllNonFixedFieldsGrow - - - 16 - - - - - UDS Server + + 0 + + + + UDS Server + + + + + 10 + 10 + 551 + 151 + + + + + QFormLayout::AllNonFixedFieldsGrow - - - - - - false + + 16 - - Uds Broker Server Addres. Use IP or FQDN - - - Enter here the UDS Broker Addres using either its IP address or its FQDN address - - - - - - - Username - - - - - - - UDS user with administration rights (Will not be stored on template) - - - <html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html> - - - - - - - Password for user (Will not be stored on template) - - - <html><head/><body><p>Administrator password for the user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html> - - - QLineEdit::Password - - - - - - - Password - - - - - - - Security - - - - - - - Select communication security with broker - - - <html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=" font-weight:600;">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html> - - - - Ignore certificate - + + + + UDS Server + + - - - Verify certificate - + + + + false + + + Uds Broker Server Addres. Use IP or FQDN + + + Enter here the UDS Broker Addres using either its IP address or its FQDN address + + - - - - - - 1 + + + + Username + + + + + + + UDS user with administration rights (Will not be stored on template) + + + <html><head/><body><p>Administrator user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html> + + + + + + + Password + + + + + + + Password for user (Will not be stored on template) + + + <html><head/><body><p>Administrator password for the user on UDS Server.</p><p>Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.</p></body></html> + + + QLineEdit::Password + + + + + + + Security + + + + + + + Select communication security with broker + + + <html><head/><body><p>Select the security for communications with UDS Broker.</p><p>The recommended method of communication is <span style=" font-weight:600;">Use SSL</span>, but selection needs to be acording to your broker configuration.</p></body></html> + + + + Ignore certificate + + + + + Verify certificate + + + + + + + + + + Advanced + + + + + 10 + 10 + 551 + 151 + + + + + QFormLayout::AllNonFixedFieldsGrow - - true + + 16 - - - DEBUG - + + + + Preconnect + + - - - INFO - + + + + 4 + + + 0 + + + + + false + + + Pre connection command. Executed just before the user is connected to machine. + + + + + + + + + + Browse + + + + - - - ERROR - + + + + Runonce + + - - - FATAL - + + + + 4 + + + 0 + + + + + Run once command. Executed on first boot, just before UDS does anything. + + + + + + + + + + Browse + + + + - - - - - - Log Level - - - - + + + + Postconfig + + + + + + + 4 + + + 0 + + + + + Command to execute after UDS finalizes the VM configuration. + + + + + + QLineEdit::Normal + + + + + + + Browse + + + + + + + + + Log Level + + + + + + + 1 + + + true + + + + DEBUG + + + + + INFO + + + + + ERROR + + + + + FATAL + + + + + + + @@ -265,10 +401,10 @@ - cancelButton - pressed() + closeButton + clicked() UdsActorSetupDialog - cancelAndDiscard() + finish() 315 @@ -281,10 +417,10 @@ - testButton - pressed() + registerButton + clicked() UdsActorSetupDialog - testParameters() + registerWithUDS() 239 @@ -296,22 +432,6 @@ - - saveButton - pressed() - UdsActorSetupDialog - acceptAndSave() - - - 71 - 165 - - - 124 - 181 - - - host textChanged(QString) @@ -319,12 +439,12 @@ textChanged() - 237 - 32 + 239 + 59 199 - 146 + 150 @@ -335,12 +455,12 @@ textChanged() - 237 - 71 + 239 + 98 199 - 146 + 150 @@ -351,20 +471,70 @@ textChanged() - 237 - 110 + 239 + 137 199 - 146 + 150 + + + + + browsePreconnectButton + clicked() + UdsActorSetupDialog + browsePreconnect() + + + 430 + 60 + + + 243 + 150 + + + + + browsePostConfigButton + clicked() + UdsActorSetupDialog + browsePostConfig() + + + 430 + 142 + + + 243 + 150 + + + + + browseRunOnceButton + clicked() + UdsActorSetupDialog + browseRunOnce() + + + 430 + 101 + + + 243 + 150 textChanged() - cancelAndDiscard() - testParameters() - acceptAndSave() + finish() + registerWithUDS() + browsePreconnect() + browseRunOnce() + browsePostConfig() diff --git a/actor/src/udsactor/__init__.py b/actor/src/udsactor/__init__.py index 445bb472..f4fca977 100644 --- a/actor/src/udsactor/__init__.py +++ b/actor/src/udsactor/__init__.py @@ -25,11 +25,21 @@ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ''' @author: Adolfo Gómez, dkmaster at dkmon dot com ''' -VERSION = '3.0.0' +import sys + +from . import types +from . import rest + +if sys.platform == 'win32': + from .windows import operations +else: + from .linux import operations + +from .info import VERSION + __title__ = 'udsactor' __version__ = VERSION @@ -37,3 +47,4 @@ __build__ = 0x010756 __author__ = 'Adolfo Gómez ' __license__ = "BSD 3-clause" __copyright__ = "Copyright 2014-2019 VirtualCable S.L.U." + diff --git a/actor/src/udsactor/info.py b/actor/src/udsactor/info.py new file mode 100644 index 00000000..aaa42644 --- /dev/null +++ b/actor/src/udsactor/info.py @@ -0,0 +1 @@ +VERSION = '3.0.0' diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index e29127d5..bbc97164 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014 Virtual Cable S.L. +# Copyright (c) 2014-2019 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -29,8 +29,6 @@ ''' @author: Adolfo Gómez, dkmaster at dkmon dot com ''' -from __future__ import unicode_literals - import socket import platform import fcntl @@ -40,49 +38,45 @@ import ctypes.util import subprocess import struct import array -import six -from udsactor import utils +import typing + +from udsactor import types + from .renamer import rename -def _getMacAddr(ifname): +def _getMacAddr(ifname: str) -> typing.Optional[str]: ''' Returns the mac address of an interface Mac is returned as unicode utf-8 encoded ''' - if isinstance(ifname, list): - return dict([(name, _getMacAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) + ifnameBytes = ifname.encode('utf-8') # If unicode, convert to bytes try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifname[:15]))) - return six.text_type(''.join(['%02x:' % char for char in info[18:24]])[:-1]).upper() + info = bytearray(fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifnameBytes[:15]))) + return str(''.join(['%02x:' % char for char in info[18:24]])[:-1]).upper() except Exception: return None -def _getIpAddr(ifname): +def _getIpAddr(ifname: str) -> typing.Optional[str]: ''' Returns the ip address of an interface Ip is returned as unicode utf-8 encoded ''' - if isinstance(ifname, list): - return dict([(name, _getIpAddr(name)) for name in ifname]) - if isinstance(ifname, six.text_type): - ifname = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) + ifnameBytes = ifname.encode('utf-8') # If unicode, convert to bytes (or str in python 2.7) try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return six.text_type(socket.inet_ntoa(fcntl.ioctl( + return str(socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR - struct.pack(str('256s'), ifname[:15]) + struct.pack(str('256s'), ifnameBytes[:15]) )[20:24])) except Exception: return None -def _getInterfaces(): +def _getInterfaces() -> typing.List[str]: ''' Returns a list of interfaces names coded in utf-8 ''' @@ -107,76 +101,68 @@ def _getInterfaces(): return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)] -def _getIpAndMac(ifname): +def _getIpAndMac(ifname: str) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]: ip, mac = _getIpAddr(ifname), _getMacAddr(ifname) return (ip, mac) -def getComputerName(): +def getComputerName() -> str: ''' Returns computer name, with no domain ''' return socket.gethostname().split('.')[0] -def getNetworkInfo(): +def getNetworkInfo() -> typing.Iterable[types.InterfaceInfo]: for ifname in _getInterfaces(): ip, mac = _getIpAndMac(ifname) - if mac != '00:00:00:00:00:00' and ip.startswith('169.254') is False: # Skips local interfaces & interfaces with no dhcp IPs - yield utils.Bunch(name=ifname, mac=mac, ip=ip) + if mac != '00:00:00:00:00:00' and mac and ip and ip.startswith('169.254') is False: # Skips local interfaces & interfaces with no dhcp IPs + yield types.InterfaceInfo(name=ifname, mac=mac, ip=ip) -def getDomainName(): +def getDomainName() -> str: return '' -def getLinuxVersion(): - lv = platform.linux_distribution() +def getLinuxVersion() -> str: + import distro + + lv = distro.linux_distribution() return lv[0] + ', ' + lv[1] -def reboot(flags=0): +def reboot(flags: int = 0): ''' Simple reboot using os command ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - subprocess.call(['/sbin/shutdown', 'now', '-r']) -def loggoff(): +def loggoff() -> None: ''' Right now restarts the machine... ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']]) # subprocess.call(['/sbin/shutdown', 'now', '-r']) # subprocess.call(['/usr/bin/systemctl', 'reboot', '-i']) -def renameComputer(newName): +def renameComputer(newName: str) -> None: rename(newName) -def joinDomain(domain, ou, account, password, executeInOneStep=False): +def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False): pass -def changeUserPassword(user, oldPassword, newPassword): +def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None: ''' Simple password change for user using command line ''' os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword)) -class XScreenSaverInfo(ctypes.Structure): +class XScreenSaverInfo(ctypes.Structure): # pylint: disable=too-few-public-methods _fields_ = [('window', ctypes.c_long), ('state', ctypes.c_int), ('kind', ctypes.c_int), @@ -198,26 +184,18 @@ try: xss.XScreenSaverQueryExtension.restype = ctypes.c_int xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure display = xlib.XOpenDisplay(None) - info = xss.XScreenSaverAllocInfo() + xssInfo = xss.XScreenSaverAllocInfo() except Exception: # Libraries not accesible, not found or whatever.. - xlib = xss = display = info = None + xlib = xss = display = xssInfo = None -def initIdleDuration(atLeastSeconds): - ''' - On linux we set the screensaver to at least required seconds, or we never will get "idle" - ''' - # Workaround for dummy thread - if six.PY3 is False: - import threading - threading._DummyThread._Thread__stop = lambda x: 42 - +def initIdleDuration(atLeastSeconds: int) -> None: subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)]) # And now reset it subprocess.call(['/usr/bin/xset', 's', 'reset']) -def getIdleDuration(): +def getIdleDuration() -> float: ''' Returns idle duration, in seconds ''' @@ -232,16 +210,16 @@ def getIdleDuration(): if available != 1: return 0 # No screen saver is available, no way of getting idle - xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), info) + xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), xssInfo) # Centos seems to set state to 1?? (weird, but it's happening don't know why... will try this way) - if info.contents.state != 0 and 'centos' not in platform.linux_distribution()[0].lower().strip(): + if xssInfo.contents.state != 0 and 'centos' not in platform.linux_distribution()[0].lower().strip(): return 3600 * 100 * 1000 # If screen saver is active, return a high enough value - return info.contents.idle / 1000.0 + return xssInfo.contents.idle / 1000.0 -def getCurrentUser(): +def getCurrentUser() -> str: ''' Returns current logged in user ''' diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index e05e8786..14129286 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -37,8 +37,7 @@ import typing import requests -from udsactor import VERSION - +from .info import VERSION from .utils import exceptionToMessage from .log import logger @@ -74,6 +73,7 @@ try: except Exception: pass # In fact, isn't too important, but will log warns to logging file +# Constants def ensureResultIsOk(result: typing.Any) -> None: if 'error' not in result: @@ -92,7 +92,7 @@ class REST: def __init__(self, host: str, validateCert: bool) -> None: self.host = host self.validateCert = validateCert - self.url = "{}://{}/rest/actor/".format(('https', 'https'), self.host) + self.url = "https://{}/uds/rest/actor/v2".format(self.host) # Disable logging requests messages except for errors, ... logging.getLogger("requests").setLevel(logging.CRITICAL) # Tries to disable all warnings @@ -101,6 +101,31 @@ class REST: except Exception: pass + @staticmethod + def headers(): + return {'content-type': 'application/json'} + + def register(self, username: str, password: str, ip: str, mac: str, preCommand: str, runOnceCommand: str, postCommand: str) -> str: + data = { + 'username': username, + 'password': password, + 'ip': ip, + 'mac': mac, + 'preCommand': preCommand, + 'runOnceCommand': runOnceCommand, + 'postCommand': postCommand + } + try: + result: requests.Response = requests.post(self.url + '/register', data=json.dumps(data), headers=REST.headers(), verify=self.validateCert) + if result.ok: + return result.json()['result'] + except (requests.ConnectionError, requests.ConnectTimeout) as e: + raise RESTConnectionError(str(e)) + except Exception as e: + pass + + raise RESTError(result.content) + def _getUrl(self, method, key=None, ids=None): url = self.url + method params = [] @@ -120,7 +145,7 @@ class REST: if data is None: # Old requests version does not support verify, but they do not checks ssl certificate by default if self.newerRequestLib: - r = requests.get(url, verify=VERIFY_CERT) + r = requests.get(url, verify=self.validateCert) else: logger.debug('Requesting with old') r = requests.get(url) # Always ignore certs?? @@ -128,14 +153,14 @@ class REST: if data == '': data = '{"dummy": true}' # Ensures no proxy rewrites POST as GET because body is empty... if self.newerRequestLib: - r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=VERIFY_CERT) + r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=self.validateCert) else: logger.debug('Requesting with old') r = requests.post(url, data=data, headers={'content-type': 'application/json'}) # From versions of requests, content maybe bytes or str. We need str for json.loads content = r.content - if not isinstance(content, six.text_type): + if not isinstance(content, str): content = content.decode('utf8') r = json.loads(content) # Using instead of r.json() to make compatible with oooold rquests lib versions except requests.exceptions.RequestException as e: @@ -184,7 +209,7 @@ class REST: raise ConnectionError('REST api has not been initialized') if processData: - if data and not isinstance(data, six.text_type): + if data and not isinstance(data, str): data = data.decode('utf8') data = json.dumps({'data': data}) url = self._getUrl('/'.join([self.uuid, msg])) diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py new file mode 100644 index 00000000..2e630729 --- /dev/null +++ b/actor/src/udsactor/types.py @@ -0,0 +1,6 @@ +import typing + +class InterfaceInfo(typing.NamedTuple): + name: str + mac: typing.Optional[str] + ip: typing.Optional[str] diff --git a/actor/src/udsactor/windows/operations.py b/actor/src/udsactor/windows/operations.py index eb04653d..18d5fdae 100644 --- a/actor/src/udsactor/windows/operations.py +++ b/actor/src/udsactor/windows/operations.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c) 2014 Virtual Cable S.L. +# Copyright (c) 2014-2019 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -29,13 +29,11 @@ ''' @author: Adolfo Gómez, dkmaster at dkmon dot com ''' -from __future__ import unicode_literals - -import win32com.client # @UnresolvedImport, pylint: disable=import-error -import win32net # @UnresolvedImport, pylint: disable=import-error -import win32security # @UnresolvedImport, pylint: disable=import-error -import win32api # @UnresolvedImport, pylint: disable=import-error -import win32con # @UnresolvedImport, pylint: disable=import-error +import win32com.client +import win32net +import win32security +import win32api +import win32con import ctypes from ctypes.wintypes import DWORD, LPCWSTR import os diff --git a/actor/src/ui/setup_dialog_ui.py b/actor/src/ui/setup_dialog_ui.py index 87cd2f91..4935a3f8 100644 --- a/actor/src/ui/setup_dialog_ui.py +++ b/actor/src/ui/setup_dialog_ui.py @@ -12,7 +12,7 @@ class Ui_UdsActorSetupDialog(object): def setupUi(self, UdsActorSetupDialog): UdsActorSetupDialog.setObjectName("UdsActorSetupDialog") UdsActorSetupDialog.setWindowModality(QtCore.Qt.WindowModal) - UdsActorSetupDialog.resize(400, 293) + UdsActorSetupDialog.resize(590, 253) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) @@ -22,6 +22,7 @@ class Ui_UdsActorSetupDialog(object): font.setFamily("Verdana") font.setPointSize(9) UdsActorSetupDialog.setFont(font) + UdsActorSetupDialog.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(":/img/img/uds-icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) UdsActorSetupDialog.setWindowIcon(icon) @@ -29,29 +30,27 @@ class Ui_UdsActorSetupDialog(object): UdsActorSetupDialog.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)) UdsActorSetupDialog.setSizeGripEnabled(False) UdsActorSetupDialog.setModal(True) - self.testButton = QtWidgets.QPushButton(UdsActorSetupDialog) - self.testButton.setEnabled(False) - self.testButton.setGeometry(QtCore.QRect(20, 220, 361, 23)) - self.testButton.setObjectName("testButton") - self.saveButton = QtWidgets.QPushButton(UdsActorSetupDialog) - self.saveButton.setEnabled(False) - self.saveButton.setGeometry(QtCore.QRect(20, 250, 101, 23)) + self.registerButton = QtWidgets.QPushButton(UdsActorSetupDialog) + self.registerButton.setEnabled(False) + self.registerButton.setGeometry(QtCore.QRect(10, 220, 181, 23)) + self.registerButton.setMinimumSize(QtCore.QSize(181, 0)) + self.registerButton.setObjectName("registerButton") + self.closeButton = QtWidgets.QPushButton(UdsActorSetupDialog) + self.closeButton.setGeometry(QtCore.QRect(410, 220, 171, 23)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.saveButton.sizePolicy().hasHeightForWidth()) - self.saveButton.setSizePolicy(sizePolicy) - self.saveButton.setObjectName("saveButton") - self.cancelButton = QtWidgets.QPushButton(UdsActorSetupDialog) - self.cancelButton.setGeometry(QtCore.QRect(260, 250, 121, 23)) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.cancelButton.sizePolicy().hasHeightForWidth()) - self.cancelButton.setSizePolicy(sizePolicy) - self.cancelButton.setObjectName("cancelButton") - self.layoutWidget = QtWidgets.QWidget(UdsActorSetupDialog) - self.layoutWidget.setGeometry(QtCore.QRect(20, 20, 361, 191)) + sizePolicy.setHeightForWidth(self.closeButton.sizePolicy().hasHeightForWidth()) + self.closeButton.setSizePolicy(sizePolicy) + self.closeButton.setMinimumSize(QtCore.QSize(171, 0)) + self.closeButton.setObjectName("closeButton") + self.tabWidget = QtWidgets.QTabWidget(UdsActorSetupDialog) + self.tabWidget.setGeometry(QtCore.QRect(10, 10, 571, 201)) + self.tabWidget.setObjectName("tabWidget") + self.tab_uds = QtWidgets.QWidget() + self.tab_uds.setObjectName("tab_uds") + self.layoutWidget = QtWidgets.QWidget(self.tab_uds) + self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 551, 151)) self.layoutWidget.setObjectName("layoutWidget") self.formLayout = QtWidgets.QFormLayout(self.layoutWidget) self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow) @@ -71,13 +70,13 @@ class Ui_UdsActorSetupDialog(object): self.username = QtWidgets.QLineEdit(self.layoutWidget) self.username.setObjectName("username") self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.username) + self.label_password = QtWidgets.QLabel(self.layoutWidget) + self.label_password.setObjectName("label_password") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_password) self.password = QtWidgets.QLineEdit(self.layoutWidget) self.password.setEchoMode(QtWidgets.QLineEdit.Password) self.password.setObjectName("password") self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.password) - self.label_password = QtWidgets.QLabel(self.layoutWidget) - self.label_password.setObjectName("label_password") - self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_password) self.label_security = QtWidgets.QLabel(self.layoutWidget) self.label_security.setObjectName("label_security") self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_security) @@ -86,7 +85,68 @@ class Ui_UdsActorSetupDialog(object): self.validateCertificate.addItem("") self.validateCertificate.addItem("") self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.validateCertificate) - self.logLevelComboBox = QtWidgets.QComboBox(self.layoutWidget) + self.tabWidget.addTab(self.tab_uds, "") + self.tab_advanced = QtWidgets.QWidget() + self.tab_advanced.setObjectName("tab_advanced") + self.layoutWidget_2 = QtWidgets.QWidget(self.tab_advanced) + self.layoutWidget_2.setGeometry(QtCore.QRect(10, 10, 551, 151)) + self.layoutWidget_2.setObjectName("layoutWidget_2") + self.formLayout_2 = QtWidgets.QFormLayout(self.layoutWidget_2) + self.formLayout_2.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow) + self.formLayout_2.setContentsMargins(0, 0, 0, 0) + self.formLayout_2.setVerticalSpacing(16) + self.formLayout_2.setObjectName("formLayout_2") + self.label_host_2 = QtWidgets.QLabel(self.layoutWidget_2) + self.label_host_2.setObjectName("label_host_2") + self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_host_2) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout.setSpacing(4) + self.horizontalLayout.setObjectName("horizontalLayout") + self.preCommand = QtWidgets.QLineEdit(self.layoutWidget_2) + self.preCommand.setAcceptDrops(False) + self.preCommand.setWhatsThis("") + self.preCommand.setObjectName("preCommand") + self.horizontalLayout.addWidget(self.preCommand) + self.browsePreconnectButton = QtWidgets.QPushButton(self.layoutWidget_2) + self.browsePreconnectButton.setObjectName("browsePreconnectButton") + self.horizontalLayout.addWidget(self.browsePreconnectButton) + self.formLayout_2.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout) + self.label_username_2 = QtWidgets.QLabel(self.layoutWidget_2) + self.label_username_2.setObjectName("label_username_2") + self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_username_2) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_2.setSpacing(4) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.runonceCommand = QtWidgets.QLineEdit(self.layoutWidget_2) + self.runonceCommand.setWhatsThis("") + self.runonceCommand.setObjectName("runonceCommand") + self.horizontalLayout_2.addWidget(self.runonceCommand) + self.browseRunOnceButton = QtWidgets.QPushButton(self.layoutWidget_2) + self.browseRunOnceButton.setObjectName("browseRunOnceButton") + self.horizontalLayout_2.addWidget(self.browseRunOnceButton) + self.formLayout_2.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2) + self.label_password_2 = QtWidgets.QLabel(self.layoutWidget_2) + self.label_password_2.setObjectName("label_password_2") + self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_password_2) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_3.setSpacing(4) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.postConfigCommand = QtWidgets.QLineEdit(self.layoutWidget_2) + self.postConfigCommand.setWhatsThis("") + self.postConfigCommand.setEchoMode(QtWidgets.QLineEdit.Normal) + self.postConfigCommand.setObjectName("postConfigCommand") + self.horizontalLayout_3.addWidget(self.postConfigCommand) + self.browsePostConfigButton = QtWidgets.QPushButton(self.layoutWidget_2) + self.browsePostConfigButton.setObjectName("browsePostConfigButton") + self.horizontalLayout_3.addWidget(self.browsePostConfigButton) + self.formLayout_2.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) + self.label_loglevel = QtWidgets.QLabel(self.layoutWidget_2) + self.label_loglevel.setObjectName("label_loglevel") + self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_loglevel) + self.logLevelComboBox = QtWidgets.QComboBox(self.layoutWidget_2) self.logLevelComboBox.setFrame(True) self.logLevelComboBox.setObjectName("logLevelComboBox") self.logLevelComboBox.addItem("") @@ -97,47 +157,56 @@ class Ui_UdsActorSetupDialog(object): self.logLevelComboBox.setItemText(2, "ERROR") self.logLevelComboBox.addItem("") self.logLevelComboBox.setItemText(3, "FATAL") - self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.logLevelComboBox) - self.label_loglevel = QtWidgets.QLabel(self.layoutWidget) - self.label_loglevel.setObjectName("label_loglevel") - self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_loglevel) + self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.logLevelComboBox) + self.tabWidget.addTab(self.tab_advanced, "") self.retranslateUi(UdsActorSetupDialog) + self.tabWidget.setCurrentIndex(0) self.logLevelComboBox.setCurrentIndex(1) - self.cancelButton.pressed.connect(UdsActorSetupDialog.cancelAndDiscard) - self.testButton.pressed.connect(UdsActorSetupDialog.testParameters) - self.saveButton.pressed.connect(UdsActorSetupDialog.acceptAndSave) + self.closeButton.clicked.connect(UdsActorSetupDialog.finish) + self.registerButton.clicked.connect(UdsActorSetupDialog.registerWithUDS) self.host.textChanged['QString'].connect(UdsActorSetupDialog.textChanged) self.username.textChanged['QString'].connect(UdsActorSetupDialog.textChanged) self.password.textChanged['QString'].connect(UdsActorSetupDialog.textChanged) + self.browsePreconnectButton.clicked.connect(UdsActorSetupDialog.browsePreconnect) + self.browsePostConfigButton.clicked.connect(UdsActorSetupDialog.browsePostConfig) + self.browseRunOnceButton.clicked.connect(UdsActorSetupDialog.browseRunOnce) QtCore.QMetaObject.connectSlotsByName(UdsActorSetupDialog) def retranslateUi(self, UdsActorSetupDialog): _translate = QtCore.QCoreApplication.translate - UdsActorSetupDialog.setWindowTitle(_translate("UdsActorSetupDialog", "UDS Actor Setup")) - self.testButton.setToolTip(_translate("UdsActorSetupDialog", "Click to test the selecter parameters")) - self.testButton.setWhatsThis(_translate("UdsActorSetupDialog", "

Click on this button to test the server host and master key parameters.

A window will be displayed with results after the test is executed.


This button will only be active if all parameters are filled.

")) - self.testButton.setText(_translate("UdsActorSetupDialog", "Test parameters")) - self.saveButton.setToolTip(_translate("UdsActorSetupDialog", "Accepts changes and saves them")) - self.saveButton.setWhatsThis(_translate("UdsActorSetupDialog", "Clicking on this button will accept all changes and save them, closing the configuration window")) - self.saveButton.setText(_translate("UdsActorSetupDialog", "Accept && Save")) - self.cancelButton.setToolTip(_translate("UdsActorSetupDialog", "Cancel all changes and discard them")) - self.cancelButton.setWhatsThis(_translate("UdsActorSetupDialog", "Discards all changes and closes the configuration window")) - self.cancelButton.setText(_translate("UdsActorSetupDialog", "Cancel && Discard")) + UdsActorSetupDialog.setWindowTitle(_translate("UdsActorSetupDialog", "UDS Actor Configuration Tool")) + self.registerButton.setToolTip(_translate("UdsActorSetupDialog", "Click to test the selecter parameters")) + self.registerButton.setWhatsThis(_translate("UdsActorSetupDialog", "

Click on this button to test the server host and master key parameters.

A window will be displayed with results after the test is executed.


This button will only be active if all parameters are filled.

")) + self.registerButton.setText(_translate("UdsActorSetupDialog", "Register with UDS")) + self.closeButton.setToolTip(_translate("UdsActorSetupDialog", "Cancel all changes and discard them")) + self.closeButton.setWhatsThis(_translate("UdsActorSetupDialog", "Discards all changes and closes the configuration window")) + self.closeButton.setText(_translate("UdsActorSetupDialog", "Close")) self.label_host.setText(_translate("UdsActorSetupDialog", "UDS Server")) self.host.setToolTip(_translate("UdsActorSetupDialog", "Uds Broker Server Addres. Use IP or FQDN")) self.host.setWhatsThis(_translate("UdsActorSetupDialog", "Enter here the UDS Broker Addres using either its IP address or its FQDN address")) self.label_username.setText(_translate("UdsActorSetupDialog", "Username")) self.username.setToolTip(_translate("UdsActorSetupDialog", "UDS user with administration rights (Will not be stored on template)")) self.username.setWhatsThis(_translate("UdsActorSetupDialog", "

Administrator user on UDS Server.

Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.

")) + self.label_password.setText(_translate("UdsActorSetupDialog", "Password")) self.password.setToolTip(_translate("UdsActorSetupDialog", "Password for user (Will not be stored on template)")) self.password.setWhatsThis(_translate("UdsActorSetupDialog", "

Administrator password for the user on UDS Server.

Note: This credential will not be stored on client. Will be used to obtain an unique key for this image.

")) - self.label_password.setText(_translate("UdsActorSetupDialog", "Password")) self.label_security.setText(_translate("UdsActorSetupDialog", "Security")) self.validateCertificate.setToolTip(_translate("UdsActorSetupDialog", "Select communication security with broker")) self.validateCertificate.setWhatsThis(_translate("UdsActorSetupDialog", "

Select the security for communications with UDS Broker.

The recommended method of communication is Use SSL, but selection needs to be acording to your broker configuration.

")) self.validateCertificate.setItemText(0, _translate("UdsActorSetupDialog", "Ignore certificate")) self.validateCertificate.setItemText(1, _translate("UdsActorSetupDialog", "Verify certificate")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_uds), _translate("UdsActorSetupDialog", "UDS Server")) + self.label_host_2.setText(_translate("UdsActorSetupDialog", "Preconnect")) + self.preCommand.setToolTip(_translate("UdsActorSetupDialog", "Pre connection command. Executed just before the user is connected to machine.")) + self.browsePreconnectButton.setText(_translate("UdsActorSetupDialog", "Browse")) + self.label_username_2.setText(_translate("UdsActorSetupDialog", "Runonce")) + self.runonceCommand.setToolTip(_translate("UdsActorSetupDialog", "Run once command. Executed on first boot, just before UDS does anything.")) + self.browseRunOnceButton.setText(_translate("UdsActorSetupDialog", "Browse")) + self.label_password_2.setText(_translate("UdsActorSetupDialog", "Postconfig")) + self.postConfigCommand.setToolTip(_translate("UdsActorSetupDialog", "Command to execute after UDS finalizes the VM configuration.")) + self.browsePostConfigButton.setText(_translate("UdsActorSetupDialog", "Browse")) self.label_loglevel.setText(_translate("UdsActorSetupDialog", "Log Level")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_advanced), _translate("UdsActorSetupDialog", "Advanced")) from ui import uds_rc diff --git a/actors/src/udsactor/__init__.py b/actors/src/udsactor/__init__.py index 339282da..7155431f 100644 --- a/actors/src/udsactor/__init__.py +++ b/actors/src/udsactor/__init__.py @@ -34,7 +34,7 @@ from __future__ import unicode_literals # On centos, old six release does not includes byte2int, nor six.PY2 import six -VERSION = '2.5.0' +VERSION = '3.0.0' __title__ = 'udsactor' __version__ = VERSION diff --git a/server/src/uds/REST/__init__.py b/server/src/uds/REST/__init__.py index 5a795d15..4a331bac 100644 --- a/server/src/uds/REST/__init__.py +++ b/server/src/uds/REST/__init__.py @@ -57,7 +57,7 @@ from . import processors logger = logging.getLogger(__name__) -__all__ = [str(v) for v in ['Handler', 'Dispatcher']] +__all__ = ['Handler', 'Dispatcher'] AUTH_TOKEN_HEADER = 'X-Auth-Token' diff --git a/actor/src/udsactor/operations.py b/server/src/uds/REST/methods/actor_v2.py similarity index 53% rename from actor/src/udsactor/operations.py rename to server/src/uds/REST/methods/actor_v2.py index fc241ab4..70392269 100644 --- a/actor/src/udsactor/operations.py +++ b/server/src/uds/REST/methods/actor_v2.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- + # -# Copyright (c) 2014 Virtual Cable S.L. +# Copyright (c) 2014-2019 Virtual Cable S.L. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -26,15 +27,55 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -''' +""" @author: Adolfo Gómez, dkmaster at dkmon dot com -''' -# pylint: disable=unused-wildcard-import,wildcard-import +""" +import logging +import typing -from __future__ import unicode_literals +from django.utils.translation import ugettext as _ -import sys -if sys.platform == 'win32': - from .windows.operations import * # @UnusedWildImport -else: - from .linux.operations import * # @UnusedWildImport +from uds.models.util import getSqlDatetimeAsUnix +from uds.core import VERSION +from ..handlers import Handler + +logger = logging.getLogger(__name__) + +def actorResult(result: typing.Any = None, error: typing.Optional[str] = None) -> typing.MutableMapping[str, typing.Any]: + result = result or '' + res = {'result': result, 'stamp': getSqlDatetimeAsUnix()} + if error: + res['error'] = error + return res + +# Enclosed methods under /actor path +class ActorV2(Handler): + """ + Processes actor requests + """ + authenticated = False # Actor requests are not authenticated normally + path = 'actor' + name = 'v2' + + def get(self): # pylint: disable=too-many-return-statements + """ + Processes get requests + """ + logger.debug('Actor args for GET: %s', self._args) + + return actorResult({'version': VERSION, 'required': '3.0.0'}) + +class ActorV2Register(Handler): + """ + Tests the process + """ + authenticated = False # Actor requests are not authenticated normally + path = 'actor/v2' + name = 'register' + + def get(self): + return actorResult('Ok') + + def post(self): + logger.debug('Args: %s, Params: %s', self._args, self._params) + return actorResult('ok') diff --git a/server/src/uds/transports/HTML5RDP/html5rdp.py b/server/src/uds/transports/HTML5RDP/html5rdp.py index 74d95702..bf0fa326 100644 --- a/server/src/uds/transports/HTML5RDP/html5rdp.py +++ b/server/src/uds/transports/HTML5RDP/html5rdp.py @@ -251,9 +251,11 @@ class HTML5RDPTransport(transports.Transport): ticket = models.TicketStore.create(params, validity=self.ticketValidity.num()) - return HttpResponseRedirect("{}/transport/?{}.{}&{}".format( - self.guacamoleServer.value, - ticket, - scrambler, - request.build_absolute_uri(reverse('utility.closer')) - )) + return HttpResponseRedirect( + "{}/transport/?{}.{}&{}".format( + self.guacamoleServer.value, + ticket, + scrambler, + 'javascript:window.close();' + ) + )