perf: 修改系统用户

pull/8566/head
ibuler 2022-07-28 18:50:58 +08:00
parent 43d3791ddc
commit fb0fb71ea3
34 changed files with 244 additions and 1063 deletions

View File

@ -1,9 +1,7 @@
from .mixin import *
from .admin_user import *
from .asset import *
from .label import *
from .system_user import *
from .system_user_relation import *
from .accounts import *
from .node import *
from .domain import *

View File

@ -1,30 +0,0 @@
from django.db.models import Count
from orgs.mixins.api import OrgBulkModelViewSet
from common.utils import get_logger
from ..models import SystemUser
from .. import serializers
from rbac.permissions import RBACPermission
logger = get_logger(__file__)
__all__ = ['AdminUserViewSet']
# 兼容一下老的 api
class AdminUserViewSet(OrgBulkModelViewSet):
"""
Admin user api set, for add,delete,update,list,retrieve resource
"""
model = SystemUser
filterset_fields = ("name", "username")
search_fields = filterset_fields
serializer_class = serializers.AdminUserSerializer
permission_classes = (RBACPermission,)
ordering_fields = ('name',)
ordering = ('name', )
def get_queryset(self):
queryset = super().get_queryset().filter(type=SystemUser.Type.admin)
queryset = queryset.annotate(assets_amount=Count('assets'))
return queryset

View File

@ -42,7 +42,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
filterset_fields = {
'hostname': ['exact'],
'ip': ['exact'],
'system_users__id': ['exact'],
'platform__base': ['exact'],
'is_active': ['exact'],
'protocols': ['exact', 'icontains']

View File

@ -5,23 +5,17 @@ from rest_framework.decorators import action
from rest_framework.viewsets import GenericViewSet
from common.utils import get_logger, get_object_or_none
from common.permissions import IsValidUser
from common.mixins.api import SuggestionMixin
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from orgs.utils import tmp_to_root_org
from ..models import SystemUser, CommandFilterRule, Account
from .. import serializers
from ..tasks import (
push_system_user_to_assets_manual, test_system_user_connectivity_manual,
push_system_user_to_assets
)
logger = get_logger(__file__)
__all__ = [
'SystemUserViewSet', 'SystemUserAuthInfoApi', 'SystemUserAssetAuthInfoApi',
'SystemUserCommandFilterRuleListApi', 'SystemUserTaskApi', 'SystemUserAssetsListView',
'SystemUserTempAuthInfoApi', 'SystemUserAppAuthInfoApi', 'SystemUserAssetAccountApi',
'SystemUserViewSet', 'SystemUserAuthInfoApi',
'SystemUserCommandFilterRuleListApi',
'SystemUserAssetAccountApi',
'SystemUserAssetAccountSecretApi',
]
@ -35,7 +29,6 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
'name': ['exact'],
'username': ['exact'],
'protocol': ['exact', 'in'],
'type': ['exact', 'in'],
}
search_fields = filterset_fields
serializer_class = serializers.SystemUserSerializer
@ -154,116 +147,6 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
return Response(status=204)
class SystemUserTempAuthInfoApi(generics.CreateAPIView):
model = SystemUser
permission_classes = (IsValidUser,)
serializer_class = serializers.SystemUserTempAuthSerializer
def create(self, request, *args, **kwargs):
serializer = super().get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
pk = kwargs.get('pk')
data = serializer.validated_data
asset_or_app_id = data.get('instance_id')
with tmp_to_root_org():
instance = get_object_or_404(SystemUser, pk=pk)
instance.set_temp_auth(asset_or_app_id, self.request.user.id, data)
return Response(serializer.data, status=201)
class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
"""
Get system user with asset auth info
"""
model = SystemUser
serializer_class = serializers.SystemUserWithAuthInfoSerializer
def get_object(self):
instance = super().get_object()
asset_id = self.kwargs.get('asset_id')
user_id = self.request.query_params.get("user_id")
username = self.request.query_params.get("username")
instance.load_asset_more_auth(asset_id, username, user_id)
return instance
class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
"""
Get system user with asset auth info
"""
model = SystemUser
serializer_class = serializers.SystemUserWithAuthInfoSerializer
rbac_perms = {
'retrieve': 'assets.view_systemusersecret',
}
def get_object(self):
instance = super().get_object()
app_id = self.kwargs.get('app_id')
user_id = self.request.query_params.get("user_id")
username = self.request.query_params.get("username")
instance.load_app_more_auth(app_id, username, user_id)
return instance
class SystemUserTaskApi(generics.CreateAPIView):
serializer_class = serializers.SystemUserTaskSerializer
def do_push(self, system_user, asset_ids=None):
if asset_ids is None:
task = push_system_user_to_assets_manual.delay(system_user)
else:
username = self.request.query_params.get('username')
task = push_system_user_to_assets.delay(
system_user.id, asset_ids, username=username
)
return task
@staticmethod
def do_test(system_user, asset_ids):
task = test_system_user_connectivity_manual.delay(system_user, asset_ids)
return task
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(SystemUser, pk=pk)
def check_permissions(self, request):
action = request.data.get('action')
action_perm_require = {
'push': 'assets.push_assetsystemuser',
'test': 'assets.test_assetconnectivity'
}
perm_required = action_perm_require.get(action)
has = self.request.user.has_perm(perm_required)
if not has:
self.permission_denied(request)
def perform_create(self, serializer):
action = serializer.validated_data["action"]
asset = serializer.validated_data.get('asset')
if asset:
assets = [asset]
else:
assets = serializer.validated_data.get('assets') or []
asset_ids = [asset.id for asset in assets]
asset_ids = asset_ids if asset_ids else None
system_user = self.get_object()
if action == 'push':
task = self.do_push(system_user, asset_ids)
else:
task = self.do_test(system_user, asset_ids)
data = getattr(serializer, '_data', {})
data["task"] = task.id
setattr(serializer, '_data', data)
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
rbac_perms = {
'list': 'assets.view_commandfilterule'
@ -291,19 +174,3 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
)
return rules
class SystemUserAssetsListView(generics.ListAPIView):
serializer_class = serializers.AssetSimpleSerializer
filterset_fields = ("hostname", "ip")
search_fields = filterset_fields
rbac_perms = {
'list': 'assets.view_asset'
}
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(SystemUser, pk=pk)
def get_queryset(self):
system_user = self.get_object()
return system_user.get_all_assets()

View File

@ -1,138 +0,0 @@
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.db.models import F, Value, Model
from django.db.models.signals import m2m_changed
from django.db.models.functions import Concat
from common.utils import get_logger
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import current_org
from .. import models, serializers
__all__ = [
'SystemUserAssetRelationViewSet', 'SystemUserNodeRelationViewSet',
'SystemUserUserRelationViewSet', 'BaseRelationViewSet',
]
logger = get_logger(__name__)
class RelationMixin:
model: Model
def get_queryset(self):
queryset = self.model.objects.all()
if not current_org.is_root():
org_id = current_org.org_id()
queryset = queryset.filter(systemuser__org_id=org_id)
queryset = queryset.annotate(systemuser_display=Concat(
F('systemuser__name'), Value('('),
F('systemuser__username'), Value(')')
))
return queryset
def send_post_add_signal(self, instance):
if not isinstance(instance, list):
instance = [instance]
system_users_objects_map = defaultdict(list)
model, object_field = self.get_objects_attr()
for i in instance:
_id = getattr(i, object_field).id
system_users_objects_map[i.systemuser].append(_id)
sender = self.get_sender()
for system_user, object_ids in system_users_objects_map.items():
logger.debug('System user relation changed, send m2m_changed signals')
m2m_changed.send(
sender=sender, instance=system_user, action='post_add',
reverse=False, model=model, pk_set=set(object_ids)
)
def get_sender(self):
return self.model
def get_objects_attr(self):
return models.Asset, 'asset'
def perform_create(self, serializer):
instance = serializer.save()
self.send_post_add_signal(instance)
class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet):
perm_model = models.SystemUser
class SystemUserAssetRelationViewSet(BaseRelationViewSet):
serializer_class = serializers.SystemUserAssetRelationSerializer
model = models.SystemUser.assets.through
filterset_fields = [
'id', 'asset', 'systemuser',
]
search_fields = [
"id", "asset__hostname", "asset__ip",
"systemuser__name", "systemuser__username",
]
def get_objects_attr(self):
return models.Asset, 'asset'
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(
asset_display=Concat(
F('asset__hostname'), Value('('),
F('asset__ip'), Value(')')
)
)
return queryset
class SystemUserNodeRelationViewSet(BaseRelationViewSet):
serializer_class = serializers.SystemUserNodeRelationSerializer
model = models.SystemUser.nodes.through
filterset_fields = [
'id', 'node', 'systemuser',
]
search_fields = [
"node__value", "systemuser__name", "systemuser__username"
]
def get_objects_attr(self):
return models.Node, 'node'
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset \
.annotate(node_key=F('node__key'))
return queryset
class SystemUserUserRelationViewSet(BaseRelationViewSet):
serializer_class = serializers.SystemUserUserRelationSerializer
model = models.SystemUser.users.through
filterset_fields = [
'id', 'user', 'systemuser',
]
search_fields = [
"user__username", "user__name",
"systemuser__name", "systemuser__username",
]
def get_objects_attr(self):
from users.models import User
return User, 'user'
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(
user_display=Concat(
F('user__name'), Value('('),
F('user__username'), Value(')')
)
)
return queryset

View File

@ -0,0 +1,89 @@
# Generated by Django 3.2.14 on 2022-07-28 03:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0093_auto_20220711_1413'),
]
operations = [
migrations.RemoveField(
model_name='cluster',
name='admin_user',
),
migrations.RemoveField(
model_name='systemuser',
name='ad_domain',
),
migrations.RemoveField(
model_name='systemuser',
name='assets',
),
migrations.RemoveField(
model_name='systemuser',
name='auto_push',
),
migrations.RemoveField(
model_name='systemuser',
name='groups',
),
migrations.RemoveField(
model_name='systemuser',
name='home',
),
migrations.RemoveField(
model_name='systemuser',
name='nodes',
),
migrations.RemoveField(
model_name='systemuser',
name='priority',
),
migrations.RemoveField(
model_name='systemuser',
name='sftp_root',
),
migrations.RemoveField(
model_name='systemuser',
name='shell',
),
migrations.RemoveField(
model_name='systemuser',
name='sudo',
),
migrations.RemoveField(
model_name='systemuser',
name='system_groups',
),
migrations.RemoveField(
model_name='systemuser',
name='token',
),
migrations.RemoveField(
model_name='systemuser',
name='type',
),
migrations.RemoveField(
model_name='systemuser',
name='users',
),
migrations.AlterField(
model_name='historicalaccount',
name='version',
field=models.IntegerField(default=0, verbose_name='Version'),
),
migrations.AlterField(
model_name='systemuser',
name='login_mode',
field=models.CharField(choices=[('auto', '使用账号'), ('manual', 'Manually input')], default='auto', max_length=10, verbose_name='Login mode'),
),
migrations.DeleteModel(
name='AdminUser',
),
migrations.DeleteModel(
name='Cluster',
),
]

View File

@ -2,7 +2,6 @@ from .base import *
from .asset import *
from .label import Label
from .user import *
from .cluster import *
from .group import *
from .domain import *
from .node import *
@ -12,5 +11,5 @@ from .utils import *
from .authbook import *
from .gathered_user import *
from .favorite_asset import *
from .backup import *
from .account import *
from .backup import *

View File

@ -1,40 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import logging
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
__all__ = ['Cluster']
logger = logging.getLogger(__name__)
class Cluster(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name'))
admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
address = models.CharField(max_length=128, blank=True, verbose_name=_("Address"))
intranet = models.TextField(blank=True, verbose_name=_('Intranet'))
extranet = models.TextField(blank=True, verbose_name=_('Extranet'))
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created'))
operator = models.CharField(max_length=32, blank=True, verbose_name=_('Operator'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __str__(self):
return self.name
@classmethod
def initial(cls):
return cls.objects.get_or_create(name=_('Default'), created_by=_('System'), comment=_('Default Cluster'))[0]
class Meta:
ordering = ['name']
verbose_name = _("Cluster")

View File

@ -0,0 +1,65 @@
from django.db import models
class ProtocolMixin:
protocol: str
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
vnc = 'vnc', 'VNC'
mysql = 'mysql', 'MySQL'
oracle = 'oracle', 'Oracle'
mariadb = 'mariadb', 'MariaDB'
postgresql = 'postgresql', 'PostgreSQL'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
k8s = 'k8s', 'K8S'
SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp]
ASSET_CATEGORY_PROTOCOLS = [
Protocol.ssh, Protocol.rdp, Protocol.telnet, Protocol.vnc
]
APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS = [
Protocol.rdp
]
APPLICATION_CATEGORY_DB_PROTOCOLS = [
Protocol.mysql, Protocol.mariadb, Protocol.oracle,
Protocol.postgresql, Protocol.sqlserver,
Protocol.redis, Protocol.mongodb
]
APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [
Protocol.k8s
]
APPLICATION_CATEGORY_PROTOCOLS = [
*APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS,
*APPLICATION_CATEGORY_DB_PROTOCOLS,
*APPLICATION_CATEGORY_CLOUD_PROTOCOLS
]
@property
def is_protocol_support_push(self):
return self.protocol in self.SUPPORT_PUSH_PROTOCOLS
@classmethod
def get_protocol_by_application_type(cls, app_type):
from applications.const import AppType
if app_type in cls.APPLICATION_CATEGORY_PROTOCOLS:
protocol = app_type
elif app_type in AppType.remote_app_types():
protocol = cls.Protocol.rdp
else:
protocol = None
return protocol
@property
def can_perm_to_asset(self):
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS
@property
def is_asset_protocol(self):
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS

View File

@ -6,83 +6,17 @@ import logging
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from django.shortcuts import get_object_or_404
from django.core.cache import cache
from common.utils import signer
from users.models import User
from .base import BaseUser
from .asset import Asset
from .protocol import ProtocolMixin
__all__ = ['AdminUser', 'SystemUser', 'ProtocolMixin']
__all__ = ['SystemUser']
logger = logging.getLogger(__name__)
class ProtocolMixin:
protocol: str
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
vnc = 'vnc', 'VNC'
mysql = 'mysql', 'MySQL'
oracle = 'oracle', 'Oracle'
mariadb = 'mariadb', 'MariaDB'
postgresql = 'postgresql', 'PostgreSQL'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
k8s = 'k8s', 'K8S'
SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp]
ASSET_CATEGORY_PROTOCOLS = [
Protocol.ssh, Protocol.rdp, Protocol.telnet, Protocol.vnc
]
APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS = [
Protocol.rdp
]
APPLICATION_CATEGORY_DB_PROTOCOLS = [
Protocol.mysql, Protocol.mariadb, Protocol.oracle,
Protocol.postgresql, Protocol.sqlserver,
Protocol.redis, Protocol.mongodb
]
APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [
Protocol.k8s
]
APPLICATION_CATEGORY_PROTOCOLS = [
*APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS,
*APPLICATION_CATEGORY_DB_PROTOCOLS,
*APPLICATION_CATEGORY_CLOUD_PROTOCOLS
]
@property
def is_protocol_support_push(self):
return self.protocol in self.SUPPORT_PUSH_PROTOCOLS
@classmethod
def get_protocol_by_application_type(cls, app_type):
from applications.const import AppType
if app_type in cls.APPLICATION_CATEGORY_PROTOCOLS:
protocol = app_type
elif app_type in AppType.remote_app_types():
protocol = cls.Protocol.rdp
else:
protocol = None
return protocol
@property
def can_perm_to_asset(self):
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS
@property
def is_asset_protocol(self):
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS
class SystemUser(ProtocolMixin, BaseUser):
LOGIN_AUTO = 'auto'
LOGIN_MANUAL = 'manual'
@ -91,39 +25,10 @@ class SystemUser(ProtocolMixin, BaseUser):
(LOGIN_MANUAL, _('Manually input'))
)
class Type(models.TextChoices):
common = 'common', _('Common user')
admin = 'admin', _('Admin user')
username_same_with_user = models.BooleanField(default=False, verbose_name=_("Username same with user"))
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
assets = models.ManyToManyField(
'assets.Asset', blank=True, verbose_name=_("Assets"),
related_name='system_users'
)
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
priority = models.IntegerField(
default=81, verbose_name=_("Priority"),
help_text=_("1-100, the lower the value will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)]
)
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
# Todo: 重构平台后或许这里也得变化
# 账号模版
account_template_enabled = models.BooleanField(default=False, verbose_name=_("启用账号模版"))
auto_push_account = models.BooleanField(default=True, verbose_name=_('自动推送账号'))
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
token = models.TextField(default='', verbose_name=_('Token'))
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)
system_groups = models.CharField(default='', max_length=4096, verbose_name=_('System groups'), blank=True)
ad_domain = models.CharField(default='', max_length=256)
# linux su 命令 (switch user)
# Todo: 修改为 username, 不必系统用户了
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
@ -135,32 +40,6 @@ class SystemUser(ProtocolMixin, BaseUser):
username = '*'
return '{0.name}({1})'.format(self, username)
@property
def nodes_amount(self):
return self.nodes.all().count()
@property
def login_mode_display(self):
return self.get_login_mode_display()
def is_need_push(self):
if self.auto_push_account and self.is_protocol_support_push:
return True
else:
return False
@property
def is_admin_user(self):
return self.type == self.Type.admin
@property
def is_need_cmd_filter(self):
return self.protocol not in [self.Protocol.rdp, self.Protocol.vnc]
@property
def is_need_test_asset_connective(self):
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS
@property
def cmd_filter_rules(self):
from .cmd_filter import CommandFilterRule
@ -178,30 +57,6 @@ class SystemUser(ProtocolMixin, BaseUser):
return False, matched_cmd
return True, None
def get_all_assets(self):
from assets.models import Node
nodes_keys = self.nodes.all().values_list('key', flat=True)
asset_ids = set(self.assets.all().values_list('id', flat=True))
nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
asset_ids.update(nodes_asset_ids)
assets = Asset.objects.filter(id__in=asset_ids)
return assets
def add_related_assets(self, assets_or_ids):
self.assets.add(*tuple(assets_or_ids))
self.add_related_assets_to_su_from_if_need(assets_or_ids)
def add_related_assets_to_su_from_if_need(self, assets_or_ids):
if self.protocol not in [self.Protocol.ssh.value]:
return
if not self.su_enabled:
return
if not self.su_from:
return
if self.su_from.protocol != self.protocol:
return
self.su_from.assets.add(*tuple(assets_or_ids))
@classmethod
def create_accounts_with_assets(cls, asset_ids, system_user_ids):
pass
@ -216,6 +71,7 @@ class SystemUser(ProtocolMixin, BaseUser):
def get_auto_account(self, user_id, asset_id):
from .account import Account
from users.models import User
username = self.username
if self.username_same_with_user:
user = get_object_or_404(User, id=user_id)
@ -235,52 +91,3 @@ class SystemUser(ProtocolMixin, BaseUser):
permissions = [
('match_systemuser', _('Can match system user')),
]
# Deprecated: 准备废弃
class AdminUser(BaseUser):
"""
A privileged user that ansible can use it to push system user and so on
"""
BECOME_METHOD_CHOICES = (
('sudo', 'sudo'),
('su', 'su'),
)
become = models.BooleanField(default=True)
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
become_user = models.CharField(default='root', max_length=64)
_become_pass = models.CharField(default='', blank=True, max_length=128)
CONNECTIVITY_CACHE_KEY = '_ADMIN_USER_CONNECTIVE_{}'
_prefer = "admin_user"
def __str__(self):
return self.name
@property
def become_pass(self):
password = signer.unsign(self._become_pass)
if password:
return password
else:
return ""
@become_pass.setter
def become_pass(self, password):
self._become_pass = signer.sign(password)
@property
def become_info(self):
if self.become:
info = {
"method": self.become_method,
"user": self.become_user,
"pass": self.become_pass,
}
else:
info = None
return info
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
verbose_name = _("Admin user")

View File

@ -2,7 +2,6 @@
#
from .asset import *
from .admin_user import *
from .label import *
from .system_user import *
from .node import *

View File

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
#
from ..models import SystemUser
from .system_user import SystemUserSerializer as SuS
class AdminUserSerializer(SuS):
"""
管理用户
"""
class Meta(SuS.Meta):
fields = SuS.Meta.fields_mini + \
SuS.Meta.fields_write_only + \
SuS.Meta.fields_m2m + \
[
'type', 'protocol', "priority", 'sftp_root', 'ssh_key_fingerprint',
'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment', 'created_by',
]
def validate_type(self, val):
return SystemUser.Type.admin
def validate_protocol(self, val):
return 'ssh'

View File

@ -91,7 +91,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
'cpu_info', 'hardware_info',
]
fields_fk = [
'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display'
'domain', 'domain_display', 'platform',
]
fields_m2m = [
'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts'
@ -114,19 +114,10 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
self.accounts_data = data.pop('accounts', [])
super().__init__(*args, **kwargs)
def get_fields(self):
fields = super().get_fields()
admin_user_field = fields.get('admin_user')
# 因为 mixin 中对 fields 有处理,可能不需要返回 admin_user
if admin_user_field:
admin_user_field.queryset = SystemUser.objects.filter(type=SystemUser.Type.admin)
return fields
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('domain', 'platform', 'admin_user')
queryset = queryset.prefetch_related('domain', 'platform')
queryset = queryset.prefetch_related('nodes', 'labels')
return queryset
@ -162,7 +153,6 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
def add_accounts(instance, accounts_data):
for data in accounts_data:
data['asset'] = instance.id
print("Data: ", accounts_data)
serializer = AccountSerializer(data=accounts_data, many=True)
try:
serializer.is_valid(raise_exception=True)

View File

@ -1,97 +1,38 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from django.db.models import Count
from common.mixins.serializers import BulkSerializerMixin
from common.utils import ssh_pubkey_gen
from common.drf.fields import EncryptedField
from common.drf.serializers import SecretReadableMixin
from common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import SystemUser, Asset
from .utils import validate_password_for_ansible
from .base import AuthSerializerMixin
from ..models import SystemUser
__all__ = [
'SystemUserSerializer', 'MiniSystemUserSerializer',
'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer',
'SystemUserNodeRelationSerializer', 'SystemUserTaskSerializer',
'SystemUserUserRelationSerializer', 'SystemUserWithAuthInfoSerializer',
'SystemUserTempAuthSerializer', 'RelationMixin',
'SystemUserSimpleSerializer',
]
class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class SystemUserSerializer(BulkOrgResourceModelSerializer):
"""
系统用户
"""
password = EncryptedField(
label=_('Password'), required=False, allow_blank=True, allow_null=True, max_length=1024,
trim_whitespace=False, validators=[validate_password_for_ansible],
write_only=True
)
auto_generate_key = serializers.BooleanField(initial=True, required=False, write_only=True)
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
ssh_key_fingerprint = serializers.ReadOnlyField(label=_('SSH key fingerprint'))
token = EncryptedField(
label=_('Token'), required=False, write_only=True, style={'base_template': 'textarea.html'}
)
applications_amount = serializers.IntegerField(
source='apps_amount', read_only=True, label=_('Apps amount')
)
class Meta:
model = SystemUser
fields_mini = ['id', 'name', 'username']
fields_write_only = ['password', 'public_key', 'private_key', 'passphrase']
fields_small = fields_mini + fields_write_only + [
'token', 'ssh_key_fingerprint',
'type', 'type_display', 'protocol', 'is_asset_protocol',
'account_template_enabled', 'login_mode', 'login_mode_display', 'priority',
'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain',
'username_same_with_user', 'auto_push_account', 'auto_generate_key',
'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment', 'created_by',
fields_mini = ['id', 'name', 'username', 'protocol']
fields_small = fields_mini + [
'login_mode', 'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment',
'created_by',
]
fields_m2m = ['cmd_filters', 'assets_amount', 'applications_amount', 'nodes']
fields = fields_small + fields_m2m
fields = fields_small
extra_kwargs = {
'cmd_filters': {"required": False, 'label': _('Command filter')},
'public_key': {"write_only": True},
'private_key': {"write_only": True},
'nodes_amount': {'label': _('Nodes amount')},
'assets_amount': {'label': _('Assets amount')},
'login_mode_display': {'label': _('Login mode display')},
'created_by': {'read_only': True},
'ad_domain': {'required': False, 'allow_blank': True, 'label': _('Ad domain')},
'is_asset_protocol': {'label': _('Is asset protocol')},
'su_from': {'help_text': _('Only ssh and automatic login system users are supported')}
}
def validate_auto_push(self, value):
login_mode = self.get_initial_value("login_mode")
protocol = self.get_initial_value("protocol")
if login_mode == SystemUser.LOGIN_MANUAL:
value = False
elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
value = False
return value
def validate_auto_generate_key(self, value):
login_mode = self.get_initial_value("login_mode")
protocol = self.get_initial_value("protocol")
if self.context["request"].method.lower() != "post":
value = False
elif self.instance:
value = False
elif login_mode == SystemUser.LOGIN_MANUAL:
value = False
elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
value = False
return value
def validate_username_same_with_user(self, username_same_with_user):
if not username_same_with_user:
return username_same_with_user
@ -132,12 +73,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
raise serializers.ValidationError(msg)
return username
def validate_home(self, home):
username_same_with_user = self.get_initial_value("username_same_with_user")
if username_same_with_user:
return ''
return home
@staticmethod
def validate_sftp_root(value):
if value in ['home', 'tmp']:
@ -147,17 +82,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
raise serializers.ValidationError(error)
return value
def validate_password(self, password):
super().validate_password(password)
auto_gen_key = self.get_initial_value('auto_generate_key', False)
private_key = self.get_initial_value('private_key')
login_mode = self.get_initial_value('login_mode')
if not self.instance and not auto_gen_key and not password and \
not private_key and login_mode == SystemUser.LOGIN_AUTO:
raise serializers.ValidationError(_("Password or private key required"))
return password
def validate_su_from(self, su_from: SystemUser):
# self: su enabled
su_enabled = self.get_initial_value('su_enabled', default=False)
@ -181,70 +105,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
raise serializers.ValidationError(error)
return su_from
def _validate_admin_user(self, attrs):
if self.instance:
tp = self.instance.type
else:
tp = attrs.get('type')
if tp != SystemUser.Type.admin:
return attrs
attrs['protocol'] = SystemUser.Protocol.ssh
attrs['login_mode'] = SystemUser.LOGIN_AUTO
attrs['username_same_with_user'] = False
attrs['auto_push_account'] = False
return attrs
def _validate_gen_key(self, attrs):
username = attrs.get('username', 'manual')
auto_gen_key = attrs.pop('auto_generate_key', False)
protocol = attrs.get('protocol')
if protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
return attrs
# 自动生成
if auto_gen_key and not self.instance:
password = SystemUser.gen_password()
attrs['password'] = password
if protocol == SystemUser.Protocol.ssh:
private_key, public_key = SystemUser.gen_key(username)
attrs['private_key'] = private_key
attrs['public_key'] = public_key
# 如果设置了private key没有设置public key则生成
elif attrs.get('private_key'):
private_key = attrs['private_key']
password = attrs.get('password')
public_key = ssh_pubkey_gen(private_key, password=password, username=username)
attrs['public_key'] = public_key
return attrs
def _validate_login_mode(self, attrs):
if 'login_mode' in attrs:
login_mode = attrs['login_mode']
else:
login_mode = self.instance.login_mode if self.instance else SystemUser.LOGIN_AUTO
if login_mode == SystemUser.LOGIN_MANUAL:
attrs['password'] = ''
attrs['private_key'] = ''
attrs['public_key'] = ''
return attrs
def validate(self, attrs):
attrs = self._validate_admin_user(attrs)
attrs = self._validate_gen_key(attrs)
attrs = self._validate_login_mode(attrs)
return attrs
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset \
.annotate(assets_amount=Count("assets")) \
.prefetch_related('nodes', 'cmd_filters')
return queryset
class MiniSystemUserSerializer(serializers.ModelSerializer):
class Meta:
@ -252,28 +112,6 @@ class MiniSystemUserSerializer(serializers.ModelSerializer):
fields = SystemUserSerializer.Meta.fields_mini
class SystemUserWithAuthInfoSerializer(SecretReadableMixin, SystemUserSerializer):
class Meta(SystemUserSerializer.Meta):
fields_mini = ['id', 'name', 'username']
fields_write_only = ['password', 'public_key', 'private_key']
fields_small = fields_mini + fields_write_only + [
'protocol', 'login_mode', 'login_mode_display', 'priority',
'sudo', 'shell', 'ad_domain', 'sftp_root', 'token',
"username_same_with_user", 'auto_push_account', 'auto_generate_key',
'comment',
]
fields = fields_small
extra_kwargs = {
'nodes_amount': {'label': _('Node')},
'assets_amount': {'label': _('Asset')},
'login_mode_display': {'label': _('Login mode display')},
'created_by': {'read_only': True},
'password': {'write_only': False},
'private_key': {'write_only': False},
'token': {'write_only': False}
}
class SystemUserSimpleSerializer(serializers.ModelSerializer):
"""
系统用户最基本信息的数据结构
@ -284,70 +122,6 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
fields = ('id', 'name', 'username')
class RelationMixin(BulkSerializerMixin, serializers.Serializer):
systemuser_display = serializers.ReadOnlyField(label=_("System user name"))
org_name = serializers.ReadOnlyField(label=_("Org name"))
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
fields.extend(['systemuser', "systemuser_display", "org_name"])
return fields
class SystemUserAssetRelationSerializer(RelationMixin, serializers.ModelSerializer):
asset_display = serializers.ReadOnlyField(label=_('Asset hostname'))
class Meta:
model = SystemUser.assets.through
fields = [
"id", "asset", "asset_display",
"systemuser", "systemuser_display",
]
use_model_bulk_create = True
model_bulk_create_kwargs = {
'ignore_conflicts': True
}
class SystemUserNodeRelationSerializer(RelationMixin, serializers.ModelSerializer):
node_display = serializers.SerializerMethodField()
class Meta:
model = SystemUser.nodes.through
fields = [
'id', 'node', "node_display",
]
def get_node_display(self, obj):
return obj.node.full_value
class SystemUserUserRelationSerializer(RelationMixin, serializers.ModelSerializer):
user_display = serializers.ReadOnlyField()
class Meta:
model = SystemUser.users.through
fields = [
'id', "user", "user_display",
]
class SystemUserTaskSerializer(serializers.Serializer):
ACTION_CHOICES = (
("test", "test"),
("push", "push"),
)
action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True)
asset = serializers.PrimaryKeyRelatedField(
queryset=Asset.objects, allow_null=True, required=False, write_only=True
)
assets = serializers.PrimaryKeyRelatedField(
queryset=Asset.objects, allow_null=True, required=False, write_only=True,
many=True
)
task = serializers.CharField(read_only=True)
class SystemUserTempAuthSerializer(SystemUserSerializer):
instance_id = serializers.CharField()

View File

@ -1,5 +1,4 @@
from .asset import *
from .system_user import *
from .authbook import *
from .account import *
from .node_assets_amount import *
from .node_assets_mapping import *

View File

@ -17,7 +17,6 @@ router.register(r'accounts-history', api.AccountHistoryViewSet, 'account-history
router.register(r'account-history-secrets', api.AccountHistorySecretsViewSet, 'account-history-secret')
router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
router.register(r'system-users', api.SystemUserViewSet, 'system-user')
router.register(r'admin-users', api.AdminUserViewSet, 'admin-user')
router.register(r'labels', api.LabelViewSet, 'label')
router.register(r'nodes', api.NodeViewSet, 'node')
router.register(r'domains', api.DomainViewSet, 'domain')
@ -25,9 +24,6 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway')
router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter')
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
router.register(r'system-users-assets-relations', api.SystemUserAssetRelationViewSet, 'system-users-assets-relation')
router.register(r'system-users-nodes-relations', api.SystemUserNodeRelationViewSet, 'system-users-nodes-relation')
router.register(r'system-users-users-relations', api.SystemUserUserRelationViewSet, 'system-users-users-relation')
router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup')
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
@ -45,11 +41,8 @@ urlpatterns = [
path('assets/<uuid:pk>/perm-user-groups/', api.AssetPermUserGroupListApi.as_view(), name='asset-perm-user-group-list'),
path('assets/<uuid:pk>/perm-user-groups/<uuid:perm_user_group_id>/permissions/', api.AssetPermUserGroupPermissionsListApi.as_view(), name='asset-perm-user-group-permission-list'),
path('system-users/<uuid:pk>/assets/', api.SystemUserAssetsListView.as_view(), name='system-user-assets'),
path('system-users/<uuid:pk>/applications/<uuid:app_id>/auth-info/', api.SystemUserAppAuthInfoApi.as_view(), name='system-user-app-auth-info'),
path('system-users/<uuid:pk>/assets/<uuid:asset_id>/users/<uuid:user_id>/account/', api.SystemUserAssetAccountApi.as_view(), name='system-user-asset-account'),
path('system-users/<uuid:pk>/assets/<uuid:asset_id>/users/<uuid:user_id>/account-secret/', api.SystemUserAssetAccountSecretApi.as_view(), name='system-user-asset-account-secret'),
path('system-users/<uuid:pk>/tasks/', api.SystemUserTaskApi.as_view(), name='system-user-task-create'),
path('system-users/<uuid:pk>/cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'),
path('cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='cmd-filter-rules'),

View File

@ -69,11 +69,6 @@ M2M_NEED_RECORD = {
_('{User} JOINED {UserGroup}'),
_('{User} LEFT {UserGroup}')
),
SystemUser.assets.through._meta.object_name: (
_('Asset and SystemUser'),
_('{Asset} ADD {SystemUser}'),
_('{Asset} REMOVE {SystemUser}')
),
Asset.nodes.through._meta.object_name: (
_('Node and Asset'),
_('{Node} ADD {Asset}'),
@ -99,11 +94,6 @@ M2M_NEED_RECORD = {
_('{AssetPermission} ADD {Node}'),
_('{AssetPermission} REMOVE {Node}'),
),
AssetPermission.system_users.through._meta.object_name: (
_('Asset permission and SystemUser'),
_('{AssetPermission} ADD {SystemUser}'),
_('{AssetPermission} REMOVE {SystemUser}'),
),
ApplicationPermission.users.through._meta.object_name: (
_('User application permissions'),
_('{ApplicationPermission} ADD {User}'),

View File

@ -62,20 +62,19 @@ class ValidateUserApplicationPermissionApi(APIView):
def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '')
application_id = request.query_params.get('application_id', '')
system_user_id = request.query_params.get('system_user_id', '')
account = system_user_id = request.query_params.get('account', '')
data = {
'has_permission': False,
'expire_at': int(time.time()),
'actions': []
}
if not all((user_id, application_id, system_user_id)):
if not all((user_id, application_id, account)):
return Response(data)
user = User.objects.get(id=user_id)
application = Application.objects.get(id=application_id)
system_user = SystemUser.objects.get(id=system_user_id)
has_perm, actions, expire_at = validate_permission(user, application, system_user)
has_perm, actions, expire_at = validate_permission(user, application, account)
status_code = status.HTTP_200_OK if has_perm else status.HTTP_403_FORBIDDEN
data = {
'has_permission': has_perm,

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
#
from rest_framework import generics
from django.db.models import F, Value
from django.db.models.functions import Concat
from django.db.models import F
from django.shortcuts import get_object_or_404
from orgs.mixins.api import OrgRelationMixin
@ -15,8 +14,7 @@ from perms.utils.asset.user_permission import UserGrantedAssetsQueryUtils
__all__ = [
'AssetPermissionUserRelationViewSet', 'AssetPermissionUserGroupRelationViewSet',
'AssetPermissionAssetRelationViewSet', 'AssetPermissionNodeRelationViewSet',
'AssetPermissionSystemUserRelationViewSet', 'AssetPermissionAllAssetListApi',
'AssetPermissionAllUserListApi',
'AssetPermissionAllAssetListApi', 'AssetPermissionAllUserListApi',
]
@ -117,21 +115,3 @@ class AssetPermissionNodeRelationViewSet(RelationMixin):
.annotate(node_key=F('node__key'))
return queryset
class AssetPermissionSystemUserRelationViewSet(RelationMixin):
serializer_class = serializers.AssetPermissionSystemUserRelationSerializer
m2m_field = models.AssetPermission.system_users.field
filterset_fields = [
'id', 'systemuser', 'assetpermission',
]
search_fields = [
"assetpermission__name", "systemuser__name", "systemuser__username"
]
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(
systemuser_display=Concat(
F('systemuser__name'), Value('('), F('systemuser__username'), Value(')')
))
return queryset

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.14 on 2022-07-28 09:10
from django.db import migrations, models
def migrate_system_user_to_accounts(apps, schema_editor):
# Todo: 迁移 系统用户为账号
pass
class Migration(migrations.Migration):
dependencies = [
('perms', '0028_auto_20220316_2028'),
]
operations = [
migrations.AddField(
model_name='assetpermission',
name='accounts',
field=models.JSONField(default=list, verbose_name='Accounts'),
),
migrations.RunPython(migrate_system_user_to_accounts),
migrations.RemoveField(
model_name='assetpermission',
name='system_users',
),
]

View File

@ -22,14 +22,13 @@ logger = logging.getLogger(__name__)
class AssetPermission(BasePermission):
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', blank=True, verbose_name=_("System user"))
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
class Meta:
unique_together = [('org_id', 'name')]
verbose_name = _("Asset permission")
ordering = ('name',)
permissions = [
]
permissions = []
@lazyproperty
def users_amount(self):
@ -47,10 +46,6 @@ class AssetPermission(BasePermission):
def nodes_amount(self):
return self.nodes.count()
@lazyproperty
def system_users_amount(self):
return self.system_users.count()
@classmethod
def get_queryset_with_prefetch(cls):
return cls.objects.all().valid().prefetch_related(
@ -80,10 +75,6 @@ class AssetPermission(BasePermission):
names = [asset.hostname for asset in self.assets.all()]
return names
def system_users_display(self):
names = [system_user.name for system_user in self.system_users.all()]
return names
def nodes_display(self):
names = [node.full_value for node in self.nodes.all()]
return names

View File

@ -22,7 +22,6 @@ class AssetPermissionSerializer(BasePermissionSerializer):
user_groups_display = serializers.ListField(child=serializers.CharField(), label=_('User groups display'), required=False)
assets_display = serializers.ListField(child=serializers.CharField(), label=_('Assets display'), required=False)
nodes_display = serializers.ListField(child=serializers.CharField(), label=_('Nodes display'), required=False)
system_users_display = serializers.ListField(child=serializers.CharField(), label=_('System users display'), required=False)
class Meta:
model = AssetPermission
@ -34,9 +33,9 @@ class AssetPermissionSerializer(BasePermissionSerializer):
]
fields_m2m = [
'users', 'users_display', 'user_groups', 'user_groups_display', 'assets',
'assets_display', 'nodes', 'nodes_display', 'system_users', 'system_users_display',
'assets_display', 'nodes', 'nodes_display', 'accounts',
'users_amount', 'user_groups_amount', 'assets_amount',
'nodes_amount', 'system_users_amount',
'nodes_amount',
]
fields = fields_small + fields_m2m
read_only_fields = ['created_by', 'date_created', 'from_ticket']
@ -48,30 +47,16 @@ class AssetPermissionSerializer(BasePermissionSerializer):
'user_groups_amount': {'label': _('User groups amount')},
'assets_amount': {'label': _('Assets amount')},
'nodes_amount': {'label': _('Nodes amount')},
'system_users_amount': {'label': _('System users amount')},
}
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related(
'users', 'user_groups', 'assets', 'nodes', 'system_users'
'users', 'user_groups', 'assets', 'nodes',
)
return queryset
def to_internal_value(self, data):
if 'system_users_display' in data:
# system_users_display 转化为 system_users
system_users = data.get('system_users', [])
system_users_display = data.pop('system_users_display')
for name in system_users_display:
system_user = SystemUser.objects.filter(name=name).first()
if system_user and system_user.id not in system_users:
system_users.append(system_user.id)
data['system_users'] = system_users
return super().to_internal_value(data)
@staticmethod
def perform_display_create(instance, **kwargs):
# 用户

View File

@ -12,7 +12,6 @@ __all__ = [
'AssetPermissionUserGroupRelationSerializer',
"AssetPermissionAssetRelationSerializer",
'AssetPermissionNodeRelationSerializer',
'AssetPermissionSystemUserRelationSerializer',
'AssetPermissionAllAssetSerializer',
'AssetPermissionAllUserSerializer',
]
@ -99,13 +98,3 @@ class AssetPermissionNodeRelationSerializer(RelationMixin, serializers.ModelSeri
fields = [
'id', 'node', "node_display",
]
class AssetPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer):
systemuser_display = serializers.ReadOnlyField()
class Meta:
model = AssetPermission.system_users.through
fields = [
'id', 'systemuser', 'systemuser_display'
]

View File

@ -1,138 +1,10 @@
# -*- coding: utf-8 -*-
#
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from users.models import User
from assets.models import SystemUser
from common.utils import get_logger
from common.decorator import on_transaction_commit
from common.exceptions import M2MReverseNotAllowed
from common.const.signals import POST_ADD
from perms.models import AssetPermission
logger = get_logger(__file__)
@receiver(m2m_changed, sender=User.groups.through)
@on_transaction_commit
def on_user_groups_change(sender, instance, action, reverse, pk_set, **kwargs):
"""
UserGroup 增加 User 增加的 User 需要与 UserGroup 关联的动态系统用户相关联
"""
user: User
if action != POST_ADD:
return
if not reverse:
# 一个用户添加了多个用户组
user_ids = [instance.id]
system_users = SystemUser.objects.filter(groups__id__in=pk_set).distinct()
else:
# 一个用户组添加了多个用户
user_ids = pk_set
system_users = SystemUser.objects.filter(groups__id=instance.pk).distinct()
for system_user in system_users:
system_user.users.add(*user_ids)
@receiver(m2m_changed, sender=AssetPermission.nodes.through)
@on_transaction_commit
def on_permission_nodes_changed(instance, action, reverse, pk_set, model, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Asset permission nodes change signal received")
nodes = model.objects.filter(pk__in=pk_set)
system_users = instance.system_users.all()
# TODO 待优化
for system_user in system_users:
system_user.nodes.add(*nodes)
@receiver(m2m_changed, sender=AssetPermission.assets.through)
@on_transaction_commit
def on_permission_assets_changed(instance, action, reverse, pk_set, model, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Asset permission assets change signal received")
assets = model.objects.filter(pk__in=pk_set)
# TODO 待优化
system_users = instance.system_users.all()
for system_user in system_users:
system_user: SystemUser
system_user.add_related_assets(assets)
@receiver(m2m_changed, sender=AssetPermission.system_users.through)
@on_transaction_commit
def on_asset_permission_system_users_changed(instance, action, reverse, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Asset permission system_users change signal received")
system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
assets = instance.assets.all().values_list('id', flat=True)
nodes = instance.nodes.all().values_list('id', flat=True)
for system_user in system_users:
system_user.nodes.add(*tuple(nodes))
system_user.add_related_assets(assets)
# 动态系统用户,需要关联用户和用户组了
if system_user.username_same_with_user:
users = instance.users.all().values_list('id', flat=True)
groups = instance.user_groups.all().values_list('id', flat=True)
system_user.groups.add(*tuple(groups))
system_user.users.add(*tuple(users))
@receiver(m2m_changed, sender=AssetPermission.users.through)
@on_transaction_commit
def on_asset_permission_users_changed(instance, action, reverse, pk_set, model, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Asset permission users change signal received")
users = model.objects.filter(pk__in=pk_set)
system_users = instance.system_users.all()
# TODO 待优化
for system_user in system_users:
if system_user.username_same_with_user:
system_user.users.add(*tuple(users))
@receiver(m2m_changed, sender=AssetPermission.user_groups.through)
@on_transaction_commit
def on_asset_permission_user_groups_changed(instance, action, pk_set, model, reverse, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Asset permission user groups change signal received")
groups = model.objects.filter(pk__in=pk_set)
system_users = instance.system_users.all()
# TODO 待优化
for system_user in system_users:
if system_user.username_same_with_user:
system_user.groups.add(*tuple(groups))

View File

@ -11,7 +11,6 @@ router.register('asset-permissions-users-relations', api.AssetPermissionUserRela
router.register('asset-permissions-user-groups-relations', api.AssetPermissionUserGroupRelationViewSet, 'asset-permissions-user-groups-relation')
router.register('asset-permissions-assets-relations', api.AssetPermissionAssetRelationViewSet, 'asset-permissions-assets-relation')
router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRelationViewSet, 'asset-permissions-nodes-relation')
router.register('asset-permissions-system-users-relations', api.AssetPermissionSystemUserRelationViewSet, 'asset-permissions-system-users-relation')
user_permission_urlpatterns = [
# 统一说明:

View File

@ -11,11 +11,7 @@ from perms.utils.asset.user_permission import get_user_all_asset_perm_ids
logger = get_logger(__file__)
def validate_permission(user, asset, system_user, action='connect'):
if not system_user.protocol in asset.protocols_as_dict.keys():
return False, time.time()
def validate_permission(user, asset, account, action='connect'):
asset_perm_ids = get_user_all_asset_perm_ids(user)
asset_perm_ids_from_asset = AssetPermission.assets.through.objects.filter(
@ -28,9 +24,7 @@ def validate_permission(user, asset, system_user, action='connect'):
for node in nodes:
ancestor_keys = node.get_ancestor_keys(with_self=True)
node_keys.update(ancestor_keys)
node_ids = Node.objects.filter(key__in=node_keys).values_list('id', flat=True)
node_ids = set(node_ids)
node_ids = set(Node.objects.filter(key__in=node_keys).values_list('id', flat=True))
asset_perm_ids_from_node = AssetPermission.nodes.through.objects.filter(
assetpermission_id__in=asset_perm_ids,
@ -39,16 +33,9 @@ def validate_permission(user, asset, system_user, action='connect'):
asset_perm_ids = {*asset_perm_ids_from_asset, *asset_perm_ids_from_node}
asset_perm_ids = AssetPermission.system_users.through.objects.filter(
assetpermission_id__in=asset_perm_ids,
systemuser_id=system_user.id
).values_list('assetpermission_id', flat=True)
asset_perm_ids = set(asset_perm_ids)
asset_perms = AssetPermission.objects.filter(
id__in=asset_perm_ids
).order_by('-date_expired')
asset_perms = AssetPermission.objects\
.filter(id__in=asset_perm_ids, accounts__contains=account)\
.order_by('-date_expired')
if asset_perms:
actions = set()

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2022-07-28 03:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0017_auto_20220623_1027'),
]
operations = [
migrations.AlterField(
model_name='applyapplicationticket',
name='apply_permission_name',
field=models.CharField(max_length=128, verbose_name='Permission name'),
),
migrations.AlterField(
model_name='applyassetticket',
name='apply_permission_name',
field=models.CharField(max_length=128, verbose_name='Permission name'),
),
]

View File

@ -47,12 +47,6 @@ class AssetPermissionGenerator(FakeDataGenerator):
relation_name = 'node_id'
self.set_relations(perms, through, relation_name, choices)
def set_system_users(self, perms):
through = AssetPermission.system_users.through
choices = self.system_user_ids
relation_name = 'systemuser_id'
self.set_relations(perms, through, relation_name, choices)
def set_relations(self, perms, through, relation_name, choices, choice_count=None):
relations = []
@ -79,4 +73,3 @@ class AssetPermissionGenerator(FakeDataGenerator):
self.set_user_groups(created)
self.set_assets(created)
self.set_nodes(created)
self.set_system_users(created)