mirror of
https://github.com/dkmstr/openuds.git
synced 2025-03-13 08:58:35 +03:00
Added meta pool recursion save restiction and some more tests
This commit is contained in:
parent
5b5beb30d6
commit
f71659b5a6
@ -33,10 +33,10 @@ import functools
|
||||
import logging
|
||||
|
||||
from uds import models
|
||||
from uds.core import VERSION
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from ...utils import rest
|
||||
from ...fixtures import rest as rest_fixtures
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -66,16 +66,7 @@ class GroupsTest(rest.test.RESTActorTestCase):
|
||||
for group in groups:
|
||||
# Locate the group in the auth
|
||||
dbgrp = self.auth.groups.get(name=group['name'])
|
||||
self.assertEqual(dbgrp.uuid, group['id'])
|
||||
self.assertEqual(dbgrp.comments, group['comments'])
|
||||
self.assertEqual(dbgrp.state, group['state'])
|
||||
self.assertEqual(dbgrp.is_meta, group['type'] == 'meta')
|
||||
self.assertEqual(dbgrp.meta_if_any, group['meta_if_any'])
|
||||
if dbgrp.is_meta:
|
||||
self.assertEqual(
|
||||
sorted([x.uuid for x in dbgrp.groups.all()]),
|
||||
sorted(group['groups'])
|
||||
)
|
||||
self.assertTrue(rest.assertions.assertGroupIs(dbgrp, group, compare_uuid=True))
|
||||
|
||||
def test_groups_tableinfo(self) -> None:
|
||||
url = f'authenticators/{self.auth.uuid}/groups/tableinfo'
|
||||
@ -113,15 +104,7 @@ class GroupsTest(rest.test.RESTActorTestCase):
|
||||
response = self.client.rest_get(f'{url}/{i.uuid}')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
group = response.json()
|
||||
|
||||
self.assertEqual(group['name'], i.name)
|
||||
self.assertEqual(group['id'], i.uuid)
|
||||
self.assertEqual(group['comments'], i.comments)
|
||||
self.assertEqual(group['state'], i.state)
|
||||
self.assertEqual(group['type'], 'meta' if i.is_meta else 'group')
|
||||
self.assertEqual(group['meta_if_any'], i.meta_if_any)
|
||||
if i.is_meta:
|
||||
self.assertEqual(sorted(group['groups']), sorted([x.uuid for x in i.groups.all()]))
|
||||
self.assertTrue(rest.assertions.assertGroupIs(i, group, compare_uuid=True))
|
||||
|
||||
# invalid user
|
||||
response = self.client.rest_get(f'{url}/invalid')
|
||||
@ -129,6 +112,30 @@ class GroupsTest(rest.test.RESTActorTestCase):
|
||||
|
||||
|
||||
def test_group_create_edit(self) -> None:
|
||||
url = f'authenticators/{self.auth.uuid}/groups'
|
||||
# Normal group
|
||||
group_dct = rest_fixtures.createGroup()
|
||||
response = self.client.rest_put(
|
||||
url,
|
||||
group_dct,
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
group = models.Group.objects.get(name=group_dct['name'])
|
||||
self.assertTrue(rest.assertions.assertGroupIs(group, group_dct))
|
||||
|
||||
# Now, will fail because name is already in use
|
||||
response = self.client.rest_put(
|
||||
url,
|
||||
group_dct,
|
||||
content_type='application/json',
|
||||
)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
# Now a meta group, with some groups inside
|
||||
groups = [self.simple_groups[0].uuid]
|
||||
|
||||
|
||||
return
|
||||
url = f'authenticators/{self.auth.uuid}/users'
|
||||
user_dct: typing.Dict[str, typing.Any] = {
|
||||
|
@ -28,13 +28,12 @@
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import time
|
||||
import typing
|
||||
import functools
|
||||
import logging
|
||||
|
||||
from uds import models
|
||||
from uds.core import VERSION
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from ...utils import rest
|
||||
from ...fixtures import rest as rest_fixtures
|
||||
@ -66,7 +65,7 @@ class UsersTest(rest.test.RESTActorTestCase):
|
||||
for user in users:
|
||||
# Locate the user in the auth
|
||||
self.assertTrue(
|
||||
rest_fixtures.assertUserIs(self.auth.users.get(name=user['name']), user)
|
||||
rest.assertions.assertUserIs(self.auth.users.get(name=user['name']), user)
|
||||
)
|
||||
|
||||
def test_users_tableinfo(self) -> None:
|
||||
@ -109,7 +108,7 @@ class UsersTest(rest.test.RESTActorTestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
user = response.json()
|
||||
self.assertTrue(
|
||||
rest_fixtures.assertUserIs(i, user),
|
||||
rest.assertions.assertUserIs(i, user),
|
||||
'User {} {} is not correct'.format(
|
||||
i, models.User.objects.filter(uuid=i.uuid).values()[0]
|
||||
),
|
||||
@ -134,18 +133,20 @@ class UsersTest(rest.test.RESTActorTestCase):
|
||||
def test_user_create_edit(self) -> None:
|
||||
url = f'authenticators/{self.auth.uuid}/users'
|
||||
user_dct = rest_fixtures.createUser(
|
||||
groups=[self.groups[0].uuid, self.groups[1].uuid]
|
||||
groups=[self.simple_groups[0].uuid, self.simple_groups[1].uuid, self.meta_groups[0].uuid]
|
||||
)
|
||||
# Now, will work
|
||||
response = self.client.rest_put(
|
||||
url,
|
||||
user_dct,
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
# Get user from database and ensure values are correct
|
||||
dbusr = self.auth.users.get(name=user_dct['name'])
|
||||
self.assertTrue(rest_fixtures.assertUserIs(dbusr, user_dct))
|
||||
|
||||
# Fix user_dct to remove it for comparison. Meta groups cannot be directly "assigned" to users
|
||||
user_dct['groups'] = user_dct['groups'][:-1]
|
||||
self.assertTrue(rest.assertions.assertUserIs(dbusr, user_dct))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Returns nothing
|
||||
@ -160,7 +161,7 @@ class UsersTest(rest.test.RESTActorTestCase):
|
||||
|
||||
user_dct = rest_fixtures.createUser( # nosec: test password, also, "fixme" means "create a random password" in this case
|
||||
id=dbusr.uuid,
|
||||
groups=[self.groups[2].uuid],
|
||||
groups=[self.simple_groups[2].uuid],
|
||||
password='fixme',
|
||||
mfa_data='mfadata',
|
||||
)
|
||||
@ -175,7 +176,7 @@ class UsersTest(rest.test.RESTActorTestCase):
|
||||
# Get user from database and ensure values are correct
|
||||
dbusr = self.auth.users.get(name=user_dct['name'])
|
||||
self.assertTrue(
|
||||
rest_fixtures.assertUserIs(dbusr, user_dct, compare_password=True)
|
||||
rest.assertions.assertUserIs(dbusr, user_dct, compare_password=True)
|
||||
)
|
||||
|
||||
def test_user_delete(self) -> None:
|
||||
|
51
server/src/tests/fixtures/rest.py
vendored
51
server/src/tests/fixtures/rest.py
vendored
@ -30,11 +30,7 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import typing
|
||||
|
||||
from uds import models
|
||||
from uds.core.auths.user import User as aUser
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from ..utils import rest, ensure_data
|
||||
from ..utils import rest
|
||||
|
||||
# User REST structure
|
||||
class UserRestStruct(rest.RestStruct):
|
||||
@ -49,42 +45,19 @@ class UserRestStruct(rest.RestStruct):
|
||||
mfa_data: typing.Optional[str]
|
||||
password: typing.Optional[str]
|
||||
|
||||
# Group REST structure
|
||||
class GroupRestStruct(rest.RestStruct):
|
||||
id: rest.uuid_type
|
||||
name: str
|
||||
comments: str
|
||||
state: str
|
||||
type: str
|
||||
is_meta: bool
|
||||
meta_if_any: bool
|
||||
|
||||
# Provide a "random" dictionary based on a
|
||||
def createUser(**kwargs) -> typing.Dict[str, typing.Any]:
|
||||
return UserRestStruct.random_create(**kwargs).as_dict()
|
||||
|
||||
|
||||
def assertUserIs(
|
||||
user: models.User, compare_to: typing.Mapping[str, typing.Any], compare_uuid=False, compare_password=False
|
||||
) -> bool:
|
||||
ignore_fields = ['password', 'groups', 'mfa_data', 'last_access', 'role']
|
||||
|
||||
if not compare_uuid:
|
||||
ignore_fields.append('id')
|
||||
|
||||
# If last_access is present, compare it here, because it's a datetime object
|
||||
if 'last_access' in compare_to:
|
||||
if int(user.last_access.timestamp()) != compare_to['last_access']:
|
||||
return False
|
||||
|
||||
if ensure_data(user, compare_to, ignore_keys=ignore_fields):
|
||||
# Compare groups
|
||||
if 'groups' in compare_to:
|
||||
if set(g.dbGroup().uuid for g in aUser(user).groups()) != set(
|
||||
compare_to['groups']
|
||||
):
|
||||
return False
|
||||
|
||||
# Compare mfa_data
|
||||
if 'mfa_data' in compare_to:
|
||||
if user.mfa_data != compare_to['mfa_data']:
|
||||
return False
|
||||
|
||||
# Compare password
|
||||
if compare_password:
|
||||
return cryptoManager().checkHash(compare_to['password'], user.password)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
def createGroup(**kwargs) -> typing.Dict[str, typing.Any]:
|
||||
return GroupRestStruct.random_create(**kwargs).as_dict()
|
||||
|
@ -74,9 +74,6 @@ def compare_dicts(
|
||||
if v != actual[k]:
|
||||
errors.append((k, f'Value for key "{k}" is "{actual[k]}" instead of "{v}"'))
|
||||
|
||||
if errors:
|
||||
logger.info('Errors found: %s', errors)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
@ -95,6 +92,11 @@ def ensure_data(
|
||||
db_data['id'] = db_data['uuid']
|
||||
del db_data['uuid']
|
||||
|
||||
return not compare_dicts(
|
||||
errors = compare_dicts(
|
||||
dct, db_data, ignore_keys=ignore_keys, ignore_values=ignore_values
|
||||
)
|
||||
if errors:
|
||||
logger.info('Errors found: %s', errors)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -31,13 +31,15 @@
|
||||
|
||||
import logging
|
||||
import random
|
||||
import uuid
|
||||
import typing
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.client import Client
|
||||
|
||||
# Not used, alloes "rest.test" or "rest.assertions"
|
||||
from . import test
|
||||
from . import assertions
|
||||
|
||||
from .. import generators
|
||||
|
||||
from uds.REST.handlers import AUTH_TOKEN_HEADER
|
||||
|
114
server/src/tests/utils/rest/assertions.py
Normal file
114
server/src/tests/utils/rest/assertions.py
Normal file
@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2022 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from uds import models
|
||||
from uds.core.auths.user import User as aUser
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from .. import ensure_data
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def assertUserIs(
|
||||
user: models.User, compare_to: typing.Mapping[str, typing.Any], compare_uuid=False, compare_password=False
|
||||
) -> bool:
|
||||
ignore_fields = ['password', 'groups', 'mfa_data', 'last_access', 'role']
|
||||
|
||||
if not compare_uuid:
|
||||
ignore_fields.append('id')
|
||||
|
||||
# If last_access is present, compare it here, because it's a datetime object
|
||||
if 'last_access' in compare_to:
|
||||
if int(user.last_access.timestamp()) != compare_to['last_access']:
|
||||
logger.info('User last_access do not match: %s != %s', user.last_access.timestamp(), compare_to['last_access'])
|
||||
return False
|
||||
|
||||
if ensure_data(user, compare_to, ignore_keys=ignore_fields):
|
||||
# Compare groups
|
||||
if 'groups' in compare_to:
|
||||
groups = set(i.uuid for i in user.groups.all() if i.is_meta is False)
|
||||
compare_to_groups = set(compare_to['groups'])
|
||||
# Ensure groups are PART compare_to_groups
|
||||
if groups - compare_to_groups != set():
|
||||
logger.info('User groups do not match: %s != %s', groups, compare_to_groups)
|
||||
return False
|
||||
|
||||
# Compare mfa_data
|
||||
if 'mfa_data' in compare_to:
|
||||
if user.mfa_data != compare_to['mfa_data']:
|
||||
logger.info('User mfa_data do not match: %s != %s', user.mfa_data, compare_to['mfa_data'])
|
||||
return False
|
||||
|
||||
# Compare password
|
||||
if compare_password:
|
||||
if not cryptoManager().checkHash(compare_to['password'], user.password):
|
||||
logger.info('User password do not match: %s != %s', user.password, compare_to['password'])
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def assertGroupIs(
|
||||
group: models.Group, compare_to: typing.Mapping[str, typing.Any], compare_uuid=False
|
||||
) -> bool:
|
||||
ignore_fields = ['groups', 'users', 'is_meta', 'type', 'pools']
|
||||
|
||||
if not compare_uuid:
|
||||
ignore_fields.append('id')
|
||||
|
||||
if ensure_data(group, compare_to, ignore_keys=ignore_fields):
|
||||
|
||||
if group.is_meta:
|
||||
grps = set(i.uuid for i in group.groups.all())
|
||||
compare_to_groups = set(compare_to['groups'])
|
||||
if grps != compare_to_groups:
|
||||
logger.info('Group groups do not match: %s != %s', grps, compare_to_groups)
|
||||
return False
|
||||
|
||||
if 'type' in compare_to:
|
||||
if group.is_meta != (compare_to['type'] == 'meta'):
|
||||
logger.info('Group type do not match: %s != %s', group.is_meta, compare_to['type'])
|
||||
return False
|
||||
|
||||
if 'pools' in compare_to:
|
||||
pools = set(i.uuid for i in group.deployedServices.all())
|
||||
compare_to_pools = set(compare_to['pools'])
|
||||
if pools != compare_to_pools:
|
||||
logger.info('Group pools do not match: %s != %s', pools, compare_to_pools)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
return False
|
@ -145,15 +145,15 @@ class UDSClient(UDSClientMixin, Client):
|
||||
return typing.cast('UDSHttpResponse', super().put(*args, **kwargs))
|
||||
|
||||
def rest_put(self, method: str, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
# compose url
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return self.put(self.compose_rest_url(method), *args, **kwargs)
|
||||
|
||||
def delete(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
self.append_remote_addr(kwargs)
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return typing.cast('UDSHttpResponse', super().delete(*args, **kwargs))
|
||||
|
||||
def rest_delete(self, method: str, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
# compose url
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return self.delete(self.compose_rest_url(method), *args, **kwargs)
|
||||
|
||||
|
||||
@ -192,15 +192,15 @@ class UDSAsyncClient(UDSClientMixin, AsyncClient):
|
||||
return typing.cast('UDSHttpResponse', await super().post(*args, **kwargs))
|
||||
|
||||
async def rest_post(self, method: str, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
# compose url
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return await self.post(self.compose_rest_url(method), *args, **kwargs)
|
||||
|
||||
async def put(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
self.append_remote_addr(kwargs)
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return typing.cast('UDSHttpResponse', await super().put(*args, **kwargs))
|
||||
|
||||
async def rest_put(self, method: str, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
# compose url
|
||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||
return await self.put(self.compose_rest_url(method), *args, **kwargs)
|
||||
|
||||
async def delete(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||
|
@ -34,7 +34,7 @@ import typing
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
from django.forms.models import model_to_dict
|
||||
from django.db import IntegrityError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from uds.core.util.state import State
|
||||
@ -82,7 +82,9 @@ class Users(DetailHandler):
|
||||
|
||||
def getItems(self, parent: Authenticator, item: typing.Optional[str]):
|
||||
# processes item to change uuid key for id
|
||||
def uuid_to_id(iterable: typing.Iterable[typing.MutableMapping[str, typing.Any]]):
|
||||
def uuid_to_id(
|
||||
iterable: typing.Iterable[typing.MutableMapping[str, typing.Any]]
|
||||
):
|
||||
for v in iterable:
|
||||
v['id'] = v['uuid']
|
||||
del v['uuid']
|
||||
@ -94,18 +96,21 @@ class Users(DetailHandler):
|
||||
if item is None:
|
||||
values = list(
|
||||
uuid_to_id(
|
||||
(i for i in parent.users.all().values(
|
||||
'uuid',
|
||||
'name',
|
||||
'real_name',
|
||||
'comments',
|
||||
'state',
|
||||
'staff_member',
|
||||
'is_admin',
|
||||
'last_access',
|
||||
'parent',
|
||||
'mfa_data',
|
||||
))
|
||||
(
|
||||
i
|
||||
for i in parent.users.all().values(
|
||||
'uuid',
|
||||
'name',
|
||||
'real_name',
|
||||
'comments',
|
||||
'state',
|
||||
'staff_member',
|
||||
'is_admin',
|
||||
'last_access',
|
||||
'parent',
|
||||
'mfa_data',
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
for res in values:
|
||||
@ -206,7 +211,7 @@ class Users(DetailHandler):
|
||||
if 'password' in self._params:
|
||||
valid_fields.append('password')
|
||||
self._params['password'] = cryptoManager().hash(self._params['password'])
|
||||
|
||||
|
||||
if 'mfa_data' in self._params:
|
||||
valid_fields.append('mfa_data')
|
||||
self._params['mfa_data'] = self._params['mfa_data'].strip()
|
||||
@ -218,27 +223,29 @@ class Users(DetailHandler):
|
||||
|
||||
user = None
|
||||
try:
|
||||
auth = parent.getInstance()
|
||||
if item is None: # Create new
|
||||
auth.createUser(
|
||||
fields
|
||||
) # this throws an exception if there is an error (for example, this auth can't create users)
|
||||
user = parent.users.create(**fields)
|
||||
else:
|
||||
auth.modifyUser(fields) # Notifies authenticator
|
||||
user = parent.users.get(uuid=processUuid(item))
|
||||
user.__dict__.update(fields)
|
||||
|
||||
logger.debug('User parent: %s', user.parent)
|
||||
# If internal auth, threat it "special"
|
||||
if auth.isExternalSource is False and not user.parent:
|
||||
groups = self.readFieldsFromParams(['groups'])['groups']
|
||||
logger.debug('Groups: %s', groups)
|
||||
logger.debug('Got Groups %s', parent.groups.filter(uuid__in=groups))
|
||||
user.groups.set(parent.groups.filter(uuid__in=groups))
|
||||
|
||||
user.save()
|
||||
with transaction.atomic():
|
||||
auth = parent.getInstance()
|
||||
if item is None: # Create new
|
||||
auth.createUser(
|
||||
fields
|
||||
) # this throws an exception if there is an error (for example, this auth can't create users)
|
||||
user = parent.users.create(**fields)
|
||||
else:
|
||||
auth.modifyUser(fields) # Notifies authenticator
|
||||
user = parent.users.get(uuid=processUuid(item))
|
||||
user.__dict__.update(fields)
|
||||
user.save()
|
||||
|
||||
logger.debug('User parent: %s', user.parent)
|
||||
# If internal auth, and not a child user, save groups
|
||||
if not auth.isExternalSource and not user.parent:
|
||||
groups = self.readFieldsFromParams(['groups'])['groups']
|
||||
# Save but skip meta groups, they are not real groups, but just a way to group users based on rules
|
||||
user.groups.set(
|
||||
g
|
||||
for g in parent.groups.filter(uuid__in=groups)
|
||||
if g.is_meta is False
|
||||
)
|
||||
except User.DoesNotExist:
|
||||
raise self.invalidItemException()
|
||||
except IntegrityError: # Duplicate key probably
|
||||
@ -351,29 +358,18 @@ class Groups(DetailHandler):
|
||||
if multi:
|
||||
return res
|
||||
if not i:
|
||||
raise # Invalid item
|
||||
raise # Invalid item
|
||||
# Add pools field if 1 item only
|
||||
result = res[0]
|
||||
if i.is_meta:
|
||||
result[
|
||||
'pools'
|
||||
] = (
|
||||
[]
|
||||
) # Meta groups do not have "assigned "pools, they get it from groups interaction
|
||||
else:
|
||||
result['pools'] = [v.uuid for v in i.deployedServices.all()]
|
||||
result['pools'] = [v.uuid for v in getPoolsForGroups([i])]
|
||||
return result
|
||||
except Exception:
|
||||
logger.exception('REST groups')
|
||||
raise self.invalidItemException()
|
||||
|
||||
def getTitle(self, parent: str):
|
||||
def getTitle(self, parent: Authenticator) -> str:
|
||||
try:
|
||||
return _('Groups of {0}').format(
|
||||
Authenticator.objects.get(
|
||||
uuid=processUuid(self._kwargs['parent_id'])
|
||||
).name
|
||||
)
|
||||
return _('Groups of {0}').format(parent.name)
|
||||
except Exception:
|
||||
return _('Current groups')
|
||||
|
||||
@ -464,7 +460,8 @@ class Groups(DetailHandler):
|
||||
group.__dict__.update(toSave)
|
||||
|
||||
if is_meta:
|
||||
group.groups.set(parent.groups.filter(uuid__in=self._params['groups']))
|
||||
# Do not allow to add meta groups to meta groups
|
||||
group.groups.set(i for i in parent.groups.filter(uuid__in=self._params['groups']) if i.is_meta is False)
|
||||
|
||||
if pools:
|
||||
# Update pools
|
||||
|
@ -96,10 +96,10 @@ class ServicePool(UUIDModel, TaggingMixin): # type: ignore
|
||||
related_name='deployedServices',
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
transports = models.ManyToManyField(
|
||||
transports: 'models.ManyToManyField[Transport, ServicePool]' = models.ManyToManyField(
|
||||
Transport, related_name='deployedServices', db_table='uds__ds_trans'
|
||||
)
|
||||
assignedGroups = models.ManyToManyField(
|
||||
assignedGroups: 'models.ManyToManyField[Group, ServicePool]' = models.ManyToManyField(
|
||||
Group, related_name='deployedServices', db_table='uds__ds_grps'
|
||||
)
|
||||
state = models.CharField(
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -7,6 +7,7 @@ gettext("No entries found");
|
||||
gettext(", (%i more items)");
|
||||
gettext("Search");
|
||||
gettext("No entries found");
|
||||
gettext("Select");
|
||||
gettext("Main");
|
||||
gettext("Yes");
|
||||
gettext("No");
|
||||
@ -220,10 +221,10 @@ gettext("Successfully saved");
|
||||
gettext("dismiss");
|
||||
gettext("Delete image");
|
||||
gettext("Generate report");
|
||||
gettext("Generate report");
|
||||
gettext("Generating report...");
|
||||
gettext("Report finished");
|
||||
gettext("dismiss");
|
||||
gettext("Generate report");
|
||||
gettext("Delete tunnel token - USE WITH EXTREME CAUTION!!!");
|
||||
gettext("New Notifier");
|
||||
gettext("Edit Notifier");
|
||||
@ -249,6 +250,7 @@ gettext("Blocked");
|
||||
gettext("Service pools");
|
||||
gettext("Users");
|
||||
gettext("Groups");
|
||||
gettext("Match mode");
|
||||
gettext("Any");
|
||||
gettext("All");
|
||||
gettext("Group");
|
||||
@ -282,7 +284,6 @@ gettext("yes");
|
||||
gettext("no");
|
||||
// HTML
|
||||
gettext("Remove all");
|
||||
gettext("Add");
|
||||
gettext("Cancel");
|
||||
gettext("Ok");
|
||||
gettext("Discard & close");
|
||||
@ -474,6 +475,8 @@ gettext("Enabled");
|
||||
gettext("Disabled");
|
||||
gettext("Service Pools");
|
||||
gettext("Match mode");
|
||||
gettext("Any group");
|
||||
gettext("All groups");
|
||||
gettext("Selected Groups");
|
||||
gettext("Cancel");
|
||||
gettext("Ok");
|
||||
|
@ -99,7 +99,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
</uds-root>
|
||||
<script src="/uds/res/admin/runtime.js?stamp=1673892564" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1673892564" type="module"></script><script src="/uds/res/admin/main.js?stamp=1673892564" type="module"></script>
|
||||
<script src="/uds/res/admin/runtime.js?stamp=1675356496" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1675356496" type="module"></script><script src="/uds/res/admin/main.js?stamp=1675356496" type="module"></script>
|
||||
|
||||
|
||||
</body></html>
|
Loading…
x
Reference in New Issue
Block a user