1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-11-05 16:24:21 +03:00

Reafactorization and fixes

Refactorized some exceptions names
Fixed Spice Protocol
Added "getConsoleConnection" as optional UserDeployment method
This commit is contained in:
Adolfo Gómez García
2023-02-14 15:09:20 +01:00
parent 2961469244
commit 6165d2db15
31 changed files with 114 additions and 78 deletions

View File

@@ -194,7 +194,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
)
)
raise RequestError(_('Element already exists (duplicate key error)'))
except exceptions.ValidationException as e:
except exceptions.ValidationError as e:
if (
not item and service
): # Only remove partially saved element if creating new (if editing, ignore this)

View File

@@ -1148,7 +1148,7 @@ class ModelHandler(BaseModelHandler):
raise exceptions.NotFound('Item not found')
except IntegrityError: # Duplicate key probably
raise exceptions.RequestError('Element already exists (duplicate key error)')
except (exceptions.SaveException, g_exceptions.ValidationException) as e:
except (exceptions.SaveException, g_exceptions.ValidationError) as e:
raise exceptions.RequestError(str(e))
except (exceptions.RequestError, exceptions.ResponseError):
raise

View File

@@ -250,7 +250,7 @@ class RegexLdap(auths.Authenticator):
try:
re.search(pattern, '')
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
'Invalid pattern in {0}: {1}'.format(fieldLabel, line)
)

View File

@@ -324,7 +324,7 @@ class SAMLAuthenticator(auths.Authenticator):
return
if ' ' in values['name']:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext(
'This kind of Authenticator does not support white spaces on field NAME'
)
@@ -338,7 +338,7 @@ class SAMLAuthenticator(auths.Authenticator):
self.serverCertificate.value.startswith('-----BEGIN CERTIFICATE-----\n')
is False
):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext(
'Server certificate should be a valid PEM (PEM certificates starts with -----BEGIN CERTIFICATE-----)'
)
@@ -347,7 +347,7 @@ class SAMLAuthenticator(auths.Authenticator):
try:
cryptoManager().loadCertificate(self.serverCertificate.value)
except Exception as e:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('Invalid server certificate. ') + str(e)
)
@@ -357,7 +357,7 @@ class SAMLAuthenticator(auths.Authenticator):
and self.privateKey.value.startswith('-----BEGIN PRIVATE KEY-----\n')
is False
):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext(
'Private key should be a valid PEM (PEM private keys starts with -----BEGIN RSA PRIVATE KEY-----'
)
@@ -366,7 +366,7 @@ class SAMLAuthenticator(auths.Authenticator):
try:
pk = cryptoManager().loadPrivateKey(self.privateKey.value)
except Exception as e:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('Invalid private key. ') + str(e)
)
@@ -385,7 +385,7 @@ class SAMLAuthenticator(auths.Authenticator):
resp = requests.get(idpMetadata.split('\n')[0], verify=self.checkSSLCertificate.isTrue())
idpMetadata = resp.content.decode()
except Exception as e:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('Can\'t fetch url {0}: {1}').format(
self.idpMetadata.value, str(e)
)
@@ -398,7 +398,7 @@ class SAMLAuthenticator(auths.Authenticator):
xml.sax.parseString(idpMetadata, xml.sax.ContentHandler()) # type: ignore # nosec: url provided by admin
except Exception as e:
msg = (gettext(' (obtained from URL)') if fromUrl else '') + str(e)
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('XML does not seem valid for IDP Metadata ') + msg
)
@@ -531,7 +531,7 @@ class SAMLAuthenticator(auths.Authenticator):
try:
re.search(pattern, '')
except:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
'Invalid pattern at {0}: {1}'.format(field.label, line)
)

View File

@@ -131,7 +131,7 @@ class SampleAuth(auths.Authenticator):
# unserialization, and at this point all will be default values
# so self.groups.value will be []
if values and len(self.groups.value) < 2:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('We need more than two groups!')
)

View File

@@ -39,9 +39,14 @@ class UDSException(Exception):
"""
pass
class ValidationException(UDSException):
class ValidationError(UDSException):
"""
Exception used to indicate that the params assigned are invalid
"""
pass
class TransportError(UDSException):
"""
Exception used to indicate that the transport is not available
"""
pass

View File

@@ -608,6 +608,29 @@ class UserDeployment(Environmentable, Serializable):
"""
return None
def getConsoleConnection(
self,
) -> typing.Optional[typing.MutableMapping[str, typing.Any]]:
"""
This method is invoked by any connection that needs special connection data
to connenct to it using, for example, SPICE protocol. (that currently is the only one)
Returns a dictionary with the data needed to connect to the console.
Required (as SPICE protocol):
type: type of connection ('spice', 'vnc', ...)
address: address to connect to
port: port to connect to
secure_port: secure port to connect to
cert_subject: certificate subject to use
ticket: ticket to use to connect (basically, this is the password)
Optional:
ca: certificate authority to use
proxy: proxy to use
monitors: number of monitors to use
"""
return None
def __str__(self):
"""
Mainly used for debugging purposses

View File

@@ -574,12 +574,12 @@ class gui:
elif pattern == gui.TextField.PatternTypes.HOST:
try:
validators.validateHostname(self.value, allowDomain=True)
except exceptions.ValidationException:
except exceptions.ValidationError:
validators.validateIpv4OrIpv6(self.value)
elif pattern == gui.TextField.PatternTypes.PATH:
validators.validatePath(self.value)
return True
except exceptions.ValidationException:
except exceptions.ValidationError:
return False
elif isinstance(self._data['pattern'], str):
# It's a regex

View File

@@ -64,7 +64,7 @@ def validateNumeric(
try:
numeric = int(value)
if minValue is not None and numeric < minValue:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_(
'{0} must be greater than or equal to {1}'.format(
fieldName, minValue
@@ -73,14 +73,14 @@ def validateNumeric(
)
if maxValue is not None and numeric > maxValue:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{0} must be lower than or equal to {1}'.format(fieldName, maxValue))
)
value = str(numeric)
except ValueError:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{0} contains invalid characters').format(fieldName)
)
@@ -89,20 +89,20 @@ def validateNumeric(
def validateHostname(hostname: str, maxLength: int = 64, allowDomain=False) -> str:
if len(hostname) > maxLength:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} exceeds maximum host name length.').format(hostname)
)
if not allowDomain:
if '.' in hostname:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid hostname').format(hostname)
)
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
if not all(allowed.match(x) for x in hostname.split(".")):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid hostname').format(hostname)
)
@@ -115,14 +115,14 @@ def validateFqdn(fqdn: str, maxLength: int = 255) -> str:
def validateUrl(url: str, maxLength: int = 1024) -> str:
if len(url) > maxLength:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} exceeds maximum url length.').format(url)
)
try:
url_validator(url)
except Exception as e:
raise exceptions.ValidationException(str(e))
raise exceptions.ValidationError(str(e))
return url
@@ -137,7 +137,7 @@ def validateIpv4(ipv4: str) -> str:
try:
dj_validators.validate_ipv4_address(ipv4)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid IPv4 address').format(ipv4)
)
return ipv4
@@ -153,7 +153,7 @@ def validateIpv6(ipv6: str) -> str:
try:
dj_validators.validate_ipv6_address(ipv6)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid IPv6 address').format(ipv6)
)
return ipv6
@@ -169,7 +169,7 @@ def validateIpv4OrIpv6(ipv4OrIpv6: str) -> str:
try:
dj_validators.validate_ipv46_address(ipv4OrIpv6)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid IPv4 or IPv6 address').format(ipv4OrIpv6)
)
return ipv4OrIpv6
@@ -197,7 +197,7 @@ def validatePath(
str: path
"""
if len(path) > maxLength:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} exceeds maximum path length.').format(path)
)
@@ -206,17 +206,17 @@ def validatePath(
if mustBeWindows:
if not valid_for_windows.match(path):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid windows path').format(path)
)
elif mustBeUnix:
if not valid_for_unix.match(path):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid unix path').format(path)
)
else:
if not valid_for_windows.match(path) and not valid_for_unix.match(path):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid path').format(path)
)
@@ -244,7 +244,7 @@ def validateHostPortPair(hostPortPair: str) -> typing.Tuple[str, int]:
host, port = hostPortPair.split(':')
return validateHostname(host, 255, False), validatePort(port)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid host:port pair').format(hostPortPair)
)
@@ -273,7 +273,7 @@ def validateMac(mac: str) -> str:
) # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
if macRE.match(mac) is None:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid MAC address').format(mac)
)
@@ -291,7 +291,7 @@ def validateMacRange(macRange: str) -> str:
validateMac(macRangeStart)
validateMac(macRangeEnd)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('{} is not a valid MAC range').format(macRange)
)
@@ -305,10 +305,10 @@ def validateEmail(email: str) -> str:
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
if len(email) > 254:
raise exceptions.ValidationException(_('Email address is too long'))
raise exceptions.ValidationError(_('Email address is too long'))
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
raise exceptions.ValidationException(_('Email address is not valid'))
raise exceptions.ValidationError(_('Email address is not valid'))
return email
@@ -328,22 +328,22 @@ def validateBasename(baseName: str, length: int = -1) -> str:
None -- [description]
"""
if re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', baseName) is None:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The basename is not a valid for a hostname')
)
if length == 0:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The length of basename plus length must be greater than 0')
)
if length != -1 and len(baseName) + length > 15:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The length of basename plus length must not be greater than 15')
)
if baseName.isdigit():
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The machine name can\'t be only numbers')
)

View File

@@ -165,7 +165,7 @@ class EmailMFA(mfas.MFA):
# if hostname is not valid, we will raise an exception
hostname = self.hostname.cleanStr()
if not hostname:
raise exceptions.ValidationException(_('Invalid SMTP hostname'))
raise exceptions.ValidationError(_('Invalid SMTP hostname'))
# Now check is valid format
if ':' in hostname:

View File

@@ -147,7 +147,7 @@ class EmailNotifier(messaging.Notifier):
# if hostname is not valid, we will raise an exception
hostname = self.hostname.cleanStr()
if not hostname:
raise exceptions.ValidationException(_('Invalid SMTP hostname'))
raise exceptions.ValidationError(_('Invalid SMTP hostname'))
# Now check is valid format
if ':' in hostname:

View File

@@ -77,7 +77,7 @@ class LinuxRandomPassManager(LinuxOsManager):
super(LinuxRandomPassManager, self).__init__(environment, values)
if values is not None:
if values['userAccount'] == '':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Must provide an user account!!!')
)
self._userAccount = values['userAccount']

View File

@@ -95,11 +95,11 @@ class WindowsOsManager(osmanagers.OSManager):
try:
length = int(length)
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Length must be numeric!!')
)
if length > 6 or length < 1:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Length must be betwen 1 and 6')
)
return length

View File

@@ -146,19 +146,19 @@ class WinDomainOsManager(WindowsOsManager):
super().__init__(environment, values)
if values:
if values['domain'] == '':
raise exceptions.ValidationException(_('Must provide a domain!'))
raise exceptions.ValidationError(_('Must provide a domain!'))
# if values['domain'].find('.') == -1:
# raise exceptions.ValidationException(_('Must provide domain in FQDN'))
if values['account'] == '':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Must provide an account to add machines to domain!')
)
if values['account'].find('\\') != -1:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('DOM\\USER form is not allowed!')
)
if values['password'] == '':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Must provide a password for the account!')
)
self._domain = values['domain']

View File

@@ -88,11 +88,11 @@ class WinRandomPassManager(WindowsOsManager):
super().__init__(environment, values)
if values:
if values['userAccount'] == '':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Must provide an user account!!!')
)
if values['password'] == '':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Must provide a password for the account!!!')
)
self._userAccount = values['userAccount']

View File

@@ -37,8 +37,8 @@ import ovirtsdk4 as ovirt
# Sometimes, we import ovirtsdk4 but "types" does not get imported... event can't be found????
# With this seems to work propertly
try:
from ovirtsdk4 import types as ovirtTypes # pylint: disable=unused-import
except Exception:
from ovirtsdk4 import types as ovirtTypes
except Exception: # nosec just to bring on the types if they exist
pass
# Not imported at runtime, just for type checking
@@ -103,7 +103,7 @@ class Client:
if Client.cached_api:
try:
Client.cached_api.close()
except Exception:
except Exception: # nosec: this is a "best effort" close
# Nothing happens, may it was already disconnected
pass
try:

View File

@@ -227,7 +227,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
if values:
validators.validateBasename(self.baseName.value, self.lenName.num())
if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The minimum allowed memory is 256 Mb')
)
if int(self.memoryGuaranteed.value) > int(self.memory.value):

View File

@@ -83,13 +83,13 @@ class PhysicalMachinesProvider(services.ServiceProvider):
config.read_string(self.config.value)
# Seems a valid configuration file, let's see if all se
except Exception as e:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Invalid advanced configuration: ') + str(e)
)
for section in config.sections():
if section not in VALID_CONFIG_SECTIONS:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Invalid section in advanced configuration: ') + section
)
@@ -99,12 +99,12 @@ class PhysicalMachinesProvider(services.ServiceProvider):
try:
net.networksFromString(key) # Raises exception if net is invalid
except Exception:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Invalid network in advanced configuration: ') + key
)
# Now check value is an url
if config['wol'][key][:4] != 'http':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Invalid url in advanced configuration: ') + key
)

View File

@@ -151,7 +151,7 @@ class IPMachinesService(IPServiceBase):
# Check that ips are valid
for v in values['ipList']:
if not net.isValidHost(v.split(';')[0]): # Get only IP/hostname
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('Invalid value detected on servers list: "{}"').format(
v
)

View File

@@ -83,7 +83,7 @@ class IPSingleMachineService(IPServiceBase):
return
if not net.isValidHost(self.ip.value):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
gettext('Invalid server used: "{}"'.format(self.ip.value))
)

View File

@@ -30,7 +30,7 @@
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
import pickle
import pickle # nosec: controled data
import logging
import typing
@@ -144,7 +144,7 @@ class ProxmoxDeployment(services.UserDeployment):
self._task = vals[4].decode('utf8')
self._vmid = vals[5].decode('utf8')
self._reason = vals[6].decode('utf8')
self._queue = pickle.loads(vals[7])
self._queue = pickle.loads(vals[7]) # nosec: controled data
def getName(self) -> str:
if self._name == '':

View File

@@ -181,7 +181,7 @@ class Provider(services.ServiceProvider):
# values are only passed from administration client. Internals
# instantiations are always empty.
if values and self.methAlive.isTrue():
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('Methuselah is not alive!!! :-)')
)
@@ -222,7 +222,7 @@ class Provider(services.ServiceProvider):
instance.methAge.value,
instance.methAlive.value,
)
except exceptions.ValidationException as e:
except exceptions.ValidationError as e:
# If we say that meth is alive, instantiation will
return [False, str(e)]
except Exception as e:

View File

@@ -167,7 +167,7 @@ class ServiceOne(services.Service):
# so we only need to validate params if values is not None
if values:
if self.colour.value == 'nonsense':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
'The selected colour is invalid!!!'
)

View File

@@ -192,7 +192,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
validators.validateBasename(self.baseName.value, self.lenName.num())
if int(self.memory.value) < 256:
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The minimum allowed memory is 256 Mb')
)

View File

@@ -319,7 +319,7 @@ class HTML5RDPTransport(transports.Transport):
# Strip spaces and all trailing '/'
self.guacamoleServer.value = self.guacamoleServer.value.strip().rstrip('/')
if self.guacamoleServer.value[0:4] != 'http':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The server must be http or https')
)
#if self.useEmptyCreds.isTrue() and self.security.value != 'rdp':

View File

@@ -180,7 +180,7 @@ class HTML5VNCTransport(transports.Transport):
# Remove trailing / (one or more) from url if it exists from "guacamoleServer" field
self.guacamoleServer.value = self.guacamoleServer.value.strip().rstrip('/')
if self.guacamoleServer.value[0:4] != 'http':
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The server must be http or https')
)

View File

@@ -34,7 +34,10 @@ import logging
import typing
from django.utils.translation import gettext_noop as _
from uds.core.util import os_detector as OsDetector
from uds.core import exceptions
from .spice_base import BaseSpiceTransport
from .remote_viewer_file import RemoteViewerFile
@@ -81,13 +84,15 @@ class SPICETransport(BaseSpiceTransport):
request: 'ExtendedHttpRequestWithUser',
) -> 'transports.TransportScript':
try:
userServiceInstance: typing.Any = userService.getInstance()
con: typing.Dict[str, typing.Any] = userServiceInstance.getConsoleConnection()
userServiceInstance = userService.getInstance()
con: typing.Optional[typing.MutableMapping[str, typing.Any]] = userServiceInstance.getConsoleConnection()
except Exception:
logger.exception('Error getting console connection data')
raise
logger.debug('Connection data: %s', con)
if not con:
raise exceptions.TransportError('No console connection data')
port: str = con['port'] or '-1'
secure_port: str = con['secure_port'] or '-1'

View File

@@ -144,9 +144,7 @@ class BaseSpiceTransport(transports.Transport):
"""
ready = self.cache.get(ip)
if ready is None:
userServiceInstance: typing.Any = (
userService.getInstance()
) # Disable mypy checks on this
userServiceInstance = userService.getInstance()
con = userServiceInstance.getConsoleConnection()
logger.debug('Connection data: %s', con)

View File

@@ -35,7 +35,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core.ui import gui
from uds.core import transports
from uds.core import transports, exceptions
from uds.core.util import os_detector as OsDetector
from uds.core.util import validators
from uds.models import TicketStore
@@ -120,12 +120,17 @@ class TSPICETransport(BaseSpiceTransport):
request: 'ExtendedHttpRequestWithUser',
) -> transports.TransportScript:
try:
userServiceInstance: typing.Any = userService.getInstance()
con: typing.Dict[str, typing.Any] = userServiceInstance.getConsoleConnection()
userServiceInstance = userService.getInstance()
con = userServiceInstance.getConsoleConnection()
except Exception:
logger.exception('Error getting console connection data')
raise
if not con:
raise exceptions.TransportError(
_('No console connection data received'),
)
tunHost, tunPort = self.tunnelServer.value.split(':')
# We MAY need two tickets, one for 'insecure' port an one for secure

View File

@@ -92,7 +92,7 @@ class TestTransport(transports.Transport):
self.testURL.value.startswith('http://')
or self.testURL.value.startswith('https://')
):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The url must be http or https')
)

View File

@@ -92,7 +92,7 @@ class URLCustomTransport(transports.Transport):
self.urlPattern.value.startswith('http://')
or self.urlPattern.value.startswith('https://')
):
raise exceptions.ValidationException(
raise exceptions.ValidationError(
_('The url must be http or https')
)