mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-20 14:03:49 +03:00
Actor work
This commit is contained in:
parent
2cf869150f
commit
07d337364d
@ -53,13 +53,16 @@ class UDSConfigDialog(QDialog):
|
||||
|
||||
def __init__(self):
|
||||
QDialog.__init__(self, None)
|
||||
# Get local config config
|
||||
config: udsactor.types.ActorConfigurationType = udsactor.store.readConfig()
|
||||
|
||||
self.ui = Ui_UdsActorSetupDialog()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.host.setText('172.27.0.1:8443')
|
||||
self.ui.username.setText('admin')
|
||||
self.ui.password.setText('temporal')
|
||||
self.ui.postConfigCommand.setText(r'c:\windows\post-uds.bat')
|
||||
self.ui.preCommand.setText(r'c:\windows\pre-uds.bat')
|
||||
self.ui.host.setText(config.host)
|
||||
self.ui.username.setText('')
|
||||
self.ui.password.setText('')
|
||||
self.ui.postConfigCommand.setText('')
|
||||
self.ui.preCommand.setText('')
|
||||
self.ui.runonceCommand.setText(r'c:\windows\runonce.bat')
|
||||
|
||||
@property
|
||||
@ -67,7 +70,7 @@ class UDSConfigDialog(QDialog):
|
||||
return udsactor.rest.REST(self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1)
|
||||
|
||||
def browse(self, lineEdit: 'QLineEdit', caption: str) -> None:
|
||||
name = QFileDialog.getOpenFileName(parent=self, caption='')[0] # Returns tuple (filename, filter)
|
||||
name = QFileDialog.getOpenFileName(parent=self, caption=caption)[0] # Returns tuple (filename, filter)
|
||||
if name:
|
||||
lineEdit.setText(name)
|
||||
|
||||
@ -85,10 +88,12 @@ class UDSConfigDialog(QDialog):
|
||||
self._host = self.ui.host.text()
|
||||
self.ui.authenticators.clear()
|
||||
auth: udsactor.types.AuthenticatorType
|
||||
for auth in self.api.enumerateAuthenticators():
|
||||
self.ui.authenticators.addItem(auth.auth, userData=auth)
|
||||
# Last, add "admin" authenticator (for uds root user)
|
||||
self.ui.authenticators.addItem('Administration', userData=udsactor.types.AuthenticatorType('admin', 'admin', 'admin', 'admin', 1, False))
|
||||
auths = list(self.api.enumerateAuthenticators())
|
||||
if auths:
|
||||
for auth in auths:
|
||||
self.ui.authenticators.addItem(auth.auth, userData=auth)
|
||||
# Last, add "admin" authenticator (for uds root user)
|
||||
self.ui.authenticators.addItem('Administration', userData=udsactor.types.AuthenticatorType('admin', 'admin', 'admin', 'admin', 1, False))
|
||||
|
||||
def textChanged(self):
|
||||
enableButtons = bool(self.ui.host.text() and self.ui.username.text() and self.ui.password.text() and self.ui.authenticators.currentText())
|
||||
@ -99,8 +104,7 @@ class UDSConfigDialog(QDialog):
|
||||
|
||||
def registerWithUDS(self):
|
||||
# Get network card. Will fail if no network card is available, but don't mind (not contempled)
|
||||
data: udsactor.types.InterfaceInfo = next(udsactor.operations.getNetworkInfo())
|
||||
|
||||
data: udsactor.types.InterfaceInfoType = next(udsactor.operations.getNetworkInfo())
|
||||
try:
|
||||
token = self.api.register(
|
||||
self.ui.authenticators.currentData().auth,
|
||||
@ -134,9 +138,9 @@ if __name__ == "__main__":
|
||||
|
||||
app = 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)
|
||||
if udsactor.store.checkPermissions() is False:
|
||||
QMessageBox.critical(None, 'UDS Actor', 'This Program must be executed as administrator', QMessageBox.Ok)
|
||||
sys.exit(1)
|
||||
|
||||
myapp = UDSConfigDialog()
|
||||
myapp.show()
|
||||
|
@ -29,7 +29,6 @@
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import uuid
|
||||
import warnings
|
||||
import json
|
||||
import logging
|
||||
@ -39,11 +38,6 @@ import requests
|
||||
|
||||
from . import types
|
||||
|
||||
from .info import VERSION
|
||||
from .utils import exceptionToMessage
|
||||
from .log import logger
|
||||
|
||||
|
||||
class RESTError(Exception):
|
||||
ERRCODE = 0
|
||||
|
||||
@ -105,12 +99,30 @@ class REST:
|
||||
pass
|
||||
|
||||
@property
|
||||
def headers(self) -> typing.MutableMapping[str, str]:
|
||||
def _headers(self) -> typing.MutableMapping[str, str]:
|
||||
return {'content-type': 'application/json'}
|
||||
|
||||
def _login(self, auth: str, username: str, password: str) -> typing.MutableMapping[str, str]:
|
||||
try:
|
||||
# First, try to login
|
||||
authInfo = {'auth': auth, 'username': username, 'password': password}
|
||||
headers = self._headers
|
||||
result = requests.post(self.url + 'auth/login', data=json.dumps(authInfo), headers=headers, verify=self.validateCert)
|
||||
if not result.ok or result.json()['result'] == 'error':
|
||||
raise Exception() # Invalid credentials
|
||||
except requests.ConnectionError as e:
|
||||
raise RESTConnectionError(str(e))
|
||||
except Exception as e:
|
||||
raise RESTError('Invalid credentials')
|
||||
|
||||
headers['X-Auth-Token'] = result.json()['token']
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
|
||||
try:
|
||||
result = requests.get(self.url + 'auth/auths', headers=self.headers, verify=self.validateCert, timeout=4)
|
||||
result = requests.get(self.url + 'auth/auths', headers=self._headers, verify=self.validateCert, timeout=4)
|
||||
if result.ok:
|
||||
for v in sorted(result.json(), key=lambda x: x['priority']):
|
||||
yield types.AuthenticatorType(
|
||||
@ -125,7 +137,18 @@ class REST:
|
||||
pass
|
||||
|
||||
|
||||
def register(self, auth: str, username: str, password: str, ip: str, mac: str, preCommand: str, runOnceCommand: str, postCommand: str, logLevel: int) -> str:
|
||||
def register( #pylint: disable=too-many-arguments
|
||||
self,
|
||||
auth: str,
|
||||
username: str,
|
||||
password: str,
|
||||
ip: str,
|
||||
mac: str,
|
||||
preCommand: str,
|
||||
runOnceCommand: str,
|
||||
postCommand: str,
|
||||
logLevel: int
|
||||
) -> str:
|
||||
"""
|
||||
Raises an exception if could not register, or registers and returns the "authorization token"
|
||||
"""
|
||||
@ -138,155 +161,38 @@ class REST:
|
||||
'post_command': postCommand,
|
||||
'log_level': logLevel
|
||||
}
|
||||
try:
|
||||
# First, try to login
|
||||
authInfo = {'auth': auth, 'username': username, 'password': password }
|
||||
headers = self.headers
|
||||
result = requests.post(self.url + 'auth/login', data=json.dumps(authInfo), headers=headers, verify=self.validateCert)
|
||||
if not result.ok or result.json()['result'] == 'error':
|
||||
raise Exception() # Invalid credentials
|
||||
except (requests.ConnectionError, requests.ConnectTimeout) as e:
|
||||
raise RESTConnectionError(str(e))
|
||||
except Exception as e:
|
||||
raise RESTError('Invalid credentials')
|
||||
|
||||
try:
|
||||
headers['X-Auth-Token'] = result.json()['token']
|
||||
headers = self._login(auth, username, password)
|
||||
result = requests.post(self.url + 'actor/v2/register', data=json.dumps(data), headers=headers, verify=self.validateCert)
|
||||
if result.ok:
|
||||
return result.json()['result']
|
||||
except (requests.ConnectionError, requests.ConnectTimeout) as e:
|
||||
except requests.ConnectionError as e:
|
||||
raise RESTConnectionError(str(e))
|
||||
except Exception as e:
|
||||
except RESTError:
|
||||
raise
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
raise RESTError(result.content)
|
||||
|
||||
def _getUrl(self, method, key=None, ids=None):
|
||||
url = self.url + method
|
||||
params = []
|
||||
if key is not None:
|
||||
params.append('key=' + key)
|
||||
if ids is not None:
|
||||
params.append('id=' + ids)
|
||||
params.append('version=' + VERSION)
|
||||
|
||||
if len(params) > 0:
|
||||
url += '?' + '&'.join(params)
|
||||
|
||||
return url
|
||||
|
||||
def _request(self, url, data=None):
|
||||
def readConfig(
|
||||
self,
|
||||
auth: str,
|
||||
username: str,
|
||||
password: str,
|
||||
mac: str,
|
||||
config: typing.Optional[types.ActorConfigurationType] = None
|
||||
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
|
||||
try:
|
||||
if data is None:
|
||||
# Old requests version does not support verify, but they do not checks ssl certificate by default
|
||||
if self.newerRequestLib:
|
||||
r = requests.get(url, verify=self.validateCert)
|
||||
else:
|
||||
logger.debug('Requesting with old')
|
||||
r = requests.get(url) # Always ignore certs??
|
||||
else:
|
||||
if data == '':
|
||||
data = '{"dummy": true}' # Ensures no proxy rewrites POST as GET because body is empty...
|
||||
if self.newerRequestLib:
|
||||
r = requests.post(url, data=data, headers={'content-type': 'application/json'}, verify=self.validateCert)
|
||||
else:
|
||||
logger.debug('Requesting with old')
|
||||
r = requests.post(url, data=data, headers={'content-type': 'application/json'})
|
||||
|
||||
# From versions of requests, content maybe bytes or str. We need str for json.loads
|
||||
content = r.content
|
||||
if not isinstance(content, str):
|
||||
content = content.decode('utf8')
|
||||
r = json.loads(content) # Using instead of r.json() to make compatible with oooold rquests lib versions
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise ConnectionError(e)
|
||||
except Exception as e:
|
||||
raise ConnectionError(exceptionToMessage(e))
|
||||
|
||||
ensureResultIsOk(r)
|
||||
|
||||
return r
|
||||
|
||||
@property
|
||||
def isConnected(self):
|
||||
return self.uuid is not None
|
||||
|
||||
def test(self):
|
||||
url = self._getUrl('test', self.masterKey)
|
||||
return self._request(url)['result']
|
||||
|
||||
def init(self, ids):
|
||||
'''
|
||||
Ids is a comma separated values indicating MAC=ip
|
||||
Server returns:
|
||||
uuid, mac
|
||||
Optionally can return an third parameter, that is max "idle" request time
|
||||
'''
|
||||
logger.debug('Invoking init')
|
||||
url = self._getUrl('init', key=self.masterKey, ids=ids)
|
||||
res = self._request(url)['result']
|
||||
logger.debug('Got response parameters: {}'.format(res))
|
||||
self.uuid, self.mac = res[0:2]
|
||||
# Optional idle parameter
|
||||
try:
|
||||
self.idle = int(res[2])
|
||||
if self.idle < 30:
|
||||
self.idle = None # No values under 30 seconds are allowed :)
|
||||
res = None
|
||||
headers = self._login(auth, username, password)
|
||||
result = requests.post(self.url + 'actor/v2/config', data=json.dumps(mac), headers=headers, verify=self.validateCert)
|
||||
if result.ok:
|
||||
res = result.json()['result']
|
||||
except Exception:
|
||||
self.idle = None
|
||||
|
||||
return self.uuid
|
||||
|
||||
def postMessage(self, msg, data, processData=True):
|
||||
logger.debug('Invoking post message {} with data {}'.format(msg, data))
|
||||
|
||||
if self.uuid is None:
|
||||
raise ConnectionError('REST api has not been initialized')
|
||||
|
||||
if processData:
|
||||
if data and not isinstance(data, str):
|
||||
data = data.decode('utf8')
|
||||
data = json.dumps({'data': data})
|
||||
url = self._getUrl('/'.join([self.uuid, msg]))
|
||||
return self._request(url, data)['result']
|
||||
|
||||
def notifyComm(self, url):
|
||||
logger.debug('Notifying comms {}'.format(url))
|
||||
return self.postMessage('notifyComms', url)
|
||||
|
||||
def login(self, username):
|
||||
logger.debug('Notifying login {}'.format(username))
|
||||
return self.postMessage('login', username)
|
||||
|
||||
def logout(self, username):
|
||||
logger.debug('Notifying logout {}'.format(username))
|
||||
return self.postMessage('logout', username)
|
||||
|
||||
def information(self):
|
||||
logger.debug('Requesting information'.format())
|
||||
return self.postMessage('information', '')
|
||||
|
||||
def setReady(self, ipsInfo, hostName=None):
|
||||
logger.debug('Notifying readyness: {}'.format(ipsInfo))
|
||||
# data = ','.join(['{}={}'.format(v[0], v[1]) for v in ipsInfo])
|
||||
data = {
|
||||
'ips': ipsInfo,
|
||||
'hostname': hostName
|
||||
}
|
||||
return self.postMessage('ready', data)
|
||||
|
||||
def notifyIpChanges(self, ipsInfo):
|
||||
logger.debug('Notifying ip changes: {}'.format(ipsInfo))
|
||||
data = ','.join(['{}={}'.format(v[0], v[1]) for v in ipsInfo])
|
||||
return self.postMessage('ip', data)
|
||||
|
||||
def getTicket(self, ticketId, secure=False):
|
||||
url = self._getUrl('ticket/' + ticketId, self.masterKey) + "&secure={}".format('1' if secure else '0')
|
||||
return self._request(url)['result']
|
||||
|
||||
|
||||
def log(self, logLevel, message):
|
||||
data = json.dumps({'message': message, 'level': logLevel})
|
||||
return self.postMessage('log', data, processData=False)
|
||||
pass
|
||||
|
||||
if config:
|
||||
config
|
||||
return None
|
||||
|
@ -18,4 +18,7 @@ class ActorConfigurationType(typing.NamedTuple):
|
||||
validateCertificate: bool
|
||||
master_token: typing.Optional[str] = None
|
||||
own_token: typing.Optional[str] = None
|
||||
pre_command: typing.Optional[str] = None
|
||||
run_once_command: typing.Optional[str] = None
|
||||
post_command: typing.Optional[str] = None
|
||||
data: typing.Optional[typing.Dict[str, str]] = None
|
||||
|
@ -25,27 +25,25 @@
|
||||
# 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
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import pickle
|
||||
import typing
|
||||
|
||||
from win32com.shell import shell
|
||||
import winreg as wreg
|
||||
import win32security
|
||||
|
||||
from win32com.shell import shell # pylint: disable=no-name-in-module,import-error
|
||||
|
||||
from .. import types
|
||||
|
||||
PATH = 'Software\\UDSActor'
|
||||
BASEKEY = wreg.HKEY_LOCAL_MACHINE
|
||||
|
||||
|
||||
def checkPermissions() -> bool:
|
||||
return shell.IsUserAnAdmin()
|
||||
|
||||
|
||||
def fixRegistryPermissions(handle) -> None:
|
||||
# Fix permissions so users can't read this key
|
||||
v = win32security.GetSecurityInfo(handle, win32security.SE_REGISTRY_KEY, win32security.DACL_SECURITY_INFORMATION)
|
||||
@ -83,7 +81,8 @@ def writeConfig(config: types.ActorConfigurationType) -> None:
|
||||
key = wreg.OpenKey(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
|
||||
except Exception:
|
||||
key = wreg.CreateKeyEx(BASEKEY, PATH, 0, wreg.KEY_ALL_ACCESS) # @UndefinedVariable
|
||||
fixRegistryPermissions(key.handle)
|
||||
|
||||
fixRegistryPermissions(key.handle)
|
||||
|
||||
wreg.SetValueEx(key, "", 0, wreg.REG_BINARY, pickle.dumps(config)) # @UndefinedVariable
|
||||
wreg.CloseKey(key) # @UndefinedVariable
|
||||
|
Loading…
x
Reference in New Issue
Block a user