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/ # /server/
*_enterprise *_enterprise
/server/openuds.sublime-project
/server/openuds.sublime-workspace
# /server/src/ # /server/src/
/server/src/taskmanager.pid /server/src/taskmanager.pid
@ -86,6 +88,7 @@
# /server/src/uds/ # /server/src/uds/
/server/src/uds/*_enterprise.py /server/src/uds/*_enterprise.py
/server/src/uds/fixtures /server/src/uds/fixtures
/server/src/uds/tests
# /server/src/uds/auths/ # /server/src/uds/auths/
/server/src/uds/auths/*-enterprise /server/src/uds/auths/*-enterprise
@ -162,4 +165,3 @@
.vscode .vscode
.mypy_cache .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. 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.** **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 | cat udsactor-template.spec |
sed -e s/"version 0.0.0"/"version ${VERSION}"/g | sed -e s/"version 0.0.0"/"version ${VERSION}"/g |
sed -e s/"release 1"/"release ${RELEASE}"/g > udsactor-$VERSION.spec 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 # Now fix dependencies for opensuse
# Note that, although on opensuse the library is "libXss1" on newer, # 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 # 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 udsactor-opensuse-$VERSION.spec; do
for pkg in udsactor-*$VERSION.spec; do for pkg in udsactor-$VERSION.spec; do
rm -rf rpm rm -rf rpm
for folder in SOURCES BUILD RPMS SPECS SRPMS; do 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 udsactor (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release * Upgraded to 3.5.0 release

View File

@ -1,3 +1,3 @@
udsactor-unmanaged_3.6.0_all.deb admin optional udsactor-unmanaged_3.5.0_all.deb admin optional
udsactor_3.6.0_all.deb admin optional udsactor_3.5.0_all.deb admin optional
udsactor_3.6.0_amd64.buildinfo 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 @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
# pylint: disable=invalid-name
import sys import sys
import os import os
import PyQt5 # noqa import PyQt5 # pylint: disable=unused-import
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMainWindow from PyQt5.QtWidgets import QMainWindow
from udsactor.log import logger, INFO from udsactor.log import logger, INFO
from udsactor.client import UDSClientQApp from udsactor.client import UDSClientQApp
from udsactor import platform from udsactor.platform import operations
if __name__ == "__main__": if __name__ == "__main__":
logger.setLevel(INFO) logger.setLevel(INFO)
# Ensure idle operations is initialized on start # 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' os.environ['QT_X11_NO_MITSHM'] = '1'
UDSClientQApp.setQuitOnLastWindowClosed(False) UDSClientQApp.setQuitOnLastWindowClosed(False)
qApp = UDSClientQApp(sys.argv) qApp = UDSClientQApp(sys.argv)
if platform.is_windows or platform.is_mac: if 'linux' not in sys.platform:
# The "hidden window" is not needed on linux # The "hidden window" is only needed to process events on Windows
# Not needed on Linux # Not needed on Linux
mw = QMainWindow() mw = QMainWindow()
mw.showMinimized() # Start minimized, will be hidden (not destroyed) as soon as qApp.init is invoked 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 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2020-2022 Virtual Cable S.L.U. # Copyright (c) 2020 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # Redistribution and use in source and binary forms, with or without modification,
@ -35,7 +35,7 @@ import os
import logging import logging
import typing 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 from PyQt5.QtWidgets import QApplication, QDialog, QFileDialog, QMessageBox
import udsactor import udsactor

View File

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

View File

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

View File

@ -214,10 +214,10 @@
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="serviceToken"> <widget class="QLineEdit" name="serviceToken">
<property name="toolTip"> <property name="toolTip">
<string>UDS Service Token</string> <string>UDS user with administration rights (Will not be stored on template)</string>
</property> </property>
<property name="whatsThis"> <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> </property>
</widget> </widget>
</item> </item>
@ -268,10 +268,10 @@
<item row="3" column="1"> <item row="3" column="1">
<widget class="QLineEdit" name="restrictNet"> <widget class="QLineEdit" name="restrictNet">
<property name="toolTip"> <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>
<property name="whatsThis"> <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> </property>
</widget> </widget>
</item> </item>

View File

@ -35,4 +35,4 @@ from . import platform
__title__ = 'udsactor' __title__ = 'udsactor'
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>' __author__ = 'Adolfo Gómez <dkmaster@dkmon.com>'
__license__ = "BSD 3-clause" __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.checkIdle()
self.checkDeadLine() 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) self.api.logout(user + self._extraLogoff, sessionType)
logger.info('Notified logout for %s (%s)', user, sessionType) # Log logout 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 pixmap: 'QPixmap' = self._qApp.primaryScreen().grabWindow(0) # type: ignore
ba = QByteArray() ba = QByteArray()
buffer = QBuffer(ba) buffer = QBuffer(ba)
buffer.open(QIODevice.WriteOnly) # type: ignore buffer.open(QIODevice.WriteOnly)
pixmap.save(buffer, 'PNG') pixmap.save(buffer, 'PNG')
buffer.close() buffer.close()
scrBase64 = bytes(ba.toBase64()).decode() # type: ignore # there are problems with Pylance and connects on PyQt5... :) 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._app = app
self.port = -1 self.port = -1
self.id = secrets.token_urlsafe(24) self.id = secrets.token_urlsafe(16)
@property @property
def url(self) -> str: def url(self) -> str:

View File

@ -33,8 +33,8 @@ import json
import typing import typing
import requests import requests
from udsactor import tools, types
from udsactor.log import logger from ..log import logger
# For avoid proxy on localhost connections # For avoid proxy on localhost connections
NO_PROXY = { NO_PROXY = {
@ -42,108 +42,55 @@ NO_PROXY = {
'https': None, 'https': None,
} }
class UDSActorClientPool:
class UDSActorClientPool(metaclass=tools.Singleton): _clientUrl: typing.List[str]
_clients: typing.List[types.ClientInfo]
def __init__(self) -> None: def __init__(self) -> None:
self._clients = [] self._clientUrl = []
def _post( def _post(self, method: str, data: typing.MutableMapping[str, str], timeout=2) -> typing.List[requests.Response]:
self, removables: typing.List[str] = []
session_id: typing.Optional[str], result: typing.List[typing.Any] = []
method: str, for clientUrl in self._clientUrl:
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
try: try:
result.append( result.append(requests.post(clientUrl + '/' + method, data=json.dumps(data), verify=False, timeout=timeout, proxies=NO_PROXY))
(
client,
requests.post(
clientUrl + '/' + method,
data=json.dumps(data),
verify=False,
timeout=timeout,
proxies=NO_PROXY, # type: ignore
),
)
)
except Exception as e: except Exception as e:
logger.info( # If cannot request to a clientUrl, remove it from list
'Could not connect with client %s: %s. ', logger.info('Could not connect with client %s: %s. Removed from registry.', e, clientUrl)
e, removables.append(clientUrl)
clientUrl,
) # Remove failed connections
result.append((client, None)) for clientUrl in removables:
self.unregister(clientUrl)
return result return result
@property def register(self, clientUrl: str) -> None:
def clients(self) -> typing.List[types.ClientInfo]:
return self._clients
def register(self, client_url: str) -> None:
# Remove first if exists, to avoid duplicates # Remove first if exists, to avoid duplicates
self.unregister(client_url) self.unregister(clientUrl)
# And add it again # 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: def unregister(self, clientUrl: str) -> None:
"""Set the session id for a client self._clientUrl = list((i for i in self._clientUrl if i != clientUrl))
Args: def executeScript(self, script: str) -> None:
clientUrl (str): _description_ self._post('script', {'script': script}, timeout=30)
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 unregister(self, client_url: str) -> None: def logout(self) -> None:
# remove client url from array if found self._post('logout', {})
for i, client in enumerate(self._clients):
if client.url == client_url:
self._clients.pop(i)
return
def executeScript(self, session_id: typing.Optional[str], script: str) -> None: def message(self, message: str) -> None:
self._post(session_id, 'script', {'script': script}, timeout=30) self._post('message', {'message': message})
def logout(self, session_id: typing.Optional[str]) -> None: def ping(self) -> bool:
self._post(session_id, 'logout', {}) 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: def screenshot(self) -> typing.Optional[str]: # Screenshot are returned as base64
self._post(session_id, 'message', {'message': message}) for r in self._post('screenshot', {}, timeout=3):
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
try: try:
return r.json()['result'] return r.json()['result']
except Exception: except Exception:

View File

@ -30,23 +30,19 @@
''' '''
import typing import typing
from udsactor.http import handler, clients_pool from . import handler
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from udsactor.service import CommonService from ..service import CommonService
class LocalProvider(handler.Handler): class LocalProvider(handler.Handler):
def post_login(self) -> typing.Any: def post_login(self) -> typing.Any:
result = self._service.login(self._params['username'], self._params['session_type']) 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() return result._asdict()
def post_logout(self) -> typing.Any: 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' return 'ok'
def post_ping(self) -> typing.Any: def post_ping(self) -> typing.Any:

View File

@ -71,7 +71,7 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
# Very simple path & params splitter # Very simple path & params splitter
path = self.path.split('?')[0][1:].split('/') 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 handlerType: typing.Optional[typing.Type['Handler']] = None

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
import subprocess # nosec import os
from .common import renamers from .common import renamers
from ...log import logger from ...log import logger
@ -46,8 +46,8 @@ def rename(newName: str) -> bool:
hostname.write(newName) hostname.write(newName)
# Force system new name # Force system new name
subprocess.run(['hostnamectl', 'set-hostname', newName]) # nosec: ok, we are root os.system('/bin/hostname {}'.format(newName))
subprocess.run(['/bin/hostname', newName]) # nosec: ok, we are root os.system('/usr/bin/hostnamectl set-hostname {}'.format(newName))
# add name to "hosts" # add name to "hosts"
with open('/etc/hosts', 'r') as 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()) r = client.login(sys.argv[2], platform.operations.getSessionType())
print('{},{},{},{}\n'.format(r.ip, r.hostname, r.max_idle, r.dead_line or '')) print('{},{},{},{}\n'.format(r.ip, r.hostname, r.max_idle, r.dead_line or ''))
elif sys.argv[1] == 'logout': elif sys.argv[1] == 'logout':
client.logout(sys.argv[2], platform.operations.getSessionType()) client.logout(sys.argv[2])
except Exception as e: except Exception as e:
logger.exception() logger.exception()
logger.error('Got exception while processing command: %s', e) logger.error('Got exception while processing command: %s', e)

View File

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

View File

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

View File

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

View File

@ -35,8 +35,6 @@ import typing
if sys.platform == 'win32': if sys.platform == 'win32':
from .windows.log import LocalLogger from .windows.log import LocalLogger
elif sys.platform == 'darwin':
from .macos.log import LocalLogger
else: else:
from .linux.log import LocalLogger from .linux.log import LocalLogger
@ -57,7 +55,7 @@ class Logger:
self.logLevel = INFO self.logLevel = INFO
self.localLogger = LocalLogger() self.localLogger = LocalLogger()
self.remoteLogger = None 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: 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 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2014-2022 Virtual Cable S.L.U. # Copyright (c) 2014 Virtual Cable S.L.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # 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, # * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation # this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution. # 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 # may be used to endorse or promote products derived from this software
# without specific prior written permission. # without specific prior written permission.
# #
@ -31,15 +31,7 @@
import sys import sys
name = sys.platform name = sys.platform
is_windows = is_linux = is_mac = False
if sys.platform == 'win32': if sys.platform == 'win32':
from .windows import operations, store, runner from .windows import operations, store # pylint: disable=unused-import
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
else: 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 import requests
from udsactor import types, tools from . import types
from udsactor.version import VERSION, BUILD from .version import VERSION
# Default public listen port # Default public listen port
LISTEN_PORT = 43910 LISTEN_PORT = 43910
@ -90,9 +90,9 @@ class UDSApi: # pylint: disable=too-few-public-methods
Base for remote api accesses Base for remote api accesses
""" """
_host: str = '' _host: str
_validateCert: bool = True _validateCert: bool
_url: str = '' _url: str
def __init__(self, host: str, validateCert: bool) -> None: def __init__(self, host: str, validateCert: bool) -> None:
self._host = host self._host = host
@ -103,17 +103,17 @@ class UDSApi: # pylint: disable=too-few-public-methods
logging.getLogger('urllib3').setLevel(logging.ERROR) logging.getLogger('urllib3').setLevel(logging.ERROR)
try: try:
warnings.simplefilter('ignore') # Disables all warnings warnings.simplefilter('ignore') # Disables all warnings
except Exception: # nosec: not interested in exceptions except Exception:
pass pass
@property @property
def _headers(self) -> typing.MutableMapping[str, str]: def _headers(self) -> typing.MutableMapping[str, str]:
return { return {
'Content-Type': 'application/json', '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 raise NotImplementedError
def _doPost( def _doPost(
@ -126,7 +126,7 @@ class UDSApi: # pylint: disable=too-few-public-methods
headers = headers or self._headers headers = headers or self._headers
try: try:
result = requests.post( result = requests.post(
self._api_url(method), self._apiURL(method),
data=json.dumps(payLoad), data=json.dumps(payLoad),
headers=headers, headers=headers,
verify=self._validateCert, verify=self._validateCert,
@ -157,7 +157,7 @@ class UDSApi: # pylint: disable=too-few-public-methods
# UDS Broker API access # UDS Broker API access
# #
class UDSServerApi(UDSApi): class UDSServerApi(UDSApi):
def _api_url(self, method: str) -> str: def _apiURL(self, method: str) -> str:
return self._url + 'actor/v3/' + method return self._url + 'actor/v3/' + method
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]: def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
@ -178,10 +178,10 @@ class UDSServerApi(UDSApi):
priority=v['priority'], priority=v['priority'],
isCustom=v['isCustom'], isCustom=v['isCustom'],
) )
except Exception: # nosec: not interested in exceptions except Exception:
pass pass
def register( def register( # pylint: disable=too-many-arguments, too-many-locals
self, self,
auth: str, auth: str,
username: str, username: str,
@ -225,7 +225,7 @@ class UDSServerApi(UDSApi):
headers['X-Auth-Token'] = result.json()['token'] headers['X-Auth-Token'] = result.json()['token']
result = requests.post( result = requests.post(
self._api_url('register'), self._apiURL('register'),
data=json.dumps(data), data=json.dumps(data),
headers=headers, headers=headers,
verify=self._validateCert, verify=self._validateCert,
@ -252,7 +252,6 @@ class UDSServerApi(UDSApi):
'type': actor_type or types.MANAGED, 'type': actor_type or types.MANAGED,
'token': token, 'token': token,
'version': VERSION, 'version': VERSION,
'build': BUILD,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces], 'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
} }
r = self._doPost('initialize', payload) r = self._doPost('initialize', payload)
@ -271,7 +270,6 @@ class UDSServerApi(UDSApi):
) )
if r['os'] if r['os']
else None, else None,
alias_token=r.get('alias_token'), # Possible alias for unmanaged
) )
def ready( def ready(
@ -324,20 +322,20 @@ class UDSServerApi(UDSApi):
actor_type: typing.Optional[str], actor_type: typing.Optional[str],
token: str, token: str,
username: str, username: str,
session_type: str, sessionType: str,
interfaces: typing.Iterable[types.InterfaceInfoType], interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str], secret: typing.Optional[str],
) -> types.LoginResultInfoType: ) -> types.LoginResultInfoType:
if not token: if not token:
return types.LoginResultInfoType( 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 = { payload = {
'type': actor_type or types.MANAGED, 'type': actor_type or types.MANAGED,
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces], 'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token, 'token': token,
'username': username, 'username': username,
'session_type': session_type, 'session_type': sessionType,
'secret': secret or '', 'secret': secret or '',
} }
result = self._doPost('login', payload) result = self._doPost('login', payload)
@ -346,7 +344,6 @@ class UDSServerApi(UDSApi):
hostname=result['hostname'], hostname=result['hostname'],
dead_line=result['dead_line'], dead_line=result['dead_line'],
max_idle=result['max_idle'], max_idle=result['max_idle'],
session_id=result.get('session_id', ''),
) )
def logout( def logout(
@ -354,8 +351,7 @@ class UDSServerApi(UDSApi):
actor_type: typing.Optional[str], actor_type: typing.Optional[str],
token: str, token: str,
username: str, username: str,
session_id: str, sessionType: str,
session_type: str,
interfaces: typing.Iterable[types.InterfaceInfoType], interfaces: typing.Iterable[types.InterfaceInfoType],
secret: typing.Optional[str], secret: typing.Optional[str],
) -> typing.Optional[str]: ) -> typing.Optional[str]:
@ -366,8 +362,7 @@ class UDSServerApi(UDSApi):
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces], 'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
'token': token, 'token': token,
'username': username, 'username': username,
'session_type': session_type, 'session_type': sessionType,
'session_id': session_id,
'secret': secret or '', 'secret': secret or '',
} }
return self._doPost('logout', payload) # Can be 'ok' or 'notified' return self._doPost('logout', payload) # Can be 'ok' or 'notified'
@ -386,17 +381,13 @@ class UDSServerApi(UDSApi):
return self._doPost('test', payLoad) == 'ok' return self._doPost('test', payLoad) == 'ok'
class UDSClientApi(UDSApi, metaclass=tools.Singleton): class UDSClientApi(UDSApi):
_session_id: str = ''
_callback_url: str = ''
def __init__(self) -> None: def __init__(self) -> None:
super().__init__('127.0.0.1:{}'.format(LISTEN_PORT), False) super().__init__('127.0.0.1:{}'.format(LISTEN_PORT), False)
# Override base url
# Replace base url
self._url = "https://{}/ui/".format(self._host) self._url = "https://{}/ui/".format(self._host)
def _api_url(self, method: str) -> str: def _apiURL(self, method: str) -> str:
return self._url + method return self._url + method
def post( def post(
@ -406,15 +397,13 @@ class UDSClientApi(UDSApi, metaclass=tools.Singleton):
) -> typing.Any: ) -> typing.Any:
return self._doPost(method=method, payLoad=payLoad, disableProxy=True) return self._doPost(method=method, payLoad=payLoad, disableProxy=True)
def register(self, callback_url: str) -> None: def register(self, callbackUrl: str) -> None:
self._callback_url = callback_url payLoad = {'callback_url': callbackUrl}
payLoad = {'callback_url': callback_url}
self.post('register', payLoad) self.post('register', payLoad)
def unregister(self, callback_url: str) -> None: def unregister(self, callbackUrl: str) -> None:
payLoad = {'callback_url': callback_url} payLoad = {'callback_url': callbackUrl}
self.post('unregister', payLoad) self.post('unregister', payLoad)
self._callback_url = ''
def login( def login(
self, username: str, sessionType: typing.Optional[str] = None self, username: str, sessionType: typing.Optional[str] = None
@ -422,26 +411,19 @@ class UDSClientApi(UDSApi, metaclass=tools.Singleton):
payLoad = { payLoad = {
'username': username, 'username': username,
'session_type': sessionType or UNKNOWN, 'session_type': sessionType or UNKNOWN,
'callback_url': self._callback_url, # So we identify ourselves
} }
result = self.post('login', payLoad) result = self.post('login', payLoad)
res = types.LoginResultInfoType( return types.LoginResultInfoType(
ip=result['ip'], ip=result['ip'],
hostname=result['hostname'], hostname=result['hostname'],
dead_line=result['dead_line'], dead_line=result['dead_line'],
max_idle=result['max_idle'], 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: def logout(self, username: str, sessionType: typing.Optional[str]) -> None:
payLoad = { payLoad = {
'username': username, 'username': username,
'session_type': sessionType or UNKNOWN, '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
} }
self.post('logout', payLoad) self.post('logout', payLoad)

View File

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

View File

@ -30,43 +30,14 @@
''' '''
import threading import threading
import ipaddress import ipaddress
import time
import typing import typing
import functools
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from udsactor.types import InterfaceInfoType 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): class ScriptExecutorThread(threading.Thread):
def __init__(self, script: str) -> None: def __init__(self, script: str) -> None:
super(ScriptExecutorThread, self).__init__() super(ScriptExecutorThread, self).__init__()
self.script = script self.script = script
@ -76,40 +47,14 @@ class ScriptExecutorThread(threading.Thread):
try: try:
logger.debug('Executing script: {}'.format(self.script)) logger.debug('Executing script: {}'.format(self.script))
exec( exec(self.script, globals(), None) # pylint: disable=exec-used
self.script, globals(), None
) # nosec: exec is fine, it's a "trusted" script
except Exception as e: except Exception as e:
logger.error('Error executing script: {}'.format(e)) logger.error('Error executing script: {}'.format(e))
logger.exception() 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 # Convert "X.X.X.X/X" to ipaddress.IPv4Network
def strToNoIPV4Network( def strToNoIPV4Network(net: typing.Optional[str]) -> typing.Optional[ipaddress.IPv4Network]:
net: typing.Optional[str],
) -> typing.Optional[ipaddress.IPv4Network]:
if not net: # Empty or None if not net: # Empty or None
return None return None
try: try:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -183,7 +183,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService):
operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True) operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True)
return 'done' 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)) logger.debug('Windows onLogout invoked: {}, {}'.format(userName, self._user))
try: try:
p = win32security.GetBinarySid(REMOTE_USERS_SID) 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.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.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.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.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_loglevel.setText(_translate("UdsActorSetupDialog", "Log Level"))
self.label_restrictNet.setText(_translate("UdsActorSetupDialog", "Restrict Net")) 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 udsclient3 (3.5.0) stable; urgency=medium
* Upgraded to 3.5.0 release * 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_3.5.0_all.deb admin optional
udsclient3_4.0.0_amd64.buildinfo admin optional udsclient3_3.5.0_amd64.buildinfo admin optional

View File

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

View File

@ -45,7 +45,7 @@ class UdsApplication(QtWidgets.QApplication):
tunnel.kill() tunnel.kill()
def event(self, evnt: QtCore.QEvent) -> bool: 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) fe = typing.cast(QtGui.QFileOpenEvent, evnt)
logger.debug('Got url: %s', fe.url().url()) logger.debug('Got url: %s', fe.url().url())
fe.accept() fe.accept()

View File

@ -29,11 +29,13 @@
''' '''
@author: Adolfo Gómez, dkmaster at dkmon dot com @author: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
VERSION = '3.6.0' from __future__ import unicode_literals
VERSION = '3.5.0'
__title__ = 'udclient' __title__ = 'udclient'
__version__ = VERSION __version__ = VERSION
__build__ = 0x010712 __build__ = 0x010760
__author__ = 'Adolfo Gómez <dkmaster@dkmon.com>' __author__ = 'Adolfo Gómez'
__license__ = "BSD 3-clause" __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 -*- # -*- coding: utf-8 -*-
# #
# Copyright (c) 2022 Virtual Cable S.L.U. # Copyright (c) 2021 Virtual Cable S.L.U.
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without modification, # 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 _topdir %(echo $PWD)/rpm
%define name udsactor-unmanaged %define name udsactor
%define version 0.0.0 %define version 0.0.0
%define release 1 %define release 1
%define buildroot %{_topdir}/%{name}-%{version}-%{release}-root %define buildroot %{_topdir}/%{name}-%{version}-%{release}-root
@ -11,7 +11,7 @@ Release: %{release}
Summary: Actor for Universal Desktop Services (UDS) Broker Summary: Actor for Universal Desktop Services (UDS) Broker
License: BSD3 License: BSD3
Group: Admin Group: Admin
Requires: python3-six python3-requests python3-qt5 libXScrnSaver Requires: python-six python-requests PyQt4 libXScrnSaver
Vendor: Virtual Cable S.L.U. Vendor: Virtual Cable S.L.U.
URL: http://www.udsenterprise.com URL: http://www.udsenterprise.com
Provides: udsactor Provides: udsactor
@ -23,7 +23,7 @@ Provides: udsactor
%install %install
curdir=`pwd` curdir=`pwd`
cd ../.. cd ../..
make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-udsactor-unmanaged make DESTDIR=$RPM_BUILD_ROOT DISTRO=rh install-udsactor
cd $curdir cd $curdir
%clean %clean
@ -51,13 +51,13 @@ fi
rm -rf /usr/share/UDSActor > /dev/null 2>&1 rm -rf /usr/share/UDSActor > /dev/null 2>&1
%description %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 %files
%defattr(-,root,root) %defattr(-,root,root)
/etc/udsactor /etc/udsactor
/etc/xdg/autostart/UDSActorTool.desktop /etc/xdg/autostart/UDSActorTool.desktop
/etc/systemd/system/udsactor.service /etc/init.d/udsactor
/usr/bin/UDSActorTool-startup /usr/bin/UDSActorTool-startup
/usr/bin/udsactor /usr/bin/udsactor
/usr/bin/udsvapp /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