Compare commits

...

27 Commits

Author SHA1 Message Date
ibuler
043d24a8f7 fix: bug 2021-01-14 10:38:11 +08:00
xinwen
1a011f34a1 fix: 系统审计员不应该能添加到组 2020-12-15 19:25:04 +08:00
xinwen
9e16f6c1a3 fix(orgs): 用户离开组织后授权的资产没主动刷新 2020-12-15 14:01:54 +08:00
xinwen
4a32016f14 fix: 工单申请资产审批时系统用户没有推荐 2020-12-15 13:07:25 +08:00
fit2bot
30c4723fc9 perf: 数据库应用database字段添加allow_null=True (#5197)
* perf: 数据库应用database字段修改为required

* perf: 数据库应用database字段添加allow_null=True

Co-authored-by: Bai <bugatti_it@163.com>
2020-12-09 13:44:36 +08:00
ibuler
03ff53546e fix: 修复centos的mirror问题 2020-12-07 10:50:22 +08:00
Bai
fedb650cf9 fix: Node ordering [parent_key, value]; 修复默认组织Default节点显示问题(存在key为0的Default节点) 2020-12-03 10:45:45 +08:00
fit2bot
a99cda7bc7 build(pip): 锁定pip版本 (#5153)
* build(pip): 锁定pip版本
2020-12-02 11:10:42 +08:00
xinwen
8eb46b6450 fix(assets): 推送动态系统用户未指定 username 取全部 usernames 2020-12-01 20:09:28 +08:00
xinwen
c389c5f5f6 fix(perms): 新建授权时动态用户可能推送不成功 2020-12-01 20:09:28 +08:00
xinwen
2aefecec04 perf(User): 用户列表在大规模数据情况下慢 2020-11-26 12:33:21 +08:00
xinwen
4ca5728f89 perf(Node): 修改节点资产数量自检程序执行时间 2020-11-25 17:30:43 +08:00
xinwen
18d005b860 fix(Node): Node 保存的时候,在信号里设置 parent_key 2020-11-25 16:35:33 +08:00
fit2bot
aef9bb2305 fix(assets): 动态系统用户和用户关系变化时没有推送到资产 (#5091)
Co-authored-by: xinwen <coderWen@126.com>
2020-11-25 16:12:11 +08:00
fit2bot
d04c65dbe4 fix(orgs): 兼容旧的组织用户关系接口 (#5087)
Co-authored-by: xinwen <coderWen@126.com>
2020-11-24 19:08:35 +08:00
xinwen
50bb04de8d refactor(perms): 在动态用户所绑定的授权规则中,如授权给用户组,当用户组增加成员后,动态系统用户下没有相应增加用户,因此也不会自动推送 (#5084) 2020-11-24 11:40:46 +08:00
xinwen
a72098632b fix(perms): 在动态用户所绑定的授权规则中,如授权给用户组,当用户组增加成员后,动态系统用户下没有相应增加用户,因此也不会自动推送 2020-11-24 10:26:32 +08:00
ibuler
c6f798d32e perf(build): 优化使用pip mirror 2020-11-22 18:18:29 +08:00
ibuler
47cac841e2 perf(build): 优化构建时用的mirror 2020-11-22 17:50:45 +08:00
xinwen
c3a32d27f2 perf(perms): 获取用户所有授权时转换成 list 2020-11-22 17:30:21 +08:00
ibuler
0104b9455f fix(perms): 修复我的资产页面问题 2020-11-22 16:54:52 +08:00
xinwen
fb4d11a5b1 fix(perms): 修复用户组授权树与资产问题 2020-11-22 15:03:43 +08:00
xinwen
0476959847 fix(perms): 当用户授权为空时,清空旧的授权树 2020-11-22 11:27:09 +08:00
xinwen
00867b698d perf(perms): 优化用户授权资产列表加载速度 2020-11-21 19:56:58 +08:00
fit2bot
b8f175e4fe perf(celery-task): 优化检查节点资产数量的 Celery 任务 (#5052)
Co-authored-by: xinwen <coderWen@126.com>
2020-11-20 20:23:09 +08:00
xinwen
a626ff5ad1 perf(assets): 限制搜索授权资产返回的条数 2020-11-20 15:25:10 +08:00
xinwen
b5fcc10925 fix(old-api): 调整旧的组织与用户关联接口 2020-11-19 15:22:34 +08:00
28 changed files with 319 additions and 172 deletions

View File

@@ -11,6 +11,8 @@ RUN cd utils && bash -ixeu build.sh
FROM registry.fit2cloud.com/public/python:v3 FROM registry.fit2cloud.com/public/python:v3
ARG PIP_MIRROR=https://pypi.douban.com/simple ARG PIP_MIRROR=https://pypi.douban.com/simple
ENV PIP_MIRROR=$PIP_MIRROR ENV PIP_MIRROR=$PIP_MIRROR
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
ARG MYSQL_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/ ARG MYSQL_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/
ENV MYSQL_MIRROR=$MYSQL_MIRROR ENV MYSQL_MIRROR=$MYSQL_MIRROR
@@ -18,13 +20,16 @@ WORKDIR /opt/jumpserver
COPY ./requirements ./requirements COPY ./requirements ./requirements
RUN useradd jumpserver RUN useradd jumpserver
RUN wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo \
&& sed -i 's@/centos/@/centos-vault/@g' /etc/yum.repos.d/CentOS-Base.repo \
&& sed -i 's@$releasever@6.10@g' /etc/yum.repos.d/CentOS-Base.repo
RUN yum -y install epel-release && \ RUN yum -y install epel-release && \
echo -e "[mysql]\nname=mysql\nbaseurl=${MYSQL_MIRROR}\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo echo -e "[mysql]\nname=mysql\nbaseurl=${MYSQL_MIRROR}\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo
RUN yum -y install $(cat requirements/rpm_requirements.txt) RUN yum -y install $(cat requirements/rpm_requirements.txt)
RUN pip install --upgrade pip setuptools==49.6.0 wheel -i ${PIP_MIRROR} && \ RUN pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel -i ${PIP_MIRROR} && \
pip config set global.index-url ${PIP_MIRROR} pip config set global.index-url ${PIP_MIRROR}
RUN pip install $(grep 'jms' requirements/requirements.txt) -i https://pypi.org/simple RUN pip install --no-cache-dir $(grep 'jms' requirements/requirements.txt) -i ${PIP_JMS_MIRROR}
RUN pip install -r requirements/requirements.txt RUN pip install --no-cache-dir -r requirements/requirements.txt
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config

View File

@@ -12,9 +12,8 @@ from .. import models
class DBAttrsSerializer(serializers.Serializer): class DBAttrsSerializer(serializers.Serializer):
host = serializers.CharField(max_length=128, label=_('Host')) host = serializers.CharField(max_length=128, label=_('Host'))
port = serializers.IntegerField(label=_('Port')) port = serializers.IntegerField(label=_('Port'))
database = serializers.CharField( # 添加allow_null=True兼容之前数据库中database字段为None的情况
max_length=128, required=False, allow_blank=True, allow_null=True, label=_('Database') database = serializers.CharField(max_length=128, required=True, allow_null=True, label=_('Database'))
)
class MySQLAttrsSerializer(DBAttrsSerializer): class MySQLAttrsSerializer(DBAttrsSerializer):

View File

@@ -173,7 +173,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
return [] return []
assets = self.instance.get_assets().only( assets = self.instance.get_assets().only(
"id", "hostname", "ip", "os", "id", "hostname", "ip", "os",
"org_id", "protocols", "org_id", "protocols", "is_active"
) )
return self.serialize_assets(assets, self.instance.key) return self.serialize_assets(assets, self.instance.key)
@@ -201,10 +201,8 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
def put(self, request, *args, **kwargs): def put(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()
nodes_id = request.data.get("nodes") nodes_id = request.data.get("nodes")
children = [get_object_or_none(Node, id=pk) for pk in nodes_id] children = Node.objects.filter(id__in=nodes_id)
for node in children: for node in children:
if not node:
continue
node.parent = instance node.parent = instance
return Response("OK") return Response("OK")

View File

@@ -103,7 +103,7 @@ class FamilyMixin:
if value is None: if value is None:
value = child_key value = child_key
child = self.__class__.objects.create( child = self.__class__.objects.create(
id=_id, key=child_key, value=value, parent_key=self.key, id=_id, key=child_key, value=value
) )
return child return child
@@ -354,7 +354,8 @@ class SomeNodesMixin:
def org_root(cls): def org_root(cls):
root = cls.objects.filter(parent_key='')\ root = cls.objects.filter(parent_key='')\
.filter(key__regex=r'^[0-9]+$')\ .filter(key__regex=r'^[0-9]+$')\
.exclude(key__startswith='-') .exclude(key__startswith='-')\
.order_by('key')
if root: if root:
return root[0] return root[0]
else: else:
@@ -411,7 +412,7 @@ class Node(OrgModelMixin, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
class Meta: class Meta:
verbose_name = _("Node") verbose_name = _("Node")
ordering = ['value'] ordering = ['parent_key', 'value']
def __str__(self): def __str__(self):
return self.full_value return self.full_value

View File

@@ -4,7 +4,7 @@ from operator import add, sub
from assets.utils import is_asset_exists_in_node from assets.utils import is_asset_exists_in_node
from django.db.models.signals import ( from django.db.models.signals import (
post_save, m2m_changed, pre_delete, post_delete post_save, m2m_changed, pre_delete, post_delete, pre_save
) )
from django.db.models import Q, F from django.db.models import Q, F
from django.dispatch import receiver from django.dispatch import receiver
@@ -37,6 +37,11 @@ def test_asset_conn_on_created(asset):
test_asset_connectivity_util.delay([asset]) test_asset_connectivity_util.delay([asset])
@receiver(pre_save, sender=Node)
def on_node_pre_save(sender, instance: Node, **kwargs):
instance.parent_key = instance.compute_parent_key()
@receiver(post_save, sender=Asset) @receiver(post_save, sender=Asset)
@on_transaction_commit @on_transaction_commit
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs): def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
@@ -73,6 +78,7 @@ def on_system_user_update(instance: SystemUser, created, **kwargs):
@receiver(m2m_changed, sender=SystemUser.assets.through) @receiver(m2m_changed, sender=SystemUser.assets.through)
@on_transaction_commit
def on_system_user_assets_change(instance, action, model, pk_set, **kwargs): def on_system_user_assets_change(instance, action, model, pk_set, **kwargs):
""" """
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中 当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
@@ -91,25 +97,29 @@ def on_system_user_assets_change(instance, action, model, pk_set, **kwargs):
@receiver(m2m_changed, sender=SystemUser.users.through) @receiver(m2m_changed, sender=SystemUser.users.through)
def on_system_user_users_change(sender, instance=None, action='', model=None, pk_set=None, **kwargs): @on_transaction_commit
def on_system_user_users_change(sender, instance: SystemUser, action, model, pk_set, reverse, **kwargs):
""" """
当系统用户和用户关系发生变化时,应该重新推送系统用户资产中 当系统用户和用户关系发生变化时,应该重新推送系统用户资产中
""" """
if action != POST_ADD: if action != POST_ADD:
return return
if reverse:
raise M2MReverseNotAllowed
if not instance.username_same_with_user: if not instance.username_same_with_user:
return return
logger.debug("System user users change signal recv: {}".format(instance)) logger.debug("System user users change signal recv: {}".format(instance))
queryset = model.objects.filter(pk__in=pk_set) usernames = model.objects.filter(pk__in=pk_set).values_list('username', flat=True)
if model == SystemUser:
system_users = queryset for username in usernames:
else: push_system_user_to_assets_manual.delay(instance, username)
system_users = [instance]
for s in system_users:
push_system_user_to_assets_manual.delay(s)
@receiver(m2m_changed, sender=SystemUser.nodes.through) @receiver(m2m_changed, sender=SystemUser.nodes.through)
@on_transaction_commit
def on_system_user_nodes_change(sender, instance=None, action=None, model=None, pk_set=None, **kwargs): def on_system_user_nodes_change(sender, instance=None, action=None, model=None, pk_set=None, **kwargs):
""" """
当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上 当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上

View File

@@ -1,14 +1,13 @@
from celery import shared_task from celery import shared_task
from ops.celery.decorator import register_as_period_task
from assets.utils import check_node_assets_amount from assets.utils import check_node_assets_amount
from common.utils import get_logger from common.utils import get_logger
from common.utils.timezone import now
logger = get_logger(__file__) logger = get_logger(__file__)
@shared_task() @register_as_period_task(crontab='0 2 * * *')
@shared_task(queue='celery_heavy_tasks')
def check_node_assets_amount_celery_task(): def check_node_assets_amount_celery_task():
logger.info(f'>>> {now()} begin check_node_assets_amount_celery_task ...')
check_node_assets_amount() check_node_assets_amount()
logger.info(f'>>> {now()} end check_node_assets_amount_celery_task ...')

View File

@@ -2,13 +2,13 @@
from itertools import groupby from itertools import groupby
from celery import shared_task from celery import shared_task
from common.db.utils import get_object_if_need, get_objects_if_need, get_objects from common.db.utils import get_object_if_need, get_objects
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db.models import Empty from django.db.models import Empty
from common.utils import encrypt_password, get_logger from common.utils import encrypt_password, get_logger
from assets.models import SystemUser, Asset from assets.models import SystemUser, Asset, AuthBook
from orgs.utils import org_aware_func from orgs.utils import org_aware_func, tmp_to_root_org
from . import const from . import const
from .utils import clean_ansible_task_hosts, group_asset_by_platform from .utils import clean_ansible_task_hosts, group_asset_by_platform
@@ -190,15 +190,12 @@ def get_push_system_user_tasks(system_user, platform="unixlike", username=None):
@org_aware_func("system_user") @org_aware_func("system_user")
def push_system_user_util(system_user, assets, task_name, username=None): def push_system_user_util(system_user, assets, task_name, username=None):
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
hosts = clean_ansible_task_hosts(assets, system_user=system_user) assets = clean_ansible_task_hosts(assets, system_user=system_user)
if not hosts: if not assets:
return {} return {}
platform_hosts_map = {} assets_sorted = sorted(assets, key=group_asset_by_platform)
hosts_sorted = sorted(hosts, key=group_asset_by_platform) platform_hosts = groupby(assets_sorted, key=group_asset_by_platform)
platform_hosts = groupby(hosts_sorted, key=group_asset_by_platform)
for i in platform_hosts:
platform_hosts_map[i[0]] = list(i[1])
def run_task(_tasks, _hosts): def run_task(_tasks, _hosts):
if not _tasks: if not _tasks:
@@ -209,27 +206,59 @@ def push_system_user_util(system_user, assets, task_name, username=None):
) )
task.run() task.run()
for platform, _hosts in platform_hosts_map.items(): if system_user.username_same_with_user:
if not _hosts: if username is None:
# 动态系统用户,但是没有指定 username
usernames = list(system_user.users.all().values_list('username', flat=True).distinct())
else:
usernames = [username]
else:
# 非动态系统用户指定 username 无效
assert username is None, 'Only Dynamic user can assign `username`'
usernames = [system_user.username]
for platform, _assets in platform_hosts:
_assets = list(_assets)
if not _assets:
continue continue
print(_("Start push system user for platform: [{}]").format(platform)) print(_("Start push system user for platform: [{}]").format(platform))
print(_("Hosts count: {}").format(len(_hosts))) print(_("Hosts count: {}").format(len(_assets)))
# 如果没有特殊密码设置,就不需要单独推送某台机器了 id_asset_map = {_asset.id: _asset for _asset in _assets}
if not system_user.has_special_auth(username=username): assets_id = id_asset_map.keys()
logger.debug("System user not has special auth") no_special_auth = []
tasks = get_push_system_user_tasks(system_user, platform, username=username) special_auth_set = set()
run_task(tasks, _hosts)
continue
for _host in _hosts: auth_books = AuthBook.objects.filter(username__in=usernames, asset_id__in=assets_id)
system_user.load_asset_special_auth(_host, username=username)
tasks = get_push_system_user_tasks(system_user, platform, username=username) for auth_book in auth_books:
run_task(tasks, [_host]) special_auth_set.add((auth_book.username, auth_book.asset_id))
for _username in usernames:
no_special_assets = []
for asset_id in assets_id:
if (_username, asset_id) not in special_auth_set:
no_special_assets.append(id_asset_map[asset_id])
if no_special_assets:
no_special_auth.append((_username, no_special_assets))
for _username, no_special_assets in no_special_auth:
tasks = get_push_system_user_tasks(system_user, platform, username=_username)
run_task(tasks, no_special_assets)
for auth_book in auth_books:
system_user._merge_auth(auth_book)
tasks = get_push_system_user_tasks(system_user, platform, username=auth_book.username)
asset = id_asset_map[auth_book.asset_id]
run_task(tasks, [asset])
@shared_task(queue="ansible") @shared_task(queue="ansible")
@tmp_to_root_org()
def push_system_user_to_assets_manual(system_user, username=None): def push_system_user_to_assets_manual(system_user, username=None):
"""
将系统用户推送到与它关联的所有资产上
"""
system_user = get_object_if_need(SystemUser, system_user) system_user = get_object_if_need(SystemUser, system_user)
assets = system_user.get_related_assets() assets = system_user.get_related_assets()
task_name = _("Push system users to assets: {}").format(system_user.name) task_name = _("Push system users to assets: {}").format(system_user.name)
@@ -237,7 +266,11 @@ def push_system_user_to_assets_manual(system_user, username=None):
@shared_task(queue="ansible") @shared_task(queue="ansible")
@tmp_to_root_org()
def push_system_user_a_asset_manual(system_user, asset, username=None): def push_system_user_a_asset_manual(system_user, asset, username=None):
"""
将系统用户推送到一个资产上
"""
if username is None: if username is None:
username = system_user.username username = system_user.username
task_name = _("Push system users to asset: {}({}) => {}").format( task_name = _("Push system users to asset: {}({}) => {}").format(
@@ -247,10 +280,15 @@ def push_system_user_a_asset_manual(system_user, asset, username=None):
@shared_task(queue="ansible") @shared_task(queue="ansible")
@tmp_to_root_org()
def push_system_user_to_assets(system_user_id, assets_id, username=None): def push_system_user_to_assets(system_user_id, assets_id, username=None):
"""
推送系统用户到指定的若干资产上
"""
system_user = SystemUser.objects.get(id=system_user_id) system_user = SystemUser.objects.get(id=system_user_id)
assets = get_objects(Asset, assets_id) assets = get_objects(Asset, assets_id)
task_name = _("Push system users to assets: {}").format(system_user.name) task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name, username=username) return push_system_user_util(system_user, assets, task_name, username=username)
# @shared_task # @shared_task

View File

@@ -1,5 +1,7 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
import time
from django.db.models import Q from django.db.models import Q
from common.utils import get_logger, dict_get_any, is_uuid, get_object_or_none from common.utils import get_logger, dict_get_any, is_uuid, get_object_or_none
@@ -12,15 +14,18 @@ logger = get_logger(__file__)
def check_node_assets_amount(): def check_node_assets_amount():
for node in Node.objects.all(): for node in Node.objects.all():
logger.info(f'Check node assets amount: {node}')
assets_amount = Asset.objects.filter( assets_amount = Asset.objects.filter(
Q(nodes__key__istartswith=f'{node.key}:') | Q(nodes=node) Q(nodes__key__istartswith=f'{node.key}:') | Q(nodes=node)
).distinct().count() ).distinct().count()
if node.assets_amount != assets_amount: if node.assets_amount != assets_amount:
print(f'>>> <Node:{node.key}> wrong assets amount ' logger.warn(f'Node wrong assets amount <Node:{node.key}> '
f'{node.assets_amount} right is {assets_amount}') f'{node.assets_amount} right is {assets_amount}')
node.assets_amount = assets_amount node.assets_amount = assets_amount
node.save() node.save()
# 防止自检程序给数据库的压力太大
time.sleep(0.1)
def is_asset_exists_in_node(asset_pk, node_key): def is_asset_exists_in_node(asset_pk, node_key):

View File

@@ -54,12 +54,3 @@ class UserConnectionTokenApi(RootOrgViewMixin, APIView):
return Response(value) return Response(value)
else: else:
return Response({'user': value['user']}) return Response({'user': value['user']})
def get_permissions(self):
if self.request.query_params.get('user-only', None):
self.permission_classes = (AllowAny,)
return super().get_permissions()

View File

@@ -29,16 +29,3 @@ configs["CELERY_ROUTES"] = {
app.namespace = 'CELERY' app.namespace = 'CELERY'
app.conf.update(configs) app.conf.update(configs)
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS]) app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
app.conf.beat_schedule = {
'check-asset-permission-expired': {
'task': 'perms.tasks.check_asset_permission_expired',
'schedule': settings.PERM_EXPIRED_CHECK_PERIODIC,
'args': ()
},
'check-node-assets-amount': {
'task': 'assets.tasks.nodes_amount.check_node_assets_amount_celery_task',
'schedule': crontab(minute=0, hour=0),
'args': ()
},
}

View File

@@ -15,7 +15,11 @@ class CeleryLogWebsocket(JsonWebsocketConsumer):
disconnected = False disconnected = False
def connect(self): def connect(self):
self.accept() user = self.scope["user"]
if user.is_authenticated and user.is_org_admin:
self.accept()
else:
self.close()
def receive(self, text_data=None, bytes_data=None, **kwargs): def receive(self, text_data=None, bytes_data=None, **kwargs):
data = json.loads(text_data) data = json.loads(text_data)

View File

@@ -92,6 +92,7 @@ class OrgMemberAdminRelationBulkViewSet(JMSBulkRelationModelViewSet):
serializer_class = OrgMemberAdminSerializer serializer_class = OrgMemberAdminSerializer
filterset_class = OrgMemberRelationFilterSet filterset_class = OrgMemberRelationFilterSet
search_fields = ('user__name', 'user__username', 'org__name') search_fields = ('user__name', 'user__username', 'org__name')
lookup_field = 'user_id'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
@@ -116,6 +117,7 @@ class OrgMemberUserRelationBulkViewSet(JMSBulkRelationModelViewSet):
serializer_class = OrgMemberUserSerializer serializer_class = OrgMemberUserSerializer
filterset_class = OrgMemberRelationFilterSet filterset_class = OrgMemberRelationFilterSet
search_fields = ('user__name', 'user__username', 'org__name') search_fields = ('user__name', 'user__username', 'org__name')
lookup_field = 'user_id'
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .models import Organization
from .utils import get_org_from_request, set_current_org from .utils import get_org_from_request, set_current_org

View File

@@ -74,26 +74,29 @@ class OrgMemberSerializer(BulkModelSerializer):
).distinct() ).distinct()
class OrgMemberAdminSerializer(BulkModelSerializer): class OrgMemberOldBaseSerializer(BulkModelSerializer):
organization = serializers.PrimaryKeyRelatedField(
label=_('Organization'), queryset=Organization.objects.all(), required=True, source='org'
)
def to_internal_value(self, data):
view = self.context['view']
org_id = view.kwargs.get('org_id')
if org_id:
data['organization'] = org_id
return super().to_internal_value(data)
class Meta:
model = OrganizationMember
fields = ('id', 'organization', 'user', 'role')
class OrgMemberAdminSerializer(OrgMemberOldBaseSerializer):
role = serializers.HiddenField(default=ROLE.ADMIN) role = serializers.HiddenField(default=ROLE.ADMIN)
organization = serializers.PrimaryKeyRelatedField(
label=_('Organization'), queryset=Organization.objects.all(), required=True, source='org'
)
class Meta:
model = OrganizationMember
fields = ('id', 'organization', 'user', 'role')
class OrgMemberUserSerializer(BulkModelSerializer): class OrgMemberUserSerializer(OrgMemberOldBaseSerializer):
role = serializers.HiddenField(default=ROLE.USER) role = serializers.HiddenField(default=ROLE.USER)
organization = serializers.PrimaryKeyRelatedField(
label=_('Organization'), queryset=Organization.objects.all(), required=True, source='org'
)
class Meta:
model = OrganizationMember
fields = ('id', 'organization', 'user', 'role')
class OrgRetrieveSerializer(OrgReadSerializer): class OrgRetrieveSerializer(OrgReadSerializer):

View File

@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from collections import defaultdict
from functools import partial
from django.db.models.signals import m2m_changed from django.db.models.signals import m2m_changed
from django.db.models.signals import post_save from django.db.models.signals import post_save
@@ -7,10 +9,10 @@ from django.dispatch import receiver
from orgs.utils import tmp_to_org from orgs.utils import tmp_to_org
from .models import Organization, OrganizationMember from .models import Organization, OrganizationMember
from .hands import set_current_org, current_org, Node, get_current_org from .hands import set_current_org, Node, get_current_org
from perms.models import (AssetPermission, DatabaseAppPermission, from perms.models import (AssetPermission, ApplicationPermission)
K8sAppPermission, RemoteAppPermission) from users.models import UserGroup, User
from users.models import UserGroup from common.const.signals import PRE_REMOVE, POST_REMOVE
@receiver(post_save, sender=Organization) @receiver(post_save, sender=Organization)
@@ -34,11 +36,44 @@ def _remove_users(model, users, org):
users = (users, ) users = (users, )
m2m_model = model.users.through m2m_model = model.users.through
if model.users.reverse: reverse = model.users.reverse
if reverse:
m2m_field_name = model.users.field.m2m_reverse_field_name() m2m_field_name = model.users.field.m2m_reverse_field_name()
else: else:
m2m_field_name = model.users.field.m2m_field_name() m2m_field_name = model.users.field.m2m_field_name()
m2m_model.objects.filter(**{'user__in': users, f'{m2m_field_name}__org_id': org.id}).delete() relations = m2m_model.objects.filter(**{
'user__in': users,
f'{m2m_field_name}__org_id': org.id
})
object_id_users_id_map = defaultdict(set)
m2m_field_attr_name = f'{m2m_field_name}_id'
for relation in relations:
object_id = getattr(relation, m2m_field_attr_name)
object_id_users_id_map[object_id].add(relation.user_id)
objects = model.objects.filter(id__in=object_id_users_id_map.keys())
send_m2m_change_signal = partial(
m2m_changed.send,
sender=m2m_model, reverse=reverse, model=User, using=model.objects.db
)
for obj in objects:
send_m2m_change_signal(
instance=obj,
pk_set=object_id_users_id_map[obj.id],
action=PRE_REMOVE
)
relations.delete()
for obj in objects:
send_m2m_change_signal(
instance=obj,
pk_set=object_id_users_id_map[obj.id],
action=POST_REMOVE
)
def _clear_users_from_org(org, users): def _clear_users_from_org(org, users):
@@ -48,8 +83,7 @@ def _clear_users_from_org(org, users):
if not users: if not users:
return return
models = (AssetPermission, DatabaseAppPermission, models = (AssetPermission, ApplicationPermission, UserGroup)
RemoteAppPermission, K8sAppPermission, UserGroup)
for m in models: for m in models:
_remove_users(m, users, org) _remove_users(m, users, org)

View File

@@ -32,9 +32,6 @@ class UserGroupMixin:
class UserGroupGrantedAssetsApi(ListAPIView): class UserGroupGrantedAssetsApi(ListAPIView):
"""
获取用户组直接授权的资产
"""
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.AssetGrantedSerializer serializer_class = serializers.AssetGrantedSerializer
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
@@ -44,11 +41,27 @@ class UserGroupGrantedAssetsApi(ListAPIView):
def get_queryset(self): def get_queryset(self):
user_group_id = self.kwargs.get('pk', '') user_group_id = self.kwargs.get('pk', '')
return Asset.objects.filter( asset_perms_id = list(AssetPermission.objects.valid().filter(
Q(granted_by_permissions__user_groups__id=user_group_id) user_groups__id=user_group_id
).distinct().values_list('id', flat=True))
granted_node_keys = Node.objects.filter(
granted_by_permissions__id__in=asset_perms_id,
).distinct().values_list('key', flat=True)
granted_q = Q()
for _key in granted_node_keys:
granted_q |= Q(nodes__key__startswith=f'{_key}:')
granted_q |= Q(nodes__key=_key)
granted_q |= Q(granted_by_permissions__id__in=asset_perms_id)
assets = Asset.objects.filter(
granted_q
).distinct().only( ).distinct().only(
*self.only_fields *self.only_fields
) )
return assets
class UserGroupGrantedNodeAssetsApi(ListAPIView): class UserGroupGrantedNodeAssetsApi(ListAPIView):
@@ -66,7 +79,7 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
granted = AssetPermission.objects.filter( granted = AssetPermission.objects.filter(
user_groups__id=user_group_id, user_groups__id=user_group_id,
nodes__id=node_id nodes__id=node_id
).exists() ).valid().exists()
if granted: if granted:
assets = Asset.objects.filter( assets = Asset.objects.filter(
Q(nodes__key__startswith=f'{node.key}:') | Q(nodes__key__startswith=f'{node.key}:') |
@@ -74,8 +87,12 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
) )
return assets return assets
else: else:
asset_perms_id = list(AssetPermission.objects.valid().filter(
user_groups__id=user_group_id
).distinct().values_list('id', flat=True))
granted_node_keys = Node.objects.filter( granted_node_keys = Node.objects.filter(
granted_by_permissions__user_groups__id=user_group_id, granted_by_permissions__id__in=asset_perms_id,
key__startswith=f'{node.key}:' key__startswith=f'{node.key}:'
).distinct().values_list('key', flat=True) ).distinct().values_list('key', flat=True)
@@ -85,7 +102,7 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
granted_node_q |= Q(nodes__key=_key) granted_node_q |= Q(nodes__key=_key)
granted_asset_q = ( granted_asset_q = (
Q(granted_by_permissions__user_groups__id=user_group_id) & Q(granted_by_permissions__id__in=asset_perms_id) &
( (
Q(nodes__key__startswith=f'{node.key}:') | Q(nodes__key__startswith=f'{node.key}:') |
Q(nodes__key=node.key) Q(nodes__key=node.key)
@@ -129,12 +146,16 @@ class UserGroupGrantedNodeChildrenAsTreeApi(SerializeToTreeNodeMixin, ListAPIVie
group_id = self.kwargs.get('pk') group_id = self.kwargs.get('pk')
node_key = self.request.query_params.get('key', None) node_key = self.request.query_params.get('key', None)
asset_perms_id = list(AssetPermission.objects.valid().filter(
user_groups__id=group_id
).distinct().values_list('id', flat=True))
granted_keys = Node.objects.filter( granted_keys = Node.objects.filter(
granted_by_permissions__user_groups__id=group_id granted_by_permissions__id__in=asset_perms_id
).values_list('key', flat=True) ).values_list('key', flat=True)
asset_granted_keys = Node.objects.filter( asset_granted_keys = Node.objects.filter(
assets__granted_by_permissions__user_groups__id=group_id assets__granted_by_permissions__id__in=asset_perms_id
).values_list('key', flat=True) ).values_list('key', flat=True)
if node_key is None: if node_key is None:

View File

@@ -3,6 +3,7 @@
from perms.api.asset.user_permission.mixin import UserNodeGrantStatusDispatchMixin from perms.api.asset.user_permission.mixin import UserNodeGrantStatusDispatchMixin
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.request import Request
from django.conf import settings from django.conf import settings
from assets.api.mixin import SerializeToTreeNodeMixin from assets.api.mixin import SerializeToTreeNodeMixin
@@ -55,8 +56,12 @@ class AssetsAsTreeMixin(SerializeToTreeNodeMixin):
""" """
将 资产 序列化成树的结构返回 将 资产 序列化成树的结构返回
""" """
def list(self, request, *args, **kwargs): def list(self, request: Request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
if request.query_params.get('search'):
# 如果用户搜索的条件不精准,会导致返回大量的无意义数据。
# 这里限制一下返回数据的最大条数
queryset = queryset[:999]
data = self.serialize_assets(queryset, None) data = self.serialize_assets(queryset, None)
return Response(data=data) return Response(data=data)

View File

@@ -6,7 +6,7 @@ from django.dispatch import receiver
from perms.tasks import create_rebuild_user_tree_task, \ from perms.tasks import create_rebuild_user_tree_task, \
create_rebuild_user_tree_task_by_related_nodes_or_assets create_rebuild_user_tree_task_by_related_nodes_or_assets
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset from assets.models import Asset, SystemUser
from common.utils import get_logger from common.utils import get_logger
from common.exceptions import M2MReverseNotAllowed from common.exceptions import M2MReverseNotAllowed
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
@@ -16,6 +16,42 @@ from .models import AssetPermission, RemoteAppPermission
logger = get_logger(__file__) logger = get_logger(__file__)
def handle_rebuild_user_tree(instance, action, reverse, pk_set, **kwargs):
if action.startswith('post'):
if reverse:
create_rebuild_user_tree_task(pk_set)
else:
create_rebuild_user_tree_task([instance.id])
def handle_bind_groups_systemuser(instance, action, reverse, pk_set, **kwargs):
"""
UserGroup 增加 User 时,增加的 User 需要与 UserGroup 关联的动态系统用户相关联
"""
user: User
if action != POST_ADD:
return
if not reverse:
# 一个用户添加了多个用户组
users_id = [instance.id]
system_users = SystemUser.objects.filter(groups__id__in=pk_set).distinct()
else:
# 一个用户组添加了多个用户
users_id = pk_set
system_users = SystemUser.objects.filter(groups__id=instance.pk).distinct()
for system_user in system_users:
system_user.users.add(*users_id)
@receiver(m2m_changed, sender=User.groups.through)
def on_user_groups_change(**kwargs):
handle_rebuild_user_tree(**kwargs)
handle_bind_groups_systemuser(**kwargs)
@receiver([pre_save], sender=AssetPermission) @receiver([pre_save], sender=AssetPermission)
def on_asset_perm_deactive(instance: AssetPermission, **kwargs): def on_asset_perm_deactive(instance: AssetPermission, **kwargs):
try: try:

View File

@@ -5,10 +5,12 @@ from datetime import timedelta
from django.db import transaction from django.db import transaction
from django.db.models import Q from django.db.models import Q
from django.db.transaction import atomic from django.db.transaction import atomic
from django.conf import settings
from celery import shared_task from celery import shared_task
from common.utils import get_logger from common.utils import get_logger
from common.utils.timezone import now, dt_formater, dt_parser from common.utils.timezone import now, dt_formater, dt_parser
from users.models import User from users.models import User
from ops.celery.decorator import register_as_period_task
from assets.models import Node from assets.models import Node
from perms.models import RebuildUserTreeTask, AssetPermission from perms.models import RebuildUserTreeTask, AssetPermission
from perms.utils.asset.user_permission import rebuild_user_mapping_nodes_if_need_with_lock, lock from perms.utils.asset.user_permission import rebuild_user_mapping_nodes_if_need_with_lock, lock
@@ -33,7 +35,8 @@ def dispatch_mapping_node_tasks():
rebuild_user_mapping_nodes_celery_task.delay(id) rebuild_user_mapping_nodes_celery_task.delay(id)
@shared_task(queue='check_asset_perm_expired') @register_as_period_task(interval=settings.PERM_EXPIRED_CHECK_PERIODIC)
@shared_task(queue='celery_check_asset_perm_expired')
@atomic() @atomic()
def check_asset_permission_expired(): def check_asset_permission_expired():
""" """

View File

@@ -21,7 +21,7 @@ user_permission_urlpatterns = [
# --------------------------------------------------------- # ---------------------------------------------------------
# 以 serializer 格式返回 # 以 serializer 格式返回
path('<uuid:pk>/assets/', api.UserAllGrantedAssetsApi.as_view(), name='user-assets'), path('<uuid:pk>/assets/', api.UserAllGrantedAssetsApi.as_view(), name='user-assets'),
path('assets/', api.MyAllAssetsAsTreeApi.as_view(), name='my-assets'), path('assets/', api.MyAllGrantedAssetsApi.as_view(), name='my-assets'),
# Tree Node 的数据格式返回 # Tree Node 的数据格式返回
path('<uuid:pk>/assets/tree/', api.UserDirectGrantedAssetsAsTreeForAdminApi.as_view(), name='user-assets-as-tree'), path('<uuid:pk>/assets/tree/', api.UserDirectGrantedAssetsAsTreeForAdminApi.as_view(), name='user-assets-as-tree'),

View File

@@ -34,27 +34,6 @@ TMP_ASSET_GRANTED_FIELD = '_asset_granted'
TMP_GRANTED_ASSETS_AMOUNT_FIELD = '_granted_assets_amount' TMP_GRANTED_ASSETS_AMOUNT_FIELD = '_granted_assets_amount'
# 使用场景
# Asset.objects.filter(get_user_resources_q_granted_by_permissions(user))
def get_user_resources_q_granted_by_permissions(user: User):
"""
获取用户关联的 asset permission 或者 用户组关联的 asset permission 获取规则,
前提 AssetPermission 对象中的 related_name 为 granted_by_permissions
:param user:
:return:
"""
_now = now()
return reduce(and_, (
Q(granted_by_permissions__date_start__lt=_now),
Q(granted_by_permissions__date_expired__gt=_now),
Q(granted_by_permissions__is_active=True),
(
Q(granted_by_permissions__users=user) |
Q(granted_by_permissions__user_groups__users=user)
)
))
# 使用场景 # 使用场景
# `Node.objects.annotate(**node_annotate_mapping_node)` # `Node.objects.annotate(**node_annotate_mapping_node)`
node_annotate_mapping_node = { node_annotate_mapping_node = {
@@ -215,7 +194,7 @@ def compute_tmp_mapping_node_from_perm(user: User, asset_perms_id=None):
return [*leaf_nodes, *ancestors] return [*leaf_nodes, *ancestors]
def create_mapping_nodes(user, nodes, clear=True): def create_mapping_nodes(user, nodes):
to_create = [] to_create = []
for node in nodes: for node in nodes:
_granted = getattr(node, TMP_GRANTED_FIELD, False) _granted = getattr(node, TMP_GRANTED_FIELD, False)
@@ -231,8 +210,6 @@ def create_mapping_nodes(user, nodes, clear=True):
assets_amount=_granted_assets_amount, assets_amount=_granted_assets_amount,
)) ))
if clear:
UserGrantedMappingNode.objects.filter(user=user).delete()
UserGrantedMappingNode.objects.bulk_create(to_create) UserGrantedMappingNode.objects.bulk_create(to_create)
@@ -254,6 +231,9 @@ def set_node_granted_assets_amount(user, node, asset_perms_id=None):
@tmp_to_root_org() @tmp_to_root_org()
def rebuild_user_mapping_nodes(user): def rebuild_user_mapping_nodes(user):
logger.info(f'>>> {dt_formater(now())} start rebuild {user} mapping nodes') logger.info(f'>>> {dt_formater(now())} start rebuild {user} mapping nodes')
# 先删除旧的授权树🌲
UserGrantedMappingNode.objects.filter(user=user).delete()
asset_perms_id = get_user_all_assetpermissions_id(user) asset_perms_id = get_user_all_assetpermissions_id(user)
if not asset_perms_id: if not asset_perms_id:
# 没有授权直接返回 # 没有授权直接返回
@@ -384,7 +364,8 @@ def get_node_all_granted_assets(user: User, key):
if only_asset_granted_nodes_qs: if only_asset_granted_nodes_qs:
only_asset_granted_nodes_q = reduce(or_, only_asset_granted_nodes_qs) only_asset_granted_nodes_q = reduce(or_, only_asset_granted_nodes_qs)
only_asset_granted_nodes_q &= get_user_resources_q_granted_by_permissions(user) asset_perms_id = get_user_all_assetpermissions_id(user)
only_asset_granted_nodes_q &= Q(granted_by_permissions__id__in=list(asset_perms_id))
q.append(only_asset_granted_nodes_q) q.append(only_asset_granted_nodes_q)
if q: if q:
@@ -484,6 +465,9 @@ def get_user_all_assetpermissions_id(user: User):
asset_perms_id = AssetPermission.objects.valid().filter( asset_perms_id = AssetPermission.objects.valid().filter(
Q(users=user) | Q(user_groups__users=user) Q(users=user) | Q(user_groups__users=user)
).distinct().values_list('id', flat=True) ).distinct().values_list('id', flat=True)
# !!! 这个很重要,必须转换成 list避免 Django 生成嵌套子查询
asset_perms_id = list(asset_perms_id)
return asset_perms_id return asset_perms_id

View File

@@ -1,5 +1,3 @@
from itertools import chain
from rest_framework import serializers from rest_framework import serializers
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@@ -9,7 +7,7 @@ from django.db.models import Q
from common.utils.timezone import dt_parser, dt_formater from common.utils.timezone import dt_parser, dt_formater
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
from orgs.models import Organization, ROLE as ORG_ROLE from orgs.models import Organization, ROLE as ORG_ROLE
from assets.models.asset import Asset from assets.models import Asset, SystemUser
from users.models.user import User from users.models.user import User
from perms.serializers import ActionsField from perms.serializers import ActionsField
from perms.models import Action from perms.models import Action
@@ -130,12 +128,23 @@ class RequestAssetPermTicketSerializer(serializers.ModelSerializer):
if hostname: if hostname:
q |= Q(hostname__icontains=hostname) q |= Q(hostname__icontains=hostname)
data['confirmed_assets'] = list( recomand_assets_id = Asset.objects.filter(q)[:limit].values_list('id', flat=True)
map(lambda x: str(x), chain(*Asset.objects.filter(q)[0: limit].values_list('id')))) data['confirmed_assets'] = [str(i) for i in recomand_assets_id]
def _recommend_system_users(self, data, instance):
confirmed_system_users = data.get('confirmed_system_users')
if not confirmed_system_users and self._is_assignee(instance):
system_user = data.get('system_user')
recomand_system_users_id = SystemUser.objects.filter(
name__icontains=system_user
)[:3].values_list('id', flat=True)
data['confirmed_system_users'] = [str(i) for i in recomand_system_users_id]
def to_representation(self, instance): def to_representation(self, instance):
data = super().to_representation(instance) data = super().to_representation(instance)
self._recommend_assets(data, instance) self._recommend_assets(data, instance)
self._recommend_system_users(data, instance)
return data return data
def _create_body(self, validated_data): def _create_body(self, validated_data):

View File

@@ -28,3 +28,12 @@ class UserUserGroupRelationViewSet(JMSBulkRelationModelViewSet):
return False return False
else: else:
return True return True
def perform_create(self, serializer):
validated_data = []
for item in serializer.validated_data:
if item['user'].role == User.ROLE.AUDITOR:
continue
validated_data.append(item)
serializer._validated_data = validated_data
return super().perform_create(serializer)

View File

@@ -6,8 +6,8 @@ from rest_framework.decorators import action
from rest_framework import generics from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from django.db.models import Prefetch
from common.db.aggregates import GroupConcat
from common.permissions import ( from common.permissions import (
IsOrgAdmin, IsOrgAdminOrAppUser, IsOrgAdmin, IsOrgAdminOrAppUser,
CanUpdateDeleteUser, IsSuperUser CanUpdateDeleteUser, IsSuperUser
@@ -44,9 +44,18 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
extra_filter_backends = [OrgRoleUserFilterBackend] extra_filter_backends = [OrgRoleUserFilterBackend]
def get_queryset(self): def get_queryset(self):
return super().get_queryset().annotate( queryset = super().get_queryset().prefetch_related(
gc_m2m_org_members__role=GroupConcat('m2m_org_members__role'), 'groups'
).prefetch_related('groups') )
if current_org.is_real():
# 为在列表中计算用户在真实组织里的角色
queryset = queryset.prefetch_related(
Prefetch(
'm2m_org_members',
queryset=OrganizationMember.objects.filter(org__id=current_org.id)
)
)
return queryset
def send_created_signal(self, users): def send_created_signal(self, users):
if not isinstance(users, list): if not isinstance(users, list):

View File

@@ -170,22 +170,18 @@ class RoleMixin:
from orgs.models import ROLE as ORG_ROLE from orgs.models import ROLE as ORG_ROLE
if not current_org.is_real(): if not current_org.is_real():
# 不是真实的组织,取 User 本身的角色
if self.is_superuser: if self.is_superuser:
return [ORG_ROLE.ADMIN] return [ORG_ROLE.ADMIN]
else: else:
return [ORG_ROLE.USER] return [ORG_ROLE.USER]
if hasattr(self, 'gc_m2m_org_members__role'): # 是真实组织,取 OrganizationMember 中的角色
names = self.gc_m2m_org_members__role roles = [
if isinstance(names, str): org_member.role
roles = set(self.gc_m2m_org_members__role.split(',')) for org_member in self.m2m_org_members.all()
else: if org_member.org_id == current_org.id
roles = set() ]
else:
roles = set(self.m2m_org_members.filter(
org_id=current_org.id
).values_list('role', flat=True))
roles = list(roles)
roles.sort() roles.sort()
return roles return roles

View File

@@ -2,14 +2,12 @@
# #
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import m2m_changed
from django_auth_ldap.backend import populate_user from django_auth_ldap.backend import populate_user
from django.conf import settings from django.conf import settings
from django_cas_ng.signals import cas_user_authenticated from django_cas_ng.signals import cas_user_authenticated
from jms_oidc_rp.signals import openid_create_or_update_user from jms_oidc_rp.signals import openid_create_or_update_user
from perms.tasks import create_rebuild_user_tree_task
from common.utils import get_logger from common.utils import get_logger
from .signals import post_user_create from .signals import post_user_create
from .models import User from .models import User
@@ -27,15 +25,6 @@ def on_user_create(sender, user=None, **kwargs):
send_user_created_mail(user) send_user_created_mail(user)
@receiver(m2m_changed, sender=User.groups.through)
def on_user_groups_change(instance, action, reverse, pk_set, **kwargs):
if action.startswith('post'):
if reverse:
create_rebuild_user_tree_task(pk_set)
else:
create_rebuild_user_tree_task([instance.id])
@receiver(cas_user_authenticated) @receiver(cas_user_authenticated)
def on_cas_user_authenticated(sender, user, created, **kwargs): def on_cas_user_authenticated(sender, user, created, **kwargs):
if created: if created:

15
jms
View File

@@ -156,7 +156,10 @@ def is_running(s, unlink=True):
def parse_service(s): def parse_service(s):
web_services = ['gunicorn', 'flower', 'daphne'] web_services = ['gunicorn', 'flower', 'daphne']
celery_services = ["celery_ansible", "celery_default", "celery_node_tree", "check_asset_perm_expired"] celery_services = [
"celery_ansible", "celery_default", "celery_node_tree",
"celery_check_asset_perm_expired", "celery_heavy_tasks"
]
task_services = celery_services + ['beat'] task_services = celery_services + ['beat']
all_services = web_services + task_services all_services = web_services + task_services
if s == 'all': if s == 'all':
@@ -225,9 +228,14 @@ def get_start_celery_node_tree_kwargs():
return get_start_worker_kwargs('node_tree', 2) return get_start_worker_kwargs('node_tree', 2)
def get_start_celery_heavy_tasks_kwargs():
print("\n- Start Celery as Distributed Task Queue: HeavyTasks")
return get_start_worker_kwargs('celery_heavy_tasks', 1)
def get_start_celery_check_asset_perm_expired_kwargs(): def get_start_celery_check_asset_perm_expired_kwargs():
print("\n- Start Celery as Distributed Task Queue: CheckAseetPermissionExpired") print("\n- Start Celery as Distributed Task Queue: CheckAseetPermissionExpired")
return get_start_worker_kwargs('check_asset_perm_expired', 1) return get_start_worker_kwargs('celery_check_asset_perm_expired', 1)
def get_start_worker_kwargs(queue, num): def get_start_worker_kwargs(queue, num):
@@ -366,7 +374,8 @@ def start_service(s):
"celery_ansible": get_start_celery_ansible_kwargs, "celery_ansible": get_start_celery_ansible_kwargs,
"celery_default": get_start_celery_default_kwargs, "celery_default": get_start_celery_default_kwargs,
"celery_node_tree": get_start_celery_node_tree_kwargs, "celery_node_tree": get_start_celery_node_tree_kwargs,
"check_asset_perm_expired": get_start_celery_check_asset_perm_expired_kwargs, "celery_heavy_tasks": get_start_celery_heavy_tasks_kwargs,
"celery_check_asset_perm_expired": get_start_celery_check_asset_perm_expired_kwargs,
"beat": get_start_beat_kwargs, "beat": get_start_beat_kwargs,
"flower": get_start_flower_kwargs, "flower": get_start_flower_kwargs,
"daphne": get_start_daphne_kwargs, "daphne": get_start_daphne_kwargs,

View File

@@ -53,6 +53,8 @@ Pillow==7.1.0
pyasn1==0.4.8 pyasn1==0.4.8
pycparser==2.19 pycparser==2.19
pycrypto==2.6.1 pycrypto==2.6.1
pycryptodome==3.9.9
pycryptodomex==3.9.9
pyotp==2.2.6 pyotp==2.2.6
PyNaCl==1.2.1 PyNaCl==1.2.1
python-dateutil==2.6.1 python-dateutil==2.6.1