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

Almost finished connector

This commit is contained in:
Adolfo Gómez García 2015-04-20 07:33:01 +02:00
commit 199e0d3559
11 changed files with 254 additions and 11 deletions

1
client/.gitignore vendored
View File

@ -1,3 +1,4 @@
/bin /bin
/udsclient_* /udsclient_*
/udsclient-*.tar.gz
/*.rpm /*.rpm

View File

@ -1,4 +1,4 @@
/udsclient-opensuse-1.*.spec /udsclient-opensuse-1.*.spec
/udsclient-1.*.spec /udsclient-1.*.spec
/debian/udsclient /debian/udsclient
/targz

View File

@ -1,6 +1,9 @@
#!/usr/bin/make -f #!/usr/bin/make -f
# -*- makefile -*- # -*- makefile -*-
# Version
VERSION := 1.7.5
# Directories # Directories
SOURCEDIR := ../src SOURCEDIR := ../src
LIBDIR := $(DESTDIR)/usr/lib/UDSClient LIBDIR := $(DESTDIR)/usr/lib/UDSClient
@ -16,8 +19,8 @@ clean:
install: install:
rm -rf $(DESTDIR) rm -rf $(DESTDIR)
mkdir -p $(LIBDIR) mkdir -p $(LIBDIR)
mkdir -p $(BINDIR) #mkdir -p $(BINDIR)
mkdir -p $(SBINDIR) #mkdir -p $(SBINDIR)
mkdir -p $(APPSDIR) mkdir -p $(APPSDIR)
mkdir $(LIBDIR)/uds mkdir $(LIBDIR)/uds
@ -34,9 +37,15 @@ install:
cp desktop/UDSClient.desktop $(APPSDIR) cp desktop/UDSClient.desktop $(APPSDIR)
chmod 755 $(LIBDIR)/UDSClient.py chmod 755 $(LIBDIR)/UDSClient.py
ifeq ($(DISTRO),targz)
cp installer.sh $(DESTDIR)/install.sh
tar czvf ../udsclient-$(VERSION).tar.gz -C $(DESTDIR) .
endif
# chmod 0755 $(BINDIR)/udsclient # chmod 0755 $(BINDIR)/udsclient
uninstall: uninstall:
rm -rf $(LIBDIR) rm -rf $(LIBDIR)
# rm -f $(BINDIR)/udsclient # rm -f $(BINDIR)/udsclient
rm -rf $(CFGDIR) # rm -rf $(CFGDIR)

View File

@ -2,7 +2,10 @@
VERSION=1.7.5 VERSION=1.7.5
RELEASE=1 RELEASE=1
# Debian based
dpkg-buildpackage -b
# Now rpm based
top=`pwd` top=`pwd`
cat udsclient-template.spec | cat udsclient-template.spec |

14
client/linux/installer.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/sh
cp -r usr/lib/UDSClient /usr/lib/UDSClient
cp -r usr/share/applications /usr/lib/applications -R
update-desktop-database
echo "Installation process done."
echo "Remembar that the following packages must be installed on system:"
echo "* Python paramiko"
echo "* Python pyqt4"
echo "Theese packages (as their names), are dependent on your platform, so you must locate and install them"
echo "You can install them directly on any platform with pip, using this simple command: "
echo "pip install PyQt4 paramiko"

View File

@ -12,7 +12,7 @@ import threading
import random import random
import time import time
g_verbose = False g_verbose = True
class ForwardServer (SocketServer.ThreadingTCPServer): class ForwardServer (SocketServer.ThreadingTCPServer):
@ -117,8 +117,9 @@ class ForwardThread(threading.Thread):
verbose('Connecting to ssh host %s:%d ...' % (self.server, self.port)) verbose('Connecting to ssh host %s:%d ...' % (self.server, self.port))
try: try:
self.client.connect(self.server, self.port, username=self.username, password=self.password) self.client.connect(self.server, self.port, username=self.username, password=self.password, timeout=5, banner_timeout=10)
except Exception: except Exception as e:
verbose('Exception connecting: {}'.format(e))
self.status = 2 # Error self.status = 2 # Error
return return
@ -173,3 +174,4 @@ def forward(server, port, username, password, redirectHost, redirectPort, localP
time.sleep(0.1) time.sleep(0.1)
return (ft, localPort) return (ft, localPort)

View File

@ -70,6 +70,5 @@ class Cache(Handler):
if len(self._args) != 1: if len(self._args) != 1:
raise RequestError('Invalid Request') raise RequestError('Invalid Request')
uCache.purge() uCache.purge()
return 'done' return 'done'

View File

@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015 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
from django.utils.translation import ugettext as _
from uds.REST import Handler
from uds.REST import RequestError
from uds.models import UserService, DeployedService, Transport
from uds.core.managers.UserServiceManager import UserServiceManager
from uds.core.util import log
from uds.core.util.stats import events
import datetime
import six
import logging
logger = logging.getLogger(__name__)
# Enclosed methods under /actor path
class Connection(Handler):
'''
Processes actor requests
'''
authenticated = True # Actor requests are not authenticated
needs_admin = False
needs_staff = False
@staticmethod
def result(result=None, error=None):
'''
Helper method to create a "result" set for connection response
:param result: Result value to return (can be None, in which case it is converted to empty string '')
:param error: If present, This response represents an error. Result will contain an "Explanation" and error contains the error code
:return: A dictionary, suitable for response to Caller
'''
result = result if result is not None else ''
res = {'result': result, 'date': datetime.datetime.now()}
if error is not None:
res['error'] = error
return res
def serviceList(self):
# We look for services for this authenticator groups. User is logged in in just 1 authenticator, so his groups must coincide with those assigned to ds
groups = list(self._user.getGroups())
availServices = DeployedService.getDeployedServicesForGroups(groups)
availUserServices = UserService.getUserAssignedServices(self._user)
# Extract required data to show to user
services = []
# Select assigned user services
for svr in availUserServices:
# Skip maintenance services...
trans = []
for t in svr.transports.all().order_by('priority'):
typeTrans = t.getType()
if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo():
trans.append({'id': t.uuid, 'name': t.name, 'needsJava': t.getType().needsJava})
services.append({'id': 'A' + svr.uuid,
'name': svr['name'],
'transports': trans,
'maintenance': svr.deployed_service.service.provider.maintenance_mode,
'in_use': svr.in_use})
logger.debug(services)
# Now generic user service
for svr in availServices:
trans = []
for t in svr.transports.all().order_by('priority'):
if t.validForIp(self._request.ip) and t.getType().providesConnetionInfo():
typeTrans = t.getType()
trans.append({'id': t.uuid, 'name': t.name, 'needsJava': typeTrans.needsJava})
# Locate if user service has any already assigned user service for this
ads = UserServiceManager.manager().getExistingAssignationForUser(svr, self._user)
if ads is None:
in_use = False
else:
in_use = ads.in_use
services.append({'id': 'F' + svr.uuid,
'name': svr.name,
'transports': trans,
'maintenance': svr.service.provider.maintenance_mode,
'in_use': in_use})
logger.debug('Services: {0}'.format(services))
services = sorted(services, key=lambda s: s['name'].upper())
return Connection.result(result=services)
def connection(self, doNotCheck=False):
kind, idService = self._args[0][0], self._args[0][1:]
idTransport = self._args[1]
logger.debug('Type: {}, Service: {}, Transport: {}'.format(kind, idService, idTransport))
try:
logger.debug('Kind of service: {0}, idService: {1}'.format(kind, idService))
if kind == 'A': # This is an assigned service
ads = UserService.objects.get(uuid=idService)
else:
ds = DeployedService.objects.get(uuid=idService)
# We first do a sanity check for this, if the user has access to this service
# If it fails, will raise an exception
ds.validateUser(self._user)
# Now we have to locate an instance of the service, so we can assign it to user.
ads = UserServiceManager.manager().getAssignationForUser(ds, self._user)
if ads.isInMaintenance() is True:
return Connection.result(error='Service in maintenance')
logger.debug('Found service: {0}'.format(ads))
trans = Transport.objects.get(uuid=idTransport)
if trans.validForIp(self._request.ip) is False:
return Connection.result(error='Access denied')
# Test if the service is ready
if doNotCheck or ads.isReady():
log.doLog(ads, log.INFO, "User {0} from {1} has initiated access".format(self._user.name, self._request.ip), log.WEB)
# If ready, show transport for this service, if also ready ofc
iads = ads.getInstance()
ip = iads.getIp()
logger.debug('IP: {}'.format(ip))
events.addEvent(ads.deployed_service, events.ET_ACCESS, username=self._user.name, srcip=self._request.ip, dstip=ip, uniqueid=ads.unique_id)
if ip is not None:
itrans = trans.getInstance()
if itrans.providesConnetionInfo() and (doNotCheck or itrans.isAvailableFor(ip)):
ads.setConnectionSource(self._request.ip, 'unknown')
log.doLog(ads, log.INFO, "User service ready, rendering transport", log.WEB)
ci = {
'username': '',
'password': '',
'domain': '',
'protocol': 'unknown',
'ip': ip
}
ci.update(itrans.getConnectionInfo(ads, self._user, 'UNKNOWN'))
UserServiceManager.manager().notifyPreconnect(ads, itrans.processedUser(ads, self._user), itrans.protocol)
return Connection.result(result=ci)
else:
log.doLog(ads, log.WARN, "User service is not accessible by REST (ip {0})".format(ip), log.TRANSPORT)
logger.debug('Transport {} is not accesible for user service {} from {}'.format(trans, ads, self._request.ip))
logger.debug("{}, {}".format(itrans.providesConnetionInfo(), itrans.isAvailableFor(ip)))
else:
logger.debug('Ip not available from user service {0}'.format(ads))
else:
log.doLog(ads, log.WARN, "User {0} from {1} tried to access, but service was not ready".format(self._user.name, self._request.ip), log.WEB)
# Not ready, show message and return to this page in a while
return Connection.result(error='Service not ready')
except Exception as e:
return Connection.result(error=six.text_type(e))
def get(self):
'''
Processes get requests
'''
logger.debug("Connection args for GET: {0}".format(self._args))
if len(self._args) == 0:
# Return list of services/transports
return self.serviceList()
if len(self._args) == 2:
# Return connection & validate access for service/transport
return self.connection()
if len(self._args) == 3 and self._args[2] == 'skipChecking':
return self.connection(True)
raise RequestError('Invalid Request')

View File

@ -43,7 +43,7 @@ import threading
import time import time
import logging import logging
__updated__ = '2015-02-03' __updated__ = '2015-04-15'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -185,3 +185,4 @@ class Scheduler(object):
except Exception, e: except Exception, e:
logger.exception('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e)) logger.exception('Unexpected exception at run loop {0}: {1}'.format(e.__class__, e))
logger.info('Exiting Scheduler because stop has been requested') logger.info('Exiting Scheduler because stop has been requested')
self.releaseOwnShedules()

View File

@ -17,7 +17,7 @@ import six
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389) forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
if forwardThread.status == 2: if forwardThread.status == 2:
raise Exception('Unable to open file') raise Exception('Unable to open tunnel')
# The password must be encoded, to be included in a .rdp file, as 'UTF-16LE' before protecting (CtrpyProtectData) it in order to work with mstsc # The password must be encoded, to be included in a .rdp file, as 'UTF-16LE' before protecting (CtrpyProtectData) it in order to work with mstsc
theFile = '''{m.r.as_file}'''.format( theFile = '''{m.r.as_file}'''.format(

View File

@ -30,7 +30,7 @@
''' '''
from __future__ import unicode_literals from __future__ import unicode_literals
__updated__ = '2015-04-01' __updated__ = '2015-04-16'
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
@ -88,6 +88,10 @@ def getService(request, idService, idTransport, doTest=True):
if trans not in ads.deployed_service.transports.all(): if trans not in ads.deployed_service.transports.all():
raise InvalidServiceException() raise InvalidServiceException()
# If transport is not available for the request IP...
if trans.validForIp(request.ip) is False:
raise InvalidServiceException()
if doTest is False: if doTest is False:
return (None, ads, None, trans, None) return (None, ads, None, trans, None)