diff --git a/server/src/uds/REST/methods/client.py b/server/src/uds/REST/methods/client.py index 82b1577c..c9487fd1 100644 --- a/server/src/uds/REST/methods/client.py +++ b/server/src/uds/REST/methods/client.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - # -# Copyright (c) 2014-2019 Virtual Cable S.L. +# Copyright (c) 2014-2020 Virtual Cable S.L.U. # All rights reserved. # # Redistribution and use in source and binary forms, with or without modification, @@ -26,11 +25,11 @@ # 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 json +import codecs import logging import typing @@ -46,7 +45,6 @@ from uds.core.managers import cryptoManager, userServiceManager from uds.core.util.config import GlobalConfig from uds.core.services.exceptions import ServiceNotReadyError from uds.core import VERSION as UDS_VERSION -from uds.core.util import encoders logger = logging.getLogger(__name__) @@ -147,7 +145,8 @@ class Client(Handler): password = cryptoManager().symDecrpyt(data['password'], scrambler) # userService.setConnectionSource(srcIp, hostname) # Store where we are accessing from so we can notify Service - + if not ip: + raise ServiceNotReadyError transportScript, signature, params = transportInstance.getEncodedTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request) logger.debug('Signature: %s', signature) @@ -156,7 +155,7 @@ class Client(Handler): return Client.result(result={ 'script': transportScript, 'signature': signature, # It is already on base64 - 'params': encoders.encode(encoders.encode(json.dumps(params), 'bz2'), 'base64', asText=True), + 'params': codecs.encode(codecs.encode(json.dumps(params), 'bz2'), 'base64').decode(), }) except ServiceNotReadyError as e: # Refresh ticket and make this retrayable diff --git a/server/src/uds/REST/methods/system.py b/server/src/uds/REST/methods/system.py index f6bdfce7..553f9590 100644 --- a/server/src/uds/REST/methods/system.py +++ b/server/src/uds/REST/methods/system.py @@ -31,8 +31,9 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com """ import pickle -import logging import datetime +import codecs +import logging import typing from uds import models @@ -40,7 +41,6 @@ from uds import models from uds.core.util.stats import counters from uds.core.util.cache import Cache from uds.core.util.state import State -from uds.core.util import encoders from uds.REST import Handler, RequestError, ResponseError logger = logging.getLogger(__name__) @@ -53,10 +53,16 @@ SINCE = 365 # Days USE_MAX = True -def getServicesPoolsCounters(servicePool: typing.Optional[models.ServicePool], counter_type: int) -> typing.List[typing.Dict[str, typing.Any]]: - # pylint: disable=no-value-for-parameter +def getServicesPoolsCounters( + servicePool: typing.Optional[models.ServicePool], counter_type: int +) -> typing.List[typing.Dict[str, typing.Any]]: try: - cacheKey = (servicePool and servicePool.id or 'all') + str(counter_type) + str(POINTS) + str(SINCE) + cacheKey = ( + (servicePool and str(servicePool.id) or 'all') + + str(counter_type) + + str(POINTS) + + str(SINCE) + ) to = models.getSqlDatetime() since: datetime.datetime = to - datetime.timedelta(days=SINCE) @@ -69,14 +75,22 @@ def getServicesPoolsCounters(servicePool: typing.Optional[models.ServicePool], c us = servicePool complete = False val = [] - for x in counters.getCounters(us, counter_type, since=since, to=to, max_intervals=POINTS, use_max=USE_MAX, all=complete): + for x in counters.getCounters( + us, + counter_type, + since=since, + to=to, + max_intervals=POINTS, + use_max=USE_MAX, + all=complete, + ): val.append({'stamp': x[0], 'value': int(x[1])}) if len(val) > 2: - cache.put(cacheKey, encoders.encode(pickle.dumps(val), 'zip'), 600) + cache.put(cacheKey, codecs.encode(pickle.dumps(val), 'zip'), 600) else: val = [{'stamp': since, 'value': 0}, {'stamp': to, 'value': 0}] else: - val = pickle.loads(typing.cast(bytes, encoders.decode(val, 'zip'))) + val = pickle.loads(codecs.decode(val, 'zip')) return val except: @@ -91,13 +105,17 @@ class System(Handler): logger.debug('args: %s', self._args) if len(self._args) == 1: if self._args[0] == 'overview': # System overview - users: models.User = models.User.objects.count() - groups: models.Group = models.Group.objects.count() - services: models.Service = models.Service.objects.count() - service_pools: models.ServicePool = models.ServicePool.objects.count() - meta_pools: models.MetaPool = models.MetaPool.objects.count() - user_services: models.UserService = models.UserService.objects.exclude(state__in=(State.REMOVED, State.ERROR)).count() - restrained_services_pools: int = models.ServicePool.getRestrainedsQuerySet().count() + users: int = models.User.objects.count() + groups: int = models.Group.objects.count() + services: int = models.Service.objects.count() + service_pools: int = models.ServicePool.objects.count() + meta_pools: int = models.MetaPool.objects.count() + user_services: int = models.UserService.objects.exclude( + state__in=(State.REMOVED, State.ERROR) + ).count() + restrained_services_pools: int = ( + models.ServicePool.getRestrainedsQuerySet().count() + ) return { 'users': users, 'groups': groups, diff --git a/server/src/uds/core/managers/user_service.py b/server/src/uds/core/managers/user_service.py index 783ca4b0..d9810449 100644 --- a/server/src/uds/core/managers/user_service.py +++ b/server/src/uds/core/managers/user_service.py @@ -393,7 +393,7 @@ class UserServiceManager: checks if we can do a "remove" from a deployed service serviceIsntance is just a helper, so if we already have unserialized deployedService """ - removing = self.getServicesInStateForProvider(servicePool.service.provider_id, State.REMOVING) + removing = self.getServicesInStateForProvider(servicePool.service.provider.id, State.REMOVING) serviceInstance = servicePool.service.getInstance() if removing >= serviceInstance.parent().getMaxRemovingServices() and serviceInstance.parent().getIgnoreLimits() is False: return False @@ -403,7 +403,7 @@ class UserServiceManager: """ Checks if we can start a new service """ - preparing = self.getServicesInStateForProvider(servicePool.service.provider_id, State.PREPARING) + preparing = self.getServicesInStateForProvider(servicePool.service.provider.id, State.PREPARING) serviceInstance = servicePool.service.getInstance() if preparing >= serviceInstance.parent().getMaxPreparingServices() and serviceInstance.parent().getIgnoreLimits() is False: return False @@ -539,7 +539,7 @@ class UserServiceManager: def getService( # pylint: disable=too-many-locals, too-many-branches, too-many-statements self, user: User, - os: typing.Dict, + os: typing.MutableMapping, srcIp: str, idService: str, idTransport: str, @@ -643,7 +643,7 @@ class UserServiceManager: self, user: User, srcIp: str, - os: typing.Dict, + os: typing.MutableMapping, idMetaPool: str, clientHostName: typing.Optional[str] = None ) -> typing.Tuple[typing.Optional[str], UserService, typing.Optional['services.UserDeployment'], Transport, typing.Optional[transports.Transport]]: @@ -660,9 +660,9 @@ class UserServiceManager: if meta.policy == MetaPool.PRIORITY_POOL: sortPools = [(p.priority, p.pool) for p in meta.members.all()] elif meta.policy == MetaPool.MOST_AVAILABLE_BY_NUMBER: - sortPools = [(p.usage(), p) for p in meta.pools.all()] + sortPools = [(p.pool.usage(), p.pool) for p in meta.members.all()] else: - sortPools = [(random.randint(0, 10000), p) for p in meta.pools.all()] # Just shuffle them + sortPools = [(random.randint(0, 10000), p.pool) for p in meta.members.all()] # Just shuffle them # Sort pools related to policy now, and xtract only pools, not sort keys # Remove "full" pools (100%) from result and pools in maintenance mode, not ready pools, etc... diff --git a/server/src/uds/core/services/user_deployment.py b/server/src/uds/core/services/user_deployment.py index 0feb2a40..7fdacee7 100644 --- a/server/src/uds/core/services/user_deployment.py +++ b/server/src/uds/core/services/user_deployment.py @@ -345,7 +345,7 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many """ return State.FINISHED - def deployForCache(self, cacheLevel: int): + def deployForCache(self, cacheLevel: int) -> str: """ Deploys a user deployment as cache. diff --git a/server/src/uds/core/util/encoders.py b/server/src/uds/core/util/encoders.py deleted file mode 100644 index 4abcd950..00000000 --- a/server/src/uds/core/util/encoders.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - -# -# Copyright (c) 2017-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. - -""" -@author: Adolfo Gómez, dkmaster at dkmon dot com -""" -import typing -import codecs - -def __toBinary(data: typing.Union[str, bytes]) -> bytes: - if isinstance(data, str): - return data.encode('utf8') - return data - - -def encode(data: typing.Union[str, bytes], encoder: str, asText: bool = False) -> typing.Union[str, bytes]: - res = codecs.encode(__toBinary(data), encoder) # type: ignore - if asText: - return res.decode('utf8') - return res - - -def decode(data: typing.Union[str, bytes], encoder: str, asText: bool = False) -> typing.Union[str, bytes]: - res = codecs.decode(__toBinary(data), encoder) - if asText: - return res.decode('utf8') # type: ignore - return res - - -def encodeAsStr(data: typing.Union[str, bytes], encoder: str) -> str: - return codecs.encode(__toBinary(data), encoder).decode('utf8') # type: ignore - - -def decodeAsStr(data: typing.Union[str, bytes], encoder: str) -> str: - return codecs.decode(__toBinary(data), encoder).decode('utf8') # type: ignore diff --git a/server/src/uds/core/util/tools.py b/server/src/uds/core/util/tools.py index b9bdaf64..fe1ef540 100644 --- a/server/src/uds/core/util/tools.py +++ b/server/src/uds/core/util/tools.py @@ -42,7 +42,7 @@ import django.template.defaultfilters as filters from uds.core import services -class DictAsObj: +class DictAsObj(dict): """ Returns a mix between a dict and an obj Can be accesses as .xxxx or ['xxx'] diff --git a/server/src/uds/migrations/0009_TransportsToNewerModel.py b/server/src/uds/migrations/0009_TransportsToNewerModel.py index 8b6558f5..3fb807b4 100644 --- a/server/src/uds/migrations/0009_TransportsToNewerModel.py +++ b/server/src/uds/migrations/0009_TransportsToNewerModel.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import migrations from uds.core.ui import gui -from uds.core.util import encoders # Disabled, not needed on 3.0 and forward migrations... # (can be squashed, this transforms data, that is nonexistant on 3.0 new install or recent migrations) diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py index b9ac9c8c..fc171faa 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py @@ -32,6 +32,7 @@ """ import random import string +import codecs import logging import typing @@ -39,7 +40,6 @@ from django.utils.translation import ugettext_noop as _ from uds.core.ui import gui from uds.core import osmanagers from uds.core.util import log -from uds.core.util import encoders from .linux_osmanager import LinuxOsManager @@ -61,6 +61,8 @@ class LinuxRandomPassManager(LinuxOsManager): onLogout = LinuxOsManager.onLogout idle = LinuxOsManager.idle + _userAccount: str + def __init__(self, environment, values): super(LinuxRandomPassManager, self).__init__(environment, values) if values is not None: @@ -104,13 +106,13 @@ class LinuxRandomPassManager(LinuxOsManager): Serializes the os manager data so we can store it in database """ base = LinuxOsManager.marshal(self) - return '\t'.join(['v1', self._userAccount, typing.cast(str, encoders.encode(base, 'hex', asText=True))]).encode('utf8') + return '\t'.join(['v1', self._userAccount, codecs.encode(base, 'hex').decode()]).encode('utf8') def unmarshal(self, data: bytes) -> None: - values = data.decode('utf8').split('\t') - if values[0] == 'v1': - self._userAccount = values[1] - LinuxOsManager.unmarshal(self, typing.cast(bytes, encoders.decode(values[2], 'hex'))) + values = data.split(b'\t') + if values[0] == b'v1': + self._userAccount = values[1].decode() + LinuxOsManager.unmarshal(self, codecs.decode(values[2], 'hex')) def valuesDict(self): dic = LinuxOsManager.valuesDict(self) diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows.py b/server/src/uds/osmanagers/WindowsOsManager/windows.py index b116edfa..875faa58 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows.py @@ -8,6 +8,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import codecs import logging import typing @@ -17,7 +18,6 @@ from uds.core.services import types as serviceTypes from uds.core.ui import gui from uds.core.managers import userServiceManager from uds.core.util.state import State -from uds.core.util import encoders from uds.core.util import log from uds.models import TicketStore @@ -38,7 +38,7 @@ def scrambleMsg(msg: str) -> str: for c in data[::-1]: res += bytes([c ^ n]) n = (n + c) & 0xFF - return typing.cast(str, encoders.encode(res, 'hex', asText=True)) + return codecs.encode(res, 'hex').decode() class WindowsOsManager(osmanagers.OSManager): @@ -56,9 +56,12 @@ class WindowsOsManager(osmanagers.OSManager): values=[ {'id': 'keep', 'text': ugettext_lazy('Keep service assigned')}, {'id': 'remove', 'text': ugettext_lazy('Remove service')}, - {'id': 'keep-always', 'text': ugettext_lazy('Keep service assigned even on new publication')}, + { + 'id': 'keep-always', + 'text': ugettext_lazy('Keep service assigned even on new publication'), + }, ], - defvalue='keep' + defvalue='keep', ) idle = gui.NumericField( @@ -67,18 +70,26 @@ class WindowsOsManager(osmanagers.OSManager): defvalue=-1, rdonly=False, order=11, - tooltip=_('Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max. idle time)'), - required=True + tooltip=_( + 'Maximum idle time (in seconds) before session is automatically closed to the user (<= 0 means no max. idle time)' + ), + required=True, ) + _onLogout: str + _idle: int @staticmethod def validateLen(length): try: length = int(length) except Exception: - raise osmanagers.OSManager.ValidationException(_('Length must be numeric!!')) + raise osmanagers.OSManager.ValidationException( + _('Length must be numeric!!') + ) if length > 6 or length < 1: - raise osmanagers.OSManager.ValidationException(_('Length must be betwen 1 and 6')) + raise osmanagers.OSManager.ValidationException( + _('Length must be betwen 1 and 6') + ) return length def __setProcessUnusedMachines(self): @@ -96,11 +107,13 @@ class WindowsOsManager(osmanagers.OSManager): self.__setProcessUnusedMachines() def isRemovableOnLogout(self, userService: 'UserService') -> bool: - ''' + """ Says if a machine is removable on logout - ''' + """ if not userService.in_use: - if (self._onLogout == 'remove') or (not userService.isValidPublication() and self._onLogout == 'keep'): + if (self._onLogout == 'remove') or ( + not userService.isValidPublication() and self._onLogout == 'keep' + ): return True return False @@ -117,7 +130,9 @@ class WindowsOsManager(osmanagers.OSManager): def infoValue(self, userService: 'UserService') -> str: return 'rename\r' + self.getName(userService) - def notifyIp(self, uid: str, userService, data: typing.Dict[str, typing.Any]) -> None: + def notifyIp( + self, uid: str, userService, data: typing.Dict[str, typing.Any] + ) -> None: userServiceInstance = userService.getInstance() ip = '' @@ -138,13 +153,15 @@ class WindowsOsManager(osmanagers.OSManager): try: level = int(levelStr) except Exception: - logger.debug('Do not understand level %s', level) + logger.debug('Do not understand level %s', levelStr) level = log.INFO log.doLog(userService, level, msg, origin) except Exception: logger.exception('WindowsOs Manager message log: ') - log.doLog(userService, log.ERROR, "do not understand {0}".format(data), origin) + log.doLog( + userService, log.ERROR, "do not understand {0}".format(data), origin + ) # default "ready received" does nothing def readyReceived(self, userService, data): @@ -162,13 +179,18 @@ class WindowsOsManager(osmanagers.OSManager): def readyNotified(self, userService): return - def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]: - return { - 'action': 'rename', - 'name': userService.getName() - } + def actorData( + self, userService: 'UserService' + ) -> typing.MutableMapping[str, typing.Any]: + return {'action': 'rename', 'name': userService.getName()} - def process(self, userService: 'UserService', message: str, data: typing.Any, options: typing.Optional[typing.Dict[str, typing.Any]] = None) -> str: # pylint: disable=too-many-branches + def process( + self, + userService: 'UserService', + message: str, + data: typing.Any, + options: typing.Optional[typing.Dict[str, typing.Any]] = None, + ) -> str: # pylint: disable=too-many-branches """ We understand this messages: * msg = info, data = None. Get information about name of machine (or domain, in derived WinDomainOsManager class) (old method) @@ -177,12 +199,19 @@ class WindowsOsManager(osmanagers.OSManager): * msg = logoff, data = Username, Informs that the username has logged out of the machine * msg = ready, data = None, Informs machine ready to be used """ - logger.info("Invoked WindowsOsManager for %s with params: %s,%s", userService, message, data) + logger.info( + "Invoked WindowsOsManager for %s with params: %s,%s", + userService, + message, + data, + ) - if message in ('ready', 'ip') and not isinstance(data, dict): # Old actors, previous to 2.5, convert it information.. + if message in ('ready', 'ip') and not isinstance( + data, dict + ): # Old actors, previous to 2.5, convert it information.. data = { 'ips': [v.split('=') for v in data.split(',')], - 'hostname': userService.friendly_name + 'hostname': userService.friendly_name, } # We get from storage the name for this userService. If no name, we try to assign a new one @@ -197,20 +226,25 @@ class WindowsOsManager(osmanagers.OSManager): ret = self.infoValue(userService) state = State.PREPARING elif message == "log": - self.doLog(userService, data, log.ACTOR) - elif message in("logon", 'login'): + self.doLog(userService, str(data), log.ACTOR) + elif message in ("logon", 'login'): if '\\' not in data: - osmanagers.OSManager.loggedIn(userService, data) + osmanagers.OSManager.loggedIn(userService, str(data)) userService.setInUse(True) # We get the userService logged hostname & ip and returns this ip, hostname = userService.getConnectionSource() deadLine = userService.deployed_service.getDeadline() - if typing.cast(str, userService.getProperty('actor_version', '0.0.0')) >= '2.0.0': - ret = "{0}\t{1}\t{2}".format(ip, hostname, 0 if deadLine is None else deadLine) + if ( + typing.cast(str, userService.getProperty('actor_version', '0.0.0')) + >= '2.0.0' + ): + ret = "{0}\t{1}\t{2}".format( + ip, hostname, 0 if deadLine is None else deadLine + ) else: ret = "{0}\t{1}".format(ip, hostname) elif message in ('logoff', 'logout'): - osmanagers.OSManager.loggedOut(userService, data) + osmanagers.OSManager.loggedOut(userService, str(data)) doRemove = self.isRemovableOnLogout(userService) elif message == "ip": # This ocurss on main loop inside machine, so userService is usable @@ -230,7 +264,9 @@ class WindowsOsManager(osmanagers.OSManager): userService.release() else: if notifyReady is False: - userService.save(update_fields=['in_use', 'in_use_date', 'os_state', 'state', 'data']) + userService.save( + update_fields=['in_use', 'in_use_date', 'os_state', 'state', 'data'] + ) else: logger.debug('Notifying ready') userServiceManager().notifyReadyFromOsManager(userService, '') @@ -239,7 +275,9 @@ class WindowsOsManager(osmanagers.OSManager): return ret return scrambleMsg(ret) - def processUserPassword(self, userService: 'UserService', username: str, password: str) -> typing.Tuple[str, str]: + def processUserPassword( + self, userService: 'UserService', username: str, password: str + ) -> typing.Tuple[str, str]: if userService.getProperty('sso_available') == '1': # Generate a ticket, store it and return username with no password domain = '' @@ -248,15 +286,15 @@ class WindowsOsManager(osmanagers.OSManager): elif '\\' in username: username, domain = username.split('\\') - creds = { - 'username': username, - 'password': password, - 'domain': domain - } - ticket = TicketStore.create(creds, validatorFnc=None, validity=300) # , owner=SECURE_OWNER, secure=True) + creds = {'username': username, 'password': password, 'domain': domain} + ticket = TicketStore.create( + creds, validatorFnc=None, validity=300 + ) # , owner=SECURE_OWNER, secure=True) return ticket, '' - return osmanagers.OSManager.processUserPassword(self, userService, username, password) + return osmanagers.OSManager.processUserPassword( + self, userService, username, password + ) def processUnused(self, userService: 'UserService') -> None: """ @@ -264,7 +302,12 @@ class WindowsOsManager(osmanagers.OSManager): This function can update userService values. Normal operation will be remove machines if this state is not valid """ if self.isRemovableOnLogout(userService): - log.doLog(userService, log.INFO, 'Unused user service for too long. Removing due to OS Manager parameters.', log.OSMANAGER) + log.doLog( + userService, + log.INFO, + 'Unused user service for too long. Removing due to OS Manager parameters.', + log.OSMANAGER, + ) userService.remove() def isPersistent(self): @@ -279,7 +322,9 @@ class WindowsOsManager(osmanagers.OSManager): """ On production environments, will return no idle for non removable machines """ - if self._idle <= 0: # or (settings.DEBUG is False and self._onLogout != 'remove'): + if ( + self._idle <= 0 + ): # or (settings.DEBUG is False and self._onLogout != 'remove'): return None return self._idle @@ -299,9 +344,11 @@ class WindowsOsManager(osmanagers.OSManager): elif vals[0] == 'v2': self._onLogout, self._idle = vals[1], int(vals[2]) except Exception: - logger.exception('Exception unmarshalling. Some values left as default ones') + logger.exception( + 'Exception unmarshalling. Some values left as default ones' + ) self.__setProcessUnusedMachines() def valuesDict(self) -> gui.ValuesDictType: - return {'onLogout': self._onLogout, 'idle': self._idle} + return {'onLogout': self._onLogout, 'idle': str(self._idle)} diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py index bceda5b4..be1dbcb2 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py @@ -31,6 +31,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import codecs import logging import typing @@ -42,7 +43,6 @@ from uds.core.ui import gui from uds.core.managers import cryptoManager from uds.core import osmanagers from uds.core.util import log -from uds.core.util import encoders from uds.core.util import ldaputil from .windows import WindowsOsManager @@ -64,14 +64,69 @@ class WinDomainOsManager(WindowsOsManager): iconFile = 'wosmanager.png' # Apart form data from windows os manager, we need also domain and credentials - domain = gui.TextField(length=64, label=_('Domain'), order=1, tooltip=_('Domain to join machines to (use FQDN form, Netbios name not supported for most operations)'), required=True) - account = gui.TextField(length=64, label=_('Account'), order=2, tooltip=_('Account with rights to add machines to domain'), required=True) - password = gui.PasswordField(length=64, label=_('Password'), order=3, tooltip=_('Password of the account'), required=True) - ou = gui.TextField(length=256, label=_('OU'), order=4, tooltip=_('Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local')) - grp = gui.TextField(length=64, label=_('Machine Group'), order=7, tooltip=_('Group to which add machines on creation. If empty, no group will be used. (experimental)'), tab=_('Advanced')) - removeOnExit = gui.CheckBoxField(label=_('Machine clean'), order=8, tooltip=_('If checked, UDS will try to remove the machine from the domain USING the provided credentials'), tab=_('Advanced'), defvalue=gui.TRUE) - serverHint = gui.TextField(length=64, label=_('Server Hint'), order=9, tooltip=_('In case of several AD servers, which one is preferred'), tab=_('Advanced')) - ssl = gui.CheckBoxField(label=_('Use SSL'), order=10, tooltip=_('If checked, a ssl connection to Active Directory will be used'), tab=_('Advanced'), defvalue=gui.TRUE) + domain = gui.TextField( + length=64, + label=_('Domain'), + order=1, + tooltip=_( + 'Domain to join machines to (use FQDN form, Netbios name not supported for most operations)' + ), + required=True, + ) + account = gui.TextField( + length=64, + label=_('Account'), + order=2, + tooltip=_('Account with rights to add machines to domain'), + required=True, + ) + password = gui.PasswordField( + length=64, + label=_('Password'), + order=3, + tooltip=_('Password of the account'), + required=True, + ) + ou = gui.TextField( + length=256, + label=_('OU'), + order=4, + tooltip=_( + 'Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local' + ), + ) + grp = gui.TextField( + length=64, + label=_('Machine Group'), + order=7, + tooltip=_( + 'Group to which add machines on creation. If empty, no group will be used. (experimental)' + ), + tab=_('Advanced'), + ) + removeOnExit = gui.CheckBoxField( + label=_('Machine clean'), + order=8, + tooltip=_( + 'If checked, UDS will try to remove the machine from the domain USING the provided credentials' + ), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) + serverHint = gui.TextField( + length=64, + label=_('Server Hint'), + order=9, + tooltip=_('In case of several AD servers, which one is preferred'), + tab=_('Advanced'), + ) + ssl = gui.CheckBoxField( + label=_('Use SSL'), + order=10, + tooltip=_('If checked, a ssl connection to Active Directory will be used'), + tab=_('Advanced'), + defvalue=gui.TRUE, + ) # Inherits base "onLogout" onLogout = WindowsOsManager.onLogout @@ -90,15 +145,23 @@ class WinDomainOsManager(WindowsOsManager): super().__init__(environment, values) if values: if values['domain'] == '': - raise osmanagers.OSManager.ValidationException(_('Must provide a domain!')) + raise osmanagers.OSManager.ValidationException( + _('Must provide a domain!') + ) # if values['domain'].find('.') == -1: # raise osmanagers.OSManager.ValidationException(_('Must provide domain in FQDN')) if values['account'] == '': - raise osmanagers.OSManager.ValidationException(_('Must provide an account to add machines to domain!')) + raise osmanagers.OSManager.ValidationException( + _('Must provide an account to add machines to domain!') + ) if values['account'].find('\\') != -1: - raise osmanagers.OSManager.ValidationException(_('DOM\\USER form is not allowed!')) + raise osmanagers.OSManager.ValidationException( + _('DOM\\USER form is not allowed!') + ) if values['password'] == '': - raise osmanagers.OSManager.ValidationException(_('Must provide a password for the account!')) + raise osmanagers.OSManager.ValidationException( + _('Must provide a password for the account!') + ) self._domain = values['domain'] self._ou = values['ou'].strip() self._account = values['account'] @@ -127,10 +190,17 @@ class WinDomainOsManager(WindowsOsManager): if self._serverHint != '': yield (self._serverHint, 389) - for server in reversed(sorted(dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'), key=lambda i: i.priority * 10000 + i.weight)): + for server in reversed( + sorted( + dns.resolver.query('_ldap._tcp.' + self._domain, 'SRV'), + key=lambda i: i.priority * 10000 + i.weight, + ) + ): yield (str(server.target)[:-1], server.port) - def __connectLdap(self, servers: typing.Optional[typing.Iterable[typing.Tuple[str, int]]] = None) -> typing.Any: + def __connectLdap( + self, servers: typing.Optional[typing.Iterable[typing.Tuple[str, int]]] = None + ) -> typing.Any: """ Tries to connect to LDAP Raises an exception if not found: @@ -150,7 +220,15 @@ class WinDomainOsManager(WindowsOsManager): ssl = self._ssl == 'y' port = server[1] if not ssl else -1 try: - return ldaputil.connection(account, self._password, server[0], port, ssl=ssl, timeout=10, debug=False) + return ldaputil.connection( + account, + self._password, + server[0], + port, + ssl=ssl, + timeout=10, + debug=False, + ) except Exception as e: _str = 'Error: {}'.format(e) @@ -161,7 +239,17 @@ class WinDomainOsManager(WindowsOsManager): group = ldaputil.escape(self._group) obj: typing.Optional[typing.MutableMapping[str, typing.Any]] try: - obj = next(ldaputil.getAsDict(ldapConnection, base, "(&(objectClass=group)(|(cn={0})(sAMAccountName={0})))".format(group), ['dn'], sizeLimit=50)) + obj = next( + ldaputil.getAsDict( + ldapConnection, + base, + "(&(objectClass=group)(|(cn={0})(sAMAccountName={0})))".format( + group + ), + ['dn'], + sizeLimit=50, + ) + ) except StopIteration: obj = None @@ -176,10 +264,14 @@ class WinDomainOsManager(WindowsOsManager): # else: base = ','.join(['DC=' + i for i in self._domain.split('.')]) - fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format(ldaputil.escape(machineName)) + fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format( + ldaputil.escape(machineName) + ) obj: typing.Optional[typing.MutableMapping[str, typing.Any]] try: - obj = next(ldaputil.getAsDict(ldapConnection, base, fltr, ['dn'], sizeLimit=50)) + obj = next( + ldaputil.getAsDict(ldapConnection, base, fltr, ['dn'], sizeLimit=50) + ) except StopIteration: obj = None @@ -208,21 +300,34 @@ class WinDomainOsManager(WindowsOsManager): # # # Direct LDAP operation "modify", maybe this need to be added to ldaputil? :) # # - ldapConnection.modify_s(group, ((ldap.MOD_ADD, 'member', machine),)) # @UndefinedVariable + ldapConnection.modify_s( + group, ((ldap.MOD_ADD, 'member', machine),) # type: ignore # (valid) + ) # @UndefinedVariable error = None break except dns.resolver.NXDOMAIN: # No domain found, log it and pass logger.warning('Could not find _ldap._tcp.%s', self._domain) - log.doLog(userService, log.WARN, "Could not remove machine from domain (_ldap._tcp.{0} not found)".format(self._domain), log.OSMANAGER) - except ldap.ALREADY_EXISTS: # @UndefinedVariable + log.doLog( + userService, + log.WARN, + "Could not remove machine from domain (_ldap._tcp.{0} not found)".format( + self._domain + ), + log.OSMANAGER, + ) + except ldap.ALREADY_EXISTS: # type: ignore # (valid) # Already added this machine to this group, pass error = None break except ldaputil.LDAPError: logger.exception('Ldap Exception caught') - error = "Could not add machine (invalid credentials? for {0})".format(self._account) + error = "Could not add machine (invalid credentials? for {0})".format( + self._account + ) except Exception as e: - error = "Could not add machine {} to group {}: {}".format(userService.friendly_name, self._group, e) + error = "Could not add machine {} to group {}: {}".format( + userService.friendly_name, self._group, e + ) # logger.exception('Ldap Exception caught') if error: @@ -238,31 +343,59 @@ class WinDomainOsManager(WindowsOsManager): if '.' not in self._domain: # logger.info('Releasing from a not FQDN domain is not supported') - log.doLog(userService, log.INFO, "Removing a domain machine form a non FQDN domain is not supported.", log.OSMANAGER) + log.doLog( + userService, + log.INFO, + "Removing a domain machine form a non FQDN domain is not supported.", + log.OSMANAGER, + ) return try: ldapConnection = self.__connectLdap() except dns.resolver.NXDOMAIN: # No domain found, log it and pass logger.warning('Could not find _ldap._tcp.%s', self._domain) - log.doLog(userService, log.WARN, "Could not remove machine from domain (_ldap._tcp.{} not found)".format(self._domain), log.OSMANAGER) + log.doLog( + userService, + log.WARN, + "Could not remove machine from domain (_ldap._tcp.{} not found)".format( + self._domain + ), + log.OSMANAGER, + ) return except ldaputil.LDAPError as e: # logger.exception('Ldap Exception caught') - log.doLog(userService, log.WARN, "Could not remove machine from domain ({})".format(e), log.OSMANAGER) + log.doLog( + userService, + log.WARN, + "Could not remove machine from domain ({})".format(e), + log.OSMANAGER, + ) return except Exception as e: # logger.exception('Exception caught') - log.doLog(userService, log.WARN, "Could not remove machine from domain ({})".format(e), log.OSMANAGER) + log.doLog( + userService, + log.WARN, + "Could not remove machine from domain ({})".format(e), + log.OSMANAGER, + ) return try: res = self.__getMachine(ldapConnection, userService.friendly_name) if res is None: - raise Exception('Machine {} not found on AD (permissions?)'.format(userService.friendly_name)) + raise Exception( + 'Machine {} not found on AD (permissions?)'.format( + userService.friendly_name + ) + ) ldaputil.recursive_delete(ldapConnection, res) except IndexError: - logger.error('Error deleting %s from BASE %s', userService.friendly_name, self._ou) + logger.error( + 'Error deleting %s from BASE %s', userService.friendly_name, self._ou + ) except Exception: logger.exception('Deleting from AD: ') @@ -272,30 +405,36 @@ class WinDomainOsManager(WindowsOsManager): except ldaputil.LDAPError as e: return _('Check error: {}').format(e) except dns.resolver.NXDOMAIN: - return _('Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)').format(self._domain) + return _( + 'Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)' + ).format(self._domain) except Exception as e: logger.exception('Exception ') return str(e) try: - ldapConnection.search_st(self._ou, ldap.SCOPE_BASE) # @UndefinedVariable + ldapConnection.search_st(self._ou, ldap.SCOPE_BASE) # type: ignore # (valid) except ldaputil.LDAPError as e: return _('Check error: {}').format(e) # Group if self._group != '': if self.__getGroup(ldapConnection) is None: - return _('Check Error: group "{}" not found (using "cn" to locate it)').format(self._group) + return _( + 'Check Error: group "{}" not found (using "cn" to locate it)' + ).format(self._group) return _('Server check was successful') # pylint: disable=protected-access @staticmethod - def test(env: 'Environment', data: typing.Dict[str, str]) -> typing.List[typing.Any]: + def test( + env: 'Environment', data: typing.Dict[str, str] + ) -> typing.List[typing.Any]: logger.debug('Test invoked') + wd = WinDomainOsManager(env, data) + logger.debug(wd) try: - wd: WinDomainOsManager = WinDomainOsManager(env, data) - logger.debug(wd) try: ldapConnection = wd.__connectLdap() except ldaputil.LDAPError as e: @@ -306,22 +445,34 @@ class WinDomainOsManager(WindowsOsManager): ou = 'cn=Computers,dc=' + ',dc='.join(wd._domain.split('.')) logger.info('Checking %s with ou %s', wd._domain, ou) - r = ldapConnection.search_st(ou, ldap.SCOPE_BASE) # @UndefinedVariable + r = ldapConnection.search_st(ou, ldap.SCOPE_BASE) # type: ignore # (valid) logger.info('Result of search: %s', r) except ldaputil.LDAPError: - if not wd._ou: - return [False, _('The default path {0} for computers was not found!!!').format(wd._ou)] + if wd and not wd._ou: + return [ + False, + _('The default path {0} for computers was not found!!!').format( + wd._ou + ), + ] return [False, _('The ou path {0} was not found!!!').format(wd._ou)] except dns.resolver.NXDOMAIN: - return [True, _('Could not check parameters (_ldap._tcp.{0} can\'r be resolved)').format(wd._domain)] + return [ + True, + _( + 'Could not check parameters (_ldap._tcp.{0} can\'r be resolved)' + ).format(wd._domain), + ] except Exception as e: logger.exception('Exception ') return [False, str(e)] return [True, _("All parameters seem to work fine.")] - def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]: + def actorData( + self, userService: 'UserService' + ) -> typing.MutableMapping[str, typing.Any]: return { 'action': 'rename_ad', 'name': userService.getName(), @@ -332,23 +483,42 @@ class WinDomainOsManager(WindowsOsManager): } def infoVal(self, userService: 'UserService') -> str: - return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(userService), self._domain, self._ou, self._account, self._password) + return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format( + self.getName(userService), + self._domain, + self._ou, + self._account, + self._password, + ) def infoValue(self, userService: 'UserService') -> str: - return 'domain\r{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(userService), self._domain, self._ou, self._account, self._password) + return 'domain\r{0}\t{1}\t{2}\t{3}\t{4}'.format( + self.getName(userService), + self._domain, + self._ou, + self._account, + self._password, + ) def marshal(self) -> bytes: - ''' + """ Serializes the os manager data so we can store it in database - ''' - base = typing.cast(str, encoders.encode(super().marshal(), 'hex', asText=True)) - return '\t'.join([ - 'v4', - self._domain, self._ou, self._account, - cryptoManager().encrypt(self._password), - base, - self._group, self._serverHint, self._ssl, self._removeOnExit - ]).encode('utf8') + """ + base = codecs.encode(super().marshal(), 'hex').decode() + return '\t'.join( + [ + 'v4', + self._domain, + self._ou, + self._account, + cryptoManager().encrypt(self._password), + base, + self._group, + self._serverHint, + self._ssl, + self._removeOnExit, + ] + ).encode('utf8') def unmarshal(self, data: bytes) -> None: values = data.decode('utf8').split('\t') @@ -374,8 +544,7 @@ class WinDomainOsManager(WindowsOsManager): else: self._ssl = 'n' self._removeOnExit = 'y' - - super().unmarshal(typing.cast(bytes, encoders.decode(values[5], 'hex'))) + super().unmarshal(codecs.decode(values[5].encode(), 'hex')) def valuesDict(self) -> gui.ValuesDictType: dct = super().valuesDict() diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py index 808e011e..96696c60 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py @@ -31,6 +31,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import codecs import random import string import logging @@ -41,7 +42,6 @@ from uds.core.ui import gui from uds.core.managers import cryptoManager from uds.core import osmanagers from uds.core.util import log -from uds.core.util import encoders from .windows import WindowsOsManager @@ -120,7 +120,7 @@ class WinRandomPassManager(WindowsOsManager): ''' Serializes the os manager data so we can store it in database ''' - base = typing.cast(str, encoders.encode(super().marshal(), 'hex', asText=True)) + base = codecs.encode(super().marshal(), 'hex').decode() return '\t'.join(['v1', self._userAccount, cryptoManager().encrypt(self._password), base]).encode('utf8') def unmarshal(self, data: bytes) -> None: @@ -128,7 +128,7 @@ class WinRandomPassManager(WindowsOsManager): if values[0] == 'v1': self._userAccount = values[1] self._password = cryptoManager().decrypt(values[2]) - super().unmarshal(typing.cast(bytes, encoders.decode(values[3], 'hex'))) + super().unmarshal(codecs.decode(values[3].encode(), 'hex')) def valuesDict(self) -> gui.ValuesDictType: dic = super().valuesDict() diff --git a/server/src/uds/web/util/errors.py b/server/src/uds/web/util/errors.py index e2c8bd06..ecec0745 100644 --- a/server/src/uds/web/util/errors.py +++ b/server/src/uds/web/util/errors.py @@ -30,6 +30,7 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com """ import traceback +import codecs import logging import typing @@ -39,7 +40,6 @@ from django.shortcuts import render from django.http import HttpResponseRedirect from django.urls import reverse -from uds.core.util import encoders from uds.models import ServicePool, Transport, UserService, Authenticator # Not imported at runtime, just for type checking @@ -108,7 +108,7 @@ def errorView(request: 'HttpRequest', errorCode: int) -> HttpResponseRedirect: if code != 0: errStr += ' (code {0:04X})'.format(code) - errStr = encoders.encodeAsStr(str(errStr), 'base64').replace('\n', '') + errStr = codecs.encode(str(errStr), 'base64').decode().replace('\n', '') logger.debug('Redirection to error view with %s', errStr) return HttpResponseRedirect(reverse('page.error', kwargs={'err': errStr}))