mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-10 01:17:59 +03:00
More linting (a lot more :) )
This commit is contained in:
parent
ed90cd3995
commit
a058b61276
@ -11,7 +11,7 @@ clear-cache-post-run=no
|
|||||||
|
|
||||||
# Load and enable all available extensions. Use --list-extensions to see a list
|
# Load and enable all available extensions. Use --list-extensions to see a list
|
||||||
# all available extensions.
|
# all available extensions.
|
||||||
#enable-all-extensions=
|
enable-all-extensions=yes
|
||||||
|
|
||||||
# In error mode, messages with a category besides ERROR or FATAL are
|
# In error mode, messages with a category besides ERROR or FATAL are
|
||||||
# suppressed, and no reports are done by default. Error mode is compatible with
|
# suppressed, and no reports are done by default. Error mode is compatible with
|
||||||
@ -278,34 +278,34 @@ exclude-too-few-public-methods=
|
|||||||
ignored-parents=
|
ignored-parents=
|
||||||
|
|
||||||
# Maximum number of arguments for function / method.
|
# Maximum number of arguments for function / method.
|
||||||
max-args=5
|
max-args=10
|
||||||
|
|
||||||
# Maximum number of attributes for a class (see R0902).
|
# Maximum number of attributes for a class (see R0902).
|
||||||
max-attributes=7
|
max-attributes=12
|
||||||
|
|
||||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||||
max-bool-expr=5
|
max-bool-expr=5
|
||||||
|
|
||||||
# Maximum number of branch for function / method body.
|
# Maximum number of branch for function / method body.
|
||||||
max-branches=20
|
max-branches=24
|
||||||
|
|
||||||
# Maximum number of locals for function / method body.
|
# Maximum number of locals for function / method body.
|
||||||
max-locals=15
|
max-locals=24
|
||||||
|
|
||||||
# Maximum number of parents for a class (see R0901).
|
# Maximum number of parents for a class (see R0901).
|
||||||
max-parents=7
|
max-parents=7
|
||||||
|
|
||||||
# Maximum number of public methods for a class (see R0904).
|
# Maximum number of public methods for a class (see R0904).
|
||||||
max-public-methods=20
|
max-public-methods=32
|
||||||
|
|
||||||
# Maximum number of return / yield for function / method body.
|
# Maximum number of return / yield for function / method body.
|
||||||
max-returns=6
|
max-returns=6
|
||||||
|
|
||||||
# Maximum number of statements in function / method body.
|
# Maximum number of statements in function / method body.
|
||||||
max-statements=50
|
max-statements=64
|
||||||
|
|
||||||
# Minimum number of public methods for a class (see R0903).
|
# Minimum number of public methods for a class (see R0903).
|
||||||
min-public-methods=2
|
min-public-methods=1
|
||||||
|
|
||||||
|
|
||||||
[EXCEPTIONS]
|
[EXCEPTIONS]
|
||||||
@ -428,7 +428,8 @@ disable=raw-checker-failed,
|
|||||||
broad-except,
|
broad-except,
|
||||||
no-name-in-module, # Too many false positives... :(
|
no-name-in-module, # Too many false positives... :(
|
||||||
import-error,
|
import-error,
|
||||||
too-many-lines
|
too-many-lines,
|
||||||
|
redefined-builtin,
|
||||||
|
|
||||||
# Enable the message, report, category or checker with the given id(s). You can
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
# either give multiple identifier separated by comma (,) or put this option
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
@ -93,7 +93,7 @@ class Client(Handler):
|
|||||||
# error += ' (code {0:04X})'.format(errorCode)
|
# error += ' (code {0:04X})'.format(errorCode)
|
||||||
error = _(
|
error = _(
|
||||||
'Your service is being created. Please, wait while we complete it'
|
'Your service is being created. Please, wait while we complete it'
|
||||||
) + ' ({}%)'.format(int(errorCode * 25))
|
) + f' ({int(errorCode)*25}%)'
|
||||||
|
|
||||||
res['error'] = error
|
res['error'] = error
|
||||||
res['retryable'] = '1' if retryable else '0'
|
res['retryable'] = '1' if retryable else '0'
|
||||||
|
@ -459,10 +459,10 @@ def webPassword(request: HttpRequest) -> str:
|
|||||||
return cryptoManager().symDecrpyt(
|
return cryptoManager().symDecrpyt(
|
||||||
passkey, getUDSCookie(request)
|
passkey, getUDSCookie(request)
|
||||||
) # recover as original unicode string
|
) # recover as original unicode string
|
||||||
else: # No session, get from _session instead, this is an "client" REST request
|
# No session, get from _session instead, this is an "client" REST request
|
||||||
return cryptoManager().symDecrpyt(
|
return cryptoManager().symDecrpyt(
|
||||||
getattr(request, '_cryptedpass'), getattr(request, '_scrambler')
|
getattr(request, '_cryptedpass'), getattr(request, '_scrambler')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def webLogout(
|
def webLogout(
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
@ -33,7 +34,6 @@ Base module for all authenticators
|
|||||||
"""
|
"""
|
||||||
import enum
|
import enum
|
||||||
import logging
|
import logging
|
||||||
from re import A
|
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from django.utils.translation import gettext_noop as _
|
from django.utils.translation import gettext_noop as _
|
||||||
@ -65,6 +65,7 @@ class AuthenticationSuccess(enum.IntEnum):
|
|||||||
OK = 1
|
OK = 1
|
||||||
REDIRECT = 2
|
REDIRECT = 2
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationInternalUrl(enum.Enum):
|
class AuthenticationInternalUrl(enum.Enum):
|
||||||
"""
|
"""
|
||||||
Enumeration for authentication success
|
Enumeration for authentication success
|
||||||
@ -78,6 +79,7 @@ class AuthenticationInternalUrl(enum.Enum):
|
|||||||
"""
|
"""
|
||||||
return reverse(self.value)
|
return reverse(self.value)
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationResult(typing.NamedTuple):
|
class AuthenticationResult(typing.NamedTuple):
|
||||||
success: AuthenticationSuccess
|
success: AuthenticationSuccess
|
||||||
url: typing.Optional[str] = None
|
url: typing.Optional[str] = None
|
||||||
@ -186,8 +188,8 @@ class Authenticator(Module):
|
|||||||
# : If this authenticators casues a temporal block of an user on repeated login failures
|
# : If this authenticators casues a temporal block of an user on repeated login failures
|
||||||
blockUserOnLoginFailures: typing.ClassVar[bool] = True
|
blockUserOnLoginFailures: typing.ClassVar[bool] = True
|
||||||
|
|
||||||
from .user import User
|
from .user import User # pylint: disable=import-outside-toplevel
|
||||||
from .group import Group
|
from .group import Group # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
# : The type of user provided, normally standard user will be enough.
|
# : The type of user provided, normally standard user will be enough.
|
||||||
# : This is here so if we need it in some case, we can write our own
|
# : This is here so if we need it in some case, we can write our own
|
||||||
@ -213,10 +215,12 @@ class Authenticator(Module):
|
|||||||
@param environment: Environment for the authenticator
|
@param environment: Environment for the authenticator
|
||||||
@param values: Values passed to element
|
@param values: Values passed to element
|
||||||
"""
|
"""
|
||||||
from uds.models import Authenticator as AuthenticatorModel
|
from uds.models import ( # pylint: disable=import-outside-toplevel
|
||||||
|
Authenticator as AuthenticatorModel,
|
||||||
|
)
|
||||||
|
|
||||||
self._dbAuth = dbAuth or AuthenticatorModel() # Fake dbAuth if not provided
|
self._dbAuth = dbAuth or AuthenticatorModel() # Fake dbAuth if not provided
|
||||||
super(Authenticator, self).__init__(environment, values)
|
super().__init__(environment, values)
|
||||||
self.initialize(values)
|
self.initialize(values)
|
||||||
|
|
||||||
def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None:
|
def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None:
|
||||||
@ -249,9 +253,9 @@ class Authenticator(Module):
|
|||||||
|
|
||||||
user param is a database user object
|
user param is a database user object
|
||||||
"""
|
"""
|
||||||
from uds.core.auths.groups_manager import (
|
from uds.core.auths.groups_manager import ( # pylint: disable=import-outside-toplevel
|
||||||
GroupsManager,
|
GroupsManager,
|
||||||
) # pylint: disable=redefined-outer-name
|
)
|
||||||
|
|
||||||
if self.isExternalSource:
|
if self.isExternalSource:
|
||||||
groupsManager = GroupsManager(self._dbAuth)
|
groupsManager = GroupsManager(self._dbAuth)
|
||||||
@ -268,7 +272,7 @@ class Authenticator(Module):
|
|||||||
This method will allow us to know where to do redirection in case
|
This method will allow us to know where to do redirection in case
|
||||||
we need to use callback for authentication
|
we need to use callback for authentication
|
||||||
"""
|
"""
|
||||||
from .auth import authCallbackUrl
|
from .auth import authCallbackUrl # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
return authCallbackUrl(self.dbAuthenticator())
|
return authCallbackUrl(self.dbAuthenticator())
|
||||||
|
|
||||||
@ -276,7 +280,7 @@ class Authenticator(Module):
|
|||||||
"""
|
"""
|
||||||
Helper method to return info url for this authenticator
|
Helper method to return info url for this authenticator
|
||||||
"""
|
"""
|
||||||
from .auth import authInfoUrl
|
from .auth import authInfoUrl # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
return authInfoUrl(self.dbAuthenticator())
|
return authInfoUrl(self.dbAuthenticator())
|
||||||
|
|
||||||
@ -394,18 +398,26 @@ class Authenticator(Module):
|
|||||||
"""
|
"""
|
||||||
return FAILED_AUTH
|
return FAILED_AUTH
|
||||||
|
|
||||||
def isAccesibleFrom(self, request: 'HttpRequest'):
|
def isAccesibleFrom(self, request: 'HttpRequest') -> bool:
|
||||||
"""
|
"""
|
||||||
Used by the login interface to determine if the authenticator is visible on the login page.
|
Used by the login interface to determine if the authenticator is visible on the login page.
|
||||||
"""
|
"""
|
||||||
from uds.core.util.request import ExtendedHttpRequest
|
from uds.core.util.request import ( # pylint: disable=import-outside-toplevel
|
||||||
from uds.models import Authenticator as dbAuth
|
ExtendedHttpRequest,
|
||||||
|
)
|
||||||
|
from uds.models import ( # pylint: disable=import-outside-toplevel
|
||||||
|
Authenticator as dbAuth,
|
||||||
|
)
|
||||||
|
|
||||||
return self._dbAuth.state != dbAuth.DISABLED and self._dbAuth.validForIp(
|
return self._dbAuth.state != dbAuth.DISABLED and self._dbAuth.validForIp(
|
||||||
typing.cast('ExtendedHttpRequest', request).ip
|
typing.cast('ExtendedHttpRequest', request).ip
|
||||||
)
|
)
|
||||||
|
|
||||||
def transformUsername(self, username: str, request: 'ExtendedHttpRequest') -> str:
|
def transformUsername(
|
||||||
|
self,
|
||||||
|
username: str,
|
||||||
|
request: 'ExtendedHttpRequest',
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
On login, this method get called so we can "transform" provided user name.
|
On login, this method get called so we can "transform" provided user name.
|
||||||
|
|
||||||
@ -462,7 +474,11 @@ class Authenticator(Module):
|
|||||||
"""
|
"""
|
||||||
return self.authenticate(username, credentials, groupsManager, request)
|
return self.authenticate(username, credentials, groupsManager, request)
|
||||||
|
|
||||||
def logout(self, request: 'ExtendedHttpRequest', username: str) -> AuthenticationResult:
|
def logout(
|
||||||
|
self,
|
||||||
|
request: 'ExtendedHttpRequest',
|
||||||
|
username: str,
|
||||||
|
) -> AuthenticationResult:
|
||||||
"""
|
"""
|
||||||
Invoked whenever an user logs out.
|
Invoked whenever an user logs out.
|
||||||
|
|
||||||
@ -491,7 +507,10 @@ class Authenticator(Module):
|
|||||||
return SUCCESS_AUTH
|
return SUCCESS_AUTH
|
||||||
|
|
||||||
def webLogoutHook(
|
def webLogoutHook(
|
||||||
self, username: str, request: 'HttpRequest', response: 'HttpResponse'
|
self,
|
||||||
|
username: str,
|
||||||
|
request: 'HttpRequest',
|
||||||
|
response: 'HttpResponse',
|
||||||
) -> None:
|
) -> None:
|
||||||
'''
|
'''
|
||||||
Invoked on web logout of an user
|
Invoked on web logout of an user
|
||||||
|
@ -64,7 +64,7 @@ class JobThread(threading.Thread):
|
|||||||
_freq: int
|
_freq: int
|
||||||
|
|
||||||
def __init__(self, jobInstance: 'Job', dbJob: DBScheduler) -> None:
|
def __init__(self, jobInstance: 'Job', dbJob: DBScheduler) -> None:
|
||||||
super(JobThread, self).__init__()
|
super().__init__()
|
||||||
self._jobInstance = jobInstance
|
self._jobInstance = jobInstance
|
||||||
self._dbJobId = dbJob.id
|
self._dbJobId = dbJob.id
|
||||||
self._freq = dbJob.frecuency
|
self._freq = dbJob.frecuency
|
||||||
|
@ -85,7 +85,7 @@ class CryptoManager(metaclass=singleton.Singleton):
|
|||||||
while len(key) < length:
|
while len(key) < length:
|
||||||
key += key # Dup key
|
key += key # Dup key
|
||||||
|
|
||||||
kl: typing.List[int] = [v for v in key]
|
kl: typing.List[int] = list(key)
|
||||||
pos = 0
|
pos = 0
|
||||||
while len(kl) > length:
|
while len(kl) > length:
|
||||||
kl[pos] ^= kl[length]
|
kl[pos] ^= kl[length]
|
||||||
@ -279,17 +279,18 @@ class CryptoManager(metaclass=singleton.Singleton):
|
|||||||
return secrets.compare_digest(
|
return secrets.compare_digest(
|
||||||
hashlib.sha3_256(value).hexdigest(), hashValue[8:]
|
hashlib.sha3_256(value).hexdigest(), hashValue[8:]
|
||||||
)
|
)
|
||||||
elif hashValue[:12] == '{SHA256SALT}':
|
if hashValue[:12] == '{SHA256SALT}':
|
||||||
# Extract 16 chars salt and hash
|
# Extract 16 chars salt and hash
|
||||||
salt = hashValue[12:28].encode()
|
salt = hashValue[12:28].encode()
|
||||||
value = salt + value
|
value = salt + value
|
||||||
return secrets.compare_digest(
|
return secrets.compare_digest(
|
||||||
hashlib.sha3_256(value).hexdigest(), hashValue[28:]
|
hashlib.sha3_256(value).hexdigest(), hashValue[28:]
|
||||||
)
|
)
|
||||||
else: # Old sha1
|
|
||||||
return secrets.compare_digest(
|
# Old sha1
|
||||||
hashValue, str(hashlib.sha1(value).hexdigest()) # nosec: Old compatibility SHA1, not used anymore but need to be supported
|
return secrets.compare_digest(
|
||||||
) # nosec: Old compatibility SHA1, not used anymore but need to be supported
|
hashValue, str(hashlib.sha1(value).hexdigest()) # nosec: Old compatibility SHA1, not used anymore but need to be supported
|
||||||
|
) # nosec: Old compatibility SHA1, not used anymore but need to be supported
|
||||||
|
|
||||||
def uuid(self, obj: typing.Any = None) -> str:
|
def uuid(self, obj: typing.Any = None) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -106,7 +106,7 @@ class DownloadsManager(metaclass=singleton.Singleton):
|
|||||||
memory at once. The FileWrapper will turn the file object into an
|
memory at once. The FileWrapper will turn the file object into an
|
||||||
iterator for chunks of 8KB.
|
iterator for chunks of 8KB.
|
||||||
"""
|
"""
|
||||||
wrapper = FileWrapper(open(filename, 'rb'))
|
wrapper = FileWrapper(open(filename, 'rb')) # pylint: disable=consider-using-with
|
||||||
response = HttpResponse(wrapper, content_type=mime)
|
response = HttpResponse(wrapper, content_type=mime)
|
||||||
response['Content-Length'] = os.path.getsize(filename)
|
response['Content-Length'] = os.path.getsize(filename)
|
||||||
response['Content-Disposition'] = 'attachment; filename=' + name
|
response['Content-Disposition'] = 'attachment; filename=' + name
|
||||||
|
@ -149,7 +149,7 @@ class PublicationFinishChecker(DelayedTask):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, publication: ServicePoolPublication) -> None:
|
def __init__(self, publication: ServicePoolPublication) -> None:
|
||||||
super(PublicationFinishChecker, self).__init__()
|
super().__init__()
|
||||||
self._publishId = publication.id
|
self._publishId = publication.id
|
||||||
self._state = publication.state
|
self._state = publication.state
|
||||||
|
|
||||||
|
@ -1021,12 +1021,15 @@ class UserServiceManager(metaclass=singleton.Singleton):
|
|||||||
sortedPools = sorted(
|
sortedPools = sorted(
|
||||||
sortPools, key=operator.itemgetter(0)
|
sortPools, key=operator.itemgetter(0)
|
||||||
) # sort by priority (first element)
|
) # sort by priority (first element)
|
||||||
pools: typing.List[ServicePool] = [
|
pools: typing.List[ServicePool] = []
|
||||||
p[1] for p in sortedPools if p[1].usage() < 100 and p[1].isUsable()
|
poolsFull: typing.List[ServicePool] = []
|
||||||
]
|
for p in sortedPools:
|
||||||
poolsFull: typing.List[ServicePool] = [
|
if not p[1].isUsable():
|
||||||
p[1] for p in sortedPools if p[1].usage() == 100 and p[1].isUsable()
|
continue
|
||||||
]
|
if p[1].usage() == 100:
|
||||||
|
poolsFull.append(p[1])
|
||||||
|
else:
|
||||||
|
pools.append(p[1])
|
||||||
|
|
||||||
logger.debug('Pools: %s/%s', pools, poolsFull)
|
logger.debug('Pools: %s/%s', pools, poolsFull)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2022 Virtual Cable S.L.U.
|
# Copyright (c) 2022 Virtual Cable S.L.U.
|
||||||
@ -300,7 +300,6 @@ class MFA(Module):
|
|||||||
This method allows to reset the MFA state of an user.
|
This method allows to reset the MFA state of an user.
|
||||||
Normally, this will do nothing, but for persistent MFA data (as Google Authenticator), this will remove the data.
|
Normally, this will do nothing, but for persistent MFA data (as Google Authenticator), this will remove the data.
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getUserId(user: 'User') -> str:
|
def getUserId(user: 'User') -> str:
|
||||||
|
@ -216,11 +216,16 @@ def surfaceChart(
|
|||||||
|
|
||||||
if data.get('wireframe', False):
|
if data.get('wireframe', False):
|
||||||
axis.plot_wireframe(
|
axis.plot_wireframe(
|
||||||
x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
rstride=1,
|
||||||
|
cstride=1,
|
||||||
|
cmap=cm.coolwarm, # pylint: disable=no-member # type: ignore
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
axis.plot_surface(
|
axis.plot_surface(
|
||||||
x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore
|
x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm # type: ignore # pylint: disable=no-member
|
||||||
)
|
)
|
||||||
|
|
||||||
axis.set_title(data.get('title', ''))
|
axis.set_title(data.get('title', ''))
|
||||||
|
@ -48,14 +48,20 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Report(UserInterface):
|
class Report(UserInterface):
|
||||||
mime_type: typing.ClassVar[str] = 'application/pdf' # Report returns pdfs by default, but could be anything else
|
mime_type: typing.ClassVar[
|
||||||
|
str
|
||||||
|
] = 'application/pdf' # Report returns pdfs by default, but could be anything else
|
||||||
name: typing.ClassVar[str] = _('Base Report') # Report name
|
name: typing.ClassVar[str] = _('Base Report') # Report name
|
||||||
group: typing.ClassVar[str] = '' # So we can "group" reports by kind?
|
group: typing.ClassVar[str] = '' # So we can "group" reports by kind?
|
||||||
encoded: typing.ClassVar[bool] = True # If the report is mean to be encoded (binary reports as PDFs == True, text reports must be False so utf-8 is correctly threated
|
encoded: typing.ClassVar[
|
||||||
|
bool
|
||||||
|
] = True # If the report is mean to be encoded (binary reports as PDFs == True, text reports must be False so utf-8 is correctly threated
|
||||||
uuid: typing.ClassVar[str] = ''
|
uuid: typing.ClassVar[str] = ''
|
||||||
|
|
||||||
description: str = _('Base report') # Report description
|
description: str = _('Base report') # Report description
|
||||||
filename: str = 'file.pdf' # Filename that will be returned as 'hint' on rest report request
|
filename: str = (
|
||||||
|
'file.pdf' # Filename that will be returned as 'hint' on rest report request
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def translated_name(cls):
|
def translated_name(cls):
|
||||||
@ -81,35 +87,42 @@ class Report(UserInterface):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def getUuid(cls):
|
def getUuid(cls):
|
||||||
if cls.uuid is None:
|
if cls.uuid is None:
|
||||||
raise Exception('Class does not includes an uuid!!!: {}'.format(cls))
|
raise Exception(f'Class does not includes an uuid!!!: {cls}')
|
||||||
return cls.uuid
|
return cls.uuid
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def asPDF(html: str, header: typing.Optional[str] = None, water: typing.Optional[str] = None, images: typing.Optional[typing.Dict[str, bytes]] = None) -> bytes:
|
def asPDF(
|
||||||
|
html: str,
|
||||||
|
header: typing.Optional[str] = None,
|
||||||
|
water: typing.Optional[str] = None,
|
||||||
|
images: typing.Optional[typing.Dict[str, bytes]] = None,
|
||||||
|
) -> bytes:
|
||||||
"""
|
"""
|
||||||
Renders an html as PDF.
|
Renders an html as PDF.
|
||||||
Uses the "report.css" as stylesheet
|
Uses the "report.css" as stylesheet
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# url fetcher for weasyprint
|
# url fetcher for weasyprint
|
||||||
def report_fetcher(url: str, timeout=10, ssl_context=None) -> typing.Dict:
|
def report_fetcher(
|
||||||
|
url: str, timeout=10, ssl_context=None # pylint: disable=unused-argument
|
||||||
|
) -> typing.Dict:
|
||||||
logger.debug('Getting url for weasyprint %s', url)
|
logger.debug('Getting url for weasyprint %s', url)
|
||||||
if url.startswith('stock://'):
|
if url.startswith('stock://'):
|
||||||
imagePath = stock.getStockImagePath(url[8:])
|
imagePath = stock.getStockImagePath(url[8:])
|
||||||
with open(imagePath, 'rb') as f:
|
with open(imagePath, 'rb') as f:
|
||||||
image = f.read()
|
image = f.read()
|
||||||
return dict(string=image, mime_type='image/png')
|
return {'string': image, 'mime_type': 'image/png'}
|
||||||
|
|
||||||
if url.startswith('image://'):
|
if url.startswith('image://'):
|
||||||
img: typing.Optional[bytes] = b'' # Empty image
|
img: typing.Optional[bytes] = b'' # Empty image
|
||||||
if images:
|
if images:
|
||||||
img = images.get(url[8:])
|
img = images.get(url[8:])
|
||||||
logger.debug('Getting image %s? %s', url[8:], img is not None)
|
logger.debug('Getting image %s? %s', url[8:], img is not None)
|
||||||
return dict(string=img, mime_type='image/png')
|
return {'string': img, 'mime_type': 'image/png'}
|
||||||
|
|
||||||
return default_url_fetcher(url)
|
return default_url_fetcher(url)
|
||||||
|
|
||||||
with open(stock.getStockCssPath('report.css'), 'r') as f:
|
with open(stock.getStockCssPath('report.css'), 'r', encoding='utf-8') as f:
|
||||||
css = f.read()
|
css = f.read()
|
||||||
|
|
||||||
css = (
|
css = (
|
||||||
@ -117,13 +130,20 @@ class Report(UserInterface):
|
|||||||
.replace('{page}', _('Page'))
|
.replace('{page}', _('Page'))
|
||||||
.replace('{of}', _('of'))
|
.replace('{of}', _('of'))
|
||||||
.replace('{water}', water or 'UDS Report')
|
.replace('{water}', water or 'UDS Report')
|
||||||
.replace('{printed}', _('Printed in {now:%Y, %b %d} at {now:%H:%M}').format(now=datetime.datetime.now()))
|
.replace(
|
||||||
|
'{printed}',
|
||||||
|
_('Printed in {now:%Y, %b %d} at {now:%H:%M}').format(
|
||||||
|
now=datetime.datetime.now()
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
h = HTML(string=html, url_fetcher=report_fetcher)
|
h = HTML(string=html, url_fetcher=report_fetcher)
|
||||||
c = CSS(string=css)
|
c = CSS(string=css)
|
||||||
|
|
||||||
return typing.cast(bytes, h.write_pdf(stylesheets=[c])) # Return a new bytes object
|
return typing.cast(
|
||||||
|
bytes, h.write_pdf(stylesheets=[c])
|
||||||
|
) # Return a new bytes object
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def templateAsPDF(templateName, dct, header=None, water=None, images=None) -> bytes:
|
def templateAsPDF(templateName, dct, header=None, water=None, images=None) -> bytes:
|
||||||
@ -184,4 +204,4 @@ class Report(UserInterface):
|
|||||||
return typing.cast(str, data)
|
return typing.cast(str, data)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Report {} with uuid {}'.format(self.name, self.uuid)
|
return f'Report {self.name} with uuid {self.uuid}'
|
||||||
|
@ -32,13 +32,16 @@
|
|||||||
"""
|
"""
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
from uds.core.exceptions import UDSException
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from uds.models import UserService, Transport
|
from uds.models import UserService, Transport
|
||||||
|
|
||||||
|
|
||||||
class ServiceException(Exception):
|
class ServiceException(UDSException):
|
||||||
def __init__(self, *args, **kwargs):
|
"""
|
||||||
super(ServiceException, self).__init__(*args)
|
Base class for all service exceptions
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedException(ServiceException):
|
class UnsupportedException(ServiceException):
|
||||||
|
@ -37,7 +37,6 @@ from uds.core.module import Module
|
|||||||
from uds.core.environment import Environment
|
from uds.core.environment import Environment
|
||||||
|
|
||||||
from uds.core.util import log
|
from uds.core.util import log
|
||||||
from uds.core.util.config import GlobalConfig
|
|
||||||
from uds.core.ui import gui
|
from uds.core.ui import gui
|
||||||
|
|
||||||
# Not imported at runtime, just for type checking
|
# Not imported at runtime, just for type checking
|
||||||
@ -213,7 +212,7 @@ class ServiceProvider(Module):
|
|||||||
"""
|
"""
|
||||||
Logs a message with requested level associated with this service
|
Logs a message with requested level associated with this service
|
||||||
"""
|
"""
|
||||||
from uds.models import Provider as DBProvider
|
from uds.models import Provider as DBProvider # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
if self.getUuid():
|
if self.getUuid():
|
||||||
log.doLog(
|
log.doLog(
|
||||||
|
@ -33,10 +33,11 @@
|
|||||||
import typing
|
import typing
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from uds.core.util import factory
|
||||||
|
|
||||||
from .provider import ServiceProvider
|
from .provider import ServiceProvider
|
||||||
from .service import Service
|
from .service import Service
|
||||||
|
|
||||||
from uds.core.util import factory
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"""
|
"""
|
||||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
|
import abc
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from uds.core.environment import Environmentable
|
from uds.core.environment import Environmentable
|
||||||
@ -158,6 +159,7 @@ class Publication(Environmentable, Serializable):
|
|||||||
def getUuid(self) -> str:
|
def getUuid(self) -> str:
|
||||||
return self._uuid
|
return self._uuid
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def publish(self) -> str:
|
def publish(self) -> str:
|
||||||
"""
|
"""
|
||||||
This method is invoked whenever the administrator requests a new publication.
|
This method is invoked whenever the administrator requests a new publication.
|
||||||
@ -192,11 +194,10 @@ class Publication(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'publish method for class {0} not provided! '.format(
|
f'publish method for class {self.__class__.__name__} not provided! '
|
||||||
self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def checkState(self) -> str:
|
def checkState(self) -> str:
|
||||||
"""
|
"""
|
||||||
This is a task method. As that, the expected return values are
|
This is a task method. As that, the expected return values are
|
||||||
@ -222,9 +223,7 @@ class Publication(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'checkState method for class {0} not provided!!!'.format(
|
f'checkState method for class {self.__class__.__name__} not provided!!!'
|
||||||
self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def finish(self) -> None:
|
def finish(self) -> None:
|
||||||
@ -251,6 +250,7 @@ class Publication(Environmentable, Serializable):
|
|||||||
"""
|
"""
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def destroy(self) -> str:
|
def destroy(self) -> str:
|
||||||
"""
|
"""
|
||||||
This is a task method. As that, the expected return values are
|
This is a task method. As that, the expected return values are
|
||||||
@ -270,9 +270,10 @@ class Publication(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'destroy method for class {0} not provided!'.format(self.__class__.__name__)
|
f'destroy method for class {self.__class__.__name__} not provided!'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
def cancel(self) -> str:
|
def cancel(self) -> str:
|
||||||
"""
|
"""
|
||||||
This is a task method. As that, the expected return values are
|
This is a task method. As that, the expected return values are
|
||||||
@ -292,11 +293,11 @@ class Publication(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'cancel method for class {0} not provided!'.format(self.__class__.__name__)
|
f'cancel method for class {self.__class__.__name__} not provided!'
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
String method, mainly used for debugging purposes
|
String method, mainly used for debugging purposes
|
||||||
"""
|
"""
|
||||||
return "Base Publication"
|
return 'Base Publication'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||||
@ -281,9 +281,7 @@ class Service(Module):
|
|||||||
name returned is unique :-)
|
name returned is unique :-)
|
||||||
"""
|
"""
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format(
|
f'The class {self.__class__.__name__} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'
|
||||||
self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def macGenerator(self) -> typing.Optional['UniqueMacGenerator']:
|
def macGenerator(self) -> typing.Optional['UniqueMacGenerator']:
|
||||||
@ -313,7 +311,9 @@ class Service(Module):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
def assignFromAssignables(
|
def assignFromAssignables(
|
||||||
self, assignableId: str, user: 'models.User', userDeployment: UserDeployment
|
self, assignableId: str,
|
||||||
|
user: 'models.User',
|
||||||
|
userDeployment: UserDeployment
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Assigns from it internal assignable list to an user
|
Assigns from it internal assignable list to an user
|
||||||
@ -398,7 +398,7 @@ class Service(Module):
|
|||||||
"""
|
"""
|
||||||
Logs a message with requested level associated with this service
|
Logs a message with requested level associated with this service
|
||||||
"""
|
"""
|
||||||
from uds.models import Service as DBService
|
from uds.models import Service as DBService # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
if self.getUuid():
|
if self.getUuid():
|
||||||
log.doLog(
|
log.doLog(
|
||||||
|
@ -271,7 +271,9 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError('Base getUniqueId for User Deployment called!!!')
|
raise NotImplementedError('Base getUniqueId for User Deployment called!!!')
|
||||||
|
|
||||||
def notifyReadyFromOsManager(self, data: typing.Any) -> str:
|
def notifyReadyFromOsManager(
|
||||||
|
self, data: typing.Any # pylint: disable=unused-argument
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
This is a task method. As that, the excepted return values are
|
This is a task method. As that, the excepted return values are
|
||||||
State values RUNNING, FINISHED or ERROR.
|
State values RUNNING, FINISHED or ERROR.
|
||||||
@ -388,9 +390,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'Base deploy for cache invoked! for class {0}'.format(
|
f'Base deploy for cache invoked! for class {self.__class__.__name__}'
|
||||||
self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def deployForUser(self, user: 'models.User') -> str:
|
def deployForUser(self, user: 'models.User') -> str:
|
||||||
@ -427,9 +427,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Base deploy for user invoked! for class {0}'.format(
|
f'Base deploy for user invoked! for class {self.__class__.__name__}'
|
||||||
self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def checkState(self) -> str:
|
def checkState(self) -> str:
|
||||||
@ -456,7 +454,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'Base check state invoked! for class {0}'.format(self.__class__.__name__)
|
f'Base check state invoked! for class {self.__class__.__name__}'
|
||||||
)
|
)
|
||||||
|
|
||||||
def finish(self) -> None:
|
def finish(self) -> None:
|
||||||
@ -473,7 +471,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
nothing)
|
nothing)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def moveToCache(self, newLevel: int) -> str:
|
def moveToCache(self, newLevel: int) -> str: # pylint: disable=unused-argument
|
||||||
"""
|
"""
|
||||||
This method is invoked whenever the core needs to move from the current
|
This method is invoked whenever the core needs to move from the current
|
||||||
cache level to a new cache level an user deployment.
|
cache level to a new cache level an user deployment.
|
||||||
@ -564,7 +562,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
this method.
|
this method.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
'destroy method for class {0} not provided!'.format(self.__class__.__name__)
|
f'destroy method for class {self.__class__.__name__} not provided!'
|
||||||
)
|
)
|
||||||
|
|
||||||
def cancel(self) -> str:
|
def cancel(self) -> str:
|
||||||
@ -631,6 +629,7 @@ class UserDeployment(Environmentable, Serializable):
|
|||||||
monitors: number of monitors to use
|
monitors: number of monitors to use
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Mainly used for debugging purposses
|
Mainly used for debugging purposses
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# pylint: disable=unused-argument # this has a lot of "default" methods, so we need to ignore unused arguments most of the time
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2012-2022 Virtual Cable S.L.U.
|
# Copyright (c) 2012-2022 Virtual Cable S.L.U.
|
||||||
@ -30,7 +30,6 @@
|
|||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import codecs
|
import codecs
|
||||||
import logging
|
import logging
|
||||||
@ -156,7 +155,7 @@ class Transport(Module):
|
|||||||
Returns a customized error message, that will be used when a service fails to check "isAvailableFor"
|
Returns a customized error message, that will be used when a service fails to check "isAvailableFor"
|
||||||
Override this in yours transports if needed
|
Override this in yours transports if needed
|
||||||
"""
|
"""
|
||||||
return "Not accessible (using service ip {})".format(ip)
|
return f'Not accessible (using service ip {ip})'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def supportsProtocol(cls, protocol: typing.Union[typing.Iterable, str]):
|
def supportsProtocol(cls, protocol: typing.Union[typing.Iterable, str]):
|
||||||
@ -229,7 +228,7 @@ class Transport(Module):
|
|||||||
userService: 'models.UserService',
|
userService: 'models.UserService',
|
||||||
transport: 'models.Transport',
|
transport: 'models.Transport',
|
||||||
ip: str,
|
ip: str,
|
||||||
os: 'DetectedOsInfo',
|
os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
|
||||||
user: 'models.User',
|
user: 'models.User',
|
||||||
password: str,
|
password: str,
|
||||||
request: 'ExtendedHttpRequestWithUser',
|
request: 'ExtendedHttpRequestWithUser',
|
||||||
@ -263,7 +262,7 @@ class Transport(Module):
|
|||||||
userService: 'models.UserService',
|
userService: 'models.UserService',
|
||||||
transport: 'models.Transport',
|
transport: 'models.Transport',
|
||||||
ip: str,
|
ip: str,
|
||||||
os: 'DetectedOsInfo',
|
os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
|
||||||
user: 'models.User',
|
user: 'models.User',
|
||||||
password: str,
|
password: str,
|
||||||
request: 'ExtendedHttpRequestWithUser',
|
request: 'ExtendedHttpRequestWithUser',
|
||||||
@ -296,12 +295,16 @@ class Transport(Module):
|
|||||||
params: Parameters for the return tuple
|
params: Parameters for the return tuple
|
||||||
"""
|
"""
|
||||||
# Reads script and signature
|
# Reads script and signature
|
||||||
|
import os # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
basePath = os.path.dirname(
|
basePath = os.path.dirname(
|
||||||
sys.modules[self.__module__].__file__ or 'not_found'
|
sys.modules[self.__module__].__file__ or 'not_found'
|
||||||
) # Will raise if not found
|
) # Will raise if not found
|
||||||
|
|
||||||
script = open(os.path.join(basePath, scriptName), 'r').read()
|
with open(os.path.join(basePath, scriptName), 'r', encoding='utf8') as scriptFile:
|
||||||
signature = open(os.path.join(basePath, scriptName + '.signature'), 'r').read()
|
script = scriptFile.read()
|
||||||
|
with open(os.path.join(basePath, scriptName + '.signature'), 'r', encoding='utf8') as signatureFile:
|
||||||
|
signature = signatureFile.read()
|
||||||
|
|
||||||
return TransportScript(
|
return TransportScript(
|
||||||
script=script,
|
script=script,
|
||||||
@ -320,7 +323,7 @@ class Transport(Module):
|
|||||||
Returns a script for the given os and type
|
Returns a script for the given os and type
|
||||||
"""
|
"""
|
||||||
return self.getRelativeScript(
|
return self.getRelativeScript(
|
||||||
'scripts/{os}/{type}.py'.format(os=osName, type=type), params
|
f'scripts/{osName}/{type}.py', params
|
||||||
)
|
)
|
||||||
|
|
||||||
def getLink(
|
def getLink(
|
||||||
@ -328,7 +331,7 @@ class Transport(Module):
|
|||||||
userService: 'models.UserService',
|
userService: 'models.UserService',
|
||||||
transport: 'models.Transport',
|
transport: 'models.Transport',
|
||||||
ip: str,
|
ip: str,
|
||||||
os: 'DetectedOsInfo',
|
os: 'DetectedOsInfo', # pylint: disable=redefined-outer-name
|
||||||
user: 'models.User',
|
user: 'models.User',
|
||||||
password: str,
|
password: str,
|
||||||
request: 'ExtendedHttpRequestWithUser',
|
request: 'ExtendedHttpRequestWithUser',
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
# Allow old serialized autoattributes to load correctly (register path...)
|
# Allow old serialized autoattributes to load correctly (register path...)
|
||||||
|
|
||||||
|
# pylint: disable=unused-wildcard-import,wildcard-import
|
||||||
from .auto_attributes import *
|
from .auto_attributes import *
|
||||||
|
@ -105,7 +105,9 @@ class AutoAttributes(Serializable):
|
|||||||
return
|
return
|
||||||
# We keep original data (maybe incomplete)
|
# We keep original data (maybe incomplete)
|
||||||
if data[:2] == b'v1':
|
if data[:2] == b'v1':
|
||||||
self.attrs = pickle.loads(data[2:]) # nosec: pickle is used to load data from trusted source
|
self.attrs = pickle.loads(
|
||||||
|
data[2:]
|
||||||
|
) # nosec: pickle is used to load data from trusted source
|
||||||
return
|
return
|
||||||
# We try to load as v0
|
# We try to load as v0
|
||||||
try:
|
try:
|
||||||
@ -117,16 +119,18 @@ class AutoAttributes(Serializable):
|
|||||||
k, v = pair.split(b'\1')
|
k, v = pair.split(b'\1')
|
||||||
# logger.debug('k: %s --- v: %s', k, v)
|
# logger.debug('k: %s --- v: %s', k, v)
|
||||||
try:
|
try:
|
||||||
self.attrs[k.decode()] = pickle.loads(v) # nosec: pickle is used to load data from trusted source
|
self.attrs[k.decode()] = pickle.loads(
|
||||||
|
v
|
||||||
|
) # nosec: pickle is used to load data from trusted source
|
||||||
except Exception: # Old encoding on python2, set encoding for loading
|
except Exception: # Old encoding on python2, set encoding for loading
|
||||||
self.attrs[k.decode()] = pickle.loads(v, encoding='utf8') # nosec: pickle is used to load data from trusted source
|
self.attrs[k.decode()] = pickle.loads(
|
||||||
|
v, encoding='utf8'
|
||||||
|
) # nosec: pickle is used to load data from trusted source
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
'AutoAttributes('
|
'AutoAttributes('
|
||||||
+ ', '.join(
|
+ ', '.join(f'{k}={v.getType().__name__}' for k, v in self.attrs.items())
|
||||||
'{}={}'.format(k, v.getType().__name__) for k, v in self.attrs.items()
|
|
||||||
)
|
|
||||||
+ ')'
|
+ ')'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -134,7 +138,7 @@ class AutoAttributes(Serializable):
|
|||||||
return (
|
return (
|
||||||
'<AutoAttribute '
|
'<AutoAttribute '
|
||||||
+ ','.join(
|
+ ','.join(
|
||||||
'{} ({}) = {}'.format(k, v.getType(), v.getStrValue())
|
f'{k} ({v.getType()}) = {v.getStrValue()}'
|
||||||
for k, v in self.attrs.items()
|
for k, v in self.attrs.items()
|
||||||
)
|
)
|
||||||
+ '>'
|
+ '>'
|
||||||
|
@ -59,6 +59,7 @@ from cryptography import fernet
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
class _Unassigned:
|
class _Unassigned:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -97,7 +98,7 @@ def fernet_key(crypt_key: bytes) -> str:
|
|||||||
# First, with seed + password, generate a 32 bytes key based on seed + password
|
# First, with seed + password, generate a 32 bytes key based on seed + password
|
||||||
return base64.b64encode(hashlib.sha256(crypt_key).digest()).decode()
|
return base64.b64encode(hashlib.sha256(crypt_key).digest()).decode()
|
||||||
|
|
||||||
|
# pylint: disable=unnecessary-dunder-call
|
||||||
class _SerializableField(typing.Generic[T]):
|
class _SerializableField(typing.Generic[T]):
|
||||||
name: str
|
name: str
|
||||||
type: typing.Type[T]
|
type: typing.Type[T]
|
||||||
@ -110,10 +111,9 @@ class _SerializableField(typing.Generic[T]):
|
|||||||
def _default(self) -> T:
|
def _default(self) -> T:
|
||||||
if isinstance(self.default, _Unassigned):
|
if isinstance(self.default, _Unassigned):
|
||||||
return self.type()
|
return self.type()
|
||||||
elif callable(self.default):
|
if callable(self.default):
|
||||||
return self.default()
|
return self.default()
|
||||||
else:
|
return self.default
|
||||||
return self.default
|
|
||||||
|
|
||||||
def __get__(
|
def __get__(
|
||||||
self,
|
self,
|
||||||
@ -309,13 +309,12 @@ class PasswordField(StringField):
|
|||||||
class _FieldNameSetter(type):
|
class _FieldNameSetter(type):
|
||||||
"""Simply adds the name of the field in the class to the field object"""
|
"""Simply adds the name of the field in the class to the field object"""
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(mcs, name, bases, attrs):
|
||||||
fields = dict()
|
|
||||||
for k, v in attrs.items():
|
for k, v in attrs.items():
|
||||||
if isinstance(v, _SerializableField):
|
if isinstance(v, _SerializableField):
|
||||||
v.name = k
|
v.name = k
|
||||||
|
|
||||||
return super().__new__(cls, name, bases, attrs)
|
return super().__new__(mcs, name, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
class AutoSerializable(metaclass=_FieldNameSetter):
|
class AutoSerializable(metaclass=_FieldNameSetter):
|
||||||
@ -346,8 +345,6 @@ class AutoSerializable(metaclass=_FieldNameSetter):
|
|||||||
"""
|
"""
|
||||||
return bytes(a ^ b for a, b in zip(data, itertools.cycle(header)))
|
return bytes(a ^ b for a, b in zip(data, itertools.cycle(header)))
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def unprocess_data(self, header: bytes, data: bytes) -> bytes:
|
def unprocess_data(self, header: bytes, data: bytes) -> bytes:
|
||||||
"""Process data after unmarshalling
|
"""Process data after unmarshalling
|
||||||
|
|
||||||
@ -367,7 +364,7 @@ class AutoSerializable(metaclass=_FieldNameSetter):
|
|||||||
def marshal(self) -> bytes:
|
def marshal(self) -> bytes:
|
||||||
# Iterate over own members and extract fields
|
# Iterate over own members and extract fields
|
||||||
fields = {}
|
fields = {}
|
||||||
for k, v in self.__class__.__dict__.items():
|
for _, v in self.__class__.__dict__.items():
|
||||||
if isinstance(v, _SerializableField):
|
if isinstance(v, _SerializableField):
|
||||||
fields[v.name] = (str(v.__class__.__name__), v.marshal(self))
|
fields[v.name] = (str(v.__class__.__name__), v.marshal(self))
|
||||||
|
|
||||||
@ -435,16 +432,17 @@ class AutoSerializable(metaclass=_FieldNameSetter):
|
|||||||
# Remove from data
|
# Remove from data
|
||||||
data = data[8 + name_len + type_name_len + data_len :]
|
data = data[8 + name_len + type_name_len + data_len :]
|
||||||
|
|
||||||
for k, v in self.__class__.__dict__.items():
|
for _, v in self.__class__.__dict__.items():
|
||||||
if isinstance(v, _SerializableField):
|
if isinstance(v, _SerializableField):
|
||||||
if v.name in fields and fields[v.name][0] == str(v.__class__.__name__):
|
if v.name in fields and fields[v.name][0] == str(v.__class__.__name__):
|
||||||
v.unmarshal(self, fields[v.name][1])
|
v.unmarshal(self, fields[v.name][1])
|
||||||
else:
|
else:
|
||||||
if not v.name in fields:
|
if not v.name in fields:
|
||||||
logger.warning(f"Field {v.name} not found in unmarshalled data")
|
logger.warning('Field %s not found in unmarshalled data', v.name)
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Field {v.name} has wrong type in unmarshalled data (should be {fields[v.name][0]} and is {v.__class__.name}"
|
'Field %s has wrong type in unmarshalled data (should be %s and is %s',
|
||||||
|
v.name, fields[v.name][0], v.__class__.__name__,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other: typing.Any) -> bool:
|
def __eq__(self, other: typing.Any) -> bool:
|
||||||
|
@ -107,10 +107,9 @@ class Cache:
|
|||||||
Cache.misses += 1
|
Cache.misses += 1
|
||||||
# logger.debug('key not found: %s', skey)
|
# logger.debug('key not found: %s', skey)
|
||||||
return defValue
|
return defValue
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception('Error getting cache key: %s', skey)
|
logger.exception('Error getting cache key: %s', skey)
|
||||||
Cache.misses += 1
|
Cache.misses += 1
|
||||||
# logger.debug('Cache inaccesible: %s:%s', skey, e)
|
|
||||||
return defValue
|
return defValue
|
||||||
|
|
||||||
def __getitem__(self, key: typing.Union[str, bytes]) -> typing.Any:
|
def __getitem__(self, key: typing.Union[str, bytes]) -> typing.Any:
|
||||||
|
@ -55,7 +55,7 @@ ONE_DAY = 3600 * 24
|
|||||||
|
|
||||||
class CalendarChecker:
|
class CalendarChecker:
|
||||||
__slots__ = ('calendar',)
|
__slots__ = ('calendar',)
|
||||||
|
|
||||||
calendar: Calendar
|
calendar: Calendar
|
||||||
|
|
||||||
# For performance checking
|
# For performance checking
|
||||||
@ -225,13 +225,12 @@ class CalendarChecker:
|
|||||||
return next_event
|
return next_event
|
||||||
|
|
||||||
def debug(self) -> str:
|
def debug(self) -> str:
|
||||||
return "Calendar checker for {}".format(self.calendar)
|
return f'Calendar checker for {self.calendar}'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _cacheKey(key: str) -> str:
|
def _cacheKey(key: str) -> str:
|
||||||
# Returns a valid cache key for all caching backends (memcached, redis, or whatever)
|
# Returns a valid cache key for all caching backends (memcached, redis, or whatever)
|
||||||
# Simple, fastest algorihm is to use md5
|
# Simple, fastest algorihm is to use md5
|
||||||
h = hashlib.md5() # nosec Thisis just a simpele cache key, no need to use a more secure algorithm
|
return hashlib.md5(
|
||||||
h.update(key.encode('utf-8'))
|
key.encode('utf-8')
|
||||||
return h.hexdigest()
|
).hexdigest() # nosec simple fast algorithm for cache keys
|
||||||
|
|
||||||
|
@ -150,10 +150,10 @@ class Config:
|
|||||||
self._data = readed.value
|
self._data = readed.value
|
||||||
self._crypt = readed.crypt or self._crypt
|
self._crypt = readed.crypt or self._crypt
|
||||||
self._longText = readed.long
|
self._longText = readed.long
|
||||||
if self._type != -1 and self._type != readed.field_type:
|
if self._type not in (-1, readed.field_type):
|
||||||
readed.field_type = self._type
|
readed.field_type = self._type
|
||||||
readed.save(update_fields=['field_type'])
|
readed.save(update_fields=['field_type'])
|
||||||
if self._help != '' and self._help != readed.help:
|
if self._help not in ('', readed.help):
|
||||||
readed.help = self._help
|
readed.help = self._help
|
||||||
readed.save(
|
readed.save(
|
||||||
update_fields=['help']
|
update_fields=['help']
|
||||||
@ -169,9 +169,7 @@ class Config:
|
|||||||
self._data = self._default
|
self._data = self._default
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(
|
logger.info(
|
||||||
'Error accessing db config {0}.{1}'.format(
|
'Error accessing db config %s.%s', self._section.name(), self._key
|
||||||
self._section.name(), self._key
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
self._data = self._default
|
self._data = self._default
|
||||||
@ -273,7 +271,7 @@ class Config:
|
|||||||
self._data = value
|
self._data = value
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return '{}.{}'.format(self._section.name(), self._key)
|
return f'{self._section.name()}.{self._key}'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def section(sectionName):
|
def section(sectionName):
|
||||||
@ -314,7 +312,10 @@ class Config:
|
|||||||
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[
|
cfg: DBConfig = DBConfig.objects.filter(section=section, key=key)[
|
||||||
0 # type: ignore # Slicing is not supported by pylance right now
|
0 # type: ignore # Slicing is not supported by pylance right now
|
||||||
]
|
]
|
||||||
if checkType and cfg.field_type in (Config.FieldType.READ, Config.FieldType.HIDDEN):
|
if checkType and cfg.field_type in (
|
||||||
|
Config.FieldType.READ,
|
||||||
|
Config.FieldType.HIDDEN,
|
||||||
|
):
|
||||||
return False # Skip non writable elements
|
return False # Skip non writable elements
|
||||||
|
|
||||||
if cfg.crypt:
|
if cfg.crypt:
|
||||||
@ -565,14 +566,18 @@ class GlobalConfig:
|
|||||||
'statsAccumFrequency',
|
'statsAccumFrequency',
|
||||||
'14400',
|
'14400',
|
||||||
type=Config.FieldType.NUMERIC,
|
type=Config.FieldType.NUMERIC,
|
||||||
help=_('Frequency of stats collection in seconds. Default is 4 hours (14400 seconds)'),
|
help=_(
|
||||||
|
'Frequency of stats collection in seconds. Default is 4 hours (14400 seconds)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
# Statisctis accumulation chunk size, in days
|
# Statisctis accumulation chunk size, in days
|
||||||
STATS_ACCUM_MAX_CHUNK_TIME = Config.section(GLOBAL_SECTION).value(
|
STATS_ACCUM_MAX_CHUNK_TIME = Config.section(GLOBAL_SECTION).value(
|
||||||
'statsAccumMaxChunkTime',
|
'statsAccumMaxChunkTime',
|
||||||
'7',
|
'7',
|
||||||
type=Config.FieldType.NUMERIC,
|
type=Config.FieldType.NUMERIC,
|
||||||
help=_('Maximum number of time to accumulate on one run. Default is 7 (1 week)'),
|
help=_(
|
||||||
|
'Maximum number of time to accumulate on one run. Default is 7 (1 week)'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# If disallow login showing authenticatiors
|
# If disallow login showing authenticatiors
|
||||||
@ -716,7 +721,10 @@ class GlobalConfig:
|
|||||||
help=_('Custom CSS styles applied to the user accesible site'),
|
help=_('Custom CSS styles applied to the user accesible site'),
|
||||||
)
|
)
|
||||||
SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value(
|
SITE_INFO: Config.Value = Config.section(CUSTOM_SECTION).value(
|
||||||
'Site information', '', type=Config.FieldType.LONGTEXT, help=_('Site information')
|
'Site information',
|
||||||
|
'',
|
||||||
|
type=Config.FieldType.LONGTEXT,
|
||||||
|
help=_('Site information'),
|
||||||
)
|
)
|
||||||
SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value(
|
SITE_FILTER_ONTOP: Config.Value = Config.section(CUSTOM_SECTION).value(
|
||||||
'Show Filter on Top',
|
'Show Filter on Top',
|
||||||
@ -781,7 +789,9 @@ class GlobalConfig:
|
|||||||
for v in GlobalConfig.__dict__.values():
|
for v in GlobalConfig.__dict__.values():
|
||||||
if isinstance(v, Config.Value):
|
if isinstance(v, Config.Value):
|
||||||
v.get()
|
v.get()
|
||||||
logger.debug('Initialized global config value %s=%s', v.key(), v.get())
|
logger.debug(
|
||||||
|
'Initialized global config value %s=%s', v.key(), v.get()
|
||||||
|
)
|
||||||
|
|
||||||
for c in _getLater:
|
for c in _getLater:
|
||||||
logger.debug('Get later: %s', c)
|
logger.debug('Get later: %s', c)
|
||||||
|
@ -30,11 +30,7 @@
|
|||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import typing
|
|
||||||
import logging
|
|
||||||
import socket
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
# Import alias
|
||||||
|
# pylint: disable=unused-import
|
||||||
# import Alias
|
|
||||||
from .net import testConnection as testServer
|
from .net import testConnection as testServer
|
||||||
|
@ -247,7 +247,7 @@ def allowCache(
|
|||||||
keyFnc(args[0] if len(args) > 0 else fnc.__name__).encode('utf-8')
|
keyFnc(args[0] if len(args) > 0 else fnc.__name__).encode('utf-8')
|
||||||
)
|
)
|
||||||
# compute cache key
|
# compute cache key
|
||||||
cacheKey = '{}-{}'.format(cachePrefix, keyHash.hexdigest())
|
cacheKey = f'{cachePrefix}-{keyHash.hexdigest()}'
|
||||||
|
|
||||||
cache = getattr(args[0], 'cache', None) or Cache('functionCache')
|
cache = getattr(args[0], 'cache', None) or Cache('functionCache')
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ def ensure_list(obj: typing.Union[T, typing.Iterable[T]]) -> typing.List[T]:
|
|||||||
return [typing.cast(T, obj)]
|
return [typing.cast(T, obj)]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return [_ for _ in typing.cast(typing.List[T], obj)]
|
return list(typing.cast(typing.List[T], obj))
|
||||||
except Exception: # Not iterable
|
except Exception: # Not iterable
|
||||||
return [typing.cast(T, obj)]
|
return [typing.cast(T, obj)]
|
||||||
|
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
import typing
|
import typing
|
||||||
|
import logging
|
||||||
|
|
||||||
from uds.core.util import singleton
|
from uds.core.util import singleton
|
||||||
from uds.core import module
|
from uds.core import module
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
T = typing.TypeVar('T', bound=module.Module)
|
T = typing.TypeVar('T', bound=module.Module)
|
||||||
V = typing.TypeVar('V')
|
V = typing.TypeVar('V')
|
||||||
|
|
||||||
|
|
||||||
class Factory(typing.Generic[V], metaclass=singleton.Singleton):
|
class Factory(typing.Generic[V], metaclass=singleton.Singleton):
|
||||||
'''
|
'''
|
||||||
Generic factory class.
|
Generic factory class.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
_objects: typing.MutableMapping[str, typing.Type[V]]
|
_objects: typing.MutableMapping[str, typing.Type[V]]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -46,11 +48,11 @@ class Factory(typing.Generic[V], metaclass=singleton.Singleton):
|
|||||||
__getitem__ = get
|
__getitem__ = get
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleFactory(Factory[T]):
|
class ModuleFactory(Factory[T]):
|
||||||
'''
|
'''
|
||||||
Module Factory class.
|
Module Factory class.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def providers(self) -> typing.Mapping[str, typing.Type[T]]:
|
def providers(self) -> typing.Mapping[str, typing.Type[T]]:
|
||||||
'''
|
'''
|
||||||
Returns all providers.
|
Returns all providers.
|
||||||
|
@ -15,12 +15,13 @@
|
|||||||
|
|
||||||
# Modified to add type checking, fix bugs, etc.. by dkmaster@dkmon.com
|
# Modified to add type checking, fix bugs, etc.. by dkmaster@dkmon.com
|
||||||
|
|
||||||
|
# pylint: disable=protected-access,too-few-public-methods,unused-argument
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import ctypes
|
import ctypes
|
||||||
import errno
|
import errno
|
||||||
import typing
|
import typing
|
||||||
import warnings
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ctypes.util import find_library
|
from ctypes.util import find_library
|
||||||
@ -44,29 +45,26 @@ if _system == 'Windows':
|
|||||||
#
|
#
|
||||||
# We have to fix up c_long and c_ulong so that it matches the
|
# We have to fix up c_long and c_ulong so that it matches the
|
||||||
# Cygwin (and UNIX) sizes when run on Windows.
|
# Cygwin (and UNIX) sizes when run on Windows.
|
||||||
import sys
|
|
||||||
|
|
||||||
if sys.maxsize > 0xFFFFFFFF:
|
if sys.maxsize > 0xFFFFFFFF:
|
||||||
c_win_long = ctypes.c_int64
|
c_win_long = ctypes.c_int64
|
||||||
c_win_ulong = ctypes.c_uint64
|
c_win_ulong = ctypes.c_uint64
|
||||||
|
|
||||||
if _system == 'Windows' or _system.startswith('CYGWIN'):
|
if _system == 'Windows' or _system.startswith('CYGWIN'):
|
||||||
|
|
||||||
class c_timespec(ctypes.Structure): # type: ignore
|
class c_timespec(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
|
||||||
_fields_ = [('tv_sec', c_win_long), ('tv_nsec', c_win_long)]
|
_fields_ = [('tv_sec', c_win_long), ('tv_nsec', c_win_long)]
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
class c_timespec(ctypes.Structure): # type: ignore
|
class c_timespec(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
|
||||||
_fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
|
_fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
|
||||||
|
|
||||||
|
|
||||||
class c_utimbuf(ctypes.Structure):
|
class c_utimbuf(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
|
||||||
_fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
|
_fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
|
||||||
|
|
||||||
|
|
||||||
class c_stat(ctypes.Structure):
|
class c_stat(ctypes.Structure): # type: ignore # pylint: disable=too-few-public-methods
|
||||||
pass # Platform dependent
|
pass # Platform dependent
|
||||||
|
|
||||||
|
|
||||||
@ -92,7 +90,7 @@ if not _libfuse_path:
|
|||||||
rootkey, keyname, 0, reg.KEY_READ | reg.KEY_WOW64_32KEY # type: ignore
|
rootkey, keyname, 0, reg.KEY_READ | reg.KEY_WOW64_32KEY # type: ignore
|
||||||
)
|
)
|
||||||
val = str(reg.QueryValueEx(key, valname)[0]) # type: ignore
|
val = str(reg.QueryValueEx(key, valname)[0]) # type: ignore
|
||||||
except WindowsError: # type: ignore
|
except WindowsError: # type: ignore # pylint: disable=undefined-variable
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
if key is not None:
|
if key is not None:
|
||||||
@ -103,16 +101,19 @@ if not _libfuse_path:
|
|||||||
reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WinFsp", r"InstallDir" # type: ignore
|
reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WinFsp", r"InstallDir" # type: ignore
|
||||||
)
|
)
|
||||||
if _libfuse_path:
|
if _libfuse_path:
|
||||||
_libfuse_path += r"bin\winfsp-%s.dll" % (
|
_libfuse_path += (
|
||||||
"x64" if sys.maxsize > 0xFFFFFFFF else "x86"
|
r"bin\winfsp-%s.dll" # pylint: disable=consider-using-f-string
|
||||||
|
% ( # pylint: disable=consider-using-f-string
|
||||||
|
"x64" if sys.maxsize > 0xFFFFFFFF else "x86"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_libfuse_path = find_library('fuse')
|
_libfuse_path = find_library('fuse')
|
||||||
|
|
||||||
if not _libfuse_path:
|
if not _libfuse_path:
|
||||||
raise EnvironmentError('Unable to find libfuse')
|
raise EnvironmentError('Unable to find libfuse')
|
||||||
else:
|
|
||||||
_libfuse = ctypes.CDLL(_libfuse_path)
|
_libfuse = ctypes.CDLL(_libfuse_path)
|
||||||
|
|
||||||
if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
|
if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
|
||||||
_system = 'Darwin-MacFuse'
|
_system = 'Darwin-MacFuse'
|
||||||
@ -131,7 +132,6 @@ getxattr_t: typing.Type
|
|||||||
|
|
||||||
if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
|
if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
|
||||||
ENOTSUP = 45
|
ENOTSUP = 45
|
||||||
|
|
||||||
c_dev_t = ctypes.c_int32
|
c_dev_t = ctypes.c_int32
|
||||||
c_fsblkcnt_t = ctypes.c_ulong
|
c_fsblkcnt_t = ctypes.c_ulong
|
||||||
c_fsfilcnt_t = ctypes.c_ulong
|
c_fsfilcnt_t = ctypes.c_ulong
|
||||||
@ -295,7 +295,7 @@ elif _system == 'Linux':
|
|||||||
('st_mtimespec', c_timespec),
|
('st_mtimespec', c_timespec),
|
||||||
('st_ctimespec', c_timespec),
|
('st_ctimespec', c_timespec),
|
||||||
]
|
]
|
||||||
elif _machine == 'ppc64' or _machine == 'ppc64le':
|
elif _machine in ('ppc64', 'ppc64le'):
|
||||||
c_stat._fields_ = [
|
c_stat._fields_ = [
|
||||||
('st_dev', c_dev_t),
|
('st_dev', c_dev_t),
|
||||||
('st_ino', ctypes.c_ulong),
|
('st_ino', ctypes.c_ulong),
|
||||||
@ -392,7 +392,7 @@ elif _system == 'Windows' or _system.startswith('CYGWIN'):
|
|||||||
('st_birthtimespec', c_timespec),
|
('st_birthtimespec', c_timespec),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('%s is not supported.' % _system)
|
raise NotImplementedError(f'{_system} is not supported.')
|
||||||
|
|
||||||
|
|
||||||
if _system == 'FreeBSD':
|
if _system == 'FreeBSD':
|
||||||
@ -428,7 +428,6 @@ if _system == 'FreeBSD':
|
|||||||
('f_frsize', ctypes.c_ulong),
|
('f_frsize', ctypes.c_ulong),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
elif _system == 'Windows' or _system.startswith('CYGWIN'):
|
elif _system == 'Windows' or _system.startswith('CYGWIN'):
|
||||||
|
|
||||||
class c_statvfs(ctypes.Structure): # type: ignore
|
class c_statvfs(ctypes.Structure): # type: ignore
|
||||||
@ -446,7 +445,6 @@ elif _system == 'Windows' or _system.startswith('CYGWIN'):
|
|||||||
('f_namemax', c_win_ulong),
|
('f_namemax', c_win_ulong),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
class c_statvfs(ctypes.Structure): # type: ignore
|
class c_statvfs(ctypes.Structure): # type: ignore
|
||||||
@ -481,7 +479,6 @@ if _system == 'Windows' or _system.startswith('CYGWIN'):
|
|||||||
('lock_owner', ctypes.c_uint64),
|
('lock_owner', ctypes.c_uint64),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
class fuse_file_info(ctypes.Structure): # type: ignore
|
class fuse_file_info(ctypes.Structure): # type: ignore
|
||||||
@ -713,7 +710,7 @@ class FuseOperations(ctypes.Structure):
|
|||||||
|
|
||||||
|
|
||||||
def time_of_timespec(ts: c_timespec, use_ns=False):
|
def time_of_timespec(ts: c_timespec, use_ns=False):
|
||||||
return ts.tv_sec * 10 ** 9 + ts.tv_nsec # type: ignore
|
return ts.tv_sec * 10**9 + ts.tv_nsec # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None:
|
def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None:
|
||||||
@ -723,7 +720,7 @@ def set_st_attrs(st: c_stat, attrs: typing.Mapping[str, int]) -> None:
|
|||||||
if timespec is None:
|
if timespec is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
timespec.tv_sec, timespec.tv_nsec = divmod(int(val), 10 ** 9)
|
timespec.tv_sec, timespec.tv_nsec = divmod(int(val), 10**9)
|
||||||
elif hasattr(st, key):
|
elif hasattr(st, key):
|
||||||
setattr(st, key, val)
|
setattr(st, key, val)
|
||||||
|
|
||||||
@ -749,10 +746,11 @@ def fuse_exit():
|
|||||||
|
|
||||||
|
|
||||||
class FuseOSError(OSError):
|
class FuseOSError(OSError):
|
||||||
def __init__(self, errno):
|
def __init__(self, errno): # pylint: disable=redefined-outer-name
|
||||||
super(FuseOSError, self).__init__(errno, os.strerror(errno))
|
super().__init__(errno, os.strerror(errno))
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-public-methods
|
||||||
class FUSE:
|
class FUSE:
|
||||||
'''
|
'''
|
||||||
This class is the lower level interface and should not be subclassed under
|
This class is the lower level interface and should not be subclassed under
|
||||||
@ -776,7 +774,6 @@ class FUSE:
|
|||||||
encoding: typing.Optional[str] = None,
|
encoding: typing.Optional[str] = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Setting raw_fi to True will cause FUSE to pass the fuse_file_info
|
Setting raw_fi to True will cause FUSE to pass the fuse_file_info
|
||||||
class as is to Operations, instead of just the fh field.
|
class as is to Operations, instead of just the fh field.
|
||||||
@ -839,8 +836,8 @@ class FUSE:
|
|||||||
|
|
||||||
del self.operations # Invoke the destructor
|
del self.operations # Invoke the destructor
|
||||||
|
|
||||||
if type(self.__critical_exception) is not Exception:
|
if not isinstance(self.__critical_exception, Exception):
|
||||||
raise self.__critical_exception
|
raise self.__critical_exception # type: ignore
|
||||||
if err:
|
if err:
|
||||||
raise RuntimeError(err)
|
raise RuntimeError(err)
|
||||||
|
|
||||||
@ -863,37 +860,35 @@ class FUSE:
|
|||||||
# private_data field of struct fuse_context
|
# private_data field of struct fuse_context
|
||||||
return func(*args, **kwargs) or 0
|
return func(*args, **kwargs) or 0
|
||||||
|
|
||||||
else:
|
try:
|
||||||
try:
|
return func(*args, **kwargs) or 0
|
||||||
return func(*args, **kwargs) or 0
|
|
||||||
|
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if e.errno > 0:
|
if e.errno > 0:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"FUSE operation %s raised a %s, returning errno %s.",
|
"FUSE operation %s raised a %s, returning errno %s.",
|
||||||
func.__name__,
|
|
||||||
type(e),
|
|
||||||
e.errno,
|
|
||||||
)
|
|
||||||
return -e.errno
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"FUSE operation %s raised an OSError with negative "
|
|
||||||
"errno %s, returning errno.EINVAL.",
|
|
||||||
func.__name__,
|
|
||||||
e.errno,
|
|
||||||
exc_info=True,
|
|
||||||
)
|
|
||||||
return -errno.EINVAL
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
logger.error(
|
|
||||||
"Uncaught exception from FUSE operation %s, "
|
|
||||||
"returning errno.EINVAL.",
|
|
||||||
func.__name__,
|
func.__name__,
|
||||||
exc_info=True,
|
type(e),
|
||||||
|
e.errno,
|
||||||
)
|
)
|
||||||
return -errno.EINVAL
|
return -e.errno
|
||||||
|
logger.error(
|
||||||
|
"FUSE operation %s raised an OSError with negative "
|
||||||
|
"errno %s, returning errno.EINVAL.",
|
||||||
|
func.__name__,
|
||||||
|
e.errno,
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
return -errno.EINVAL
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.error(
|
||||||
|
"Uncaught exception from FUSE operation %s, "
|
||||||
|
"returning errno.EINVAL.",
|
||||||
|
func.__name__,
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
return -errno.EINVAL
|
||||||
|
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
if len(args) > 0 and isinstance(args[0], FUSE):
|
if len(args) > 0 and isinstance(args[0], FUSE):
|
||||||
@ -1003,9 +998,7 @@ class FUSE:
|
|||||||
logger.debug('Read operation on %s returned %d bytes', path, retsize)
|
logger.debug('Read operation on %s returned %d bytes', path, retsize)
|
||||||
|
|
||||||
if retsize > size:
|
if retsize > size:
|
||||||
raise RuntimeError(
|
raise RuntimeError(f'read too much data ({retsize} bytes, expected {size})')
|
||||||
"read too much data ({} bytes, expected {})".format(retsize, size)
|
|
||||||
)
|
|
||||||
|
|
||||||
ctypes.memmove(buf, ret, retsize)
|
ctypes.memmove(buf, ret, retsize)
|
||||||
return retsize
|
return retsize
|
||||||
@ -1271,8 +1264,8 @@ class Operations:
|
|||||||
def __call__(self, op: str, *args) -> typing.Any:
|
def __call__(self, op: str, *args) -> typing.Any:
|
||||||
try:
|
try:
|
||||||
return getattr(self, op)(*args)
|
return getattr(self, op)(*args)
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
raise FuseOSError(errno.EFAULT)
|
raise FuseOSError(errno.EFAULT) from e
|
||||||
|
|
||||||
def access(self, path: str, amode: int) -> None:
|
def access(self, path: str, amode: int) -> None:
|
||||||
return
|
return
|
||||||
@ -1297,8 +1290,7 @@ class Operations:
|
|||||||
raise FuseOSError(errno.EROFS)
|
raise FuseOSError(errno.EROFS)
|
||||||
|
|
||||||
def destroy(self, path: str) -> None:
|
def destroy(self, path: str) -> None:
|
||||||
'Called on filesystem destruction. Path is always /'
|
'''Called on filesystem destruction. Path is always /'''
|
||||||
pass
|
|
||||||
|
|
||||||
def flush(self, path: typing.Optional[str], fh: typing.Any) -> None:
|
def flush(self, path: typing.Optional[str], fh: typing.Any) -> None:
|
||||||
pass
|
pass
|
||||||
@ -1325,7 +1317,7 @@ class Operations:
|
|||||||
|
|
||||||
if path != '/':
|
if path != '/':
|
||||||
raise FuseOSError(errno.ENOENT)
|
raise FuseOSError(errno.ENOENT)
|
||||||
return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2)
|
return {'st_mode': (S_IFDIR | 0o755), 'st_nlink': 2}
|
||||||
|
|
||||||
def getxattr(self, path: str, name: str, position: int = 0) -> str:
|
def getxattr(self, path: str, name: str, position: int = 0) -> str:
|
||||||
raise FuseOSError(ENOTSUP)
|
raise FuseOSError(ENOTSUP)
|
||||||
@ -1336,7 +1328,6 @@ class Operations:
|
|||||||
|
|
||||||
Use it instead of __init__ if you start threads on initialization.
|
Use it instead of __init__ if you start threads on initialization.
|
||||||
'''
|
'''
|
||||||
pass
|
|
||||||
|
|
||||||
def ioctl(
|
def ioctl(
|
||||||
self, path: str, cmd: bytes, arg: bytes, fip: int, flags: int, data: bytes
|
self, path: str, cmd: bytes, arg: bytes, fip: int, flags: int, data: bytes
|
||||||
|
@ -50,6 +50,6 @@ def hash_key(key: typing.Union[str, bytes]) -> str:
|
|||||||
Returns a hash of the given key
|
Returns a hash of the given key
|
||||||
"""
|
"""
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
return hasher(key.encode('utf-8')).hexdigest()
|
key = key.encode('utf-8')
|
||||||
|
|
||||||
return hasher(key).hexdigest()
|
return hasher(key).hexdigest()
|
||||||
|
@ -43,24 +43,23 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def udsLink(request: 'HttpRequest', ticket: str, scrambler: str) -> str:
|
def udsLink(request: 'HttpRequest', ticket: str, scrambler: str) -> str:
|
||||||
|
|
||||||
if request.is_secure():
|
if request.is_secure():
|
||||||
proto = 'udss'
|
proto = 'udss'
|
||||||
else:
|
else:
|
||||||
proto = 'uds'
|
proto = 'uds'
|
||||||
|
|
||||||
return "{}://{}{}/{}".format(
|
return f'{proto}://{request.build_absolute_uri("/")}{ticket}/{scrambler}'
|
||||||
proto, request.build_absolute_uri('/').split('//')[1], ticket, scrambler
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def udsAccessLink(
|
def udsAccessLink(
|
||||||
request: 'HttpRequest', serviceId: str, transportId: typing.Optional[str]
|
request: 'HttpRequest', # pylint: disable=unused-argument
|
||||||
|
serviceId: str,
|
||||||
|
transportId: typing.Optional[str],
|
||||||
) -> str:
|
) -> str:
|
||||||
'''
|
'''
|
||||||
If transportId (uuid) is None, this will be a metaLink
|
If transportId (uuid) is None, this will be a metaLink
|
||||||
'''
|
'''
|
||||||
return 'udsa://{}/{}'.format(serviceId, transportId or 'meta')
|
return f'udsa://{serviceId}/{transportId or "meta"}'
|
||||||
|
|
||||||
|
|
||||||
def parseDate(dateToParse) -> datetime.date:
|
def parseDate(dateToParse) -> datetime.date:
|
||||||
@ -97,4 +96,3 @@ def extractKey(dictionary: typing.Dict, key: typing.Any, **kwargs) -> str:
|
|||||||
else:
|
else:
|
||||||
value = default
|
value = default
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# pylint: disable=no-member
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2021 Virtual Cable S.L.U.
|
# Copyright (c) 2016-2021 Virtual Cable S.L.U.
|
||||||
@ -36,6 +36,13 @@ import tempfile
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import ldap.filter
|
import ldap.filter
|
||||||
|
from ldap import (
|
||||||
|
SCOPE_BASE, # type: ignore
|
||||||
|
SCOPE_SUBTREE, # type: ignore
|
||||||
|
SCOPE_ONELEVEL, # type: ignore
|
||||||
|
# SCOPE_SUBORDINATE, # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from uds.core.util import tools
|
from uds.core.util import tools
|
||||||
@ -44,23 +51,16 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
LDAPResultType = typing.MutableMapping[str, typing.Any]
|
LDAPResultType = typing.MutableMapping[str, typing.Any]
|
||||||
|
|
||||||
from ldap import (
|
|
||||||
SCOPE_BASE, # type: ignore
|
|
||||||
SCOPE_SUBTREE, # type: ignore
|
|
||||||
SCOPE_ONELEVEL, # type: ignore
|
|
||||||
SCOPE_SUBORDINATE, # type: ignore
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LDAPError(Exception):
|
class LDAPError(Exception):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def reraise(e: typing.Any):
|
def reraise(e: typing.Any):
|
||||||
_str = _('Connection error: ')
|
_str = _('Connection error: ')
|
||||||
if hasattr(e, 'message') and isinstance(e.message, dict):
|
if hasattr(e, 'message') and isinstance(e.message, dict):
|
||||||
_str += '{}, {}'.format(e.message.get('info', ''), e.message.get('desc'))
|
_str += f'{e.message.get("info", "")}, {e.message.get("desc", "")}'
|
||||||
else:
|
else:
|
||||||
_str += '{}'.format(e)
|
_str += str(e)
|
||||||
raise LDAPError(_str)
|
raise LDAPError(_str) from e
|
||||||
|
|
||||||
|
|
||||||
def escape(value: str):
|
def escape(value: str):
|
||||||
@ -80,7 +80,9 @@ def connection(
|
|||||||
timeout: int = 3,
|
timeout: int = 3,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
verify_ssl: bool = False,
|
verify_ssl: bool = False,
|
||||||
certificate: typing.Optional[str] = None, # Content of the certificate, not the file itself
|
certificate: typing.Optional[
|
||||||
|
str
|
||||||
|
] = None, # Content of the certificate, not the file itself
|
||||||
) -> typing.Any:
|
) -> typing.Any:
|
||||||
"""
|
"""
|
||||||
Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
|
Tries to connect to ldap. If username is None, it tries to connect using user provided credentials.
|
||||||
@ -100,7 +102,7 @@ def connection(
|
|||||||
schema = 'ldaps' if ssl else 'ldap'
|
schema = 'ldaps' if ssl else 'ldap'
|
||||||
if port == -1:
|
if port == -1:
|
||||||
port = 636 if ssl else 389
|
port = 636 if ssl else 389
|
||||||
uri = "{}://{}:{}".format(schema, host, port)
|
uri = f'{schema}://{host}:{port}'
|
||||||
logger.debug('Ldap uri: %s', uri)
|
logger.debug('Ldap uri: %s', uri)
|
||||||
|
|
||||||
l = ldap.initialize(uri=uri) # type: ignore
|
l = ldap.initialize(uri=uri) # type: ignore
|
||||||
@ -112,30 +114,34 @@ def connection(
|
|||||||
certificate = (certificate or '').strip()
|
certificate = (certificate or '').strip()
|
||||||
|
|
||||||
if ssl:
|
if ssl:
|
||||||
if certificate and verify_ssl: # If not verify_ssl, we don't need the certificate
|
if (
|
||||||
|
certificate and verify_ssl
|
||||||
|
): # If not verify_ssl, we don't need the certificate
|
||||||
# Create a semi-temporary ca file, with the content of the certificate
|
# Create a semi-temporary ca file, with the content of the certificate
|
||||||
# The name is from the host, so we can ovwerwrite it if needed
|
# The name is from the host, so we can ovwerwrite it if needed
|
||||||
cert_filename = os.path.join(tempfile.gettempdir(), f'ldap-cert-{host}.pem')
|
cert_filename = os.path.join(
|
||||||
with open(cert_filename, 'w') as f:
|
tempfile.gettempdir(), f'ldap-cert-{host}.pem'
|
||||||
|
)
|
||||||
|
with open(cert_filename, 'w', encoding='utf8') as f:
|
||||||
f.write(certificate)
|
f.write(certificate)
|
||||||
l.set_option(ldap.OPT_X_TLS_CACERTFILE, cert_filename) # type: ignore
|
l.set_option(ldap.OPT_X_TLS_CACERTFILE, cert_filename) # type: ignore
|
||||||
|
|
||||||
if not verify_ssl:
|
if not verify_ssl:
|
||||||
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # type: ignore
|
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # type: ignore
|
||||||
# Disable TLS1 and TLS1.1
|
# Disable TLS1 and TLS1.1
|
||||||
# 0x304 = TLS1.3, 0x303 = TLS1.2, 0x302 = TLS1.1, 0x301 = TLS1.0, but use ldap module constants
|
# 0x304 = TLS1.3, 0x303 = TLS1.2, 0x302 = TLS1.1, 0x301 = TLS1.0, but use ldap module constants
|
||||||
l.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2) # type: ignore
|
l.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN, ldap.OPT_X_TLS_PROTOCOL_TLS1_2) # type: ignore
|
||||||
|
|
||||||
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0) # type: ignore
|
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0) # type: ignore
|
||||||
|
|
||||||
l.simple_bind_s(who=username, cred=password)
|
l.simple_bind_s(who=username, cred=password)
|
||||||
except ldap.SERVER_DOWN as e: # type: ignore
|
except ldap.SERVER_DOWN as e: # type: ignore
|
||||||
raise LDAPError(_('Can\'t contact LDAP server') + ': {}'.format(e))
|
raise LDAPError(_('Can\'t contact LDAP server') + f': {e}') from e
|
||||||
except ldap.LDAPError as e: # type: ignore
|
except ldap.LDAPError as e: # type: ignore
|
||||||
LDAPError.reraise(e)
|
LDAPError.reraise(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('Exception connection:')
|
logger.exception('Exception connection:')
|
||||||
raise LDAPError('{}'.format(e))
|
raise LDAPError(str(e)) from e
|
||||||
|
|
||||||
logger.debug('Connection was successful')
|
logger.debug('Connection was successful')
|
||||||
return l
|
return l
|
||||||
@ -145,8 +151,8 @@ def getAsDict(
|
|||||||
con: typing.Any,
|
con: typing.Any,
|
||||||
base: str,
|
base: str,
|
||||||
ldapFilter: str,
|
ldapFilter: str,
|
||||||
attrList: typing.Optional[typing.Iterable[str]]=None,
|
attrList: typing.Optional[typing.Iterable[str]] = None,
|
||||||
sizeLimit: int=100,
|
sizeLimit: int = 100,
|
||||||
scope=SCOPE_SUBTREE,
|
scope=SCOPE_SUBTREE,
|
||||||
) -> typing.Generator[LDAPResultType, None, None]:
|
) -> typing.Generator[LDAPResultType, None, None]:
|
||||||
"""
|
"""
|
||||||
@ -156,7 +162,7 @@ def getAsDict(
|
|||||||
logger.debug('Filter: %s, attr list: %s', ldapFilter, attrList)
|
logger.debug('Filter: %s, attr list: %s', ldapFilter, attrList)
|
||||||
|
|
||||||
if attrList:
|
if attrList:
|
||||||
attrList = [i for i in attrList] # Ensures iterable is a list
|
attrList = list(attrList) # Ensures iterable is a list
|
||||||
|
|
||||||
res = None
|
res = None
|
||||||
try:
|
try:
|
||||||
@ -172,7 +178,7 @@ def getAsDict(
|
|||||||
LDAPError.reraise(e)
|
LDAPError.reraise(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('Exception connection:')
|
logger.exception('Exception connection:')
|
||||||
raise LDAPError('{}'.format(e))
|
raise LDAPError(str(e)) from e
|
||||||
|
|
||||||
logger.debug('Result of search %s on %s: %s', ldapFilter, base, res)
|
logger.debug('Result of search %s on %s: %s', ldapFilter, base, res)
|
||||||
|
|
||||||
@ -214,9 +220,9 @@ def getFirst(
|
|||||||
"""
|
"""
|
||||||
value = ldap.filter.escape_filter_chars(value)
|
value = ldap.filter.escape_filter_chars(value)
|
||||||
|
|
||||||
attrList = [field] + [i for i in attributes] if attributes else []
|
attrList = [field] + list(attributes) if attributes else []
|
||||||
|
|
||||||
ldapFilter = '(&(objectClass={})({}={}))'.format(objectClass, field, value)
|
ldapFilter = f'(&(objectClass={objectClass})({field}={value}))'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj = next(getAsDict(con, base, ldapFilter, attrList, sizeLimit))
|
obj = next(getAsDict(con, base, ldapFilter, attrList, sizeLimit))
|
||||||
@ -246,9 +252,11 @@ def getRootDSE(con: typing.Any) -> typing.Optional[LDAPResultType]:
|
|||||||
@param cont: Connection to LDAP server
|
@param cont: Connection to LDAP server
|
||||||
@return: None if root DSE is not found, an dictionary of LDAP entry attributes if found (all in unicode on py2, str on py3).
|
@return: None if root DSE is not found, an dictionary of LDAP entry attributes if found (all in unicode on py2, str on py3).
|
||||||
"""
|
"""
|
||||||
return next(getAsDict(
|
return next(
|
||||||
con=con,
|
getAsDict(
|
||||||
base='',
|
con=con,
|
||||||
ldapFilter='(objectClass=*)',
|
base='',
|
||||||
scope=SCOPE_BASE,
|
ldapFilter='(objectClass=*)',
|
||||||
))
|
scope=SCOPE_BASE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ -37,6 +37,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
T = typing.TypeVar('T', bound=typing.Any)
|
T = typing.TypeVar('T', bound=typing.Any)
|
||||||
|
|
||||||
|
|
||||||
# We want to write something like this:
|
# We want to write something like this:
|
||||||
# (('<arg>', '<arg2>', 'literal', '<other_arg>', '<other_arg2>', 'literal2', ...), callback)
|
# (('<arg>', '<arg2>', 'literal', '<other_arg>', '<other_arg2>', 'literal2', ...), callback)
|
||||||
# Where callback is a function that will be called with the arguments in the order they are
|
# Where callback is a function that will be called with the arguments in the order they are
|
||||||
@ -71,19 +72,26 @@ def match(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Check if all the arguments match
|
# Check if all the arguments match
|
||||||
match = True
|
doMatch = True
|
||||||
for i, arg in enumerate(arg_list):
|
for i, arg in enumerate(arg_list):
|
||||||
if matcher[0][i].startswith('<') and matcher[0][i].endswith('>'):
|
if matcher[0][i].startswith('<') and matcher[0][i].endswith('>'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if arg != matcher[0][i]:
|
if arg != matcher[0][i]:
|
||||||
match = False
|
doMatch = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if match:
|
if doMatch:
|
||||||
# All the arguments match, call the callback
|
# All the arguments match, call the callback
|
||||||
return matcher[1](*[arg for i, arg in enumerate(arg_list) if matcher[0][i].startswith('<') and matcher[0][i].endswith('>')])
|
return matcher[1](
|
||||||
|
*[
|
||||||
|
arg
|
||||||
|
for i, arg in enumerate(arg_list)
|
||||||
|
if matcher[0][i].startswith('<') and matcher[0][i].endswith('>')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
logger.warning('No match found for %s with %s', arg_list, args)
|
logger.warning('No match found for %s with %s', arg_list, args)
|
||||||
# Invoke error callback
|
# Invoke error callback
|
||||||
error()
|
error()
|
||||||
|
return None # In fact, error is expected to raise an exception, so this is never reached
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
# Copyright (c) 2015-2019 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -12,7 +12,7 @@
|
|||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
# and/or other materials provided with the distribution.
|
# and/or other materials provided with the distribution.
|
||||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||||
# may be used to endorse or promote products derived from this software
|
# may be used to endorse or promote products derived from this software
|
||||||
# without specific prior written permission.
|
# without specific prior written permission.
|
||||||
#
|
#
|
||||||
@ -31,7 +31,7 @@
|
|||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .common import (
|
from .common import ( # pylint: disable=unused-import
|
||||||
ACTIVE,
|
ACTIVE,
|
||||||
INACTIVE,
|
INACTIVE,
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
# Copyright (c) 2015-2023 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -12,7 +12,7 @@
|
|||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
# and/or other materials provided with the distribution.
|
# and/or other materials provided with the distribution.
|
||||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||||
# may be used to endorse or promote products derived from this software
|
# may be used to endorse or promote products derived from this software
|
||||||
# without specific prior written permission.
|
# without specific prior written permission.
|
||||||
#
|
#
|
||||||
@ -28,7 +28,7 @@
|
|||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from .common import ERROR, FINISHED, RUNNING # @UnusedImport
|
from .common import ERROR, FINISHED, RUNNING
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
# Copyright (c) 2015-2023 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -12,7 +12,7 @@
|
|||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
# and/or other materials provided with the distribution.
|
# and/or other materials provided with the distribution.
|
||||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||||
# may be used to endorse or promote products derived from this software
|
# may be used to endorse or promote products derived from this software
|
||||||
# without specific prior written permission.
|
# without specific prior written permission.
|
||||||
#
|
#
|
||||||
@ -40,4 +40,4 @@ from .common import (
|
|||||||
CANCELED,
|
CANCELED,
|
||||||
LAUNCHING,
|
LAUNCHING,
|
||||||
PREPARING,
|
PREPARING,
|
||||||
) # @UnusedImport
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
# Copyright (c) 2015-2023 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -12,7 +12,7 @@
|
|||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
# and/or other materials provided with the distribution.
|
# and/or other materials provided with the distribution.
|
||||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||||
# may be used to endorse or promote products derived from this software
|
# may be used to endorse or promote products derived from this software
|
||||||
# without specific prior written permission.
|
# without specific prior written permission.
|
||||||
#
|
#
|
||||||
@ -32,4 +32,4 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from .common import ACTIVE, REMOVABLE, REMOVING, REMOVED # @UnusedImport
|
from .common import ACTIVE, REMOVABLE, REMOVING, REMOVED
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2021 Virtual Cable S.L.U.
|
# Copyright (c) 2015-2023 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -32,4 +32,4 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from .common import FOR_EXECUTE # @UnusedImport
|
from .common import FOR_EXECUTE
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright (c) 2015-2021 Virtual Cable S.L.U.
|
# Copyright (c) 2015-2023 Virtual Cable S.L.U.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
@ -41,4 +41,4 @@ from .common import (
|
|||||||
REMOVED,
|
REMOVED,
|
||||||
INFO_STATES,
|
INFO_STATES,
|
||||||
VALID_STATES,
|
VALID_STATES,
|
||||||
) # @UnusedImport
|
)
|
||||||
|
@ -67,6 +67,7 @@ CounterClass = typing.TypeVar(
|
|||||||
|
|
||||||
OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR = range(4)
|
OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR = range(4)
|
||||||
|
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
def _get_Id(obj):
|
def _get_Id(obj):
|
||||||
return obj.id if obj.id != -1 else None
|
return obj.id if obj.id != -1 else None
|
||||||
@ -244,7 +245,7 @@ def getAcumCounters(
|
|||||||
since=since,
|
since=since,
|
||||||
points=points,
|
points=points,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Data initialization
|
# Data initialization
|
||||||
def _initializeData() -> None:
|
def _initializeData() -> None:
|
||||||
|
@ -63,7 +63,7 @@ if typing.TYPE_CHECKING:
|
|||||||
# Os Manager
|
# Os Manager
|
||||||
ET_OSMANAGER_INIT,
|
ET_OSMANAGER_INIT,
|
||||||
ET_OSMANAGER_READY,
|
ET_OSMANAGER_READY,
|
||||||
ET_OSMANAGER_RELEASE
|
ET_OSMANAGER_RELEASE,
|
||||||
) = range(11)
|
) = range(11)
|
||||||
|
|
||||||
# Events names
|
# Events names
|
||||||
@ -81,20 +81,14 @@ EVENT_NAMES: typing.Mapping[int, str] = {
|
|||||||
ET_OSMANAGER_RELEASE: 'OS Manager release',
|
ET_OSMANAGER_RELEASE: 'OS Manager release',
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
(OT_PROVIDER, OT_SERVICE, OT_SERVICEPOOL, OT_AUTHENTICATOR, OT_OSMANAGER) = range(5)
|
||||||
OT_PROVIDER,
|
|
||||||
OT_SERVICE,
|
|
||||||
OT_SERVICEPOOL,
|
|
||||||
OT_AUTHENTICATOR,
|
|
||||||
OT_OSMANAGER
|
|
||||||
) = range(5)
|
|
||||||
|
|
||||||
TYPES_NAMES: typing.Mapping[int, str] = {
|
TYPES_NAMES: typing.Mapping[int, str] = {
|
||||||
OT_PROVIDER: 'Provider',
|
OT_PROVIDER: 'Provider',
|
||||||
OT_SERVICE: 'Service',
|
OT_SERVICE: 'Service',
|
||||||
OT_SERVICEPOOL: 'Deployed',
|
OT_SERVICEPOOL: 'Deployed',
|
||||||
OT_AUTHENTICATOR: 'Authenticator',
|
OT_AUTHENTICATOR: 'Authenticator',
|
||||||
OT_OSMANAGER: 'OS Manager'
|
OT_OSMANAGER: 'OS Manager',
|
||||||
}
|
}
|
||||||
|
|
||||||
MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
|
MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
|
||||||
@ -102,7 +96,7 @@ MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
|
|||||||
Service: OT_SERVICE,
|
Service: OT_SERVICE,
|
||||||
Provider: OT_PROVIDER,
|
Provider: OT_PROVIDER,
|
||||||
Authenticator: OT_AUTHENTICATOR,
|
Authenticator: OT_AUTHENTICATOR,
|
||||||
OSManager: OT_OSMANAGER
|
OSManager: OT_OSMANAGER,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Events data (fld1, fld2, fld3, fld4):
|
# Events data (fld1, fld2, fld3, fld4):
|
||||||
@ -154,21 +148,22 @@ MODEL_TO_EVENT: typing.Mapping[typing.Type['models.Model'], int] = {
|
|||||||
# OT_OSMANAGER_RELEASE: -> On OsManager
|
# OT_OSMANAGER_RELEASE: -> On OsManager
|
||||||
# (servicepool_uuid, '', userservice_uuid)
|
# (servicepool_uuid, '', userservice_uuid)
|
||||||
|
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
# get owner by type and id
|
# get owner by type and id
|
||||||
def getOwner(ownerType: int, ownerId: int) -> typing.Optional['models.Model']:
|
def getOwner(ownerType: int, ownerId: int) -> typing.Optional['models.Model']:
|
||||||
if ownerType == OT_PROVIDER:
|
if ownerType == OT_PROVIDER:
|
||||||
return Provider.objects.get(pk=ownerId)
|
return Provider.objects.get(pk=ownerId)
|
||||||
elif ownerType == OT_SERVICE:
|
if ownerType == OT_SERVICE:
|
||||||
return Service.objects.get(pk=ownerId)
|
return Service.objects.get(pk=ownerId)
|
||||||
elif ownerType == OT_SERVICEPOOL:
|
if ownerType == OT_SERVICEPOOL:
|
||||||
return ServicePool.objects.get(pk=ownerId)
|
return ServicePool.objects.get(pk=ownerId)
|
||||||
elif ownerType == OT_AUTHENTICATOR:
|
if ownerType == OT_AUTHENTICATOR:
|
||||||
return Authenticator.objects.get(pk=ownerId)
|
return Authenticator.objects.get(pk=ownerId)
|
||||||
elif ownerType == OT_OSMANAGER:
|
if ownerType == OT_OSMANAGER:
|
||||||
return OSManager.objects.get(pk=ownerId)
|
return OSManager.objects.get(pk=ownerId)
|
||||||
else:
|
return None
|
||||||
return None
|
|
||||||
|
|
||||||
class EventTupleType(typing.NamedTuple):
|
class EventTupleType(typing.NamedTuple):
|
||||||
stamp: datetime.datetime
|
stamp: datetime.datetime
|
||||||
@ -188,13 +183,14 @@ class EventTupleType(typing.NamedTuple):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
# Convert Event type to string first
|
# Convert Event type to string first
|
||||||
eventName = EVENT_NAMES[self.event_type]
|
eventName = EVENT_NAMES[self.event_type]
|
||||||
return '{} {} {} {} {} {}'.format(self.stamp, eventName, self.fld1, self.fld2, self.fld3, self.fld4 )
|
return (
|
||||||
|
f'{self.stamp} {eventName} {self.fld1} {self.fld2} {self.fld3} {self.fld4}'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
EventClass = typing.Union[Provider, Service, ServicePool, Authenticator]
|
EventClass = typing.Union[Provider, Service, ServicePool, Authenticator]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addEvent(obj: EventClass, eventType: int, **kwargs) -> bool:
|
def addEvent(obj: EventClass, eventType: int, **kwargs) -> bool:
|
||||||
"""
|
"""
|
||||||
Adds a event stat to specified object
|
Adds a event stat to specified object
|
||||||
@ -237,7 +233,11 @@ def getEvents(
|
|||||||
owner_id = obj.pk
|
owner_id = obj.pk
|
||||||
|
|
||||||
for i in StatsManager.manager().getEvents(
|
for i in StatsManager.manager().getEvents(
|
||||||
MODEL_TO_EVENT[objType], eventType, owner_id=owner_id, since=kwargs.get('since'), to=kwargs.get('to')
|
MODEL_TO_EVENT[objType],
|
||||||
|
eventType,
|
||||||
|
owner_id=owner_id,
|
||||||
|
since=kwargs.get('since'),
|
||||||
|
to=kwargs.get('to'),
|
||||||
):
|
):
|
||||||
yield EventTupleType(
|
yield EventTupleType(
|
||||||
datetime.datetime.fromtimestamp(i.stamp),
|
datetime.datetime.fromtimestamp(i.stamp),
|
||||||
@ -247,8 +247,9 @@ def getEvents(
|
|||||||
i.fld4,
|
i.fld4,
|
||||||
i.event_type,
|
i.event_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
# tail the events table
|
|
||||||
|
# tail the events table
|
||||||
def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, None]:
|
def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, None]:
|
||||||
fromId = None
|
fromId = None
|
||||||
while True:
|
while True:
|
||||||
@ -263,4 +264,3 @@ def tailEvents(sleepTime: int = 2) -> typing.Generator[EventTupleType, None, Non
|
|||||||
)
|
)
|
||||||
fromId = i.pk if i.pk > (fromId or 0) else fromId
|
fromId = i.pk if i.pk > (fromId or 0) else fromId
|
||||||
time.sleep(sleepTime)
|
time.sleep(sleepTime)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user