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

Working on Spice protocol support.

* Added "provider specific" transport support
* Fixed dashboard to new transport support
This commit is contained in:
Adolfo Gómez García 2015-05-06 09:31:37 +02:00
parent dad83c5c50
commit c1c54f38cc
19 changed files with 2665 additions and 9 deletions

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,7 @@ class ServicesPools(ModelHandler):
'id': item.uuid,
'name': item.name,
'parent': item.service.name,
'parent_type': item.service.data_type,
'comments': item.comments,
'state': item.state if item.service.provider.maintenance_mode is False else State.MAINTENANCE,
'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64,
@ -97,7 +98,7 @@ class ServicesPools(ModelHandler):
'restrained': item.isRestrained(),
'show_transports': item.show_transports,
'permission': permissions.getEffectivePermission(self._user, item),
'info': Services.serviceInfo(item.service)
'info': Services.serviceInfo(item.service),
}
if item.osmanager is not None:

View File

@ -94,6 +94,7 @@ class Transports(ModelHandler):
'networks': [{'id': n.id} for n in item.networks.all()],
'deployed_count': item.deployedServices.count(),
'type': type_.type(),
'allowedProviders': list(i.type() for i in type_.allowedProviders),
'permission': permissions.getEffectivePermission(self._user, item)
}

View File

@ -62,8 +62,8 @@ class Transport(Module):
# Linux
supportedOss = OsDetector.desktopOss # Supported operating systems
# If this transport is for an specific provider, this will be != None
onlyForProviders = None
# If this transport is for an specific provider, this will be an non empty tuple/list
allowedProviders = ()
# If this transport is visible via Web, via Thin Client or both
webTransport = False

View File

@ -33,15 +33,15 @@
from __future__ import unicode_literals
__updated__ = '2014-09-16'
from django.db import models
from uds.core.Environment import Environment
from uds.models.UUIDModel import UUIDModel
import logging
__updated__ = '2015-05-06'
logger = logging.getLogger(__name__)

View File

@ -63,7 +63,7 @@ class Client(object):
pass
try:
cached_api_key = aKey
cached_api = API(url='https://' + self._host + '/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=False)
cached_api = API(url='https://' + self._host + '/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=True)
return cached_api
except:
logger.exception('Exception connection ovirt at {0}'.format(self._host))
@ -116,6 +116,8 @@ class Client(object):
vms = api.vms.list(query='name!=UDS*')
logger.debug('oVirt VMS: {}'.format(vms))
res = []
for vm in vms:
@ -346,7 +348,6 @@ class Client(object):
api = self.__getApi()
cluster = api.clusters.get(id=clusterId)
vm = api.vms.get(id=machineId)
@ -620,3 +621,34 @@ class Client(object):
finally:
lock.release()
def getConnetcionInfo(self, machineId):
'''
Gets the connetion info for the specified machine
'''
try:
lock.acquire(True)
api = self.__getApi()
vm = api.vms.get(id=machineId)
if vm is None:
raise Exception('Machine not found')
display = vm.get_display()
ticket = vm.ticket().get_ticket()
return {
'type': display.get_type(),
'address': display.get_address(),
'port': display.get_port(),
'secure_port': display.get_secure_port(),
'monitors': display.get_monitors(),
'cert_subject': display.get_certificate().get_subject(),
'ticket': {
'value': ticket.get_value(),
'expiry': ticket.get_expiry()
}
}
finally:
lock.release()

View File

@ -315,8 +315,13 @@ gui.servicesPools.link = (event) ->
onNew: (value, table, refreshFnc) ->
api.templates.get "pool_add_transport", (tmpl) ->
api.transports.overview (data) ->
gui.doLog "Data Received: ", servPool, data
valid = []
for i in data
if i.allowedProviders.length == 0 or (servPool.parent_type in i.allowedProviders)
valid.push(i)
modalId = gui.launchModal(gettext("Add transport"), api.templates.evaluate(tmpl,
transports: data
transports: valid
))
$(modalId + " .button-accept").on "click", (event) ->
transport = $(modalId + " #id_transport_select").val()

View File

@ -0,0 +1,119 @@
# -*- 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.managers.UserPrefsManager import CommonPrefs
from uds.core.ui.UserInterface import gui
from uds.core.transports.BaseTransport import Transport
from uds.core.transports import protocols
from uds.core.util import connection
# This transport is specific for oVirt, so we need to point to it
from uds.services.OVirt.OVirtProvider import Provider as oVirtProvider
import logging
import os
logger = logging.getLogger(__name__)
READY_CACHE_TIMEOUT = 30
class BaseSpiceTransport(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
'''
iconFile = 'rdp.png'
protocol = protocols.SPICE
allowedProviders = oVirtProvider.offers
useEmptyCreds = gui.CheckBoxField(
label=_('Empty creds'),
order=1,
tooltip=_('If checked, the credentials used to connect will be emtpy')
)
fixedName = gui.TextField(
label=_('Username'),
order=2,
tooltip=_('If not empty, this username will be always used as credential')
)
fixedPassword = gui.PasswordField(
label=_('Password'),
order=3,
tooltip=_('If not empty, this password will be always used as credential')
)
serverCertificate = gui.TextField(
order=2,
length=4096,
multiline=4,
label=_('Certificate'),
tooltip=_('Server certificate (public), can be found on your ovirt engine, probably at /etc/pki/ovirt-engine/certs/ca.der'),
required=True
)
def isAvailableFor(self, userService, 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))
return True # Spice is available, no matter what IP machine has (even if it does not have one)
def processedUser(self, userService, userName):
v = self.processUserPassword(userService, userName, '')
return v['username']
def processUserPassword(self, service, user, password):
username = user.getUsernameForAuth()
if self.fixedName.value != '':
username = self.fixedName.value
if self.fixedPassword.value != '':
password = self.fixedPassword.value
if self.useEmptyCreds.isTrue():
username, password = '', '', ''
# Fix username/password acording to os manager
username, password = service.processUserPassword(username, password)
return {'protocol': self.protocol, 'username': username, 'password': password}
def getConnectionInfo(self, service, user, password):
return self.processUserPassword(service, user, password)
def getScript(self, script):
with open(os.path.join(os.path.dirname(__file__), script)) as f:
data = f.read()
return data

View File

@ -0,0 +1,33 @@
'''
Created on May 6, 2015
@author: dkmaster
'''
'''
[virt-viewer]
type=spice
host=ovirtnode1.dkmon.com
port=5900
password=S9MATvnrQNGr
tls-port=5901
fullscreen=0
title=ovirt00004:%d - Press SHIFT+F12 to Release Cursor
enable-smartcard=0
enable-usb-autoshare=1
delete-this-file=1
usb-filter=-1,-1,-1,-1,0
tls-ciphers=DEFAULT
host-subject=O=dkmon.com,CN=ovirtnode1.dkmon.com
ca=-----BEGIN CERTIFICATE-----\nMIIDsjCCApqgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMCVVMxEjAQBgNVBAoT\nCWRrbW9uLmNvbTEeMBwGA1UEAxMVb3ZpcnQuZGttb24uY29tLjk5MTAxMCIXETEzMTIxMTE5MDA0\nNCswMDAwFw0yMzEyMTAxOTAwNDRaMEExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlka21vbi5jb20x\nHjAcBgNVBAMTFW92aXJ0LmRrbW9uLmNvbS45OTEwMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAMeMD3njBOLjuS5XQY0kUu3+dT5S0HUhk2RyPv0Bf59Zun215jLX7qJLrDWCxq8xcdB0\n79Krn2wm+QpPBArSJGVZNXkwQSqnmQK7PGuC119wfOx/bQbgHGJuL6XzuRnLn1xMmcp9N33Q+Cic\nkV5B8bVF+bV5FUWzDbSQKsWJUMFwUXd7px9mZJWBCkY4cOoXdhdu5EMGWcEm2e4RkE+Fb611RAU2\nPL1iJwEu1qtG/OYvWh8DsO9OfFP4dZuJhy2oXmKudHgalNCm8hWYa498c1DA/uXZCGLjoN5en9yy\nXU2wkTDJGA0M95mkGlwoIaNXJMM4i4Q5lmzkU6xntp3/zNkCAwEAAaOBrzCBrDAdBgNVHQ4EFgQU\nOQAXE0qEasnOi3qEFFtMg19GqqYwagYDVR0jBGMwYYAUOQAXE0qEasnOi3qEFFtMg19GqqahRaRD\nMEExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlka21vbi5jb20xHjAcBgNVBAMTFW92aXJ0LmRrbW9u\nLmNvbS45OTEwMYICEAAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN\nAQEFBQADggEBABDS5koE1TcBf8hvRP86AEu07KfWby/BvWXMcGuWRgPnkjrLS0mYshXx2Y40Pblm\nYm/DHXtefrDHJ203T/zkuCLtMSI28peeyX5HS32lGhVAMaylKrQ4mRTBQ+TM2hVLyBv7o16FUcei\nBuGNsm6F8b1RtrGTvSlkNxDUU3haDDH6gRMmMjgYrFqVMrozlPgPyHlFc4A1Q9rM/WIXtRioFgAz\ntCL1n5PK61ZIjk6j6a/upj6jYDfq1G0hpR9XOV8CD1bY3ZaCaMwOaILf1yIzQFqNDmx0p5e949at\nZIflG2pSv/5NA7PoRPF2tHf/PfrNmkA81R03dRXps1UAGg5ds3c=\n-----END CERTIFICATE-----\n
toggle-fullscreen=shift+f11
release-cursor=shift+f12
secure-attention=ctrl+alt+end
secure-channels=main;inputs;cursor;playback;record;display;usbredir;smartcard
'''
class RemoteViewerFile(object):
pass

View File

@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2012 Virtual Cable S.L.
# All rights reservem.
#
# 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.managers.UserPrefsManager import CommonPrefs
from uds.core.util import OsDetector
from uds.core.util import tools
from .BaseSPICETransport import BaseSpiceTransport
import logging
logger = logging.getLogger(__name__)
READY_CACHE_TIMEOUT = 30
class SPICETransport(BaseSpiceTransport):
'''
Provides access via SPICE 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 = _('SPICE Transport (direct)')
typeType = 'SPICETransport'
typeDescription = _('SPICE Transport for direct connection')
useEmptyCreds = BaseSpiceTransport.useEmptyCreds
fixedName = BaseSpiceTransport.fixedName
fixedPassword = BaseSpiceTransport.fixedPassword
serverCertificate = BaseSpiceTransport.serverCertificate
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
# We use helper to keep this clean
prefs = user.prefs('rdp')
ci = self.getConnectionInfo(userService, user, password)
username, password, domain = ci['username'], ci['password'], ci['domain']
width, height = CommonPrefs.getWidthHeight(prefs)
depth = CommonPrefs.getDepth(prefs)
# r = SPICEFile(width == -1 or height == -1, width, height, depth, target=os['OS'])
# r.address = '{}:{}'.format(ip, 3389)
# r.username = username
# r.password = password
# r.domain = domain
# r.redirectPrinters = self.allowPrinters.isTrue()
# r.redirectSmartcards = self.allowSmartcards.isTrue()
# r.redirectDrives = self.allowDrives.isTrue()
# r.redirectSerials = self.allowSerials.isTrue()
# r.showWallpaper = self.wallpaper.isTrue()
# r.multimon = self.multimon.isTrue()
#
# # data
# data = {
# 'os': os['OS'],
# 'ip': ip,
# 'port': 3389,
# 'username': username,
# 'password': password,
# 'hasCredentials': username != '' and password != '',
# 'domain': domain,
# 'width': width,
# 'height': height,
# 'depth': depth,
# 'printers': self.allowPrinters.isTrue(),
# 'smartcards': self.allowSmartcards.isTrue(),
# 'drives': self.allowDrives.isTrue(),
# 'serials': self.allowSerials.isTrue(),
# 'compression': True,
# 'wallpaper': self.wallpaper.isTrue(),
# 'multimon': self.multimon.isTrue(),
# 'fullScreen': width == -1 or height == -1,
# 'this_server': request.build_absolute_uri('/'),
# 'r': r,
# }
#
# m = tools.DictAsObj(data)
#
# if m.domain != '':
# m.usernameWithDomain = '{}\\\\{}'.format(m.domain, m.username)
# else:
# m.usernameWithDomain = m.username
#
# if m.os == OsDetector.Windows:
# m.r.password = '{password}'
#
# os = {
# OsDetector.Windows: 'windows',
# OsDetector.Linux: 'linux',
# OsDetector.Macintosh: 'macosx'
#
# }.get(m.os)
#
# if os is None:
# return super(SPICETransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request)
#
# return self.getScript('scripts/{}/direct.py'.format(os)).format(m=m)

View File

@ -0,0 +1,157 @@
# -*- 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.managers.UserPrefsManager import CommonPrefs
from uds.core.ui.UserInterface import gui
from uds.core.transports.BaseTransport import Transport
from uds.core.transports import protocols
from uds.models import TicketStore
from uds.core.util import OsDetector
from uds.core.util import tools
from .BaseSPICETransport import BaseSpiceTransport
import logging
import random
import string
import time
logger = logging.getLogger(__name__)
READY_CACHE_TIMEOUT = 30
class TSPICETransport(BaseSpiceTransport):
'''
Provides access via SPICE 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 = _('SPICE Transport (tunneled)')
typeType = 'TSSPICETransport'
typeDescription = _('SPICE Transport for tunneled connection')
iconFile = 'rdp.png'
needsJava = True # If this transport needs java for rendering
protocol = protocols.SPICE
tunnelServer = gui.TextField(label=_('Tunnel server'), order=1, tooltip=_('IP or Hostname of tunnel server sent to client device ("public" ip) and port. (use HOST:PORT format)'))
tunnelCheckServer = gui.TextField(label=_('Tunnel host check'), order=2, tooltip=_('If not empty, this server will be used to check if service is running before assigning it to user. (use HOST:PORT format)'))
useEmptyCreds = BaseSpiceTransport.useEmptyCreds
fixedName = BaseSpiceTransport.fixedName
fixedPassword = BaseSpiceTransport.fixedPassword
serverCertificate = BaseSpiceTransport.serverCertificate
def initialize(self, values):
if values is not None:
if values['tunnelServer'].count(':') != 1:
raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
# We use helper to keep this clean
prefs = user.prefs('rdp')
ci = self.getConnectionInfo(userService, user, password)
username, password, domain = ci['username'], ci['password'], ci['domain']
width, height = CommonPrefs.getWidthHeight(prefs)
depth = CommonPrefs.getDepth(prefs)
tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
tunuser = TicketStore.create(tunpass)
sshHost, sshPort = self.tunnelServer.value.split(':')
logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))
# r = SPICEFile(width == -1 or height == -1, width, height, depth, target=os['OS'])
# r.address = '{address}'
# r.username = username
# r.password = password
# r.domain = domain
# r.redirectPrinters = self.allowPrinters.isTrue()
# r.redirectSmartcards = self.allowSmartcards.isTrue()
# r.redirectDrives = self.allowDrives.isTrue()
# r.redirectSerials = self.allowSerials.isTrue()
# r.showWallpaper = self.wallpaper.isTrue()
# r.multimon = self.multimon.isTrue()
#
#
# # data
# data = {
# 'os': os['OS'],
# 'ip': ip,
# 'tunUser': tunuser,
# 'tunPass': tunpass,
# 'tunHost': sshHost,
# 'tunPort': sshPort,
# 'username': username,
# 'password': password,
# 'hasCredentials': username != '' and password != '',
# 'domain': domain,
# 'width': width,
# 'height': height,
# 'depth': depth,
# 'printers': self.allowPrinters.isTrue(),
# 'smartcards': self.allowSmartcards.isTrue(),
# 'drives': self.allowDrives.isTrue(),
# 'serials': self.allowSerials.isTrue(),
# 'compression': True,
# 'wallpaper': self.wallpaper.isTrue(),
# 'multimon': self.multimon.isTrue(),
# 'fullScreen': width == -1 or height == -1,
# 'this_server': request.build_absolute_uri('/'),
# 'r': r,
# }
#
# m = tools.DictAsObj(data)
#
# if m.domain != '':
# m.usernameWithDomain = '{}\\\\{}'.format(m.domain, m.username)
# else:
# m.usernameWithDomain = m.username
#
# if m.os == OsDetector.Windows:
# r.password = '{password}'
#
# os = {
# OsDetector.Windows: 'windows',
# OsDetector.Linux: 'linux',
# OsDetector.Macintosh: 'macosx'
#
# }.get(m.os)
#
# if os is None:
# return super(TSPICETransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request)
#
# return self.getScript('scripts/{}/tunnel.py'.format(os)).format(m=m)

View File

@ -0,0 +1,45 @@
# -*- 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.managers.UserPrefsManager import UserPrefsManager, CommonPrefs
from .SPICETransport import SPICETransport
from .TSPICETransport import TSPICETransport
UserPrefsManager.manager().registerPrefs('rdp',
_('Remote Desktop Protocol'),
[
CommonPrefs.screenSizePref,
CommonPrefs.depthPref
]
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,62 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable, invalid-sequence-index
from PyQt4 import QtCore, QtGui
import subprocess
import re
from uds import tools # @UnresolvedImport
import six
def execNewXFreeRdp(parent, xfreerdp):
import subprocess # @Reimport
params = [xfreerdp] + {m.r.as_new_xfreerdp_params} + ['/v:{m.r.address}'] # @UndefinedVariable
tools.addTaskToWait(subprocess.Popen(params))
def execRdesktop(parent, rdesktop):
import subprocess # @Reimport
params = [rdesktop] + {m.r.as_rdesktop_params} + ['{m.r.address}'] # @UndefinedVariable
p = subprocess.Popen(params, stdin=subprocess.PIPE)
if '{m.password}' != '':
p.stdin.write('{m.password}')
p.stdin.close()
tools.addTaskToWait(p)
# Try to locate a "valid" version of xfreerdp as first option (<1.1 does not allows drive redirections, so it will not be used if found)
xfreerdp = tools.findApp('xfreerdp')
rdesktop = tools.findApp('rdesktop')
fnc, app = None, None
if rdesktop is not None:
fnc, app = execRdesktop, rdesktop
if xfreerdp is not None:
# Check for nice version
try:
try:
version = subprocess.check_output([xfreerdp, '--version'])
except subprocess.CalledProcessError as e:
version = e.output
version = float(re.search(r'version ([0-9]*\.[0-9]*)', version).groups()[0])
if version < 1.1:
raise Exception()
else:
fnc, app = execNewXFreeRdp, xfreerdp
except Exception as e: # Valid version not found, pass to check rdesktop
# QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
pass
if app is None or fnc is None:
raise Exception('''<p>You need to have installed xfreerdp (>= 1.1) or rdesktop, and have them in your PATH in order to connect to this UDS service.</p>
<p>Please, install apropiate package for your system.</p>
<p>Also note that xfreerdp prior to version 1.1 will not be taken into consideration.</p>
''')
else:
fnc(parent, app) # @UndefinedVariable

View File

@ -0,0 +1,69 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable, invalid-sequence-index
from PyQt4 import QtCore, QtGui
import subprocess
import re
from uds.forward import forward # @UnresolvedImport
from uds import tools # @UnresolvedImport
import six
def execNewXFreeRdp(parent, xfreerdp, port):
import subprocess # @Reimport
params = [xfreerdp] + {m.r.as_new_xfreerdp_params} + ['/v:127.0.0.1:{{}}'.format(port)] # @UndefinedVariable
tools.addTaskToWait(subprocess.Popen(params))
def execRdesktop(parent, rdesktop, port):
import subprocess # @Reimport
params = [rdesktop] + {m.r.as_rdesktop_params} + ['127.0.0.1:{{}}'.format(port)] # @UndefinedVariable
p = subprocess.Popen(params, stdin=subprocess.PIPE)
if {m.hasCredentials}: # @UndefinedVariable
p.stdin.write('{m.password}')
p.stdin.close()
tools.addTaskToWait(p)
# Try to locate a "valid" version of xfreerdp as first option (<1.1 does not allows drive redirections, so it will not be used if found)
xfreerdp = tools.findApp('xfreerdp')
rdesktop = tools.findApp('rdesktop')
fnc, app = None, None
if rdesktop is not None:
fnc, app = execRdesktop, rdesktop
if xfreerdp is not None:
# Check for nice version
try:
try:
version = subprocess.check_output([xfreerdp, '--version'])
except subprocess.CalledProcessError as e:
version = e.output
version = float(re.search(r'version ([0-9]*\.[0-9]*)', version).groups()[0])
if version < 1.1:
raise Exception()
else:
fnc, app = execNewXFreeRdp, xfreerdp
except Exception as e: # Valid version not found, pass to check rdesktop
# QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
pass
if app is None or fnc is None:
raise Exception('''<p>You need to have installed xfreerdp (>= 1.1) or rdesktop, and have them in your PATH in order to connect to this UDS service.</p>
<p>Please, install apropiate package for your system.</p>
<p>Also note that xfreerdp prior to version 1.1 will not be taken into consideration.</p>
''')
else:
# Open tunnel
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
if forwardThread.status == 2:
raise Exception('Unable to open tunnel')
fnc(parent, app, port) # @UndefinedVariable

View File

@ -0,0 +1,111 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable, invalid-sequence-index
from PyQt4 import QtCore, QtGui
import subprocess
import os
import urllib
from uds import tools # @UnresolvedImport
import six
theFile = '''{m.r.as_file}'''
# First, try to locate Remote Desktop Connection (version 2, from Microsoft website, not the app store one)
filename = tools.saveTempFile(theFile)
msrdc = '/Applications/Remote Desktop Connection.app/Contents/MacOS/Remote Desktop Connection'
cord = "/Applications/CoRD.app/Contents/MacOS/CoRD"
if os.path.isfile(msrdc):
executable = msrdc
elif os.path.isfile(cord):
executable = cord
else:
executable = None
def onExit():
import subprocess # @Reimport
subprocess.call(
[
'security',
'delete-generic-password',
'-a', '{m.usernameWithDomain}',
'-s', 'Remote Desktop Connection 2 Password for {m.ip}',
]
)
if executable is None:
QtGui.QMessageBox.critical(parent, 'Notice', # @UndefinedVariable
'''<p><b>Microsoft Remote Desktop Connection not found</b></p>
<p>In order to connect to UDS RDP Sessions, you need to have at least one of the following:<p>
<ul>
<li>
<p><b>Microsoft Remote Desktop Connection version 2.</b> (Recommended)</p>
<p>You can get it from <a href="http://www.microsoft.com/es-es/download/details.aspx?id=18140">this link</a></p>
<p>Remember that you need to use the One from the Microsoft site (the link provided), not the one from the AppStore</p>
</li>
<li>
<p><b>CoRD</b> (A bit unstable from 10.7 onwards)</p>
<p>You can get it from <a href="{m.this_server}static/other/CoRD.pkg">this link</a></p>
</li>
</ul>
<p>If both apps are installed, Remote Desktop Connection will be used as first option</p>
''', QtGui.QMessageBox.Ok)
elif executable == msrdc:
try:
if {m.hasCredentials}: # @UndefinedVariable
subprocess.call(
[
'security',
'add-generic-password',
'-w', '{m.password}',
'-U',
'-a', '{m.usernameWithDomain}',
'-s', 'Remote Desktop Connection 2 Password for {m.ip}',
'-T', '/Applications/Remote Desktop Connection.app',
]
)
tools.addExecBeforeExit(onExit)
# Call and wait for exit
tools.addTaskToWait(subprocess.Popen([executable, filename]))
tools.addFileToUnlink(filename)
except Exception as e:
QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
else: # CoRD
url = 'rdp://'
username, domain = '{m.username}', '{m.domain}'
if username != '':
url += username
if '{m.password}' != '':
url += ':' + urllib.quote('{m.password}')
url += '@'
url += '{m.ip}/'
if domain != '':
url += domain
url += '?screenDepth={m.r.bpp}'
if {m.r.fullScreen}: # @UndefinedVariable
url += '&fullscreen=true'
else:
url += 'screenWidth={m.r.width}&screenHeight={m.r.height}'
url += '&forwardAudio=' + '01'[{m.r.redirectAudio}] # @UndefinedVariable
if {m.r.redirectDrives}: # @UndefinedVariable
url += '&forwardDisks=true'
if {m.r.redirectPrinters}: # @UndefinedVariable
url += '&forwardPrinters=true'
tools.addTaskToWait(subprocess.Popen(['open', url]))

View File

@ -0,0 +1,120 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable, invalid-sequence-index
from PyQt4 import QtCore, QtGui
import subprocess
import os
import urllib
from uds.forward import forward # @UnresolvedImport
from uds import tools # @UnresolvedImport
import six
# First, try to locate Remote Desktop Connection (version 2, from Microsoft website, not the app store one)
msrdc = '/Applications/Remote Desktop Connection.app/Contents/MacOS/Remote Desktop Connection'
cord = "/Applications/CoRD.app/Contents/MacOS/CoRD"
if os.path.isfile(msrdc):
executable = msrdc
elif os.path.isfile(cord):
executable = cord
else:
executable = None
def onExit():
import subprocess # @Reimport
subprocess.call(
[
'security',
'delete-generic-password',
'-a', '{m.username}',
'-s', 'Remote Desktop Connection 2 Password for 127.0.0.1',
]
)
if executable is None:
QtGui.QMessageBox.critical(parent, 'Notice', # @UndefinedVariable
'''<p><b>Microsoft Remote Desktop Connection not found</b></p>
<p>In order to connect to UDS RDP Sessions, you need to have at least one of the following:<p>
<ul>
<li>
<p><b>Microsoft Remote Desktop Connection version 2.</b> (Recommended)</p>
<p>You can get it from <a href="http://www.microsoft.com/es-es/download/details.aspx?id=18140">this link</a></p>
<p>Remember that you need to use the One from the Microsoft site (the link provided), not the one from the AppStore</p>
</li>
<li>
<p><b>CoRD</b> (A bit unstable from 10.7 onwards)</p>
<p>You can get it from <a href="{m.this_server}static/other/CoRD.pkg">this link</a></p>
</li>
</ul>
<p>If both apps are installed, Remote Desktop Connection will be used as first option</p>
''', QtGui.QMessageBox.Ok)
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
if forwardThread.status == 2:
raise Exception('Unable to open tunnel')
else:
theFile = '''{m.r.as_file}'''.format(
address='127.0.0.1:{{}}'.format(port)
)
filename = tools.saveTempFile(theFile)
tools.addFileToUnlink(filename)
if executable == msrdc:
try:
if {m.hasCredentials}: # @UndefinedVariable
subprocess.call(
[
'security',
'add-generic-password',
'-w', '{m.password}',
'-U',
'-a', '{m.username}',
'-s', 'Remote Desktop Connection 2 Password for 127.0.0.1'.format(port),
'-T', '/Applications/Remote Desktop Connection.app',
]
)
tools.addExecBeforeExit(onExit)
# Call but do not wait for exit
tools.addTaskToWait(subprocess.Popen([executable, filename]))
tools.addFileToUnlink(filename)
except Exception as e:
QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
else: # CoRD
url = 'rdp://'
username, domain = '{m.username}', '{m.domain}'
if username != '':
url += username
if '{m.password}' != '':
url += ':' + urllib.quote('{m.password}')
url += '@'
url += '127.0.0.1:3389/'
if domain != '':
url += domain
url += '?screenDepth={m.r.bpp}'
if {m.r.fullScreen}: # @UndefinedVariable
url += '&fullscreen=true'
else:
url += 'screenWidth={m.r.width}&screenHeight={m.r.height}'
url += '&forwardAudio=' + '01'[{m.r.redirectAudio}] # @UndefinedVariable
if {m.r.redirectDrives}: # @UndefinedVariable
url += '&forwardDisks=true'
if {m.r.redirectPrinters}: # @UndefinedVariable
url += '&forwardPrinters=true'
tools.addTaskToWait(subprocess.Popen(['open', url]))

View File

@ -0,0 +1,23 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module
from PyQt4 import QtCore, QtGui
import win32crypt # @UnresolvedImport
import os
import subprocess
from uds import tools # @UnresolvedImport
import six
# 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(password=win32crypt.CryptProtectData(six.binary_type('{m.password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'))
filename = tools.saveTempFile(theFile)
executable = tools.findApp('mstsc.exe')
subprocess.call([executable, filename])
tools.addFileToUnlink(filename)
# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok)

View File

@ -0,0 +1,36 @@
# This is a template
# Saved as .py for easier editing
from __future__ import unicode_literals
# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable
from PyQt4 import QtCore, QtGui
import win32crypt # @UnresolvedImport
import os
import subprocess
from uds.forward import forward # @UnresolvedImport
from uds import tools # @UnresolvedImport
import six
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
if forwardThread.status == 2:
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
theFile = '''{m.r.as_file}'''.format(
password=win32crypt.CryptProtectData(six.binary_type('{m.password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'),
address='127.0.0.1:{{}}'.format(port)
)
filename = tools.saveTempFile(theFile)
executable = tools.findApp('mstsc.exe')
if executable is None:
raise Exception('Unable to find mstsc.exe')
subprocess.call([executable, filename])
tools.addFileToUnlink(filename)
# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok)