1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-20 14:03:49 +03:00

Actor work

This commit is contained in:
Adolfo Gómez 2019-11-22 08:43:25 +01:00
parent 2cf869150f
commit 07d337364d
4 changed files with 81 additions and 169 deletions

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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