1
0
mirror of https://github.com/dkmstr/openuds.git synced 2024-12-23 17:34:17 +03:00

Refactoring UDS Exceptions

This commit is contained in:
Adolfo Gómez García 2023-02-13 16:35:35 +01:00
parent 4385fdf358
commit da823d2e6c
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
46 changed files with 598 additions and 243 deletions

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023 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.U. 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
"""

View File

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2022 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.U. 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
"""
import typing
import logging
from uds.REST.handlers import AUTH_TOKEN_HEADER
from uds.REST.methods.actor_v3 import MANAGED, UNMANAGED, ALLOWED_FAILS
from uds import models
logger = logging.getLogger(__name__)
from ...utils import rest
from ...fixtures import rest as rest_fixtures
class ServicePoolTest(rest.test.RESTTestCase):
def setUp(self) -> None:
# Override number of items to create
super().setUp()
self.login()
def test_invalid_servicepool(self) -> None:
url = f'servicespools/INVALID/overview'
response = self.client.rest_get(url)
self.assertEqual(response.status_code, 404)
def test_service_pools(self) -> None:
url = f'servicespools/overview'
# Now, will work
response = self.client.rest_get(url)
self.assertEqual(response.status_code, 200)
# Get the list of service pools from DB
db_pools_len = models.ServicePool.objects.all().count()
re_pools: typing.List[typing.Dict[str, typing.Any]] = response.json()
self.assertIsInstance(re_pools, list)
self.assertEqual(db_pools_len, len(re_pools))
for service_pool in re_pools:
# Get from DB the service pool
db_pool = models.ServicePool.objects.get(uuid=service_pool['id'])
self.assertTrue(rest.assertions.assertServicePoolIs(db_pool, service_pool))

View File

@ -32,6 +32,7 @@ import typing
from ..utils import rest
# User REST structure
class UserRestStruct(rest.RestStruct):
id: rest.uuid_type
@ -45,6 +46,7 @@ class UserRestStruct(rest.RestStruct):
mfa_data: typing.Optional[str]
password: typing.Optional[str]
# Group REST structure
class GroupRestStruct(rest.RestStruct):
id: rest.uuid_type
@ -55,9 +57,45 @@ class GroupRestStruct(rest.RestStruct):
is_meta: bool
meta_if_any: bool
# ServicePool REST structure
class ServicePoolRestStruct(rest.RestStruct):
id: rest.uuid_type
name: str
short_name: str
tags: typing.List[str]
parent: str
parent_type: str
comments: str
state: str
thumb: str
account: str
account_id: rest.uuid_type
service_id: rest.uuid_type
provider_id: rest.uuid_type
image_id: rest.uuid_type
initial_srvs: int
cache_l1_srvs: int
cache_l2_srvs: int
max_srvs: int
show_transports: bool
visible: bool
allow_users_remove: bool
allow_users_reset: bool
ignores_unused: bool
fallbackAccess: str
meta_member: typing.List[typing.Dict[str, rest.uuid_type]]
calendar_message: str
# Provide a "random" dictionary based on a
def createUser(**kwargs) -> typing.Dict[str, typing.Any]:
return UserRestStruct.random_create(**kwargs).as_dict()
def createGroup(**kwargs) -> typing.Dict[str, typing.Any]:
return GroupRestStruct.random_create(**kwargs).as_dict()
def createServicePool(**kwargs) -> typing.Dict[str, typing.Any]:
return ServicePoolRestStruct.random_create(**kwargs).as_dict()

View File

@ -64,7 +64,9 @@ def createProvider() -> models.Provider:
return provider
def createService(provider: models.Provider) -> models.Service:
def createService(
provider: models.Provider, useCachingVersion: bool = True
) -> models.Service:
from uds.services.Test.service import TestServiceCache, TestServiceNoCache
service = provider.services.create(
@ -72,6 +74,10 @@ def createService(provider: models.Provider) -> models.Service:
data_type=TestServiceCache.typeType,
data=TestServiceCache(
environment.Environment(str(glob['service_id'])), provider.getInstance()
).serialize()
if useCachingVersion
else TestServiceNoCache(
environment.Environment(str(glob['service_id'])), provider.getInstance()
).serialize(),
token=generators.random_string(16) + str(glob['service_id']),
)
@ -231,7 +237,6 @@ def createOneCacheTestingUserService(
groups: typing.List['models.Group'],
type_: typing.Union[typing.Literal['managed'], typing.Literal['unmanaged']],
) -> 'models.UserService':
from uds.services.Test.service import TestServiceCache, TestServiceNoCache
from uds.osmanagers.Test import TestOSManager
from uds.transports.Test import TestTransport

View File

@ -40,8 +40,12 @@ from .. import ensure_data
logger = logging.getLogger(__name__)
def assertUserIs(
user: models.User, compare_to: typing.Mapping[str, typing.Any], compare_uuid=False, compare_password=False
user: models.User,
compare_to: typing.Mapping[str, typing.Any],
compare_uuid=False,
compare_password=False,
) -> bool:
ignore_fields = ['password', 'groups', 'mfa_data', 'last_access', 'role']
@ -51,7 +55,11 @@ def assertUserIs(
# If last_access is present, compare it here, because it's a datetime object
if 'last_access' in compare_to:
if int(user.last_access.timestamp()) != compare_to['last_access']:
logger.info('User last_access do not match: %s != %s', user.last_access.timestamp(), compare_to['last_access'])
logger.info(
'User last_access do not match: %s != %s',
user.last_access.timestamp(),
compare_to['last_access'],
)
return False
if ensure_data(user, compare_to, ignore_keys=ignore_fields):
@ -61,25 +69,36 @@ def assertUserIs(
compare_to_groups = set(compare_to['groups'])
# Ensure groups are PART compare_to_groups
if groups - compare_to_groups != set():
logger.info('User groups do not match: %s != %s', groups, compare_to_groups)
logger.info(
'User groups do not match: %s != %s', groups, compare_to_groups
)
return False
# Compare mfa_data
if 'mfa_data' in compare_to:
if user.mfa_data != compare_to['mfa_data']:
logger.info('User mfa_data do not match: %s != %s', user.mfa_data, compare_to['mfa_data'])
logger.info(
'User mfa_data do not match: %s != %s',
user.mfa_data,
compare_to['mfa_data'],
)
return False
# Compare password
if compare_password:
if not cryptoManager().checkHash(compare_to['password'], user.password):
logger.info('User password do not match: %s != %s', user.password, compare_to['password'])
logger.info(
'User password do not match: %s != %s',
user.password,
compare_to['password'],
)
return False
return True
return False
def assertGroupIs(
group: models.Group, compare_to: typing.Mapping[str, typing.Any], compare_uuid=False
) -> bool:
@ -89,26 +108,68 @@ def assertGroupIs(
ignore_fields.append('id')
if ensure_data(group, compare_to, ignore_keys=ignore_fields):
if group.is_meta:
grps = set(i.uuid for i in group.groups.all())
compare_to_groups = set(compare_to['groups'])
if grps != compare_to_groups:
logger.info('Group groups do not match: %s != %s', grps, compare_to_groups)
logger.info(
'Group groups do not match: %s != %s', grps, compare_to_groups
)
return False
if 'type' in compare_to:
if group.is_meta != (compare_to['type'] == 'meta'):
logger.info('Group type do not match: %s != %s', group.is_meta, compare_to['type'])
logger.info(
'Group type do not match: %s != %s',
group.is_meta,
compare_to['type'],
)
return False
if 'pools' in compare_to:
pools = set(i.uuid for i in group.deployedServices.all())
compare_to_pools = set(compare_to['pools'])
if pools != compare_to_pools:
logger.info('Group pools do not match: %s != %s', pools, compare_to_pools)
logger.info(
'Group pools do not match: %s != %s', pools, compare_to_pools
)
return False
return True
return False
def assertServicePoolIs(
pool: models.ServicePool,
compare_to: typing.Mapping[str, typing.Any],
compare_uuid=False,
) -> bool:
ignore_fields = [
'tags',
'parent',
'parent_type',
'thumb',
'account',
'service_id',
'provider_id',
'meta_member',
'user_services_count',
'user_services_in_preparation',
'restrained',
'permission',
'info',
'pool_group_id',
'pool_group_name',
'pool_group_thumb',
'usage',
'osmanager_id',
]
if not compare_uuid:
ignore_fields.append('id')
if ensure_data(pool, compare_to, ignore_keys=ignore_fields):
return True
return False

View File

@ -35,14 +35,14 @@ import typing
logger = logging.getLogger(__name__)
# Convenience imports, must be present before initializing handlers
from .handlers import (
from .exceptions import (
AccessDenied,
Handler,
HandlerError,
NotFound,
NotSupportedError,
RequestError,
ResponseError,
)
from .handlers import Handler
from .dispatcher import Dispatcher, AUTH_TOKEN_HEADER

View File

@ -32,6 +32,7 @@
import logging
import sys
import typing
import traceback
from django import http
from django.utils.decorators import method_decorator
@ -43,15 +44,15 @@ from uds.core import VERSION, VERSION_STAMP
from uds.core.util import modfinder
from . import processors, log
from .handlers import (
from .exceptions import (
AccessDenied,
Handler,
HandlerError,
NotFound,
NotSupportedError,
RequestError,
ResponseError,
)
from .handlers import Handler
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
@ -203,7 +204,12 @@ class Dispatcher(View):
return http.HttpResponseBadRequest(str(e), content_type="text/plain")
except Exception as e:
log.log_operation(handler, 500, log.ERROR)
logger.exception('Error processing request')
# Get ecxeption backtrace
trace_back = traceback.format_exc()
logger.error('Exception processing request: %s', full_path)
for i in trace_back.splitlines():
logger.error(f'* {i}')
return http.HttpResponseServerError(str(e), content_type="text/plain")
@staticmethod

View File

@ -0,0 +1,68 @@
# -*- 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.U. nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
from uds.core.exceptions import UDSException
class HandlerError(UDSException):
"""
Generic error for a REST handler
"""
class NotFound(HandlerError):
"""
Item not found error
"""
class AccessDenied(HandlerError):
"""
Access denied error
"""
class RequestError(HandlerError):
"""
Request is invalid error
"""
class ResponseError(HandlerError):
"""
Generic response error
"""
class NotSupportedError(HandlerError):
"""
Some elements do not support some operations (as searching over an authenticator that does not supports it)
"""

View File

@ -42,7 +42,7 @@ from uds.core.util import net
from uds.models import Authenticator, User
from uds.core.managers import cryptoManager
from . import log
from .exceptions import AccessDenied
# Not imported at runtime, just for type checking
@ -54,42 +54,6 @@ logger = logging.getLogger(__name__)
AUTH_TOKEN_HEADER: typing.Final[str] = 'HTTP_X_AUTH_TOKEN' # nosec: this is not a password
class HandlerError(Exception):
"""
Generic error for a REST handler
"""
class NotFound(HandlerError):
"""
Item not found error
"""
class AccessDenied(HandlerError):
"""
Access denied error
"""
class RequestError(HandlerError):
"""
Request is invalid error
"""
class ResponseError(HandlerError):
"""
Generic response error
"""
class NotSupportedError(HandlerError):
"""
Some elements do not support some operations (as searching over an authenticator that does not supports it)
"""
class Handler:
"""
REST requests handler base class

View File

@ -68,7 +68,7 @@ def replacePath(path: str) -> str:
uuid = path.split(f'/{type}/')[1].split('/')[0]
name = model.objects.get(uuid=uuid).name # type: ignore
path = path.replace(uuid, f'[{name}]')
except Exception:
except Exception: # nosec: intentionally broad exception
pass
return path

View File

@ -36,7 +36,7 @@ import typing
from django.utils.translation import gettext_lazy as _
from uds.models import ActorToken
from uds.REST.handlers import RequestError, NotFound
from uds.REST.exceptions import RequestError, NotFound
from uds.REST.model import ModelHandler, OK
from uds.core.util import permissions

View File

@ -52,7 +52,8 @@ from uds.core.util.cache import Cache
from uds.core.util.config import GlobalConfig
from uds.models.service import ServiceTokenAlias
from ..handlers import Handler, AccessDenied, RequestError
from ..handlers import Handler
from ..exceptions import AccessDenied, RequestError
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:

View File

@ -38,7 +38,7 @@ from django.utils.translation import gettext as _
from uds import models
from uds.core import services
from uds.core import exceptions
from uds.core.util import log
from uds.core.util import permissions
from uds.core.util.model import processUuid
@ -194,7 +194,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
)
)
raise RequestError(_('Element already exists (duplicate key error)'))
except Module.ValidationException as e:
except exceptions.ValidationException as e:
if (
not item and service
): # Only remove partially saved element if creating new (if editing, ignore this)

View File

@ -145,7 +145,7 @@ class System(Handler):
help_text = 'Provides system information. Must be admin to access this'
def get(self):
def get(self) -> typing.Any:
logger.debug('args: %s', self._args)
# Only allow admin user for global stats
if len(self._args) == 1:

View File

@ -36,7 +36,7 @@ import typing
from django.utils.translation import gettext_lazy as _
from uds.models import TunnelToken
from uds.REST.handlers import RequestError, NotFound
from uds.REST.exceptions import RequestError, NotFound
from uds.REST.model import ModelHandler, OK
from uds.core.util import permissions

View File

@ -45,12 +45,11 @@ from uds.core.ui import gui as uiGui
from uds.core.util import log
from uds.core.util import permissions
from uds.core.util.model import processUuid
from uds.core import Module
from uds.core import Module, exceptions
from uds.models import Tag, TaggingMixin, ManagedObjectModel, Network
from .handlers import (
Handler,
from .exceptions import (
HandlerError,
NotFound,
RequestError,
@ -59,6 +58,8 @@ from .handlers import (
NotSupportedError,
)
from .handlers import Handler
# Not imported at runtime, just for type checking
if typing.TYPE_CHECKING:
from uds.models import User
@ -898,10 +899,13 @@ class ModelHandler(BaseModelHandler):
method = getattr(detail, self._operation)
return method()
except KeyError:
raise self.invalidMethodException()
except AttributeError:
except IndexError:
raise self.invalidItemException()
except (KeyError, AttributeError):
raise self.invalidMethodException()
except Exception as e:
logger.error('Exception processing detail: %s', e)
raise self.invalidRequestException()
def getItems(
self, *args, **kwargs
@ -940,7 +944,7 @@ class ModelHandler(BaseModelHandler):
is False
):
continue
if kwargs.get('overview', True):
if overview:
yield self.item_as_dict_overview(item)
else:
res = self.item_as_dict(item)
@ -1155,7 +1159,7 @@ class ModelHandler(BaseModelHandler):
raise NotFound('Item not found')
except IntegrityError: # Duplicate key probably
raise RequestError('Element already exists (duplicate key error)')
except (SaveException, Module.ValidationException) as e:
except (SaveException, exceptions.ValidationException) as e:
raise RequestError(str(e))
except (RequestError, ResponseError):
raise

View File

@ -39,7 +39,7 @@ import ldap
from django.utils.translation import gettext_noop as _
from uds.core import auths
from uds.core import auths, exceptions
from uds.core.ui import gui
from uds.core.util import ldaputil
from uds.core.auths.auth import authLogLogin
@ -250,7 +250,7 @@ class RegexLdap(auths.Authenticator):
try:
re.search(pattern, '')
except Exception:
raise auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
'Invalid pattern in {0}: {1}'.format(fieldLabel, line)
)

View File

@ -47,7 +47,7 @@ from django.utils.translation import gettext_noop as _, gettext
from uds.models import getSqlDatetime
from uds.core.ui import gui
from uds.core import auths
from uds.core import auths, exceptions
from uds.core.managers import cryptoManager
from uds.core.util.decorators import allowCache
@ -324,7 +324,7 @@ class SAMLAuthenticator(auths.Authenticator):
return
if ' ' in values['name']:
raise auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
gettext('XML does not seem valid for IDP Metadata ') + msg
)
@ -531,7 +531,7 @@ class SAMLAuthenticator(auths.Authenticator):
try:
re.search(pattern, '')
except:
raise auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
'Invalid pattern at {0}: {1}'.format(field.label, line)
)

View File

@ -36,7 +36,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core.auths.authenticator import AuthenticationResult, AuthenticationSuccess
from uds.core.ui import gui
from uds.core import auths
from uds.core import auths, exceptions
if typing.TYPE_CHECKING:
from django.http import (
@ -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 auths.Authenticator.ValidationException(
raise exceptions.ValidationException(
_('We need more than two groups!')
)

View File

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023 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.U. 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
"""
import typing
class UDSException(Exception):
"""
Base class for all UDS exceptions
"""
pass
class ValidationException(UDSException):
"""
Exception used to indicate that the params assigned are invalid
"""
pass

View File

@ -123,11 +123,6 @@ class Module(UserInterface, Environmentable, Serializable):
# Not defined, but declared. If module is groupable, this value will contain to which group belongs
group: typing.ClassVar[str]
class ValidationException(Exception):
"""
Exception used to indicate that the params assigned are invalid
"""
@classmethod
def name(cls: typing.Type['Module']) -> str:
"""

View File

@ -39,13 +39,14 @@ import typing
import logging
import enum
from collections import abc
import re
from django.utils.translation import get_language, gettext as _, gettext_noop
from django.conf import settings
from uds.core.managers.crypto import CryptoManager, UDSK
from uds.core.util.decorators import deprecatedClassValue
from uds.core.util import serializer
from uds.core.util import serializer, validators
logger = logging.getLogger(__name__)
@ -463,6 +464,12 @@ class gui:
"""Unserialize value from an string"""
self.value = value
def validate(self) -> bool:
"""
Validates the value of this field.
"""
return True
class TextField(InputField):
"""
This represents a text field.
@ -494,17 +501,53 @@ class gui:
tooltip = _('Other info'), rdonly = True)
"""
class PatternTypes:
IPV4 = 'ipv4'
IPV6 = 'ipv6'
MAC = 'mac'
URL = 'url'
EMAIL = 'email'
FQDN = 'fqdn'
HOSTNAME = 'hostname'
HOST = 'host'
PATH = 'path'
NONE = ''
def __init__(self, **options) -> None:
super().__init__(**options, type=gui.InputField.Types.TEXT)
multiline = int(options.get('multiline', 0))
if multiline > 8:
multiline = 8
self._data['multiline'] = multiline
self._data['multiline'] = min(max(int(options.get('multiline', 0)), 0), 8)
# Pattern to validate the value
# Can contain an regex or this special values: (empty string means no validation)
# - 'ipv4' # IPv4 address
# - 'ipv6' # IPv6 address
# - 'mac' # MAC address
# - 'url' # URL
# - 'email' # Email
# - 'fqdn' # Fully qualified domain name
# - 'hostname' # Hostname (without domain)
# - 'host' # Hostname with or without domain or IP address
# - 'path' # Path (absolute or relative, Windows or Unix)
# Note:
# Checks are performed on admin side, so they are not 100% reliable.
self._data['pattern'] = options.get('pattern', gui.TextField.PatternTypes.NONE)
def cleanStr(self):
return str(self.value).strip()
def validate(self) -> bool:
return super().validate() and self._validatePattern()
def _validatePattern(self) -> bool:
if isinstance(self._data['pattern'], gui.TextField.PatternTypes):
pattern: gui.TextField.PatternTypes = self._data['pattern']
if pattern == gui.TextField.PatternTypes.IPV4:
pass
elif isinstance(self._data['pattern'], str):
# It's a regex
return re.match(self._data['pattern'], self.value) is not None
return True # No pattern, so it's valid
class TextAutocompleteField(TextField):
"""
This represents a text field that holds autocomplete values.

View File

@ -41,7 +41,7 @@ from django.utils import formats
from django.utils.translation import gettext
import django.template.defaultfilters as filters
from uds.core import services
from uds.core import exceptions
class CaseInsensitiveDict(dict):
@ -141,41 +141,6 @@ def secondsToTimeString(seconds: int) -> str:
return gettext('{} days {:d}:{:02d}:{:02d}').format(days, hours, minutes, seconds)
def checkValidBasename(baseName: str, length: int = -1) -> None:
""" "Checks if the basename + length is valid for services. Raises an exception if not valid"
Arguments:
baseName {str} -- basename to check
Keyword Arguments:
length {int} -- length to check, if -1 do not checm (default: {-1})
Raises:
services.Service.ValidationException: If anything goes wrong
Returns:
None -- [description]
"""
if re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', baseName) is None:
raise services.Service.ValidationException(
gettext('The basename is not a valid for a hostname')
)
if length == 0:
raise services.Service.ValidationException(
gettext('The length of basename plus length must be greater than 0')
)
if length != -1 and len(baseName) + length > 15:
raise services.Service.ValidationException(
gettext('The length of basename plus length must not be greater than 15')
)
if baseName.isdigit():
raise services.Service.ValidationException(
gettext('The machine name can\'t be only numbers')
)
def removeControlCharacters(s: str) -> str:
"""
Removes control characters from an unicode string

View File

@ -35,7 +35,8 @@ import logging
import typing
from django.utils.translation import gettext as _
from uds.core.module import Module
from django.core import validators as dj_validators
from uds.core import exceptions
logger = logging.getLogger(__name__)
@ -53,7 +54,7 @@ def validateNumeric(
:param maxValue: If not None, max value that must be the numeric or exception is thrown
:param returnAsInteger: if True, returs value as integer (default), else returns as string
:param fieldName: If present, the name of the field for "Raising" exceptions, defaults to "Numeric value"
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
value = value.replace(' ', '')
fieldName = fieldName or _('Numeric')
@ -61,7 +62,7 @@ def validateNumeric(
try:
numeric = int(value)
if minValue is not None and numeric < minValue:
raise Module.ValidationException(
raise exceptions.ValidationException(
_(
'{0} must be greater than or equal to {1}'.format(
fieldName, minValue
@ -70,14 +71,14 @@ def validateNumeric(
)
if maxValue is not None and numeric > maxValue:
raise Module.ValidationException(
raise exceptions.ValidationException(
_('{0} must be lower than or equal to {1}'.format(fieldName, maxValue))
)
value = str(numeric)
except ValueError:
raise Module.ValidationException(
raise exceptions.ValidationException(
_('{0} contains invalid characters').format(fieldName)
)
@ -86,7 +87,7 @@ def validateNumeric(
def validateHostname(hostname: str, maxLength: int, asPattern: bool) -> str:
if len(hostname) > maxLength:
raise Module.ValidationException(
raise exceptions.ValidationException(
_('{} exceeds maximum host name length.').format(hostname)
)
@ -99,19 +100,33 @@ def validateHostname(hostname: str, maxLength: int, asPattern: bool) -> str:
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
if not all(allowed.match(x) for x in hostname.split(".")):
raise Module.ValidationException(
raise exceptions.ValidationException(
_('{} is not a valid hostname').format(hostname)
)
return hostname
def validateUrl(url: str, maxLength: int = 1024) -> str:
if len(url) > maxLength:
raise exceptions.ValidationException(
_('{} exceeds maximum url length.').format(url)
)
validator = dj_validators.URLValidator(['http', 'https'])
try:
validator(url)
except Exception as e:
raise exceptions.ValidationException(str(e))
return url
def validatePort(portStr: str) -> int:
"""
Validates that a port number is valid
:param portStr: port to validate, as string
:param returnAsInteger: if True, returns value as integer, if not, as string
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
return validateNumeric(portStr, minValue=0, maxValue=65535, fieldName='Port')
@ -121,13 +136,13 @@ def validateHostPortPair(hostPortPair: str) -> typing.Tuple[str, int]:
Validates that a host:port pair is valid
:param hostPortPair: host:port pair to validate
:param returnAsInteger: if True, returns value as integer, if not, as string
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
try:
host, port = hostPortPair.split(':')
return validateHostname(host, 255, False), validatePort(port)
except Exception:
raise Module.ValidationException(
raise exceptions.ValidationException(
_('{} is not a valid host:port pair').format(hostPortPair)
)
@ -137,54 +152,91 @@ def validateTimeout(timeOutStr: str) -> int:
Validates that a timeout value is valid
:param timeOutStr: timeout to validate
:param returnAsInteger: if True, returns value as integer, if not, as string
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
return validateNumeric(timeOutStr, minValue=0, fieldName='Timeout')
def validateMacRange(macRange: str) -> str:
def validateMac(mac: str) -> str:
"""
Corrects mac range (uppercase, without spaces), and checks that is range is valid
:param macRange: Range to fix
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
Validates that a mac address is valid
:param mac: mac address to validate
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
# Removes white spaces and all to uppercase
macRange = macRange.upper().replace(' ', '')
mac = mac.upper().replace(' ', '')
macRE = re.compile(
r'^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$'
) # 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(_('{} is not a valid MAC address').format(mac))
return mac
def validateMacRange(macRange: str) -> str:
"""
Corrects mac range (uppercase, without spaces), and checks that is range is valid
:param macRange: Range to fix
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
try:
macRangeStart, macRangeEnd = macRange.split('-')
if macRE.match(macRangeStart) is None or macRE.match(macRangeEnd) is None:
raise Exception()
if macRangeStart > macRangeEnd:
raise Exception()
validateMac(macRangeStart)
validateMac(macRangeEnd)
except Exception:
raise Module.ValidationException(
_(
'Invalid mac range. Mac range must be in format XX:XX:XX:XX:XX:XX-XX:XX:XX:XX:XX:XX'
)
raise exceptions.ValidationException(
_('{} is not a valid MAC range').format(macRange)
)
return macRange
def validateEmail(email: str) -> str:
"""
Validates that an email is valid
:param email: email to validate
:return: Raises Module.Validation exception if is invalid, else return the value "fixed"
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
"""
if len(email) > 254:
raise Module.ValidationException(
_('Email address is too long')
)
raise exceptions.ValidationException(_('Email address is too long'))
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
raise Module.ValidationException(
_('Email address is not valid')
)
raise exceptions.ValidationException(_('Email address is not valid'))
return email
def validateBasename(baseName: str, length: int = -1) -> None:
""" "Checks if the basename + length is valid for services. Raises an exception if not valid"
Arguments:
baseName {str} -- basename to check
Keyword Arguments:
length {int} -- length to check, if -1 do not checm (default: {-1})
Raises:
exceptions.ValidationException: If anything goes wrong
Returns:
None -- [description]
"""
if re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', baseName) is None:
raise exceptions.ValidationException(
_('The basename is not a valid for a hostname')
)
if length == 0:
raise exceptions.ValidationException(
_('The length of basename plus length must be greater than 0')
)
if length != -1 and len(baseName) + length > 15:
raise exceptions.ValidationException(
_('The length of basename plus length must not be greater than 15')
)
if baseName.isdigit():
raise exceptions.ValidationException(
_('The machine name can\'t be only numbers')
)

View File

@ -40,7 +40,7 @@ import logging
from django.utils.translation import gettext_noop as _, gettext
from uds import models
from uds.core import mfas
from uds.core import mfas, exceptions
from uds.core.ui import gui
from uds.core.util import validators, decorators
@ -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 EmailMFA.ValidationException(_('Invalid SMTP hostname'))
raise exceptions.ValidationException(_('Invalid SMTP hostname'))
# Now check is valid format
if ':' in hostname:

View File

@ -39,7 +39,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core import messaging
from uds.core import messaging, exceptions
from uds.core.ui import gui
from uds.core.util import validators
@ -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 messaging.Notifier.ValidationException(_('Invalid SMTP hostname'))
raise exceptions.ValidationException(_('Invalid SMTP hostname'))
# Now check is valid format
if ':' in hostname:

View File

@ -38,7 +38,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core.ui import gui
from uds.core import osmanagers
from uds.core import exceptions
from uds.core.util import log
from .linux_osmanager import LinuxOsManager
@ -77,7 +77,7 @@ class LinuxRandomPassManager(LinuxOsManager):
super(LinuxRandomPassManager, self).__init__(environment, values)
if values is not None:
if values['userAccount'] == '':
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Must provide an user account!!!')
)
self._userAccount = values['userAccount']

View File

@ -13,7 +13,7 @@ import logging
import typing
from django.utils.translation import gettext_noop as _, gettext_lazy
from uds.core import osmanagers
from uds.core import osmanagers, exceptions
from uds.core.services import types as serviceTypes
from uds.core.ui import gui
from uds.core.managers import userServiceManager
@ -95,11 +95,11 @@ class WindowsOsManager(osmanagers.OSManager):
try:
length = int(length)
except Exception:
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Length must be numeric!!')
)
if length > 6 or length < 1:
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Length must be betwen 1 and 6')
)
return length

View File

@ -41,7 +41,7 @@ import ldap
from django.utils.translation import gettext_noop as _
from uds.core.ui import gui
from uds.core.managers import cryptoManager
from uds.core import osmanagers
from uds.core import exceptions
from uds.core.util import log
from uds.core.util import ldaputil
@ -146,21 +146,19 @@ class WinDomainOsManager(WindowsOsManager):
super().__init__(environment, values)
if values:
if values['domain'] == '':
raise osmanagers.OSManager.ValidationException(
_('Must provide a domain!')
)
raise exceptions.ValidationException(_('Must provide a domain!'))
# if values['domain'].find('.') == -1:
# raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN'))
# raise exceptions.ValidationException(_('Must provide domain in FQDN'))
if values['account'] == '':
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Must provide an account to add machines to domain!')
)
if values['account'].find('\\') != -1:
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('DOM\\USER form is not allowed!')
)
if values['password'] == '':
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Must provide a password for the account!')
)
self._domain = values['domain']
@ -172,12 +170,12 @@ class WinDomainOsManager(WindowsOsManager):
self._ssl = 'y' if values['ssl'] else 'n'
self._removeOnExit = 'y' if values['removeOnExit'] else 'n'
else:
self._domain = ""
self._ou = ""
self._account = ""
self._password = ""
self._group = ""
self._serverHint = ""
self._domain = ''
self._ou = ''
self._account = ''
self._password = '' # nosec: no encoded password
self._group = ''
self._serverHint = ''
self._removeOnExit = 'n'
self._ssl = 'n'
@ -191,10 +189,15 @@ class WinDomainOsManager(WindowsOsManager):
if self._serverHint != '':
yield (self._serverHint, 389)
server: typing.Any
def key(server: typing.Any) -> int:
return server.priority * 10000 + server.weight
for server in reversed(
sorted(
dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'),
key=lambda i: i.priority * 10000 + i.weight,
iter(dns.resolver.resolve('_ldap._tcp.' + self._domain, 'SRV')),
key=key,
)
):
yield (str(server.target)[:-1], server.port)

View File

@ -40,7 +40,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core.ui import gui
from uds.core.managers import cryptoManager
from uds.core import osmanagers
from uds.core import exceptions
from uds.core.util import log
from .windows import WindowsOsManager
@ -88,11 +88,11 @@ class WinRandomPassManager(WindowsOsManager):
super().__init__(environment, values)
if values:
if values['userAccount'] == '':
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Must provide an user account!!!')
)
if values['password'] == '':
raise osmanagers.OSManager.ValidationException(
raise exceptions.ValidationException(
_('Must provide a password for the account!!!')
)
self._userAccount = values['userAccount']

View File

@ -537,7 +537,7 @@ class OVirtProvider(
# instance = Provider(env, data)
# logger.debug('Methuselah has {0} years and is {1} :-)'
# .format(instance.methAge.value, instance.methAlive.value))
# except ServiceProvider.ValidationException as e:
# except exceptions.ValidationException as e:
# # If we say that meth is alive, instantiation will
# return [False, str(e)]
# except Exception as e:

View File

@ -37,8 +37,8 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core.transports import protocols
from uds.core.services import Service, types as serviceTypes
from uds.core.util import tools
from uds.core import services, exceptions
from uds.core.util import validators
from uds.core.ui import gui
from .publication import OVirtPublication
@ -53,7 +53,7 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__)
class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-methods
"""
oVirt Linked clones service. This is based on creating a template from selected vm, and then use it to
"""
@ -105,7 +105,7 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
deployedType = OVirtLinkedDeployment
allowedProtocols = protocols.GENERIC + (protocols.SPICE,)
servicesTypeProvided = (serviceTypes.VDI,)
servicesTypeProvided = (services.types.VDI,)
# Now the form part
cluster = gui.ChoiceField(
@ -225,9 +225,9 @@ class OVirtLinkedService(Service): # pylint: disable=too-many-public-methods
initialized by __init__ method of base class, before invoking this.
"""
if values:
tools.checkValidBasename(self.baseName.value, self.lenName.num())
validators.validateBasename(self.baseName.value, self.lenName.num())
if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256:
raise Service.ValidationException(
raise exceptions.ValidationException(
_('The minimum allowed memory is 256 Mb')
)
if int(self.memoryGuaranteed.value) > int(self.memory.value):

View File

@ -38,7 +38,7 @@ from django.utils.translation import gettext_noop as _
import dns.resolver
from uds.core import services
from uds.core import services, exceptions
from uds.core.ui.user_interface import gui
from uds.core.util import net
from uds.core.util import log
@ -70,7 +70,7 @@ class PhysicalMachinesProvider(services.ServiceProvider):
values (Module.ValuesType): List of values on initialization (maybe None)
Raises:
services.ServiceProvider.ValidationException
exceptions.ValidationException
"""
if values is None:
return
@ -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 services.ServiceProvider.ValidationException(
raise exceptions.ValidationException(
_('Invalid advanced configuration: ') + str(e)
)
for section in config.sections():
if section not in VALID_CONFIG_SECTIONS:
raise services.ServiceProvider.ValidationException(
raise exceptions.ValidationException(
_('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 services.ServiceProvider.ValidationException(
raise exceptions.ValidationException(
_('Invalid network in advanced configuration: ') + key
)
# Now check value is an url
if config['wol'][key][:4] != 'http':
raise services.ServiceProvider.ValidationException(
raise exceptions.ValidationException(
_('Invalid url in advanced configuration: ') + key
)
@ -136,7 +136,7 @@ class PhysicalMachinesProvider(services.ServiceProvider):
# Try to resolve name...
try:
# Prefer ipv4
res = dns.resolver.resolve(ip)
res: typing.Any = dns.resolver.resolve(ip)
ip = res[0].address
except Exception:
# Try ipv6

View File

@ -41,7 +41,7 @@ from uds.models import getSqlDatetimeAsUnix
from uds.core.ui import gui
from uds.core.util import log
from uds.core.util import net
from uds.core import services
from uds.core import services, exceptions
from .deployment import IPMachineDeployed
from .service_base import IPServiceBase
@ -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 IPServiceBase.ValidationException(
raise exceptions.ValidationException(
gettext('Invalid value detected on servers list: "{}"').format(
v
)

View File

@ -37,7 +37,7 @@ from django.utils.translation import gettext_lazy as _, gettext
from uds.core.ui import gui
from uds.core.util import net
from uds.core.services import types as serviceTypes
from uds.core import services, exceptions
from .deployment import IPMachineDeployed
from .service_base import IPServiceBase
@ -76,14 +76,14 @@ class IPSingleMachineService(IPServiceBase):
deployedType = IPMachineDeployed
servicesTypeProvided = (serviceTypes.VDI,)
servicesTypeProvided = (services.types.VDI,)
def initialize(self, values: 'Module.ValuesType') -> None:
if values is None:
return
if not net.isValidHost(self.ip.value):
raise IPServiceBase.ValidationException(
raise exceptions.ValidationException(
gettext('Invalid server used: "{}"'.format(self.ip.value))
)

View File

@ -336,7 +336,7 @@ class ProxmoxProvider(
# instance = Provider(env, data)
# logger.debug('Methuselah has {0} years and is {1} :-)'
# .format(instance.methAge.value, instance.methAlive.value))
# except ServiceProvider.ValidationException as e:
# except exceptions.ValidationException as e:
# # If we say that meth is alive, instantiation will
# return [False, str(e)]
# except Exception as e:

View File

@ -186,7 +186,7 @@ class ProxmoxLinkedService(Service): # pylint: disable=too-many-public-methods
self.baseName.value, 15, asPattern=True
)
# if int(self.memory.value) < 128:
# raise Service.ValidationException(_('The minimum allowed memory is 128 Mb'))
# raise exceptions.ValidationException(_('The minimum allowed memory is 128 Mb'))
def initGui(self) -> None:
# Here we have to use "default values", cause values aren't used at form initialization

View File

@ -37,7 +37,7 @@ import typing
from django.utils.translation import gettext_noop as _
from uds.core import services
from uds.core import services, exceptions
from uds.core.ui import gui
from .service import ServiceOne, ServiceTwo
@ -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 services.ServiceProvider.ValidationException(
raise exceptions.ValidationException(
_('Methuselah is not alive!!! :-)')
)
@ -222,7 +222,7 @@ class Provider(services.ServiceProvider):
instance.methAge.value,
instance.methAlive.value,
)
except services.ServiceProvider.ValidationException as e:
except exceptions.ValidationException as e:
# If we say that meth is alive, instantiation will
return [False, str(e)]
except Exception as e:

View File

@ -34,7 +34,7 @@ import logging
import typing
from django.utils.translation import gettext_noop as _
from uds.core import services
from uds.core import services, exceptions
from uds.core.ui import gui
from .publication import SamplePublication
@ -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 services.Service.ValidationException(
raise exceptions.ValidationException(
'The selected colour is invalid!!!'
)

View File

@ -482,7 +482,7 @@ class XenProvider(ServiceProvider): # pylint: disable=too-many-public-methods
# instance = Provider(env, data)
# logger.debug('Methuselah has {0} years and is {1} :-)'
# .format(instance.methAge.value, instance.methAlive.value))
# except ServiceProvider.ValidationException as e:
# except exceptions.ValidationException as e:
# # If we say that meth is alive, instantiation will
# return [False, str(e)]
# except Exception as e:

View File

@ -33,8 +33,8 @@ import logging
import typing
from django.utils.translation import gettext_noop as _
from uds.core.services import Service, types as serviceTypes
from uds.core.util import tools
from uds.core import services, exceptions
from uds.core.util import validators
from uds.core.ui import gui
from .publication import XenPublication
@ -48,7 +48,7 @@ if typing.TYPE_CHECKING:
logger = logging.getLogger(__name__)
class XenLinkedService(Service): # pylint: disable=too-many-public-methods
class XenLinkedService(services.Service): # pylint: disable=too-many-public-methods
"""
Xen Linked clones service. This is based on creating a template from selected vm, and then use it to
@ -101,7 +101,7 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
# : Types of deploys (services in cache and/or assigned to users)
deployedType = XenLinkedDeployment
servicesTypeProvided = (serviceTypes.VDI,)
servicesTypeProvided = (services.types.VDI,)
# Now the form part
datastore = gui.ChoiceField(
@ -189,10 +189,10 @@ class XenLinkedService(Service): # pylint: disable=too-many-public-methods
initialized by __init__ method of base class, before invoking this.
"""
if values:
tools.checkValidBasename(self.baseName.value, self.lenName.num())
validators.validateBasename(self.baseName.value, self.lenName.num())
if int(self.memory.value) < 256:
raise Service.ValidationException(
raise exceptions.ValidationException(
_('The minimum allowed memory is 256 Mb')
)

View File

@ -37,7 +37,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.managers import cryptoManager
from uds import models
@ -319,11 +319,11 @@ 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 transports.Transport.ValidationException(
raise exceptions.ValidationException(
_('The server must be http or https')
)
#if self.useEmptyCreds.isTrue() and self.security.value != 'rdp':
# raise transports.Transport.ValidationException(
# raise exceptions.ValidationException(
# _(
# 'Empty credentials (on Credentials tab) is only allowed with Security level (on Parameters tab) set to "RDP"'
# )

View File

@ -34,12 +34,9 @@ import logging
import typing
from django.utils.translation import gettext_noop as _
from django.http import HttpResponseRedirect
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.managers import cryptoManager
from uds import models
@ -183,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 transports.Transport.ValidationException(
raise exceptions.ValidationException(
_('The server must be http or https')
)

View File

@ -30,7 +30,6 @@
"""
@author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import os
import logging
import typing
@ -122,6 +121,15 @@ class BaseSpiceTransport(transports.Transport):
tab=gui.Tab.ADVANCED,
)
overridedProxy = gui.TextField(
order=10,
label=_('Proxy'),
tooltip=_('If not empty, this proxy will be used to connect to the service'),
required=False,
tab=gui.Tab.ADVANCED,
pattern=''
)
def isAvailableFor(self, userService: 'models.UserService', ip: str) -> bool:
"""
Checks if the transport is available for the requested destination ip

View File

@ -36,11 +36,8 @@ 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.managers import cryptoManager
from uds import models
# Not imported at runtime, just for type checking
@ -95,7 +92,7 @@ class TestTransport(transports.Transport):
self.testURL.value.startswith('http://')
or self.testURL.value.startswith('https://')
):
raise transports.Transport.ValidationException(
raise exceptions.ValidationException(
_('The url must be http or https')
)

View File

@ -36,9 +36,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 import models
@ -94,7 +92,7 @@ class URLCustomTransport(transports.Transport):
self.urlPattern.value.startswith('http://')
or self.urlPattern.value.startswith('https://')
):
raise transports.Transport.ValidationException(
raise exceptions.ValidationException(
_('The url must be http or https')
)