From 9a86682c9d58fc1d79c2c3748c7b21e27eaacdd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Thu, 4 Dec 2014 13:03:58 +0100 Subject: [PATCH] =?UTF-8?q?Adding=20support=20for=20pre-access=20scripts?= =?UTF-8?q?=20so=20UDS=20can=20support=20certain=20operations=20(i.e.=20ad?= =?UTF-8?q?d=20an=20user=20to=20an=20specific=20group,=20or=20allowing=20u?= =?UTF-8?q?ser=20to=20access=20using=20specific=20protocol,=20thanks=20to?= =?UTF-8?q?=20Jos=C3=A9=20Luis=20Sep=C3=BAlveda=20from=20upv=20for=20the?= =?UTF-8?q?=20feedback=20;-)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/uds/REST/methods/actor.py | 11 +++-- server/src/uds/REST/methods/user_services.py | 1 + server/src/uds/REST/model.py | 2 +- .../uds/core/managers/UserServiceManager.py | 42 +++++++++++++++++++ .../src/uds/core/osmanagers/BaseOsManager.py | 12 +++++- server/src/uds/models/UserService.py | 15 +++++-- .../WindowsOsManager/WindowsOsManager.py | 4 +- server/src/uds/web/views.py | 1 + 8 files changed, 77 insertions(+), 11 deletions(-) diff --git a/server/src/uds/REST/methods/actor.py b/server/src/uds/REST/methods/actor.py index a10b176c..a013e873 100644 --- a/server/src/uds/REST/methods/actor.py +++ b/server/src/uds/REST/methods/actor.py @@ -122,7 +122,7 @@ class Actor(Handler): if len(self._args) < 1: raise RequestError('Invalid request') - # if path is .../test (/rest/actor/[test|init]?key=.....&version=....&id=....) + # if path is .../test (/rest/actor/[test|init]?key=.....&version=....&id=....) version & ids are only used on init if self._args[0] in ('test', 'init'): v = self.validateRequestKey() if v is not None: @@ -142,7 +142,10 @@ class Actor(Handler): if service.deployed_service.osmanager is not None: maxIdle = service.deployed_service.osmanager.getInstance().maxIdle() logger.debug('Max idle: {}'.format(maxIdle)) - return Actor.result((service.uuid, service.unique_id, 0 if maxIdle is None else maxIdle)) + return Actor.result((service.uuid, + service.unique_id, + 0 if maxIdle is None else maxIdle) + ) raise RequestError('Invalid request') # Must be invoked as '/rest/actor/UUID/[message], with message data in post body @@ -170,7 +173,7 @@ class Actor(Handler): username = '' if message == 'notifyComms': - service.setProperty('comms_url', data) + service.setCommsUrl(data) return Actor.result('ok') # Preprocess some messages, common to all clients, such as "log" @@ -181,7 +184,7 @@ class Actor(Handler): username = data try: - res = service.getInstance().osmanager().process(service, message, data) + res = service.getInstance().osmanager().process(service, message, data, options={'scramble': False}) except Exception as e: return Actor.result(six.text_type(e), ERR_OSMANAGER_ERROR) diff --git a/server/src/uds/REST/methods/user_services.py b/server/src/uds/REST/methods/user_services.py index a3c0f935..081bbcae 100644 --- a/server/src/uds/REST/methods/user_services.py +++ b/server/src/uds/REST/methods/user_services.py @@ -155,6 +155,7 @@ class CachedService(AssignedService): {'friendly_name': {'title': _('Friendly name')}}, {'state': {'title': _('State'), 'type': 'dict', 'dict': State.dictionary()}}, {'cache_level': {'title': _('Cache level')}}, + {'actor_version': {'title': _('Actor version')}} ] def getLogs(self, parent, item): diff --git a/server/src/uds/REST/model.py b/server/src/uds/REST/model.py index c7f36224..3e585721 100644 --- a/server/src/uds/REST/model.py +++ b/server/src/uds/REST/model.py @@ -47,7 +47,7 @@ import logging logger = logging.getLogger(__name__) -__updated__ = '2014-11-12' +__updated__ = '2014-12-04' # a few constants diff --git a/server/src/uds/core/managers/UserServiceManager.py b/server/src/uds/core/managers/UserServiceManager.py index 8783571c..bb27add8 100644 --- a/server/src/uds/core/managers/UserServiceManager.py +++ b/server/src/uds/core/managers/UserServiceManager.py @@ -45,6 +45,8 @@ from uds.core.services.Exceptions import MaxServicesReachedException from uds.models import UserService, getSqlDatetime from uds.core import services from uds.core.services import Service +import requests +import json import logging logger = logging.getLogger(__name__) @@ -444,6 +446,46 @@ class UserServiceManager(object): UserServiceOpChecker.makeUnique(uService, ui, state) return False + def manageOsManagerPreConnection(self, uService, user): + ''' + Sends, if the user service has os manager and the os manager "wants" to send an pre-script to actor + the script to the Service + If fails, it will silently ignore it, but probably connection will not success + This is so right now to keep compatibility with previos xmlrpc actor.. + @return: Nothing + ''' + logger.debug('Managing specific OS Manager data before connection') + if uService.needsOsManager() is False: + logger.debug('No os manager for service, finishing') + return + + osm = uService.getOsManager() + instanceOsManager = osm.getInstance() + script = instanceOsManager.preAccessScript(uService, user) + if script is None: + logger.debug('OS Manager does not provides a pre access script') + + logger.debug('Pre access script: {}'.format(script)) + return self.sendScript(uService, script) + + def sendScript(self, uService, script): + ''' + If allowed, send script to user service + ''' + url = uService.getCommsUrl() + if url is None: + logger.error('Can\'t connect with actor (no actor or legacy actor)') + return + + try: + r = requests.post(url, data={'script': script}, headers={'content-type': 'application/json'}, verify=False, timeout=5) + r = json.loads(r.content) + # In fact we ignore result right now + except Exception as e: + logger.error('Exception caught sending script: {}. Check connection on destination machine: {}'.format(e, url)) + + # All done + def checkForRemoval(self, uService): ''' This method is used by UserService when a request for setInUse(False) is made diff --git a/server/src/uds/core/osmanagers/BaseOsManager.py b/server/src/uds/core/osmanagers/BaseOsManager.py index 28542f80..d91bdbb3 100644 --- a/server/src/uds/core/osmanagers/BaseOsManager.py +++ b/server/src/uds/core/osmanagers/BaseOsManager.py @@ -36,7 +36,7 @@ from django.utils.translation import ugettext_noop as _ from uds.core.util.State import State from uds.core import Module -__updated__ = '2014-12-01' +__updated__ = '2014-12-04' STORAGE_KEY = 'osmk' @@ -89,7 +89,7 @@ class OSManager(Module): pass # These methods must be overriden - def process(self, service, message, data): + def process(self, service, message, data, options=None): ''' This method must be overriden so your so manager can manage requests and responses from agent. @param service: Service that sends the request (virtual machine or whatever) @@ -126,6 +126,14 @@ class OSManager(Module): ''' return None + def preAccessScript(self, userService, user): + ''' + This gives us the chance to include "customized" initialization for any os manager for an specifyc user & service on assignation to an user + such as "include" in allowed user list, etc... + Both values are db objects + ''' + return None + @classmethod def transformsUserOrPasswordForService(cls): ''' diff --git a/server/src/uds/models/UserService.py b/server/src/uds/models/UserService.py index 5bc34280..54ca9801 100644 --- a/server/src/uds/models/UserService.py +++ b/server/src/uds/models/UserService.py @@ -35,7 +35,7 @@ from __future__ import unicode_literals -__updated__ = '2014-12-02' +__updated__ = '2014-12-04' from django.db import models from django.db.models import signals @@ -246,11 +246,14 @@ class UserService(UUIDModel): ''' return [self.src_ip, self.src_hostname] + def getOsManager(self): + return self.deployed_service.osmanager + def needsOsManager(self): ''' Returns True if this User Service needs an os manager (i.e. parent services pools is marked to use an os manager) ''' - return self.deployed_service.osmanager is not None + return self.getOsManager() is not None def transformsUserOrPasswordForService(self): ''' @@ -419,9 +422,15 @@ class UserService(UUIDModel): def setProperty(self, propName, propValue): prop, _ = self.properties.get_or_create(name=propName) - prop.value = propValue + prop.value = propValue if propValue is None else '' prop.save() + def setCommsUrl(self, commsUrl=None): + self.setProperty('comms_url', commsUrl) + + def getCommsUrl(self): + return self.getProperty('comms_url', None) + def __str__(self): return "User service {0}, cache_level {1}, user {2}, name {3}, state {4}:{5}".format(self.id, self.cache_level, self.user, self.friendly_name, State.toString(self.state), State.toString(self.os_state)) diff --git a/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py b/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py index 48524289..7a51ce09 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py +++ b/server/src/uds/osmanagers/WindowsOsManager/WindowsOsManager.py @@ -115,7 +115,7 @@ class WindowsOsManager(osmanagers.OSManager): except Exception: log.doLog(service, log.ERROR, "do not understand {0}".format(data), origin) - def process(self, service, msg, data): + def process(self, service, msg, data, options): ''' We understand this messages: * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (old method) @@ -180,6 +180,8 @@ class WindowsOsManager(osmanagers.OSManager): else: UserServiceManager.manager().notifyReadyFromOsManager(service, '') logger.debug('Returning {} to {} message'.format(ret, msg)) + if options is not None and options.get('scramble', True) is False: + return ret return scrambleMsg(ret) def processUnused(self, userService): diff --git a/server/src/uds/web/views.py b/server/src/uds/web/views.py index 61317c19..02dd89b2 100644 --- a/server/src/uds/web/views.py +++ b/server/src/uds/web/views.py @@ -276,6 +276,7 @@ def service(request, idService, idTransport): if ip is not None: itrans = trans.getInstance() if itrans.isAvailableFor(ip): + UserServiceManager.manager().manageOsManagerPreConnection(ads, request.user) log.doLog(ads, log.INFO, "User service ready, rendering transport", log.WEB) transport = itrans.renderForHtml(ads, ads.uuid, trans.uuid, ip, request.session['OS'], request.user, webPassword(request)) return render_to_response(theme.template('show_transport.html'), {'transport': transport, 'nolang': True}, context_instance=RequestContext(request))