mirror of
https://github.com/dkmstr/openuds.git
synced 2025-10-07 15:33:51 +03:00
Compare commits
14 Commits
tunnel3.0-
...
nonMultiCl
Author | SHA1 | Date | |
---|---|---|---|
|
1417a66b21 | ||
|
9ba4234313 | ||
|
e85e4c4e54 | ||
|
693d14cd81 | ||
|
4be9e9ea69 | ||
|
e38cd73f30 | ||
|
43b785eb73 | ||
|
9c4a4ed35c | ||
|
02737c0e8d | ||
|
8bbd897cd0 | ||
|
c98933b6ed | ||
|
6e0292e76e | ||
|
8e6fced2ac | ||
|
c5a02686c4 |
@@ -128,11 +128,13 @@ class UDSClient(QtWidgets.QMainWindow):
|
||||
self.anim = 0
|
||||
self.animInverted = False
|
||||
self.ui.progressBar.setInvertedAppearance(self.animInverted)
|
||||
self.animTimer.start(40)
|
||||
if self.animTimer:
|
||||
self.animTimer.start(40)
|
||||
|
||||
def stopAnim(self):
|
||||
self.ui.progressBar.invertedAppearance = False # type: ignore
|
||||
self.animTimer.stop()
|
||||
if self.animTimer:
|
||||
self.animTimer.stop()
|
||||
|
||||
def getVersion(self):
|
||||
try:
|
||||
|
@@ -93,6 +93,7 @@ class Handler(socketserver.BaseRequestHandler):
|
||||
class ForwardThread(threading.Thread):
|
||||
status = 0 # Connecting
|
||||
client: typing.Optional[paramiko.SSHClient]
|
||||
fs: typing.Optional[ForwardServer]
|
||||
|
||||
def __init__(self, server, port, username, password, localPort, redirectHost, redirectPort, waitTime, fingerPrints):
|
||||
threading.Thread.__init__(self)
|
||||
@@ -180,7 +181,9 @@ class ForwardThread(threading.Thread):
|
||||
self.timer.cancel()
|
||||
|
||||
self.stopEvent.set()
|
||||
self.fs.shutdown()
|
||||
|
||||
if self.fs:
|
||||
self.fs.shutdown()
|
||||
|
||||
if self.client is not None:
|
||||
self.client.useCount -= 1 # type: ignore
|
||||
|
@@ -132,12 +132,15 @@ class ForwardServer(socketserver.ThreadingTCPServer):
|
||||
if self.status == TUNNEL_ERROR:
|
||||
return False
|
||||
|
||||
logger.debug('Checking tunnel availability')
|
||||
|
||||
try:
|
||||
with self.connect() as ssl_socket:
|
||||
ssl_socket.sendall(HANDSHAKE_V1 + b'TEST')
|
||||
resp = ssl_socket.recv(2)
|
||||
if resp != b'OK':
|
||||
raise Exception({'Invalid tunnelresponse: {resp}'})
|
||||
logger.debug('Tunnel is available!')
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
@@ -220,7 +223,7 @@ class Handler(socketserver.BaseRequestHandler):
|
||||
if not data:
|
||||
break
|
||||
self.request.sendall(data)
|
||||
logger.debug('Finished tunnel with ticekt %s', self.server.ticket)
|
||||
logger.debug('Finished tunnel with ticket %s', self.server.ticket)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
@@ -112,11 +112,11 @@ class Login(Handler):
|
||||
logger.info('Access to REST API %s is blocked for %s seconds since last fail', self._request.ip, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
|
||||
try:
|
||||
if 'authId' not in self._params and 'authSmallName' not in self._params and 'auth' not in self._params:
|
||||
if 'auth_id' not in self._params and 'authId' not in self._params and 'authSmallName' not in self._params and 'auth' not in self._params:
|
||||
raise RequestError('Invalid parameters (no auth)')
|
||||
|
||||
scrambler: str = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(32)) # @UndefinedVariable
|
||||
authId: typing.Optional[str] = self._params.get('authId', None)
|
||||
authId: typing.Optional[str] = self._params.get('authId', self._params.get('auth_id', None))
|
||||
authSmallName: typing.Optional[str] = self._params.get('authSmallName', None)
|
||||
authName: typing.Optional[str] = self._params.get('auth', None)
|
||||
platform: str = self._params.get('platform', self._request.os)
|
||||
@@ -126,7 +126,7 @@ class Login(Handler):
|
||||
|
||||
username, password = self._params['username'], self._params['password']
|
||||
locale: str = self._params.get('locale', 'en')
|
||||
if authName == 'admin' or authSmallName == 'admin':
|
||||
if authName == 'admin' or authSmallName == 'admin' or authId == '00000000-0000-0000-0000-000000000000':
|
||||
if GlobalConfig.SUPER_USER_LOGIN.get(True) == username and GlobalConfig.SUPER_USER_PASS.get(True) == password:
|
||||
self.genAuthToken(-1, username, password, locale, platform, True, True, scrambler)
|
||||
return Login.result(result='ok', token=self.getAuthToken())
|
||||
|
@@ -38,9 +38,8 @@ from uds.core import managers
|
||||
from uds.REST import Handler
|
||||
from uds.REST import AccessDenied
|
||||
from uds.core.auths.auth import isTrustedSource
|
||||
from uds.core.util import log, net
|
||||
from uds.core.util import log, net, request
|
||||
from uds.core.util.stats import events
|
||||
from uds.models.util import getSqlDatetime
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -71,9 +70,12 @@ class TunnelTicket(Handler):
|
||||
# Invalid requests
|
||||
raise AccessDenied()
|
||||
|
||||
# If args is 3, the last one is the authId
|
||||
authId = self._args[2][:48]
|
||||
# TODO: Check auth Id
|
||||
# Take token from url
|
||||
token = self._args[2][:48]
|
||||
if not models.TunnelToken.validateToken(token):
|
||||
logger.error('Invalid token %s from %s', token, self._request.ip)
|
||||
raise AccessDenied()
|
||||
|
||||
|
||||
# Try to get ticket from DB
|
||||
try:
|
||||
|
@@ -37,8 +37,8 @@ import typing
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
from uds.core import auths
|
||||
from uds.core.util import net
|
||||
from uds.core.util.request import getRequest
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util.request import getRequest
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -72,7 +72,7 @@ class IPAuth(auths.Authenticator):
|
||||
blockUserOnLoginFailures = False
|
||||
|
||||
def getIp(self) -> str:
|
||||
ip = getRequest().ip_proxy if self.acceptProxy.isTrue() else getRequest().ip # pylint: disable=maybe-no-member
|
||||
ip = getRequest().ip_proxy if self.acceptProxy.isTrue() else getRequest().ip
|
||||
logger.debug('Client IP: %s', ip)
|
||||
return ip
|
||||
|
||||
@@ -88,7 +88,6 @@ class IPAuth(auths.Authenticator):
|
||||
|
||||
def authenticate(self, username: str, credentials: str, groupsManager: 'auths.GroupsManager') -> bool:
|
||||
# If credentials is a dict, that can't be sent directly from web interface, we allow entering
|
||||
# We use this "trick" so authenticators
|
||||
if username == self.getIp():
|
||||
self.getGroups(username, groupsManager)
|
||||
return True
|
||||
|
@@ -44,6 +44,7 @@ from uds.core.ui import gui
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds.core.util.state import State
|
||||
from uds.core.util.request import getRequest
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -135,6 +136,7 @@ class InternalDBAuth(auths.Authenticator):
|
||||
try:
|
||||
user: 'models.User' = dbAuth.users.get(name=username, state=State.ACTIVE)
|
||||
except Exception:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid user')
|
||||
return False
|
||||
|
||||
if user.parent: # Direct auth not allowed for "derived" users
|
||||
@@ -144,6 +146,7 @@ class InternalDBAuth(auths.Authenticator):
|
||||
if cryptoManager().checkHash(credentials, user.password):
|
||||
groupsManager.validate([g.name for g in user.groups.all()])
|
||||
return True
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid password')
|
||||
return False
|
||||
|
||||
def getGroups(self, username: str, groupsManager: 'auths.GroupsManager'):
|
||||
|
@@ -34,16 +34,17 @@ import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
|
||||
from uds.core.ui import gui
|
||||
from uds.core import auths
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.util.request import getRequest
|
||||
|
||||
from . import client
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import (
|
||||
HttpRequest
|
||||
)
|
||||
pass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -131,6 +132,7 @@ class RadiusAuth(auths.Authenticator):
|
||||
connection = self.radiusClient()
|
||||
groups = connection.authenticate(username=username, password=credentials)
|
||||
except Exception:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Access denied by Raiuds')
|
||||
return False
|
||||
|
||||
if self.globalGroup.value.strip():
|
||||
|
@@ -38,9 +38,12 @@ import typing
|
||||
import ldap
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
|
||||
from uds.core import auths
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import ldaputil
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.util.request import getRequest
|
||||
|
||||
try:
|
||||
# pylint: disable=no-name-in-module
|
||||
@@ -498,12 +501,17 @@ class RegexLdap(auths.Authenticator):
|
||||
usr = self.__getUser(username)
|
||||
|
||||
if usr is None:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid user')
|
||||
return False
|
||||
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
usr['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
try:
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
usr['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
except:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid password')
|
||||
return False
|
||||
|
||||
groupsManager.validate(self.__getGroups(usr))
|
||||
|
||||
|
@@ -37,9 +37,12 @@ import ldap.filter
|
||||
import ldap
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
|
||||
from uds.core.ui import gui
|
||||
from uds.core import auths
|
||||
from uds.core.util import ldaputil
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.util.request import getRequest
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@@ -398,12 +401,17 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
user = self.__getUser(username)
|
||||
|
||||
if user is None:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid user')
|
||||
return False
|
||||
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
user['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
try:
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
user['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
except:
|
||||
authLogLogin(getRequest(), self.dbAuthenticator(), username, 'Invalid password')
|
||||
return False
|
||||
|
||||
groupsManager.validate(self.__getGroups(user))
|
||||
|
||||
|
@@ -51,7 +51,6 @@ from uds.core import auths
|
||||
from uds.core.util import log
|
||||
from uds.core.util import net
|
||||
from uds.core.util.config import GlobalConfig
|
||||
from uds.core.util.decorators import deprecated
|
||||
from uds.core.util.stats import events
|
||||
from uds.core.util.state import State
|
||||
from uds.core.managers import cryptoManager
|
||||
@@ -378,7 +377,7 @@ def webLogin(
|
||||
manager_id,
|
||||
user.name,
|
||||
password,
|
||||
get_language(),
|
||||
get_language() or '',
|
||||
request.os['OS'],
|
||||
user.is_admin,
|
||||
user.staff_member,
|
||||
|
@@ -31,13 +31,11 @@
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from django.conf.urls import url
|
||||
from .views import guacamole
|
||||
from .views import guacamole, guacamole_authenticated
|
||||
|
||||
urlpatterns = [
|
||||
# Old, compat
|
||||
url(r'^guacamole/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole'),
|
||||
# New path
|
||||
url(r'^uds/guacamole/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole'),
|
||||
# Authenticated path
|
||||
url(r'^uds/guacamole/auth/(?P<authId>.+)/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole'),
|
||||
url(r'^uds/guacamole/auth/(?P<token>[^/]+)/(?P<tunnelId>.+)$', guacamole_authenticated, name='dispatcher.guacamole'),
|
||||
# Non authenticated path. Disabled
|
||||
# url(r'^uds/guacamole/(?P<tunnelId>.+)$', guacamole, name='dispatcher.guacamole.noauth'),
|
||||
]
|
||||
|
@@ -34,10 +34,10 @@ import logging
|
||||
|
||||
from django.http import HttpResponse, HttpRequest
|
||||
|
||||
from uds.models import TicketStore, UserService
|
||||
from uds.models import TicketStore, UserService, TunnelToken
|
||||
from uds.core.auths import auth
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from uds.core.util.request import ExtendedHttpRequestWithUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -52,7 +52,7 @@ def dict2resp(dct):
|
||||
|
||||
|
||||
@auth.trustedSourceRequired
|
||||
def guacamole(request: HttpRequest, tunnelId: str) -> HttpResponse:
|
||||
def guacamole(request: ExtendedHttpRequestWithUser, tunnelId: str) -> HttpResponse:
|
||||
logger.debug('Received credentials request for tunnel id %s', tunnelId)
|
||||
|
||||
try:
|
||||
@@ -85,7 +85,9 @@ def guacamole(request: HttpRequest, tunnelId: str) -> HttpResponse:
|
||||
return HttpResponse(response, content_type=CONTENT_TYPE)
|
||||
|
||||
@auth.trustedSourceRequired
|
||||
def guacamole_authenticated(request: HttpRequest, authId: str, tunnelId: str) -> HttpResponse:
|
||||
authId = authId[:48]
|
||||
def guacamole_authenticated(request: ExtendedHttpRequestWithUser, token: str, tunnelId: str) -> HttpResponse:
|
||||
if not TunnelToken.validateToken(token):
|
||||
logger.error('Invalid token %s from %s', token, request.ip)
|
||||
return HttpResponse(ERROR, content_type=CONTENT_TYPE)
|
||||
# TODO: Check the authId validity
|
||||
return guacamole(request, tunnelId)
|
@@ -35,6 +35,7 @@ import logging
|
||||
from django.http import HttpResponseNotAllowed, HttpResponse, HttpRequest
|
||||
from uds.models import TicketStore
|
||||
from uds.core.auths import auth
|
||||
from uds.core.util.request import ExtendedHttpRequestWithUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -42,7 +43,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@auth.trustedSourceRequired
|
||||
def pam(request: HttpRequest) -> HttpResponse:
|
||||
def pam(request: ExtendedHttpRequestWithUser) -> HttpResponse:
|
||||
response = ''
|
||||
if request.method == 'POST':
|
||||
return HttpResponseNotAllowed(['GET'])
|
||||
|
@@ -28,8 +28,10 @@
|
||||
'''
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from django.db import models
|
||||
import typing
|
||||
|
||||
from django.db import models
|
||||
from uds.core.util.request import ExtendedHttpRequest
|
||||
|
||||
class TunnelToken(models.Model):
|
||||
"""
|
||||
@@ -52,7 +54,18 @@ class TunnelToken(models.Model):
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=['ip', 'hostname'], name='tt_ip_hostname')
|
||||
]
|
||||
|
||||
|
||||
@staticmethod
|
||||
def validateToken(token: str, request: typing.Optional[ExtendedHttpRequest] = None) -> bool:
|
||||
try:
|
||||
tt = TunnelToken.objects.get(token=token)
|
||||
# We could check the request ip here
|
||||
if request and request.ip != tt.ip:
|
||||
raise Exception('Invalid ip')
|
||||
return True
|
||||
except TunnelToken.DoesNotExist:
|
||||
pass
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return '<TunnelToken {} created on {} by {} from {}/{}>'.format(
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -12,7 +12,7 @@
|
||||
# * 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
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@@ -49,6 +49,7 @@ def ensureConnected(fnc: typing.Callable[..., RT]) -> typing.Callable[..., RT]:
|
||||
def inner(*args, **kwargs) -> RT:
|
||||
args[0].connect()
|
||||
return fnc(*args, **kwargs)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
@@ -113,10 +114,14 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
def enumStorage(self, storageType: int = 0) -> typing.Iterable[types.StorageType]:
|
||||
sstorageType = str(storageType) # Ensure it is an string
|
||||
# Invoke datastore pools info, no parameters except connection string
|
||||
result, _ = checkResult(self.connection.one.datastorepool.info(self.sessionString))
|
||||
result, _ = checkResult(
|
||||
self.connection.one.datastorepool.info(self.sessionString)
|
||||
)
|
||||
for ds in asIterable(result['DATASTORE_POOL']['DATASTORE']):
|
||||
if ds['TYPE'] == sstorageType:
|
||||
yield types.StorageType(ds['ID'], ds['NAME'], int(ds['TOTAL_MB']), int(ds['FREE_MB']), None)
|
||||
yield types.StorageType(
|
||||
ds['ID'], ds['NAME'], int(ds['TOTAL_MB']), int(ds['FREE_MB']), None
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
def enumTemplates(self) -> typing.Iterable[types.TemplateType]:
|
||||
@@ -127,10 +132,14 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
3.- When the next parameter is >= -1 this is the Range start ID. Can be -1. For smaller values this is the offset used for pagination.
|
||||
4.- For values >= -1 this is the Range end ID. Can be -1 to get until the last ID. For values < -1 this is the page size used for pagination.
|
||||
"""
|
||||
result, _ = checkResult(self.connection.one.templatepool.info(self.sessionString, -1, -1, -1))
|
||||
result, _ = checkResult(
|
||||
self.connection.one.templatepool.info(self.sessionString, -1, -1, -1)
|
||||
)
|
||||
for ds in asIterable(result['VMTEMPLATE_POOL']['VMTEMPLATE']):
|
||||
try:
|
||||
yield types.TemplateType(ds['ID'], ds['NAME'], int(ds['TEMPLATE']['MEMORY']), None)
|
||||
yield types.TemplateType(
|
||||
ds['ID'], ds['NAME'], int(ds['TEMPLATE']['MEMORY']), None
|
||||
)
|
||||
except Exception: # Maybe no memory? (then template is not usable)
|
||||
pass
|
||||
|
||||
@@ -143,7 +152,9 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
3.- When the next parameter is >= -1 this is the Range start ID. Can be -1. For smaller values this is the offset used for pagination.
|
||||
4.- For values >= -1 this is the Range end ID. Can be -1 to get until the last ID. For values < -1 this is the page size used for pagination.
|
||||
"""
|
||||
result, _ = checkResult(self.connection.one.imagepool.info(self.sessionString, -1, -1, -1))
|
||||
result, _ = checkResult(
|
||||
self.connection.one.imagepool.info(self.sessionString, -1, -1, -1)
|
||||
)
|
||||
for ds in asIterable(result['IMAGE_POOL']['IMAGE']):
|
||||
yield types.ImageType(
|
||||
ds['ID'],
|
||||
@@ -152,22 +163,38 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
ds.get('PERSISTENT', '0') != '0',
|
||||
int(ds.get('RUNNING_VMS', '0')),
|
||||
types.ImageState.fromState(ds['STATE']),
|
||||
None
|
||||
None,
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
def templateInfo(self, templateId: str, extraInfo: bool = False) -> types.TemplateType:
|
||||
def templateInfo(
|
||||
self, templateId: str, extraInfo: bool = False
|
||||
) -> types.TemplateType:
|
||||
"""
|
||||
Returns a list
|
||||
first element is a dictionary (built from XML)
|
||||
second is original XML
|
||||
"""
|
||||
result = self.connection.one.template.info(self.sessionString, int(templateId), extraInfo)
|
||||
result = self.connection.one.template.info(
|
||||
self.sessionString, int(templateId), extraInfo
|
||||
)
|
||||
ds, xml = checkResult(result)
|
||||
return types.TemplateType(ds['VMTEMPLATE']['ID'], ds['VMTEMPLATE']['NAME'], int(ds['VMTEMPLATE']['TEMPLATE']['MEMORY']), xml)
|
||||
return types.TemplateType(
|
||||
ds['VMTEMPLATE']['ID'],
|
||||
ds['VMTEMPLATE']['NAME'],
|
||||
int(ds['VMTEMPLATE']['TEMPLATE']['MEMORY']),
|
||||
xml,
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
def instantiateTemplate(self, templateId: str, vmName: str, createHold: bool = False, templateToMerge: str = '', privatePersistent: bool = False) -> str:
|
||||
def instantiateTemplate(
|
||||
self,
|
||||
templateId: str,
|
||||
vmName: str,
|
||||
createHold: bool = False,
|
||||
templateToMerge: str = '',
|
||||
privatePersistent: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Instantiates a template (compatible with open nebula 4 & 5)
|
||||
1.- Session string
|
||||
@@ -178,15 +205,26 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
6.- true to create a private persistent copy of the template plus any image defined in DISK, and instantiate that copy.
|
||||
Note: This parameter is ignored on version 4, it is new for version 5.
|
||||
"""
|
||||
if self.version[0] == '4': # Version 4 has one less parameter than version 5
|
||||
result = self.connection.one.template.instantiate(self.sessionString, int(templateId), vmName, createHold, templateToMerge)
|
||||
if self.version[0] == '4': # type: ignore # Version 4 has one less parameter than version 5
|
||||
result = self.connection.one.template.instantiate(
|
||||
self.sessionString, int(templateId), vmName, createHold, templateToMerge
|
||||
)
|
||||
else:
|
||||
result = self.connection.one.template.instantiate(self.sessionString, int(templateId), vmName, createHold, templateToMerge, privatePersistent)
|
||||
result = self.connection.one.template.instantiate(
|
||||
self.sessionString,
|
||||
int(templateId),
|
||||
vmName,
|
||||
createHold,
|
||||
templateToMerge,
|
||||
privatePersistent,
|
||||
)
|
||||
|
||||
return checkResultRaw(result)
|
||||
|
||||
@ensureConnected
|
||||
def updateTemplate(self, templateId: str, templateData: str, updateType: int = 0) -> str:
|
||||
def updateTemplate(
|
||||
self, templateId: str, templateData: str, updateType: int = 0
|
||||
) -> str:
|
||||
"""
|
||||
Updates the template with the templateXml
|
||||
1.- Session string
|
||||
@@ -194,7 +232,9 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
3.- The new template contents. Syntax can be the usual attribute=value or XML.
|
||||
4.- Update type. 0 replace the whole template, 1 merge with the existing one
|
||||
"""
|
||||
result = self.connection.one.template.update(self.sessionString, int(templateId), templateData, int(updateType))
|
||||
result = self.connection.one.template.update(
|
||||
self.sessionString, int(templateId), templateData, int(updateType)
|
||||
)
|
||||
return checkResultRaw(result)
|
||||
|
||||
@ensureConnected
|
||||
@@ -202,10 +242,14 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Clones the template
|
||||
"""
|
||||
if self.version[0] == '4':
|
||||
result = self.connection.one.template.clone(self.sessionString, int(templateId), name)
|
||||
if self.version[0] == '4': # type: ignore
|
||||
result = self.connection.one.template.clone(
|
||||
self.sessionString, int(templateId), name
|
||||
)
|
||||
else:
|
||||
result = self.connection.one.template.clone(self.sessionString, int(templateId), name, False) # This works as previous version clone
|
||||
result = self.connection.one.template.clone(
|
||||
self.sessionString, int(templateId), name, False
|
||||
) # This works as previous version clone
|
||||
|
||||
return checkResultRaw(result)
|
||||
|
||||
@@ -214,15 +258,21 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Deletes the template (not images)
|
||||
"""
|
||||
result = self.connection.one.template.delete(self.sessionString, int(templateId))
|
||||
result = self.connection.one.template.delete(
|
||||
self.sessionString, int(templateId)
|
||||
)
|
||||
return checkResultRaw(result)
|
||||
|
||||
@ensureConnected
|
||||
def cloneImage(self, srcId: str, name: str, datastoreId: typing.Union[str, int] = -1) -> str:
|
||||
def cloneImage(
|
||||
self, srcId: str, name: str, datastoreId: typing.Union[str, int] = -1
|
||||
) -> str:
|
||||
"""
|
||||
Clones the image.
|
||||
"""
|
||||
result = self.connection.one.image.clone(self.sessionString, int(srcId), name, int(datastoreId))
|
||||
result = self.connection.one.image.clone(
|
||||
self.sessionString, int(srcId), name, int(datastoreId)
|
||||
)
|
||||
return checkResultRaw(result)
|
||||
|
||||
@ensureConnected
|
||||
@@ -230,7 +280,9 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Clones the image.
|
||||
"""
|
||||
result = self.connection.one.image.persistent(self.sessionString, int(imageId), persistent)
|
||||
result = self.connection.one.image.persistent(
|
||||
self.sessionString, int(imageId), persistent
|
||||
)
|
||||
return checkResultRaw(result)
|
||||
|
||||
@ensureConnected
|
||||
@@ -248,7 +300,9 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
first element is a dictionary (built from XML)
|
||||
second is original XML
|
||||
"""
|
||||
result, xml = checkResult(self.connection.one.image.info(self.sessionString, int(imageInfo)))
|
||||
result, xml = checkResult(
|
||||
self.connection.one.image.info(self.sessionString, int(imageInfo))
|
||||
)
|
||||
ds = result['IMAGE']
|
||||
return types.ImageType(
|
||||
ds['ID'],
|
||||
@@ -257,7 +311,7 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
ds.get('PERSISTENT', '0') != '0',
|
||||
int(ds.get('RUNNING_VMS', '0')),
|
||||
types.ImageState.fromState(ds['STATE']),
|
||||
xml
|
||||
xml,
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
@@ -270,10 +324,18 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
4.- For values >= -1 this is the Range end ID. Can be -1 to get until the last ID. For values < -1 this is the page size used for pagination.
|
||||
5.- VM state to filter by. (-2 = any state including DONE, -1 = any state EXCEPT DONE)
|
||||
"""
|
||||
result, _ = checkResult(self.connection.one.vmpool.info(self.sessionString, -1, -1, -1, -1))
|
||||
result, _ = checkResult(
|
||||
self.connection.one.vmpool.info(self.sessionString, -1, -1, -1, -1)
|
||||
)
|
||||
if result['VM_POOL']:
|
||||
for ds in asIterable(result['VM_POOL'].get('VM', [])):
|
||||
yield types.VirtualMachineType(ds['ID'], ds['NAME'], int(ds.get('MEMORY', '0')), types.VmState.fromState(ds['STATE']), None)
|
||||
yield types.VirtualMachineType(
|
||||
ds['ID'],
|
||||
ds['NAME'],
|
||||
int(ds.get('MEMORY', '0')),
|
||||
types.VmState.fromState(ds['STATE']),
|
||||
None,
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
def VMInfo(self, vmId: str) -> types.VirtualMachineType:
|
||||
@@ -282,16 +344,24 @@ class OpenNebulaClient: # pylint: disable=too-many-public-methods
|
||||
first element is a dictionary (built from XML)
|
||||
second is original XML
|
||||
"""
|
||||
result, xml = checkResult(self.connection.one.vm.info(self.sessionString, int(vmId)))
|
||||
result, xml = checkResult(
|
||||
self.connection.one.vm.info(self.sessionString, int(vmId))
|
||||
)
|
||||
ds = result['VM']
|
||||
return types.VirtualMachineType(ds['ID'], ds['NAME'], int(ds.get('MEMORY', '0')), types.VmState.fromState(ds['STATE']), xml)
|
||||
return types.VirtualMachineType(
|
||||
ds['ID'],
|
||||
ds['NAME'],
|
||||
int(ds.get('MEMORY', '0')),
|
||||
types.VmState.fromState(ds['STATE']),
|
||||
xml,
|
||||
)
|
||||
|
||||
@ensureConnected
|
||||
def deleteVM(self, vmId: str) -> str:
|
||||
"""
|
||||
Deletes an vm
|
||||
"""
|
||||
if self.version[0] == '4':
|
||||
if self.version[0] == '4': # type: ignore
|
||||
return self.VMAction(vmId, 'delete')
|
||||
|
||||
# Version 5
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -12,7 +12,7 @@
|
||||
# * 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
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,42 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# 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
|
||||
"""
|
||||
# Disabled OLD NX. Will be removed soon
|
||||
|
||||
# from django.utils.translation import ugettext_noop as _
|
||||
# from uds.core.managers.user_preferences import UserPrefsManager, CommonPrefs
|
||||
# from uds.core.util.config import Config
|
||||
# from .nxtransport import NXTransport
|
||||
# from .nxtunneltransport import TSNXTransport
|
||||
|
||||
# Config.section('NX').value('downloadUrl', 'http://sourceforge.net/projects/opennx/files/opennx/CI-win32/OpenNX-0.16.0.725-Setup.exe/download').get()
|
||||
# Config.section('NX').value('downloadUrlMACOS', 'http://opennx.net/download.html').get()
|
Binary file not shown.
Before Width: | Height: | Size: 4.5 KiB |
@@ -1,74 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 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.
|
||||
'''
|
||||
Created on Apr 29, 2019
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
import os
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from uds.core import transports
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds import models
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
class BaseNXTransport(transports.Transport):
|
||||
_listenPort: str = ''
|
||||
|
||||
def isAvailableFor(self, userService: 'models.UserService', ip: str) -> bool:
|
||||
"""
|
||||
Checks if the transport is available for the requested destination ip
|
||||
Override this in yours transports
|
||||
"""
|
||||
logger.debug('Checking availability for %s', ip)
|
||||
ready = self.cache.get(ip)
|
||||
if not ready:
|
||||
# Check again for readyness
|
||||
if self.testServer(userService, ip, self._listenPort) is True:
|
||||
self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT)
|
||||
return True
|
||||
self.cache.put(ip, 'N', READY_CACHE_TIMEOUT)
|
||||
return ready == 'Y'
|
||||
|
||||
def getScript(self, scriptNameTemplate: str, osName: str, params: typing.Dict[str, typing.Any]) -> typing.Tuple[str, str, typing.Dict[str, typing.Any]]:
|
||||
# Reads script
|
||||
scriptNameTemplate = scriptNameTemplate.format(osName)
|
||||
with open(os.path.join(os.path.dirname(__file__), scriptNameTemplate)) as f:
|
||||
script = f.read()
|
||||
# Reads signature
|
||||
with open(os.path.join(os.path.dirname(__file__), scriptNameTemplate + '.signature')) as f:
|
||||
signature = f.read()
|
||||
return script, signature, params
|
@@ -1,220 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 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.
|
||||
|
||||
|
||||
"""
|
||||
Created on Jul 29, 2011
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
|
||||
"""
|
||||
from .nxpassword import NXPassword
|
||||
|
||||
EMPTY_PASSWORD = "EMPTY_PASSWORD"
|
||||
|
||||
NXTEMPLATE = (
|
||||
"<!DOCTYPE NXClientSettings>\n"
|
||||
"<NXClientSettings application=\"nxclient\" version=\"1.3\" >\n"
|
||||
" <group name=\"Advanced\" >\n"
|
||||
" <option key=\"Cache size\" value=\"{CACHEMEM}\" />\n"
|
||||
" <option key=\"Cache size on disk\" value=\"{CACHEDISK}\" />\n"
|
||||
" <option key=\"Current keyboard\" value=\"true\" />\n"
|
||||
" <option key=\"Custom keyboard layout\" value=\"{KEYLAYOUT}\" />\n"
|
||||
" <option key=\"Disable ZLIB stream compression\" value=\"false\" />\n"
|
||||
" <option key=\"Disable TCP no-delay\" value=\"false\" />\n" +
|
||||
" <option key=\"Disable deferred updates\" value=\"false\" />\n"
|
||||
" <option key=\"Enable HTTP proxy\" value=\"false\" />\n"
|
||||
" <option key=\"Enable SSL encryption\" value=\"true\" />\n"
|
||||
" <option key=\"Enable response time optimisations\" value=\"true\" />\n"
|
||||
" <option key=\"Grab keyboard\" value=\"false\" />\n"
|
||||
" <option key=\"HTTP proxy host\" value=\"\" />\n"
|
||||
" <option key=\"HTTP proxy port\" value=\"8080\" />\n"
|
||||
" <option key=\"HTTP proxy username\" value=\"\" />\n"
|
||||
" <option key=\"Remember HTTP proxy password\" value=\"false\" />\n"
|
||||
" <option key=\"Restore cache\" value=\"true\" />\n"
|
||||
" <option key=\"StreamCompression\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Environment\" >\n"
|
||||
" <option key=\"Font server host\" value=\"\" />\n"
|
||||
" <option key=\"Font server port\" value=\"7100\" />\n"
|
||||
" <option key=\"Use font server\" value=\"false\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"General\" >\n"
|
||||
" <option key=\"Automatic reconnect\" value=\"true\" />\n"
|
||||
" <option key=\"Disable SHM\" value=\"false\" />\n"
|
||||
" <option key=\"Disable emulate shared pixmaps\" value=\"false\" />\n"
|
||||
" <option key=\"Link speed\" value=\"{LINKSPEED}\" />\n"
|
||||
" <option key=\"Remember password\" value=\"{REMEMBERPASS}\" />\n"
|
||||
" <option key=\"Resolution\" value=\"{RESOLUTION}\" />\n"
|
||||
" <option key=\"Resolution width\" value=\"{WIDTH}\" />\n"
|
||||
" <option key=\"Resolution height\" value=\"{HEIGHT}\" />\n"
|
||||
" <option key=\"Server host\" value=\"{HOST}\" />\n"
|
||||
" <option key=\"Server port\" value=\"{PORT}\" />\n"
|
||||
" <option key=\"Session\" value=\"unix\" />\n"
|
||||
" <option key=\"Desktop\" value=\"{DESKTOP}\" />\n"
|
||||
" <option key=\"Use default image encoding\" value=\"1\" />\n"
|
||||
" <option key=\"Use render\" value=\"false\" />\n"
|
||||
" <option key=\"Use taint\" value=\"true\" />\n"
|
||||
" <option key=\"Virtual desktop\" value=\"false\" />\n"
|
||||
" <option key=\"XAgent encoding\" value=\"true\" />\n"
|
||||
" <option key=\"displaySaveOnExit\" value=\"true\" />\n"
|
||||
" <option key=\"xdm broadcast port\" value=\"177\" />\n"
|
||||
" <option key=\"xdm list host\" value=\"localhost\" />\n"
|
||||
" <option key=\"xdm list port\" value=\"177\" />\n"
|
||||
" <option key=\"xdm mode\" value=\"server decide\" />\n"
|
||||
" <option key=\"xdm query host\" value=\"localhost\" />\n"
|
||||
" <option key=\"xdm query port\" value=\"177\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Images\" >\n"
|
||||
" <option key=\"Disable JPEG Compression\" value=\"0\" />\n"
|
||||
" <option key=\"Disable all image optimisations\" value=\"false\" />\n"
|
||||
" <option key=\"Disable backingstore\" value=\"false\" />\n"
|
||||
" <option key=\"Disable composite\" value=\"false\" />\n"
|
||||
" <option key=\"Image Compression Type\" value=\"3\" />\n"
|
||||
" <option key=\"Image Encoding Type\" value=\"0\" />\n"
|
||||
" <option key=\"Image JPEG Encoding\" value=\"false\" />\n"
|
||||
" <option key=\"JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"RDP Image Encoding\" value=\"3\" />\n"
|
||||
" <option key=\"RDP JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"RDP optimization for low-bandwidth link\" value=\"false\" />\n"
|
||||
" <option key=\"Reduce colors to\" value=\"\" />\n"
|
||||
" <option key=\"Use PNG Compression\" value=\"true\" />\n"
|
||||
" <option key=\"VNC JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"VNC images compression\" value=\"3\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Login\" >\n"
|
||||
" <option key=\"User\" value=\"{USERNAME}\" />\n"
|
||||
" <option key=\"Auth\" value=\"{PASSWORD}\" />\n"
|
||||
" <option key=\"Guest Mode\" value=\"false\" />\n"
|
||||
" <option key=\"Guest password\" value=\"\" />\n"
|
||||
" <option key=\"Guest username\" value=\"\" />\n"
|
||||
" <option key=\"Login Method\" value=\"nx\" />\n"
|
||||
" <option key=\"Public Key\" value=\"-----BEGIN DSA PRIVATE KEY-----\n"
|
||||
"MIIBuwIBAAKBgQCXv9AzQXjxvXWC1qu3CdEqskX9YomTfyG865gb4D02ZwWuRU/9\n"
|
||||
"C3I9/bEWLdaWgJYXIcFJsMCIkmWjjeSZyTmeoypI1iLifTHUxn3b7WNWi8AzKcVF\n"
|
||||
"aBsBGiljsop9NiD1mEpA0G+nHHrhvTXz7pUvYrsrXcdMyM6rxqn77nbbnwIVALCi\n"
|
||||
"xFdHZADw5KAVZI7r6QatEkqLAoGBAI4L1TQGFkq5xQ/nIIciW8setAAIyrcWdK/z\n"
|
||||
"5/ZPeELdq70KDJxoLf81NL/8uIc4PoNyTRJjtT3R4f8Az1TsZWeh2+ReCEJxDWgG\n"
|
||||
"fbk2YhRqoQTtXPFsI4qvzBWct42WonWqyyb1bPBHk+JmXFscJu5yFQ+JUVNsENpY\n"
|
||||
"+Gkz3HqTAoGANlgcCuA4wrC+3Cic9CFkqiwO/Rn1vk8dvGuEQqFJ6f6LVfPfRTfa\n"
|
||||
"QU7TGVLk2CzY4dasrwxJ1f6FsT8DHTNGnxELPKRuLstGrFY/PR7KeafeFZDf+fJ3\n"
|
||||
"mbX5nxrld3wi5titTnX+8s4IKv29HJguPvOK/SI7cjzA+SqNfD7qEo8CFDIm1xRf\n"
|
||||
"8xAPsSKs6yZ6j1FNklfu\n"
|
||||
"-----END DSA PRIVATE KEY-----\n"
|
||||
"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Services\" >\n"
|
||||
" <option key=\"Audio\" value=\"true\" />\n"
|
||||
" <option key=\"IPPPort\" value=\"631\" />\n"
|
||||
" <option key=\"IPPPrinting\" value=\"false\" />\n"
|
||||
" <option key=\"Shares\" value=\"false\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"VNC Session\" >\n"
|
||||
" <option key=\"Display\" value=\"0\" />\n"
|
||||
" <option key=\"Remember\" value=\"false\" />\n"
|
||||
" <option key=\"Server\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Windows Session\" >\n"
|
||||
" <option key=\"Application\" value=\"\" />\n"
|
||||
" <option key=\"Authentication\" value=\"2\" />\n"
|
||||
" <option key=\"Color Depth\" value=\"16\" />\n"
|
||||
" <option key=\"Domain\" value=\"\" />\n"
|
||||
" <option key=\"Image Cache\" value=\"true\" />\n"
|
||||
" <option key=\"Password\" value=\"EMPTY_PASSWORD\" />\n"
|
||||
" <option key=\"Remember\" value=\"true\" />\n"
|
||||
" <option key=\"Run application\" value=\"false\" />\n"
|
||||
" <option key=\"Server\" value=\"\" />\n"
|
||||
" <option key=\"User\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"share chosen\" >\n"
|
||||
" <option key=\"Share number\" value=\"0\" />\n"
|
||||
" </group>\n"
|
||||
"</NXClientSettings>"
|
||||
)
|
||||
|
||||
|
||||
class NXFile:
|
||||
fullScreen: bool = False
|
||||
width: int = 800
|
||||
height: int = 600
|
||||
cachemem: str = '4'
|
||||
cachedisk: str = '32'
|
||||
keyboardLayout: str = ''
|
||||
linkSpeed: str = 'wan'
|
||||
host: str = ''
|
||||
port: str = ''
|
||||
username: str = ''
|
||||
password: str = ''
|
||||
desktop: str = 'gnome'
|
||||
|
||||
def __init__(self, username: str = '', password: str = '', width: int = 1024, height: int = 768):
|
||||
self.fullScreen = width == -1 or height == -1
|
||||
self.width = int(width)
|
||||
self.height = int(height)
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
@property
|
||||
def as_file(self):
|
||||
return self.get()
|
||||
|
||||
@property
|
||||
def as_file_for_format(self):
|
||||
return self.get(True)
|
||||
|
||||
def get(self, processPassword=False):
|
||||
rememberPass = 'true'
|
||||
password = NXPassword.scrambleString(self.password)
|
||||
if processPassword:
|
||||
password = password.replace('{', '{{')
|
||||
password = password.replace('}', '}}')
|
||||
|
||||
if password == '':
|
||||
rememberPass = "false"
|
||||
password = EMPTY_PASSWORD
|
||||
|
||||
resolution = '{}x{}'.format(self.width, self.height)
|
||||
if self.fullScreen:
|
||||
resolution = "fullscreen"
|
||||
|
||||
return NXTEMPLATE.format(
|
||||
CACHEMEM=self.cachemem,
|
||||
CACHEDISK=self.cachedisk,
|
||||
KEYLAYOUT=self.keyboardLayout,
|
||||
LINKSPEED=self.linkSpeed,
|
||||
REMEMBERPASS=rememberPass,
|
||||
RESOLUTION=resolution,
|
||||
WIDTH=self.width,
|
||||
HEIGHT=self.height,
|
||||
HOST=self.host,
|
||||
PORT=self.port,
|
||||
DESKTOP=self.desktop,
|
||||
USERNAME=self.username,
|
||||
PASSWORD=password
|
||||
)
|
@@ -1,110 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# 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.
|
||||
|
||||
|
||||
"""
|
||||
Created on Apr 20, 2015
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
|
||||
"""
|
||||
|
||||
class NXPassword: # pylint: disable=too-few-public-methods
|
||||
# Encoding method extracted from nomachine web site:
|
||||
# http://www.nomachine.com/ar/view.php?ar_id=AR01C00125
|
||||
|
||||
dummyString = "{{{{"
|
||||
numValidCharList = 85
|
||||
validCharList = [
|
||||
'!', '#', '$', '%', '&', '(', ')', '*', '+', '-',
|
||||
'.', '0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||
'9', ':', ';', '<', '>', '?', '@', 'A', 'B', 'C',
|
||||
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', '[', ']', '_', 'a', 'b', 'c', 'd',
|
||||
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||||
'y', 'z', '{', '|', '}'
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _encodePassword(p):
|
||||
sPass = ':'
|
||||
|
||||
if p == '':
|
||||
return ''
|
||||
|
||||
for i, ch in enumerate(p):
|
||||
sPass += '{}:'.format(ord(ch) + i + 1)
|
||||
|
||||
return sPass
|
||||
|
||||
@staticmethod
|
||||
def _findCharInList(c):
|
||||
try:
|
||||
return NXPassword.validCharList.index(c)
|
||||
except ValueError:
|
||||
return -1
|
||||
|
||||
@staticmethod
|
||||
def _getRandomValidCharFromList():
|
||||
# k = random.randint(0, NXPassword.numValidCharList)
|
||||
k = 0
|
||||
return NXPassword.validCharList[k]
|
||||
|
||||
@staticmethod
|
||||
def scrambleString(s):
|
||||
if s is None or s == '':
|
||||
return ''
|
||||
|
||||
_str = NXPassword._encodePassword(s)
|
||||
|
||||
if len(_str) < 32:
|
||||
_str += NXPassword.dummyString
|
||||
|
||||
password = _str[::-1] # Reversed string
|
||||
|
||||
if len(password) < 32:
|
||||
password += NXPassword.dummyString
|
||||
|
||||
startChar = NXPassword._getRandomValidCharFromList()
|
||||
l = ord(startChar) + len(password) - 2
|
||||
|
||||
pw = startChar
|
||||
|
||||
for i1, ch in enumerate(password):
|
||||
j = NXPassword._findCharInList(ch)
|
||||
if j == -1:
|
||||
return s
|
||||
|
||||
i = (j + l * (i1 + 2)) % NXPassword.numValidCharList
|
||||
pw += NXPassword.validCharList[i]
|
||||
|
||||
pw += chr(ord(NXPassword._getRandomValidCharFromList()) + 2)
|
||||
|
||||
return pw.replace('&', '&').replace('<', '<').replace('"', '"').replace('\'', ''') # .replace('$', '\\$')
|
@@ -1,243 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 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.
|
||||
|
||||
"""
|
||||
Created on Jul 29, 2011
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import ugettext_noop as _, ugettext_lazy
|
||||
from uds.core.managers.user_preferences import CommonPrefs
|
||||
from uds.core.ui import gui
|
||||
from uds.core import transports
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
|
||||
from .nxbase import BaseNXTransport
|
||||
from .nxfile import NXFile
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core import Module
|
||||
from uds import models
|
||||
from django.http import HttpRequest # pylint: disable=ungrouped-imports
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class NXTransport(BaseNXTransport):
|
||||
"""
|
||||
Provides access via NX 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 = _('NX v3.5 (DEPRECATED)')
|
||||
typeType = 'NXTransport'
|
||||
typeDescription = _('NX Protocol v3.5. Direct connection.')
|
||||
iconFile = 'nx.png'
|
||||
protocol = transports.protocols.NX
|
||||
|
||||
useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy'), tab=gui.CREDENTIALS_TAB)
|
||||
fixedName = gui.TextField(label=_('Username'), order=2, tooltip=_('If not empty, this username will be always used as credential'), tab=gui.CREDENTIALS_TAB)
|
||||
fixedPassword = gui.PasswordField(label=_('Password'), order=3, tooltip=_('If not empty, this password will be always used as credential'), tab=gui.CREDENTIALS_TAB)
|
||||
listenPort = gui.NumericField(label=_('Listening port'), length=5, order=4, tooltip=_('Listening port of NX (ssh) at client machine'), defvalue='22')
|
||||
connection = gui.ChoiceField(
|
||||
label=_('Connection'),
|
||||
order=6,
|
||||
tooltip=_('Connection speed for this transport (quality)'),
|
||||
values=[
|
||||
{'id': 'modem', 'text': 'modem'},
|
||||
{'id': 'isdn', 'text': 'isdn'},
|
||||
{'id': 'adsl', 'text': 'adsl'},
|
||||
{'id': 'wan', 'text': 'wan'},
|
||||
{'id': 'lan', 'text': 'lan'}
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB
|
||||
)
|
||||
session = gui.ChoiceField(
|
||||
label=_('Session'),
|
||||
order=7,
|
||||
tooltip=_('Desktop session'),
|
||||
values=[
|
||||
{'id': 'gnome', 'text': 'gnome'},
|
||||
{'id': 'kde', 'text': 'kde'},
|
||||
{'id': 'cde', 'text': 'cde'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB
|
||||
)
|
||||
cacheDisk = gui.ChoiceField(
|
||||
label=_('Disk Cache'),
|
||||
order=8,
|
||||
tooltip=_('Cache size en Mb stored at disk'),
|
||||
values=[
|
||||
{'id': '0', 'text': '0 Mb'},
|
||||
{'id': '32', 'text': '32 Mb'},
|
||||
{'id': '64', 'text': '64 Mb'},
|
||||
{'id': '128', 'text': '128 Mb'},
|
||||
{'id': '256', 'text': '256 Mb'},
|
||||
{'id': '512', 'text': '512 Mb'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB
|
||||
)
|
||||
cacheMem = gui.ChoiceField(
|
||||
label=_('Memory Cache'),
|
||||
order=9,
|
||||
tooltip=_('Cache size en Mb kept at memory'),
|
||||
values=[
|
||||
{'id': '4', 'text': '4 Mb'},
|
||||
{'id': '8', 'text': '8 Mb'},
|
||||
{'id': '16', 'text': '16 Mb'},
|
||||
{'id': '32', 'text': '32 Mb'},
|
||||
{'id': '64', 'text': '64 Mb'},
|
||||
{'id': '128', 'text': '128 Mb'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB
|
||||
)
|
||||
screenSize = gui.ChoiceField(
|
||||
label=_('Screen size'),
|
||||
order=10,
|
||||
tooltip=_('Screen size'),
|
||||
defvalue=CommonPrefs.SZ_FULLSCREEN,
|
||||
values=[
|
||||
{'id': CommonPrefs.SZ_640x480, 'text': '640x480'},
|
||||
{'id': CommonPrefs.SZ_800x600, 'text': '800x600'},
|
||||
{'id': CommonPrefs.SZ_1024x768, 'text': '1024x768'},
|
||||
{'id': CommonPrefs.SZ_1366x768, 'text': '1366x768'},
|
||||
{'id': CommonPrefs.SZ_1920x1080, 'text': '1920x1080'},
|
||||
{'id': CommonPrefs.SZ_FULLSCREEN, 'text': ugettext_lazy('Full Screen')}
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB
|
||||
)
|
||||
|
||||
_useEmptyCreds: bool = False
|
||||
_fixedName: str = ''
|
||||
_fixedPassword: str = ''
|
||||
_listenPort: str = ''
|
||||
_connection: str = ''
|
||||
_session: str = ''
|
||||
_cacheDisk: str = ''
|
||||
_cacheMem: str = ''
|
||||
_screenSize: str = ''
|
||||
|
||||
|
||||
def initialize(self, values: 'Module.ValuesType'):
|
||||
if values:
|
||||
self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
|
||||
self._fixedName = values['fixedName']
|
||||
self._fixedPassword = values['fixedPassword']
|
||||
self._listenPort = values['listenPort']
|
||||
self._connection = values['connection']
|
||||
self._session = values['session']
|
||||
self._cacheDisk = values['cacheDisk']
|
||||
self._cacheMem = values['cacheMem']
|
||||
self._screenSize = values['screenSize']
|
||||
|
||||
def marshal(self) -> bytes:
|
||||
"""
|
||||
Serializes the transport data so we can store it in database
|
||||
"""
|
||||
return str.join('\t', [
|
||||
'v2', gui.boolToStr(self._useEmptyCreds), self._fixedName, self._fixedPassword, self._listenPort,
|
||||
self._connection, self._session, self._cacheDisk, self._cacheMem, self._screenSize
|
||||
]).encode('utf8')
|
||||
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
values = data.decode('utf8').split('\t')
|
||||
if values[0] in ('v1', 'v2'):
|
||||
self._useEmptyCreds = gui.strToBool(values[1])
|
||||
self._fixedName, self._fixedPassword, self._listenPort, self._connection, self._session, self._cacheDisk, self._cacheMem = values[2:9]
|
||||
self._screenSize = values[9] if values[0] == 'v2' else CommonPrefs.SZ_FULLSCREEN
|
||||
|
||||
def valuesDict(self) -> gui.ValuesDictType:
|
||||
return {
|
||||
'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
|
||||
'fixedName': self._fixedName,
|
||||
'fixedPassword': self._fixedPassword,
|
||||
'listenPort': self._listenPort,
|
||||
'connection': self._connection,
|
||||
'session': self._session,
|
||||
'cacheDisk': self._cacheDisk,
|
||||
'cacheMem': self._cacheMem
|
||||
}
|
||||
|
||||
def getUDSTransportScript( # pylint: disable=too-many-locals
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest'
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, typing.Any]]:
|
||||
username = user.getUsernameForAuth()
|
||||
proc = username.split('@')
|
||||
username = proc[0]
|
||||
|
||||
if self._fixedName:
|
||||
username = self._fixedName
|
||||
if self._fixedPassword:
|
||||
password = self._fixedPassword
|
||||
if self._useEmptyCreds:
|
||||
username, password = '', ''
|
||||
|
||||
# We have the credentials right now, let os manager
|
||||
|
||||
width, height = CommonPrefs.getWidthHeight(self._screenSize)
|
||||
|
||||
# Fix username/password acording to os manager
|
||||
username, password = userService.processUserPassword(username, password)
|
||||
|
||||
r = NXFile(username=username, password=password, width=width, height=height)
|
||||
r.host = ip
|
||||
r.port = self._listenPort
|
||||
r.linkSpeed = self._connection
|
||||
r.desktop = self._session
|
||||
r.cachedisk = self._cacheDisk
|
||||
r.cachemem = self._cacheMem
|
||||
|
||||
osName = {
|
||||
OsDetector.Windows: 'windows',
|
||||
OsDetector.Linux: 'linux',
|
||||
OsDetector.Macintosh: 'macosx'
|
||||
|
||||
}.get(os['OS'])
|
||||
|
||||
if osName is None:
|
||||
return super().getUDSTransportScript(userService, transport, ip, os, user, password, request)
|
||||
|
||||
sp = {
|
||||
'as_file': r.as_file,
|
||||
}
|
||||
|
||||
return self.getScript('scripts/{}/direct.py', osName, sp)
|
@@ -1,359 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Created on Jul 29, 2011
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import random
|
||||
import string
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import ugettext_noop as _, ugettext_lazy
|
||||
|
||||
from uds.core.managers.user_preferences import CommonPrefs
|
||||
from uds.core.ui import gui
|
||||
from uds.core import transports
|
||||
from uds.models import TicketStore
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
|
||||
from .nxfile import NXFile
|
||||
from .nxbase import BaseNXTransport
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core import Module
|
||||
from uds import models
|
||||
from django.http import HttpRequest # pylint: disable=ungrouped-imports
|
||||
|
||||
|
||||
class TSNXTransport(BaseNXTransport):
|
||||
"""
|
||||
Provides access via NX 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 = _('NX v3.5 (DEPRECATED)')
|
||||
typeType = 'TSNXTransport'
|
||||
typeDescription = _('NX protocol v3.5. Tunneled connection.')
|
||||
iconFile = 'nx.png'
|
||||
protocol = transports.protocols.NX
|
||||
group = transports.TUNNELED_GROUP
|
||||
|
||||
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)'
|
||||
),
|
||||
tab=gui.TUNNEL_TAB,
|
||||
)
|
||||
|
||||
tunnelWait = gui.NumericField(
|
||||
length=3,
|
||||
label=_('Tunnel wait time'),
|
||||
defvalue='30',
|
||||
minValue=5,
|
||||
maxValue=3600 * 24,
|
||||
order=2,
|
||||
tooltip=_('Maximum time to wait before closing the tunnel listener'),
|
||||
required=True,
|
||||
tab=gui.TUNNEL_TAB,
|
||||
)
|
||||
|
||||
verifyCertificate = gui.CheckBoxField(
|
||||
label=_('Force SSL certificate verification'),
|
||||
order=23,
|
||||
tooltip=_(
|
||||
'If enabled, the certificate of tunnel server will be verified (recommended).'
|
||||
),
|
||||
defvalue=gui.FALSE,
|
||||
tab=gui.TUNNEL_TAB,
|
||||
)
|
||||
|
||||
useEmptyCreds = gui.CheckBoxField(
|
||||
label=_('Empty creds'),
|
||||
order=3,
|
||||
tooltip=_('If checked, the credentials used to connect will be emtpy'),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
fixedName = gui.TextField(
|
||||
label=_('Username'),
|
||||
order=4,
|
||||
tooltip=_('If not empty, this username will be always used as credential'),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
fixedPassword = gui.PasswordField(
|
||||
label=_('Password'),
|
||||
order=5,
|
||||
tooltip=_('If not empty, this password will be always used as credential'),
|
||||
tab=gui.CREDENTIALS_TAB,
|
||||
)
|
||||
listenPort = gui.NumericField(
|
||||
label=_('Listening port'),
|
||||
length=5,
|
||||
order=6,
|
||||
tooltip=_('Listening port of NX (ssh) at client machine'),
|
||||
defvalue='22',
|
||||
)
|
||||
connection = gui.ChoiceField(
|
||||
label=_('Connection'),
|
||||
order=7,
|
||||
tooltip=_('Connection speed for this transport (quality)'),
|
||||
values=[
|
||||
{'id': 'modem', 'text': 'modem'},
|
||||
{'id': 'isdn', 'text': 'isdn'},
|
||||
{'id': 'adsl', 'text': 'adsl'},
|
||||
{'id': 'wan', 'text': 'wan'},
|
||||
{'id': 'lan', 'text': 'lan'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
session = gui.ChoiceField(
|
||||
label=_('Session'),
|
||||
order=8,
|
||||
tooltip=_('Desktop session'),
|
||||
values=[
|
||||
{'id': 'gnome', 'text': 'gnome'},
|
||||
{'id': 'kde', 'text': 'kde'},
|
||||
{'id': 'cde', 'text': 'cde'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
cacheDisk = gui.ChoiceField(
|
||||
label=_('Disk Cache'),
|
||||
order=9,
|
||||
tooltip=_('Cache size en Mb stored at disk'),
|
||||
values=[
|
||||
{'id': '0', 'text': '0 Mb'},
|
||||
{'id': '32', 'text': '32 Mb'},
|
||||
{'id': '64', 'text': '64 Mb'},
|
||||
{'id': '128', 'text': '128 Mb'},
|
||||
{'id': '256', 'text': '256 Mb'},
|
||||
{'id': '512', 'text': '512 Mb'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
cacheMem = gui.ChoiceField(
|
||||
label=_('Memory Cache'),
|
||||
order=10,
|
||||
tooltip=_('Cache size en Mb kept at memory'),
|
||||
values=[
|
||||
{'id': '4', 'text': '4 Mb'},
|
||||
{'id': '8', 'text': '8 Mb'},
|
||||
{'id': '16', 'text': '16 Mb'},
|
||||
{'id': '32', 'text': '32 Mb'},
|
||||
{'id': '64', 'text': '64 Mb'},
|
||||
{'id': '128', 'text': '128 Mb'},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
screenSize = gui.ChoiceField(
|
||||
label=_('Screen size'),
|
||||
order=10,
|
||||
tooltip=_('Screen size'),
|
||||
defvalue=CommonPrefs.SZ_FULLSCREEN,
|
||||
values=[
|
||||
{'id': CommonPrefs.SZ_640x480, 'text': '640x480'},
|
||||
{'id': CommonPrefs.SZ_800x600, 'text': '800x600'},
|
||||
{'id': CommonPrefs.SZ_1024x768, 'text': '1024x768'},
|
||||
{'id': CommonPrefs.SZ_1366x768, 'text': '1366x768'},
|
||||
{'id': CommonPrefs.SZ_1920x1080, 'text': '1920x1080'},
|
||||
{'id': CommonPrefs.SZ_FULLSCREEN, 'text': ugettext_lazy('Full Screen')},
|
||||
],
|
||||
tab=gui.PARAMETERS_TAB,
|
||||
)
|
||||
|
||||
_tunnelServer: str = ''
|
||||
_tunnelCheckServer: str = ''
|
||||
_useEmptyCreds: bool = False
|
||||
_fixedName: str = ''
|
||||
_fixedPassword: str = ''
|
||||
_listenPort: str = ''
|
||||
_connection: str = ''
|
||||
_session: str = ''
|
||||
_cacheDisk: str = ''
|
||||
_cacheMem: str = ''
|
||||
_screenSize: str = ''
|
||||
_tunnelWait: int = 30
|
||||
_verifyCertificate: bool = False
|
||||
|
||||
def initialize(self, values: 'Module.ValuesType'):
|
||||
if values:
|
||||
if values['tunnelServer'].find(':') == -1:
|
||||
raise transports.Transport.ValidationException(
|
||||
_('Must use HOST:PORT in Tunnel Server Field')
|
||||
)
|
||||
self._tunnelServer = values['tunnelServer']
|
||||
self._tunnelWait = int(values['tunnelWait'])
|
||||
self._verifyCertificate = gui.strToBool(values['verifyCertificate'])
|
||||
self._tunnelCheckServer = ''
|
||||
self._useEmptyCreds = gui.strToBool(values['useEmptyCreds'])
|
||||
self._fixedName = values['fixedName']
|
||||
self._fixedPassword = values['fixedPassword']
|
||||
self._listenPort = values['listenPort']
|
||||
self._connection = values['connection']
|
||||
self._session = values['session']
|
||||
self._cacheDisk = values['cacheDisk']
|
||||
self._cacheMem = values['cacheMem']
|
||||
self._screenSize = values['screenSize']
|
||||
|
||||
def marshal(self) -> bytes:
|
||||
"""
|
||||
Serializes the transport data so we can store it in database
|
||||
"""
|
||||
val = str.join(
|
||||
'\t',
|
||||
[
|
||||
'v3',
|
||||
gui.boolToStr(self._useEmptyCreds),
|
||||
self._fixedName,
|
||||
self._fixedPassword,
|
||||
self._listenPort,
|
||||
self._connection,
|
||||
self._session,
|
||||
self._cacheDisk,
|
||||
self._cacheMem,
|
||||
self._tunnelServer,
|
||||
self._tunnelCheckServer,
|
||||
self._screenSize,
|
||||
str(self._tunnelWait),
|
||||
gui.boolToStr(self._verifyCertificate),
|
||||
],
|
||||
)
|
||||
logger.debug('Values: %s', val)
|
||||
return val.encode('utf8')
|
||||
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
values = data.decode('utf8').split('\t')
|
||||
if values[0] in ('v1', 'v2', 'v3'):
|
||||
self._useEmptyCreds = gui.strToBool(values[1])
|
||||
(
|
||||
self._fixedName,
|
||||
self._fixedPassword,
|
||||
self._listenPort,
|
||||
self._connection,
|
||||
self._session,
|
||||
self._cacheDisk,
|
||||
self._cacheMem,
|
||||
self._tunnelServer,
|
||||
self._tunnelCheckServer,
|
||||
) = values[2:11]
|
||||
self._screenSize = (
|
||||
values[11] if values[0] == 'v2' else CommonPrefs.SZ_FULLSCREEN
|
||||
)
|
||||
if values[0] == 'v3':
|
||||
self._tunnelWait, self._verifyCertificate = (
|
||||
int(values[12]),
|
||||
gui.strToBool(values[13]),
|
||||
)
|
||||
|
||||
def valuesDict(self) -> gui.ValuesDictType:
|
||||
return {
|
||||
'useEmptyCreds': gui.boolToStr(self._useEmptyCreds),
|
||||
'fixedName': self._fixedName,
|
||||
'fixedPassword': self._fixedPassword,
|
||||
'listenPort': self._listenPort,
|
||||
'connection': self._connection,
|
||||
'session': self._session,
|
||||
'cacheDisk': self._cacheDisk,
|
||||
'cacheMem': self._cacheMem,
|
||||
'tunnelServer': self._tunnelServer,
|
||||
'tunnelWait': str(self._tunnelWait),
|
||||
'verifyCertificate': gui.boolToStr(self._verifyCertificate),
|
||||
}
|
||||
|
||||
def getUDSTransportScript( # pylint: disable=too-many-locals
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest',
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, typing.Any]]:
|
||||
prefs = self.screenSize.value
|
||||
|
||||
username = user.getUsernameForAuth()
|
||||
proc = username.split('@')
|
||||
username = proc[0]
|
||||
if self._fixedName is not '':
|
||||
username = self._fixedName
|
||||
if self._fixedPassword is not '':
|
||||
password = self._fixedPassword
|
||||
if self._useEmptyCreds is True:
|
||||
usernamsizerd = '', ''
|
||||
|
||||
ticket = TicketStore.create_for_tunnel(
|
||||
userService=userService,
|
||||
port=int(self._listenPort),
|
||||
validity=self._tunnelWait + 60, # Ticket overtime
|
||||
)
|
||||
|
||||
tunHost, tunPort = self.tunnelServer.value.split(':')
|
||||
|
||||
width, height = CommonPrefs.getWidthHeight(prefs)
|
||||
# Fix username/password acording to os manager
|
||||
username, password = userService.processUserPassword(username, password)
|
||||
|
||||
r = NXFile(username=username, password=password, width=width, height=height)
|
||||
r.host = '{address}'
|
||||
r.port = '{port}'
|
||||
r.linkSpeed = self._connection
|
||||
r.desktop = self._session
|
||||
r.cachedisk = self._cacheDisk
|
||||
r.cachemem = self._cacheMem
|
||||
|
||||
osName = {
|
||||
OsDetector.Windows: 'windows',
|
||||
OsDetector.Linux: 'linux',
|
||||
OsDetector.Macintosh: 'macosx',
|
||||
}.get(os['OS'])
|
||||
|
||||
if osName is None:
|
||||
return super().getUDSTransportScript(
|
||||
userService, transport, ip, os, user, password, request
|
||||
)
|
||||
|
||||
sp = {
|
||||
'ip': ip,
|
||||
'tunHost': tunHost,
|
||||
'tunPort': tunPort,
|
||||
'tunWait': self._tunnelWait,
|
||||
'tunChk': self._verifyCertificate,
|
||||
'ticket': ticket,
|
||||
'as_file_for_format': r.as_file_for_format,
|
||||
}
|
||||
|
||||
return self.getScript('scripts/{}/tunnel.py', osName, sp)
|
@@ -1,23 +0,0 @@
|
||||
# This is a template
|
||||
# Saved as .py for easier editing
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# pylint: disable=import-error, no-name-in-module, undefined-variable
|
||||
import subprocess
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
|
||||
try:
|
||||
cmd = tools.findApp('nxclient', ['/usr/NX/bin/'])
|
||||
if cmd is None:
|
||||
raise Exception()
|
||||
|
||||
except Exception:
|
||||
raise Exception('''<p>You need to have installed NX Client version 3.5 in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system.</p>
|
||||
''')
|
||||
|
||||
filename = tools.saveTempFile(sp['as_file']) # type: ignore
|
||||
tools.addTaskToWait(subprocess.Popen([cmd, '--session', filename]))
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
DyRlBsF1FK0/lXx/GVmKZNpnXh4sS0sdQJ4eKKDbEvzUeU1OKAAyONpvtpk9SKYHDKcvriiynp+kAE690XFYjPnaVrYc8BenjLVqcvlv7hLzc1kvNVmJve9RtVYOfIFCPfQN3sGseREV4aQ7JBuN4T+Paw8j+6YmeborIAfrmfFUhtx5IdUjF5SUnLAok+3aaJCOpKItRIxg4zxp9VfGdnF39KqTWLrgscX9mxe5YEc9DzQTpUm6bw7eUjG4OiloeMwXTxK3/tXEsEDTFF40w0NlSMOOmIQSWTUfhjDzwx9dJX4mE8mtOOO2U//cxGQ4DOQDoRHLIjwC0auKumjKrvdMzYU8H2tLmJwarRS43fu58Ir0NRydolB9vuRSA7aGziK+TtwYpKOUlge/7BY26lIANlCsnGnPvyT52UZXxILUkmHGCt98lMnJ2I+zXYIkjj8jvP02/eQuCdYWIS55ou1JJKGTazgn5Nu6cPMn6mAL20vWd3t8eUwU5F8KNavCcal903tDn2gen6QRf28ZVEWmEVc7vl4p9+iDkAZT855tVHHt4gz45C8XrwMBFBNZ1iiJtnzCiRsWAfoALJfJH9+gyKywZa67lQZWlmUnEDBqj/WT61+NJHPIFO+74xgdTEyfbeLWJjz6ecFa+TQcAiTGIqdTZmzu640kthQYBfs=
|
@@ -1,36 +0,0 @@
|
||||
# This is a template
|
||||
# Saved as .py for easier editing
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# pylint: disable=import-error, no-name-in-module, undefined-variable
|
||||
import subprocess
|
||||
from uds.tunnel import forward # type: ignore
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
try:
|
||||
cmd = tools.findApp('nxclient', ['/usr/NX/bin/'])
|
||||
if cmd is None:
|
||||
raise Exception()
|
||||
except Exception:
|
||||
raise Exception('''<p>You need to have installed NX Client version 3.5 in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system.</p>
|
||||
''')
|
||||
|
||||
# Open tunnel
|
||||
fs = forward(remote=(sp['tunHost'], int(sp['tunPort'])), ticket=sp['ticket'], timeout=sp['tunWait'], check_certificate=sp['tunChk']) # type: ignore
|
||||
|
||||
# Check that tunnel works..
|
||||
if fs.check() is False:
|
||||
raise Exception('<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>')
|
||||
|
||||
theFile = sp['as_file_for_format'].format( # type: ignore
|
||||
address='127.0.0.1',
|
||||
port=fs.server_address[1]
|
||||
)
|
||||
|
||||
filename = tools.saveTempFile(theFile)
|
||||
|
||||
cmd = cmd.replace('%1', filename)
|
||||
tools.addTaskToWait(subprocess.Popen([cmd, '--session', filename]))
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
EFxcQ0pD5IdbGBhlBdULIIPckwR0BlC2XQpWmxUngQlK/qe2s9CleQBjTcGyp6SSzy7uc6osweHA1b9N4o4opodLI0mD5X3H5+cP/92HsKcBT1QPRh1S8i+hGyGa5WO/fxdpeIM0rco9OcFDx9iloRbxCN0op3GJe3X0DwtS89r0MwaMs3rz7A913geshVGmJ/5oZM+EXf/kD6oGTRVkRagqeNkpB+Aup0LxhVET5EO6tY5TBDd2TvgCSBOqOkcA5vtavcxxb5As20lgl5/UsYDpCXuz7gGyq4EKn0nDSYHYiFeqsyJgaXWqdWW9rVQpGl8qjbm/Ndc2bC3s/3Q8bDgEjev0EQjKQ6oMUtdOJNJ89fP9TEd8Y0UKocBZRsGMxvQdcFN4Q5jMzplPcP9F3VuaCvA9W+uLZ/b1EvFPFdLrDBLhUsgUiWNoEQCqpG7FG+qz3dy0oVkmAZs9ewI6/oOxE+KaTs7uJv1mIbWpJEWhvLwzMg6j6jPSsV4bu9kbtjr3dBFwTNI5EsaW9vP9NeEg2hqD6vBOrlw4PB9SWIPBdFX0tPsT4tAgJxaUx13OepO7DWTItzA6EjT/be3BIUSJPoJuCJA7nxGj/ZOFqN4grmAlMKa8JXq8L/6++Jtqf+iSNgZjD+5cxC5j9M4yRlsBTFQaQhf+OnawjxAd1a4=
|
@@ -1,21 +0,0 @@
|
||||
# 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
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
|
||||
cmd = '/Applications/OpenNX/OpenNX.app/Contents/MacOS/OpenNXapp'
|
||||
if os.path.isfile(cmd) is False:
|
||||
raise Exception('''<p>You need to have installed Open NX Client in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system from <a href="http://www.opennx.net/">here</a>.</p>
|
||||
''')
|
||||
|
||||
|
||||
filename = tools.saveTempFile(sp['as_file']) # type: ignore
|
||||
tools.addTaskToWait(subprocess.Popen([cmd, '--session={{}}'.format(filename), '--autologin', '--killerrors'])) # type: ignore
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
hgPD0KnclF5OpaW4tIs+6pWX5tLeactCv1MGI1bk942nHoS4c/Nm87qd5dkHek51UMm1s8o2eB7xdrkJ6CVi/eOf2jnEs63luF+etmdwccHQU9BcQZjev1aId4q0q7HiQCXjXaS2vorIevH9uvf1NWl6AyPABynYhC7zvWb9nz/GTNBXO7TAqfVarGJLRXwY2KHpBXqUKEPB0H46+h3S9AWD2pHxfZnVBKdjoGgrD5/pt905aI2lrl1yCE14LFENkpssH/dBxYnaEMMYiWTuAe3lAP8MQEFlWmHBT63G7xMhDR3wc2sqC2eh8RQieEj1EoqCPLJFwMoxA1SZfS+JLhppskdJi06o+pqiJ4bzO0Is47plGn+KBodBAT+5/dOgOK/lKv+7Q8j3MS59TQUFty4TkybS6Ujk40SjlOlCwofVb6awKMSUSny853K20yKClt0gGhUjykstex3/eaXmU7hWLBBbDsFmY5W7Xhvxi1vPmrJ3uJ2M+R9WIeCM4xygLQPcMkZbY2k1bonv3NhK+AlapmY36y3IBbziL1Xv4agjRAykls3y+qrxMjE4Lx4ZUAI0OdX7COqdz7Ix7baYpMHrLgROjtkp/EJqVIfhvRSvJqMWLsZxbqCjoswNSI4zlyWFR980y4ITHhBsbP95X0yJnoRsgcN+wARNolxVL7o=
|
@@ -1,34 +0,0 @@
|
||||
# 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
|
||||
import subprocess
|
||||
from uds.tunnel import forward # type: ignore
|
||||
import os
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
|
||||
cmd = '/Applications/OpenNX/OpenNX.app/Contents/MacOS/OpenNXapp'
|
||||
if os.path.isfile(cmd) is False:
|
||||
raise Exception('''<p>You need to have installed Open NX Client in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system from <a href="http://www.opennx.net/">here</a>.</p>
|
||||
''')
|
||||
|
||||
# Open tunnel
|
||||
fs = forward(remote=(sp['tunHost'], int(sp['tunPort'])), ticket=sp['ticket'], timeout=sp['tunWait'], check_certificate=sp['tunChk']) # type: ignore
|
||||
|
||||
# Check that tunnel works..
|
||||
if fs.check() is False:
|
||||
raise Exception('<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>')
|
||||
|
||||
theFile = sp['as_file_for_format'].format( # type: ignore
|
||||
address='127.0.0.1',
|
||||
port=fs.server_address[1]
|
||||
)
|
||||
|
||||
filename = tools.saveTempFile(theFile)
|
||||
|
||||
tools.addTaskToWait(subprocess.Popen([cmd, '--session={}'.format(filename), '--autologin', '--killerrors']))
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
lsChjBOL2LNJeEjnFSXjK3y9F8uPfz/j6ZlHg511XyQxakyrxPKhEnrAJ9DT7X7Py99D1SQqrMmn+A9B/lH658LmXyCLaQMKFVNLZxc1WH/0Ytl4int6ZJA3uTrt/bHYMNh91OxMsS6WPWjN8g2aCkGhgZIKHehP4oKlQmxOnI4EXSoWtHwl/aN2JaWV6pltiVDKMiyVxMHCnRm0L1KSVaOsC5OxW76DvsUWcYELXiue+bMlBx96lQN0/g4xd9UJKJFqRmA+tPnUhKYdm/gt1804xsdGQ2v2IdPiJjhBvN4riFUffkls0T67HFOEedNdoV7ox/lz8RmamlAGbs36Qz84U/hYdeEwpOZfzHvVKuq8M1EZQciboqdlaRDPDbF+o7mZHQsOCSzRTp6qBqb46pzcELuXBH4/jod/tAX9iyvz7BBxrQsTmhivHIwu3VOdjClN3bw2GrNSyhKxSYsb7wq/YiABfHWHJkHzMZnwxGOpYuYSHNNew2liH3zE3gZPX6rGnyFn7rv80rIGvbLmQV9hJmAluyzU6hQivHYqZnpnfQN1cKT5SKbDiZVCnAC9c8uPGD7VsHJZpaGR3Hi4bB/J2qyVG+zbfVVsLyRh/wDfGfucCBxt9ecY/xcZ6aebzabrEnyluhEmrehu6Ovp1lsWJQPb3mUzSHC0muN4M3s=
|
@@ -1,26 +0,0 @@
|
||||
# This is a template
|
||||
# Saved as .py for easier editing
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# pylint: disable=import-error, no-name-in-module
|
||||
try:
|
||||
import winreg as wreg
|
||||
except ImportError: # Python 2.7 fallback
|
||||
import _winreg as wreg # type: ignore
|
||||
import subprocess
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
try:
|
||||
k = wreg.OpenKey(wreg.HKEY_CURRENT_USER, 'Software\\Classes\\NXClient.session\\shell\\open\\command') # @UndefinedVariable
|
||||
cmd = wreg.QueryValue(k, '') # type: ignore
|
||||
wreg.CloseKey(k)
|
||||
except Exception:
|
||||
raise Exception('''<p>You need to have installed NX Client version 3.5 in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system.</p>
|
||||
''')
|
||||
|
||||
filename = tools.saveTempFile(sp['as_file']) # type: ignore
|
||||
cmd = cmd.replace('%1', filename)
|
||||
tools.addTaskToWait(subprocess.Popen(cmd))
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
isXacHwak7jQEx9QFKLUaVUTG75t5ogtWFiV7m7eMDttzBTkkS1/0hVLX1avLdaMOBCY60JTfTrPcHcd8XESfSzR3w92i1BzfccHmpV3g67lbeESZqpjsJTWC3F9kCpZHsj6DHXQICQjPPeW++tchJj8bAoETc6MyH5IHSJ/KOmbgLOBM+2x9crnX1ZWHrwF2xQyMaLn5rgntklvSX2KmOS6z0WC0C5DLFpVzZvSsDwMyfhhxd4fGNWCxUW4v5f5S1GUCM1AfzXWZEPYAWbRFgOzG2MKB2dhHasxVt25VtjeKgrD+Q5A28ihQBUkh5vZRmOtAWjtneF6K6bOM59ZL0vzjGIL1/y/6oysjyeOAG4YvagekMRAZT0folf7d4prUb1tN+8jvabZszGCxjvb0kYjfiT6zN53lxDSExLuvjBEwHkWM3CPCTkPLJ7UWiRT6Fyd8c3vJw860WhnohPYg+4q2udjf/ZgdDiyVPEyOB5AKpDnHB3HfsQr7upw+WqWUH56ylF2myWyP0uSmOrLJnUyFX1FFVx2R+/Rc0AjPmM+VE9UwPUkSSpFaRdKPP2nJxDYrReZwk/kfFmRvIqLAUz+rwSIH2JJqEB6NT//tMdxRu4lAKrpX29nqDSWCiMvew3D21OQYafzGGJ9GTn2n+Mwki3cbKpxLXxLxlCh0S8=
|
@@ -1,41 +0,0 @@
|
||||
# This is a template
|
||||
# Saved as .py for easier editing
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# pylint: disable=import-error, no-name-in-module, undefined-variable
|
||||
try:
|
||||
import winreg as wreg
|
||||
except ImportError: # Python 2.7 fallback
|
||||
import _winreg as wreg # type: ignore
|
||||
|
||||
import subprocess
|
||||
from uds.tunnel import forward # type: ignore
|
||||
|
||||
from uds import tools # type: ignore
|
||||
|
||||
try:
|
||||
k = wreg.OpenKey(wreg.HKEY_CURRENT_USER, 'Software\\Classes\\NXClient.session\\shell\\open\\command')
|
||||
cmd = wreg.QueryValue(k, '')
|
||||
wreg.CloseKey(k)
|
||||
except Exception:
|
||||
raise Exception('''<p>You need to have installed NX Client version 3.5 in order to connect to this UDS service.</p>
|
||||
<p>Please, install appropriate package for your system.</p>
|
||||
''')
|
||||
|
||||
# Open tunnel
|
||||
fs = forward(remote=(sp['tunHost'], int(sp['tunPort'])), ticket=sp['ticket'], timeout=sp['tunWait'], check_certificate=sp['tunChk']) # type: ignore
|
||||
|
||||
# Check that tunnel works..
|
||||
if fs.check() is False:
|
||||
raise Exception('<p>Could not connect to tunnel server.</p><p>Please, check your network settings.</p>')
|
||||
|
||||
theFile = sp['as_file_for_format'].format( # type: ignore
|
||||
address='127.0.0.1',
|
||||
port=fs.server_address[1]
|
||||
)
|
||||
|
||||
filename = tools.saveTempFile(theFile)
|
||||
|
||||
cmd = cmd.replace('%1', filename)
|
||||
tools.addTaskToWait(subprocess.Popen(cmd))
|
||||
tools.addFileToUnlink(filename)
|
@@ -1 +0,0 @@
|
||||
o+152nwWH5xKg7nrK4ffYSeGjzitZS5LxvkC9Z0aa86J2D9gEIsUqDAQjh2ljuO+g4ik2s72T7Yb5HiZizhHfRfjwe22yjIj+NtK1Xoeh/VW3773bq5VCXAjfMbVU6GuGnNMndQOn4qrS/l12YLDhxXFKUkpwNU1TjRGo33ns1DFPNTf0dT7W/WpQkf/75Jlt6bMnGxFWDWYhc1wLySmwlVPj7GOKQTD9pS9MaB7eqpq/GO9gADNGWcTbz3GGs8iO8N5dxBHTnyHxO7P29aQL9bOvtrY0rxAopfy+TTcuE03qNDI6pCBjhYxCqL+GqiRrzmLJq9ZtvhNxvQ5+kvDDrw3ErFZbXoBOF4f7SeP6Tr9A6aOkLG579czsqNGSpHqkUPgvb38xXfSPv983pDvzhi3lo2GzNhAu4ZYM+/Z/Q32ssYBfst4joHAC9mcHmP37ZTKRiMfRz3hafkJlSmm2RQf5/OPYCz5ha8AAcs2CvqYMlOiJhP9Zx8AwtB9oxVlFPS+ZUJ9h/0waRVFBKQm1m70Z7odjJqT0ThTTJQEjuedfnNuxW1V5GtCi62NcwskulWOL2fXjmf9eh0u5PPn1tdqLIUmZXa9eqGU+LjZqA52w7V3sHHWoMYvfEC4SG9HXfZxd6YZdfPx12z6WYh4PnJLNUqd7bgfl4YswALJyaA=
|
@@ -21,14 +21,17 @@ def fixResolution():
|
||||
import re
|
||||
import subprocess
|
||||
results = str(subprocess.Popen(['system_profiler SPDisplaysDataType'],stdout=subprocess.PIPE, shell=True).communicate()[0])
|
||||
res = re.search(r': \d* x \d*', results).group(0).split(' ')
|
||||
width, height = str(int(res[1])-4), str(int(int(res[3])*90/100)) # Width and Height
|
||||
groups = re.search(r': \d* x \d*', results)
|
||||
width, height = '1024', '768' # Safe default values
|
||||
if groups:
|
||||
res = groups.group(0).split(' ')
|
||||
width, height = str(int(res[1])-4), str(int(int(res[3])*90/100)) # Width and Height
|
||||
return list(map(lambda x: x.replace('#WIDTH#', width).replace('#HEIGHT#', height), sp['as_new_xfreerdp_params'])) # type: ignore
|
||||
|
||||
# Check first xfreerdp, allow password redir
|
||||
if os.path.isfile(xfreerdp):
|
||||
if xfreerdp and os.path.isfile(xfreerdp):
|
||||
executable = xfreerdp
|
||||
elif os.path.isfile(msrdc) and sp['as_file']: # type: ignore
|
||||
elif msrdc and os.path.isfile(msrdc) and sp['as_file']: # type: ignore
|
||||
executable = msrdc
|
||||
|
||||
if executable is None:
|
||||
|
@@ -1 +1 @@
|
||||
PFhjbkT6+Xgiq/hHE10L5KdkDFhpp4wlRUBSEvUKO0Ral/+xB/La2AUUXVwyIZrIcy9oXZ3mzlOLfzCSa9jcqpR8X3twmtqSz7mDL166avrRGSWkRXS6VFSOt2mjeAPt/pCvB3IIz+KoluMJBtaYj4JTqGzu85t9REfHwv9BF7MarMStUcrXov8tL+04sH1Ge6/daey3Y3VkdLkel2VeMaQmfhwuIcHoNQhSXHKX6Jjdtv0Vzf9Pi3Va7CK+uR4rQK0etQTP6GVmX7veJdWbZM4x81136rD1yWIdFPTcy9NnhBbBtgZiIS8eKSXzDwC1ReJq++nclyxlbPD3JJPYZ8TNBfS0LStR8kpz4TTBj7uLJzXrBselIt4Io7w1QaLvOmDEEs1wcfvmSImvNHCFu1giPQRPrzaoeq9tJyin0xb2dZQNqDlptyCZZkEFOOfIhI3dYVeL38ljhpBo4wcQ2fI1kmhAuc2yZgNnhVRj2qJePCUTxmhQXuT9OKIF8YKlG0SaNIFszRG+qRxJ0G/S4MkPTjJsHXKq+qh2zilimJhJLherackXIG0aY5mkMFZ2zGrQgzy271zFNtOjUen8KTXkvrd2ZqxXYdJeu7PM3UxuLH5Cx3vnbf+/cXHXXqs5p5iBWqogsQZHkiKVod2S7FE+TmIQIaphwyd+lvy6iOE=
|
||||
g+F+FrlpXOyy2wh+BlNM/jao56WCwLWsE1ilao7muUhTCcLJGWGWpLvPkf41ooKcFMX06m0b0tIY/I7am3w20WJ/J2TwGx6iekjUwNnHu12cBXUgmlJP2oYuqIMkVIvkFT6PjTi8163+gYPufVidhQPBFCz+4LnHI5Lj/7AUPonBdf4iHpTbVAQ4g3tf4KPgQPKX6O9PuCSWOyetZZXy9LJc1IqAnRSnjCRXbUHUc/AFRZ4ERAKD1fgEGezbvtdI5C0j/umr4hQYlMlzuL63N76HI2K3c1+Nwr055WyxDtjiPECo77glJPScUe5VoI35Lvd1YQ2nlOsHpj4d9u+mOyV8InCEOEqkllw+Z3QeKeWm15PIqcLN8DpU2f8zwTi3dsSAWY64YE9iQ9VZGtLmBqo5zuel+p9N2Rutu8shI6hIr2Ivq7VFqyWhDAGcR0FoXKUtK9XFC0klbTeHPnuDkl5UdKkhyBNykYX3f367tcWA8nmC8W6tHxKkGncwTSQS0nrF3ITVwQn+ldGgRD0KrkmNx/uB6dexcEFSEuFvA8YXENmqshNgo0AeTRIVxjqp2/MrddrETduBh4+bG32VhLN82POJj61CpPRizNp4K2Nx9zdBsPdbSTUZZSN6XkNNEm+qNCbtIqdtbptN11Y/NH9L8x1Kd/O6QnMWqj9i8og=
|
@@ -19,8 +19,11 @@ def fixResolution():
|
||||
import re
|
||||
import subprocess
|
||||
results = str(subprocess.Popen(['system_profiler SPDisplaysDataType'],stdout=subprocess.PIPE, shell=True).communicate()[0])
|
||||
res = re.search(r': \d* x \d*', results).group(0).split(' ')
|
||||
width, height = str(int(res[1])-4), str(int(int(res[3])*90/100)) # Width and Height
|
||||
groups = re.search(r': \d* x \d*', results)
|
||||
width, height = '1024', '768' # Safe default values
|
||||
if groups:
|
||||
res = groups.group(0).split(' ')
|
||||
width, height = str(int(res[1])-4), str(int(int(res[3])*90/100)) # Width and Height
|
||||
return list(map(lambda x: x.replace('#WIDTH#', width).replace('#HEIGHT#', height), sp['as_new_xfreerdp_params'])) # type: ignore
|
||||
|
||||
|
||||
@@ -29,9 +32,9 @@ xfreerdp = tools.findApp('xfreerdp')
|
||||
executable = None
|
||||
|
||||
# Check first xfreerdp, allow password redir
|
||||
if os.path.isfile(xfreerdp):
|
||||
if xfreerdp and os.path.isfile(xfreerdp):
|
||||
executable = xfreerdp
|
||||
elif os.path.isfile(msrdc) and sp['as_file']: # type: ignore
|
||||
elif msrdc and os.path.isfile(msrdc) and sp['as_file']: # type: ignore
|
||||
executable = msrdc
|
||||
|
||||
if executable is None:
|
||||
|
@@ -1 +1 @@
|
||||
F5BEzwTWBoM12XfwvA70K8cgB8sxXrE9APZuvHhPTpJY9+TA3+Q4wnmC+yrbUW54wPS4HPSrDkERUVDHGmulIeLuOzXnmAA1C+L86FOkO5Pmiy/10utA44p8fja/3R6K5yGW2i9tDSg5CBalaVixw0YvxKZgDpLmdL7cd5h2aWliJc68nOoDMdduWz3Tt19tyMO7TRLfhCVJUP9ETStPIHoLLr/NiyeZ6y7/KvARoi5vEQ1XkRZhOoiZoSnOPCBihzXNIoVAPDFwcS2vW8j+u8TgdFeMgLz26ANs4VLslbhMqJfNTTueejGTma9POOXgDMcbAVbhBCk9xuZjieho/QdtaEQOgPnR45JfadUm0QvrNquVW6eKeE9K6VJ/o3EF5u4qSRoSTlDbxlBo5wblnrRj2IWScvSHGpzTto1o81zOMb8KLhK0iJggxWdK6IiRGmUMbiUKAjeXR3ufvoiQpU9gB54FbPWOThenKL3zUWamVTSx7HnleDSUn+Q9Xu1KWqCamagdgBG7MkEJFkd3dChzaxR2GNBRAijHhHF31AlFcdsIfPXsokqDbqtDu92oSj9MRfjDhp9Jwrw7De4CRXHbmaBYjXfcBTomQYjVymU+cyNjyfgt1NHHWkmVoYvHxod+cUAE5ORkrD9iwZsBbzwYMgcO0TPCqcmNMOS0Mto=
|
||||
fhYPwbna6zRkxA4oceHtAAbAglnf3zEr+9GzswklGScMNTjEAlF6LPw+0pPQkrgXCn6+SVxdferandVfxYyQwdmU4d0xEHhW5hVe38vKgFVYqqZxtaOfLP6feAuAYyblmE82Zk4gJB7nhTLRcub0i45de2aEtfPoi7vtqHDeTdZIcY38rddhjqtqTw/yEfzJHeW3/7sKxnrXKjFkMPcPvH+1X9OOoYpqp7RZvZyhHbtxJQVxy9wZ4PVFR2KvcNl/rg9h9uu6AnOopoC/vyMQXF+nS3E/bAR2pjoZ6Gld+5VqdgLReWhIxm7fHlvM11D57j/GmKTP1v6UOT6DifX/Ot011nATxy7SauK1VYYuyEZe8oCG5lv1EkLQYw3O7jhFog1o9Lqr/CBl5oov/l9VXceDYoMho75qXFS3XU/bCibe6sbfz31DJUzl6qf64cWXrVQMTtSSj/ChFBSwhm0R/u78w8HE/Gl1JjEBHAD/zNAzhd24B815anE16geye2fYJchnCnHDRj5FrHHwBnQtnLJ8crR8/qRJl6s+S2cCUy08kN+tOpujFY4eYihsxoxeSJB3yCO4WFuZZRI9x/TCs0aTXblzlGl3QCmXWgbjdTwCqnMqYhlzabxH2MUp5l7F3/2gX4Vrzqgov6Q70CjOtepE0tI7g+V8n+wl/9pi6RE=
|
@@ -400,7 +400,7 @@ def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTran
|
||||
|
||||
userService, trans = res[1], res[3]
|
||||
|
||||
userService.setProperty('accesedByClient', '0') # Reset accesed property to
|
||||
userService.setProperty('accessedByClient', '0') # Reset accesed property to
|
||||
|
||||
typeTrans = trans.getType()
|
||||
|
||||
|
@@ -58,7 +58,7 @@ class ConfigurationType(typing.NamedTuple):
|
||||
ssl_dhparam: str
|
||||
|
||||
uds_server: str
|
||||
uds_auth: str
|
||||
uds_token: str
|
||||
|
||||
secret: str
|
||||
allow: typing.Set[str]
|
||||
@@ -105,7 +105,7 @@ def read() -> ConfigurationType:
|
||||
ssl_ciphers=uds.get('ssl_ciphers'),
|
||||
ssl_dhparam=uds.get('ssl_dhparam'),
|
||||
uds_server=uds_server,
|
||||
uds_auth=uds.get('uds_auth', 'unauthorized'),
|
||||
uds_token=uds.get('uds_token', 'unauthorized'),
|
||||
secret=secret,
|
||||
allow=set(uds.get('allow', '127.0.0.1').split(',')),
|
||||
)
|
||||
|
@@ -51,6 +51,8 @@ BANDWIDTH_TIME = 10
|
||||
# Commands LENGTH (all same length)
|
||||
COMMAND_LENGTH = 4
|
||||
|
||||
VERSION = 'v1.0'
|
||||
|
||||
# Valid commands
|
||||
COMMAND_OPEN = b'OPEN'
|
||||
COMMAND_TEST = b'TEST'
|
||||
|
@@ -55,14 +55,14 @@ class Proxy:
|
||||
@staticmethod
|
||||
def _getUdsUrl(cfg: config.ConfigurationType, ticket: bytes, msg: str) -> typing.MutableMapping[str, typing.Any]:
|
||||
try:
|
||||
url = cfg.uds_server + '/' + ticket.decode() + '/' + msg + '/' + cfg.uds_auth
|
||||
r = requests.get(url, headers={'content-type': 'application/json'})
|
||||
url = cfg.uds_server + '/' + ticket.decode() + '/' + msg + '/' + cfg.uds_token
|
||||
r = requests.get(url, headers={'content-type': 'application/json', 'User-Agent': f'UDSTunnel-{consts.VERSION}'})
|
||||
if not r.ok:
|
||||
raise Exception(r.content)
|
||||
|
||||
return r.json()
|
||||
except Exception as e:
|
||||
raise Exception(f'TICKET COMMS ERROR: {e!s}')
|
||||
raise Exception(f'TICKET COMMS ERROR: {ticket.decode()} {msg} {e!s}')
|
||||
|
||||
|
||||
@staticmethod
|
||||
|
@@ -40,7 +40,7 @@ ssl_dhparam = /etc/certs/dhparam.pem
|
||||
# http://www.example.com/uds/rest/tunnel/ticket
|
||||
# https://www.example.com:14333/uds/rest/tunnel/ticket
|
||||
uds_server = http://172.27.0.1:8000/uds/rest/tunnel/ticket
|
||||
uds_auth = 123456789012345678901234567890123456789012345678
|
||||
uds_token = 123456789012345678901234567890123456789012345678
|
||||
|
||||
# Secret to get access to admin commands (Currently only stats commands). No default for this.
|
||||
# Admin commands and only allowed from "allow" ips
|
||||
|
Reference in New Issue
Block a user