Several Fixes:

* Upgraded typing information on models
* Removed unused DBFile
* renamed osmanager.png wrong name
This commit is contained in:
Adolfo Gómez García 2022-10-01 06:45:41 +02:00
parent 8c40320b64
commit 57f2c35af0
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
49 changed files with 375 additions and 226 deletions

View File

@ -129,7 +129,7 @@ class Authenticators(ModelHandler):
'values': [gui.choiceItem('', _('None'))] 'values': [gui.choiceItem('', _('None'))]
+ gui.sortedChoices( + gui.sortedChoices(
[ [
gui.choiceItem(v.uuid, v.name) gui.choiceItem(v.uuid or '', v.name)
for v in MFA.objects.all() for v in MFA.objects.all()
] ]
), ),

View File

@ -97,6 +97,7 @@ class Module(UserInterface, Environmentable, Serializable):
Environmentable is a base class that provides utility method to access a separate Environment for every single Environmentable is a base class that provides utility method to access a separate Environment for every single
module. module.
""" """
__slots__ = ['_uuid'] __slots__ = ['_uuid']
# Import variable indicating this module is a base class not a real module # Import variable indicating this module is a base class not a real module
# Note that Module is not a real module, but a base class for all modules so isBase is not used on this class # Note that Module is not a real module, but a base class for all modules so isBase is not used on this class
@ -184,12 +185,25 @@ class Module(UserInterface, Environmentable, Serializable):
Base 64 encoded or raw image, obtained from the specified file at Base 64 encoded or raw image, obtained from the specified file at
'iconFile' class attribute 'iconFile' class attribute
""" """
file_ = open( try:
os.path.dirname(typing.cast(str, sys.modules[cls.__module__].__file__)) + '/' + cls.iconFile, with open(
'rb', os.path.dirname(typing.cast(str, sys.modules[cls.__module__].__file__))
) + '/'
data = file_.read() + cls.iconFile,
file_.close() 'rb',
) as f:
data = f.read()
except Exception as e:
logger.error('Error reading icon file for module %s: %s', cls.type(), e)
# blank png bytes
data = codecs.decode(
(
b'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAY0lEQVR42u3QAREAAAQEMJKL'
b'/nI4W4R1KlOPtQABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAg'
b'AABAgQIECBAgAABAgQIECBAgAABAgQIEHDfAvLdn4FABR1mAAAAAElFTkSuQmCC'
),
'base64',
)
return data return data

View File

@ -1,7 +1,8 @@
# Generated by Django 4.1 on 2022-09-01 14:46 # Generated by Django 4.1 on 2022-10-01 06:23
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import uds.core.util.model
import uds.models.notifications import uds.models.notifications
import uds.models.user_service_session import uds.models.user_service_session
import uds.models.util import uds.models.util
@ -52,7 +53,9 @@ class Migration(migrations.Migration):
( (
"uuid", "uuid",
models.CharField( models.CharField(
default=None, max_length=50, null=True, unique=True default=uds.core.util.model.generateUuid,
max_length=50,
unique=True,
), ),
), ),
("data_type", models.CharField(max_length=128)), ("data_type", models.CharField(max_length=128)),
@ -66,7 +69,6 @@ class Migration(migrations.Migration):
default=uds.models.notifications.NotificationLevel["ERROR"] default=uds.models.notifications.NotificationLevel["ERROR"]
), ),
), ),
("tags", models.ManyToManyField(to="uds.tag")),
], ],
options={ options={
"db_table": "uds_notify_prov", "db_table": "uds_notify_prov",
@ -115,6 +117,9 @@ class Migration(migrations.Migration):
"db_table": "uds__user_service_session", "db_table": "uds__user_service_session",
}, },
), ),
migrations.DeleteModel(
name="DBFile",
),
migrations.RemoveField( migrations.RemoveField(
model_name="authenticator", model_name="authenticator",
name="visible", name="visible",
@ -175,6 +180,125 @@ class Migration(migrations.Migration):
name="comments", name="comments",
field=models.CharField(default="", max_length=256), field=models.CharField(default="", max_length=256),
), ),
migrations.AlterField(
model_name="account",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="accountusage",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="authenticator",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="calendar",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="calendaraccess",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="calendaraccessmeta",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="calendaraction",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="calendarrule",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="group",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="image",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="metapool",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="metapoolmember",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="mfa",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="network",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="osmanager",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="permissions",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="provider",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField( migrations.AlterField(
model_name="service", model_name="service",
name="token", name="token",
@ -182,6 +306,55 @@ class Migration(migrations.Migration):
blank=True, default=None, max_length=64, null=True, unique=True blank=True, default=None, max_length=64, null=True, unique=True
), ),
), ),
migrations.AlterField(
model_name="service",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="servicepool",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="servicepoolgroup",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="servicepoolpublication",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="tag",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="ticketstore",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField(
model_name="transport",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField( migrations.AlterField(
model_name="tunneltoken", model_name="tunneltoken",
name="ip", name="ip",
@ -192,11 +365,25 @@ class Migration(migrations.Migration):
name="ip_from", name="ip_from",
field=models.CharField(max_length=128), field=models.CharField(max_length=128),
), ),
migrations.AlterField(
model_name="user",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.AlterField( migrations.AlterField(
model_name="userservice", model_name="userservice",
name="src_ip", name="src_ip",
field=models.CharField(default="", max_length=128), field=models.CharField(default="", max_length=128),
), ),
migrations.AlterField(
model_name="userservice",
name="uuid",
field=models.CharField(
default=uds.core.util.model.generateUuid, max_length=50, unique=True
),
),
migrations.DeleteModel( migrations.DeleteModel(
name="Proxy", name="Proxy",
), ),
@ -218,6 +405,11 @@ class Migration(migrations.Migration):
to="uds.service", to="uds.service",
), ),
), ),
migrations.AddField(
model_name="notifier",
name="tags",
field=models.ManyToManyField(to="uds.tag"),
),
migrations.AddConstraint( migrations.AddConstraint(
model_name="userservicesession", model_name="userservicesession",
constraint=models.UniqueConstraint( constraint=models.UniqueConstraint(

View File

@ -106,9 +106,6 @@ from .account_usage import AccountUsage
# Tagging # Tagging
from .tag import Tag, TaggingMixin from .tag import Tag, TaggingMixin
# Utility
from .dbfile import DBFile
# Tokens # Tokens
from .actor_token import ActorToken from .actor_token import ActorToken
from .tunnel_token import TunnelToken from .tunnel_token import TunnelToken

View File

@ -55,7 +55,7 @@ class Account(UUIDModel, TaggingMixin):
comments = models.CharField(max_length=256, default='') comments = models.CharField(max_length=256, default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["Account"]' #objects: 'models.manager.Manager["Account"]'
usages: 'models.manager.RelatedManager[AccountUsage]' usages: 'models.manager.RelatedManager[AccountUsage]'
def startUsageAccounting(self, userService: 'UserService') -> typing.Optional['AccountUsage']: def startUsageAccounting(self, userService: 'UserService') -> typing.Optional['AccountUsage']:

View File

@ -54,7 +54,7 @@ class AccountUsage(UUIDModel):
""" """
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["AccountUsage"]' # objects: 'models.manager.Manager["AccountUsage"]'
user_name = models.CharField(max_length=128, db_index=True, default='') user_name = models.CharField(max_length=128, db_index=True, default='')
user_uuid = models.CharField(max_length=50, db_index=True, default='') user_uuid = models.CharField(max_length=50, db_index=True, default='')
@ -62,7 +62,7 @@ class AccountUsage(UUIDModel):
pool_uuid = models.CharField(max_length=50, db_index=True, default='') pool_uuid = models.CharField(max_length=50, db_index=True, default='')
start = models.DateTimeField(default=NEVER) start = models.DateTimeField(default=NEVER)
end = models.DateTimeField(default=NEVER) end = models.DateTimeField(default=NEVER)
user_service: 'models.OneToOneField["AccountUsage", UserService]' = ( user_service: 'models.OneToOneField[UserService | None]' = (
models.OneToOneField( models.OneToOneField(
UserService, UserService,
null=True, null=True,
@ -71,7 +71,7 @@ class AccountUsage(UUIDModel):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
) )
account: 'models.ForeignKey["AccountUsage", Account]' = models.ForeignKey( account: 'models.ForeignKey[Account]' = models.ForeignKey(
Account, related_name='usages', on_delete=models.CASCADE Account, related_name='usages', on_delete=models.CASCADE
) )

View File

@ -50,7 +50,7 @@ class ActorToken(models.Model):
stamp = models.DateTimeField() # Date creation or validation of this entry stamp = models.DateTimeField() # Date creation or validation of this entry
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[ActorToken]' # objects: 'models.manager.Manager[ActorToken]'
class Meta: class Meta:
app_label = 'uds' app_label = 'uds'

View File

@ -76,7 +76,7 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
net_filtering = models.CharField(max_length=1, default=NO_FILTERING, db_index=True) net_filtering = models.CharField(max_length=1, default=NO_FILTERING, db_index=True)
# "fake" relations declarations for type checking # "fake" relations declarations for type checking
objects: 'models.manager.Manager["Authenticator"]' # objects: 'models.manager.Manager["Authenticator"]'
users: 'models.manager.RelatedManager[User]' users: 'models.manager.RelatedManager[User]'
groups: 'models.manager.RelatedManager[Group]' groups: 'models.manager.RelatedManager[Group]'
@ -163,7 +163,7 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
Raises: Raises:
""" """
user: 'User' user: 'User'
realName = realName if realName is None else username realName = realName or username
user, _ = self.users.get_or_create( user, _ = self.users.get_or_create(
name=username, name=username,
defaults={ defaults={

View File

@ -49,13 +49,12 @@ class Cache(models.Model):
owner = models.CharField(max_length=128, db_index=True) owner = models.CharField(max_length=128, db_index=True)
key = models.CharField(max_length=64, primary_key=True) key = models.CharField(max_length=64, primary_key=True)
value = models.TextField(default='') value = models.TextField(default='')
created = ( # Date creation or validation of this entry. Set at write time
models.DateTimeField() created = models.DateTimeField()
) # Date creation or validation of this entry. Set at write time
validity = models.IntegerField(default=60) # Validity of this entry, in seconds validity = models.IntegerField(default=60) # Validity of this entry, in seconds
# "fake" relations declarations for type checking # "fake" relations declarations for type checking
objects: 'models.manager.Manager[Cache]' # objects: 'models.manager.Manager[Cache]'
class Meta: class Meta:
""" """

View File

@ -52,7 +52,7 @@ class Calendar(UUIDModel, TaggingMixin):
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["Calendar"]' # objects: 'models.manager.Manager["Calendar"]'
rules: 'models.manager.RelatedManager[CalendarRule]' rules: 'models.manager.RelatedManager[CalendarRule]'
calendaraction_set: 'models.manager.RelatedManager[CalendarAction]' calendaraction_set: 'models.manager.RelatedManager[CalendarAction]'
calendaraccess_set: 'models.manager.RelatedManager[CalendarAccess]' calendaraccess_set: 'models.manager.RelatedManager[CalendarAccess]'

View File

@ -47,17 +47,17 @@ logger = logging.getLogger(__name__)
class CalendarAccess(UUIDModel): class CalendarAccess(UUIDModel):
calendar: 'models.ForeignKey[CalendarAccess, Calendar]' = models.ForeignKey( calendar: 'models.ForeignKey[Calendar]' = models.ForeignKey(
Calendar, on_delete=models.CASCADE Calendar, on_delete=models.CASCADE
) )
service_pool: 'models.ForeignKey[CalendarAccess, ServicePool]' = models.ForeignKey( service_pool: 'models.ForeignKey[ServicePool]' = models.ForeignKey(
ServicePool, related_name='calendarAccess', on_delete=models.CASCADE ServicePool, related_name='calendarAccess', on_delete=models.CASCADE
) )
access = models.CharField(max_length=8, default=states.action.DENY) access = models.CharField(max_length=8, default=states.action.DENY)
priority = models.IntegerField(default=0, db_index=True) priority = models.IntegerField(default=0, db_index=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[CalendarAccess]' # objects: 'models.manager.Manager[CalendarAccess]'
class Meta: class Meta:
""" """

View File

@ -231,10 +231,10 @@ CALENDAR_ACTION_DICT: typing.Dict[str, typing.Dict] = {
class CalendarAction(UUIDModel): class CalendarAction(UUIDModel):
calendar: 'models.ForeignKey[CalendarAction, Calendar]' = models.ForeignKey( calendar: 'models.ForeignKey[Calendar]' = models.ForeignKey(
Calendar, on_delete=models.CASCADE Calendar, on_delete=models.CASCADE
) )
service_pool: 'models.ForeignKey[CalendarAction, ServicePool]' = models.ForeignKey( service_pool: 'models.ForeignKey[ServicePool]' = models.ForeignKey(
ServicePool, on_delete=models.CASCADE ServicePool, on_delete=models.CASCADE
) )
action = models.CharField(max_length=64, default='') action = models.CharField(max_length=64, default='')
@ -252,7 +252,7 @@ class CalendarAction(UUIDModel):
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[CalendarAction]' # objects: 'models.manager.Manager[CalendarAction]'
class Meta: class Meta:
""" """

View File

@ -109,12 +109,12 @@ class CalendarRule(UUIDModel):
duration = models.IntegerField(default=0) # Duration in "duration_unit" units duration = models.IntegerField(default=0) # Duration in "duration_unit" units
duration_unit = models.CharField(choices=dunits, default='MINUTES', max_length=32) duration_unit = models.CharField(choices=dunits, default='MINUTES', max_length=32)
calendar: 'models.ForeignKey["CalendarRule", Calendar]' = models.ForeignKey( calendar: 'models.ForeignKey[Calendar]' = models.ForeignKey(
Calendar, related_name='rules', on_delete=models.CASCADE Calendar, related_name='rules', on_delete=models.CASCADE
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["CalendarRule"]' # objects: 'models.manager.Manager["CalendarRule"]'
class Meta: class Meta:
""" """
@ -134,7 +134,11 @@ class CalendarRule(UUIDModel):
) )
# If at end of interval is requested, displace dstart to match end of interval # If at end of interval is requested, displace dstart to match end of interval
dstart = self.start if not atEnd else self.start + datetime.timedelta(minutes=self.duration_as_minutes) dstart = (
self.start
if not atEnd
else self.start + datetime.timedelta(minutes=self.duration_as_minutes)
)
if self.frequency == WEEKDAYS: if self.frequency == WEEKDAYS:
dw = [] dw = []

View File

@ -53,7 +53,7 @@ class Config(models.Model):
help = models.CharField(max_length=256, default='') help = models.CharField(max_length=256, default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Config]' # objects: 'models.manager.Manager[Config]'
class Meta: class Meta:
""" """

View File

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
# Model based on https://github.com/llazzaro/django-scheduler
#
# Copyright (c) 2016-2020 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. 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.
"""
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
"""
import codecs
import logging
from django.db import models
from .uuid_model import UUIDModel
logger = logging.getLogger(__name__)
class DBFile(UUIDModel):
# Not indexed, used for cleanups only
owner = models.CharField(max_length=32, default='')
name = models.CharField(max_length=255, primary_key=True)
content = models.TextField(blank=True)
size = models.IntegerField(default=0)
created = models.DateTimeField()
modified = models.DateTimeField()
# "fake" declarations for type checking
objects: 'models.manager.Manager[DBFile]'
@property
def data(self) -> bytes:
try:
return codecs.decode(codecs.decode(self.content.encode(), 'base64'), 'zip')
except Exception:
logger.error('DBFile %s has errors and cannot be used', self.name)
try:
self.delete() # Autodelete, invalid...
except Exception:
logger.error('Could not even delete %s!!', self.name)
return b''
@data.setter
def data(self, value: bytes):
self.size = len(value)
self.content = codecs.encode(codecs.encode(value, 'zip'), 'base64').decode()
def __str__(self) -> str:
return 'File: {} {} {} {}'.format(
self.name, self.size, self.created, self.modified
)

View File

@ -59,7 +59,7 @@ class DelayedTask(models.Model):
execution_time = models.DateTimeField(db_index=True) execution_time = models.DateTimeField(db_index=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[DelayedTask]' # objects: 'models.manager.Manager[DelayedTask]'
class Meta: class Meta:
""" """

View File

@ -56,7 +56,7 @@ class Group(UUIDModel):
This class represents a group, associated with one authenticator This class represents a group, associated with one authenticator
""" """
manager: 'models.ForeignKey["Group", Authenticator]' = UnsavedForeignKey( manager: 'models.ForeignKey[Authenticator]' = UnsavedForeignKey(
Authenticator, on_delete=models.CASCADE, related_name='groups' Authenticator, on_delete=models.CASCADE, related_name='groups'
) )
name = models.CharField(max_length=128, db_index=True) name = models.CharField(max_length=128, db_index=True)
@ -69,7 +69,7 @@ class Group(UUIDModel):
created = models.DateTimeField(default=getSqlDatetime, blank=True) created = models.DateTimeField(default=getSqlDatetime, blank=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["Group"]' # objects: 'models.manager.Manager["Group"]'
deployedServices: 'models.manager.RelatedManager[ServicePool]' deployedServices: 'models.manager.RelatedManager[ServicePool]'
permissions: 'models.manager.RelatedManager[Permissions]' permissions: 'models.manager.RelatedManager[Permissions]'

View File

@ -71,7 +71,7 @@ class Image(UUIDModel):
height = models.IntegerField(default=0) height = models.IntegerField(default=0)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.RelatedManager["Image"]' # objects: 'models.manager.RelatedManager["Image"]'
deployedServices: 'models.manager.RelatedManager[ServicePool]' deployedServices: 'models.manager.RelatedManager[ServicePool]'
metaPools: 'models.manager.RelatedManager[MetaPool]' metaPools: 'models.manager.RelatedManager[MetaPool]'

View File

@ -55,7 +55,7 @@ class Log(models.Model):
data = models.CharField(max_length=255, default='') data = models.CharField(max_length=255, default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Log]' # objects: 'models.manager.Manager[Log]'
class Meta: class Meta:
""" """

View File

@ -68,9 +68,11 @@ class ManagedObjectModel(UUIDModel):
""" """
Returns an environment valid for the record this object represents Returns an environment valid for the record this object represents
""" """
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore
def deserialize(self, obj: Module, values: typing.Optional[typing.Mapping[str, str]]): def deserialize(
self, obj: Module, values: typing.Optional[typing.Mapping[str, str]]
):
""" """
Conditionally deserializes obj if not initialized via user interface and data holds something Conditionally deserializes obj if not initialized via user interface and data holds something
""" """

View File

@ -129,7 +129,7 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
ha_policy = models.SmallIntegerField(default=HA_POLICY_DISABLED) ha_policy = models.SmallIntegerField(default=HA_POLICY_DISABLED)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.BaseManager["MetaPool"]' # objects: 'models.BaseManager["MetaPool"]'
calendarAccess: 'models.QuerySet[CalendarAccessMeta]' calendarAccess: 'models.QuerySet[CalendarAccessMeta]'
members: 'models.QuerySet["MetaPoolMember"]' members: 'models.QuerySet["MetaPoolMember"]'
@ -182,7 +182,9 @@ class MetaPool(UUIDModel, TaggingMixin): # type: ignore
access = self.fallbackAccess access = self.fallbackAccess
# Let's see if we can access by current datetime # Let's see if we can access by current datetime
for ac in sorted(self.calendarAccess.all(), key=operator.attrgetter('priority')): for ac in sorted(
self.calendarAccess.all(), key=operator.attrgetter('priority')
):
if CalendarChecker(ac.calendar).check(chkDateTime): if CalendarChecker(ac.calendar).check(chkDateTime):
access = ac.access access = ac.access
break # Stops on first rule match found break # Stops on first rule match found
@ -278,10 +280,10 @@ signals.pre_delete.connect(MetaPool.beforeDelete, sender=MetaPool)
class MetaPoolMember(UUIDModel): class MetaPoolMember(UUIDModel):
pool: 'models.ForeignKey["MetaPoolMember", ServicePool]' = models.ForeignKey( pool: 'models.ForeignKey[ServicePool]' = models.ForeignKey(
ServicePool, related_name='memberOfMeta', on_delete=models.CASCADE ServicePool, related_name='memberOfMeta', on_delete=models.CASCADE
) )
meta_pool: 'models.ForeignKey["MetaPoolMember", MetaPool]' = models.ForeignKey( meta_pool: 'models.ForeignKey[MetaPool]' = models.ForeignKey(
MetaPool, related_name='members', on_delete=models.CASCADE MetaPool, related_name='members', on_delete=models.CASCADE
) )
priority = models.PositiveIntegerField(default=0) priority = models.PositiveIntegerField(default=0)

View File

@ -51,12 +51,14 @@ class MFA(ManagedObjectModel, TaggingMixin): # type: ignore
An OS Manager represents a manager for responding requests for agents inside services. An OS Manager represents a manager for responding requests for agents inside services.
""" """
# "fake" declarations for type checking # Time to remember the device MFA in hours
objects: 'models.BaseManager[MFA]' remember_device = models.IntegerField(default=0)
authenticators: 'models.manager.RelatedManager[Authenticator]' # Limit of time for this MFA to be used, in seconds
validity = models.IntegerField(default=0)
remember_device = models.IntegerField(default=0) # Time to remember the device MFA in hours # "fake" declarations for type checking
validity = models.IntegerField(default=0) # Limit of time for this MFA to be used, in seconds # objects: 'models.BaseManager[MFA]'
authenticators: 'models.manager.RelatedManager[Authenticator]'
def getInstance( def getInstance(
self, values: typing.Optional[typing.Dict[str, str]] = None self, values: typing.Optional[typing.Dict[str, str]] = None
@ -64,15 +66,15 @@ class MFA(ManagedObjectModel, TaggingMixin): # type: ignore
return typing.cast('mfas.MFA', super().getInstance(values=values)) return typing.cast('mfas.MFA', super().getInstance(values=values))
def getType(self) -> typing.Type['mfas.MFA']: def getType(self) -> typing.Type['mfas.MFA']:
""" """Get the type of the object this record represents.
Get the type of the object this record represents.
The type is Python type, it obtains this OsManagersFactory and associated record field. The type is a Python type, it obtains this MFA and associated record field.
Returns: Returns:
The python type for this record object The python type for this record object
:note: We only need to get info from this, not access specific data (class specific info) Note:
We only need to get info from this, not access specific data (class specific info)
""" """
# We only need to get info from this, not access specific data (class specific info) # We only need to get info from this, not access specific data (class specific info)
from uds.core import mfas from uds.core import mfas
@ -100,7 +102,11 @@ class MFA(ManagedObjectModel, TaggingMixin): # type: ignore
s.destroy() s.destroy()
s.env.clearRelatedData() s.env.clearRelatedData()
except Exception as e: except Exception as e:
logger.error('Error processing deletion of notifier %s: %s (forced deletion)', toDelete.name, e) logger.error(
'Error processing deletion of notifier %s: %s (forced deletion)',
toDelete.name,
e,
)
logger.debug('Before delete mfa provider %s', toDelete) logger.debug('Before delete mfa provider %s', toDelete)

View File

@ -63,7 +63,7 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Network]' # objects: 'models.manager.Manager[Network]'
class Meta(UUIDModel.Meta): class Meta(UUIDModel.Meta):
""" """

View File

@ -73,7 +73,7 @@ class Notification(models.Model):
processed = models.BooleanField(default=False) processed = models.BooleanField(default=False)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.BaseManager[Notification]' # objects: 'models.BaseManager[Notification]'
class Meta: class Meta:
""" """

View File

@ -52,7 +52,7 @@ class OSManager(ManagedObjectModel, TaggingMixin):
""" """
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[OSManager]' # objects: 'models.manager.Manager[OSManager]'
deployedServices: 'models.manager.RelatedManager[ServicePool]' deployedServices: 'models.manager.RelatedManager[ServicePool]'
class Meta(ManagedObjectModel.Meta): class Meta(ManagedObjectModel.Meta):

View File

@ -84,7 +84,7 @@ class Permissions(UUIDModel):
permission = models.SmallIntegerField(default=PERMISSION_NONE, db_index=True) permission = models.SmallIntegerField(default=PERMISSION_NONE, db_index=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Permissions]' # objects: 'models.manager.Manager[Permissions]'
@staticmethod @staticmethod
def permissionAsString(perm: int) -> str: def permissionAsString(perm: int) -> str:

View File

@ -56,7 +56,7 @@ class Provider(ManagedObjectModel, TaggingMixin): # type: ignore
maintenance_mode = models.BooleanField(default=False, db_index=True) maintenance_mode = models.BooleanField(default=False, db_index=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Provider]' # objects: 'models.manager.Manager[Provider]'
services: 'models.manager.RelatedManager[Service]' services: 'models.manager.RelatedManager[Service]'
class Meta(ManagedObjectModel.Meta): class Meta(ManagedObjectModel.Meta):

View File

@ -73,7 +73,7 @@ class Scheduler(models.Model):
# primary key id declaration (for type checking) # primary key id declaration (for type checking)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Scheduler]' # objects: 'models.manager.Manager[Scheduler]'
id: int # Primary key (Autogenerated by model, just for type checking) id: int # Primary key (Autogenerated by model, just for type checking)
class Meta: class Meta:
@ -87,7 +87,7 @@ class Scheduler(models.Model):
""" """
Returns an environment valid for the record this object represents Returns an environment valid for the record this object represents
""" """
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore
def getInstance(self) -> typing.Optional[jobs.Job]: def getInstance(self) -> typing.Optional[jobs.Job]:
""" """

View File

@ -74,7 +74,7 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
Server configuration). Server configuration).
""" """
provider: 'models.ForeignKey["Service", Provider]' = models.ForeignKey( provider: 'models.ForeignKey[Provider]' = models.ForeignKey(
Provider, related_name='services', on_delete=models.CASCADE Provider, related_name='services', on_delete=models.CASCADE
) )
@ -89,11 +89,10 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
_cachedInstance: typing.Optional['services.Service'] = None _cachedInstance: typing.Optional['services.Service'] = None
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["Service"]' # objects: 'models.manager.Manager["Service"]'
deployedServices: 'models.manager.RelatedManager[ServicePool]' deployedServices: 'models.manager.RelatedManager[ServicePool]'
aliases: 'models.manager.RelatedManager[ServiceTokenAlias]' aliases: 'models.manager.RelatedManager[ServiceTokenAlias]'
class Meta(ManagedObjectModel.Meta): class Meta(ManagedObjectModel.Meta):
""" """
Meta class to declare default order and unique multiple field index Meta class to declare default order and unique multiple field index
@ -112,7 +111,7 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
Returns an environment valid for the record this object represents Returns an environment valid for the record this object represents
""" """
return Environment.getEnvForTableElement( return Environment.getEnvForTableElement(
self._meta.verbose_name, self._meta.verbose_name, # type: ignore
self.id, self.id,
{ {
'mac': unique.UniqueMacGenerator, 'mac': unique.UniqueMacGenerator,
@ -194,11 +193,9 @@ class Service(ManagedObjectModel, TaggingMixin): # type: ignore
# Counts EVERYTHING for max limit checking # Counts EVERYTHING for max limit checking
return self.max_services_count_type == 1 return self.max_services_count_type == 1
def __str__(self) -> str: def __str__(self) -> str:
return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id) return '{} of type {} (id:{})'.format(self.name, self.data_type, self.id)
@staticmethod @staticmethod
def beforeDelete(sender, **kwargs) -> None: def beforeDelete(sender, **kwargs) -> None:
""" """

View File

@ -70,7 +70,7 @@ if typing.TYPE_CHECKING:
Group, Group,
MetaPoolMember, MetaPoolMember,
CalendarAccess, CalendarAccess,
CalendarAction CalendarAction,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -84,14 +84,14 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
name = models.CharField(max_length=128, default='') name = models.CharField(max_length=128, default='')
short_name = models.CharField(max_length=32, default='') short_name = models.CharField(max_length=32, default='')
comments = models.CharField(max_length=256, default='') comments = models.CharField(max_length=256, default='')
service: 'models.ForeignKey["ServicePool", Service]' = models.ForeignKey( service: 'models.ForeignKey[Service | None]' = models.ForeignKey(
Service, Service,
null=True, null=True,
blank=True, blank=True,
related_name='deployedServices', related_name='deployedServices',
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
osmanager: 'models.ForeignKey["ServicePool", OSManager]' = models.ForeignKey( osmanager: 'models.ForeignKey[OSManager | None]' = models.ForeignKey(
OSManager, OSManager,
null=True, null=True,
blank=True, blank=True,
@ -115,7 +115,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
ignores_unused = models.BooleanField(default=False) ignores_unused = models.BooleanField(default=False)
image: 'models.ForeignKey["ServicePool", Image]' = models.ForeignKey( image: 'models.ForeignKey[Image | None]' = models.ForeignKey(
Image, Image,
null=True, null=True,
blank=True, blank=True,
@ -123,14 +123,12 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
servicesPoolGroup: 'models.ForeignKey["ServicePool", ServicePoolGroup]' = ( servicesPoolGroup: 'models.ForeignKey[ServicePoolGroup | None]' = models.ForeignKey(
models.ForeignKey( ServicePoolGroup,
ServicePoolGroup, null=True,
null=True, blank=True,
blank=True, related_name='servicesPools',
related_name='servicesPools', on_delete=models.SET_NULL,
on_delete=models.SET_NULL,
)
) )
# Message if access denied # Message if access denied
@ -139,7 +137,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
fallbackAccess = models.CharField(default=states.action.ALLOW, max_length=8) fallbackAccess = models.CharField(default=states.action.ALLOW, max_length=8)
# Usage accounting # Usage accounting
account: 'models.ForeignKey["ServicePool", Account]' = models.ForeignKey( account: 'models.ForeignKey[Account | None]' = models.ForeignKey(
Account, Account,
null=True, null=True,
blank=True, blank=True,
@ -162,7 +160,6 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
calendaraction_set: 'models.manager.RelatedManager[CalendarAction]' calendaraction_set: 'models.manager.RelatedManager[CalendarAction]'
changelog: 'models.manager.RelatedManager[ServicePoolPublicationChangelog]' changelog: 'models.manager.RelatedManager[ServicePoolPublicationChangelog]'
class Meta(UUIDModel.Meta): class Meta(UUIDModel.Meta):
""" """
Meta class to declare the name of the table at database Meta class to declare the name of the table at database
@ -175,7 +172,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
""" """
Returns an environment valid for the record this object represents Returns an environment valid for the record this object represents
""" """
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore
def activePublication(self) -> typing.Optional['ServicePoolPublication']: def activePublication(self) -> typing.Optional['ServicePoolPublication']:
""" """
@ -187,7 +184,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
None if there is no valid publication for this deployed service. None if there is no valid publication for this deployed service.
""" """
try: try:
return self.publications.filter(state=states.publication.USABLE)[0] # type: ignore # Slicing is not supported by pylance right now return self.publications.filter(state=states.publication.USABLE)[0] # type: ignore # Slicing is not supported by pylance right now
except Exception: except Exception:
return None return None
@ -316,12 +313,16 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
'UserService', 'UserService',
self.assignedUserServices().filter( self.assignedUserServices().filter(
user=forUser, state__in=states.userService.VALID_STATES user=forUser, state__in=states.userService.VALID_STATES
)[0], # type: ignore # Slicing is not supported by pylance right now )[
0
], # type: ignore # Slicing is not supported by pylance right now
) )
if activePub and found.publication and activePub.id != found.publication.id: if activePub and found.publication and activePub.id != found.publication.id:
ret = self.recoverValue('toBeReplacedIn') ret = self.recoverValue('toBeReplacedIn')
if ret: if ret:
return pickle.loads(ret) # nosec: Value is safe because it is generated by the system return pickle.loads( # nosec: Value is safe because it is generated by the system
ret
)
except Exception: # nosec: We don't want to fail if there is any exception except Exception: # nosec: We don't want to fail if there is any exception
# logger.exception('Recovering publication death line') # logger.exception('Recovering publication death line')
pass pass
@ -337,7 +338,9 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
access = self.fallbackAccess access = self.fallbackAccess
# Let's see if we can access by current datetime # Let's see if we can access by current datetime
for ac in sorted(self.calendarAccess.all(), key=operator.attrgetter('priority')): for ac in sorted(
self.calendarAccess.all(), key=operator.attrgetter('priority')
):
if CalendarChecker(ac.calendar).check(chkDateTime): if CalendarChecker(ac.calendar).check(chkDateTime):
access = ac.access access = ac.access
break # Stops on first rule match found break # Stops on first rule match found
@ -498,6 +501,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
""" """
if ( if (
self.activePublication() is None self.activePublication() is None
and self.service
and self.service.getType().publicationType is not None and self.service.getType().publicationType is not None
): ):
raise InvalidServiceException() raise InvalidServiceException()
@ -599,9 +603,9 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
) )
servicePool: 'ServicePool' servicePool: 'ServicePool'
for servicePool in query: for servicePool in query:
if ( if typing.cast(typing.Any, servicePool).pubs_active or (
typing.cast(typing.Any, servicePool).pubs_active servicePool.service
or servicePool.service.data_type in servicesNotNeedingPub and servicePool.service.data_type in servicesNotNeedingPub
): ):
yield servicePool yield servicePool
@ -661,7 +665,7 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
cachedValue is used to optimize (if known the number of assigned services, we can avoid to query the db) cachedValue is used to optimize (if known the number of assigned services, we can avoid to query the db)
""" """
maxs = self.max_srvs maxs = self.max_srvs
if maxs == 0: if maxs == 0 and self.service:
maxs = self.service.getInstance().maxDeployed maxs = self.service.getInstance().maxDeployed
if maxs <= 0: if maxs <= 0:
@ -676,8 +680,10 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
return 100 * cachedValue // maxs return 100 * cachedValue // maxs
def testServer(self, host: str, port: typing.Union[str, int], timeout: float=4) -> bool: def testServer(
return self.service.testServer(host, port, timeout) self, host: str, port: typing.Union[str, int], timeout: float = 4
) -> bool:
return bool(self.service) and self.service.testServer(host, port, timeout)
# Utility for logging # Utility for logging
def log(self, message: str, level: int = log.INFO) -> None: def log(self, message: str, level: int = log.INFO) -> None:

View File

@ -54,7 +54,7 @@ class ServicePoolGroup(UUIDModel):
name = models.CharField(max_length=128, default='', db_index=True, unique=True) name = models.CharField(max_length=128, default='', db_index=True, unique=True)
comments = models.CharField(max_length=256, default='') comments = models.CharField(max_length=256, default='')
priority = models.IntegerField(default=0, db_index=True) priority = models.IntegerField(default=0, db_index=True)
image: 'models.ForeignKey[ServicePoolGroup, Image]' = models.ForeignKey( image: 'models.ForeignKey[Image | None]' = models.ForeignKey(
Image, Image,
null=True, null=True,
blank=True, blank=True,
@ -63,7 +63,7 @@ class ServicePoolGroup(UUIDModel):
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[ServicePoolGroup]' # objects: 'models.manager.Manager[ServicePoolGroup]'
class Meta(UUIDModel.Meta): class Meta(UUIDModel.Meta):
""" """
@ -75,7 +75,7 @@ class ServicePoolGroup(UUIDModel):
def __str__(self) -> str: def __str__(self) -> str:
return 'Service Pool group {}({}): {}'.format( return 'Service Pool group {}({}): {}'.format(
self.name, self.comments, self.image.name self.name, self.comments, self.image.name if self.image else ''
) )
@property @property

View File

@ -55,17 +55,15 @@ logger = logging.getLogger(__name__)
class ServicePoolPublicationChangelog(models.Model): class ServicePoolPublicationChangelog(models.Model):
# This should be "servicePool" # This should be "servicePool"
publication: 'models.ForeignKey[ServicePoolPublicationChangelog, ServicePool]' = ( publication: 'models.ForeignKey[ServicePool]' = models.ForeignKey(
models.ForeignKey( ServicePool, on_delete=models.CASCADE, related_name='changelog'
ServicePool, on_delete=models.CASCADE, related_name='changelog'
)
) )
stamp = models.DateTimeField() stamp = models.DateTimeField()
revision = models.PositiveIntegerField(default=1) revision = models.PositiveIntegerField(default=1)
log = models.TextField(default='') log = models.TextField(default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[ServicePoolPublicationChangelog]' # objects: 'models.manager.Manager[ServicePoolPublicationChangelog]'
class Meta(UUIDModel.Meta): class Meta(UUIDModel.Meta):
""" """
@ -86,10 +84,8 @@ class ServicePoolPublication(UUIDModel):
A deployed service publication keep track of data needed by services that needs "preparation". (i.e. Virtual machine --> base machine --> children of base machines) A deployed service publication keep track of data needed by services that needs "preparation". (i.e. Virtual machine --> base machine --> children of base machines)
""" """
deployed_service: 'models.ForeignKey["ServicePoolPublication", ServicePool]' = ( deployed_service: 'models.ForeignKey[ServicePool]' = models.ForeignKey(
models.ForeignKey( ServicePool, on_delete=models.CASCADE, related_name='publications'
ServicePool, on_delete=models.CASCADE, related_name='publications'
)
) )
publish_date = models.DateTimeField(db_index=True) publish_date = models.DateTimeField(db_index=True)
# data_type = models.CharField(max_length=128) # The data type is specified by the service itself # data_type = models.CharField(max_length=128) # The data type is specified by the service itself
@ -106,7 +102,7 @@ class ServicePoolPublication(UUIDModel):
revision = models.PositiveIntegerField(default=1) revision = models.PositiveIntegerField(default=1)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["ServicePoolPublication"]' # objects: 'models.manager.Manager["ServicePoolPublication"]'
userServices: 'models.manager.RelatedManager[UserService]' userServices: 'models.manager.RelatedManager[UserService]'
class Meta(UUIDModel.Meta): class Meta(UUIDModel.Meta):
@ -122,7 +118,7 @@ class ServicePoolPublication(UUIDModel):
""" """
Returns an environment valid for the record this object represents Returns an environment valid for the record this object represents
""" """
return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) return Environment.getEnvForTableElement(self._meta.verbose_name, self.id) # type: ignore
def getInstance(self) -> 'services.Publication': def getInstance(self) -> 'services.Publication':
""" """
@ -139,6 +135,8 @@ class ServicePoolPublication(UUIDModel):
Raises: Raises:
""" """
if not self.deployed_service.service:
raise Exception('No service assigned to publication')
serviceInstance = self.deployed_service.service.getInstance() serviceInstance = self.deployed_service.service.getInstance()
osManager = self.deployed_service.osmanager osManager = self.deployed_service.osmanager
osManagerInstance = osManager.getInstance() if osManager else None osManagerInstance = osManager.getInstance() if osManager else None

View File

@ -57,7 +57,7 @@ class StatsCounters(models.Model):
value = models.IntegerField(db_index=True, default=0) value = models.IntegerField(db_index=True, default=0)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[StatsCounters]' # objects: 'models.manager.Manager[StatsCounters]'
class Meta: class Meta:
""" """
@ -125,7 +125,7 @@ class StatsCounters(models.Model):
floor = getSqlFnc('FLOOR') floor = getSqlFnc('FLOOR')
if interval > 0: if interval > 0:
q = q.extra( q = q.extra( # nosec: SQL injection is not possible here, all values are integers
select={ select={
'group_by_stamp': f'{floor}(stamp / {interval}) * {interval}', 'group_by_stamp': f'{floor}(stamp / {interval}) * {interval}',
}, },
@ -227,7 +227,7 @@ class StatsCounters(models.Model):
# fnc = getSqlFnc('MAX' if kwargs.get('use_max', False) else 'AVG') # fnc = getSqlFnc('MAX' if kwargs.get('use_max', False) else 'AVG')
query = ( query = (
'SELECT -1 as id,-1 as owner_id,-1 as owner_type,-1 as counter_type, ' 'SELECT -1 as id,-1 as owner_id,-1 as owner_type,-1 as counter_type, ' # nosec: SQL injection is not possible here, all values are controlled
+ stampValue + stampValue
+ '*{}'.format(interval) + '*{}'.format(interval)
+ ' AS stamp, ' + ' AS stamp, '

View File

@ -60,7 +60,7 @@ class StatsEvents(models.Model):
fld4 = models.CharField(max_length=128, default='') fld4 = models.CharField(max_length=128, default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[StatsEvents]' # objects: 'models.manager.Manager[StatsEvents]'
class Meta: class Meta:
""" """

View File

@ -51,7 +51,7 @@ class Storage(models.Model):
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Storage]' # objects: 'models.manager.Manager[Storage]'
class Meta: class Meta:
""" """
@ -62,5 +62,5 @@ class Storage(models.Model):
def __str__(self) -> str: def __str__(self) -> str:
return '{} {} > str= {}, {}'.format( return '{} {} > str= {}, {}'.format(
self.owner, self.key, self.data, '/'.join([self.attr1]) self.owner, self.key, self.data, '/'.join([self.attr1 or ''])
) )

View File

@ -65,7 +65,7 @@ class Tag(UUIDModel):
tag = models.CharField(max_length=32, db_index=True, unique=True) tag = models.CharField(max_length=32, db_index=True, unique=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["Tag"]' # objects: 'models.manager.Manager["Tag"]'
# Every single related class has a relation with this # Every single related class has a relation with this
# Its inverse is "xxx_set" class # Its inverse is "xxx_set" class

View File

@ -29,7 +29,7 @@
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com .. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
''' '''
import datetime import datetime
import pickle import pickle # nosec: Tickets are generated by us, so we know they are safe
import logging import logging
import typing import typing
@ -71,7 +71,7 @@ class TicketStore(UUIDModel):
) # Associated validator for this ticket ) # Associated validator for this ticket
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[TicketStore]' # objects: 'models.manager.Manager[TicketStore]'
class InvalidTicket(Exception): class InvalidTicket(Exception):
pass pass
@ -151,11 +151,11 @@ class TicketStore(UUIDModel):
data, typing.cast(str, owner).encode() data, typing.cast(str, owner).encode()
) )
data = pickle.loads(data) data = pickle.loads(data) # nosec: Tickets are generated by us, so we know they are safe
# If has validator, execute it # If has validator, execute it
if t.validator: if t.validator:
validator: ValidatorType = pickle.loads(t.validator) validator: ValidatorType = pickle.loads(t.validator) # nosec: Tickets are generated by us, so we know they are safe
if validator(data) is False: if validator(data) is False:
raise TicketStore.InvalidTicket('Validation failed') raise TicketStore.InvalidTicket('Validation failed')
@ -194,7 +194,7 @@ class TicketStore(UUIDModel):
) -> str: ) -> str:
owner = cryptoManager().randomString(length=8) owner = cryptoManager().randomString(length=8)
data = { data = {
'u': userService.user.uuid, 'u': userService.user.uuid if userService.user else '',
's': userService.uuid, 's': userService.uuid,
'h': host, 'h': host,
'p': port, 'p': port,
@ -259,7 +259,7 @@ class TicketStore(UUIDModel):
TicketStore.objects.filter(stamp__lt=cleanSince).delete() TicketStore.objects.filter(stamp__lt=cleanSince).delete()
def __str__(self) -> str: def __str__(self) -> str:
data = pickle.loads(self.data) if self.owner != SECURED else '{Secure Ticket}' data = pickle.loads(self.data) if self.owner != SECURED else '{Secure Ticket}' # nosec: Tickets are generated by us, so we know they are safe
return 'Ticket id: {}, Owner: {}, Stamp: {}, Validity: {}, Data: {}'.format( return 'Ticket id: {}, Owner: {}, Stamp: {}, Validity: {}, Data: {}'.format(
self.uuid, self.uuid,

View File

@ -71,7 +71,7 @@ class Transport(ManagedObjectModel, TaggingMixin):
label = models.CharField(max_length=32, default='', db_index=True) label = models.CharField(max_length=32, default='', db_index=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[Transport]' # objects: 'models.manager.Manager[Transport]'
deployedServices: 'models.manager.RelatedManager[ServicePool]' deployedServices: 'models.manager.RelatedManager[ServicePool]'
networks: 'models.manager.RelatedManager[Network]' networks: 'models.manager.RelatedManager[Network]'

View File

@ -48,7 +48,7 @@ class TunnelToken(models.Model):
stamp = models.DateTimeField() # Date creation or validation of this entry stamp = models.DateTimeField() # Date creation or validation of this entry
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[TunnelToken]' # objects: 'models.manager.Manager[TunnelToken]'
class Meta: class Meta:
app_label = 'uds' app_label = 'uds'

View File

@ -50,7 +50,7 @@ class UniqueId(models.Model):
stamp = models.IntegerField(db_index=True, default=0) stamp = models.IntegerField(db_index=True, default=0)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[UniqueId]' # objects: 'models.manager.Manager[UniqueId]'
class Meta: class Meta:
""" """

View File

@ -57,7 +57,7 @@ class User(UUIDModel):
This class represents a single user, associated with one authenticator This class represents a single user, associated with one authenticator
""" """
manager: 'models.ForeignKey["User", Authenticator]' = UnsavedForeignKey( manager: 'models.ForeignKey[Authenticator]' = UnsavedForeignKey(
Authenticator, on_delete=models.CASCADE, related_name='users' Authenticator, on_delete=models.CASCADE, related_name='users'
) )
name = models.CharField(max_length=128, db_index=True) name = models.CharField(max_length=128, db_index=True)
@ -79,7 +79,7 @@ class User(UUIDModel):
created = models.DateTimeField(default=getSqlDatetime, blank=True) created = models.DateTimeField(default=getSqlDatetime, blank=True)
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["User"]' # objects: 'models.manager.Manager["User"]'
groups: 'models.manager.RelatedManager[Group]' groups: 'models.manager.RelatedManager[Group]'
userServices: 'models.manager.RelatedManager[UserService]' userServices: 'models.manager.RelatedManager[UserService]'
permissions: 'models.manager.RelatedManager[Permissions]' permissions: 'models.manager.RelatedManager[Permissions]'

View File

@ -51,7 +51,7 @@ class UserPreference(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='preferences') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='preferences')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[UserPreference]' # objects: 'models.manager.Manager[UserPreference]'
class Meta: class Meta:
app_label = 'uds' app_label = 'uds'

View File

@ -71,18 +71,16 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
# The reference to deployed service is used to accelerate the queries for different methods, in fact its redundant cause we can access to the deployed service # The reference to deployed service is used to accelerate the queries for different methods, in fact its redundant cause we can access to the deployed service
# through publication, but queries are much more simple # through publication, but queries are much more simple
deployed_service: 'models.ForeignKey["UserService", ServicePool]' = models.ForeignKey( deployed_service: 'models.ForeignKey["ServicePool"]' = models.ForeignKey(
ServicePool, on_delete=models.CASCADE, related_name='userServices' ServicePool, on_delete=models.CASCADE, related_name='userServices'
) )
publication: 'models.ForeignKey["UserService", ServicePoolPublication]' = ( publication: 'models.ForeignKey[ServicePoolPublication | None]' = models.ForeignKey(
models.ForeignKey(
ServicePoolPublication, ServicePoolPublication,
on_delete=models.CASCADE, on_delete=models.CASCADE,
null=True, null=True,
blank=True, blank=True,
related_name='userServices', related_name='userServices',
) )
)
unique_id = models.CharField( unique_id = models.CharField(
max_length=128, default='', db_index=True max_length=128, default='', db_index=True
@ -116,7 +114,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
src_ip = models.CharField(max_length=128, default='') src_ip = models.CharField(max_length=128, default='')
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["UserService"]' # objects: 'models.manager.Manager["UserService"]'
properties: 'models.manager.RelatedManager[UserServiceProperty]' properties: 'models.manager.RelatedManager[UserServiceProperty]'
sessions: 'models.manager.RelatedManager[UserServiceSession]' sessions: 'models.manager.RelatedManager[UserServiceSession]'
accounting: 'AccountUsage' accounting: 'AccountUsage'
@ -174,7 +172,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
(see related classes uds.core.util.unique_name_generator and uds.core.util.unique_mac_generator) (see related classes uds.core.util.unique_name_generator and uds.core.util.unique_mac_generator)
""" """
return Environment.getEnvForTableElement( return Environment.getEnvForTableElement(
self._meta.verbose_name, self._meta.verbose_name, # type: ignore
self.id, self.id,
{ {
'mac': unique.UniqueMacGenerator, 'mac': unique.UniqueMacGenerator,
@ -201,6 +199,8 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
""" """
# We get the service instance, publication instance and osmanager instance # We get the service instance, publication instance and osmanager instance
servicePool = self.deployed_service servicePool = self.deployed_service
if not servicePool.service:
raise Exception('Service not found')
serviceInstance = servicePool.service.getInstance() serviceInstance = servicePool.service.getInstance()
if serviceInstance.needsManager is False or not servicePool.osmanager: if serviceInstance.needsManager is False or not servicePool.osmanager:
osmanagerInstance = None osmanagerInstance = None
@ -374,6 +374,8 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
:note: This method MUST be invoked by transport before using credentials passed to getJavascript. :note: This method MUST be invoked by transport before using credentials passed to getJavascript.
""" """
servicePool = self.deployed_service servicePool = self.deployed_service
if not servicePool.service:
raise Exception('Service not found')
serviceInstance = servicePool.service.getInstance() serviceInstance = servicePool.service.getInstance()
if serviceInstance.needsManager is False or not servicePool.osmanager: if serviceInstance.needsManager is False or not servicePool.osmanager:
return (username, password) return (username, password)
@ -615,7 +617,7 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
Returns True if this user service does not needs an publication, or if this deployed service publication is the current one Returns True if this user service does not needs an publication, or if this deployed service publication is the current one
""" """
return ( return (
self.deployed_service.service.getType().publicationType is None (self.deployed_service.service and self.deployed_service.service.getType().publicationType is None)
or self.publication == self.deployed_service.activePublication() or self.publication == self.deployed_service.activePublication()
) )

View File

@ -53,7 +53,7 @@ class UserServiceProperty(models.Model): # pylint: disable=too-many-public-meth
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager[UserServiceProperty]' # objects: 'models.manager.Manager[UserServiceProperty]'
class Meta: class Meta:
""" """

View File

@ -58,12 +58,12 @@ class UserServiceSession(models.Model): # pylint: disable=too-many-public-metho
start = models.DateTimeField(default=getSqlDatetime) start = models.DateTimeField(default=getSqlDatetime)
end = models.DateTimeField(null=True, blank=True) end = models.DateTimeField(null=True, blank=True)
user_service = models.ForeignKey( user_service: 'models.ForeignKey[UserService]' = models.ForeignKey(
UserService, on_delete=models.CASCADE, related_name='sessions' UserService, on_delete=models.CASCADE, related_name='sessions'
) )
# "fake" declarations for type checking # "fake" declarations for type checking
objects: 'models.manager.Manager["UserServiceSession"]' # objects: 'models.manager.Manager["UserServiceSession"]'
class Meta: class Meta:
""" """

View File

@ -55,14 +55,16 @@ class UnsavedForeignKey(models.ForeignKey):
def getSqlDatetime() -> datetime: def getSqlDatetime() -> datetime:
""" """Returns the current date/time of the database server.
Returns the current date/time of the database server.
We use this time as method of keeping all operations betwen different servers in sync. We use this time as method to keep all operations betwen different servers in sync.
We support get database datetime for: We support get database datetime for:
* mysql * mysql
* sqlite * sqlite
Returns:
datetime: Current datetime of the database server
""" """
if connection.vendor in ('mysql', 'microsoft'): if connection.vendor in ('mysql', 'microsoft'):
cursor = connection.cursor() cursor = connection.cursor()
@ -72,7 +74,7 @@ def getSqlDatetime() -> datetime:
else 'SELECT CURRENT_TIMESTAMP' else 'SELECT CURRENT_TIMESTAMP'
) )
cursor.execute(sentence) cursor.execute(sentence)
date = cursor.fetchone()[0] date = (cursor.fetchone() or [datetime.now()])[0]
else: else:
date = ( date = (
datetime.now() datetime.now()
@ -82,12 +84,19 @@ def getSqlDatetime() -> datetime:
def getSqlDatetimeAsUnix() -> int: def getSqlDatetimeAsUnix() -> int:
"""Returns the current date/time of the database server as unix timestamp
Returns:
int: Unix timestamp
"""
return int(mktime(getSqlDatetime().timetuple())) return int(mktime(getSqlDatetime().timetuple()))
def getSqlFnc(fncName: str) -> str: def getSqlFnc(fncName: str) -> str:
""" """Convert different sql functions for different platforms
Convert different sql functions for different platforms
i.e. CEIL --> CEILING on mssql
""" """
if connection.vendor == 'microsoft': if connection.vendor == 'microsoft':
return {'CEIL': 'CEILING'}.get(fncName, fncName) return {'CEIL': 'CEILING'}.get(fncName, fncName)

View File

@ -45,7 +45,7 @@ class UUIDModel(models.Model):
Base abstract model for models that require an uuid Base abstract model for models that require an uuid
""" """
uuid = models.CharField(max_length=50, default=None, null=True, unique=True) uuid = models.CharField(max_length=50, unique=True, default=generateUuid)
# Automatic field from Model without a defined specific primary_key # Automatic field from Model without a defined specific primary_key
# Just a fake declaration to allow type checking # Just a fake declaration to allow type checking
@ -58,9 +58,7 @@ class UUIDModel(models.Model):
return generateUuid() return generateUuid()
# Override default save to add uuid # Override default save to add uuid
def save( def save(self, *args, **kwargs):
self, *args, **kwargs
):
if not self.uuid: if not self.uuid:
self.uuid = self.genUuid() self.uuid = self.genUuid()
elif self.uuid != self.uuid.lower(): elif self.uuid != self.uuid.lower():

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -36,6 +36,7 @@ from uds.core import services
from uds.core.ui import gui from uds.core.ui import gui
from uds.core.util import validators from uds.core.util import validators
from uds.core.util.unique_id_generator import UniqueIDGenerator from uds.core.util.unique_id_generator import UniqueIDGenerator
from uds.core.util.unique_mac_generator import UniqueMacGenerator
from uds.core.util.cache import Cache from uds.core.util.cache import Cache
from uds.core.util.decorators import allowCache from uds.core.util.decorators import allowCache