Compare commits

..

No commits in common. "master" and "v3.5" have entirely different histories.
master ... v3.5

778 changed files with 60660 additions and 41329 deletions

4
.gitignore vendored
View File

@ -64,6 +64,8 @@
# /server/
*_enterprise
/server/openuds.sublime-project
/server/openuds.sublime-workspace
# /server/src/
/server/src/taskmanager.pid
@ -86,6 +88,7 @@
# /server/src/uds/
/server/src/uds/*_enterprise.py
/server/src/uds/fixtures
/server/src/uds/tests
# /server/src/uds/auths/
/server/src/uds/auths/*-enterprise
@ -162,4 +165,3 @@
.vscode
.mypy_cache
.pytest_cache

29
LICENSE
View File

@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2022, Virtual Cable S.L.U.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
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.

View File

@ -10,6 +10,6 @@ OpenUDS (Universal Desktop Services) is a multiplatform connection broker for:
This is an Open Source Source project, initiated by Spanish Company Virtualcable and released Open Source with the help of several Spanish Universities.
Please fell free to contribute to this project.
Any help provided will be welcome.
**Note: Master version is always under heavy development and it is not recommended for use, it will probably have unfixed bugs. Please use the latest stable branch.**

View File

@ -1 +1 @@
4.0.0
3.5.0

4
actor/deps.txt Normal file
View File

@ -0,0 +1,4 @@
Linux:
python3-prctl (recommended, but not required in fact)
python3-pyqt5

View File

@ -11,9 +11,6 @@ dpkg-buildpackage -b
cat udsactor-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsactor-$VERSION.spec
cat udsactor-unmanaged-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsactor-unmanaged-$VERSION.spec
# Now fix dependencies for opensuse
# Note that, although on opensuse the library is "libXss1" on newer,
@ -25,7 +22,7 @@ cat udsactor-unmanaged-template.spec |
# sed -e s/"libXScrnSaver"/"libXss1"/g > udsactor-opensuse-$VERSION.spec
#for pkg in udsactor-$VERSION.spec udsactor-opensuse-$VERSION.spec; do
for pkg in udsactor-*$VERSION.spec; do
for pkg in udsactor-$VERSION.spec; do
rm -rf rpm
for folder in SOURCES BUILD RPMS SPECS SRPMS; do

View File

@ -1,15 +1,3 @@
udsactor (4.0.0) stable; urgency=medium
* Upgraded to 4.0.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 1 Jul 2022 15:00:00 +0200
udsactor (3.6.0) stable; urgency=medium
* Upgraded to 3.6.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 1 Jul 2022 14:00:00 +0200
udsactor (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release

View File

@ -1,3 +1,3 @@
udsactor-unmanaged_3.6.0_all.deb admin optional
udsactor_3.6.0_all.deb admin optional
udsactor_3.6.0_amd64.buildinfo admin optional
udsactor-unmanaged_3.5.0_all.deb admin optional
udsactor_3.5.0_all.deb admin optional
udsactor_3.5.0_amd64.buildinfo admin optional

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.virtualcable.udsactor.server</string>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/UDSActor.app/Contents/MacOS/udsactor</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/var/log/udsactor.log</string>
<key>StandardOutPath</key>
<string>/var/log/nxserver.log</string>
<key>WorkingDirectory</key>
<string>/Applications/UDSActor.app/Contents/Resources/</string>
</dict>
</plist>

View File

@ -1 +0,0 @@
service file (net.virtualcable.udsactor.server.plist) goes in /Library/LaunchDaemons

View File

@ -29,32 +29,33 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
# pylint: disable=invalid-name
import sys
import os
import PyQt5 # noqa
import PyQt5 # pylint: disable=unused-import
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMainWindow
from udsactor.log import logger, INFO
from udsactor.client import UDSClientQApp
from udsactor import platform
from udsactor.platform import operations
if __name__ == "__main__":
logger.setLevel(INFO)
# Ensure idle operations is initialized on start
platform.operations.initIdleDuration(0)
operations.initIdleDuration(0)
if platform.is_linux:
if 'linux' in sys.platform:
os.environ['QT_X11_NO_MITSHM'] = '1'
UDSClientQApp.setQuitOnLastWindowClosed(False)
qApp = UDSClientQApp(sys.argv)
if platform.is_windows or platform.is_mac:
# The "hidden window" is not needed on linux
if 'linux' not in sys.platform:
# The "hidden window" is only needed to process events on Windows
# Not needed on Linux
mw = QMainWindow()
mw.showMinimized() # Start minimized, will be hidden (not destroyed) as soon as qApp.init is invoked

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020-2022 Virtual Cable S.L.U.
# Copyright (c) 2020 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -35,7 +35,7 @@ import os
import logging
import typing
import PyQt5 # Ensures PyQt is included in the package
import PyQt5 # pylint: disable=unused-import
from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog, QMessageBox
import udsactor

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020-2022 Virtual Cable S.L.U.
# Copyright (c) 2020 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -32,7 +32,7 @@
# pylint: disable=invalid-name
import sys
import os
import pickle # nosec: B403
import pickle
import logging
import typing
@ -102,7 +102,7 @@ class UDSConfigDialog(QDialog):
self,
'UDS Test',
'Service token seems to be invalid . Please, check token validity.',
QMessageBox.Ok, # type: ignore
QMessageBox.Ok,
)
else:
QMessageBox.information(
@ -111,14 +111,14 @@ class UDSConfigDialog(QDialog):
'Configuration for {} seems to be correct.'.format(
self._config.host
),
QMessageBox.Ok, # type: ignore
QMessageBox.Ok,
)
except Exception:
QMessageBox.information(
self,
'UDS Test',
'Configured host {} seems to be inaccesible.'.format(self._config.host),
QMessageBox.Ok, # type: ignore
QMessageBox.Ok,
)
def saveConfig(self) -> None:
@ -134,7 +134,7 @@ class UDSConfigDialog(QDialog):
self,
'Invalid subnet',
'Invalid subnet {}. Please, check it.'.format(restrictNet),
QMessageBox.Ok, # type: ignore
QMessageBox.Ok,
)
return
@ -153,15 +153,12 @@ class UDSConfigDialog(QDialog):
self.ui.testButton.setEnabled(True)
# Informs the user
QMessageBox.information(
self,
'UDS Configuration',
'Configuration saved.',
QMessageBox.Ok, # type: ignore
self, 'UDS Configuration', 'Configuration saved.', QMessageBox.Ok
)
if __name__ == "__main__":
# If run as "sudo" on linux, we will need this to avoid problems
# If to be run as "sudo" on linux, we will need this to avoid problems
if 'linux' in sys.platform:
os.environ['QT_X11_NO_MITSHM'] = '1'
@ -174,18 +171,16 @@ if __name__ == "__main__":
if len(sys.argv) > 2:
if sys.argv[1] == 'export':
try:
with open(sys.argv[2], 'wb') as export_:
pickle.dump(
udsactor.platform.store.readConfig(), export_, protocol=3
)
with open(sys.argv[2], 'wb') as f:
pickle.dump(udsactor.platform.store.readConfig(), f, protocol=3)
except Exception as e:
print('Error exporting configuration file: {}'.format(e))
sys.exit(1)
sys.exit(0)
elif sys.argv[1] == 'import':
if sys.argv[1] == 'import':
try:
with open(sys.argv[2], 'rb') as import_:
config = pickle.load(import_) # nosec: B301: the file is provided by user, so it's not a security issue
with open(sys.argv[2], 'rb') as f:
config = pickle.load(f)
udsactor.platform.store.writeConfig(config)
except Exception as e:
print('Error importing configuration file: {}'.format(e))

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2020-2022 Virtual Cable S.L.U.
# Copyright (c) 2020 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -12,7 +12,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -29,8 +29,12 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
from udsactor import platform
import sys
if sys.platform == 'win32':
from udsactor.windows import runner
else:
from udsactor.linux import runner
if __name__ == "__main__":
platform.runner.run()
runner.run()

View File

@ -214,10 +214,10 @@
<item row="2" column="1">
<widget class="QLineEdit" name="serviceToken">
<property name="toolTip">
<string>UDS Service Token</string>
<string>UDS user with administration rights (Will not be stored on template)</string>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Token of the service on UDS platform&lt;/p&gt;&lt;p&gt;This token can be obtainend from the service configuration on UDS.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Administrator user on UDS Server.&lt;/p&gt;&lt;p&gt;Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@ -268,10 +268,10 @@
<item row="3" column="1">
<widget class="QLineEdit" name="restrictNet">
<property name="toolTip">
<string>Restrict valid detection of network interfaces to this network.</string>
<string>UDS user with administration rights (Will not be stored on template)</string>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Restrics valid detection of network interfaces.&lt;/p&gt;&lt;p&gt;Note: Use this field only in case of several network interfaces, so UDS knows which one is the interface where the user will be connected..&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Administrator user on UDS Server.&lt;/p&gt;&lt;p&gt;Note: This credential will not be stored on client. Will be used to obtain an unique token for this image.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -35,4 +35,4 @@ from . import platform
__title__ = 'udsactor'
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
__license__ = "BSD 3-clause"
__copyright__ = "Copyright 2014-2022 VirtualCable S.L.U."
__copyright__ = "Copyright 2014-2020 VirtualCable S.L.U."

View File

@ -196,7 +196,7 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
self.checkIdle()
self.checkDeadLine()
time.sleep(1.22) # Sleeps between loop iterations
time.sleep(1.3) # Sleeps between loop iterations
self.api.logout(user + self._extraLogoff, sessionType)
logger.info('Notified logout for %s (%s)', user, sessionType) # Log logout
@ -239,7 +239,7 @@ class UDSActorClient(threading.Thread): # pylint: disable=too-many-instance-att
pixmap: 'QPixmap' = self._qApp.primaryScreen().grabWindow(0) # type: ignore
ba = QByteArray()
buffer = QBuffer(ba)
buffer.open(QIODevice.WriteOnly) # type: ignore
buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, 'PNG')
buffer.close()
scrBase64 = bytes(ba.toBase64()).decode() # type: ignore # there are problems with Pylance and connects on PyQt5... :)

View File

@ -132,7 +132,7 @@ class HTTPServerThread(threading.Thread):
self._app = app
self.port = -1
self.id = secrets.token_urlsafe(24)
self.id = secrets.token_urlsafe(16)
@property
def url(self) -> str:

View File

@ -33,8 +33,8 @@ import json
import typing
import requests
from udsactor import tools, types
from udsactor.log import logger
from ..log import logger
# For avoid proxy on localhost connections
NO_PROXY = {
@ -42,108 +42,55 @@ NO_PROXY = {
'https': None,
}
class UDSActorClientPool(metaclass=tools.Singleton):
_clients: typing.List[types.ClientInfo]
class UDSActorClientPool:
_clientUrl: typing.List[str]
def __init__(self) -> None:
self._clients = []
self._clientUrl = []
def _post(
self,
session_id: typing.Optional[str],
method: str,
data: typing.MutableMapping[str, str],
timeout: int = 2,
) -> typing.List[
typing.Tuple[types.ClientInfo, typing.Optional[requests.Response]]
]:
result: typing.List[
typing.Tuple[types.ClientInfo, typing.Optional[requests.Response]]
] = []
for client in self._clients:
# Skip if session id is provided but does not match
if session_id and client.session_id != session_id:
continue
clientUrl = client.url
def _post(self, method: str, data: typing.MutableMapping[str, str], timeout=2) -> typing.List[requests.Response]:
removables: typing.List[str] = []
result: typing.List[typing.Any] = []
for clientUrl in self._clientUrl:
try:
result.append(
(
client,
requests.post(
clientUrl + '/' + method,
data=json.dumps(data),
verify=False,
timeout=timeout,
proxies=NO_PROXY, # type: ignore
),
)
)
result.append(requests.post(clientUrl + '/' + method, data=json.dumps(data), verify=False, timeout=timeout, proxies=NO_PROXY))
except Exception as e:
logger.info(
'Could not connect with client %s: %s. ',
e,
clientUrl,
)
result.append((client, None))
# If cannot request to a clientUrl, remove it from list
logger.info('Could not connect with client %s: %s. Removed from registry.', e, clientUrl)
removables.append(clientUrl)
# Remove failed connections
for clientUrl in removables:
self.unregister(clientUrl)
return result
@property
def clients(self) -> typing.List[types.ClientInfo]:
return self._clients
def register(self, client_url: str) -> None:
def register(self, clientUrl: str) -> None:
# Remove first if exists, to avoid duplicates
self.unregister(client_url)
self.unregister(clientUrl)
# And add it again
self._clients.append(types.ClientInfo(client_url, ''))
self._clientUrl.append(clientUrl)
def set_session_id(self, client_url: str, session_id: typing.Optional[str]) -> None:
"""Set the session id for a client
def unregister(self, clientUrl: str) -> None:
self._clientUrl = list((i for i in self._clientUrl if i != clientUrl))
Args:
clientUrl (str): _description_
session_id (str): _description_
"""
for client in self._clients:
if client.url == client_url:
# remove existing client from list, create a new one and insert it
self._clients.remove(client)
self._clients.append(types.ClientInfo(client_url, session_id or ''))
break
def executeScript(self, script: str) -> None:
self._post('script', {'script': script}, timeout=30)
def unregister(self, client_url: str) -> None:
# remove client url from array if found
for i, client in enumerate(self._clients):
if client.url == client_url:
self._clients.pop(i)
return
def logout(self) -> None:
self._post('logout', {})
def executeScript(self, session_id: typing.Optional[str], script: str) -> None:
self._post(session_id, 'script', {'script': script}, timeout=30)
def message(self, message: str) -> None:
self._post('message', {'message': message})
def logout(self, session_id: typing.Optional[str]) -> None:
self._post(session_id, 'logout', {})
def ping(self) -> bool:
if not self._clientUrl:
return True # No clients, ping ok
self._post('ping', {}, timeout=1)
return bool(self._clientUrl) # There was clients, but they are now lost!!!
def message(self, session_id: typing.Optional[str], message: str) -> None:
self._post(session_id, 'message', {'message': message})
def lost_clients(
self,
session_id: typing.Optional[str] = None,
) -> typing.Iterable[types.ClientInfo]: # returns the list of "lost" clients
# Port ping to every client
for i in self._post(session_id, 'ping', {}, timeout=1):
if i[1] is None:
yield i[0]
def screenshot(
self, session_id: typing.Optional[str]
) -> typing.Optional[str]: # Screenshot are returned as base64
for client, r in self._post(session_id, 'screenshot', {}, timeout=3):
if not r:
continue # Missing client, so we ignore it
def screenshot(self) -> typing.Optional[str]: # Screenshot are returned as base64
for r in self._post('screenshot', {}, timeout=3):
try:
return r.json()['result']
except Exception:

View File

@ -30,23 +30,19 @@
'''
import typing
from udsactor.http import handler, clients_pool
from . import handler
if typing.TYPE_CHECKING:
from udsactor.service import CommonService
from ..service import CommonService
class LocalProvider(handler.Handler):
def post_login(self) -> typing.Any:
result = self._service.login(self._params['username'], self._params['session_type'])
# if callback_url is provided, record it in the clients pool
if 'callback_url' in self._params and result.session_id:
# If no session id is returned, then no login is acounted for
clients_pool.UDSActorClientPool().set_session_id(self._params['callback_url'], result.session_id)
return result._asdict()
def post_logout(self) -> typing.Any:
self._service.logout(self._params['username'], self._params['session_type'], self._params['session_id'])
self._service.logout(self._params['username'], self._params['session_type'])
return 'ok'
def post_ping(self) -> typing.Any:

View File

@ -71,7 +71,7 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
# Very simple path & params splitter
path = self.path.split('?')[0][1:].split('/')
logger.debug('Path: %s, ip: %s, params: %s', path, self.client_address, params)
logger.debug('Path: %s, params: %s', path, params)
handlerType: typing.Optional[typing.Type['Handler']] = None

View File

@ -101,7 +101,7 @@ class Daemon:
def removePidFile(self) -> None:
try:
os.remove(self.pidfile)
except Exception: # nosec: Not interested in exception
except Exception:
# Not found/not permissions or whatever, ignore it
pass

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -35,8 +35,8 @@ import logging
import typing
class LocalLogger: # pylint: disable=too-few-public-methods
linux = True
windows = False
linux = False
windows = True
serviceLogger = False
logger: typing.Optional[logging.Logger]
@ -59,8 +59,7 @@ class LocalLogger: # pylint: disable=too-few-public-methods
self.logger = logging.getLogger('udsactor')
os.chmod(fname, 0o0600)
return
except Exception: # nosec: B110: we don't care about exceptions here
# Ignore and try next
except Exception:
pass
# Logger can't be set

View File

@ -34,7 +34,7 @@ import platform
import socket
import fcntl # Only available on Linux. Expect complains if edited from windows
import os
import subprocess # nosec
import subprocess
import struct
import array
import typing
@ -53,9 +53,7 @@ def _getMacAddr(ifname: str) -> typing.Optional[str]:
ifnameBytes = ifname.encode('utf-8')
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
info = bytearray(
fcntl.ioctl(s.fileno(), 0x8927, struct.pack(str('256s'), ifnameBytes[:15]))
)
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
@ -69,15 +67,11 @@ def _getIpAddr(ifname: str) -> typing.Optional[str]:
ifnameBytes = ifname.encode('utf-8')
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return str(
socket.inet_ntoa(
fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack(str('256s'), ifnameBytes[:15]),
)[20:24]
)
)
return str(socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack(str('256s'), ifnameBytes[:15])
)[20:24]))
except Exception:
return None
@ -97,32 +91,22 @@ def _getInterfaces() -> typing.List[str]:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
names = array.array(str('B'), b'\0' * space)
outbytes = struct.unpack(
'iL',
fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack('iL', space, names.buffer_info()[0]),
),
)[0]
outbytes = struct.unpack('iL', fcntl.ioctl(
s.fileno(),
0x8912, # SIOCGIFCONF
struct.pack('iL', space, names.buffer_info()[0])
))[0]
namestr = names.tobytes()
# return namestr, outbytes
return [
namestr[i : i + offset].split(b'\0', 1)[0].decode('utf-8')
for i in range(0, outbytes, length)
]
return [namestr[i:i + offset].split(b'\0', 1)[0].decode('utf-8') for i in range(0, outbytes, length)]
def _getIpAndMac(
ifname: str,
) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
def _getIpAndMac(ifname: str) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
ip, mac = _getIpAddr(ifname), _getMacAddr(ifname)
return (ip, mac)
def checkPermissions() -> bool:
return os.getuid() == 0
return os.getuid() == 0 # getuid only available on linux. Expect "complaioins" if edited from Windows
def getComputerName() -> str:
'''
@ -130,23 +114,15 @@ def getComputerName() -> str:
'''
return socket.gethostname().split('.')[0]
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
for ifname in _getInterfaces():
ip, mac = _getIpAndMac(ifname)
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
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.InterfaceInfoType(name=ifname, mac=mac, ip=ip)
def getDomainName() -> str:
return ''
def getLinuxOs() -> str:
try:
with open('/etc/os-release', 'r') as f:
@ -157,22 +133,18 @@ def getLinuxOs() -> str:
except Exception:
return 'unknown'
def getVersion() -> str:
return 'Linux ' + getLinuxOs()
def reboot(flags: int = 0):
'''
Simple reboot using os command
'''
subprocess.call(['/sbin/shutdown', 'now', '-r']) # nosec: Fine, all under control
subprocess.call(['/sbin/shutdown', 'now', '-r'])
def loggoff() -> None:
'''
Right now restarts the machine...
'''
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']]) # nosec: Fine, all under control
subprocess.call(['/usr/bin/pkill', '-u', os.environ['USER']])
# subprocess.call(['/sbin/shutdown', 'now', '-r'])
# subprocess.call(['/usr/bin/systemctl', 'reboot', '-i'])
@ -186,9 +158,7 @@ def renameComputer(newName: str) -> bool:
return True # Always reboot right now. Not much slower but much more convenient
def joinDomain(
domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False
):
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False):
pass
@ -196,11 +166,7 @@ def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
'''
Simple password change for user using command line
'''
subprocess.run( # nosec: Fine, all under control
'echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword),
shell=True,
)
os.system('echo "{1}\n{1}" | /usr/bin/passwd {0} 2> /dev/null'.format(user, newPassword))
def initIdleDuration(atLeastSeconds: int) -> None:
@ -215,22 +181,16 @@ def getCurrentUser() -> str:
'''
Returns current logged in user
'''
return os.getlogin()
return os.environ['USER']
def getSessionType() -> str:
'''
Known values:
* Unknown -> No XDG_SESSION_TYPE environment variable
* xrdp --> xrdp session
* other types
Known values:
* Unknown -> No XDG_SESSION_TYPE environment variable
* xrdp --> xrdp session
* other types
'''
return (
'xrdp'
if 'XRDP_SESSION' in os.environ
else os.environ.get('XDG_SESSION_TYPE', 'unknown')
)
return 'xrdp' if 'XRDP_SESSION' in os.environ else os.environ.get('XDG_SESSION_TYPE', 'unknown')
def forceTimeSync() -> None:
return

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -28,7 +28,7 @@
'''
@author: Alexey Shabalin, shaba at altlinux dot org
'''
import subprocess # nosec
import os
from .common import renamers
from ...log import logger
@ -46,8 +46,8 @@ def rename(newName: str) -> bool:
hostname.write(newName)
# Force system new name
subprocess.run(['hostnamectl', 'set-hostname', newName]) # nosec: subprocess
subprocess.run(['/bin/hostname', newName]) # nosec: subprocess
os.system('/bin/hostname {}'.format(newName))
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
# add name to "hosts"
with open('/etc/hosts', 'r') as hosts:

View File

@ -29,6 +29,9 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import os
import sys
import pkgutil
import typing
from .. import operations

View File

@ -29,7 +29,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import subprocess # nosec
import os
from .common import renamers
from ...log import logger
@ -45,8 +45,8 @@ def rename(newName: str) -> bool:
hostname.write(newName)
# Force system new name
subprocess.run(['hostnamectl', 'set-hostname', newName]) # nosec: ok, we are root
subprocess.run(['/bin/hostname', newName]) # nosec: ok, we are root
os.system('/bin/hostname {}'.format(newName))
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
# add name to "hosts"
with open('/etc/hosts', 'r') as hosts:

View File

@ -28,7 +28,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import subprocess # nosec
import os
from .common import renamers
from ...log import logger
@ -46,8 +46,8 @@ def rename(newName: str) -> bool:
hostname.write(newName)
# Force system new name
subprocess.run(['hostnamectl', 'set-hostname', newName]) # nosec: ok, we are root
subprocess.run(['/bin/hostname', newName]) # nosec: ok, we are root
os.system('/bin/hostname {}'.format(newName))
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
# add name to "hosts"
with open('/etc/hosts', 'r') as hosts:

View File

@ -28,7 +28,7 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
import subprocess # nosec
import os
from .common import renamers
from ...log import logger
@ -46,8 +46,8 @@ def rename(newName: str) -> bool:
hostname.write(newName)
# Force system new name
subprocess.run(['hostnamectl', 'set-hostname', newName]) # nosec: ok, we are root
subprocess.run(['/bin/hostname', newName]) # nosec: ok, we are root
os.system('/bin/hostname {}'.format(newName))
os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
# add name to "hosts"
with open('/etc/hosts', 'r') as hosts:

View File

@ -50,7 +50,7 @@ def run() -> None:
r = client.login(sys.argv[2], platform.operations.getSessionType())
print('{},{},{},{}\n'.format(r.ip, r.hostname, r.max_idle, r.dead_line or ''))
elif sys.argv[1] == 'logout':
client.logout(sys.argv[2], platform.operations.getSessionType())
client.logout(sys.argv[2])
except Exception as e:
logger.exception()
logger.error('Got exception while processing command: %s', e)

View File

@ -37,7 +37,7 @@ from ..log import logger
from ..service import CommonService
try:
from prctl import set_proctitle # type: ignore
from prctl import set_proctitle # @UnresolvedImport
except ImportError: # Platform may not include prctl, so in case it's not available, we let the "name" as is
def set_proctitle(_):
pass

View File

@ -32,13 +32,12 @@
import os
import configparser
import base64
import pickle # nosec
import pickle
from .. import types
CONFIGFILE = '/etc/udsactor/udsactor.cfg'
def readConfig() -> types.ActorConfigurationType:
try:
cfg = configparser.ConfigParser()
@ -46,22 +45,10 @@ def readConfig() -> types.ActorConfigurationType:
uds: configparser.SectionProxy = cfg['uds']
# Extract data:
base64Config = uds.get('config', None)
config = (
pickle.loads( # nosec: file is restricted
base64.b64decode(base64Config.encode())
)
if base64Config
else None
)
config = pickle.loads(base64.b64decode(base64Config.encode())) if base64Config else None
base64Data = uds.get('data', None)
data = (
pickle.loads( # nosec: file is restricted
base64.b64decode(base64Data.encode())
)
if base64Data
else None
)
data = pickle.loads(base64.b64decode(base64Data.encode())) if base64Data else None
return types.ActorConfigurationType(
actorType=uds.get('type', types.MANAGED),
@ -75,23 +62,20 @@ def readConfig() -> types.ActorConfigurationType:
post_command=uds.get('post_command', None),
log_level=int(uds.get('log_level', '2')),
config=config,
data=data,
data=data
)
except Exception:
return types.ActorConfigurationType('', False)
def writeConfig(config: types.ActorConfigurationType) -> None:
cfg = configparser.ConfigParser()
cfg.add_section('uds')
uds: configparser.SectionProxy = cfg['uds']
uds['host'] = config.host
uds['validate'] = 'yes' if config.validateCertificate else 'no'
def writeIfValue(val, name):
if val:
uds[name] = val
writeIfValue(config.actorType, 'type')
writeIfValue(config.master_token, 'master_token')
writeIfValue(config.own_token, 'own_token')
@ -109,19 +93,15 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
# Ensures exists destination folder
dirname = os.path.dirname(CONFIGFILE)
if not os.path.exists(dirname):
os.mkdir(
dirname, mode=0o700
) # Will create only if route to path already exists, for example, /etc (that must... :-))
os.mkdir(dirname, mode=0o700) # Will create only if route to path already exists, for example, /etc (that must... :-))
with open(CONFIGFILE, 'w') as f:
cfg.write(f)
os.chmod(CONFIGFILE, 0o0600) # Ensure only readable by root
def useOldJoinSystem() -> bool:
return False
def invokeScriptOnLogin() -> str:
return ''

View File

@ -31,7 +31,7 @@
# pylint: disable=invalid-name
import ctypes
import ctypes.util
import subprocess # nosec
import subprocess
xlib = None
xss = None
@ -39,22 +39,17 @@ display = None
xssInfo = None
initialized = False
class XScreenSaverInfo(ctypes.Structure): # pylint: disable=too-few-public-methods
_fields_ = [
('window', ctypes.c_long),
('state', ctypes.c_int),
('kind', ctypes.c_int),
('til_or_since', ctypes.c_ulong),
('idle', ctypes.c_ulong),
('eventMask', ctypes.c_ulong),
]
_fields_ = [('window', ctypes.c_long),
('state', ctypes.c_int),
('kind', ctypes.c_int),
('til_or_since', ctypes.c_ulong),
('idle', ctypes.c_ulong),
('eventMask', ctypes.c_ulong)]
class c_ptr(ctypes.c_void_p):
pass
def _ensureInitialized():
global xlib, xss, xssInfo, display, initialized # pylint: disable=global-statement
@ -78,15 +73,13 @@ def _ensureInitialized():
xss.XScreenSaverQueryExtension.argtypes = [
ctypes.c_void_p,
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int),
ctypes.POINTER(ctypes.c_int)
]
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(
XScreenSaverInfo
) # Result in a XScreenSaverInfo structure
xss.XScreenSaverAllocInfo.restype = ctypes.POINTER(XScreenSaverInfo) # Result in a XScreenSaverInfo structure
xss.XScreenSaverQueryInfo.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.POINTER(XScreenSaverInfo),
ctypes.POINTER(XScreenSaverInfo)
]
xlib.XOpenDisplay.argtypes = [ctypes.c_char_p]
xlib.XOpenDisplay.restype = c_ptr
@ -102,9 +95,7 @@ def _ensureInitialized():
event_base = ctypes.c_int()
error_base = ctypes.c_int()
available = xss.XScreenSaverQueryExtension(
display, ctypes.byref(event_base), ctypes.byref(error_base)
)
available = xss.XScreenSaverQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base))
if available != 1:
raise Exception('ScreenSaver not available')
@ -116,11 +107,9 @@ def _ensureInitialized():
def initIdleDuration(atLeastSeconds: int) -> None:
_ensureInitialized()
if atLeastSeconds:
subprocess.call( # nosec, controlled params
['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)]
)
subprocess.call(['/usr/bin/xset', 's', '{}'.format(atLeastSeconds + 30)])
# And now reset it
subprocess.call(['/usr/bin/xset', 's', 'reset']) # nosec: fixed command
subprocess.call(['/usr/bin/xset', 's', 'reset'])
def getIdleDuration() -> float:
@ -133,11 +122,7 @@ def getIdleDuration() -> float:
xss.XScreenSaverQueryInfo(display, xlib.XDefaultRootWindow(display), xssInfo)
# States: 0 = off, 1 = On, 2 = Cycle, 3 = Disabled, ...?
if (
xssInfo.contents.state == 1
): # state = 1 means "active", so idle is not a valid state
return (
3600 * 100 * 1000
) # If screen saver is active, return a high enough value
if xssInfo.contents.state == 1: # state = 1 means "active", so idle is not a valid state
return 3600 * 100 * 1000 # If screen saver is active, return a high enough value
return xssInfo.contents.idle / 1000.0

View File

@ -35,8 +35,6 @@ import typing
if sys.platform == 'win32':
from .windows.log import LocalLogger
elif sys.platform == 'darwin':
from .macos.log import LocalLogger
else:
from .linux.log import LocalLogger
@ -57,7 +55,7 @@ class Logger:
self.logLevel = INFO
self.localLogger = LocalLogger()
self.remoteLogger = None
self.own_token = '' # nosec: This is no password at all
self.own_token = ''
def setLevel(self, level: typing.Union[str, int]) -> None:
'''

View File

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''

View File

@ -1,185 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''
# Note. most methods are not implemented, as they are not needed for this platform (macos)
# that only supports unmanaged machines
import socket
import os
import re
import subprocess # nosec
import typing
import psutil
from udsactor import types, tools
MACVER_RE = re.compile(
r"<key>ProductVersion</key>\s*<string>(.*)</string>", re.MULTILINE
)
MACVER_FILE = '/System/Library/CoreServices/SystemVersion.plist'
def checkPermissions() -> bool:
return os.getuid() == 0
def getComputerName() -> str:
'''
Returns computer name, with no domain
'''
return socket.gethostname().split('.')[0]
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
ifdata: typing.List['psutil._common.snicaddr']
for ifname, ifdata in psutil.net_if_addrs().items():
name, ip, mac = '', '', ''
# Get IP address, interface name and MAC address whenever possible
for row in ifdata:
if row.family == socket.AF_INET:
ip = row.address
name = ifname
elif row.family == socket.AF_LINK:
mac = row.address
# if all data is available, stop iterating
if ip and name and mac:
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.InterfaceInfoType(name=name, ip=ip, mac=mac)
break
def getDomainName() -> str:
return ''
def getMacOs() -> str:
try:
with open(MACVER_FILE, 'r') as f:
data = f.read()
m = MACVER_RE.search(data)
if m:
return m.group(1)
except Exception: # nosec: B110: ignore exception because we are not interested in it
pass
return 'unknown'
def getVersion() -> str:
return 'MacOS ' + getMacOs()
def reboot(flags: int = 0) -> None:
'''
Simple reboot using os command
'''
subprocess.call(['/sbin/shutdown', '-r', 'now']) # nosec: Command line is fixed
def loggoff() -> None:
'''
Right now restarts the machine...
'''
subprocess.run(
"/bin/launchctl bootout gui/$(id -u $USER)", shell=True
) # nosec: Command line is fixed
# Ignores output, as it may fail if user is not logged in
def renameComputer(newName: str) -> bool:
'''
Changes the computer name
Returns True if reboot needed
Note: For macOS, no configuration is supported, only "unmanaged" actor
'''
return False
def joinDomain(
domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False
):
pass
def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
pass
def initIdleDuration(atLeastSeconds: int) -> None:
pass
# se we cache for 20 seconds the result, that is enough for our needs
# and we avoid calling a system command every time we need it
@tools.cache(20)
def getIdleDuration() -> float:
# Execute:
try:
return (
int(
next(
filter(
lambda x: b"HIDIdleTime" in x,
subprocess.check_output(
["/usr/sbin/ioreg", "-c", "IOHIDSystem"]
).split(b"\n"),
)
).split(b"=")[1]
)
/ 1000000000
) # nosec: Command line is fixed
except Exception: # nosec: B110: ignore exception because we are not interested in it
return 0
def getCurrentUser() -> str:
'''
Returns current logged in user
'''
return os.getlogin()
def getSessionType() -> str:
'''
Returns the session type. Currently, only "macos" (console) is supported
'''
return 'macos'
def forceTimeSync() -> None:
return

View File

@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''
import sys
import typing
from .. import rest
from .. import platform
from ..log import logger
from .service import UDSActorSvc
def usage() -> typing.NoReturn:
sys.stderr.write('usage: udsactor start|login "username"|logout "username"\n')
sys.exit(2)
def run() -> None:
logger.setLevel(20000)
if len(sys.argv) == 3 and sys.argv[1] in ('login', 'logout'):
logger.debug('Running client udsactor')
try:
client: rest.UDSClientApi = rest.UDSClientApi()
if sys.argv[1] == 'login':
r = client.login(sys.argv[2], platform.operations.getSessionType())
print('{},{},{},{}\n'.format(r.ip, r.hostname, r.max_idle, r.dead_line or ''))
elif sys.argv[1] == 'logout':
client.logout(sys.argv[2], platform.operations.getSessionType())
except Exception as e:
logger.exception()
logger.error('Got exception while processing command: %s', e)
sys.exit(0)
elif len(sys.argv) != 2:
usage()
daemonSvr = UDSActorSvc()
if len(sys.argv) == 2:
# Daemon mode...
if sys.argv[1] in ('start', 'start-foreground'):
daemonSvr.run() # execute in foreground
else:
usage()
sys.exit(0)
else:
usage()

View File

@ -1,108 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''
import typing
import signal
from ..log import logger
from ..service import CommonService
class UDSActorSvc(CommonService):
def __init__(self) -> None:
CommonService.__init__(self)
# Captures signals so we can stop gracefully
signal.signal(signal.SIGINT, self.markForExit)
signal.signal(signal.SIGTERM, self.markForExit)
def markForExit(self, signum, frame) -> None: # pylint: disable=unused-argument
self._isAlive = False
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
self,
name: str,
domain: str,
ou: str,
account: str,
password: str
) -> None:
pass # Not implemented for unmanaged machines
def rename(
self,
name: str,
userName: typing.Optional[str] = None,
oldPassword: typing.Optional[str] = None,
newPassword: typing.Optional[str] = None,
) -> None:
pass # Not implemented for unmanaged machines
def run(self) -> None:
logger.debug('Running Daemon: {}'.format(self._isAlive))
# Linux daemon will continue running unless something is requested to
# Unmanaged services does not initializes "on start", but rather when user logs in (because userservice does not exists "as such" before that)
if self.isManaged(): # Currently, managed is not implemented for UDS on M
logger.error('Managed machines not supported on MacOS')
# Wait a bit, this is mac os and will be run by launchd
# If the daemon shuts down too quickly, launchd may think it is a crash.
self.doWait(10000)
self.finish()
return # Stop daemon if initializes told to do so
if not self.initializeUnmanaged():
# Wait a bit, this is mac os and will be run by launchd
# If the daemon shuts down too quickly, launchd may think it is a crash.
self.doWait(10000)
self.finish()
return
# Start listening for petitions
self.startHttpServer()
# *********************
# * Main Service loop *
# *********************
# Counter used to check ip changes only once every 10 seconds, for
# example
counter = 0
while self._isAlive:
counter += 1
try:
if counter % 5 == 0:
self.loop()
except Exception as e:
logger.error('Got exception on main loop: %s', e)
# In milliseconds, will break
self.doWait(1000)
self.finish()

View File

@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''
import os
import configparser
import base64
import pickle # nosec
from .. import types
CONFIGFILE = '/etc/udsactor/udsactor.cfg'
def readConfig() -> types.ActorConfigurationType:
try:
cfg = configparser.ConfigParser()
cfg.read(CONFIGFILE)
uds: configparser.SectionProxy = cfg['uds']
# Extract data:
base64Config = uds.get('config', None)
config = pickle.loads(base64.b64decode(base64Config.encode())) if base64Config else None # nosec: Read from root controled file, secure
base64Data = uds.get('data', None)
data = pickle.loads(base64.b64decode(base64Data.encode())) if base64Data else None # nosec: Read from root controled file, secure
return types.ActorConfigurationType(
actorType=uds.get('type', types.MANAGED),
host=uds.get('host', ''),
validateCertificate=uds.getboolean('validate', fallback=False),
master_token=uds.get('master_token', None),
own_token=uds.get('own_token', None),
restrict_net=uds.get('restrict_net', None),
pre_command=uds.get('pre_command', None),
runonce_command=uds.get('runonce_command', None),
post_command=uds.get('post_command', None),
log_level=int(uds.get('log_level', '2')),
config=config,
data=data
)
except Exception:
return types.ActorConfigurationType('', False)
def writeConfig(config: types.ActorConfigurationType) -> None:
cfg = configparser.ConfigParser()
cfg.add_section('uds')
uds: configparser.SectionProxy = cfg['uds']
uds['host'] = config.host
uds['validate'] = 'yes' if config.validateCertificate else 'no'
def writeIfValue(val, name):
if val:
uds[name] = val
writeIfValue(config.actorType, 'type')
writeIfValue(config.master_token, 'master_token')
writeIfValue(config.own_token, 'own_token')
writeIfValue(config.restrict_net, 'restrict_net')
writeIfValue(config.pre_command, 'pre_command')
writeIfValue(config.post_command, 'post_command')
writeIfValue(config.runonce_command, 'runonce_command')
uds['log_level'] = str(config.log_level)
if config.config: # Special case, encoded & dumped
uds['config'] = base64.b64encode(pickle.dumps(config.config)).decode()
if config.data: # Special case, encoded & dumped
uds['data'] = base64.b64encode(pickle.dumps(config.data)).decode()
# Ensures exists destination folder
dirname = os.path.dirname(CONFIGFILE)
if not os.path.exists(dirname):
os.mkdir(dirname, mode=0o700) # Will create only if route to path already exists, for example, /etc (that must... :-))
with open(CONFIGFILE, 'w') as f:
cfg.write(f)
os.chmod(CONFIGFILE, 0o0600) # Ensure only readable by root
def useOldJoinSystem() -> bool:
return False
def invokeScriptOnLogin() -> str:
return ''

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -31,15 +31,7 @@
import sys
name = sys.platform
is_windows = is_linux = is_mac = False
if sys.platform == 'win32':
from .windows import operations, store, runner
is_windows = True
elif sys.platform == 'darwin':
from .macos import operations, store, runner
is_mac = True
elif sys.platform == 'linux':
from .linux import operations, store, runner
is_linux = True
from .windows import operations, store # pylint: disable=unused-import
else:
raise Exception('Unsupported platform: {0}'.format(sys.platform))
from .linux import operations, store # pylint: disable=unused-import

View File

@ -36,8 +36,8 @@ import typing
import requests
from udsactor import types, tools
from udsactor.version import VERSION, BUILD
from . import types
from .version import VERSION
# Default public listen port
LISTEN_PORT = 43910
@ -90,9 +90,9 @@ class UDSApi: # pylint: disable=too-few-public-methods
Base for remote api accesses
"""
_host: str = ''
_validateCert: bool = True
_url: str = ''
_host: str
_validateCert: bool
_url: str
def __init__(self, host: str, validateCert: bool) -> None:
self._host = host
@ -103,17 +103,17 @@ class UDSApi: # pylint: disable=too-few-public-methods
logging.getLogger('urllib3').setLevel(logging.ERROR)
try:
warnings.simplefilter('ignore') # Disables all warnings
except Exception: # nosec: not interested in exceptions
except Exception:
pass
@property
def _headers(self) -> typing.MutableMapping[str, str]:
return {
'Content-Type': 'application/json',
'User-Agent': 'UDS Actor v{}/{}'.format(VERSION, BUILD),
'User-Agent': 'UDS Actor v{}'.format(VERSION),
}
def _api_url(self, method: str) -> str:
def _apiURL(self, method: str) -> str:
raise NotImplementedError
def _doPost(
@ -126,7 +126,7 @@ class UDSApi: # pylint: disable=too-few-public-methods
headers = headers or self._headers
try:
result = requests.post(
self._api_url(method),
self._apiURL(method),
data=json.dumps(payLoad),
headers=headers,
verify=self._validateCert,
@ -157,7 +157,7 @@ class UDSApi: # pylint: disable=too-few-public-methods
# UDS Broker API access
#
class UDSServerApi(UDSApi):
def _api_url(self, method: str) -> str:
def _apiURL(self, method: str) -> str:
return self._url + 'actor/v3/' + method
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
@ -178,10 +178,10 @@ class UDSServerApi(UDSApi):
priority=v['priority'],
isCustom=v['isCustom'],
)
except Exception: # nosec: not interested in exceptions
except Exception:
pass
def register(
def register( # pylint: disable=too-many-arguments, too-many-locals
self,
auth: str,
username: str,
@ -225,7 +225,7 @@ class UDSServerApi(UDSApi):
headers['X-Auth-Token'] = result.json()['token']
result = requests.post(
self._api_url('register'),
self._apiURL('register'),
data=json.dumps(data),
headers=headers,
verify=self._validateCert,
@ -252,7 +252,6 @@ class UDSServerApi(UDSApi):
'type': actor_type or types.MANAGED,
'token': token,
'version': VERSION,
'build': BUILD,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
}
r = self._doPost('initialize', payload)
@ -271,7 +270,6 @@ class UDSServerApi(UDSApi):
)
if r['os']
else None,
alias_token=r.get('alias_token'), # Possible alias for unmanaged
)
def ready(
@ -324,20 +322,20 @@ class UDSServerApi(UDSApi):
actor_type: typing.Optional[str],
token: str,
username: str,
session_type: str,
sessionType: str,
interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str],
) -> types.LoginResultInfoType:
if not token:
return types.LoginResultInfoType(
ip='0.0.0.0', hostname=UNKNOWN, dead_line=None, max_idle=None, session_id=None
ip='0.0.0.0', hostname=UNKNOWN, dead_line=None, max_idle=None
)
payload = {
'type': actor_type or types.MANAGED,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token,
'username': username,
'session_type': session_type,
'session_type': sessionType,
'secret': secret or '',
}
result = self._doPost('login', payload)
@ -346,7 +344,6 @@ class UDSServerApi(UDSApi):
hostname=result['hostname'],
dead_line=result['dead_line'],
max_idle=result['max_idle'],
session_id=result.get('session_id', ''),
)
def logout(
@ -354,8 +351,7 @@ class UDSServerApi(UDSApi):
actor_type: typing.Optional[str],
token: str,
username: str,
session_id: str,
session_type: str,
sessionType: str,
interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str],
) -> typing.Optional[str]:
@ -366,8 +362,7 @@ class UDSServerApi(UDSApi):
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token,
'username': username,
'session_type': session_type,
'session_id': session_id,
'session_type': sessionType,
'secret': secret or '',
}
return self._doPost('logout', payload) # Can be 'ok' or 'notified'
@ -386,17 +381,13 @@ class UDSServerApi(UDSApi):
return self._doPost('test', payLoad) == 'ok'
class UDSClientApi(UDSApi, metaclass=tools.Singleton):
_session_id: str = ''
_callback_url: str = ''
class UDSClientApi(UDSApi):
def __init__(self) -> None:
super().__init__('127.0.0.1:{}'.format(LISTEN_PORT), False)
# Replace base url
# Override base url
self._url = "https://{}/ui/".format(self._host)
def _api_url(self, method: str) -> str:
def _apiURL(self, method: str) -> str:
return self._url + method
def post(
@ -406,15 +397,13 @@ class UDSClientApi(UDSApi, metaclass=tools.Singleton):
) -> typing.Any:
return self._doPost(method=method, payLoad=payLoad, disableProxy=True)
def register(self, callback_url: str) -> None:
self._callback_url = callback_url
payLoad = {'callback_url': callback_url}
def register(self, callbackUrl: str) -> None:
payLoad = {'callback_url': callbackUrl}
self.post('register', payLoad)
def unregister(self, callback_url: str) -> None:
payLoad = {'callback_url': callback_url}
def unregister(self, callbackUrl: str) -> None:
payLoad = {'callback_url': callbackUrl}
self.post('unregister', payLoad)
self._callback_url = ''
def login(
self, username: str, sessionType: typing.Optional[str] = None
@ -422,26 +411,19 @@ class UDSClientApi(UDSApi, metaclass=tools.Singleton):
payLoad = {
'username': username,
'session_type': sessionType or UNKNOWN,
'callback_url': self._callback_url, # So we identify ourselves
}
result = self.post('login', payLoad)
res = types.LoginResultInfoType(
return types.LoginResultInfoType(
ip=result['ip'],
hostname=result['hostname'],
dead_line=result['dead_line'],
max_idle=result['max_idle'],
session_id=result['session_id'],
)
# Store session id for future use
self._session_id = res.session_id or ''
return res
def logout(self, username: str, sessionType: typing.Optional[str]) -> None:
payLoad = {
'username': username,
'session_type': sessionType or UNKNOWN,
'callback_url': self._callback_url, # So we identify ourselves
'session_id': self._session_id, # We now know the session id, provided on login
'session_type': sessionType or UNKNOWN
}
self.post('logout', payLoad)

View File

@ -36,13 +36,13 @@ import secrets
import subprocess
import typing
from udsactor import platform
from udsactor import rest
from udsactor import types
from udsactor import tools
from . import platform
from . import rest
from . import types
from . import tools
from udsactor.log import logger, DEBUG, INFO, ERROR, FATAL
from udsactor.http import clients_pool, server, cert
from .log import logger, DEBUG, INFO, ERROR, FATAL
from .http import clients_pool, server, cert
# def setup() -> None:
# cfg = platform.store.readConfig()
@ -60,12 +60,15 @@ from udsactor.http import clients_pool, server, cert
class CommonService: # pylint: disable=too-many-instance-attributes
_isAlive: bool = True
_rebootRequested: bool = False
_loggedIn: bool = False
_initialized: bool = False
_cfg: types.ActorConfigurationType
_api: rest.UDSServerApi
_interfaces: typing.List[types.InterfaceInfoType]
_secret: str
_certificate: types.CertificateInfoType
_clientsPool: clients_pool.UDSActorClientPool
_http: typing.Optional[server.HTTPServerThread]
@staticmethod
@ -98,7 +101,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
# 0 = OTHER, 10000 = DEBUG, 20000 = WARN, 30000 = INFO, 40000 = ERROR, 50000 = FATAL
# So this comes:
logger.setLevel([DEBUG, INFO, ERROR, FATAL][self._cfg.log_level])
# If windows, enable service logger FOR SERVICE only
# If windows, enable service logger
logger.enableServiceLogger()
socket.setdefaulttimeout(20)
@ -320,13 +323,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
return False
# Only removes master token for managed machines (will need it on next client execution)
# For unmanaged, if alias is present, replace master token with it
master_token = (
None
if self.isManaged()
else (initResult.alias_token or self._cfg.master_token)
)
# Replace master token with alias token if present
master_token = None if self.isManaged() else self._cfg.master_token
self._cfg = self._cfg._replace(
master_token=master_token,
own_token=initResult.own_token,
@ -335,10 +332,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
),
)
# On first successfull initialization request, master token will dissapear for managed hosts
# so it will be no more available (not needed anyway). For unmanaged, the master token will
# be replaced with an alias token.
platform.store.writeConfig(self._cfg)
# On first successfull initialization request, master token will dissapear for managed hosts so it will be no more available (not needed anyway)
if self.isManaged():
platform.store.writeConfig(self._cfg)
# Setup logger now
if self._cfg.own_token:
@ -374,23 +370,19 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self._http.stop()
# If logged in, notify UDS of logout (daemon stoped = no control = logout)
# For every connected client...
if self._cfg.own_token:
for client in clients_pool.UDSActorClientPool().clients:
if client.session_id:
try:
self._api.logout(
self._cfg.actorType,
self._cfg.own_token,
'',
client.session_id
or 'stop', # If no session id, pass "stop"
'',
self._interfaces,
self._secret,
)
except Exception as e:
logger.error('Error notifying final logout to UDS: %s', e)
if self._loggedIn and self._cfg.own_token:
self._loggedIn = False
try:
self._api.logout(
self._cfg.actorType,
self._cfg.own_token,
'',
'',
self._interfaces,
self._secret,
)
except Exception as e:
logger.error('Error notifying final logout to UDS: %s', e)
self.notifyStop()
@ -472,9 +464,8 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self.checkIpsChanged()
# Now check if every registered client is already there (if logged in OFC)
for lost_client in clients_pool.UDSActorClientPool().lost_clients():
logger.info('Lost client: {}'.format(lost_client))
self.logout('client_unavailable', '', lost_client.session_id or '') # '' means "all clients"
if self._loggedIn and not self._clientsPool.ping():
self.logout('client_unavailable', '')
except Exception as e:
logger.error('Exception on main service loop: %s', e)
@ -495,7 +486,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self, username: str, sessionType: typing.Optional[str] = None
) -> types.LoginResultInfoType:
result = types.LoginResultInfoType(
ip='', hostname='', dead_line=None, max_idle=None, session_id=None
ip='', hostname='', dead_line=None, max_idle=None
)
master_token = None
secret = None
@ -522,9 +513,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
secret,
)
if (
result.session_id
): # If logged in, process it. client_pool will take account of login response to client and session
if result.logged_in:
logger.debug('Login successful')
self._loggedIn = True
script = platform.store.invokeScriptOnLogin()
if script:
logger.info('Executing script on login: {}'.format(script))
@ -533,12 +524,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes
return result
def logout(
self,
username: str,
session_type: typing.Optional[str],
session_id: typing.Optional[str],
) -> None:
def logout(self, username: str, sessionType: typing.Optional[str]) -> None:
master_token = self._cfg.master_token
# Own token will not be set if UDS did not assigned the initialized VM to an user
@ -551,26 +537,24 @@ class CommonService: # pylint: disable=too-many-instance-attributes
self._cfg.actorType,
token,
username,
session_id or '',
session_type or '',
sessionType or '',
self._interfaces,
self._secret,
)
!= 'ok' # Can return also "notified", that means the logout has not been processed by UDS
!= 'ok'
):
logger.info(
'Logout from %s ignored as required by uds broker', username
)
logger.info('Logout from %s ignored as required by uds broker', username)
return
self.onLogout(username, session_id or '')
self._loggedIn = False
self.onLogout(username)
if not self.isManaged():
self.uninitialize()
# ******************************************************
# Methods that CAN BE overriden by specific OS Actor
# ******************************************************
# ****************************************
# Methods that CAN BE overriden by actors
# ****************************************
def doWait(self, miliseconds: int) -> None:
'''
Invoked to wait a bit
@ -611,5 +595,5 @@ class CommonService: # pylint: disable=too-many-instance-attributes
return 'ok'
def onLogout(self, userName: str, session_id: str) -> None:
def onLogout(self, userName: str) -> None:
logger.debug('On logout invoked for {}'.format(userName))

View File

@ -30,43 +30,14 @@
'''
import threading
import ipaddress
import time
import typing
import functools
if typing.TYPE_CHECKING:
from udsactor.types import InterfaceInfoType
# Simple cache for n seconds (default = 30) decorator
def cache(seconds: int = 30) -> typing.Callable:
'''
Simple cache for n seconds (default = 30) decorator
'''
def decorator(func) -> typing.Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs) -> typing.Any:
if not hasattr(wrapper, 'cache'):
wrapper.cache = {} # type: ignore
cache = wrapper.cache # type: ignore
# Compose a key for the cache
key = '{}:{}'.format(args, kwargs)
if key in cache:
if time.time() - cache[key][0] < seconds:
return cache[key][1]
# Call the function
result = func(*args, **kwargs)
cache[key] = (time.time(), result)
return result
return wrapper
return decorator
# Simple sub-script exectution thread
class ScriptExecutorThread(threading.Thread):
def __init__(self, script: str) -> None:
super(ScriptExecutorThread, self).__init__()
self.script = script
@ -76,40 +47,14 @@ class ScriptExecutorThread(threading.Thread):
try:
logger.debug('Executing script: {}'.format(self.script))
exec(
self.script, globals(), None
) # nosec: exec is fine, it's a "trusted" script
exec(self.script, globals(), None) # pylint: disable=exec-used
except Exception as e:
logger.error('Error executing script: {}'.format(e))
logger.exception()
class Singleton(type):
'''
Metaclass for singleton pattern
Usage:
class MyClass(metaclass=Singleton):
...
'''
_instance: typing.Optional[typing.Any]
# We use __init__ so we customise the created class from this metaclass
def __init__(self, *args, **kwargs) -> None:
self._instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs) -> typing.Any:
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
# Convert "X.X.X.X/X" to ipaddress.IPv4Network
def strToNoIPV4Network(
net: typing.Optional[str],
) -> typing.Optional[ipaddress.IPv4Network]:
def strToNoIPV4Network(net: typing.Optional[str]) -> typing.Optional[ipaddress.IPv4Network]:
if not net: # Empty or None
return None
try:

View File

@ -51,22 +51,16 @@ class InitializationResultType(typing.NamedTuple):
own_token: typing.Optional[str] = None
unique_id: typing.Optional[str] = None
os: typing.Optional[ActorOsConfigurationType] = None
alias_token: typing.Optional[str] = None
class LoginResultInfoType(typing.NamedTuple):
ip: str
hostname: str
dead_line: typing.Optional[int]
max_idle: typing.Optional[int]
session_id: typing.Optional[str]
max_idle: typing.Optional[int] # Not provided by broker
@property
def logged_in(self) -> bool:
return bool(self.session_id)
class ClientInfo(typing.NamedTuple):
url: str
session_id: str
return self.hostname != '' or self.ip != ''
class CertificateInfoType(typing.NamedTuple):
private_key: str

View File

@ -1,2 +1 @@
VERSION = '4.0.0'
BUILD = '20220901'
VERSION = '3.5.0'

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014-2022 Virtual Cable S.L.U.
# Copyright (c) 2014-2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
@ -45,46 +45,32 @@ import win32con
from .. import types
from ..log import logger
def checkPermissions() -> bool:
return shell.IsUserAnAdmin()
def getErrorMessage(resultCode: int = 0) -> str:
# sys_fs_enc = sys.getfilesystemencoding() or 'mbcs'
msg = win32api.FormatMessage(resultCode)
return msg
def getComputerName() -> str:
return win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]:
obj = win32com.client.Dispatch("WbemScripting.SWbemLocator")
wmobj = obj.ConnectServer("localhost", "root\\cimv2")
adapters = wmobj.ExecQuery(
"Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True"
)
adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True")
try:
for obj in adapters:
for ip in obj.IPAddress:
if ':' in ip: # Is IPV6, skip this
continue
if (
ip is None
or ip == ''
or ip.startswith('169.254')
or ip.startswith('0.')
): # If single link ip, or no ip
if ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.'): # If single link ip, or no ip
continue
yield types.InterfaceInfoType(
name=obj.Caption, mac=obj.MACAddress, ip=ip
)
yield types.InterfaceInfoType(name=obj.Caption, mac=obj.MACAddress, ip=ip)
except Exception:
return
def getDomainName() -> str:
'''
Will return the domain name if we belong a domain, else None
@ -101,19 +87,9 @@ def getDomainName() -> str:
return domain
def getWindowsVersion() -> typing.Tuple[int, int, int, int, str]:
return win32api.GetVersionEx()
def getVersion() -> str:
verinfo = getWindowsVersion()
# Remove platform id i
return 'Windows-{}.{} Build {} ({})'.format(
verinfo[0], verinfo[1], verinfo[2], verinfo[4]
)
EWX_LOGOFF = 0x00000000
EWX_SHUTDOWN = 0x00000001
EWX_REBOOT = 0x00000002
@ -121,53 +97,31 @@ EWX_FORCE = 0x00000004
EWX_POWEROFF = 0x00000008
EWX_FORCEIFHUNG = 0x00000010
def reboot(flags: int = EWX_FORCEIFHUNG | EWX_REBOOT) -> None:
hproc = win32api.GetCurrentProcess()
htok = win32security.OpenProcessToken(
hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
)
privs = (
(
win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME),
win32security.SE_PRIVILEGE_ENABLED,
),
)
htok = win32security.OpenProcessToken(hproc, win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
privs = ((win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), win32security.SE_PRIVILEGE_ENABLED),)
win32security.AdjustTokenPrivileges(htok, 0, privs)
win32api.ExitWindowsEx(flags, 0)
def loggoff() -> None:
win32api.ExitWindowsEx(EWX_LOGOFF)
def renameComputer(newName: str) -> bool:
'''
Changes the computer name
Returns True if reboot needed
'''
# Needs admin privileges to work
if (
ctypes.windll.kernel32.SetComputerNameExW(
DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)
)
== 0
): # @UndefinedVariable
if ctypes.windll.kernel32.SetComputerNameExW(DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName)) == 0: # @UndefinedVariable
# win32api.FormatMessage -> returns error string
# win32api.GetLastError -> returns error code
# (just put this comment here to remember to log this when logger is available)
error = getErrorMessage()
computerName = win32api.GetComputerNameEx(
win32con.ComputerNamePhysicalDnsHostname
)
raise Exception(
'Error renaming computer from {} to {}: {}'.format(
computerName, newName, error
)
)
computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname)
raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error))
return True
NETSETUP_JOIN_DOMAIN = 0x00000001
NETSETUP_ACCT_CREATE = 0x00000002
NETSETUP_ACCT_DELETE = 0x00000004
@ -178,10 +132,7 @@ NETSETUP_MACHINE_PWD_PASSED = 0x00000080
NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400
NETSETUP_DEFER_SPN_SET = 0x1000000
def joinDomain(
domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False
) -> None:
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False) -> None:
'''
Joins machine to a windows domain
:param domain: Domain to join to
@ -198,9 +149,7 @@ def joinDomain(
account = domain + '\\' + account
# Do log
flags: typing.Any = (
NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
)
flags: typing.Any = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN
if executeInOneStep:
flags |= NETSETUP_JOIN_WITH_NEW_NAME
@ -214,31 +163,18 @@ def joinDomain(
lpAccount = LPCWSTR(account)
lpPassword = LPCWSTR(password)
res = ctypes.windll.netapi32.NetJoinDomain(
None, lpDomain, lpOu, lpAccount, lpPassword, flags
)
res = ctypes.windll.netapi32.NetJoinDomain(None, lpDomain, lpOu, lpAccount, lpPassword, flags)
# Machine found in another ou, use it and warn this on log
if res == 2224:
flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN)
res = ctypes.windll.netapi32.NetJoinDomain(
None, lpDomain, None, lpAccount, lpPassword, flags
)
res = ctypes.windll.netapi32.NetJoinDomain(None, lpDomain, None, lpAccount, lpPassword, flags)
if res:
# Log the error
error = getErrorMessage(res)
if res == 1355:
error = "DC Is not reachable"
logger.error('Error joining domain: {}, {}'.format(error, res))
raise Exception(
'Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(
domain,
account,
', under OU {}'.format(ou) if ou is not None else '',
res,
error,
)
)
raise Exception('Error joining domain {}, with credentials {}/*****{}: {}, {}'.format(domain, account, ', under OU {}'.format(ou) if ou is not None else '', res, error))
def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
# lpUser = LPCWSTR(user)
@ -252,10 +188,7 @@ def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None:
if res:
# Log the error, and raise exception to parent
error = getErrorMessage(res)
raise Exception(
'Error changing password for user {}: {} {}'.format(user, res, error)
)
raise Exception('Error changing password for user {}: {} {}'.format(user, res, error))
class LASTINPUTINFO(ctypes.Structure): # pylint: disable=too-few-public-methods
_fields_ = [
@ -263,20 +196,16 @@ class LASTINPUTINFO(ctypes.Structure): # pylint: disable=too-few-public-methods
('dwTime', ctypes.c_uint),
]
def initIdleDuration(atLeastSeconds: int): # pylint: disable=unused-argument
'''
In windows, there is no need to set screensaver
'''
return
def getIdleDuration() -> float:
try:
lastInputInfo = LASTINPUTINFO()
lastInputInfo.cbSize = ctypes.sizeof(
lastInputInfo
) # pylint: disable=attribute-defined-outside-init
lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) # pylint: disable=attribute-defined-outside-init
if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0:
return 0
current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value
@ -288,27 +217,22 @@ def getIdleDuration() -> float:
logger.error('Getting idle duration: {}'.format(e))
return 0
def getCurrentUser() -> str:
'''
Returns current logged in username
'''
return os.environ['USERNAME']
def getSessionType() -> str:
'''
Known values:
* Unknown -> No SESSIONNAME environment variable
* Console -> Local session
* RDP-Tcp#[0-9]+ -> RDP Session
Known values:
* Unknown -> No SESSIONNAME environment variable
* Console -> Local session
* RDP-Tcp#[0-9]+ -> RDP Session
'''
return os.environ.get('SESSIONNAME', 'unknown')
def writeToPipe(
pipeName: str, bytesPayload: bytes, waitForResponse: bool
) -> typing.Optional[bytes]:
def writeToPipe(pipeName: str, bytesPayload: bytes, waitForResponse: bool) -> typing.Optional[bytes]:
# (str, bytes, bool) -> Optional[bytes]
try:
with open(pipeName, 'r+b', 0) as f:
@ -320,11 +244,8 @@ def writeToPipe(
except Exception:
return None
def forceTimeSync() -> None:
try:
subprocess.call(
[r'c:\WINDOWS\System32\w32tm.exe', ' /resync']
) # , '/rediscover'])
subprocess.call([r'c:\WINDOWS\System32\w32tm.exe', ' /resync']) # , '/rediscover'])
except Exception as e:
logger.error('Error invoking time sync command: %s', e)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019-2022 Virtual Cable S.L.U.
# Copyright (c) 2019 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
@ -11,7 +11,7 @@
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#

View File

@ -183,7 +183,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True)
return 'done'
def onLogout(self, userName: str, session_id: str) -> None:
def onLogout(self, userName) -> None:
logger.debug('Windows onLogout invoked: {}, {}'.format(userName, self._user))
try:
p = win32security.GetBinarySid(REMOTE_USERS_SID)

View File

@ -146,7 +146,7 @@ class Ui_UdsActorSetupDialog(object):
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_serviceToken.setText(_translate("UdsActorSetupDialog", "Service Token"))
self.serviceToken.setToolTip(_translate("UdsActorSetupDialog", "UDS Service Token"))
self.serviceToken.setToolTip(_translate("UdsActorSetupDialog", "UDS user with administration rights (Will not be stored on template)"))
self.serviceToken.setWhatsThis(_translate("UdsActorSetupDialog", "<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 token for this image.</p></body></html>"))
self.label_loglevel.setText(_translate("UdsActorSetupDialog", "Log Level"))
self.label_restrictNet.setText(_translate("UdsActorSetupDialog", "Restrict Net"))

View File

@ -1,15 +1,3 @@
udsclient3 (4.0.0) stable; urgency=medium
* Upgraded to 4.0.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 1 Jul 2022 15:12:10 +0200
udsclient3 (4.0.0) stable; urgency=medium
* Upgraded to 3.6.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Fri, 1 Jul 2022 14:12:10 +0200
udsclient3 (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release

View File

@ -1 +1 @@
10
9

View File

@ -1,2 +1,2 @@
udsclient3_4.0.0_all.deb admin optional
udsclient3_4.0.0_amd64.buildinfo admin optional
udsclient3_3.5.0_all.deb admin optional
udsclient3_3.5.0_amd64.buildinfo admin optional

View File

@ -38,7 +38,7 @@ import webbrowser
import threading
import typing
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QSettings
from uds.rest import RestApi, RetryException, InvalidVersion, UDSException
@ -79,10 +79,10 @@ class UDSClient(QtWidgets.QMainWindow):
self.ui.info.setText('Initializing...')
screen_geometry = QtGui.QGuiApplication.primaryScreen().geometry()
screen = QtWidgets.QDesktopWidget().screenGeometry()
mysize = self.geometry()
hpos = (screen_geometry.width() - mysize.width()) // 2
vpos = (screen_geometry.height() - mysize.height() - mysize.height()) // 2
hpos = (screen.width() - mysize.width()) // 2
vpos = (screen.height() - mysize.height() - mysize.height()) // 2
self.move(hpos, vpos)
self.animTimer = QtCore.QTimer()

View File

@ -45,7 +45,7 @@ class UdsApplication(QtWidgets.QApplication):
tunnel.kill()
def event(self, evnt: QtCore.QEvent) -> bool:
if evnt.type() == QtCore.QEvent.FileOpen: # type: ignore
if evnt.type() == QtCore.QEvent.FileOpen:
fe = typing.cast(QtGui.QFileOpenEvent, evnt)
logger.debug('Got url: %s', fe.url().url())
fe.accept()

View File

@ -29,11 +29,13 @@
'''
@author: Adolfo Gómez, dkmaster at dkmon dot com
'''
VERSION = '3.6.0'
from __future__ import unicode_literals
VERSION = '3.5.0'
__title__ = 'udclient'
__version__ = VERSION
__build__ = 0x010712
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
__build__ = 0x010760
__author__ = 'Adolfo Gómez'
__license__ = "BSD 3-clause"
__copyright__ = "Copyright 2014-2022 VirtualCable S.L.U."
__copyright__ = "Copyright 2014-2017 VirtualCable S.L.U."

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 Virtual Cable S.L.U.
# Copyright (c) 2021 Virtual Cable S.L.U.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,

10
legacy_actors/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
bin
*_enterprise*
udsactor*.deb
udsactor*.build
udsactor*.changes
/udsactor_*.dsc
/udsactor_*.tar.xz
/udsactor_*_amd64.buildinfo
/udsactor*.rpm
linux/debian/files

17
legacy_actors/.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>actors</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}/src</path>
</pydev_pathproperty>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

1
legacy_actors/linux/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/udsactor-*[1-9].*.spec

View File

@ -0,0 +1,98 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Directories
SOURCEDIR := ../src
LIBDIR := $(DESTDIR)/usr/share/UDSActor
BINDIR := $(DESTDIR)/usr/bin
SBINDIR = $(DESTDIR)/usr/sbin
APPSDIR := $(DESTDIR)/usr/share/applications
CFGDIR := $(DESTDIR)/etc/udsactor
INITDIR := $(DESTDIR)/etc/init.d
POLKITDIR := $(DESTDIR)/usr/share/polkit-1/actions/
XDGAUTOSTARTDIR := $(DESTDIR)/etc/xdg/autostart
KDEAUTOSTARTDIR := $(DESTDIR)/usr/share/autostart
PYC := $(shell find $(SOURCEDIR) -name '*.py[co]')
CACHES := $(shell find $(SOURCEDIR) -name '__pycache__')
clean:
rm -rf $(PYC) $(CACHES) $(DESTDIR)
install-udsactor-xrdp:
mkdir -p $(BINDIR)
cp scripts/uds-sesman.sh $(BINDIR)/uds-sesman
cp scripts/uds-wait-session.sh $(BINDIR)/uds-wait-session
chmod 0755 $(BINDIR)/uds-sesman
chmod 0755 $(BINDIR)/uds-wait-session
install-udsactor-nx:
mkdir -p $(BINDIR)
cp scripts/udsnxstart.sh $(BINDIR)/udsnxstart
cp scripts/udsnxstop.sh $(BINDIR)/udsnxstop
chmod 0755 $(BINDIR)/udsnxstart
chmod 0755 $(BINDIR)/udsnxstop
install-udsactor:
rm -rf $(DESTDIR)
mkdir -p $(LIBDIR)
mkdir -p $(BINDIR)
mkdir -p $(SBINDIR)
mkdir -p $(APPSDIR)
mkdir -p $(CFGDIR)
mkdir -p $(POLKITDIR)
mkdir -p $(XDGAUTOSTARTDIR)
mkdir -p $(KDEAUTOSTARTDIR)
mkdir $(LIBDIR)/img
# Cleans up .pyc and cache folders
rm -f $(PYC) $(CACHES)
cp -r $(SOURCEDIR)/udsactor $(LIBDIR)/udsactor
cp $(SOURCEDIR)/img/uds.png $(LIBDIR)/img
cp $(SOURCEDIR)/UDSActorConfig.py $(LIBDIR)
cp $(SOURCEDIR)/UDSActorUser.py $(LIBDIR)
# QT Dialogs & resources
cp $(SOURCEDIR)/*_ui.py $(LIBDIR)
cp $(SOURCEDIR)/UDSActor_rc.py $(LIBDIR)
# Menu GUI app
cp desktop/UDS_Actor_Configuration.desktop $(APPSDIR)
# Autostart elements for gnome/kde
cp desktop/UDSActorTool.desktop $(XDGAUTOSTARTDIR)
cp desktop/UDSActorTool.desktop $(KDEAUTOSTARTDIR)
# scripts
cp scripts/udsactor $(BINDIR)
cp scripts/UDSActorConfig-pkexec $(SBINDIR)
cp scripts/UDSActorTool-startup $(BINDIR)
cp scripts/udsvapp ${BINDIR}
# Policy to run as administrator
cp policy/org.openuds.pkexec.UDSActorConfig.policy $(POLKITDIR)
# Fix permissions
chmod 755 $(BINDIR)/udsactor
chmod 755 $(BINDIR)/udsvapp
chmod 755 $(BINDIR)/UDSActorTool-startup
chmod 755 $(SBINDIR)/UDSActorConfig-pkexec
chmod 755 $(LIBDIR)/UDSActorConfig.py
chmod 755 $(LIBDIR)/UDSActorUser.py
chmod 644 $(POLKITDIR)/org.openuds.pkexec.UDSActorConfig.policy
# If for red hat based, copy init.d
ifeq ($(DISTRO),rh)
mkdir -p $(INITDIR)
cp debian/udsactor.init $(INITDIR)/udsactor
ln -s /usr/share/UDSActor/UDSActorConfig.py $(SBINDIR)/UDSActorConfig
ln -s /usr/share/UDSActor/UDSActorUser.py $(BINDIR)/UDSActorTool
endif
# chmod 0755 $(BINDIR)/udsactor
uninstall:
rm -rf $(LIBDIR)
# rm -f $(BINDIR)/udsactor
rm -rf $(CFGDIR)

View File

@ -0,0 +1,34 @@
#!/bin/bash
VERSION=`cat ../../VERSION`
RELEASE=1
top=`pwd`
# Debian based
dpkg-buildpackage -b
cat udsactor-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsactor-$VERSION.spec
# Now fix dependencies for opensuse
cat udsactor-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"name udsactor"/"name udsactor-opensuse"/g |
sed -e s/"PyQt4"/"python-qt4"/g |
sed -e s/"libXScrnSaver"/"libXss1"/g > udsactor-opensuse-$VERSION.spec
# Right now, udsactor-xrdp-1.7.0.spec is not needed
for pkg in udsactor-$VERSION.spec udsactor-opensuse-$VERSION.spec; do
rm -rf rpm
for folder in SOURCES BUILD RPMS SPECS SRPMS; do
mkdir -p rpm/$folder
done
rpmbuild -v -bb --clean --buildroot=$top/rpm/BUILD/$pkg-root --target noarch $pkg 2>&1
done
#rm udsactor-$VERSION

3
legacy_actors/linux/debian/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/udsactor/
/udsactor-xrdp/
/udsactor-nx/

View File

@ -0,0 +1,53 @@
udsactor (3.0.0) stable; urgency=medium
* Upgraded to 3.0.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Wed, 10 Jul 2019 9:24:10 +0200
udsactor (2.2.1) stable; urgency=medium
* Upgraded to 2.2.1 release
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 2 Oct 2018 12:44:12 +0200
udsactor (2.2.0) stable; urgency=medium
* Upgraded to 2.2.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 19 Oct 2017 16:44:12 +0200
udsactor (2.1.0) stable; urgency=medium
* Fixes for 2.1.0 release
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 19 Jan 2017 08:00:22 +0200
udsactor (2.0.0) stable; urgency=medium
* Upgrade for 2.0.0
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:39:21 +0100
udsactor (1.9.1) stable; urgency=medium
* Upgrade for 1.9.1
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 01 Mar 2016 03:19:21 +0100
udsactor (1.9.0) stable; urgency=medium
* Upgrade for 1.9.0 (fixed package version)
-- Adolfo Gómez García <agomez@virtualcable.es> Tue, 05 May 2015 07:10:27 +0200
udsactor (1.7.5) stable; urgency=medium
* Upgrade for 1.7.5
-- Adolfo Gómez García <agomez@virtualcable.es> Thu, 23 Apr 2015 06:08:53 +0200
udsactor (1.7.0) stable; urgency=medium
* Initial release.
-- Adolfo Gómez García <agomez@virtualcable.es> Mon, 17 Nov 2014 05:32:41 +0100

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1,17 @@
Source: udsactor
Section: admin
Priority: optional
Maintainer: Adolfo Gómez García <agomez@virtualcable.es>
Build-Depends: debhelper (>= 7), po-debconf
Standards-Version: 3.9.2
Homepage: http://www.virtualcable.es
Package: udsactor
Section: admin
Priority: optional
Architecture: all
Depends: policykit-1(>=0.100), python3-requests (>=0.8.2), python3-pyqt4 (>=4.9), python3-six(>=1.1), python3 (>=3.4), libxss1, xscreensaver, ${misc:Depends}
Recommends: python3-prctl(>=1.1.1)
Description: Actor for Universal Desktop Services (UDS) Broker
This package provides the required components to allow this machine to work on an environment managed by UDS Broker.

View File

@ -0,0 +1,26 @@
Format-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135
Name: udsactor
Maintainer: Adolfo Gómez García
Source: http://www.udsenterprise.com/
Copyright: 2014 Virtual Cable S.L.U.
License: BSD-3-clause
License: GPL-2+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.

View File

@ -0,0 +1 @@
readme.txt

View File

@ -0,0 +1,46 @@
#!/usr/bin/make -f
# -*- makefile -*-
configure: configure-stamp
configure-stamp:
dh_testdir
touch configure-stamp
build: build-arch build-indep
build-arch: build-stamp
build-indep: build-stamp
build-stamp: configure-stamp
dh_testdir
$(MAKE)
touch $@
clean:
dh_testdir
dh_testroot
rm -f build-stamp configure-stamp
dh_clean
install: build
dh_testdir
dh_testroot
dh_prep
dh_installdirs
$(MAKE) DESTDIR=$(CURDIR)/debian/udsactor install-udsactor
$(MAKE) DESTDIR=$(CURDIR)/debian/udsactor-xrdp install-udsactor-xrdp
$(MAKE) DESTDIR=$(CURDIR)/debian/udsactor-nx install-udsactor-nx
binary-arch: build install
# emptyness
binary-indep: build install
dh_testdir
dh_testroot
dh_installchangelogs
dh_installdocs
dh_installdebconf
dh_installinit --no-start
dh_python2=python
dh_compress
dh_link
dh_fixperms
dh_installdeb
dh_shlibdeps
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep
.PHONY: build clean binary-indep binary install configure

View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -0,0 +1,37 @@
#!/bin/sh
NXNODECFG=/usr/NX/etc/node.cfg
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
TMPFILE=$(mktemp /tmp/node.cfg.XXXXX)
trap "rm -f $TMPFILE" 0
cat $NXNODECFG | sed -e "s/.*udsnxst.*//; s/\(UserScriptAfterSessionStart *=.*\)/#\1/;s/\(UserScriptAfterSessionClose *=.*\)/#\1/" > $TMPFILE
echo >> $TMPFILE
echo "# Added by udsactor-nx (udsnxstart and udsnxstop)" >> $TMPFILE
echo UserScriptAfterSessionStart = \"/usr/bin/udsnxstart\" >> $TMPFILE
echo UserScriptAfterSessionClose = \"/usr/bin/udsnxstop\" >> $TMPFILE
cp $TMPFILE $NXNODECFG
invoke-rc.d nxserver restart
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
# Don't know why, but descriptors get "weird" when launched daemon, so we tell here to debconf to stop.
# Solved not starting the service right now, defered to next reboot
exit 0

View File

@ -0,0 +1,30 @@
#!/bin/sh
NXNODECFG=/usr/NX/etc/node.cfg
. /usr/share/debconf/confmodule
set -e
case "$1" in
purge)
;;
remove)
if [ -f $NXNODECFG ]; then
TMPFILE=$(mktemp /tmp/node.cfg.XXXXX)
trap "rm -f $TMPFILE" 0
cat $NXNODECFG | sed -e "s/.*udsnxst.*//" > $TMPFILE
cp $TMPFILE $NXNODECFG
invoke-rc.d nxserver restart
fi
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#

View File

@ -0,0 +1,39 @@
#!/bin/sh
SESMANFILE=/etc/pam.d/xrdp-sesman
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
trap "cp $SESMANFILE $SESMANFILE.uds.old" 0
TMPFILE=$(mktemp /tmp/sesman.XXXXX)
trap "rm -f $TMPFILE" 0
grep -v uds $SESMANFILE > $TMPFILE # Removes all UDS lines from sesman if they exists
echo >> $TMPFILE
echo "# Added by udsactor-xrdp" >> $TMPFILE
echo "session optional pam_exec.so /usr/bin/uds-sesman" >> $TMPFILE
cp $TMPFILE $SESMANFILE
trap "rm -f $TMPFILE" 0
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
# Don't know why, but descriptors get "weird" when launched daemon, so we tell here to debconf to stop.
# Solved not starting the service right now, defered to next reboot
exit 0

View File

@ -0,0 +1,30 @@
#!/bin/sh -e
SESMANFILE=/etc/pam.d/xrdp-sesman
. /usr/share/debconf/confmodule
set -e
case "$1" in
purge)
;;
remove)
if [ -f $SESMANFILE ]; then
TMPFILE=$(mktemp /tmp/sesman.XXXXX)
trap "rm -f $TMPFILE" 0
grep -v uds $SESMANFILE > $TMPFILE # Removes all UDS lines from sesman if they exists
cp $TMPFILE $SESMANFILE
trap "rm -f $TMPFILE" 0
fi
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#

View File

@ -0,0 +1,45 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
db_version 2.0
# This conf script is capable of backing up
db_capb backup
if [ -f /etc/udsactor/udsactor.cfg ] && [ "$1" != "reconfigure" ]; then
echo "/etc/udsactor/udsactor.cfg already exists, leaving untouched."
exit 0
fi
STATE=1
while [ "$STATE" != 0 -a "$STATE" != 4 ]; do
case "$STATE" in
1)
db_input high udsactor/host || true
;;
2)
db_input high udsactor/secure || true
;;
3)
db_input high udsactor/masterKey || true
;;
esac
if db_go; then
STATE=$(($STATE + 1))
else
STATE=$(($STATE - 1))
fi
done
# If "cancelled", exit
if [ "$STATE" = 0 ]; then
exit 0
fi
# If using reconfigure, and already exists an configuration file, move it to a backup
if [ -f /etc/udsactor/udsactor.cfg ] && [ "$1" = "reconfigure" ]; then
echo "/etc/udsactor.cfg backup to /etc/udsactor.cfg.back"
mv /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.back
fi

View File

@ -0,0 +1,23 @@
#!/bin/sh -e
### BEGIN INIT INFO
# Provides: udsactor
# Required-Start: $local_fs $remote_fs $network $syslog $named
# Required-Stop: $local_fs $remote_fs $network $syslog $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: UDS Actor
### END INIT INFO
#
# . /lib/lsb/init-functions
case "$1" in
start|stop|restart)
/usr/bin/udsactor $1
;;
force-reload)
/usr/bin/udsactor restart
;;
*) echo "Usage: $0 {start|stop|restart|force-reload}" >&2; exit 1 ;;
esac

View File

@ -0,0 +1,2 @@
/usr/share/UDSActor/UDSActorConfig.py /usr/sbin/UDSActorConfig
/usr/share/UDSActor/UDSActorUser.py /usr/bin/UDSActorTool

View File

@ -0,0 +1,54 @@
#!/bin/sh
. /usr/share/debconf/confmodule
set -e
case "$1" in
configure)
/usr/bin/python3 -m compileall /usr/share/UDSActor > /dev/nul 2>&1
# If new "fresh" install or if configuration file has disappeared...
if [ "$2" = "" ] || [ ! -f /etc/udsactor/udsactor.cfg ]; then
db_get udsactor/host
host=$RET
db_get udsactor/secure
ssl=$RET
if [ "$ssl" = "true" ]; then
ssl=True;
else
ssl=False;
fi
db_get udsactor/masterKey
masterKey=$RET
# If already has a config file there
if [ -f /etc/udsactor/udsactor.cfg ]; then
cp /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.dpkg-old
fi
echo "[uds]" > /etc/udsactor/udsactor.cfg
echo "host = $host" >> /etc/udsactor/udsactor.cfg
echo "logLevel = 30000" >> /etc/udsactor/udsactor.cfg
echo "ssl = $ssl" >> /etc/udsactor/udsactor.cfg
echo "masterKey = $masterKey" >> /etc/udsactor/udsactor.cfg
fi
# Fix perms so only root can access "masterKey"
chmod 0700 /etc/udsactor
chmod 0600 /etc/udsactor/udsactor.cfg
chown root:root /etc/udsactor
chown root:root /etc/udsactor/udsactor.cfg
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0

View File

@ -0,0 +1,14 @@
#!/bin/sh -e
. /usr/share/debconf/confmodule
set -e
if [ "$1" = "purge" ] ; then
if [ -f /etc/udsactor/udsactor.cfg ]; then
mv /etc/udsactor/udsactor.cfg /etc/udsactor/udsactor.cfg.dpkg-backup
# Remove .pyc leaved behind
rm -rf /usr/share/UDSActor || true > /dev/null 2>&1
fi
fi

View File

@ -0,0 +1 @@
#! /bin/bash -e

View File

@ -0,0 +1,20 @@
Template: udsactor/host
Type: string
Default:
Description: UDS Server address:
The actor needs the address of the server in order to communicate with it.
Provide here full address (or i) of the UDS server
Template: udsactor/secure
Type: boolean
Default: true
Description: Use secure (https) connection to communicate with UDS server?
If selected, the communication will be done using https.
If not selected, the communication will be done using http
Template: udsactor/masterKey
Type: string
Default:
Description: Master Key:
This key is available on UDS Administration interface.
Look for it under configuration, on Security tab.

View File

@ -0,0 +1,12 @@
[Desktop Entry]
Name=UDS Actor Tool
Comment=UDS Actor Userspace tools
Exec=/usr/bin/UDSActorTool-startup
Icon=/usr/share/UDSActor/img/uds.png
Terminal=false
Type=Application
NoDisplay=true
X-KDE-autostart-after=panel
X-KDE-StartupNotify=false
X-DBUS-StartupType=None
X-KDE-UniqueApplet=false

View File

@ -0,0 +1,11 @@
[Desktop Entry]
Name=UDS Actor Configuration
Version=1.0
Exec=/usr/sbin/UDSActorConfig-pkexec
Comment=UDS Actor Configuration Application. (Must be executed as root)
Icon=/usr/share/UDSActor/img/uds.png
Type=Application
Terminal=false
StartupNotify=true
Encoding=UTF-8
Categories=Settings;System;

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="org.freedesktop.policykit.pkexec.run-UDSActorConfig">
<description>Run UDS Actor Configuration Program</description>
<message>Authentication is required to run UDS Actor Configuration</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/UDSActorConfig</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">TRUE</annotate>
</action>
</policyconfig>

View File

@ -0,0 +1,3 @@
UDSActor is the client actor needed to get machines managed by UDS Broker.
Please, visit http://www.udsenterprise.com for more information

View File

@ -0,0 +1,3 @@
#!/bin/sh
# pkexec env DISPLAY=$DISPLAY QT_X11_NO_MITSHM=1 "/usr/sbin/UDSActorConfig" "$@"
pkexec "/usr/sbin/UDSActorConfig" "$@"

View File

@ -0,0 +1,10 @@
#!/bin/sh
# Simple hack to wait for systray to be present
# Exec tool if not already runned by session manager
ps -ef | grep "$USER" | grep -v grep | grep -v UDSActorTool-startup | grep 'UDSActorTool' -q
# If not already running
if [ $? -eq 1 ]; then
sleep 5
exec /usr/bin/UDSActorTool
fi

View File

@ -0,0 +1,13 @@
#!/bin/sh
env > /tmp/env.txt
if [ "$PAM_TYPE" = "open_session" ]; then
nohup /usr/bin/udsactor login $PAM_USER &
# Wait in backgroud to TTY to close (close_session is not being invoked right now)
nohup /usr/bin/uds-wait-session &
elif [ "$PAM_TYPE" = "close_session" ]; then
nohup /usr/bin/udsactor logout $PAM_USER &
fi
return 0

View File

@ -0,0 +1,12 @@
#!/bin/sh
while :
do
sleep 5 # Wait 5 seconds between checks
found=`ps -f -u$PAM_USER | grep -v grep | grep -v uds-wait-session | grep "$PAM_TTY" | wc -l`
if [ "$found" = "0" ]; then
/usr/bin/udsactor logout $PAM_USER
exit 0
fi
done

View File

@ -0,0 +1,6 @@
#!/bin/sh
FOLDER=/usr/share/UDSActor
cd $FOLDER
exec python3 -m udsactor.linux.UDSActorService $@

View File

@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/bin/udsactor login $2 &

View File

@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/bin/udsactor logout $2 &

View File

@ -0,0 +1,5 @@
#!/bin/sh
/usr/bin/udsactor login "$USER"
$@
/usr/bin/udsactor logout "$USER"

View File

@ -1,5 +1,5 @@
%define _topdir %(echo $PWD)/rpm
%define name udsactor-unmanaged
%define name udsactor
%define version 0.0.0
%define release 1
%define buildroot %{_topdir}/%{name}-%{version}-%{release}-root
@ -11,7 +11,7 @@ Release: %{release}
Summary: Actor for Universal Desktop Services (UDS) Broker
License: BSD3
Group: Admin
Requires: python3-six python3-requests python3-qt5 libXScrnSaver
Requires: python-six python-requests PyQt4 libXScrnSaver
Vendor: Virtual Cable S.L.U.
URL: http://www.udsenterprise.com
Provides: udsactor
@ -23,7 +23,7 @@ Provides: udsactor
%install
curdir=`pwd`
cd ../..
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-udsactor-unmanaged
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-udsactor
cd $curdir
%clean
@ -51,13 +51,13 @@ fi
rm -rf /usr/share/UDSActor > /dev/null 2>&1
%description
This package provides the required components to allow this unmanaged machine to work on an environment managed by UDS Broker.
This package provides the required components to allow this machine to work on an environment managed by UDS Broker.
%files
%defattr(-,root,root)
/etc/udsactor
/etc/xdg/autostart/UDSActorTool.desktop
/etc/systemd/system/udsactor.service
/etc/init.d/udsactor
/usr/bin/UDSActorTool-startup
/usr/bin/udsactor
/usr/bin/udsvapp

6
legacy_actors/src/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
build
dist
*.spec
.idea
*_enterprise*
/samples/

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="images">
<file>img/uds.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="UDSActorConfig"
version="1.6.0.0"
processorArchitecture="x86"
/>
<description>Description</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

View File

@ -0,0 +1,119 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# 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
'''
from __future__ import unicode_literals
import sys
import os
from PyQt4 import QtCore, QtGui
import six
from udsactor import store
from udsactor import REST
from udsactor import utils
from udsactor.log import logger
from setup_dialog_ui import Ui_UdsActorSetupDialog
class UDSConfigDialog(QtGui.QDialog):
def __init__(self, data, parent=None):
QtGui.QDialog.__init__(self, parent)
self.ui = Ui_UdsActorSetupDialog()
self.ui.setupUi(self)
if data is not None:
self.ui.host.setText(data.get('host', ''))
self.ui.masterKey.setText(data.get('masterKey', ''))
self.ui.useSSl.setCurrentIndex(1 if data.get('ssl', False) is True else 0)
self.ui.logLevelComboBox.setCurrentIndex(int(data.get('logLevel', '10000')) / 10000 - 1)
def _getCfg(self):
return {
'host': six.text_type(self.ui.host.text()),
'masterKey': six.text_type(self.ui.masterKey.text()),
'ssl': self.ui.useSSl.currentIndex() == 1,
'logLevel': (self.ui.logLevelComboBox.currentIndex() + 1) * 10000
}
def textChanged(self):
enableButtons = self.ui.host.text() != '' and self.ui.masterKey.text() != ''
self.ui.testButton.setEnabled(enableButtons)
self.ui.saveButton.setEnabled(enableButtons)
def cancelAndDiscard(self):
logger.debug('Cancelling changes')
self.close()
def testParameters(self):
logger.debug('Testing connection')
try:
cfg = self._getCfg()
api = REST.Api(
cfg['host'], cfg['masterKey'], cfg['ssl'])
api.test()
QtGui.QMessageBox.information(
self, 'Test Passed', 'The test was executed successfully', QtGui.QMessageBox.Ok)
logger.info('Test was passed successfully')
except Exception as e:
logger.info('Test error: {}'.format(utils.exceptionToMessage(e)))
QtGui.QMessageBox.critical(self, 'Test Error', utils.exceptionToMessage(e), QtGui.QMessageBox.Ok)
def acceptAndSave(self):
cfg = self._getCfg()
store.writeConfig(cfg)
self.close()
if __name__ == "__main__":
# If to be run as "sudo" on linux, we will need this to avoid problems
if 'linux' in sys.platform:
os.environ['QT_X11_NO_MITSHM'] = '1'
app = QtGui.QApplication(sys.argv)
if store.checkPermissions() is False:
QtGui.QMessageBox.critical(None, 'Notice', 'This Program must be executed as administrator', QtGui.QMessageBox.Ok)
sys.exit(1)
# Read configuration
cfg = store.readConfig()
if cfg is not None:
logger.setLevel(int(cfg.get('logLevel', 20000)))
else:
logger.setLevel(20000)
myapp = UDSConfigDialog(cfg)
myapp.show()
sys.exit(app.exec_())

Some files were not shown because too many files have changed in this diff Show More