From 062337edd83c46279b52e620dfc93d39e806dfd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez?= Date: Wed, 20 Mar 2013 20:01:20 +0000 Subject: [PATCH] Added HTML5 RDP connector (and it works!!) using guacamole as tunneler --- .../org.eclipse.core.resources.prefs | 3 +- server/src/uds/dispatchers/guacamole/views.py | 24 +++- .../src/uds/transports/HTML5RDP/HTML5RDP.py | 136 ++++++++++++++++++ .../src/uds/transports/HTML5RDP/__init__.py | 36 +++++ server/src/uds/transports/HTML5RDP/rdp.png | Bin 0 -> 1054 bytes server/src/uds/transports/RDP/__init__.py | 8 +- server/src/uds/transports/RDP/web.py | 2 +- 7 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 server/src/uds/transports/HTML5RDP/HTML5RDP.py create mode 100644 server/src/uds/transports/HTML5RDP/__init__.py create mode 100644 server/src/uds/transports/HTML5RDP/rdp.png diff --git a/server/.settings/org.eclipse.core.resources.prefs b/server/.settings/org.eclipse.core.resources.prefs index 13094f077..acedbf22f 100644 --- a/server/.settings/org.eclipse.core.resources.prefs +++ b/server/.settings/org.eclipse.core.resources.prefs @@ -169,7 +169,8 @@ encoding//src/uds/services/Vmware_enterprise/client/Exceptions.py=utf-8 encoding//src/uds/services/Vmware_enterprise/client/Server.py=utf-8 encoding//src/uds/services/Vmware_enterprise/client/Task.py=utf-8 encoding//src/uds/services/__init__.py=utf-8 -encoding//src/uds/tests.py=utf-8 +encoding//src/uds/transports/HTML5RDP/HTML5RDP.py=utf-8 +encoding//src/uds/transports/HTML5RDP/__init__.py=utf-8 encoding//src/uds/transports/NX/NXTransport.py=utf-8 encoding//src/uds/transports/NX/__init__.py=utf-8 encoding//src/uds/transports/NX/web.py=utf-8 diff --git a/server/src/uds/dispatchers/guacamole/views.py b/server/src/uds/dispatchers/guacamole/views.py index 91f0a9043..445a65c83 100644 --- a/server/src/uds/dispatchers/guacamole/views.py +++ b/server/src/uds/dispatchers/guacamole/views.py @@ -31,19 +31,35 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com ''' -from django.http import HttpResponseNotAllowed, HttpResponse +from django.http import HttpResponse from uds.core.util.Cache import Cache import logging logger = logging.getLogger(__name__) +ERROR = "ERROR" +CONTENT_TYPE = 'text/plain' + # We will use the cache to "hold" the tickets valid for users +def dict2resp(dct): + return '\r'.join(( k + '\t' + v for k, v in dct.iteritems())) + def guacamole(request, tunnelId): logger.debug('Received credentials request for tunnel id {0}'.format(tunnelId)) - cache = Cache("guacamole") + cache = Cache('guacamole') - response = 'protocol\trdp\rhostname\tw7adolfo\rusername\tadmin\rpassword\ttemporal' + val = cache.get(tunnelId, None) - return HttpResponse(response, content_type='text/plain') + if val is None: + return HttpResponse(ERROR, content_type=CONTENT_TYPE) + + # Remove key from cache, just 1 use + # Cache has a limit lifetime, so we will allow to "reload" the page + # cache.remove(tunnelId) + + #response = 'protocol\trdp\rhostname\tw7adolfo\rusername\tadmin\rpassword\ttemporal' + response = dict2resp(val) + + return HttpResponse(response, content_type=CONTENT_TYPE) diff --git a/server/src/uds/transports/HTML5RDP/HTML5RDP.py b/server/src/uds/transports/HTML5RDP/HTML5RDP.py new file mode 100644 index 000000000..8800a86da --- /dev/null +++ b/server/src/uds/transports/HTML5RDP/HTML5RDP.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 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 django.utils.translation import ugettext_noop as _ +from uds.core.ui.UserInterface import gui +from uds.core.util.Cache import Cache +from uds.core.transports.BaseTransport import Transport +from uds.core.util import connection + +import logging + +logger = logging.getLogger(__name__) + +READY_CACHE_TIMEOUT = 30 + +class HTML5RDPTransport(Transport): + ''' + Provides access via RDP to service. + This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password + ''' + typeName = _('HTML5 RDP Transport') + typeType = 'HTML5RDPTransport' + typeDescription = _('RDP Transport using HTML5 client') + iconFile = 'rdp.png' + needsJava = False # If this transport needs java for rendering + + guacamoleServer = gui.TextField(label=_('Tunnel Server'), order = 1, tooltip = _('Host of the tunnel server (use http/http & port if needed)'), defvalue = 'https://') + useEmptyCreds = gui.CheckBoxField(label = _('Empty creds'), order = 2, tooltip = _('If checked, the credentials used to connect will be emtpy')) + fixedName = gui.TextField(label=_('Username'), order = 3, tooltip = _('If not empty, this username will be always used as credential')) + fixedPassword = gui.PasswordField(label=_('Password'), order = 4, tooltip = _('If not empty, this password will be always used as credential')) + fixedDomain = gui.TextField(label=_('Domain'), order = 5, tooltip = _('If not empty, this domain will be always used as credential (used as DOMAIN\\user)')) + + def initialize(self, values): + if values is None: + return + a = '' + if self.guacamoleServer.value[0:4] != 'http': + raise Transport.ValidationException(_('The server must be http or https')) + + # Same check as normal RDP transport + def isAvailableFor(self, ip): + ''' + Checks if the transport is available for the requested destination ip + Override this in yours transports + ''' + logger.debug('Checking availability for {0}'.format(ip)) + ready = self.cache().get(ip) + if ready is None: + # Check again for readyness + if connection.testServer(ip, '3389') == True: + self.cache().put(ip, 'Y', READY_CACHE_TIMEOUT) + return True + else: + self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) + return ready == 'Y' + + def renderForHtml(self, userService, idUserService, idTransport, ip, os, user, password): + # We use helper to keep this clean + import uuid + + username = user.getUsernameForAuth() + + domain = '' + if username.find('@') != -1: + domain = username[username.find('@')+1:] + elif username.find('\\') != -1: + domain = username[:username.find('\\')] + + if self.fixedName.value is not '': + username = self.fixedName.value + if self.fixedPassword.value is not '': + password = self.fixedPassword.value + if self.fixedDomain.value is not '': + domain = self.fixedDomain.value + if self.useEmptyCreds.isTrue(): + username, password, domain = '','','' + + # Fix username/password acording to os manager + username, password = userService.processUserPassword(username, password) + + if domain != '': + if domain.find('.') == -1: + username = domain + '\\' + username + else: + username = username + '@' + username + + # Build params dict + params = { 'protocol':'rdp', 'hostname':ip, 'username': username, 'password': password } + + logger.debug('RDP Params: {0}'.format(params)) + + cache = Cache('guacamole') + key = uuid.uuid4().hex + cache.put(key, params) + + url = "{0}/transport/?{1}".format( self.guacamoleServer.value, key) + return ''' + +
+ '''.format(url) + diff --git a/server/src/uds/transports/HTML5RDP/__init__.py b/server/src/uds/transports/HTML5RDP/__init__.py new file mode 100644 index 000000000..3e429338f --- /dev/null +++ b/server/src/uds/transports/HTML5RDP/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 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 uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs +from HTML5RDP import HTML5RDPTransport +from django.utils.translation import ugettext_noop as _ diff --git a/server/src/uds/transports/HTML5RDP/rdp.png b/server/src/uds/transports/HTML5RDP/rdp.png new file mode 100644 index 0000000000000000000000000000000000000000..8d73815a83c35524e6850d8846c085654cb19d9c GIT binary patch literal 1054 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV1DZ9;uumfhvncw-Ub5>)&Px~ zs?Q0@i{5JWUF?ipP(7pIY1IA4d(B;0c3K-6dug_J%vn{%xP#GV>$SYJ*wE_i+%;Qu zjPF}M|IW<7@c%!=7#@~`Ti?sM1**s`W3tPf{mDt#Aeq7Qd(>;m?#Y8R>zvvsc));zqEJ(^nan75(hM(9_&nLlLy zq)%hppwc7B9~D$yzm5F?6U53!ur39klK+j>AGvuw_r2L?{rK!0##6;AIf834u3m0! z0-K-IxFRJXB{4ny-|r9i{_B_L8}9r2dwye}%aq1I#|e$C6MG->-^p^DAUKW^<9(3Zw(Q-zRy(Z@m3La44a6RbW_OHA2n1r3i zyiKxiK9^gz|C$tHc>n0K!g2{$Z-U779^Ls1@FtC93H7#lWzhBaRQ-?vx zDZQkG$3ISenEsQKS^2wu+l+8?3A?(Qa|-`wbU%DwFV=RxUR3gjg{$N~qyNbX|M&}m z5d$(o;=qsj4?pLdwe8%VD|w`Hzfr+|>jMYQew}mB`!QehmFv+wZGUQbc;e?78GP8A zwBz{aSr5;}tFf)%mw<-SB}c9X10IHhy}2zFU;q2-R2Vur2l>|?I`D>_-py&y*WjFVP}!zCpV|3x4wX*+T`Lv!3G5$*MstVZqzT7OHpA~sf^pwdiOrV_nG0_ z?|8cMUCY|Sky~$aeC=f?_Cp4ut^1CNgWas6q81jWTN;<<&(791+q_c3Vg{?^j+-|n z{}pK&)%-vG<^SLH_W%DG+ocISsXO`M`FruU`2GL>C8st00ERfw*9?vf`~I_lJaG5! z{Rg{u?_BL+Dk*8I!Ystk&nGA=BgMxrV9fQOfr$}_nBV>R&hYX7H3oJjZU#XCR<6&V znL&XE0??#Z@#)FQ$$$PFG5{q(H8nP|ygWTSIXOGWxVX4LM@B|N!N5RILqkr@L_tYT q%gD@|{PO1tGBYyjl7OicVh%Gy^hZM>0qLDvKzvVEKbLh*2~7Yd?Z^WF literal 0 HcmV?d00001 diff --git a/server/src/uds/transports/RDP/__init__.py b/server/src/uds/transports/RDP/__init__.py index 248592302..9e268b6f2 100644 --- a/server/src/uds/transports/RDP/__init__.py +++ b/server/src/uds/transports/RDP/__init__.py @@ -31,13 +31,13 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com ''' -from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs -from uds.transports.RDP.RDPTransport import RDPTransport -from uds.transports.RDP.TSRDPTransport import TSRDPTransport from django.utils.translation import ugettext_noop as _ +from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs +from RDPTransport import RDPTransport +from TSRDPTransport import TSRDPTransport UserPrefsManager.manager().registerPrefs('rdp', _('Remote Desktop Protocol'), [ CommonPrefs.screenSizePref, CommonPrefs.depthPref - ]) \ No newline at end of file + ]) diff --git a/server/src/uds/transports/RDP/web.py b/server/src/uds/transports/RDP/web.py index bc48270db..770b663e9 100644 --- a/server/src/uds/transports/RDP/web.py +++ b/server/src/uds/transports/RDP/web.py @@ -79,7 +79,7 @@ def generateHtmlForRdp(transport, idUserService, idTransport, os, ip, port, user data.append('tun:' + extra['tun']) data = scramble('\t'.join(data)) - res = '
' % (codebase, '1', data ) + res = '
' % (codebase, '1', data ) if isMac is True: res += ('

' + _('In order to use this service, you should first install CoRD.') + '

' '

' + _('You can obtain it from') + ' ' + _('CoRD Website') + '

'