mirror of
https://github.com/dkmstr/openuds.git
synced 2025-03-20 06:50:23 +03:00
Formating and type fixing
This commit is contained in:
parent
3934f2b88d
commit
e485374836
@ -155,6 +155,7 @@ def webLoginRequired(
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
# Helper for checking if requests is from trusted source
|
||||
def isTrustedSource(ip: str) -> bool:
|
||||
return net.ipInNetwork(ip, GlobalConfig.TRUSTED_SOURCES.get(True))
|
||||
@ -224,7 +225,7 @@ def __registerUser(
|
||||
# And add an login event
|
||||
events.addEvent(
|
||||
authenticator, events.ET_LOGIN, username=username, srcip=request.ip
|
||||
)
|
||||
)
|
||||
events.addEvent(
|
||||
authenticator,
|
||||
events.ET_PLATFORM,
|
||||
@ -345,7 +346,10 @@ def authInfoUrl(authenticator: typing.Union[str, bytes, Authenticator]) -> str:
|
||||
|
||||
|
||||
def webLogin(
|
||||
request: 'ExtendedHttpRequest', response: typing.Optional[HttpResponse], user: User, password: str
|
||||
request: 'ExtendedHttpRequest',
|
||||
response: typing.Optional[HttpResponse],
|
||||
user: User,
|
||||
password: str,
|
||||
) -> bool:
|
||||
"""
|
||||
Helper function to, once the user is authenticated, store the information at the user session.
|
||||
@ -393,7 +397,9 @@ def webPassword(request: HttpRequest) -> str:
|
||||
so we can provide it to remote sessions.
|
||||
"""
|
||||
if hasattr(request, 'session'):
|
||||
return cryptoManager().symDecrpyt(request.session.get(PASS_KEY, ''), getUDSCookie(request)) # recover as original unicode string
|
||||
return cryptoManager().symDecrpyt(
|
||||
request.session.get(PASS_KEY, ''), getUDSCookie(request)
|
||||
) # recover as original unicode string
|
||||
else: # No session, get from _session instead, this is an "client" REST request
|
||||
return cryptoManager().symDecrpyt(request._cryptedpass, request._scrambler) # type: ignore
|
||||
|
||||
@ -428,7 +434,6 @@ def webLogout(
|
||||
|
||||
# Try to delete session
|
||||
request.session.flush()
|
||||
|
||||
|
||||
response = HttpResponseRedirect(request.build_absolute_uri(exit_url))
|
||||
if authenticator:
|
||||
@ -437,7 +442,10 @@ def webLogout(
|
||||
|
||||
|
||||
def authLogLogin(
|
||||
request: 'ExtendedHttpRequest', authenticator: Authenticator, userName: str, logStr: str = ''
|
||||
request: 'ExtendedHttpRequest',
|
||||
authenticator: Authenticator,
|
||||
userName: str,
|
||||
logStr: str = '',
|
||||
) -> None:
|
||||
"""
|
||||
Logs authentication
|
||||
|
@ -39,7 +39,10 @@ from uds.core import Module
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import HttpRequest, HttpResponse # pylint: disable=ungrouped-imports
|
||||
from django.http import (
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
) # pylint: disable=ungrouped-imports
|
||||
from uds.core.environment import Environment
|
||||
from uds import models
|
||||
from .groups_manager import GroupsManager
|
||||
@ -161,7 +164,12 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
|
||||
_dbAuth: 'models.Authenticator'
|
||||
|
||||
def __init__(self, dbAuth: 'models.Authenticator', environment: 'Environment', values: typing.Optional[typing.Dict[str, str]]):
|
||||
def __init__(
|
||||
self,
|
||||
dbAuth: 'models.Authenticator',
|
||||
environment: 'Environment',
|
||||
values: typing.Optional[typing.Dict[str, str]],
|
||||
):
|
||||
"""
|
||||
Instantiathes the authenticator.
|
||||
@param dbAuth: Database object for the authenticator
|
||||
@ -202,13 +210,17 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
|
||||
user param is a database user object
|
||||
"""
|
||||
from uds.core.auths.groups_manager import GroupsManager # pylint: disable=redefined-outer-name
|
||||
from uds.core.auths.groups_manager import (
|
||||
GroupsManager,
|
||||
) # pylint: disable=redefined-outer-name
|
||||
|
||||
if self.isExternalSource:
|
||||
groupsManager = GroupsManager(self._dbAuth)
|
||||
self.getGroups(user.name, groupsManager)
|
||||
# cast for typechecking. user.groups is a "simmmilar to a QuerySet", but it's not a QuerySet, so "set" is not there
|
||||
typing.cast(typing.Any, user.groups).set([g.dbGroup() for g in groupsManager.getValidGroups()])
|
||||
typing.cast(typing.Any, user.groups).set(
|
||||
[g.dbGroup() for g in groupsManager.getValidGroups()]
|
||||
)
|
||||
|
||||
def callbackUrl(self) -> str:
|
||||
"""
|
||||
@ -218,6 +230,7 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
we need to use callback for authentication
|
||||
"""
|
||||
from .auth import authCallbackUrl
|
||||
|
||||
return authCallbackUrl(self.dbAuthenticator())
|
||||
|
||||
def infoUrl(self) -> str:
|
||||
@ -225,6 +238,7 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
Helper method to return info url for this authenticator
|
||||
"""
|
||||
from .auth import authInfoUrl
|
||||
|
||||
return authInfoUrl(self.dbAuthenticator())
|
||||
|
||||
@classmethod
|
||||
@ -276,7 +290,9 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
return []
|
||||
|
||||
def authenticate(self, username: str, credentials: str, groupsManager: 'GroupsManager') -> bool:
|
||||
def authenticate(
|
||||
self, username: str, credentials: str, groupsManager: 'GroupsManager'
|
||||
) -> bool:
|
||||
"""
|
||||
This method must be overriden, and is responsible for authenticating
|
||||
users.
|
||||
@ -332,7 +348,9 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
return username
|
||||
|
||||
def internalAuthenticate(self, username: str, credentials: str, groupsManager: 'GroupsManager') -> bool:
|
||||
def internalAuthenticate(
|
||||
self, username: str, credentials: str, groupsManager: 'GroupsManager'
|
||||
) -> bool:
|
||||
"""
|
||||
This method is provided so "plugins" (For example, a custom dispatcher), can test
|
||||
the username/credentials in an alternative way.
|
||||
@ -396,7 +414,9 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
return None
|
||||
|
||||
def webLogoutHook(self, username: str, request: 'HttpRequest', response: 'HttpResponse') -> None:
|
||||
def webLogoutHook(
|
||||
self, username: str, request: 'HttpRequest', response: 'HttpResponse'
|
||||
) -> None:
|
||||
'''
|
||||
Invoked on web logout of an user
|
||||
Args:
|
||||
@ -455,7 +475,9 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
return None
|
||||
|
||||
def authCallback(self, parameters: typing.Dict[str, typing.Any], gm: 'GroupsManager') -> typing.Optional[str]:
|
||||
def authCallback(
|
||||
self, parameters: typing.Dict[str, typing.Any], gm: 'GroupsManager'
|
||||
) -> typing.Optional[str]:
|
||||
"""
|
||||
There is a view inside UDS, an url, that will redirect the petition
|
||||
to this callback.
|
||||
@ -491,7 +513,9 @@ class Authenticator(Module): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
return None
|
||||
|
||||
def getInfo(self, parameters: typing.Mapping[str, str]) -> typing.Optional[typing.Tuple[str, typing.Optional[str]]]:
|
||||
def getInfo(
|
||||
self, parameters: typing.Mapping[str, str]
|
||||
) -> typing.Optional[typing.Tuple[str, typing.Optional[str]]]:
|
||||
"""
|
||||
This method is invoked whenever the authinfo url is invoked, with the name of the authenticator
|
||||
If this is implemented, information returned by this will be shown via web.
|
||||
|
@ -36,6 +36,7 @@ import typing
|
||||
if typing.TYPE_CHECKING:
|
||||
from .authenticator import Authenticator
|
||||
|
||||
|
||||
class AuthsFactory:
|
||||
"""
|
||||
This class holds the register of all known authentication modules
|
||||
@ -43,6 +44,7 @@ class AuthsFactory:
|
||||
|
||||
It provides a way to register and recover Authentication providers.
|
||||
"""
|
||||
|
||||
_factory: typing.Optional['AuthsFactory'] = None
|
||||
_auths: typing.Dict[str, typing.Type['Authenticator']] = {}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
|
||||
|
||||
class AuthenticatorException(Exception):
|
||||
"""
|
||||
Generic authentication exception
|
||||
|
@ -62,6 +62,7 @@ class GroupsManager:
|
||||
|
||||
Managed groups names are compared using case insensitive comparison.
|
||||
"""
|
||||
|
||||
_groups: typing.Dict[str, dict]
|
||||
|
||||
def __init__(self, dbAuthenticator: 'DBAuthenticator'):
|
||||
@ -71,11 +72,18 @@ class GroupsManager:
|
||||
to which this groupsManager will be associated
|
||||
"""
|
||||
self._dbAuthenticator = dbAuthenticator
|
||||
self._groups = {} # We just get active groups, inactive aren't visible to this class
|
||||
self._groups = (
|
||||
{}
|
||||
) # We just get active groups, inactive aren't visible to this class
|
||||
for g in dbAuthenticator.groups.filter(state=State.ACTIVE, is_meta=False):
|
||||
name = g.name.lower()
|
||||
isPattern = name.find('pat:') == 0 # Is a pattern?
|
||||
self._groups[name] = {'name': g.name, 'group': Group(g), 'valid': False, 'pattern': isPattern}
|
||||
self._groups[name] = {
|
||||
'name': g.name,
|
||||
'group': Group(g),
|
||||
'valid': False,
|
||||
'pattern': isPattern,
|
||||
}
|
||||
|
||||
def checkAllGroups(self, groupName: str):
|
||||
"""
|
||||
@ -119,11 +127,15 @@ class GroupsManager:
|
||||
yield g['group']
|
||||
|
||||
# Now, get metagroups and also return them
|
||||
for g2 in DBGroup.objects.filter(manager__id=self._dbAuthenticator.id, is_meta=True): # @UndefinedVariable
|
||||
for g2 in DBGroup.objects.filter(
|
||||
manager__id=self._dbAuthenticator.id, is_meta=True
|
||||
): # @UndefinedVariable
|
||||
gn = g2.groups.filter(id__in=lst, state=State.ACTIVE).count()
|
||||
if g2.meta_if_any and gn > 0:
|
||||
gn = g2.groups.count()
|
||||
if gn == g2.groups.count(): # If a meta group is empty, all users belongs to it. we can use gn != 0 to check that if it is empty, is not valid
|
||||
if (
|
||||
gn == g2.groups.count()
|
||||
): # If a meta group is empty, all users belongs to it. we can use gn != 0 to check that if it is empty, is not valid
|
||||
# This group matches
|
||||
yield Group(g2)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -40,9 +40,11 @@ from .delayed_task import DelayedTask
|
||||
if typing.TYPE_CHECKING:
|
||||
from .jobs_factory import JobsFactory
|
||||
|
||||
|
||||
def factory() -> 'JobsFactory':
|
||||
"""
|
||||
Returns a singleton to a jobs factory
|
||||
"""
|
||||
from .jobs_factory import JobsFactory # pylint: disable=redefined-outer-name
|
||||
|
||||
return JobsFactory.factory()
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -10,7 +10,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -54,13 +54,14 @@ class DelayedTaskThread(threading.Thread):
|
||||
"""
|
||||
Class responsible of executing a delayed task in its own thread
|
||||
"""
|
||||
|
||||
_taskInstance: DelayedTask
|
||||
|
||||
def __init__(self, taskInstance: DelayedTask) -> None:
|
||||
super().__init__()
|
||||
self._taskInstance = taskInstance
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
try:
|
||||
self._taskInstance.execute()
|
||||
except Exception as e:
|
||||
@ -73,8 +74,8 @@ class DelayedTaskRunner:
|
||||
"""
|
||||
Delayed task runner class
|
||||
"""
|
||||
# How often tasks are checked
|
||||
granularity: int = 2
|
||||
|
||||
granularity: int = 2 # we check for delayed tasks every "granularity" seconds
|
||||
|
||||
# to keep singleton DelayedTaskRunner
|
||||
_runner: typing.ClassVar[typing.Optional['DelayedTaskRunner']] = None
|
||||
@ -106,14 +107,22 @@ class DelayedTaskRunner:
|
||||
|
||||
def executeOneDelayedTask(self) -> None:
|
||||
now = getSqlDatetime()
|
||||
filt = Q(execution_time__lt=now) | Q(insert_date__gt=now + timedelta(seconds=30))
|
||||
filt = Q(execution_time__lt=now) | Q(
|
||||
insert_date__gt=now + timedelta(seconds=30)
|
||||
)
|
||||
# If next execution is before now or last execution is in the future (clock changed on this server, we take that task as executable)
|
||||
try:
|
||||
with transaction.atomic(): # Encloses
|
||||
# Throws exception if no delayed task is avilable
|
||||
task = DBDelayedTask.objects.select_for_update().filter(filt).order_by('execution_time')[0] # @UndefinedVariable
|
||||
task = (
|
||||
DBDelayedTask.objects.select_for_update()
|
||||
.filter(filt)
|
||||
.order_by('execution_time')[0]
|
||||
) # @UndefinedVariable
|
||||
if task.insert_date > now + timedelta(seconds=30):
|
||||
logger.warning('Executed %s due to insert_date being in the future!', task.type)
|
||||
logger.warning(
|
||||
'Executed %s due to insert_date being in the future!', task.type
|
||||
)
|
||||
taskInstanceDump = codecs.decode(task.instance.encode(), 'base64')
|
||||
task.delete()
|
||||
taskInstance = pickle.loads(taskInstanceDump)
|
||||
@ -140,10 +149,21 @@ class DelayedTaskRunner:
|
||||
instanceDump = codecs.encode(pickle.dumps(instance), 'base64').decode()
|
||||
typeName = str(cls.__module__ + '.' + cls.__name__)
|
||||
|
||||
logger.debug('Inserting delayed task %s with %s bytes (%s)', typeName, len(instanceDump), exec_time)
|
||||
logger.debug(
|
||||
'Inserting delayed task %s with %s bytes (%s)',
|
||||
typeName,
|
||||
len(instanceDump),
|
||||
exec_time,
|
||||
)
|
||||
|
||||
DBDelayedTask.objects.create(type=typeName, instance=instanceDump, # @UndefinedVariable
|
||||
insert_date=now, execution_delay=delay, execution_time=exec_time, tag=tag)
|
||||
DBDelayedTask.objects.create(
|
||||
type=typeName,
|
||||
instance=instanceDump, # @UndefinedVariable
|
||||
insert_date=now,
|
||||
execution_delay=delay,
|
||||
execution_time=exec_time,
|
||||
tag=tag,
|
||||
)
|
||||
|
||||
def insert(self, instance: DelayedTask, delay: int, tag: str = '') -> bool:
|
||||
retries = 3
|
||||
@ -161,14 +181,18 @@ class DelayedTaskRunner:
|
||||
time.sleep(1) # Wait a bit before next try...
|
||||
# If retries == 0, this is a big error
|
||||
if retries == 0:
|
||||
logger.error("Could not insert delayed task!!!! %s %s %s", instance, delay, tag)
|
||||
logger.error(
|
||||
"Could not insert delayed task!!!! %s %s %s", instance, delay, tag
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def remove(self, tag: str) -> None:
|
||||
try:
|
||||
with transaction.atomic():
|
||||
DBDelayedTask.objects.select_for_update().filter(tag=tag).delete() # @UndefinedVariable
|
||||
DBDelayedTask.objects.select_for_update().filter(
|
||||
tag=tag
|
||||
).delete() # @UndefinedVariable
|
||||
except Exception as e:
|
||||
logger.exception('Exception removing a delayed task %s: %s', e.__class__, e)
|
||||
|
||||
|
@ -80,16 +80,30 @@ class JobsFactory:
|
||||
# We use database server datetime
|
||||
now = getSqlDatetime()
|
||||
next_ = now
|
||||
job = Scheduler.objects.create(name=name, frecuency=type_.frecuency, last_execution=now, next_execution=next_, state=State.FOR_EXECUTE)
|
||||
job = Scheduler.objects.create(
|
||||
name=name,
|
||||
frecuency=type_.frecuency,
|
||||
last_execution=now,
|
||||
next_execution=next_,
|
||||
state=State.FOR_EXECUTE,
|
||||
)
|
||||
except Exception: # already exists
|
||||
logger.debug('Already added %s', name)
|
||||
job = Scheduler.objects.get(name=name)
|
||||
job.frecuency = type_.frecuency
|
||||
if job.next_execution > job.last_execution + datetime.timedelta(seconds=type_.frecuency):
|
||||
job.next_execution = job.last_execution + datetime.timedelta(seconds=type_.frecuency)
|
||||
if job.next_execution > job.last_execution + datetime.timedelta(
|
||||
seconds=type_.frecuency
|
||||
):
|
||||
job.next_execution = job.last_execution + datetime.timedelta(
|
||||
seconds=type_.frecuency
|
||||
)
|
||||
job.save()
|
||||
except Exception as e:
|
||||
logger.debug('Exception at ensureJobsInDatabase in JobsFactory: %s, %s', e.__class__, e)
|
||||
logger.debug(
|
||||
'Exception at ensureJobsInDatabase in JobsFactory: %s, %s',
|
||||
e.__class__,
|
||||
e,
|
||||
)
|
||||
|
||||
def lookup(self, typeName: str) -> typing.Optional[typing.Type['Job']]:
|
||||
return self._jobs.get(typeName, None)
|
||||
|
@ -43,30 +43,38 @@ if typing.TYPE_CHECKING:
|
||||
from .user_service import UserServiceManager
|
||||
from .publication import PublicationManager
|
||||
|
||||
|
||||
def cryptoManager() -> 'CryptoManager':
|
||||
from .crypto import CryptoManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return CryptoManager.manager()
|
||||
|
||||
|
||||
def taskManager() -> typing.Type['TaskManager']:
|
||||
from .task import TaskManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return TaskManager
|
||||
|
||||
|
||||
def downloadsManager() -> 'DownloadsManager':
|
||||
from .downloads import DownloadsManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return DownloadsManager.manager()
|
||||
|
||||
|
||||
def logManager() -> 'LogManager':
|
||||
from .log import LogManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return LogManager.manager()
|
||||
|
||||
|
||||
def userServiceManager() -> 'UserServiceManager':
|
||||
from .user_service import UserServiceManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return UserServiceManager.manager()
|
||||
|
||||
|
||||
def publicationManager() -> 'PublicationManager':
|
||||
from .publication import PublicationManager # pylint: disable=redefined-outer-name
|
||||
|
||||
return PublicationManager.manager()
|
||||
|
@ -55,6 +55,7 @@ class DownloadsManager:
|
||||
os.path.join(os.path.dirname(sys.modules[__package__].__file__), 'files/test.exe'),
|
||||
'application/x-msdos-program')
|
||||
"""
|
||||
|
||||
_manager: typing.Optional['DownloadsManager'] = None
|
||||
_downloadables: typing.Dict[str, typing.Dict[str, str]] = {}
|
||||
|
||||
@ -67,7 +68,9 @@ class DownloadsManager:
|
||||
DownloadsManager._manager = DownloadsManager()
|
||||
return DownloadsManager._manager
|
||||
|
||||
def registerDownloadable(self, name: str, comment: str, path: str, mime: str = 'application/octet-stream'):
|
||||
def registerDownloadable(
|
||||
self, name: str, comment: str, path: str, mime: str = 'application/octet-stream'
|
||||
):
|
||||
"""
|
||||
Registers a downloadable file.
|
||||
@param name: name shown
|
||||
@ -75,16 +78,28 @@ class DownloadsManager:
|
||||
@params zip: If download as zip
|
||||
"""
|
||||
_id = cryptoManager().uuid(name)
|
||||
self._downloadables[_id] = {'name': name, 'comment': comment, 'path': path, 'mime': mime}
|
||||
self._downloadables[_id] = {
|
||||
'name': name,
|
||||
'comment': comment,
|
||||
'path': path,
|
||||
'mime': mime,
|
||||
}
|
||||
|
||||
def getDownloadables(self) -> typing.Dict[str, typing.Dict[str, str]]:
|
||||
return self._downloadables
|
||||
|
||||
def send(self, request, _id) -> HttpResponse:
|
||||
if _id not in self._downloadables:
|
||||
logger.error('Downloadable id %s not found in %s!!!', _id, self._downloadables)
|
||||
logger.error(
|
||||
'Downloadable id %s not found in %s!!!', _id, self._downloadables
|
||||
)
|
||||
raise Http404
|
||||
return self._send_file(request, self._downloadables[_id]['name'], self._downloadables[_id]['path'], self._downloadables[_id]['mime'])
|
||||
return self._send_file(
|
||||
request,
|
||||
self._downloadables[_id]['name'],
|
||||
self._downloadables[_id]['path'],
|
||||
self._downloadables[_id]['mime'],
|
||||
)
|
||||
|
||||
def _send_file(self, _, name, filename, mime) -> HttpResponse:
|
||||
"""
|
||||
|
@ -45,7 +45,19 @@ if typing.TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
OT_USERSERVICE, OT_PUBLICATION, OT_DEPLOYED_SERVICE, OT_SERVICE, OT_PROVIDER, OT_USER, OT_GROUP, OT_AUTHENTICATOR, OT_METAPOOL = range(9) # @UndefinedVariable
|
||||
(
|
||||
OT_USERSERVICE,
|
||||
OT_PUBLICATION,
|
||||
OT_DEPLOYED_SERVICE,
|
||||
OT_SERVICE,
|
||||
OT_PROVIDER,
|
||||
OT_USER,
|
||||
OT_GROUP,
|
||||
OT_AUTHENTICATOR,
|
||||
OT_METAPOOL,
|
||||
) = range(
|
||||
9
|
||||
) # @UndefinedVariable
|
||||
|
||||
# Dict for translations
|
||||
transDict: typing.Dict[typing.Type['Model'], int] = {
|
||||
@ -65,6 +77,7 @@ class LogManager:
|
||||
"""
|
||||
Manager for logging (at database) events
|
||||
"""
|
||||
|
||||
_manager: typing.Optional['LogManager'] = None
|
||||
|
||||
def __init__(self):
|
||||
@ -76,7 +89,15 @@ class LogManager:
|
||||
LogManager._manager = LogManager()
|
||||
return LogManager._manager
|
||||
|
||||
def __log(self, owner_type: int, owner_id: int, level: int, message: str, source: str, avoidDuplicates: bool):
|
||||
def __log(
|
||||
self,
|
||||
owner_type: int,
|
||||
owner_id: int,
|
||||
level: int,
|
||||
message: str,
|
||||
source: str,
|
||||
avoidDuplicates: bool,
|
||||
):
|
||||
"""
|
||||
Logs a message associated to owner
|
||||
"""
|
||||
@ -86,12 +107,16 @@ class LogManager:
|
||||
qs = models.Log.objects.filter(owner_id=owner_id, owner_type=owner_type)
|
||||
# First, ensure we do not have more than requested logs, and we can put one more log item
|
||||
if qs.count() >= GlobalConfig.MAX_LOGS_PER_ELEMENT.getInt():
|
||||
for i in qs.order_by('-created',)[GlobalConfig.MAX_LOGS_PER_ELEMENT.getInt() - 1:]:
|
||||
for i in qs.order_by(
|
||||
'-created',
|
||||
)[GlobalConfig.MAX_LOGS_PER_ELEMENT.getInt() - 1 :]:
|
||||
i.delete()
|
||||
|
||||
if avoidDuplicates:
|
||||
try:
|
||||
lg = models.Log.objects.filter(owner_id=owner_id, owner_type=owner_type, level=level, source=source).order_by('-created', '-id')[0]
|
||||
lg = models.Log.objects.filter(
|
||||
owner_id=owner_id, owner_type=owner_type, level=level, source=source
|
||||
).order_by('-created', '-id')[0]
|
||||
if lg.data == message:
|
||||
# Do not log again, already logged
|
||||
return
|
||||
@ -100,17 +125,29 @@ class LogManager:
|
||||
|
||||
# now, we add new log
|
||||
try:
|
||||
models.Log.objects.create(owner_type=owner_type, owner_id=owner_id, created=models.getSqlDatetime(), source=source, level=level, data=message)
|
||||
models.Log.objects.create(
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
created=models.getSqlDatetime(),
|
||||
source=source,
|
||||
level=level,
|
||||
data=message,
|
||||
)
|
||||
except Exception:
|
||||
# Some objects will not get logged, such as System administrator objects, but this is fine
|
||||
pass
|
||||
|
||||
def __getLogs(self, owner_type: int, owner_id: int, limit: int) -> typing.List[typing.Dict]:
|
||||
def __getLogs(
|
||||
self, owner_type: int, owner_id: int, limit: int
|
||||
) -> typing.List[typing.Dict]:
|
||||
"""
|
||||
Get all logs associated with an user service, ordered by date
|
||||
"""
|
||||
qs = models.Log.objects.filter(owner_id=owner_id, owner_type=owner_type)
|
||||
return [{'date': x.created, 'level': x.level, 'source': x.source, 'message': x.data} for x in reversed(qs.order_by('-created', '-id')[:limit])]
|
||||
return [
|
||||
{'date': x.created, 'level': x.level, 'source': x.source, 'message': x.data}
|
||||
for x in reversed(qs.order_by('-created', '-id')[:limit])
|
||||
]
|
||||
|
||||
def __clearLogs(self, owner_type: int, owner_id: int):
|
||||
"""
|
||||
@ -118,7 +155,14 @@ class LogManager:
|
||||
"""
|
||||
models.Log.objects.filter(owner_id=owner_id, owner_type=owner_type).delete()
|
||||
|
||||
def doLog(self, wichObject: 'Model', level: int, message: str, source: str, avoidDuplicates: bool = True):
|
||||
def doLog(
|
||||
self,
|
||||
wichObject: 'Model',
|
||||
level: int,
|
||||
message: str,
|
||||
source: str,
|
||||
avoidDuplicates: bool = True,
|
||||
):
|
||||
"""
|
||||
Do the logging for the requested object.
|
||||
|
||||
@ -129,7 +173,9 @@ class LogManager:
|
||||
if owner_type is not None:
|
||||
self.__log(owner_type, wichObject.id, level, message, source, avoidDuplicates) # type: ignore
|
||||
else:
|
||||
logger.debug('Requested doLog for a type of object not covered: %s', wichObject)
|
||||
logger.debug(
|
||||
'Requested doLog for a type of object not covered: %s', wichObject
|
||||
)
|
||||
|
||||
def getLogs(self, wichObject: 'Model', limit: int) -> typing.List[typing.Dict]:
|
||||
"""
|
||||
@ -142,7 +188,9 @@ class LogManager:
|
||||
if owner_type is not None: # 0 is valid owner type
|
||||
return self.__getLogs(owner_type, wichObject.id, limit) # type: ignore
|
||||
|
||||
logger.debug('Requested getLogs for a type of object not covered: %s', wichObject)
|
||||
logger.debug(
|
||||
'Requested getLogs for a type of object not covered: %s', wichObject
|
||||
)
|
||||
return []
|
||||
|
||||
def clearLogs(self, wichObject: 'Model'):
|
||||
@ -156,4 +204,6 @@ class LogManager:
|
||||
if owner_type:
|
||||
self.__clearLogs(owner_type, wichObject.id) # type: ignore
|
||||
else:
|
||||
logger.debug('Requested clearLogs for a type of object not covered: %s', wichObject)
|
||||
logger.debug(
|
||||
'Requested clearLogs for a type of object not covered: %s', wichObject
|
||||
)
|
||||
|
@ -64,13 +64,19 @@ class PublicationOldMachinesCleaner(DelayedTask):
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
servicePoolPub: ServicePoolPublication = ServicePoolPublication.objects.get(pk=self._id)
|
||||
servicePoolPub: ServicePoolPublication = ServicePoolPublication.objects.get(
|
||||
pk=self._id
|
||||
)
|
||||
if servicePoolPub.state != State.REMOVABLE:
|
||||
logger.info('Already removed')
|
||||
|
||||
now = getSqlDatetime()
|
||||
activePub: typing.Optional[ServicePoolPublication] = servicePoolPub.deployed_service.activePublication()
|
||||
servicePoolPub.deployed_service.userServices.filter(in_use=True).update(in_use=False, state_date=now)
|
||||
activePub: typing.Optional[
|
||||
ServicePoolPublication
|
||||
] = servicePoolPub.deployed_service.activePublication()
|
||||
servicePoolPub.deployed_service.userServices.filter(in_use=True).update(
|
||||
in_use=False, state_date=now
|
||||
)
|
||||
servicePoolPub.deployed_service.markOldUserServicesAsRemovables(activePub)
|
||||
except Exception:
|
||||
pass
|
||||
@ -92,8 +98,12 @@ class PublicationLauncher(DelayedTask):
|
||||
try:
|
||||
now = getSqlDatetime()
|
||||
with transaction.atomic():
|
||||
servicePoolPub = ServicePoolPublication.objects.select_for_update().get(pk=self._publicationId)
|
||||
if servicePoolPub.state != State.LAUNCHING: # If not preparing (may has been canceled by user) just return
|
||||
servicePoolPub = ServicePoolPublication.objects.select_for_update().get(
|
||||
pk=self._publicationId
|
||||
)
|
||||
if (
|
||||
servicePoolPub.state != State.LAUNCHING
|
||||
): # If not preparing (may has been canceled by user) just return
|
||||
return
|
||||
servicePoolPub.state = State.PREPARING
|
||||
servicePoolPub.save()
|
||||
@ -101,7 +111,15 @@ class PublicationLauncher(DelayedTask):
|
||||
state = pi.publish()
|
||||
servicePool: ServicePool = servicePoolPub.deployed_service
|
||||
servicePool.current_pub_revision += 1
|
||||
servicePool.storeValue('toBeReplacedIn', pickle.dumps(now + datetime.timedelta(hours=GlobalConfig.SESSION_EXPIRE_TIME.getInt(True))))
|
||||
servicePool.storeValue(
|
||||
'toBeReplacedIn',
|
||||
pickle.dumps(
|
||||
now
|
||||
+ datetime.timedelta(
|
||||
hours=GlobalConfig.SESSION_EXPIRE_TIME.getInt(True)
|
||||
)
|
||||
),
|
||||
)
|
||||
servicePool.save()
|
||||
PublicationFinishChecker.checkAndUpdateState(servicePoolPub, pi, state)
|
||||
except ServicePoolPublication.DoesNotExist: # Deployed service publication has been removed from database, this is ok, just ignore it
|
||||
@ -128,7 +146,11 @@ class PublicationFinishChecker(DelayedTask):
|
||||
self._state = publication.state
|
||||
|
||||
@staticmethod
|
||||
def checkAndUpdateState(publication: ServicePoolPublication, publicationInstance: 'services.Publication', state: str) -> None:
|
||||
def checkAndUpdateState(
|
||||
publication: ServicePoolPublication,
|
||||
publicationInstance: 'services.Publication',
|
||||
state: str,
|
||||
) -> None:
|
||||
"""
|
||||
Checks the value returned from invocation to publish or checkPublishingState, updating the servicePoolPub database object
|
||||
Return True if it has to continue checking, False if finished
|
||||
@ -140,20 +162,33 @@ class PublicationFinishChecker(DelayedTask):
|
||||
# Now we mark, if it exists, the previous usable publication as "Removable"
|
||||
if State.isPreparing(prevState):
|
||||
old: ServicePoolPublication
|
||||
for old in publication.deployed_service.publications.filter(state=State.USABLE):
|
||||
for old in publication.deployed_service.publications.filter(
|
||||
state=State.USABLE
|
||||
):
|
||||
old.setState(State.REMOVABLE)
|
||||
|
||||
osm = publication.deployed_service.osmanager
|
||||
# If os manager says "machine is persistent", do not tray to delete "previous version" assigned machines
|
||||
doPublicationCleanup = True if osm is None else not osm.getInstance().isPersistent()
|
||||
doPublicationCleanup = (
|
||||
True
|
||||
if osm is None
|
||||
else not osm.getInstance().isPersistent()
|
||||
)
|
||||
|
||||
if doPublicationCleanup:
|
||||
pc = PublicationOldMachinesCleaner(old.id)
|
||||
pc.register(GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) * 3600, 'pclean-' + str(old.id), True)
|
||||
publication.deployed_service.markOldUserServicesAsRemovables(publication)
|
||||
pc.register(
|
||||
GlobalConfig.SESSION_EXPIRE_TIME.getInt(True) * 3600,
|
||||
'pclean-' + str(old.id),
|
||||
True,
|
||||
)
|
||||
publication.deployed_service.markOldUserServicesAsRemovables(
|
||||
publication
|
||||
)
|
||||
else: # Remove only cache services, not assigned
|
||||
publication.deployed_service.markOldUserServicesAsRemovables(publication, True)
|
||||
|
||||
publication.deployed_service.markOldUserServicesAsRemovables(
|
||||
publication, True
|
||||
)
|
||||
|
||||
publication.setState(State.USABLE)
|
||||
elif State.isRemoving(prevState):
|
||||
@ -178,36 +213,53 @@ class PublicationFinishChecker(DelayedTask):
|
||||
PublicationFinishChecker.checkLater(publication, publicationInstance)
|
||||
|
||||
@staticmethod
|
||||
def checkLater(publication: ServicePoolPublication, publicationInstance: 'services.Publication'):
|
||||
def checkLater(
|
||||
publication: ServicePoolPublication, publicationInstance: 'services.Publication'
|
||||
):
|
||||
"""
|
||||
Inserts a task in the delayedTaskRunner so we can check the state of this publication
|
||||
@param dps: Database object for ServicePoolPublication
|
||||
@param pi: Instance of Publication manager for the object
|
||||
"""
|
||||
DelayedTaskRunner.runner().insert(PublicationFinishChecker(publication), publicationInstance.suggestedTime, PUBTAG + str(publication.id))
|
||||
DelayedTaskRunner.runner().insert(
|
||||
PublicationFinishChecker(publication),
|
||||
publicationInstance.suggestedTime,
|
||||
PUBTAG + str(publication.id),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
logger.debug('Checking publication finished %s', self._publishId)
|
||||
try:
|
||||
publication: ServicePoolPublication = ServicePoolPublication.objects.get(pk=self._publishId)
|
||||
publication: ServicePoolPublication = ServicePoolPublication.objects.get(
|
||||
pk=self._publishId
|
||||
)
|
||||
if publication.state != self._state:
|
||||
logger.debug('Task overrided by another task (state of item changed)')
|
||||
else:
|
||||
publicationInstance = publication.getInstance()
|
||||
logger.debug("publication instance class: %s", publicationInstance.__class__)
|
||||
logger.debug(
|
||||
"publication instance class: %s", publicationInstance.__class__
|
||||
)
|
||||
try:
|
||||
state = publicationInstance.checkState()
|
||||
except Exception:
|
||||
state = State.ERROR
|
||||
PublicationFinishChecker.checkAndUpdateState(publication, publicationInstance, state)
|
||||
PublicationFinishChecker.checkAndUpdateState(
|
||||
publication, publicationInstance, state
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug('Deployed service not found (erased from database) %s : %s', e.__class__, e)
|
||||
logger.debug(
|
||||
'Deployed service not found (erased from database) %s : %s',
|
||||
e.__class__,
|
||||
e,
|
||||
)
|
||||
|
||||
|
||||
class PublicationManager:
|
||||
"""
|
||||
Manager responsible of controlling publications
|
||||
"""
|
||||
|
||||
_manager: typing.Optional['PublicationManager'] = None
|
||||
|
||||
def __init__(self):
|
||||
@ -222,26 +274,43 @@ class PublicationManager:
|
||||
PublicationManager._manager = PublicationManager()
|
||||
return PublicationManager._manager
|
||||
|
||||
def publish(self, servicePool: ServicePool, changeLog: typing.Optional[str] = None): # pylint: disable=no-self-use
|
||||
def publish(
|
||||
self, servicePool: ServicePool, changeLog: typing.Optional[str] = None
|
||||
): # pylint: disable=no-self-use
|
||||
"""
|
||||
Initiates the publication of a service pool, or raises an exception if this cannot be done
|
||||
:param servicePool: Service pool object (db object)
|
||||
:param changeLog: if not None, store change log string on "change log" table
|
||||
"""
|
||||
if servicePool.publications.filter(state__in=State.PUBLISH_STATES).count() > 0:
|
||||
raise PublishException(_('Already publishing. Wait for previous publication to finish and try again'))
|
||||
raise PublishException(
|
||||
_(
|
||||
'Already publishing. Wait for previous publication to finish and try again'
|
||||
)
|
||||
)
|
||||
|
||||
if servicePool.isInMaintenance():
|
||||
raise PublishException(_('Service is in maintenance mode and new publications are not allowed'))
|
||||
raise PublishException(
|
||||
_('Service is in maintenance mode and new publications are not allowed')
|
||||
)
|
||||
|
||||
publication: typing.Optional[ServicePoolPublication] = None
|
||||
try:
|
||||
now = getSqlDatetime()
|
||||
publication = servicePool.publications.create(state=State.LAUNCHING, state_date=now, publish_date=now, revision=servicePool.current_pub_revision)
|
||||
publication = servicePool.publications.create(
|
||||
state=State.LAUNCHING,
|
||||
state_date=now,
|
||||
publish_date=now,
|
||||
revision=servicePool.current_pub_revision,
|
||||
)
|
||||
if changeLog:
|
||||
servicePool.changelog.create(revision=servicePool.current_pub_revision, log=changeLog, stamp=now)
|
||||
servicePool.changelog.create(
|
||||
revision=servicePool.current_pub_revision, log=changeLog, stamp=now
|
||||
)
|
||||
if publication:
|
||||
DelayedTaskRunner.runner().insert(PublicationLauncher(publication), 4, PUBTAG + str(publication.id))
|
||||
DelayedTaskRunner.runner().insert(
|
||||
PublicationLauncher(publication), 4, PUBTAG + str(publication.id)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug('Caught exception at publish: %s', e)
|
||||
if publication is not None:
|
||||
@ -251,17 +320,26 @@ class PublicationManager:
|
||||
logger.info('Could not delete %s', publication)
|
||||
raise PublishException(str(e))
|
||||
|
||||
def cancel(self, publication: ServicePoolPublication): # pylint: disable=no-self-use
|
||||
def cancel(
|
||||
self, publication: ServicePoolPublication
|
||||
): # pylint: disable=no-self-use
|
||||
"""
|
||||
Invoked to cancel a publication.
|
||||
Double invokation (i.e. invokation over a "cancelling" item) will lead to a "forced" cancellation (unclean)
|
||||
:param servicePoolPub: Service pool publication (db object for a publication)
|
||||
"""
|
||||
publication = ServicePoolPublication.objects.get(pk=publication.id) # Reloads publication from db
|
||||
publication = ServicePoolPublication.objects.get(
|
||||
pk=publication.id
|
||||
) # Reloads publication from db
|
||||
if publication.state not in State.PUBLISH_STATES:
|
||||
if publication.state == State.CANCELING: # Double cancel
|
||||
logger.info('Double cancel invoked for a publication')
|
||||
log.doLog(publication.deployed_service, log.WARN, 'Forced cancel on publication, you must check uncleaned resources manually', log.ADMIN)
|
||||
log.doLog(
|
||||
publication.deployed_service,
|
||||
log.WARN,
|
||||
'Forced cancel on publication, you must check uncleaned resources manually',
|
||||
log.ADMIN,
|
||||
)
|
||||
publication.setState(State.CANCELED)
|
||||
publication.save()
|
||||
return publication
|
||||
@ -277,25 +355,35 @@ class PublicationManager:
|
||||
pubInstance = publication.getInstance()
|
||||
state = pubInstance.cancel()
|
||||
publication.setState(State.CANCELING)
|
||||
PublicationFinishChecker.checkAndUpdateState(publication, pubInstance, state)
|
||||
PublicationFinishChecker.checkAndUpdateState(
|
||||
publication, pubInstance, state
|
||||
)
|
||||
return publication
|
||||
except Exception as e:
|
||||
raise PublishException(str(e))
|
||||
|
||||
def unpublish(self, servicePoolPub: ServicePoolPublication): # pylint: disable=no-self-use
|
||||
def unpublish(
|
||||
self, servicePoolPub: ServicePoolPublication
|
||||
): # pylint: disable=no-self-use
|
||||
"""
|
||||
Unpublishes an active (usable) or removable publication
|
||||
:param servicePoolPub: Publication to unpublish
|
||||
"""
|
||||
if State.isUsable(servicePoolPub.state) is False and State.isRemovable(servicePoolPub.state) is False:
|
||||
raise PublishException(_('Can\'t unpublish non usable publication')
|
||||
)
|
||||
if (
|
||||
State.isUsable(servicePoolPub.state) is False
|
||||
and State.isRemovable(servicePoolPub.state) is False
|
||||
):
|
||||
raise PublishException(_('Can\'t unpublish non usable publication'))
|
||||
if servicePoolPub.userServices.exclude(state__in=State.INFO_STATES).count() > 0:
|
||||
raise PublishException(_('Can\'t unpublish publications with services in process'))
|
||||
raise PublishException(
|
||||
_('Can\'t unpublish publications with services in process')
|
||||
)
|
||||
try:
|
||||
pubInstance = servicePoolPub.getInstance()
|
||||
state = pubInstance.destroy()
|
||||
servicePoolPub.setState(State.REMOVING)
|
||||
PublicationFinishChecker.checkAndUpdateState(servicePoolPub, pubInstance, state)
|
||||
PublicationFinishChecker.checkAndUpdateState(
|
||||
servicePoolPub, pubInstance, state
|
||||
)
|
||||
except Exception as e:
|
||||
raise PublishException(str(e))
|
||||
|
@ -52,6 +52,7 @@ REVERSE_FLDS_EQUIV: typing.Mapping[str, str] = {
|
||||
i: fld for fld, aliases in FLDS_EQUIV.items() for i in aliases
|
||||
}
|
||||
|
||||
|
||||
class StatsManager:
|
||||
"""
|
||||
Manager for statistics, so we can provide usefull info about platform usage
|
||||
@ -60,6 +61,7 @@ class StatsManager:
|
||||
that has counters (such as how many users is at a time active at platform, how many services
|
||||
are assigned, are in use, in cache, etc...
|
||||
"""
|
||||
|
||||
_manager: typing.Optional['StatsManager'] = None
|
||||
|
||||
def __init__(self):
|
||||
@ -72,11 +74,23 @@ class StatsManager:
|
||||
return StatsManager._manager
|
||||
|
||||
def __doCleanup(self, model):
|
||||
minTime = time.mktime((getSqlDatetime() - datetime.timedelta(days=GlobalConfig.STATS_DURATION.getInt())).timetuple())
|
||||
minTime = time.mktime(
|
||||
(
|
||||
getSqlDatetime()
|
||||
- datetime.timedelta(days=GlobalConfig.STATS_DURATION.getInt())
|
||||
).timetuple()
|
||||
)
|
||||
model.objects.filter(stamp__lt=minTime).delete()
|
||||
|
||||
# Counter stats
|
||||
def addCounter(self, owner_type: int, owner_id: int, counterType: int, counterValue: int, stamp: typing.Optional[datetime.datetime] = None) -> bool:
|
||||
def addCounter(
|
||||
self,
|
||||
owner_type: int,
|
||||
owner_id: int,
|
||||
counterType: int,
|
||||
counterValue: int,
|
||||
stamp: typing.Optional[datetime.datetime] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Adds a new counter stats to database.
|
||||
|
||||
@ -97,13 +111,23 @@ class StatsManager:
|
||||
stamp = typing.cast(datetime.datetime, getSqlDatetime())
|
||||
|
||||
# To Unix epoch
|
||||
stampInt = int(time.mktime(stamp.timetuple())) # pylint: disable=maybe-no-member
|
||||
stampInt = int(
|
||||
time.mktime(stamp.timetuple())
|
||||
) # pylint: disable=maybe-no-member
|
||||
|
||||
try:
|
||||
StatsCounters.objects.create(owner_type=owner_type, owner_id=owner_id, counter_type=counterType, value=counterValue, stamp=stampInt)
|
||||
StatsCounters.objects.create(
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
counter_type=counterType,
|
||||
value=counterValue,
|
||||
stamp=stampInt,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
logger.error('Exception handling counter stats saving (maybe database is full?)')
|
||||
logger.error(
|
||||
'Exception handling counter stats saving (maybe database is full?)'
|
||||
)
|
||||
return False
|
||||
|
||||
def getCounters(
|
||||
@ -116,7 +140,7 @@ class StatsManager:
|
||||
interval: typing.Optional[int],
|
||||
max_intervals: typing.Optional[int],
|
||||
limit: typing.Optional[int],
|
||||
use_max: bool = False
|
||||
use_max: bool = False,
|
||||
) -> typing.Iterable:
|
||||
"""
|
||||
Retrieves counters from item
|
||||
@ -146,7 +170,7 @@ class StatsManager:
|
||||
interval=interval,
|
||||
max_intervals=max_intervals,
|
||||
limit=limit,
|
||||
use_max=use_max
|
||||
use_max=use_max,
|
||||
)
|
||||
|
||||
def cleanupCounters(self):
|
||||
@ -183,9 +207,12 @@ class StatsManager:
|
||||
stamp = getSqlDatetimeAsUnix()
|
||||
else:
|
||||
# To Unix epoch
|
||||
stamp = int(time.mktime(stamp.timetuple())) # pylint: disable=maybe-no-member
|
||||
stamp = int(
|
||||
time.mktime(stamp.timetuple())
|
||||
) # pylint: disable=maybe-no-member
|
||||
|
||||
try:
|
||||
|
||||
def getKwarg(fld: str) -> str:
|
||||
val = None
|
||||
for i in FLDS_EQUIV[fld]:
|
||||
@ -199,13 +226,29 @@ class StatsManager:
|
||||
fld3 = getKwarg('fld3')
|
||||
fld4 = getKwarg('fld4')
|
||||
|
||||
StatsEvents.objects.create(owner_type=owner_type, owner_id=owner_id, event_type=eventType, stamp=stamp, fld1=fld1, fld2=fld2, fld3=fld3, fld4=fld4)
|
||||
StatsEvents.objects.create(
|
||||
owner_type=owner_type,
|
||||
owner_id=owner_id,
|
||||
event_type=eventType,
|
||||
stamp=stamp,
|
||||
fld1=fld1,
|
||||
fld2=fld2,
|
||||
fld3=fld3,
|
||||
fld4=fld4,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
logger.exception('Exception handling event stats saving (maybe database is full?)')
|
||||
logger.exception(
|
||||
'Exception handling event stats saving (maybe database is full?)'
|
||||
)
|
||||
return False
|
||||
|
||||
def getEvents(self, ownerType: typing.Union[int, typing.Iterable[int]], eventType: typing.Union[int, typing.Iterable[int]], **kwargs):
|
||||
def getEvents(
|
||||
self,
|
||||
ownerType: typing.Union[int, typing.Iterable[int]],
|
||||
eventType: typing.Union[int, typing.Iterable[int]],
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Retrieves counters from item
|
||||
|
||||
|
@ -44,10 +44,12 @@ from uds.core.util.config import GlobalConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseThread(threading.Thread):
|
||||
def notifyTermination(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SchedulerThread(BaseThread):
|
||||
def run(self):
|
||||
Scheduler.scheduler().run()
|
||||
@ -109,7 +111,9 @@ class TaskManager:
|
||||
noSchedulers: int = GlobalConfig.SCHEDULER_THREADS.getInt()
|
||||
noDelayedTasks: int = GlobalConfig.DELAYED_TASKS_THREADS.getInt()
|
||||
|
||||
logger.info('Starting %s schedulers and %s task executors', noSchedulers, noDelayedTasks)
|
||||
logger.info(
|
||||
'Starting %s schedulers and %s task executors', noSchedulers, noDelayedTasks
|
||||
)
|
||||
|
||||
threads: typing.List[BaseThread] = []
|
||||
thread: BaseThread
|
||||
|
@ -61,7 +61,9 @@ class UserPrefsManager:
|
||||
def __nameFor(self, module, name):
|
||||
return module + "_" + name
|
||||
|
||||
def registerPrefs(self, modName: str, friendlyModName: str, prefs: typing.Any) -> None:
|
||||
def registerPrefs(
|
||||
self, modName: str, friendlyModName: str, prefs: typing.Any
|
||||
) -> None:
|
||||
"""
|
||||
Register an array of preferences for a module
|
||||
"""
|
||||
@ -81,7 +83,9 @@ class UserPrefsManager:
|
||||
logger.debug('Preferences: %s', prefs)
|
||||
return prefs
|
||||
|
||||
def setPreferenceForUser(self, user: 'User', modName: str, prefName: str, value: str):
|
||||
def setPreferenceForUser(
|
||||
self, user: 'User', modName: str, prefName: str, value: str
|
||||
):
|
||||
try:
|
||||
user.preferences.create(module=modName, name=prefName, value=value) # type: ignore
|
||||
except Exception: # Already exits, update it
|
||||
@ -99,7 +103,13 @@ class UserPrefsManager:
|
||||
name = self.__nameFor(mod, p.getName())
|
||||
val = data[name] if name in data else p.getDefValue()
|
||||
form.fields[name] = p.formField(val)
|
||||
res += '<fieldset class="prefset"><legend>' + v['friendlyName'] + '</legend>' + form.as_p() + '</fieldset>'
|
||||
res += (
|
||||
'<fieldset class="prefset"><legend>'
|
||||
+ v['friendlyName']
|
||||
+ '</legend>'
|
||||
+ form.as_p()
|
||||
+ '</fieldset>'
|
||||
)
|
||||
return res
|
||||
|
||||
def getGuiForUserPreferences(self, user=None):
|
||||
@ -113,7 +123,13 @@ class UserPrefsManager:
|
||||
for p in v['prefs']:
|
||||
name = self.__nameFor(mod, p.getName())
|
||||
val = data[name] if name in data else p.getDefValue()
|
||||
grp.append({'name': name, 'gui': p.guiField(val).guiDescription(), 'value': val})
|
||||
grp.append(
|
||||
{
|
||||
'name': name,
|
||||
'gui': p.guiField(val).guiDescription(),
|
||||
'value': val,
|
||||
}
|
||||
)
|
||||
res.append({'moduleLabel': v['friendlyName'], 'prefs': grp})
|
||||
return res
|
||||
|
||||
@ -136,11 +152,19 @@ class UserPrefsManager:
|
||||
for p in v['prefs']:
|
||||
name = self.__nameFor(mod, p.getName())
|
||||
logger.debug(name)
|
||||
prefs.append({'module': mod, 'name': p.getName(), 'value': form.cleaned_data[name]})
|
||||
prefs.append(
|
||||
{
|
||||
'module': mod,
|
||||
'name': p.getName(),
|
||||
'value': form.cleaned_data[name],
|
||||
}
|
||||
)
|
||||
user.preferences.all().delete()
|
||||
try:
|
||||
for p in prefs:
|
||||
user.preferences.create(module=p['module'], name=p['name'], value=p['value'])
|
||||
user.preferences.create(
|
||||
module=p['module'], name=p['name'], value=p['value']
|
||||
)
|
||||
except Exception: # User does not exists
|
||||
logger.info('Trying to dave user preferences failed (probably root user?)')
|
||||
return None
|
||||
@ -156,10 +180,14 @@ class UserPrefsManager:
|
||||
for p in v['prefs']:
|
||||
name = self.__nameFor(mod, p.getName())
|
||||
if name in data:
|
||||
prefs.append({'module': mod, 'name': p.getName(), 'value': data[name]})
|
||||
prefs.append(
|
||||
{'module': mod, 'name': p.getName(), 'value': data[name]}
|
||||
)
|
||||
user.preferences.all().delete()
|
||||
for p in prefs:
|
||||
user.preferences.create(module=p['module'], name=p['name'], value=p['value'])
|
||||
user.preferences.create(
|
||||
module=p['module'], name=p['name'], value=p['value']
|
||||
)
|
||||
|
||||
|
||||
class UserPreference(object):
|
||||
@ -190,7 +218,6 @@ class UserPreference(object):
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class CommonPrefs(object):
|
||||
SZ_PREF = 'screenSize'
|
||||
SZ_640x480 = '1'
|
||||
@ -219,7 +246,7 @@ class CommonPrefs(object):
|
||||
CommonPrefs.SZ_1024x768: (1024, 768),
|
||||
CommonPrefs.SZ_1366x768: (1366, 768),
|
||||
CommonPrefs.SZ_1920x1080: (1920, 1080),
|
||||
CommonPrefs.SZ_FULLSCREEN: (-1, -1)
|
||||
CommonPrefs.SZ_FULLSCREEN: (-1, -1),
|
||||
}.get(size, (1024, 768))
|
||||
|
||||
@staticmethod
|
||||
@ -231,5 +258,5 @@ class CommonPrefs(object):
|
||||
CommonPrefs.DEPTH_8: 8,
|
||||
CommonPrefs.DEPTH_16: 16,
|
||||
CommonPrefs.DEPTH_24: 24,
|
||||
CommonPrefs.DEPTH_32: 32
|
||||
CommonPrefs.DEPTH_32: 32,
|
||||
}.get(depth, 24)
|
||||
|
@ -928,14 +928,18 @@ class UserServiceManager:
|
||||
raise ServiceAccessDeniedByCalendar()
|
||||
|
||||
# Get pool members. Just pools "visible" and "usable"
|
||||
poolMembers = [p for p in meta.members.all() if p.pool.isVisible() and p.pool.isUsable()]
|
||||
poolMembers = [
|
||||
p for p in meta.members.all() if p.pool.isVisible() and p.pool.isUsable()
|
||||
]
|
||||
# Sort pools based on meta selection
|
||||
if meta.policy == MetaPool.PRIORITY_POOL:
|
||||
sortPools = [(p.priority, p.pool) for p in poolMembers]
|
||||
elif meta.policy == MetaPool.MOST_AVAILABLE_BY_NUMBER:
|
||||
sortPools = [(p.pool.usage(), p.pool) for p in poolMembers]
|
||||
else:
|
||||
sortPools = [(random.randint(0, 10000), p.pool) for p in poolMembers] # Just shuffle them
|
||||
sortPools = [
|
||||
(random.randint(0, 10000), p.pool) for p in poolMembers
|
||||
] # Just shuffle them
|
||||
|
||||
# Sort pools related to policy now, and xtract only pools, not sort keys
|
||||
# Remove "full" pools (100%) from result and pools in maintenance mode, not ready pools, etc...
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019-2020 Virtual Cable S.L.
|
||||
# Copyright (c) 2019-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -11,7 +11,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -38,7 +38,7 @@ import typing
|
||||
import requests
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.models import UserService, Proxy
|
||||
from uds.models import UserService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -51,9 +51,17 @@ class StateUpdater:
|
||||
userService: UserService
|
||||
userServiceInstalce: UserDeployment
|
||||
|
||||
def __init__(self, userService: UserService, userServiceInstance: typing.Optional[UserDeployment] = None):
|
||||
def __init__(
|
||||
self,
|
||||
userService: UserService,
|
||||
userServiceInstance: typing.Optional[UserDeployment] = None,
|
||||
):
|
||||
self.userService = userService
|
||||
self.userServiceInstance = userServiceInstance if userServiceInstance is not None else userService.getInstance()
|
||||
self.userServiceInstance = (
|
||||
userServiceInstance
|
||||
if userServiceInstance is not None
|
||||
else userService.getInstance()
|
||||
)
|
||||
|
||||
def setError(self, msg: typing.Optional[str] = None):
|
||||
logger.error('Got error on processor: %s', msg)
|
||||
@ -80,10 +88,15 @@ class StateUpdater:
|
||||
executor = {
|
||||
State.RUNNING: self.running,
|
||||
State.ERROR: self.error,
|
||||
State.FINISHED: self.finish
|
||||
State.FINISHED: self.finish,
|
||||
}.get(state, self.error)
|
||||
|
||||
logger.debug('Running Executor for %s with state %s and executor %s', self.userService.friendly_name, State.toString(state), executor)
|
||||
logger.debug(
|
||||
'Running Executor for %s with state %s and executor %s',
|
||||
self.userService.friendly_name,
|
||||
State.toString(state),
|
||||
executor,
|
||||
)
|
||||
|
||||
try:
|
||||
executor()
|
||||
@ -104,7 +117,6 @@ class StateUpdater:
|
||||
|
||||
|
||||
class UpdateFromPreparing(StateUpdater):
|
||||
|
||||
def checkOsManagerRelated(self) -> str:
|
||||
osManager = self.userServiceInstance.osmanager()
|
||||
|
||||
@ -120,7 +132,12 @@ class UpdateFromPreparing(StateUpdater):
|
||||
else:
|
||||
stateOs = State.FINISHED
|
||||
|
||||
logger.debug('State %s, StateOS %s for %s', State.toString(state), State.toString(stateOs), self.userService.friendly_name)
|
||||
logger.debug(
|
||||
'State %s, StateOS %s for %s',
|
||||
State.toString(state),
|
||||
State.toString(stateOs),
|
||||
self.userService.friendly_name,
|
||||
)
|
||||
if stateOs == State.RUNNING:
|
||||
self.userService.setOsState(State.PREPARING)
|
||||
else:
|
||||
@ -130,7 +147,9 @@ class UpdateFromPreparing(StateUpdater):
|
||||
rs = self.userServiceInstance.notifyReadyFromOsManager('')
|
||||
if rs != State.FINISHED:
|
||||
self.checkLater()
|
||||
state = self.userService.state # No not alter current state if after notifying os manager the user service keeps working
|
||||
state = (
|
||||
self.userService.state
|
||||
) # No not alter current state if after notifying os manager the user service keeps working
|
||||
else:
|
||||
self.logIp()
|
||||
|
||||
@ -142,7 +161,9 @@ class UpdateFromPreparing(StateUpdater):
|
||||
self.save(State.REMOVABLE) # And start removing it
|
||||
return
|
||||
|
||||
state = State.REMOVABLE # By default, if not valid publication, service will be marked for removal on preparation finished
|
||||
state = (
|
||||
State.REMOVABLE
|
||||
) # By default, if not valid publication, service will be marked for removal on preparation finished
|
||||
if self.userService.isValidPublication():
|
||||
logger.debug('Publication is valid for %s', self.userService.friendly_name)
|
||||
state = self.checkOsManagerRelated()
|
||||
@ -153,6 +174,7 @@ class UpdateFromPreparing(StateUpdater):
|
||||
|
||||
self.save(state)
|
||||
|
||||
|
||||
class UpdateFromRemoving(StateUpdater):
|
||||
def finish(self):
|
||||
osManager = self.userServiceInstance.osmanager()
|
||||
@ -161,6 +183,7 @@ class UpdateFromRemoving(StateUpdater):
|
||||
|
||||
self.save(State.REMOVED)
|
||||
|
||||
|
||||
class UpdateFromCanceling(StateUpdater):
|
||||
def finish(self):
|
||||
osManager = self.userServiceInstance.osmanager()
|
||||
@ -169,50 +192,76 @@ class UpdateFromCanceling(StateUpdater):
|
||||
|
||||
self.save(State.CANCELED)
|
||||
|
||||
|
||||
class UpdateFromOther(StateUpdater):
|
||||
def finish(self):
|
||||
self.setError('Unknown running transition from {}'.format(State.toString(self.userService.state)))
|
||||
self.setError(
|
||||
'Unknown running transition from {}'.format(
|
||||
State.toString(self.userService.state)
|
||||
)
|
||||
)
|
||||
|
||||
def running(self):
|
||||
self.setError('Unknown running transition from {}'.format(State.toString(self.userService.state)))
|
||||
self.setError(
|
||||
'Unknown running transition from {}'.format(
|
||||
State.toString(self.userService.state)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class UserServiceOpChecker(DelayedTask):
|
||||
"""
|
||||
This is the delayed task responsible of executing the service tasks and the service state transitions
|
||||
"""
|
||||
|
||||
def __init__(self, service):
|
||||
super().__init__()
|
||||
self._svrId = service.id
|
||||
self._state = service.state
|
||||
|
||||
@staticmethod
|
||||
def makeUnique(userService: UserService, userServiceInstance: UserDeployment, state: str):
|
||||
def makeUnique(
|
||||
userService: UserService, userServiceInstance: UserDeployment, state: str
|
||||
):
|
||||
"""
|
||||
This method ensures that there will be only one delayedtask related to the userService indicated
|
||||
"""
|
||||
DelayedTaskRunner.runner().remove(USERSERVICE_TAG + userService.uuid)
|
||||
UserServiceOpChecker.checkAndUpdateState(userService, userServiceInstance, state)
|
||||
UserServiceOpChecker.checkAndUpdateState(
|
||||
userService, userServiceInstance, state
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def checkAndUpdateState(userService: UserService, userServiceInstance: UserDeployment, state: str):
|
||||
def checkAndUpdateState(
|
||||
userService: UserService, userServiceInstance: UserDeployment, state: str
|
||||
):
|
||||
"""
|
||||
Checks the value returned from invocation to publish or checkPublishingState, updating the servicePoolPub database object
|
||||
Return True if it has to continue checking, False if finished
|
||||
"""
|
||||
try:
|
||||
# Fills up basic data
|
||||
userService.unique_id = userServiceInstance.getUniqueId() # Updates uniqueId
|
||||
userService.friendly_name = userServiceInstance.getName() # And name, both methods can modify serviceInstance, so we save it later
|
||||
userService.unique_id = (
|
||||
userServiceInstance.getUniqueId()
|
||||
) # Updates uniqueId
|
||||
userService.friendly_name = (
|
||||
userServiceInstance.getName()
|
||||
) # And name, both methods can modify serviceInstance, so we save it later
|
||||
userService.save(update_fields=['unique_id', 'friendly_name'])
|
||||
|
||||
updater = {
|
||||
State.PREPARING: UpdateFromPreparing,
|
||||
State.REMOVING: UpdateFromRemoving,
|
||||
State.CANCELING: UpdateFromCanceling
|
||||
State.CANCELING: UpdateFromCanceling,
|
||||
}.get(userService.state, UpdateFromOther)
|
||||
|
||||
logger.debug('Updating %s from %s with updater %s and state %s', userService.friendly_name, State.toString(userService.state), updater, state)
|
||||
logger.debug(
|
||||
'Updating %s from %s with updater %s and state %s',
|
||||
userService.friendly_name,
|
||||
State.toString(userService.state),
|
||||
updater,
|
||||
state,
|
||||
)
|
||||
|
||||
updater(userService, userServiceInstance).run(state)
|
||||
|
||||
@ -232,7 +281,11 @@ class UserServiceOpChecker(DelayedTask):
|
||||
# Do not add task if already exists one that updates this service
|
||||
if DelayedTaskRunner.runner().checkExists(USERSERVICE_TAG + userService.uuid):
|
||||
return
|
||||
DelayedTaskRunner.runner().insert(UserServiceOpChecker(userService), ci.suggestedTime, USERSERVICE_TAG + userService.uuid)
|
||||
DelayedTaskRunner.runner().insert(
|
||||
UserServiceOpChecker(userService),
|
||||
ci.suggestedTime,
|
||||
USERSERVICE_TAG + userService.uuid,
|
||||
)
|
||||
|
||||
def run(self):
|
||||
logger.debug('Checking user service finished %s', self._svrId)
|
||||
@ -248,14 +301,16 @@ class UserServiceOpChecker(DelayedTask):
|
||||
state = ci.checkState()
|
||||
UserServiceOpChecker.checkAndUpdateState(uService, ci, state)
|
||||
except UserService.DoesNotExist as e:
|
||||
logger.error('User service not found (erased from database?) %s : %s', e.__class__, e)
|
||||
logger.error(
|
||||
'User service not found (erased from database?) %s : %s', e.__class__, e
|
||||
)
|
||||
except Exception as e:
|
||||
# Exception caught, mark service as errored
|
||||
logger.exception("Error %s, %s :", e.__class__, e)
|
||||
if uService is not None:
|
||||
if uService:
|
||||
log.doLog(uService, log.ERROR, 'Exception: {}'.format(e), log.INTERNAL)
|
||||
try:
|
||||
uService.setState(State.ERROR)
|
||||
uService.save(update_fields=['data', 'state', 'state_date'])
|
||||
except Exception:
|
||||
logger.error('Can\'t update state of uService object')
|
||||
try:
|
||||
uService.setState(State.ERROR)
|
||||
uService.save(update_fields=['data', 'state', 'state_date'])
|
||||
except Exception:
|
||||
logger.error('Can\'t update state of uService object')
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -40,4 +40,5 @@ def factory():
|
||||
Returns factory for register/access to authenticators
|
||||
"""
|
||||
from .osmfactory import OSManagersFactory
|
||||
|
||||
return OSManagersFactory.factory()
|
||||
|
@ -34,7 +34,6 @@ body {
|
||||
background-attachment: fixed;
|
||||
background-size: 80%;
|
||||
background-position: center center;
|
||||
background-blend-mode:
|
||||
}
|
||||
|
||||
h1 { bookmark-level: none; }
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -101,9 +101,11 @@ class ServiceNotReadyError(ServiceException):
|
||||
The service is not ready
|
||||
Can include an optional code error
|
||||
"""
|
||||
|
||||
code: int
|
||||
userService: typing.Optional['UserService']
|
||||
transport: typing.Optional['Transport']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.code = kwargs.get('code', 0x0000)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -147,7 +147,12 @@ class ServiceProvider(Module):
|
||||
return _type
|
||||
return None
|
||||
|
||||
def __init__(self, environment: Environment, values: 'Module.ValuesType' = None, uuid: typing.Optional[str] = None):
|
||||
def __init__(
|
||||
self,
|
||||
environment: Environment,
|
||||
values: 'Module.ValuesType' = None,
|
||||
uuid: typing.Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Do not forget to invoke this in your derived class using "super(self.__class__, self).__init__(environment, values)"
|
||||
if you override this method. Better is to provide an "__initialize__" method, that will be invoked
|
||||
@ -177,7 +182,11 @@ class ServiceProvider(Module):
|
||||
def getMaxPreparingServices(self) -> int:
|
||||
val = self.maxPreparingServices
|
||||
if val is None:
|
||||
val = self.maxPreparingServices = GlobalConfig.MAX_PREPARING_SERVICES.getInt(force=True) # Recover global an cache till restart
|
||||
val = (
|
||||
self.maxPreparingServices
|
||||
) = GlobalConfig.MAX_PREPARING_SERVICES.getInt(
|
||||
force=True
|
||||
) # Recover global an cache till restart
|
||||
|
||||
if isinstance(val, gui.InputField):
|
||||
retVal = int(val.value)
|
||||
@ -188,7 +197,9 @@ class ServiceProvider(Module):
|
||||
def getMaxRemovingServices(self) -> int:
|
||||
val = self.maxRemovingServices
|
||||
if val is None:
|
||||
val = self.maxRemovingServices = GlobalConfig.MAX_REMOVING_SERVICES.getInt(force=True) # Recover global an cache till restart
|
||||
val = self.maxRemovingServices = GlobalConfig.MAX_REMOVING_SERVICES.getInt(
|
||||
force=True
|
||||
) # Recover global an cache till restart
|
||||
|
||||
if isinstance(val, gui.InputField):
|
||||
retVal = int(val.value)
|
||||
@ -199,7 +210,9 @@ class ServiceProvider(Module):
|
||||
def getIgnoreLimits(self) -> bool:
|
||||
val = self.ignoreLimits
|
||||
if val is None:
|
||||
val = self.ignoreLimits = GlobalConfig.IGNORE_LIMITS.getBool(force=True) # Recover global an cache till restart
|
||||
val = self.ignoreLimits = GlobalConfig.IGNORE_LIMITS.getBool(
|
||||
force=True
|
||||
) # Recover global an cache till restart
|
||||
|
||||
val = getattr(val, 'value', val)
|
||||
return val is True or val == gui.TRUE
|
||||
@ -209,8 +222,11 @@ class ServiceProvider(Module):
|
||||
Logs a message with requested level associated with this service
|
||||
"""
|
||||
from uds.models import Provider as DBProvider
|
||||
|
||||
if self.getUuid():
|
||||
log.doLog(DBProvider.objects.get(uuid=self.getUuid()), level, message, log.SERVICE)
|
||||
log.doLog(
|
||||
DBProvider.objects.get(uuid=self.getUuid()), level, message, log.SERVICE
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -46,6 +46,7 @@ class ServiceProviderFactory:
|
||||
|
||||
It provides a way to register and recover providers providers.
|
||||
"""
|
||||
|
||||
_factory: typing.Optional['ServiceProviderFactory'] = None
|
||||
|
||||
def __init__(self):
|
||||
@ -89,7 +90,12 @@ class ServiceProviderFactory:
|
||||
if s.usesCache_L2:
|
||||
s.usesCache = True
|
||||
if s.publicationType is None:
|
||||
logger.error('Provider %s offers %s, but %s needs cache and do not have publicationType defined', type_, s, s)
|
||||
logger.error(
|
||||
'Provider %s offers %s, but %s needs cache and do not have publicationType defined',
|
||||
type_,
|
||||
s,
|
||||
s,
|
||||
)
|
||||
continue
|
||||
offers.append(s)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -98,7 +98,9 @@ class Publication(Environmentable, Serializable):
|
||||
Environmentable.__init__(self, environment)
|
||||
Serializable.__init__(self)
|
||||
self._osManager = kwargs.get('osManager', None)
|
||||
self._service = kwargs['service'] # Raises an exception if service is not included
|
||||
self._service = kwargs[
|
||||
'service'
|
||||
] # Raises an exception if service is not included
|
||||
self._revision = kwargs.get('revision', -1)
|
||||
self._dbPublication = kwargs.get('dbPublication')
|
||||
self._dsName = kwargs.get('dsName', 'Unknown')
|
||||
@ -189,7 +191,11 @@ class Publication(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('publish method for class {0} not provided! '.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'publish method for class {0} not provided! '.format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def checkState(self) -> str:
|
||||
"""
|
||||
@ -215,7 +221,11 @@ class Publication(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('checkState method for class {0} not provided!!!'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'checkState method for class {0} not provided!!!'.format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def finish(self) -> None:
|
||||
"""
|
||||
@ -259,7 +269,9 @@ class Publication(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('destroy method for class {0} not provided!'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'destroy method for class {0} not provided!'.format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def cancel(self) -> str:
|
||||
"""
|
||||
@ -279,7 +291,9 @@ class Publication(Environmentable, Serializable):
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('cancel method for class {0} not provided!'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'cancel method for class {0} not provided!'.format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -55,6 +55,7 @@ if typing.TYPE_CHECKING:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Service(Module):
|
||||
"""
|
||||
This class is in fact an interface, and represents a service, that is the
|
||||
@ -138,7 +139,9 @@ class Service(Module):
|
||||
usesCache = False
|
||||
|
||||
# : Tooltip to be used if services uses cache at administration interface, indicated by :py:attr:.usesCache
|
||||
cacheTooltip = _('None') # : Tooltip shown to user when this item is pointed at admin interface
|
||||
cacheTooltip = _(
|
||||
'None'
|
||||
) # : Tooltip shown to user when this item is pointed at admin interface
|
||||
|
||||
# : If user deployments can be cached (see :py:attr:.usesCache), may he also can provide a secondary cache,
|
||||
# : that is no more that user deployments that are "almost ready" to be used, but preperably consumes less
|
||||
@ -147,7 +150,9 @@ class Service(Module):
|
||||
usesCache_L2 = False # : If we need to generate a "Level 2" cache for this service (i.e., L1 could be running machines and L2 suspended machines)
|
||||
|
||||
# : Tooltip to be used if services uses L2 cache at administration interface, indicated by :py:attr:.usesCache_L2
|
||||
cacheTooltip_L2 = _('None') # : Tooltip shown to user when this item is pointed at admin interface
|
||||
cacheTooltip_L2 = _(
|
||||
'None'
|
||||
) # : Tooltip shown to user when this item is pointed at admin interface
|
||||
|
||||
# : If the service needs a o.s. manager (see os managers section)
|
||||
needsManager: bool = False
|
||||
@ -196,7 +201,13 @@ class Service(Module):
|
||||
|
||||
_provider: 'services.ServiceProvider'
|
||||
|
||||
def __init__(self, environment, parent: 'services.ServiceProvider', values: Module.ValuesType = None, uuid: typing.Optional[str] = None):
|
||||
def __init__(
|
||||
self,
|
||||
environment,
|
||||
parent: 'services.ServiceProvider',
|
||||
values: Module.ValuesType = None,
|
||||
uuid: typing.Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Do not forget to invoke this in your derived class using "super().__init__(environment, parent, values)".
|
||||
We want to use the env, parent methods outside class. If not called, you must implement your own methods
|
||||
@ -232,7 +243,9 @@ class Service(Module):
|
||||
"""
|
||||
return self._provider
|
||||
|
||||
def requestServicesForAssignation(self, **kwargs) -> typing.Iterable[UserDeployment]:
|
||||
def requestServicesForAssignation(
|
||||
self, **kwargs
|
||||
) -> typing.Iterable[UserDeployment]:
|
||||
"""
|
||||
override this if mustAssignManualy is True
|
||||
@params kwargs: Named arguments
|
||||
@ -240,7 +253,11 @@ class Service(Module):
|
||||
We will access the returned array in "name" basis. This means that the service will be assigned by "name", so be care that every single service
|
||||
returned are not repeated... :-)
|
||||
"""
|
||||
raise Exception('The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format(self.__class__.__name__))
|
||||
raise Exception(
|
||||
'The class {0} has been marked as manually asignable but no requestServicesForAssignetion provided!!!'.format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def macGenerator(self) -> typing.Optional['UniqueMacGenerator']:
|
||||
"""
|
||||
@ -268,7 +285,9 @@ class Service(Module):
|
||||
"""
|
||||
return []
|
||||
|
||||
def assignFromAssignables(self, assignableId: str, user: 'models.User', userDeployment: UserDeployment) -> str:
|
||||
def assignFromAssignables(
|
||||
self, assignableId: str, user: 'models.User', userDeployment: UserDeployment
|
||||
) -> str:
|
||||
"""
|
||||
Assigns from it internal assignable list to an user
|
||||
|
||||
@ -296,7 +315,7 @@ class Service(Module):
|
||||
Looks for an "owned" id in the provided list. If found, returns it, else return None
|
||||
|
||||
Args:
|
||||
idsList (typing.Iterable[str]): List of IPs and MACs that acts as
|
||||
idsList (typing.Iterable[str]): List of IPs and MACs that acts as
|
||||
|
||||
Returns:
|
||||
typing.Optional[str]: [description]
|
||||
@ -340,7 +359,7 @@ class Service(Module):
|
||||
def notifyInitialization(self, id: str) -> None:
|
||||
"""
|
||||
In the case that the startup of a "tokenized" method is invoked (unmanaged method of actor_v3 rest api),
|
||||
this method is forwarded, so the tokenized method could take proper actions on a "known-to-be-free service"
|
||||
this method is forwarded, so the tokenized method could take proper actions on a "known-to-be-free service"
|
||||
|
||||
Args:
|
||||
id (str): Id validated through "getValidId"
|
||||
@ -356,21 +375,26 @@ class Service(Module):
|
||||
self.storage.delete('__nfo_' + id)
|
||||
return value
|
||||
|
||||
|
||||
def doLog(self, level: int, message: str) -> None:
|
||||
"""
|
||||
Logs a message with requested level associated with this service
|
||||
"""
|
||||
from uds.models import Service as DBService
|
||||
|
||||
if self.getUuid():
|
||||
log.doLog(DBService.objects.get(uuid=self.getUuid()), level, message, log.SERVICE)
|
||||
log.doLog(
|
||||
DBService.objects.get(uuid=self.getUuid()), level, message, log.SERVICE
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def canAssign(cls) -> bool:
|
||||
"""
|
||||
Helper to query if a class is custom (implements getJavascript method)
|
||||
"""
|
||||
return cls.listAssignables is not Service.listAssignables and cls.assignFromAssignables is not Service.assignFromAssignables
|
||||
return (
|
||||
cls.listAssignables is not Service.listAssignables
|
||||
and cls.assignFromAssignables is not Service.assignFromAssignables
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-20019 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -46,7 +46,10 @@ if typing.TYPE_CHECKING:
|
||||
from uds.core.util.unique_mac_generator import UniqueMacGenerator
|
||||
from uds.core.util.unique_gid_generator import UniqueGIDGenerator
|
||||
|
||||
class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many-public-methods
|
||||
|
||||
class UserDeployment(
|
||||
Environmentable, Serializable
|
||||
): # pylint: disable=too-many-public-methods
|
||||
"""
|
||||
Interface for deployed services.
|
||||
|
||||
@ -109,6 +112,7 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
method reasonOfError can be called multiple times, including
|
||||
serializations in middle, so remember to include reason of error in serializations
|
||||
"""
|
||||
|
||||
L1_CACHE = 1 # : Constant for Cache of level 1
|
||||
L2_CACHE = 2 # : Constant for Cache of level 2
|
||||
|
||||
@ -147,7 +151,9 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
"""
|
||||
Environmentable.__init__(self, environment)
|
||||
Serializable.__init__(self)
|
||||
self._service = kwargs['service'] # Raises an exception if service is not included. Parent
|
||||
self._service = kwargs[
|
||||
'service'
|
||||
] # Raises an exception if service is not included. Parent
|
||||
self._publication = kwargs.get('publication', None)
|
||||
self._osmanager = kwargs.get('osmanager', None)
|
||||
self._dbService = kwargs.get('dbservice', None)
|
||||
@ -383,7 +389,11 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise Exception('Base deploy for cache invoked! for class {0}'.format(self.__class__.__name__))
|
||||
raise Exception(
|
||||
'Base deploy for cache invoked! for class {0}'.format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def deployForUser(self, user: 'models.User') -> str:
|
||||
"""
|
||||
@ -418,7 +428,11 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('Base deploy for user invoked! for class {0}'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'Base deploy for user invoked! for class {0}'.format(
|
||||
self.__class__.__name__
|
||||
)
|
||||
)
|
||||
|
||||
def checkState(self) -> str:
|
||||
"""
|
||||
@ -443,7 +457,9 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('Base check state invoked! for class {0}'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'Base check state invoked! for class {0}'.format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def finish(self) -> None:
|
||||
"""
|
||||
@ -549,7 +565,9 @@ class UserDeployment(Environmentable, Serializable): # pylint: disable=too-many
|
||||
to the core. Take that into account and handle exceptions inside
|
||||
this method.
|
||||
"""
|
||||
raise NotImplementedError('destroy method for class {0} not provided!'.format(self.__class__.__name__))
|
||||
raise NotImplementedError(
|
||||
'destroy method for class {0} not provided!'.format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def cancel(self) -> str:
|
||||
"""
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -44,6 +44,7 @@ ICA = 'ica'
|
||||
NX = 'nx'
|
||||
X11 = 'x11'
|
||||
X2GO = 'x2go' # Based on NX
|
||||
NICE = 'nice'
|
||||
OTHER = 'other'
|
||||
|
||||
GENERIC = (RDP, RGS, VNC, NX, X11, X2GO, PCOIP, OTHER)
|
||||
GENERIC = (RDP, RGS, VNC, NX, X11, X2GO, PCOIP, NICE, OTHER)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -61,6 +61,7 @@ class Transport(Module):
|
||||
Server will iterate thought them and look for an identifier associated with the service. This list is a comma separated values (i.e. AA:BB:CC:DD:EE:FF,00:11:22:...)
|
||||
Remember also that we inherit the test and check methods from BaseModule
|
||||
"""
|
||||
|
||||
# Transport informational related data, inherited from BaseModule
|
||||
typeName = 'Base Transport Manager'
|
||||
typeType = 'Base Transport'
|
||||
@ -110,8 +111,16 @@ class Transport(Module):
|
||||
Invoked when Transport is deleted
|
||||
"""
|
||||
|
||||
def testServer(self, userService: 'models.UserService', ip: str, port: typing.Union[str, int], timeout: int = 4) -> bool:
|
||||
proxy: typing.Optional['models.Proxy'] = userService.deployed_service.service.proxy
|
||||
def testServer(
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
ip: str,
|
||||
port: typing.Union[str, int],
|
||||
timeout: int = 4,
|
||||
) -> bool:
|
||||
proxy: typing.Optional[
|
||||
'models.Proxy'
|
||||
] = userService.deployed_service.service.proxy
|
||||
if proxy:
|
||||
return proxy.doTestServer(ip, port, timeout)
|
||||
return connection.testServer(ip, str(port), timeout)
|
||||
@ -123,7 +132,9 @@ class Transport(Module):
|
||||
"""
|
||||
return False
|
||||
|
||||
def getCustomAvailableErrorMsg(self, userService: 'models.UserService', ip: str) -> str:
|
||||
def getCustomAvailableErrorMsg(
|
||||
self, userService: 'models.UserService', ip: str
|
||||
) -> str:
|
||||
"""
|
||||
Returns a customized error message, that will be used when a service fails to check "isAvailableFor"
|
||||
Override this in yours transports if needed
|
||||
@ -156,11 +167,11 @@ class Transport(Module):
|
||||
return cls.getConnectionInfo is not Transport.getConnectionInfo
|
||||
|
||||
def getConnectionInfo(
|
||||
self,
|
||||
userService: typing.Union['models.UserService', 'models.ServicePool'],
|
||||
user: 'models.User',
|
||||
password: str
|
||||
) -> typing.Mapping[str, str]:
|
||||
self,
|
||||
userService: typing.Union['models.UserService', 'models.ServicePool'],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
) -> typing.Mapping[str, str]:
|
||||
"""
|
||||
This method must provide information about connection.
|
||||
We don't have to implement it, but if we wont to allow some types of connections
|
||||
@ -186,7 +197,9 @@ class Transport(Module):
|
||||
"""
|
||||
return {'protocol': self.protocol, 'username': '', 'password': '', 'domain': ''}
|
||||
|
||||
def processedUser(self, userService: 'models.UserService', user: 'models.User') -> str:
|
||||
def processedUser(
|
||||
self, userService: 'models.UserService', user: 'models.User'
|
||||
) -> str:
|
||||
"""
|
||||
Used to "transform" username that will be sent to service
|
||||
This is used to make the "user" that will receive the service match with that sent in notification
|
||||
@ -195,64 +208,75 @@ class Transport(Module):
|
||||
return user.name
|
||||
|
||||
def getUDSTransportScript(
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest'
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, typing.Any]]:
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest',
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, typing.Any]]:
|
||||
"""
|
||||
If this is an uds transport, this will return the tranport script needed for executing
|
||||
this on client
|
||||
"""
|
||||
return "raise Exception('The transport {transport} is not supported on your platform.'.format(transport=params['transport']))", \
|
||||
'EH/91J7u9+/sHtB5+EUVRDW1+jqF0LuZzfRi8qxyIuSdJuWt'\
|
||||
'8V8Yngu24p0NNr13TaxPQ1rpGN8x0NsU/Ma8k4GGohc+zxdf'\
|
||||
'4xlkwMjAIytp8jaMHKkzvcihiIAMtaicP786FZCwGMmFTH4Z'\
|
||||
'A9i7YWaSzT95h84kirAG67J0GWKiyATxs6mtxBNaLgqU4juA'\
|
||||
'Qn98hYp5ffWa5FQDSAmheiDyQbCXMRwtWcxVHVQCAoZbsvCe'\
|
||||
'njKc+FaeKNmXsYOgmcj+pz8IViNOyTbueP9u7lTzuBlIyV+7'\
|
||||
'OlBPTqb5yA5wOBicKIpplPd8V71Oh3pdpRvdlvVbbwNfsCl5'\
|
||||
'v6s1X20MxaQOSwM5z02eY1lJSbLIp8d9WRkfVty0HP/4Z8JZ'\
|
||||
'kavkWNaGiKXEZXqojx/ZdzvTfvBkYrREQ8lMCIvtawBTysus'\
|
||||
'IV4vHnDRdSmRxpYdj+1SNfzB0s1VuY6F7bSdBvgzja4P3Zbo'\
|
||||
'Z63yNGuBhIsqUDA2ARmiMHRx9jr6eilFBKhoyWgNi9izTkar'\
|
||||
'3iMYtXfvcFnmz4jvuJHUccbpUo4O31K2G7OaqlLylQ5dCu62'\
|
||||
'JuVuquKKSfiwOIdYcdPJ6gvpgkQQDPqt7wN+duyZA0FI5F4h'\
|
||||
'O6acQZmbjBCqZoo9Qsg7k9cTcalNkc5flEYAk1mULnddgDM6'\
|
||||
'YGmoJgVnDr0=', {'transport': transport.name}
|
||||
return (
|
||||
"raise Exception('The transport {transport} is not supported on your platform.'.format(transport=params['transport']))",
|
||||
'EH/91J7u9+/sHtB5+EUVRDW1+jqF0LuZzfRi8qxyIuSdJuWt'
|
||||
'8V8Yngu24p0NNr13TaxPQ1rpGN8x0NsU/Ma8k4GGohc+zxdf'
|
||||
'4xlkwMjAIytp8jaMHKkzvcihiIAMtaicP786FZCwGMmFTH4Z'
|
||||
'A9i7YWaSzT95h84kirAG67J0GWKiyATxs6mtxBNaLgqU4juA'
|
||||
'Qn98hYp5ffWa5FQDSAmheiDyQbCXMRwtWcxVHVQCAoZbsvCe'
|
||||
'njKc+FaeKNmXsYOgmcj+pz8IViNOyTbueP9u7lTzuBlIyV+7'
|
||||
'OlBPTqb5yA5wOBicKIpplPd8V71Oh3pdpRvdlvVbbwNfsCl5'
|
||||
'v6s1X20MxaQOSwM5z02eY1lJSbLIp8d9WRkfVty0HP/4Z8JZ'
|
||||
'kavkWNaGiKXEZXqojx/ZdzvTfvBkYrREQ8lMCIvtawBTysus'
|
||||
'IV4vHnDRdSmRxpYdj+1SNfzB0s1VuY6F7bSdBvgzja4P3Zbo'
|
||||
'Z63yNGuBhIsqUDA2ARmiMHRx9jr6eilFBKhoyWgNi9izTkar'
|
||||
'3iMYtXfvcFnmz4jvuJHUccbpUo4O31K2G7OaqlLylQ5dCu62'
|
||||
'JuVuquKKSfiwOIdYcdPJ6gvpgkQQDPqt7wN+duyZA0FI5F4h'
|
||||
'O6acQZmbjBCqZoo9Qsg7k9cTcalNkc5flEYAk1mULnddgDM6'
|
||||
'YGmoJgVnDr0=',
|
||||
{'transport': transport.name},
|
||||
)
|
||||
|
||||
def getEncodedTransportScript(
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest'
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, str]]:
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest',
|
||||
) -> typing.Tuple[str, str, typing.Mapping[str, str]]:
|
||||
"""
|
||||
Encodes the script so the client can understand it
|
||||
"""
|
||||
script, signature, params = self.getUDSTransportScript(userService, transport, ip, os, user, password, request)
|
||||
script, signature, params = self.getUDSTransportScript(
|
||||
userService, transport, ip, os, user, password, request
|
||||
)
|
||||
logger.debug('Transport script: %s', script)
|
||||
|
||||
return codecs.encode(codecs.encode(script.encode(), 'bz2'), 'base64').decode().replace('\n', ''), signature, params
|
||||
|
||||
return (
|
||||
codecs.encode(codecs.encode(script.encode(), 'bz2'), 'base64')
|
||||
.decode()
|
||||
.replace('\n', ''),
|
||||
signature,
|
||||
params,
|
||||
)
|
||||
|
||||
def getLink(
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest'
|
||||
) -> str:
|
||||
self,
|
||||
userService: 'models.UserService',
|
||||
transport: 'models.Transport',
|
||||
ip: str,
|
||||
os: typing.Dict[str, str],
|
||||
user: 'models.User',
|
||||
password: str,
|
||||
request: 'HttpRequest',
|
||||
) -> str:
|
||||
"""
|
||||
Must override if transport does provides its own link
|
||||
If transport provides own link, this method provides the link itself
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -11,7 +11,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -404,7 +404,6 @@ class gui:
|
||||
def cleanStr(self):
|
||||
return str(self.value).strip()
|
||||
|
||||
|
||||
class NumericField(InputField):
|
||||
"""
|
||||
This represents a numeric field. It apears with an spin up/down button.
|
||||
@ -428,8 +427,12 @@ class gui:
|
||||
|
||||
def __init__(self, **options):
|
||||
super().__init__(**options)
|
||||
self._data['minValue'] = int(options.get('minValue', options.get('minvalue', '987654321')))
|
||||
self._data['maxValue'] = int(options.get('maxValue', options.get('maxvalue', '987654321')))
|
||||
self._data['minValue'] = int(
|
||||
options.get('minValue', options.get('minvalue', '987654321'))
|
||||
)
|
||||
self._data['maxValue'] = int(
|
||||
options.get('maxValue', options.get('maxvalue', '987654321'))
|
||||
)
|
||||
|
||||
self._type(gui.InputField.NUMERIC_TYPE)
|
||||
|
||||
@ -540,7 +543,7 @@ class gui:
|
||||
self._type(gui.InputField.PASSWORD_TYPE)
|
||||
|
||||
def cleanStr(self):
|
||||
return str(self.value).strip()
|
||||
return str(self.value).strip()
|
||||
|
||||
class HiddenField(InputField):
|
||||
"""
|
||||
@ -863,7 +866,12 @@ class UserInterfaceType(type):
|
||||
better place
|
||||
"""
|
||||
|
||||
def __new__(cls: typing.Type['UserInterfaceType'], classname: str, bases: typing.Tuple[type, ...], namespace: typing.Dict[str, typing.Any]) -> 'UserInterfaceType':
|
||||
def __new__(
|
||||
cls: typing.Type['UserInterfaceType'],
|
||||
classname: str,
|
||||
bases: typing.Tuple[type, ...],
|
||||
namespace: typing.Dict[str, typing.Any],
|
||||
) -> 'UserInterfaceType':
|
||||
newClassDict = {}
|
||||
_gui: typing.MutableMapping[str, gui.InputField] = {}
|
||||
# We will keep a reference to gui elements also at _gui so we can access them easily
|
||||
@ -872,7 +880,9 @@ class UserInterfaceType(type):
|
||||
_gui[attrName] = attr
|
||||
newClassDict[attrName] = attr
|
||||
newClassDict['_gui'] = _gui
|
||||
return typing.cast('UserInterfaceType', type.__new__(cls, classname, bases, newClassDict))
|
||||
return typing.cast(
|
||||
'UserInterfaceType', type.__new__(cls, classname, bases, newClassDict)
|
||||
)
|
||||
|
||||
|
||||
class UserInterface(metaclass=UserInterfaceType):
|
||||
|
@ -179,7 +179,9 @@ class CalendarChecker:
|
||||
|
||||
return bool(data[dtime.hour * 60 + dtime.minute])
|
||||
|
||||
def nextEvent(self, checkFrom=None, startEvent=True, offset=None) -> typing.Optional[datetime.datetime]:
|
||||
def nextEvent(
|
||||
self, checkFrom=None, startEvent=True, offset=None
|
||||
) -> typing.Optional[datetime.datetime]:
|
||||
"""
|
||||
Returns next event for this interval
|
||||
"""
|
||||
|
@ -1,187 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2016-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
# pylint: disable=no-name-in-module,import-error, maybe-no-member
|
||||
import os
|
||||
import io
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
import typing
|
||||
|
||||
from django.core.cache import caches
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import Storage
|
||||
from django.conf import settings
|
||||
|
||||
from uds.models import DBFile
|
||||
from uds.models import getSqlDatetime
|
||||
|
||||
from .tools import DictAsObj
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileStorage(Storage):
|
||||
_base_url: str
|
||||
cache: typing.Any
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._base_url = getattr(settings, 'FILE_STORAGE', '/uds/utility/files')
|
||||
if self._base_url[-1] != '/':
|
||||
self._base_url += '/'
|
||||
|
||||
cacheName: str = getattr(settings, 'FILE_CACHE', 'memory')
|
||||
|
||||
try:
|
||||
cache = caches[cacheName]
|
||||
except Exception:
|
||||
logger.info('No cache for FileStorage configured.')
|
||||
self.cache = None
|
||||
return
|
||||
|
||||
self.cache = cache
|
||||
if 'owner' in kwargs:
|
||||
self.owner = kwargs.get('owner')
|
||||
del kwargs['owner']
|
||||
else:
|
||||
self.owner = 'fstor'
|
||||
|
||||
# On start, ensures that cache is empty to avoid surprises
|
||||
self.cache._cache.flush_all() # pylint: disable=protected-access
|
||||
|
||||
Storage.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_valid_name(self, name):
|
||||
if name is None:
|
||||
return name
|
||||
return name.replace('\\', os.path.sep)
|
||||
|
||||
def _getKey(self, name):
|
||||
"""
|
||||
We have only a few files on db, an we are running on a 64 bits system
|
||||
memcached does not allow keys bigger than 250 chars, so we are going to use hash() to
|
||||
get a key for this
|
||||
"""
|
||||
return 'fstor' + str(hash(self.get_valid_name(name)))
|
||||
|
||||
def _dbFileForReadOnly(self, name):
|
||||
# If we have a cache, & the cache contains the object
|
||||
if self.cache is not None:
|
||||
dbf = self.cache.get(self._getKey(name))
|
||||
if dbf is not None:
|
||||
return dbf
|
||||
|
||||
return self._dbFileForReadWrite(name)
|
||||
|
||||
def _dbFileForReadWrite(self, name):
|
||||
f = DBFile.objects.get(name=self.get_valid_name(name))
|
||||
self._storeInCache(f)
|
||||
return f
|
||||
|
||||
def _storeInCache(self, f):
|
||||
if self.cache is None:
|
||||
return
|
||||
|
||||
dbf = DictAsObj(
|
||||
{
|
||||
'name': f.name,
|
||||
'uuid': f.uuid,
|
||||
'size': f.size,
|
||||
'data': f.data,
|
||||
'created': f.created,
|
||||
'modified': f.modified,
|
||||
}
|
||||
)
|
||||
|
||||
self.cache.set(self._getKey(f.name), dbf, 3600) # Cache defaults to one hour
|
||||
|
||||
def _removeFromCache(self, name):
|
||||
if self.cache is None:
|
||||
return
|
||||
self.cache.delete(self._getKey(name))
|
||||
|
||||
def _open(self, name, mode='rb'):
|
||||
f = io.BytesIO(self._dbFileForReadOnly(name).data)
|
||||
f.name = name
|
||||
return File(f)
|
||||
|
||||
def _save(self, name, content):
|
||||
name = self.get_valid_name(name)
|
||||
try:
|
||||
f = self._dbFileForReadWrite(name)
|
||||
except DBFile.DoesNotExist:
|
||||
now = getSqlDatetime()
|
||||
f = DBFile.objects.create(
|
||||
owner=self.owner, name=name, created=now, modified=now
|
||||
)
|
||||
|
||||
f.data = content.read()
|
||||
f.modified = getSqlDatetime()
|
||||
f.save()
|
||||
|
||||
# Store on cache also
|
||||
self._storeInCache(f)
|
||||
|
||||
return name
|
||||
|
||||
def accessed_time(self, name):
|
||||
raise NotImplementedError
|
||||
|
||||
def created_time(self, name):
|
||||
return self._dbFileForReadOnly(name).created
|
||||
|
||||
def modified_time(self, name):
|
||||
return self._dbFileForReadOnly(name).modified
|
||||
|
||||
def size(self, name):
|
||||
return self._dbFileForReadOnly(name).size
|
||||
|
||||
def delete(self, name):
|
||||
logger.debug('Delete callef for %s', name)
|
||||
self._dbFileForReadWrite(name).delete()
|
||||
self._removeFromCache(name)
|
||||
|
||||
def exists(self, name):
|
||||
logger.debug('Called exists for %s', name)
|
||||
try:
|
||||
_ = self._dbFileForReadOnly(name).uuid # Tries to access uuid
|
||||
return True
|
||||
except DBFile.DoesNotExist:
|
||||
return False
|
||||
|
||||
def url(self, name):
|
||||
try:
|
||||
uuid = self._dbFileForReadWrite(name).uuid
|
||||
return urlparse.urljoin(self._base_url, uuid)
|
||||
except DBFile.DoesNotExist:
|
||||
return None
|
@ -11,7 +11,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -40,6 +40,7 @@ class RedirectMiddleware:
|
||||
|
||||
Some paths will not be redirected, to avoid problems, but they are advised to use SSL (this is for backwards compat)
|
||||
"""
|
||||
|
||||
NO_REDIRECT = [
|
||||
'rest',
|
||||
'pam',
|
||||
@ -51,7 +52,7 @@ class RedirectMiddleware:
|
||||
# Test client can be http
|
||||
'uds/rest/client/test',
|
||||
# And also the tunnel
|
||||
'uds/rest/tunnel'
|
||||
'uds/rest/tunnel',
|
||||
]
|
||||
|
||||
def __init__(self, get_response):
|
||||
@ -65,7 +66,11 @@ class RedirectMiddleware:
|
||||
redirect = False
|
||||
break
|
||||
|
||||
if redirect and not request.is_secure() and GlobalConfig.REDIRECT_TO_HTTPS.getBool():
|
||||
if (
|
||||
redirect
|
||||
and not request.is_secure()
|
||||
and GlobalConfig.REDIRECT_TO_HTTPS.getBool()
|
||||
):
|
||||
if request.method == 'POST':
|
||||
# url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
||||
url = reverse('page.login')
|
||||
|
@ -44,6 +44,7 @@ class XUACompatibleMiddleware:
|
||||
This header tells to Internet Explorer to render page with latest
|
||||
possible version or to use chrome frame if it is installed.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
|
@ -73,7 +73,7 @@ string = {
|
||||
FOR_EXECUTE: _('Waiting execution'),
|
||||
BALANCING: _('Balancing'),
|
||||
MAINTENANCE: _('In maintenance'),
|
||||
WAITING_OS: _('Waiting OS')
|
||||
WAITING_OS: _('Waiting OS'),
|
||||
}
|
||||
|
||||
# States that are merely for "information" to the user. They don't contain any usable instance
|
||||
|
@ -32,4 +32,12 @@
|
||||
"""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from .common import ERROR, USABLE, REMOVABLE, REMOVED, CANCELED, LAUNCHING, PREPARING # @UnusedImport
|
||||
from .common import (
|
||||
ERROR,
|
||||
USABLE,
|
||||
REMOVABLE,
|
||||
REMOVED,
|
||||
CANCELED,
|
||||
LAUNCHING,
|
||||
PREPARING,
|
||||
) # @UnusedImport
|
||||
|
@ -33,4 +33,3 @@
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from .common import ACTIVE, REMOVABLE, REMOVING, REMOVED # @UnusedImport
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2015-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2015-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2015-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
@ -32,4 +32,13 @@
|
||||
"""
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from .common import ERROR, USABLE, PREPARING, REMOVABLE, REMOVING, REMOVED, INFO_STATES, VALID_STATES # @UnusedImport
|
||||
from .common import (
|
||||
ERROR,
|
||||
USABLE,
|
||||
PREPARING,
|
||||
REMOVABLE,
|
||||
REMOVING,
|
||||
REMOVED,
|
||||
INFO_STATES,
|
||||
VALID_STATES,
|
||||
) # @UnusedImport
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2013-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
|
@ -41,91 +41,166 @@ from uds.core.util.modfinder import loadModulesUrls
|
||||
|
||||
urlpatterns = [
|
||||
# Root url placeholder
|
||||
path(r'', RedirectView.as_view(pattern_name='page.index', permanent=False), name='page.index.placeholder'),
|
||||
path(
|
||||
r'',
|
||||
RedirectView.as_view(pattern_name='page.index', permanent=False),
|
||||
name='page.index.placeholder',
|
||||
),
|
||||
# path(r'', RedirectView.as_view(url='https://www.udsenterprise.com', permanent=False), name='page.index.placeholder')
|
||||
|
||||
# START COMPAT redirections & urls
|
||||
path(r'login/', RedirectView.as_view(pattern_name='page.login', permanent=False), name='page.login.compat'),
|
||||
path(r'logout/', RedirectView.as_view(pattern_name='page.logut'), name='page.logout.compat'),
|
||||
|
||||
path(
|
||||
r'login/',
|
||||
RedirectView.as_view(pattern_name='page.login', permanent=False),
|
||||
name='page.login.compat',
|
||||
),
|
||||
path(
|
||||
r'logout/',
|
||||
RedirectView.as_view(pattern_name='page.logut'),
|
||||
name='page.logout.compat',
|
||||
),
|
||||
# Backwards compatibility with REST API path
|
||||
re_path(r'^rest/(?P<arguments>.*)$', REST.Dispatcher.as_view(), name="REST.compat"),
|
||||
|
||||
# Old urls for federated authentications
|
||||
re_path(r'^auth/(?P<authName>.+)', uds.web.views.authCallback, name='page.auth.callback.compat'),
|
||||
re_path(r'^authinfo/(?P<authName>.+)', uds.web.views.authInfo, name='page.auth.info.compat'),
|
||||
|
||||
re_path(
|
||||
r'^auth/(?P<authName>.+)',
|
||||
uds.web.views.authCallback,
|
||||
name='page.auth.callback.compat',
|
||||
),
|
||||
re_path(
|
||||
r'^authinfo/(?P<authName>.+)',
|
||||
uds.web.views.authInfo,
|
||||
name='page.auth.info.compat',
|
||||
),
|
||||
# Ticket authentication
|
||||
re_path(r'^tkauth/(?P<ticketId>[a-zA-Z0-9-]+)$', uds.web.views.ticketAuth, name='page.auth.ticket.compat'),
|
||||
|
||||
re_path(
|
||||
r'^tkauth/(?P<ticketId>[a-zA-Z0-9-]+)$',
|
||||
uds.web.views.ticketAuth,
|
||||
name='page.auth.ticket.compat',
|
||||
),
|
||||
# END COMPAT
|
||||
|
||||
# Index
|
||||
path(r'uds/page/services', uds.web.views.modern.index, name='page.index'),
|
||||
|
||||
# Login/logout
|
||||
path(r'uds/page/login', uds.web.views.modern.login, name='page.login'),
|
||||
re_path(r'^uds/page/login/(?P<tag>[a-zA-Z0-9-]+)$', uds.web.views.modern.login, name='page.login.tag'),
|
||||
|
||||
re_path(
|
||||
r'^uds/page/login/(?P<tag>[a-zA-Z0-9-]+)$',
|
||||
uds.web.views.modern.login,
|
||||
name='page.login.tag',
|
||||
),
|
||||
path(r'uds/page/logout', uds.web.views.modern.logout, name='page.logout'),
|
||||
|
||||
# Error URL (just a placeholder, calls index with data on url for angular)
|
||||
re_path(r'^uds/page/error/(?P<err>[a-zA-Z0-9=-]+)$', uds.web.views.error, name='page.error'),
|
||||
re_path(
|
||||
r'^uds/page/error/(?P<err>[a-zA-Z0-9=-]+)$',
|
||||
uds.web.views.error,
|
||||
name='page.error',
|
||||
),
|
||||
# Download plugins URL (just a placeholder, calls index with data on url for angular)
|
||||
path(r'uds/page/client-download', uds.web.views.modern.index, name='page.client-download'),
|
||||
|
||||
path(
|
||||
r'uds/page/client-download',
|
||||
uds.web.views.modern.index,
|
||||
name='page.client-download',
|
||||
),
|
||||
# Federated authentication
|
||||
re_path(r'^uds/page/auth/(?P<authName>[^/]+)$', uds.web.views.authCallback, name='page.auth.callback'),
|
||||
re_path(r'^uds/page/auth/stage2/(?P<ticketId>[^/]+)$', uds.web.views.authCallback_stage2, name='page.auth.callback_stage2'),
|
||||
re_path(r'^uds/page/auth/info/(?P<authName>[a-zA-Z0-9.-]+)$', uds.web.views.authInfo, name='page.auth.info'),
|
||||
|
||||
re_path(
|
||||
r'^uds/page/auth/(?P<authName>[^/]+)$',
|
||||
uds.web.views.authCallback,
|
||||
name='page.auth.callback',
|
||||
),
|
||||
re_path(
|
||||
r'^uds/page/auth/stage2/(?P<ticketId>[^/]+)$',
|
||||
uds.web.views.authCallback_stage2,
|
||||
name='page.auth.callback_stage2',
|
||||
),
|
||||
re_path(
|
||||
r'^uds/page/auth/info/(?P<authName>[a-zA-Z0-9.-]+)$',
|
||||
uds.web.views.authInfo,
|
||||
name='page.auth.info',
|
||||
),
|
||||
# Ticket authentication related
|
||||
re_path(r'^uds/page/ticket/auth/(?P<ticketId>[a-zA-Z0-9.-]+)$', uds.web.views.ticketAuth, name='page.ticket.auth'),
|
||||
path(r'uds/page/ticket/launcher', uds.web.views.modern.ticketLauncher, name='page.ticket.launcher'),
|
||||
|
||||
re_path(
|
||||
r'^uds/page/ticket/auth/(?P<ticketId>[a-zA-Z0-9.-]+)$',
|
||||
uds.web.views.ticketAuth,
|
||||
name='page.ticket.auth',
|
||||
),
|
||||
path(
|
||||
r'uds/page/ticket/launcher',
|
||||
uds.web.views.modern.ticketLauncher,
|
||||
name='page.ticket.launcher',
|
||||
),
|
||||
# This must be the last, so any patition will be managed by client in fact
|
||||
re_path(r'uds/page/.*', uds.web.views.modern.index, name='page.placeholder'),
|
||||
|
||||
# Utility
|
||||
path(r'uds/utility/closer', uds.web.views.service.closer, name='utility.closer'),
|
||||
|
||||
# Javascript
|
||||
path(r'uds/utility/uds.js', uds.web.views.modern.js, name="utility.js"),
|
||||
path(r'uds/adm/utility/uds.js', uds.web.views.modern.js, name="utility-adm.js"),
|
||||
|
||||
# i18n
|
||||
re_path(r'^uds/utility/i18n/(?P<lang>[a-z_-]*).js$', JavaScriptCatalog.as_view(), name='utility.jsCatalog'),
|
||||
re_path(
|
||||
r'^uds/utility/i18n/(?P<lang>[a-z_-]*).js$',
|
||||
JavaScriptCatalog.as_view(),
|
||||
name='utility.jsCatalog',
|
||||
),
|
||||
path(r'uds/utility/i18n', include('django.conf.urls.i18n')),
|
||||
|
||||
# Downloader
|
||||
re_path(r'^uds/utility/download/(?P<idDownload>[a-zA-Z0-9-]*)$', uds.web.views.download, name='utility.downloader'),
|
||||
|
||||
re_path(
|
||||
r'^uds/utility/download/(?P<idDownload>[a-zA-Z0-9-]*)$',
|
||||
uds.web.views.download,
|
||||
name='utility.downloader',
|
||||
),
|
||||
# WEB API path (not REST api, frontend)
|
||||
re_path(r'^uds/webapi/img/transport/(?P<idTrans>[a-zA-Z0-9:-]+)$', uds.web.views.transportIcon, name='webapi.transportIcon'),
|
||||
re_path(r'^uds/webapi/img/gallery/(?P<idImage>[a-zA-Z0-9-]+)$', uds.web.views.image, name='webapi.galleryImage'),
|
||||
|
||||
re_path(
|
||||
r'^uds/webapi/img/transport/(?P<idTrans>[a-zA-Z0-9:-]+)$',
|
||||
uds.web.views.transportIcon,
|
||||
name='webapi.transportIcon',
|
||||
),
|
||||
re_path(
|
||||
r'^uds/webapi/img/gallery/(?P<idImage>[a-zA-Z0-9-]+)$',
|
||||
uds.web.views.image,
|
||||
name='webapi.galleryImage',
|
||||
),
|
||||
# Enabler and Status action are first processed, and if not match, execute the generic "action" handler
|
||||
re_path(r'^uds/webapi/action/(?P<idService>.+)/enable/(?P<idTransport>[a-zA-Z0-9:-]+)$', uds.web.views.userServiceEnabler, name='webapi.enabler'),
|
||||
re_path(r'^uds/webapi/action/(?P<idService>.+)/status/(?P<idTransport>[a-zA-Z0-9:-]+)$', uds.web.views.userServiceStatus, name='webapi.status'),
|
||||
|
||||
re_path(r'^uds/webapi/action/(?P<idService>.+)/(?P<actionString>[a-zA-Z0-9:-]+)$', uds.web.views.action, name='webapi.action'),
|
||||
|
||||
re_path(
|
||||
r'^uds/webapi/action/(?P<idService>.+)/enable/(?P<idTransport>[a-zA-Z0-9:-]+)$',
|
||||
uds.web.views.userServiceEnabler,
|
||||
name='webapi.enabler',
|
||||
),
|
||||
re_path(
|
||||
r'^uds/webapi/action/(?P<idService>.+)/status/(?P<idTransport>[a-zA-Z0-9:-]+)$',
|
||||
uds.web.views.userServiceStatus,
|
||||
name='webapi.status',
|
||||
),
|
||||
re_path(
|
||||
r'^uds/webapi/action/(?P<idService>.+)/(?P<actionString>[a-zA-Z0-9:-]+)$',
|
||||
uds.web.views.action,
|
||||
name='webapi.action',
|
||||
),
|
||||
# Services list, ...
|
||||
path(r'uds/webapi/services', uds.web.views.modern.servicesData, name='webapi.services'),
|
||||
|
||||
path(
|
||||
r'uds/webapi/services',
|
||||
uds.web.views.modern.servicesData,
|
||||
name='webapi.services',
|
||||
),
|
||||
# Transport own link processor
|
||||
re_path(r'^uds/webapi/trans/(?P<idService>.+)/(?P<idTransport>.+)$', uds.web.views.transportOwnLink, name='TransportOwnLink'),
|
||||
re_path(
|
||||
r'^uds/webapi/trans/(?P<idService>.+)/(?P<idTransport>.+)$',
|
||||
uds.web.views.transportOwnLink,
|
||||
name='TransportOwnLink',
|
||||
),
|
||||
# Authenticators custom html
|
||||
re_path(r'^uds/webapi/customAuth/(?P<idAuth>.*)$', uds.web.views.customAuth, name='uds.web.views.customAuth'),
|
||||
|
||||
re_path(
|
||||
r'^uds/webapi/customAuth/(?P<idAuth>.*)$',
|
||||
uds.web.views.customAuth,
|
||||
name='uds.web.views.customAuth',
|
||||
),
|
||||
# END WEB API
|
||||
|
||||
# Costumization of GUI
|
||||
re_path(r'^uds/custom/(?P<component>[a-zA-Z.-]+)$', uds.web.views.custom.custom, name='custom'),
|
||||
|
||||
re_path(
|
||||
r'^uds/custom/(?P<component>[a-zA-Z.-]+)$',
|
||||
uds.web.views.custom.custom,
|
||||
name='custom',
|
||||
),
|
||||
# REST Api
|
||||
re_path(r'^uds/rest/(?P<arguments>.*)$', REST.Dispatcher.as_view(), name="REST"),
|
||||
|
||||
# Web admin GUI
|
||||
re_path(r'^uds/adm', include('uds.admin.urls')),
|
||||
]
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -12,7 +12,7 @@
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
|
@ -38,10 +38,17 @@ from uds.models import Authenticator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
user = forms.CharField(label=_('Username'), max_length=64, widget=forms.TextInput())
|
||||
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput(attrs={'title': _('Password')}), required=False)
|
||||
authenticator = forms.ChoiceField(label=_('Authenticator'), choices=(), required=False)
|
||||
password = forms.CharField(
|
||||
label=_('Password'),
|
||||
widget=forms.PasswordInput(attrs={'title': _('Password')}),
|
||||
required=False,
|
||||
)
|
||||
authenticator = forms.ChoiceField(
|
||||
label=_('Authenticator'), choices=(), required=False
|
||||
)
|
||||
logouturl = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -55,11 +55,11 @@ logger = logging.getLogger(__name__)
|
||||
# (None, NumericError) if errorview redirection
|
||||
# (User, password_string) if all is ok
|
||||
def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
request: 'ExtendedHttpRequest',
|
||||
form: 'LoginForm',
|
||||
tag: typing.Optional[str] = None
|
||||
) -> typing.Tuple[typing.Optional['User'], typing.Any]:
|
||||
host = request.META.get('HTTP_HOST') or request.META.get('SERVER_NAME') or 'auth_host' # Last one is a placeholder in case we can't locate host name
|
||||
request: 'ExtendedHttpRequest', form: 'LoginForm', tag: typing.Optional[str] = None
|
||||
) -> typing.Tuple[typing.Optional['User'], typing.Any]:
|
||||
host = (
|
||||
request.META.get('HTTP_HOST') or request.META.get('SERVER_NAME') or 'auth_host'
|
||||
) # Last one is a placeholder in case we can't locate host name
|
||||
|
||||
# Get Authenticators limitation
|
||||
if GlobalConfig.DISALLOW_GLOBAL_LOGIN.getBool(False) is True:
|
||||
@ -81,7 +81,9 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
if form.is_valid():
|
||||
os = request.os
|
||||
try:
|
||||
authenticator = Authenticator.objects.get(uuid=processUuid(form.cleaned_data['authenticator']))
|
||||
authenticator = Authenticator.objects.get(
|
||||
uuid=processUuid(form.cleaned_data['authenticator'])
|
||||
)
|
||||
except Exception:
|
||||
authenticator = Authenticator()
|
||||
userName = form.cleaned_data['user']
|
||||
@ -91,11 +93,20 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
cache = Cache('auth')
|
||||
cacheKey = str(authenticator.id) + userName
|
||||
tries = cache.get(cacheKey) or 0
|
||||
triesByIp = (cache.get(request.ip) or 0) if GlobalConfig.LOGIN_BLOCK_IP.getBool() else 0
|
||||
triesByIp = (
|
||||
(cache.get(request.ip) or 0) if GlobalConfig.LOGIN_BLOCK_IP.getBool() else 0
|
||||
)
|
||||
maxTries = GlobalConfig.MAX_LOGIN_TRIES.getInt()
|
||||
if (authenticator.getInstance().blockUserOnLoginFailures is True and (tries >= maxTries) or triesByIp >= maxTries):
|
||||
if (
|
||||
authenticator.getInstance().blockUserOnLoginFailures is True
|
||||
and (tries >= maxTries)
|
||||
or triesByIp >= maxTries
|
||||
):
|
||||
authLogLogin(request, authenticator, userName, 'Temporarily blocked')
|
||||
return (None, _('Too many authentication errrors. User temporarily blocked'))
|
||||
return (
|
||||
None,
|
||||
_('Too many authentication errrors. User temporarily blocked'),
|
||||
)
|
||||
|
||||
password = form.cleaned_data['password']
|
||||
user = None
|
||||
@ -106,9 +117,14 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
|
||||
if user is None:
|
||||
logger.debug("Invalid user %s (access denied)", userName)
|
||||
cache.put(cacheKey, tries+1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
cache.put(request.ip, triesByIp+1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
authLogLogin(request, authenticator, userName, 'Access denied (user not allowed by UDS)')
|
||||
cache.put(cacheKey, tries + 1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
cache.put(request.ip, triesByIp + 1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
authLogLogin(
|
||||
request,
|
||||
authenticator,
|
||||
userName,
|
||||
'Access denied (user not allowed by UDS)',
|
||||
)
|
||||
return (None, _('Access denied'))
|
||||
|
||||
request.session.cycle_key()
|
||||
|
@ -49,7 +49,7 @@ from uds.models import Authenticator, Image, Network, Transport
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core.util.request import ExtendedHttpRequest
|
||||
from uds.core.util.request import ExtendedHttpRequest
|
||||
from uds.models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -61,13 +61,21 @@ CSRF_FIELD = 'csrfmiddlewaretoken'
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
auth_host = request.META.get('HTTP_HOST') or request.META.get('SERVER_NAME') or 'auth_host' # Last one is a placeholder in case we can't locate host name
|
||||
auth_host = (
|
||||
request.META.get('HTTP_HOST') or request.META.get('SERVER_NAME') or 'auth_host'
|
||||
) # Last one is a placeholder in case we can't locate host name
|
||||
|
||||
role: str = 'user'
|
||||
user: typing.Optional['User'] = request.user
|
||||
|
||||
if user:
|
||||
role = 'staff' if user.isStaff() and not user.is_admin else 'admin' if user.is_admin else 'user'
|
||||
role = (
|
||||
'staff'
|
||||
if user.isStaff() and not user.is_admin
|
||||
else 'admin'
|
||||
if user.is_admin
|
||||
else 'user'
|
||||
)
|
||||
if request.session.get('restricted', False):
|
||||
role = 'restricted'
|
||||
|
||||
@ -87,7 +95,9 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
try:
|
||||
# Get authenticators with auth_host or tag. If tag is None, auth_host, if exists
|
||||
# tag, later will remove "auth_host"
|
||||
authenticators = Authenticator.objects.filter(small_name__in=[auth_host, tag])[:]
|
||||
authenticators = Authenticator.objects.filter(
|
||||
small_name__in=[auth_host, tag]
|
||||
)[:]
|
||||
except Exception as e:
|
||||
authenticators = []
|
||||
else:
|
||||
@ -95,8 +105,15 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
|
||||
# logger.debug('Authenticators PRE: %s', authenticators)
|
||||
|
||||
if tag and authenticators: # Refilter authenticators, visible and with this tag if required
|
||||
authenticators = [x for x in authenticators if x.small_name == tag or (tag == 'disabled' and x.getType().isCustom() is False)]
|
||||
if (
|
||||
tag and authenticators
|
||||
): # Refilter authenticators, visible and with this tag if required
|
||||
authenticators = [
|
||||
x
|
||||
for x in authenticators
|
||||
if x.small_name == tag
|
||||
or (tag == 'disabled' and x.getType().isCustom() is False)
|
||||
]
|
||||
|
||||
if not authenticators:
|
||||
try:
|
||||
@ -117,15 +134,19 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
'name': auth.name,
|
||||
'label': auth.small_name,
|
||||
'priority': auth.priority,
|
||||
'is_custom': theType.isCustom()
|
||||
'is_custom': theType.isCustom(),
|
||||
}
|
||||
|
||||
config = {
|
||||
'version': VERSION,
|
||||
'version_stamp': VERSION_STAMP,
|
||||
'language': get_language(),
|
||||
'available_languages': [{'id': k, 'name': gettext(v)} for k, v in settings.LANGUAGES],
|
||||
'authenticators': [getAuthInfo(auth) for auth in authenticators if auth.getType()],
|
||||
'available_languages': [
|
||||
{'id': k, 'name': gettext(v)} for k, v in settings.LANGUAGES
|
||||
],
|
||||
'authenticators': [
|
||||
getAuthInfo(auth) for auth in authenticators if auth.getType()
|
||||
],
|
||||
'tag': tag,
|
||||
'os': request.os['OS'],
|
||||
'csrf_field': CSRF_FIELD,
|
||||
@ -142,7 +163,8 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
'launcher_wait_time': 5000,
|
||||
'messages': {
|
||||
# Calendar denied message
|
||||
'calendarDenied': GlobalConfig.LIMITED_BY_CALENDAR_TEXT.get().strip() or gettext('Access limited by calendar')
|
||||
'calendarDenied': GlobalConfig.LIMITED_BY_CALENDAR_TEXT.get().strip()
|
||||
or gettext('Access limited by calendar')
|
||||
},
|
||||
'urls': {
|
||||
'changeLang': reverse('set_language'),
|
||||
@ -151,11 +173,23 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
'user': reverse('page.index'),
|
||||
'customAuth': reverse('uds.web.views.customAuth', kwargs={'idAuth': ''}),
|
||||
'services': reverse('webapi.services'),
|
||||
'enabler': reverse('webapi.enabler', kwargs={'idService': 'param1', 'idTransport': 'param2'}),
|
||||
'status': reverse('webapi.status', kwargs={'idService': 'param1', 'idTransport': 'param2'}),
|
||||
'action': reverse('webapi.action', kwargs={'idService': 'param1', 'actionString': 'param2'}),
|
||||
'galleryImage': reverse('webapi.galleryImage', kwargs={'idImage': 'param1'}),
|
||||
'transportIcon': reverse('webapi.transportIcon', kwargs={'idTrans': 'param1'}),
|
||||
'enabler': reverse(
|
||||
'webapi.enabler',
|
||||
kwargs={'idService': 'param1', 'idTransport': 'param2'},
|
||||
),
|
||||
'status': reverse(
|
||||
'webapi.status', kwargs={'idService': 'param1', 'idTransport': 'param2'}
|
||||
),
|
||||
'action': reverse(
|
||||
'webapi.action',
|
||||
kwargs={'idService': 'param1', 'actionString': 'param2'},
|
||||
),
|
||||
'galleryImage': reverse(
|
||||
'webapi.galleryImage', kwargs={'idImage': 'param1'}
|
||||
),
|
||||
'transportIcon': reverse(
|
||||
'webapi.transportIcon', kwargs={'idTrans': 'param1'}
|
||||
),
|
||||
'static': static(''),
|
||||
'clientDownload': reverse('page.client-download'),
|
||||
# Launcher URL if exists
|
||||
@ -168,9 +202,11 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
if user and user.isStaff():
|
||||
info = {
|
||||
'networks': [n.name for n in Network.networksFor(request.ip)],
|
||||
'transports': [t.name for t in Transport.objects.all() if t.validForIp(request.ip)],
|
||||
'transports': [
|
||||
t.name for t in Transport.objects.all() if t.validForIp(request.ip)
|
||||
],
|
||||
'ip': request.ip,
|
||||
'ip_proxy': request.ip_proxy
|
||||
'ip_proxy': request.ip_proxy,
|
||||
}
|
||||
|
||||
# all plugins are under url clients...
|
||||
@ -180,17 +216,75 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
'description': description,
|
||||
'name': name,
|
||||
'legacy': legacy,
|
||||
} for url, description, name, legacy in (
|
||||
('UDSClientSetup-{version}.exe', gettext('Windows client'), 'Windows', False),
|
||||
}
|
||||
for url, description, name, legacy in (
|
||||
(
|
||||
'UDSClientSetup-{version}.exe',
|
||||
gettext('Windows client'),
|
||||
'Windows',
|
||||
False,
|
||||
),
|
||||
('UDSClient-{version}.pkg', gettext('Mac OS X client'), 'MacOS', False),
|
||||
('udsclient3_{version}_all.deb', gettext('Debian based Linux client') + ' ' + gettext('(requires Python-3.6 or newer)'), 'Linux', False),
|
||||
('udsclient3-{version}-1.noarch.rpm', gettext('RPM based Linux client (Fedora, Suse, ...)') + ' ' + gettext('(requires Python-3.6 or newer)'), 'Linux', False),
|
||||
('udsclient3-x86_64-{version}.tar.gz', gettext('Binary appimage X86_64 Linux client'), 'Linux', False),
|
||||
('udsclient3-armhf-{version}.tar.gz', gettext('Binary appimage Raspberry Linux client'), 'Linux', False),
|
||||
('udsclient3-{version}.tar.gz', gettext('Generic .tar.gz Linux client') + ' ' + gettext('(requires Python-3.6 or newer)'), 'Linux', False),
|
||||
('udsclient_{version}_all.deb', gettext('Legacy Debian based Python 2.7 Linux client') + ' ' + gettext('(requires outdated Python-2.7)'), 'Linux', True),
|
||||
('udsclient-{version}-1.noarch.rpm', gettext('Legacy RH based Linux client (Fedora, Centos, Suse, ...)') + ' ' + gettext('(requires outdated Python-2.7)'), 'Linux', True),
|
||||
('udsclient-opensuse-{version}-1.noarch.rpm', gettext('Legacy OpenSuse based Linux client)') + ' ' + gettext('(requires outdated Python-2.7)'), 'Linux', True),
|
||||
(
|
||||
'udsclient3_{version}_all.deb',
|
||||
gettext('Debian based Linux client')
|
||||
+ ' '
|
||||
+ gettext('(requires Python-3.6 or newer)'),
|
||||
'Linux',
|
||||
False,
|
||||
),
|
||||
(
|
||||
'udsclient3-{version}-1.noarch.rpm',
|
||||
gettext('RPM based Linux client (Fedora, Suse, ...)')
|
||||
+ ' '
|
||||
+ gettext('(requires Python-3.6 or newer)'),
|
||||
'Linux',
|
||||
False,
|
||||
),
|
||||
(
|
||||
'udsclient3-x86_64-{version}.tar.gz',
|
||||
gettext('Binary appimage X86_64 Linux client'),
|
||||
'Linux',
|
||||
False,
|
||||
),
|
||||
(
|
||||
'udsclient3-armhf-{version}.tar.gz',
|
||||
gettext('Binary appimage Raspberry Linux client'),
|
||||
'Linux',
|
||||
False,
|
||||
),
|
||||
(
|
||||
'udsclient3-{version}.tar.gz',
|
||||
gettext('Generic .tar.gz Linux client')
|
||||
+ ' '
|
||||
+ gettext('(requires Python-3.6 or newer)'),
|
||||
'Linux',
|
||||
False,
|
||||
),
|
||||
(
|
||||
'udsclient_{version}_all.deb',
|
||||
gettext('Legacy Debian based Python 2.7 Linux client')
|
||||
+ ' '
|
||||
+ gettext('(requires outdated Python-2.7)'),
|
||||
'Linux',
|
||||
True,
|
||||
),
|
||||
(
|
||||
'udsclient-{version}-1.noarch.rpm',
|
||||
gettext('Legacy RH based Linux client (Fedora, Centos, Suse, ...)')
|
||||
+ ' '
|
||||
+ gettext('(requires outdated Python-2.7)'),
|
||||
'Linux',
|
||||
True,
|
||||
),
|
||||
(
|
||||
'udsclient-opensuse-{version}-1.noarch.rpm',
|
||||
gettext('Legacy OpenSuse based Linux client)')
|
||||
+ ' '
|
||||
+ gettext('(requires outdated Python-2.7)'),
|
||||
'Linux',
|
||||
True,
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
@ -200,8 +294,7 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
# 'description': 'Cliente SPICE for download', # Text that appears on download
|
||||
# 'name': 'Linux', # Can be 'Linux', 'Windows', o 'MacOS'. Sets the icon.
|
||||
# 'legacy': False # True = Gray, False = White
|
||||
#})
|
||||
|
||||
# })
|
||||
|
||||
actors: typing.List[typing.Dict[str, str]] = []
|
||||
|
||||
@ -212,7 +305,14 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
config['auth_token'] = request.session.session_key
|
||||
config['auth_header'] = AUTH_TOKEN_HEADER
|
||||
# Actors
|
||||
actors = [{'url': reverse('utility.downloader', kwargs={'idDownload': key}), 'name': val['name'], 'description': gettext(val['comment'])} for key, val in downloadsManager().getDownloadables().items()]
|
||||
actors = [
|
||||
{
|
||||
'url': reverse('utility.downloader', kwargs={'idDownload': key}),
|
||||
'name': val['name'],
|
||||
'description': gettext(val['comment']),
|
||||
}
|
||||
for key, val in downloadsManager().getDownloadables().items()
|
||||
]
|
||||
# URLS
|
||||
config['urls']['admin'] = reverse('uds.admin.views.index')
|
||||
config['urls']['rest'] = reverse('REST', kwargs={'arguments': ''})
|
||||
@ -239,7 +339,7 @@ def udsJs(request: 'ExtendedHttpRequest') -> str:
|
||||
'plugins': plugins,
|
||||
'actors': actors,
|
||||
'errors': errors,
|
||||
'data': request.session.get('data')
|
||||
'data': request.session.get('data'),
|
||||
}
|
||||
|
||||
# Reset some 1 time values...
|
||||
|
@ -125,7 +125,9 @@ def getServicesData(
|
||||
):
|
||||
yield t
|
||||
except Exception as e:
|
||||
logger.warning('Transport %s of %s not found. Ignoring. (%s)', t, member.pool, e)
|
||||
logger.warning(
|
||||
'Transport %s of %s not found. Ignoring. (%s)', t, member.pool, e
|
||||
)
|
||||
|
||||
def buildMetaTransports(
|
||||
transports: typing.Iterable[Transport],
|
||||
@ -167,8 +169,7 @@ def getServicesData(
|
||||
inAll = tmpSet
|
||||
# tmpSet has ALL common transports
|
||||
metaTransports = buildMetaTransports(
|
||||
Transport.objects.filter(uuid__in=inAll or []),
|
||||
isLabel=False
|
||||
Transport.objects.filter(uuid__in=inAll or []), isLabel=False
|
||||
)
|
||||
elif meta.transport_grouping == MetaPool.LABEL_TRANSPORT_SELECT:
|
||||
ltrans: typing.MutableMapping[str, Transport] = {}
|
||||
@ -188,8 +189,7 @@ def getServicesData(
|
||||
inAll = tmpSet
|
||||
# tmpSet has ALL common transports
|
||||
metaTransports = buildMetaTransports(
|
||||
(v for k, v in ltrans.items() if k in (inAll or set())),
|
||||
isLabel=True
|
||||
(v for k, v in ltrans.items() if k in (inAll or set())), isLabel=True
|
||||
)
|
||||
else:
|
||||
for member in meta.members.all():
|
||||
@ -382,10 +382,13 @@ def getServicesData(
|
||||
'ip': request.ip,
|
||||
'nets': nets,
|
||||
'transports': validTrans,
|
||||
'autorun': autorun
|
||||
'autorun': autorun,
|
||||
}
|
||||
|
||||
def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str) -> typing.Mapping[str, typing.Any]:
|
||||
|
||||
def enableService(
|
||||
request: 'ExtendedHttpRequestWithUser', idService: str, idTransport: str
|
||||
) -> typing.Mapping[str, typing.Any]:
|
||||
# Maybe we could even protect this even more by limiting referer to own server /? (just a meditation..)
|
||||
logger.debug('idService: %s, idTransport: %s', idService, idTransport)
|
||||
url = ''
|
||||
@ -394,13 +397,15 @@ def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTran
|
||||
# If meta service, process and rebuild idService & idTransport
|
||||
|
||||
try:
|
||||
res = userServiceManager().getService(request.user, request.os, request.ip, idService, idTransport, doTest=False)
|
||||
res = userServiceManager().getService(
|
||||
request.user, request.os, request.ip, idService, idTransport, doTest=False
|
||||
)
|
||||
scrambler = cryptoManager().randomString(32)
|
||||
password = cryptoManager().symCrypt(webPassword(request), scrambler)
|
||||
|
||||
userService, trans = res[1], res[3]
|
||||
|
||||
userService.setProperty('accessedByClient', '0') # Reset accesed property to
|
||||
userService.setProperty('accessedByClient', '0') # Reset accesed property to
|
||||
|
||||
typeTrans = trans.getType()
|
||||
|
||||
@ -413,7 +418,7 @@ def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTran
|
||||
'service': 'A' + userService.uuid,
|
||||
'transport': trans.uuid,
|
||||
'user': request.user.uuid,
|
||||
'password': password
|
||||
'password': password,
|
||||
}
|
||||
|
||||
ticket = TicketStore.create(data)
|
||||
@ -422,7 +427,9 @@ def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTran
|
||||
logger.debug('Service not ready')
|
||||
# Not ready, show message and return to this page in a while
|
||||
# error += ' (code {0:04X})'.format(e.code)
|
||||
error = ugettext('Your service is being created, please, wait for a few seconds while we complete it.)') + '({}%)'.format(int(e.code * 25))
|
||||
error = ugettext(
|
||||
'Your service is being created, please, wait for a few seconds while we complete it.)'
|
||||
) + '({}%)'.format(int(e.code * 25))
|
||||
except MaxServicesReachedError:
|
||||
logger.info('Number of service reached MAX for service pool "%s"', idService)
|
||||
error = errors.errorString(errors.MAX_SERVICES_REACHED)
|
||||
@ -433,7 +440,4 @@ def enableService(request: 'ExtendedHttpRequestWithUser', idService: str, idTran
|
||||
logger.exception('Error')
|
||||
error = str(e)
|
||||
|
||||
return {
|
||||
'url': str(url),
|
||||
'error': str(error)
|
||||
}
|
||||
return {'url': str(url), 'error': str(error)}
|
||||
|
@ -33,7 +33,14 @@ import logging
|
||||
|
||||
# from .login import login, logout
|
||||
from uds.web.util.errors import error
|
||||
from .service import transportOwnLink, transportIcon, userServiceEnabler, userServiceStatus, serviceImage, action
|
||||
from .service import (
|
||||
transportOwnLink,
|
||||
transportIcon,
|
||||
userServiceEnabler,
|
||||
userServiceStatus,
|
||||
serviceImage,
|
||||
action,
|
||||
)
|
||||
from .auth import authCallback, authCallback_stage2, authInfo, ticketAuth, customAuth
|
||||
from .download import download
|
||||
from .images import image
|
||||
|
@ -39,7 +39,13 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
import uds.web.util.errors as errors
|
||||
from uds.core import auths
|
||||
from uds.core.auths.auth import webLogin, webLogout, authenticateViaCallback, authLogLogin, getUDSCookie
|
||||
from uds.core.auths.auth import (
|
||||
webLogin,
|
||||
webLogout,
|
||||
authenticateViaCallback,
|
||||
authLogLogin,
|
||||
getUDSCookie,
|
||||
)
|
||||
from uds.core.managers import userServiceManager, cryptoManager
|
||||
from uds.core.services.exceptions import ServiceNotReadyError
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
@ -62,6 +68,7 @@ logger = logging.getLogger(__name__)
|
||||
# * Recovers parameters from first stage
|
||||
# * Process real callback
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def authCallback(request: HttpRequest, authName: str) -> HttpResponse:
|
||||
"""
|
||||
@ -77,7 +84,9 @@ def authCallback(request: HttpRequest, authName: str) -> HttpResponse:
|
||||
params.update(request.POST)
|
||||
params['_query'] = request.META.get('QUERY_STRING', '')
|
||||
|
||||
logger.debug('Auth callback for %s with params %s', authenticator, params.keys())
|
||||
logger.debug(
|
||||
'Auth callback for %s with params %s', authenticator, params.keys()
|
||||
)
|
||||
|
||||
ticket = TicketStore.create({'params': params, 'auth': authenticator.uuid})
|
||||
return HttpResponseRedirect(reverse('page.auth.callback_stage2', args=[ticket]))
|
||||
@ -86,7 +95,9 @@ def authCallback(request: HttpRequest, authName: str) -> HttpResponse:
|
||||
return errors.exceptionView(request, e)
|
||||
|
||||
|
||||
def authCallback_stage2(request: 'ExtendedHttpRequestWithUser', ticketId: str) -> HttpResponse:
|
||||
def authCallback_stage2(
|
||||
request: 'ExtendedHttpRequestWithUser', ticketId: str
|
||||
) -> HttpResponse:
|
||||
try:
|
||||
ticket = TicketStore.get(ticketId)
|
||||
params: typing.Dict[str, typing.Any] = ticket['params']
|
||||
@ -95,14 +106,21 @@ def authCallback_stage2(request: 'ExtendedHttpRequestWithUser', ticketId: str) -
|
||||
params['_request'] = request
|
||||
# params['_session'] = request.session
|
||||
# params['_user'] = request.user
|
||||
logger.debug('Request session:%s -> %s, %s', request.ip, request.session.keys(), request.session.session_key)
|
||||
logger.debug(
|
||||
'Request session:%s -> %s, %s',
|
||||
request.ip,
|
||||
request.session.keys(),
|
||||
request.session.session_key,
|
||||
)
|
||||
|
||||
user = authenticateViaCallback(authenticator, params)
|
||||
|
||||
os = OsDetector.getOsFromUA(request.META['HTTP_USER_AGENT'])
|
||||
|
||||
if user is None:
|
||||
authLogLogin(request, authenticator, '{0}'.format(params), 'Invalid at auth callback')
|
||||
authLogLogin(
|
||||
request, authenticator, '{0}'.format(params), 'Invalid at auth callback'
|
||||
)
|
||||
raise auths.exceptions.InvalidUserException()
|
||||
|
||||
response = HttpResponseRedirect(reverse('page.index'))
|
||||
@ -114,9 +132,14 @@ def authCallback_stage2(request: 'ExtendedHttpRequestWithUser', ticketId: str) -
|
||||
|
||||
return response
|
||||
except auths.exceptions.Redirect as e:
|
||||
return HttpResponseRedirect(request.build_absolute_uri(str(e)) if e.args and e.args[0] else '/' )
|
||||
return HttpResponseRedirect(
|
||||
request.build_absolute_uri(str(e)) if e.args and e.args[0] else '/'
|
||||
)
|
||||
except auths.exceptions.Logout as e:
|
||||
return webLogout(request, request.build_absolute_uri(str(e)) if e.args and e.args[0] else None)
|
||||
return webLogout(
|
||||
request,
|
||||
request.build_absolute_uri(str(e)) if e.args and e.args[0] else None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception('authCallback')
|
||||
return errors.exceptionView(request, e)
|
||||
@ -173,7 +196,9 @@ def customAuth(request: 'HttpRequest', idAuth: str) -> HttpResponse:
|
||||
|
||||
|
||||
@never_cache
|
||||
def ticketAuth(request: 'ExtendedHttpRequestWithUser', ticketId: str) -> HttpResponse: # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||
def ticketAuth(
|
||||
request: 'ExtendedHttpRequestWithUser', ticketId: str
|
||||
) -> HttpResponse: # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||
"""
|
||||
Used to authenticate an user via a ticket
|
||||
"""
|
||||
@ -208,7 +233,9 @@ def ticketAuth(request: 'ExtendedHttpRequestWithUser', ticketId: str) -> HttpRes
|
||||
raise Exception('Invalid ticket authentication')
|
||||
|
||||
usr = auth.getOrCreateUser(username, realname)
|
||||
if usr is None or State.isActive(usr.state) is False: # If user is inactive, raise an exception
|
||||
if (
|
||||
usr is None or State.isActive(usr.state) is False
|
||||
): # If user is inactive, raise an exception
|
||||
raise auths.exceptions.InvalidUserException()
|
||||
|
||||
# Add groups to user (replace existing groups)
|
||||
@ -228,14 +255,20 @@ def ticketAuth(request: 'ExtendedHttpRequestWithUser', ticketId: str) -> HttpRes
|
||||
# Check if servicePool is part of the ticket
|
||||
if poolUuid:
|
||||
# If service pool is in there, also is transport
|
||||
res = userServiceManager().getService(request.user, request.os, request.ip, poolUuid, transport, False)
|
||||
res = userServiceManager().getService(
|
||||
request.user, request.os, request.ip, poolUuid, transport, False
|
||||
)
|
||||
_, userService, _, transport, _ = res
|
||||
|
||||
transportInstance = transport.getInstance()
|
||||
if transportInstance.ownLink is True:
|
||||
link = reverse('TransportOwnLink', args=('A' + userService.uuid, transport.uuid))
|
||||
link = reverse(
|
||||
'TransportOwnLink', args=('A' + userService.uuid, transport.uuid)
|
||||
)
|
||||
else:
|
||||
link = html.udsAccessLink(request, 'A' + userService.uuid, transport.uuid)
|
||||
link = html.udsAccessLink(
|
||||
request, 'A' + userService.uuid, transport.uuid
|
||||
)
|
||||
|
||||
request.session['launch'] = link
|
||||
response = HttpResponseRedirect(reverse('page.ticket.launcher'))
|
||||
|
@ -30,6 +30,7 @@
|
||||
"""
|
||||
|
||||
from django.http import HttpResponse
|
||||
|
||||
# from django.views.decorators.cache import cache_page
|
||||
from uds.core.util.config import GlobalConfig
|
||||
|
||||
|
@ -37,7 +37,10 @@ from .modern import index
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import HttpRequest, HttpResponse # pylint: disable=ungrouped-imports
|
||||
from django.http import (
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
) # pylint: disable=ungrouped-imports
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -33,7 +33,7 @@ import logging
|
||||
import typing
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse, HttpResponseRedirect
|
||||
from django.http import HttpRequest, HttpResponse, JsonResponse, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from uds.core.util.request import ExtendedHttpRequest, ExtendedHttpRequestWithUser
|
||||
from uds.core.auths import auth
|
||||
@ -58,14 +58,16 @@ def index(request: HttpRequest) -> HttpResponse:
|
||||
return response
|
||||
|
||||
|
||||
# Includes a request.session ticket, indicating that
|
||||
# Includes a request.session ticket, indicating that
|
||||
def ticketLauncher(request: HttpRequest) -> HttpResponse:
|
||||
request.session['restricted'] = True # Access is from ticket
|
||||
return index(request)
|
||||
|
||||
|
||||
# Basically, the original /login method, but fixed for modern interface
|
||||
def login(request: ExtendedHttpRequest, tag: typing.Optional[str] = None) -> HttpResponse:
|
||||
def login(
|
||||
request: ExtendedHttpRequest, tag: typing.Optional[str] = None
|
||||
) -> HttpResponse:
|
||||
# Default empty form
|
||||
logger.debug('Tag: %s', tag)
|
||||
if request.method == 'POST':
|
||||
@ -106,7 +108,9 @@ def logout(request: ExtendedHttpRequestWithUser) -> HttpResponse:
|
||||
|
||||
|
||||
def js(request: ExtendedHttpRequest) -> HttpResponse:
|
||||
return HttpResponse(content=configjs.udsJs(request), content_type='application/javascript')
|
||||
return HttpResponse(
|
||||
content=configjs.udsJs(request), content_type='application/javascript'
|
||||
)
|
||||
|
||||
|
||||
@auth.denyNonAuthenticated
|
||||
|
@ -158,7 +158,9 @@ def userServiceStatus(
|
||||
userService = None
|
||||
status = 'running'
|
||||
# If service exists
|
||||
userService = userServiceManager().locateUserService(user=request.user, idService=idService, create=False)
|
||||
userService = userServiceManager().locateUserService(
|
||||
user=request.user, idService=idService, create=False
|
||||
)
|
||||
if userService:
|
||||
# Service exists...
|
||||
try:
|
||||
|
Loading…
x
Reference in New Issue
Block a user