mirror of https://github.com/jumpserver/jumpserver
perf: v3
commit
d43acd8612
|
@ -1,14 +1,10 @@
|
||||||
from .mixin import *
|
from .mixin import *
|
||||||
from .platform import *
|
from .platform import *
|
||||||
from .admin_user import *
|
|
||||||
from .asset import *
|
from .asset import *
|
||||||
from .label import *
|
from .label import *
|
||||||
from .system_user import *
|
|
||||||
from .system_user_relation import *
|
|
||||||
from .accounts import *
|
from .accounts import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .cmd_filter import *
|
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
from .account_backup import *
|
from .account_backup import *
|
||||||
|
|
|
@ -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
|
|
|
@ -1,85 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.generics import CreateAPIView
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
|
|
||||||
from common.utils import reverse
|
|
||||||
from common.utils import lazyproperty
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
|
||||||
from ..models import CommandFilter, CommandFilterRule
|
|
||||||
from .. import serializers
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterViewSet(OrgBulkModelViewSet):
|
|
||||||
model = CommandFilter
|
|
||||||
filterset_fields = ("name",)
|
|
||||||
search_fields = filterset_fields
|
|
||||||
serializer_class = serializers.CommandFilterSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
|
||||||
model = CommandFilterRule
|
|
||||||
filterset_fields = ('content',)
|
|
||||||
search_fields = filterset_fields
|
|
||||||
serializer_class = serializers.CommandFilterRuleSerializer
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
fpk = self.kwargs.get('filter_pk')
|
|
||||||
if not fpk:
|
|
||||||
return CommandFilterRule.objects.none()
|
|
||||||
cmd_filter = get_object_or_404(CommandFilter, pk=fpk)
|
|
||||||
return cmd_filter.rules.all()
|
|
||||||
|
|
||||||
|
|
||||||
class CommandConfirmAPI(CreateAPIView):
|
|
||||||
serializer_class = serializers.CommandConfirmSerializer
|
|
||||||
rbac_perms = {
|
|
||||||
'POST': 'tickets.add_superticket'
|
|
||||||
}
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
ticket = self.create_command_confirm_ticket()
|
|
||||||
response_data = self.get_response_data(ticket)
|
|
||||||
return Response(data=response_data, status=200)
|
|
||||||
|
|
||||||
def create_command_confirm_ticket(self):
|
|
||||||
ticket = self.serializer.cmd_filter_rule.create_command_confirm_ticket(
|
|
||||||
run_command=self.serializer.data.get('run_command'),
|
|
||||||
session=self.serializer.session,
|
|
||||||
cmd_filter_rule=self.serializer.cmd_filter_rule,
|
|
||||||
org_id=self.serializer.org.id,
|
|
||||||
)
|
|
||||||
return ticket
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_response_data(ticket):
|
|
||||||
confirm_status_url = reverse(
|
|
||||||
view_name='api-tickets:super-ticket-status',
|
|
||||||
kwargs={'pk': str(ticket.id)}
|
|
||||||
)
|
|
||||||
ticket_detail_url = reverse(
|
|
||||||
view_name='api-tickets:ticket-detail',
|
|
||||||
kwargs={'pk': str(ticket.id)},
|
|
||||||
external=True, api_to_ui=True
|
|
||||||
)
|
|
||||||
ticket_detail_url = '{url}?type={type}'.format(url=ticket_detail_url, type=ticket.type)
|
|
||||||
ticket_assignees = ticket.current_step.ticket_assignees.all()
|
|
||||||
return {
|
|
||||||
'check_confirm_status': {'method': 'GET', 'url': confirm_status_url},
|
|
||||||
'close_confirm': {'method': 'DELETE', 'url': confirm_status_url},
|
|
||||||
'ticket_detail_url': ticket_detail_url,
|
|
||||||
'reviewers': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees]
|
|
||||||
}
|
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def serializer(self):
|
|
||||||
serializer = self.get_serializer(data=self.request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
return serializer
|
|
||||||
|
|
|
@ -1,252 +0,0 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
|
|
||||||
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 assets.const import Protocol
|
|
||||||
from ..models import SystemUser, CommandFilterRule
|
|
||||||
from .. import serializers
|
|
||||||
from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
|
|
||||||
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',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
|
||||||
"""
|
|
||||||
System user api set, for add,delete,update,list,retrieve resource
|
|
||||||
"""
|
|
||||||
model = SystemUser
|
|
||||||
filterset_fields = {
|
|
||||||
'name': ['exact'],
|
|
||||||
'username': ['exact'],
|
|
||||||
'protocol': ['exact', 'in'],
|
|
||||||
'type': ['exact', 'in'],
|
|
||||||
}
|
|
||||||
search_fields = filterset_fields
|
|
||||||
serializer_class = serializers.SystemUserSerializer
|
|
||||||
serializer_classes = {
|
|
||||||
'default': serializers.SystemUserSerializer,
|
|
||||||
'suggestion': serializers.MiniSystemUserSerializer
|
|
||||||
}
|
|
||||||
ordering_fields = ('name', 'protocol', 'login_mode')
|
|
||||||
ordering = ('name', )
|
|
||||||
rbac_perms = {
|
|
||||||
'su_from': 'assets.view_systemuser',
|
|
||||||
'su_to': 'assets.view_systemuser',
|
|
||||||
'match': 'assets.match_systemuser'
|
|
||||||
}
|
|
||||||
|
|
||||||
@action(methods=['get'], detail=False, url_path='su-from')
|
|
||||||
def su_from(self, request, *args, **kwargs):
|
|
||||||
""" API 获取可选的 su_from 系统用户"""
|
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
|
||||||
queryset = queryset.filter(
|
|
||||||
protocol=Protocol.ssh, login_mode=SystemUser.LOGIN_AUTO
|
|
||||||
)
|
|
||||||
return self.get_paginate_response_if_need(queryset)
|
|
||||||
|
|
||||||
@action(methods=['get'], detail=True, url_path='su-to')
|
|
||||||
def su_to(self, request, *args, **kwargs):
|
|
||||||
""" 获取系统用户的所有 su_to 系统用户 """
|
|
||||||
pk = kwargs.get('pk')
|
|
||||||
system_user = get_object_or_404(SystemUser, pk=pk)
|
|
||||||
queryset = system_user.su_to.all()
|
|
||||||
queryset = self.filter_queryset(queryset)
|
|
||||||
return self.get_paginate_response_if_need(queryset)
|
|
||||||
|
|
||||||
def get_paginate_response_if_need(self, queryset):
|
|
||||||
page = self.paginate_queryset(queryset)
|
|
||||||
if page is not None:
|
|
||||||
serializer = self.get_serializer(page, many=True)
|
|
||||||
return self.get_paginated_response(serializer.data)
|
|
||||||
serializer = self.get_serializer(queryset, many=True)
|
|
||||||
return Response(serializer.data)
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
|
||||||
"""
|
|
||||||
Get system user auth info
|
|
||||||
"""
|
|
||||||
model = SystemUser
|
|
||||||
serializer_class = SystemUserWithAuthInfoSerializer
|
|
||||||
rbac_perms = {
|
|
||||||
'retrieve': 'assets.view_systemusersecret',
|
|
||||||
'list': 'assets.view_systemusersecret',
|
|
||||||
'change': 'assets.change_systemuser',
|
|
||||||
'destroy': 'assets.change_systemuser',
|
|
||||||
}
|
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
instance.clear_auth()
|
|
||||||
return Response(status=204)
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserTempAuthInfoApi(generics.CreateAPIView):
|
|
||||||
model = SystemUser
|
|
||||||
permission_classes = (IsValidUser,)
|
|
||||||
serializer_class = 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 = 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 = 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'
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
|
||||||
from ..serializers import CommandFilterRuleSerializer
|
|
||||||
return CommandFilterRuleSerializer
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
user_id = self.request.query_params.get('user_id')
|
|
||||||
user_group_id = self.request.query_params.get('user_group_id')
|
|
||||||
system_user_id = self.kwargs.get('pk', None)
|
|
||||||
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
|
||||||
if not system_user:
|
|
||||||
system_user_id = self.request.query_params.get('system_user_id')
|
|
||||||
asset_id = self.request.query_params.get('asset_id')
|
|
||||||
application_id = self.request.query_params.get('application_id')
|
|
||||||
rules = CommandFilterRule.get_queryset(
|
|
||||||
user_id=user_id,
|
|
||||||
user_group_id=user_group_id,
|
|
||||||
system_user_id=system_user_id,
|
|
||||||
asset_id=asset_id,
|
|
||||||
application_id=application_id
|
|
||||||
)
|
|
||||||
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()
|
|
|
@ -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
|
|
|
@ -16,14 +16,6 @@ def add_default_group(apps, schema_editor):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_default_cluster(apps, schema_editor):
|
|
||||||
cluster_model = apps.get_model("assets", "Cluster")
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
cluster_model.objects.using(db_alias).create(
|
|
||||||
name="Default"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
@ -163,6 +155,5 @@ class Migration(migrations.Migration):
|
||||||
unique_together=set([('ip', 'port')]),
|
unique_together=set([('ip', 'port')]),
|
||||||
),
|
),
|
||||||
|
|
||||||
migrations.RunPython(add_default_cluster),
|
|
||||||
migrations.RunPython(add_default_group),
|
migrations.RunPython(add_default_group),
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,9 +14,4 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
|
||||||
model_name='asset',
|
|
||||||
name='cluster',
|
|
||||||
field=models.ForeignKey(default=assets.models.default_cluster, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='assets', to='assets.Cluster', verbose_name='Cluster'),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Generated by Django 3.2.12 on 2022-07-11 08:59
|
# Generated by Django 3.2.12 on 2022-07-11 08:59
|
||||||
|
|
||||||
import assets.models.base
|
import assets.models.base
|
||||||
import assets.models.user
|
|
||||||
import common.db.fields
|
import common.db.fields
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -36,7 +35,7 @@ class Migration(migrations.Migration):
|
||||||
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
|
||||||
('protocol', models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol')),
|
('protocol', models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol')),
|
||||||
('type', models.CharField(choices=[('common', 'Common user'), ('admin', 'Admin user')], default='common', max_length=16, verbose_name='Type')),
|
('type', models.CharField(choices=[('common', 'Common user'), ('admin', 'Admin user')], default='common', max_length=16, verbose_name='Type')),
|
||||||
('version', models.IntegerField(default=1, verbose_name='Version')),
|
('version', models.IntegerField(default=0, verbose_name='Version')),
|
||||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('history_date', models.DateTimeField(db_index=True)),
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
@ -75,9 +74,9 @@ class Migration(migrations.Migration):
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Account',
|
'verbose_name': 'Account',
|
||||||
'permissions': [('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret'), ('view_assethistoryaccount', 'Can view asset history account'), ('view_assethistoryaccountsecret', 'Can view asset history account secret')],
|
'permissions': [('view_accountsecret', 'Can view asset account secret'), ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')],
|
||||||
'unique_together': {('username', 'asset')},
|
'unique_together': {('username', 'asset')},
|
||||||
},
|
},
|
||||||
bases=(models.Model, assets.models.base.AuthMixin, assets.models.user.ProtocolMixin),
|
bases=(models.Model, assets.models.base.AuthMixin, assets.models.protocol.ProtocolMixin),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Generated by Django 3.2.14 on 2022-08-03 06:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('assets', '0093_auto_20220711_1413'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='Cluster',
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,17 +1,17 @@
|
||||||
from .base import *
|
from .base import *
|
||||||
from .platform import *
|
from .platform import *
|
||||||
|
from ._user import *
|
||||||
from .asset import *
|
from .asset import *
|
||||||
from .label import Label
|
from .label import Label
|
||||||
from .user import *
|
|
||||||
from .cluster import *
|
|
||||||
from .group import *
|
from .group import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .cmd_filter import *
|
|
||||||
from .authbook import *
|
|
||||||
from .utils import *
|
from .utils import *
|
||||||
from .authbook import *
|
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
from .backup import *
|
|
||||||
from .account import *
|
from .account import *
|
||||||
|
from .backup import *
|
||||||
|
# 废弃以下
|
||||||
|
from ._authbook import *
|
||||||
|
from .protocol import *
|
||||||
|
from .cmd_filter import *
|
||||||
|
|
|
@ -125,9 +125,8 @@ class AuthBook(BaseUser, AbsConnectivity):
|
||||||
logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
|
logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, is_history_model=False):
|
def get_queryset(cls):
|
||||||
model = cls.history.model if is_history_model else cls
|
queryset = cls.objects.all() \
|
||||||
queryset = model.objects.all() \
|
|
||||||
.annotate(ip=F('asset__ip')) \
|
.annotate(ip=F('asset__ip')) \
|
||||||
.annotate(hostname=F('asset__hostname')) \
|
.annotate(hostname=F('asset__hostname')) \
|
||||||
.annotate(platform=F('asset__platform__name')) \
|
.annotate(platform=F('asset__platform__name')) \
|
||||||
|
@ -136,5 +135,3 @@ class AuthBook(BaseUser, AbsConnectivity):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.smart_name
|
return self.smart_name
|
||||||
|
|
||||||
|
|
|
@ -11,68 +11,18 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from assets.const import Protocol
|
from assets.const import Protocol
|
||||||
from common.utils import signer
|
from common.utils import signer
|
||||||
from .base import BaseUser
|
from .base import BaseUser
|
||||||
from .asset import Asset
|
from .protocol import ProtocolMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['AdminUser', 'SystemUser', 'ProtocolMixin']
|
__all__ = ['SystemUser']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ProtocolMixin:
|
|
||||||
protocol: str
|
|
||||||
Protocol = Protocol
|
|
||||||
|
|
||||||
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):
|
class SystemUser(ProtocolMixin, BaseUser):
|
||||||
LOGIN_AUTO = 'auto'
|
LOGIN_AUTO = 'auto'
|
||||||
LOGIN_MANUAL = 'manual'
|
LOGIN_MANUAL = 'manual'
|
||||||
LOGIN_MODE_CHOICES = (
|
LOGIN_MODE_CHOICES = (
|
||||||
(LOGIN_AUTO, _('使用账号')),
|
(LOGIN_AUTO, _('Automatic managed')),
|
||||||
(LOGIN_MANUAL, _('Manually input'))
|
(LOGIN_MANUAL, _('Manually input'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,34 +34,24 @@ class SystemUser(ProtocolMixin, BaseUser):
|
||||||
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
|
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
|
||||||
assets = models.ManyToManyField(
|
assets = models.ManyToManyField(
|
||||||
'assets.Asset', blank=True, verbose_name=_("Assets"),
|
'assets.Asset', blank=True, verbose_name=_("Assets"),
|
||||||
|
through='assets.AuthBook', through_fields=['systemuser', 'asset'],
|
||||||
related_name='system_users'
|
related_name='system_users'
|
||||||
)
|
)
|
||||||
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
|
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
|
||||||
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
|
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=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'))
|
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type'))
|
||||||
|
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'))
|
||||||
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
||||||
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
|
||||||
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
||||||
|
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
|
||||||
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
|
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
|
||||||
token = models.TextField(default='', verbose_name=_('Token'))
|
token = models.TextField(default='', verbose_name=_('Token'))
|
||||||
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)
|
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)
|
system_groups = models.CharField(default='', max_length=4096, verbose_name=_('System groups'), blank=True)
|
||||||
ad_domain = models.CharField(default='', max_length=256)
|
ad_domain = models.CharField(default='', max_length=256)
|
||||||
|
|
||||||
# linux su 命令 (switch user)
|
# linux su 命令 (switch user)
|
||||||
# Todo: 修改为 username, 不必系统用户了
|
|
||||||
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
|
su_enabled = models.BooleanField(default=False, verbose_name=_('User switch'))
|
||||||
su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from"))
|
su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from"))
|
||||||
|
|
||||||
|
@ -130,7 +70,7 @@ class SystemUser(ProtocolMixin, BaseUser):
|
||||||
return self.get_login_mode_display()
|
return self.get_login_mode_display()
|
||||||
|
|
||||||
def is_need_push(self):
|
def is_need_push(self):
|
||||||
if self.auto_push_account and self.is_protocol_support_push:
|
if self.auto_push and self.is_protocol_support_push:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -165,7 +105,7 @@ class SystemUser(ProtocolMixin, BaseUser):
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def get_all_assets(self):
|
def get_all_assets(self):
|
||||||
from assets.models import Node
|
from assets.models import Node, Asset
|
||||||
nodes_keys = self.nodes.all().values_list('key', flat=True)
|
nodes_keys = self.nodes.all().values_list('key', flat=True)
|
||||||
asset_ids = set(self.assets.all().values_list('id', 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)
|
nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
|
|
@ -2,7 +2,7 @@ from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
|
||||||
from .user import ProtocolMixin
|
from .protocol import ProtocolMixin
|
||||||
from .base import BaseUser, AbsConnectivity
|
from .base import BaseUser, AbsConnectivity
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,8 +14,10 @@ class Account(BaseUser, AbsConnectivity, ProtocolMixin):
|
||||||
common = 'common', _('Common user')
|
common = 'common', _('Common user')
|
||||||
admin = 'admin', _('Admin user')
|
admin = 'admin', _('Admin user')
|
||||||
|
|
||||||
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices,
|
protocol = models.CharField(
|
||||||
default='ssh', verbose_name=_('Protocol'))
|
max_length=16, choices=ProtocolMixin.Protocol.choices,
|
||||||
|
default='ssh', verbose_name=_('Protocol')
|
||||||
|
)
|
||||||
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_("Type"))
|
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_("Type"))
|
||||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
|
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
|
||||||
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
||||||
|
@ -25,10 +27,10 @@ class Account(BaseUser, AbsConnectivity, ProtocolMixin):
|
||||||
verbose_name = _('Account')
|
verbose_name = _('Account')
|
||||||
unique_together = [('username', 'asset')]
|
unique_together = [('username', 'asset')]
|
||||||
permissions = [
|
permissions = [
|
||||||
('view_assetaccountsecret', _('Can view asset account secret')),
|
('view_accountsecret', _('Can view asset account secret')),
|
||||||
('change_assetaccountsecret', _('Can change asset account secret')),
|
('change_accountsecret', _('Can change asset account secret')),
|
||||||
('view_assethistoryaccount', _('Can view asset history account')),
|
('view_historyaccount', _('Can view asset history account')),
|
||||||
('view_assethistoryaccountsecret', _('Can view asset history account secret')),
|
('view_historyaccountsecret', _('Can view asset history account secret')),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
|
@ -9,7 +9,6 @@ from collections import OrderedDict
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
|
|
||||||
from common.db.fields import JsonDictTextField
|
from common.db.fields import JsonDictTextField
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
|
@ -22,16 +21,6 @@ __all__ = ['Asset', 'ProtocolsMixin', 'AssetQuerySet', 'default_node', 'default_
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def default_cluster():
|
|
||||||
from assets.models import Cluster
|
|
||||||
name = "Default"
|
|
||||||
defaults = {"name": name}
|
|
||||||
cluster, created = Cluster.objects.get_or_create(
|
|
||||||
defaults=defaults, name=name
|
|
||||||
)
|
|
||||||
return cluster.id
|
|
||||||
|
|
||||||
|
|
||||||
def default_node():
|
def default_node():
|
||||||
try:
|
try:
|
||||||
from assets.models import Node
|
from assets.models import Node
|
||||||
|
|
|
@ -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")
|
|
|
@ -9,7 +9,6 @@ from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from applications.models import Application
|
|
||||||
from ..models import SystemUser, Asset
|
from ..models import SystemUser, Asset
|
||||||
|
|
||||||
from common.utils import lazyproperty, get_logger, get_object_or_none
|
from common.utils import lazyproperty, get_logger, get_object_or_none
|
||||||
|
@ -125,6 +124,9 @@ class CommandFilterRule(OrgModelMixin):
|
||||||
regex.append(cmd)
|
regex.append(cmd)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not cmd:
|
||||||
|
continue
|
||||||
|
|
||||||
# 如果是单个字符
|
# 如果是单个字符
|
||||||
if cmd[-1].isalpha():
|
if cmd[-1].isalpha():
|
||||||
regex.append(r'\b{0}\b'.format(cmd))
|
regex.append(r'\b{0}\b'.format(cmd))
|
||||||
|
@ -187,6 +189,7 @@ class CommandFilterRule(OrgModelMixin):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
|
def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
|
||||||
asset_id=None, application_id=None, org_id=None):
|
asset_id=None, application_id=None, org_id=None):
|
||||||
|
from applications.models import Application
|
||||||
user_groups = []
|
user_groups = []
|
||||||
user = get_object_or_none(User, pk=user_id)
|
user = get_object_or_none(User, pk=user_id)
|
||||||
if user:
|
if user:
|
||||||
|
|
|
@ -7,3 +7,67 @@ from common.db.models import JMSBaseModel
|
||||||
class Protocol(JMSBaseModel):
|
class Protocol(JMSBaseModel):
|
||||||
name = models.CharField(max_length=32, verbose_name=_("Name"))
|
name = models.CharField(max_length=32, verbose_name=_("Name"))
|
||||||
port = models.IntegerField(verbose_name=_("Port"))
|
port = models.IntegerField(verbose_name=_("Port"))
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from .asset import *
|
from .asset import *
|
||||||
from .admin_user import *
|
|
||||||
from .label import *
|
from .label import *
|
||||||
from .system_user import *
|
from .system_user import *
|
||||||
from .node import *
|
from .node import *
|
||||||
from .domain import *
|
from .domain import *
|
||||||
from .cmd_filter import *
|
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
from .favorite_asset import *
|
from .favorite_asset import *
|
||||||
from .account import *
|
from .account import *
|
||||||
|
|
|
@ -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'
|
|
|
@ -94,8 +94,7 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin):
|
||||||
'public_ip', 'number', 'comment',
|
'public_ip', 'number', 'comment',
|
||||||
]
|
]
|
||||||
fields_fk = [
|
fields_fk = [
|
||||||
'domain', 'domain_display', 'platform', 'platform_display',
|
'domain', 'domain_display', 'platform',
|
||||||
'admin_user', 'admin_user_display'
|
|
||||||
]
|
]
|
||||||
fields_m2m = [
|
fields_m2m = [
|
||||||
'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts'
|
'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts'
|
||||||
|
@ -137,7 +136,7 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" 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')
|
queryset = queryset.prefetch_related('nodes', 'labels')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@ -173,7 +172,6 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin):
|
||||||
def add_accounts(instance, accounts_data):
|
def add_accounts(instance, accounts_data):
|
||||||
for data in accounts_data:
|
for data in accounts_data:
|
||||||
data['asset'] = instance.id
|
data['asset'] = instance.id
|
||||||
print("Data: ", accounts_data)
|
|
||||||
serializer = AccountSerializer(data=accounts_data, many=True)
|
serializer = AccountSerializer(data=accounts_data, many=True)
|
||||||
try:
|
try:
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
import re
|
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from ..models import CommandFilter, CommandFilterRule
|
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|
||||||
from orgs.utils import tmp_to_root_org
|
|
||||||
from common.utils import get_object_or_none, lazyproperty
|
|
||||||
from terminal.models import Session
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterSerializer(BulkOrgResourceModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = CommandFilter
|
|
||||||
fields_mini = ['id', 'name']
|
|
||||||
fields_small = fields_mini + [
|
|
||||||
'org_id', 'org_name', 'is_active',
|
|
||||||
'date_created', 'date_updated',
|
|
||||||
'comment', 'created_by',
|
|
||||||
]
|
|
||||||
fields_fk = ['rules']
|
|
||||||
fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications']
|
|
||||||
fields = fields_small + fields_fk + fields_m2m
|
|
||||||
extra_kwargs = {
|
|
||||||
'rules': {'read_only': True},
|
|
||||||
'date_created': {'label': _("Date created")},
|
|
||||||
'date_updated': {'label': _("Date updated")},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
|
|
||||||
type_display = serializers.ReadOnlyField(source='get_type_display', label=_("Type display"))
|
|
||||||
action_display = serializers.ReadOnlyField(source='get_action_display', label=_("Action display"))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = CommandFilterRule
|
|
||||||
fields_mini = ['id']
|
|
||||||
fields_small = fields_mini + [
|
|
||||||
'type', 'type_display', 'content', 'ignore_case', 'pattern',
|
|
||||||
'priority', 'action', 'action_display', 'reviewers',
|
|
||||||
'date_created', 'date_updated', 'comment', 'created_by',
|
|
||||||
]
|
|
||||||
fields_fk = ['filter']
|
|
||||||
fields = fields_small + fields_fk
|
|
||||||
extra_kwargs = {
|
|
||||||
'date_created': {'label': _("Date created")},
|
|
||||||
'date_updated': {'label': _("Date updated")},
|
|
||||||
'action_display': {'label': _("Action display")},
|
|
||||||
'pattern': {'label': _("Pattern")}
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.set_action_choices()
|
|
||||||
|
|
||||||
def set_action_choices(self):
|
|
||||||
from django.conf import settings
|
|
||||||
action = self.fields.get('action')
|
|
||||||
if not action:
|
|
||||||
return
|
|
||||||
choices = action._choices
|
|
||||||
if not settings.XPACK_ENABLED:
|
|
||||||
choices.pop(CommandFilterRule.ActionChoices.confirm, None)
|
|
||||||
action._choices = choices
|
|
||||||
|
|
||||||
def validate_content(self, content):
|
|
||||||
tp = self.initial_data.get("type")
|
|
||||||
if tp == CommandFilterRule.TYPE_COMMAND:
|
|
||||||
regex = CommandFilterRule.construct_command_regex(content)
|
|
||||||
else:
|
|
||||||
regex = content
|
|
||||||
ignore_case = self.initial_data.get('ignore_case')
|
|
||||||
succeed, error, pattern = CommandFilterRule.compile_regex(regex, ignore_case)
|
|
||||||
if not succeed:
|
|
||||||
raise serializers.ValidationError(error)
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
class CommandConfirmSerializer(serializers.Serializer):
|
|
||||||
session_id = serializers.UUIDField(required=True, allow_null=False)
|
|
||||||
cmd_filter_rule_id = serializers.UUIDField(required=True, allow_null=False)
|
|
||||||
run_command = serializers.CharField(required=True, allow_null=False)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.session = None
|
|
||||||
self.cmd_filter_rule = None
|
|
||||||
|
|
||||||
def validate_session_id(self, session_id):
|
|
||||||
self.session = self.validate_object_exist(Session, session_id)
|
|
||||||
return session_id
|
|
||||||
|
|
||||||
def validate_cmd_filter_rule_id(self, cmd_filter_rule_id):
|
|
||||||
self.cmd_filter_rule = self.validate_object_exist(CommandFilterRule, cmd_filter_rule_id)
|
|
||||||
return cmd_filter_rule_id
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_object_exist(model, field_id):
|
|
||||||
with tmp_to_root_org():
|
|
||||||
obj = get_object_or_none(model, id=field_id)
|
|
||||||
if not obj:
|
|
||||||
error = '{} Model object does not exist'.format(model.__name__)
|
|
||||||
raise serializers.ValidationError(error)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def org(self):
|
|
||||||
return self.session.org
|
|
|
@ -1,98 +1,37 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from assets.const import Protocol
|
|
||||||
from ..models import SystemUser, Asset
|
|
||||||
from .utils import validate_password_for_ansible
|
|
||||||
from .base import AuthSerializerMixin
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'SystemUserSerializer', 'MiniSystemUserSerializer',
|
'SystemUserSerializer', 'MiniSystemUserSerializer',
|
||||||
'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer',
|
'SystemUserSimpleSerializer',
|
||||||
'SystemUserNodeRelationSerializer', 'SystemUserTaskSerializer',
|
|
||||||
'SystemUserUserRelationSerializer', 'SystemUserWithAuthInfoSerializer',
|
|
||||||
'SystemUserTempAuthSerializer', 'RelationMixin',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
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:
|
class Meta:
|
||||||
model = SystemUser
|
model = SystemUser
|
||||||
fields_mini = ['id', 'name', 'username']
|
fields_mini = ['id', 'name', 'username', 'protocol']
|
||||||
fields_write_only = ['password', 'public_key', 'private_key', 'passphrase']
|
fields_small = fields_mini + [
|
||||||
fields_small = fields_mini + fields_write_only + [
|
'login_mode', 'su_enabled', 'su_from',
|
||||||
'token', 'ssh_key_fingerprint',
|
'date_created', 'date_updated', 'comment',
|
||||||
'type', 'type_display', 'protocol', 'is_asset_protocol',
|
'created_by',
|
||||||
'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_m2m = ['cmd_filters', 'assets_amount', 'applications_amount', 'nodes']
|
fields = fields_small
|
||||||
fields = fields_small + fields_m2m
|
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'cmd_filters': {"required": False, 'label': _('Command filter')},
|
'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')},
|
'login_mode_display': {'label': _('Login mode display')},
|
||||||
'created_by': {'read_only': True},
|
'created_by': {'read_only': True},
|
||||||
'ad_domain': {'required': False, 'allow_blank': True, 'label': _('Ad domain')},
|
'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')}
|
'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):
|
def validate_username_same_with_user(self, username_same_with_user):
|
||||||
if not username_same_with_user:
|
if not username_same_with_user:
|
||||||
return username_same_with_user
|
return username_same_with_user
|
||||||
|
@ -133,12 +72,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
raise serializers.ValidationError(msg)
|
raise serializers.ValidationError(msg)
|
||||||
return username
|
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
|
@staticmethod
|
||||||
def validate_sftp_root(value):
|
def validate_sftp_root(value):
|
||||||
if value in ['home', 'tmp']:
|
if value in ['home', 'tmp']:
|
||||||
|
@ -148,17 +81,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
raise serializers.ValidationError(error)
|
raise serializers.ValidationError(error)
|
||||||
return value
|
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):
|
def validate_su_from(self, su_from: SystemUser):
|
||||||
# self: su enabled
|
# self: su enabled
|
||||||
su_enabled = self.get_initial_value('su_enabled', default=False)
|
su_enabled = self.get_initial_value('su_enabled', default=False)
|
||||||
|
@ -182,70 +104,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
raise serializers.ValidationError(error)
|
raise serializers.ValidationError(error)
|
||||||
return su_from
|
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'] = 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 == 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 MiniSystemUserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -253,28 +111,6 @@ class MiniSystemUserSerializer(serializers.ModelSerializer):
|
||||||
fields = SystemUserSerializer.Meta.fields_mini
|
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):
|
class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
系统用户最基本信息的数据结构
|
系统用户最基本信息的数据结构
|
||||||
|
@ -285,70 +121,6 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||||
fields = ('id', 'name', 'username')
|
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
|
|
||||||
fields = [
|
|
||||||
"id", "asset", "asset_display", 'systemuser', 'systemuser_display',
|
|
||||||
"connectivity", 'date_verified', 'org_id'
|
|
||||||
]
|
|
||||||
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):
|
class SystemUserTempAuthSerializer(SystemUserSerializer):
|
||||||
instance_id = serializers.CharField()
|
instance_id = serializers.CharField()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from .asset import *
|
from .asset import *
|
||||||
from .system_user import *
|
from .account import *
|
||||||
from .authbook import *
|
|
||||||
from .node_assets_amount import *
|
from .node_assets_amount import *
|
||||||
from .node_assets_mapping import *
|
from .node_assets_mapping import *
|
||||||
|
|
|
@ -8,11 +8,10 @@ from django.dispatch import receiver
|
||||||
from common.const.signals import POST_ADD, POST_REMOVE, PRE_REMOVE
|
from common.const.signals import POST_ADD, POST_REMOVE, PRE_REMOVE
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.decorator import on_transaction_commit
|
from common.decorator import on_transaction_commit
|
||||||
from assets.models import Asset, SystemUser, Node
|
from assets.models import Asset, Node
|
||||||
from assets.tasks import (
|
from assets.tasks import (
|
||||||
update_assets_hardware_info_util,
|
update_assets_hardware_info_util,
|
||||||
test_asset_connectivity_util,
|
test_asset_connectivity_util,
|
||||||
push_system_user_to_assets,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
@ -77,15 +76,15 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
|
||||||
nodes_ancestors_keys.update(Node.get_node_ancestor_keys(node, with_self=True))
|
nodes_ancestors_keys.update(Node.get_node_ancestor_keys(node, with_self=True))
|
||||||
|
|
||||||
# 查询所有祖先节点关联的系统用户,都是要跟资产建立关系的
|
# 查询所有祖先节点关联的系统用户,都是要跟资产建立关系的
|
||||||
system_user_ids = SystemUser.objects.filter(
|
# system_user_ids = SystemUser.objects.filter(
|
||||||
nodes__key__in=nodes_ancestors_keys
|
# nodes__key__in=nodes_ancestors_keys
|
||||||
).distinct().values_list('id', flat=True)
|
# ).distinct().values_list('id', flat=True)
|
||||||
|
|
||||||
# 查询所有已存在的关系
|
# 查询所有已存在的关系
|
||||||
m2m_model = SystemUser.assets.through
|
# m2m_model = SystemUser.assets.through
|
||||||
exist = set(m2m_model.objects.filter(
|
# exist = set(m2m_model.objects.filter(
|
||||||
systemuser_id__in=system_user_ids, asset_id__in=asset_ids
|
# systemuser_id__in=system_user_ids, asset_id__in=asset_ids
|
||||||
).values_list('systemuser_id', 'asset_id'))
|
# ).values_list('systemuser_id', 'asset_id'))
|
||||||
# TODO 优化
|
# TODO 优化
|
||||||
# to_create = []
|
# to_create = []
|
||||||
# for system_user_id in system_user_ids:
|
# for system_user_id in system_user_ids:
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django.db.models.signals import (
|
|
||||||
post_save, m2m_changed, pre_save, pre_delete, post_delete
|
|
||||||
)
|
|
||||||
from django.dispatch import receiver
|
|
||||||
|
|
||||||
from common.exceptions import M2MReverseNotAllowed
|
|
||||||
from common.const.signals import POST_ADD
|
|
||||||
from common.utils import get_logger
|
|
||||||
from common.decorator import on_transaction_commit
|
|
||||||
from assets.models import Asset, SystemUser, Node
|
|
||||||
from users.models import User
|
|
||||||
from assets.tasks import (
|
|
||||||
push_system_user_to_assets_manual,
|
|
||||||
push_system_user_to_assets,
|
|
||||||
add_nodes_assets_to_system_users
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=SystemUser.assets.through)
|
|
||||||
@on_transaction_commit
|
|
||||||
def on_system_user_assets_change(instance, action, model, pk_set, **kwargs):
|
|
||||||
"""
|
|
||||||
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
|
|
||||||
"""
|
|
||||||
logger.debug("System user assets change signal recv: {}".format(instance))
|
|
||||||
|
|
||||||
if not instance:
|
|
||||||
logger.debug('No system user found')
|
|
||||||
return
|
|
||||||
|
|
||||||
if model == Asset:
|
|
||||||
system_user_ids = [instance.id]
|
|
||||||
asset_ids = pk_set
|
|
||||||
else:
|
|
||||||
system_user_ids = pk_set
|
|
||||||
asset_ids = [instance.id]
|
|
||||||
# todo: Auto create account if need
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=SystemUser.users.through)
|
|
||||||
@on_transaction_commit
|
|
||||||
def on_system_user_users_change(sender, instance: SystemUser, action, model, pk_set, reverse, **kwargs):
|
|
||||||
"""
|
|
||||||
当系统用户和用户关系发生变化时,应该重新推送系统用户资产中
|
|
||||||
"""
|
|
||||||
if action != POST_ADD:
|
|
||||||
return
|
|
||||||
|
|
||||||
if reverse:
|
|
||||||
raise M2MReverseNotAllowed
|
|
||||||
|
|
||||||
if not instance.username_same_with_user:
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.debug("System user users change signal recv: {}".format(instance))
|
|
||||||
usernames = model.objects.filter(pk__in=pk_set).values_list('username', flat=True)
|
|
||||||
|
|
||||||
for username in usernames:
|
|
||||||
push_system_user_to_assets_manual.delay(instance, username)
|
|
||||||
|
|
||||||
|
|
||||||
@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):
|
|
||||||
"""
|
|
||||||
当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上
|
|
||||||
"""
|
|
||||||
if action != POST_ADD:
|
|
||||||
return
|
|
||||||
logger.info("System user nodes update signal recv: {}".format(instance))
|
|
||||||
|
|
||||||
queryset = model.objects.filter(pk__in=pk_set)
|
|
||||||
if model == Node:
|
|
||||||
nodes_keys = queryset.values_list('key', flat=True)
|
|
||||||
system_users = [instance]
|
|
||||||
else:
|
|
||||||
nodes_keys = [instance.key]
|
|
||||||
system_users = queryset
|
|
||||||
add_nodes_assets_to_system_users.delay(nodes_keys, system_users)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(m2m_changed, sender=SystemUser.groups.through)
|
|
||||||
def on_system_user_groups_change(instance, action, pk_set, reverse, **kwargs):
|
|
||||||
"""
|
|
||||||
当系统用户和用户组关系发生变化时,应该将组下用户关联到新的系统用户上
|
|
||||||
"""
|
|
||||||
if action != POST_ADD:
|
|
||||||
return
|
|
||||||
if reverse:
|
|
||||||
raise M2MReverseNotAllowed
|
|
||||||
logger.info("System user groups update signal recv: {}".format(instance))
|
|
||||||
|
|
||||||
users = User.objects.filter(groups__id__in=pk_set).distinct()
|
|
||||||
instance.users.add(*users)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=SystemUser, dispatch_uid="jms")
|
|
||||||
@on_transaction_commit
|
|
||||||
def on_system_user_update(instance: SystemUser, created, **kwargs):
|
|
||||||
"""
|
|
||||||
当系统用户更新时,可能更新了密钥,用户名等,这时要自动推送系统用户到资产上,
|
|
||||||
其实应该当 用户名,密码,密钥 sudo等更新时再推送,这里偷个懒,
|
|
||||||
这里直接取了 instance.assets 因为nodes和系统用户发生变化时,会自动将nodes下的资产
|
|
||||||
关联到上面
|
|
||||||
"""
|
|
||||||
if instance and not created:
|
|
||||||
logger.info("System user update signal recv: {}".format(instance))
|
|
||||||
assets = instance.assets.all().valid()
|
|
||||||
push_system_user_to_assets.delay(instance.id, [_asset.id for _asset in assets])
|
|
||||||
# add assets to su_from
|
|
||||||
instance.add_related_assets_to_su_from_if_need(assets)
|
|
|
@ -6,7 +6,5 @@ from .asset_connectivity import *
|
||||||
from .account_connectivity import *
|
from .account_connectivity import *
|
||||||
from .gather_asset_users import *
|
from .gather_asset_users import *
|
||||||
from .gather_asset_hardware_info import *
|
from .gather_asset_hardware_info import *
|
||||||
from .push_system_user import *
|
|
||||||
from .system_user_connectivity import *
|
|
||||||
from .nodes_amount import *
|
from .nodes_amount import *
|
||||||
from .backup import *
|
from .backup import *
|
||||||
|
|
|
@ -1,38 +1,2 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from celery import shared_task
|
|
||||||
|
|
||||||
from orgs.utils import tmp_to_root_org
|
|
||||||
from assets.models import AuthBook
|
|
||||||
|
|
||||||
__all__ = ['add_nodes_assets_to_system_users']
|
|
||||||
|
|
||||||
|
|
||||||
# Todo: 等待优化
|
|
||||||
@shared_task
|
|
||||||
@tmp_to_root_org()
|
|
||||||
def add_nodes_assets_to_system_users(nodes_keys, system_users):
|
|
||||||
from ..models import Node
|
|
||||||
from assets.tasks import push_system_user_to_assets
|
|
||||||
|
|
||||||
nodes = Node.objects.filter(key__in=nodes_keys)
|
|
||||||
assets = Node.get_nodes_all_assets(*nodes)
|
|
||||||
|
|
||||||
for system_user in system_users:
|
|
||||||
""" 解决资产和节点进行关联时,已经关联过的节点不会触发 authbook post_save 信号,
|
|
||||||
无法更新节点下所有资产的管理用户的问题 """
|
|
||||||
need_push_asset_ids = []
|
|
||||||
for asset in assets:
|
|
||||||
defaults = {'asset': asset, 'systemuser': system_user, 'org_id': asset.org_id}
|
|
||||||
instance, created = AuthBook.objects.update_or_create(
|
|
||||||
defaults=defaults, asset=asset, systemuser=system_user
|
|
||||||
)
|
|
||||||
if created:
|
|
||||||
need_push_asset_ids.append(asset.id)
|
|
||||||
# 不再自动更新资产管理用户,只允许用户手动指定。
|
|
||||||
# 只要关联都需要更新资产的管理用户
|
|
||||||
# instance.update_asset_admin_user_if_need()
|
|
||||||
|
|
||||||
if need_push_asset_ids:
|
|
||||||
push_system_user_to_assets.delay(system_user.id, need_push_asset_ids)
|
|
||||||
|
|
|
@ -1,307 +0,0 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
|
|
||||||
from itertools import groupby
|
|
||||||
from celery import shared_task
|
|
||||||
from common.db.utils import get_object_if_need, get_objects
|
|
||||||
from django.utils.translation import ugettext as _, gettext_noop
|
|
||||||
from django.db.models import Empty
|
|
||||||
|
|
||||||
from common.utils import encrypt_password, get_logger
|
|
||||||
from assets.models import SystemUser, Asset
|
|
||||||
from orgs.utils import org_aware_func, tmp_to_root_org
|
|
||||||
from . import const
|
|
||||||
from .utils import clean_ansible_task_hosts, group_asset_by_platform
|
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
__all__ = [
|
|
||||||
'push_system_user_util', 'push_system_user_to_assets',
|
|
||||||
'push_system_user_to_assets_manual', 'push_system_user_a_asset_manual',
|
|
||||||
'push_system_users_a_asset'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _split_by_comma(raw: str):
|
|
||||||
try:
|
|
||||||
return [i.strip() for i in raw.split(',')]
|
|
||||||
except AttributeError:
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def _dump_args(args: dict):
|
|
||||||
return ' '.join([f'{k}={v}' for k, v in args.items() if v is not Empty])
|
|
||||||
|
|
||||||
|
|
||||||
def get_push_unixlike_system_user_tasks(system_user, username=None, **kwargs):
|
|
||||||
algorithm = kwargs.get('algorithm')
|
|
||||||
if username is None:
|
|
||||||
username = system_user.username
|
|
||||||
|
|
||||||
comment = system_user.name
|
|
||||||
if system_user.username_same_with_user:
|
|
||||||
from users.models import User
|
|
||||||
user = User.objects.filter(username=username).only('name', 'username').first()
|
|
||||||
if user:
|
|
||||||
comment = f'{system_user.name}[{str(user)}]'
|
|
||||||
comment = comment.replace(' ', '')
|
|
||||||
|
|
||||||
password = system_user.password
|
|
||||||
public_key = system_user.public_key
|
|
||||||
|
|
||||||
groups = _split_by_comma(system_user.system_groups)
|
|
||||||
|
|
||||||
if groups:
|
|
||||||
groups = '"%s"' % ','.join(groups)
|
|
||||||
|
|
||||||
add_user_args = {
|
|
||||||
'name': username,
|
|
||||||
'shell': system_user.shell or Empty,
|
|
||||||
'state': 'present',
|
|
||||||
'home': system_user.home or Empty,
|
|
||||||
'expires': -1,
|
|
||||||
'groups': groups or Empty,
|
|
||||||
'comment': comment
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks = [
|
|
||||||
{
|
|
||||||
'name': 'Add user {}'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'user',
|
|
||||||
'args': _dump_args(add_user_args),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Add group {}'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'group',
|
|
||||||
'args': 'name={} state=present'.format(username),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
if not system_user.home:
|
|
||||||
tasks.extend([
|
|
||||||
{
|
|
||||||
'name': 'Check home dir exists',
|
|
||||||
'action': {
|
|
||||||
'module': 'stat',
|
|
||||||
'args': 'path=/home/{}'.format(username)
|
|
||||||
},
|
|
||||||
'register': 'home_existed'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': "Set home dir permission",
|
|
||||||
'action': {
|
|
||||||
'module': 'file',
|
|
||||||
'args': "path=/home/{0} owner={0} group={0} mode=700".format(username)
|
|
||||||
},
|
|
||||||
'when': 'home_existed.stat.exists == true'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
if password:
|
|
||||||
tasks.append({
|
|
||||||
'name': 'Set {} password'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'user',
|
|
||||||
'args': 'name={} shell={} state=present password={}'.format(
|
|
||||||
username, system_user.shell,
|
|
||||||
encrypt_password(password, salt="K3mIlKK", algorithm=algorithm),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if public_key:
|
|
||||||
tasks.append({
|
|
||||||
'name': 'Set {} authorized key'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'authorized_key',
|
|
||||||
'args': "user={} state=present key='{}'".format(
|
|
||||||
username, public_key
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if system_user.sudo:
|
|
||||||
sudo = system_user.sudo.replace('\r\n', '\n').replace('\r', '\n')
|
|
||||||
sudo_list = sudo.split('\n')
|
|
||||||
sudo_tmp = []
|
|
||||||
for s in sudo_list:
|
|
||||||
sudo_tmp.append(s.strip(','))
|
|
||||||
sudo = ','.join(sudo_tmp)
|
|
||||||
tasks.append({
|
|
||||||
'name': 'Set {} sudo setting'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'lineinfile',
|
|
||||||
'args': "dest=/etc/sudoers state=present regexp='^{0} ALL=' "
|
|
||||||
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
|
|
||||||
"validate='visudo -cf %s'".format(username, sudo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
|
|
||||||
def get_push_windows_system_user_tasks(system_user: SystemUser, username=None, **kwargs):
|
|
||||||
if username is None:
|
|
||||||
username = system_user.username
|
|
||||||
password = system_user.password
|
|
||||||
groups = {'Users', 'Remote Desktop Users'}
|
|
||||||
if system_user.system_groups:
|
|
||||||
groups.update(_split_by_comma(system_user.system_groups))
|
|
||||||
groups = ','.join(groups)
|
|
||||||
|
|
||||||
tasks = []
|
|
||||||
if not password:
|
|
||||||
logger.error("Error: no password found")
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
if system_user.ad_domain:
|
|
||||||
logger.error('System user with AD domain do not support push.')
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
task = {
|
|
||||||
'name': 'Add user {}'.format(username),
|
|
||||||
'action': {
|
|
||||||
'module': 'win_user',
|
|
||||||
'args': 'fullname={} '
|
|
||||||
'name={} '
|
|
||||||
'password={} '
|
|
||||||
'state=present '
|
|
||||||
'update_password=always '
|
|
||||||
'password_expired=no '
|
|
||||||
'password_never_expires=yes '
|
|
||||||
'groups="{}" '
|
|
||||||
'groups_action=add '
|
|
||||||
''.format(username, username, password, groups),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks.append(task)
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
|
|
||||||
def get_push_system_user_tasks(system_user, platform="unixlike", username=None, algorithm=None):
|
|
||||||
"""
|
|
||||||
获取推送系统用户的 ansible 命令,跟资产无关
|
|
||||||
:param system_user:
|
|
||||||
:param platform:
|
|
||||||
:param username: 当动态时,近推送某个
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
get_task_map = {
|
|
||||||
"unixlike": get_push_unixlike_system_user_tasks,
|
|
||||||
"windows": get_push_windows_system_user_tasks,
|
|
||||||
}
|
|
||||||
get_tasks = get_task_map.get(platform, get_push_unixlike_system_user_tasks)
|
|
||||||
if not system_user.username_same_with_user:
|
|
||||||
return get_tasks(system_user, algorithm=algorithm)
|
|
||||||
tasks = []
|
|
||||||
# 仅推送这个username
|
|
||||||
if username is not None:
|
|
||||||
tasks.extend(get_tasks(system_user, username, algorithm=algorithm))
|
|
||||||
return tasks
|
|
||||||
users = system_user.users.all().values_list('username', flat=True)
|
|
||||||
print(_("System user is dynamic: {}").format(list(users)))
|
|
||||||
for _username in users:
|
|
||||||
tasks.extend(get_tasks(system_user, _username, algorithm=algorithm))
|
|
||||||
return tasks
|
|
||||||
|
|
||||||
|
|
||||||
@org_aware_func("system_user")
|
|
||||||
def push_system_user_util(system_user, assets, task_name, username=None):
|
|
||||||
from ops.utils import update_or_create_ansible_task
|
|
||||||
assets = clean_ansible_task_hosts(assets, system_user=system_user)
|
|
||||||
if not assets:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
# 资产按平台分类
|
|
||||||
assets_sorted = sorted(assets, key=group_asset_by_platform)
|
|
||||||
platform_hosts = groupby(assets_sorted, key=group_asset_by_platform)
|
|
||||||
|
|
||||||
if system_user.username_same_with_user:
|
|
||||||
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]
|
|
||||||
|
|
||||||
def run_task(_tasks, _hosts):
|
|
||||||
if not _tasks:
|
|
||||||
return
|
|
||||||
task, created = update_or_create_ansible_task(
|
|
||||||
task_name=task_name, hosts=_hosts, tasks=_tasks, pattern='all',
|
|
||||||
options=const.TASK_OPTIONS, run_as_admin=True,
|
|
||||||
)
|
|
||||||
task.run()
|
|
||||||
|
|
||||||
for platform, _assets in platform_hosts:
|
|
||||||
_assets = list(_assets)
|
|
||||||
if not _assets:
|
|
||||||
continue
|
|
||||||
print(_("Start push system user for platform: [{}]").format(platform))
|
|
||||||
print(_("Hosts count: {}").format(len(_assets)))
|
|
||||||
|
|
||||||
for u in usernames:
|
|
||||||
for a in _assets:
|
|
||||||
system_user.load_asset_special_auth(a, u)
|
|
||||||
algorithm = 'des' if a.platform.name == 'AIX' else 'sha512'
|
|
||||||
tasks = get_push_system_user_tasks(
|
|
||||||
system_user, platform, username=u,
|
|
||||||
algorithm=algorithm
|
|
||||||
)
|
|
||||||
run_task(tasks, [a])
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@tmp_to_root_org()
|
|
||||||
def push_system_user_to_assets_manual(system_user, username=None):
|
|
||||||
"""
|
|
||||||
将系统用户推送到与它关联的所有资产上
|
|
||||||
"""
|
|
||||||
system_user = get_object_if_need(SystemUser, system_user)
|
|
||||||
assets = system_user.get_related_assets()
|
|
||||||
task_name = gettext_noop("Push system users to assets: ") + system_user.name
|
|
||||||
return push_system_user_util(system_user, assets, task_name=task_name, username=username)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@tmp_to_root_org()
|
|
||||||
def push_system_user_a_asset_manual(system_user, asset, username=None):
|
|
||||||
"""
|
|
||||||
将系统用户推送到一个资产上
|
|
||||||
"""
|
|
||||||
# if username is None:
|
|
||||||
# username = system_user.username
|
|
||||||
task_name = gettext_noop("Push system users to asset: ") + "{}({}) => {}".format(
|
|
||||||
system_user.name, username or system_user.username, asset
|
|
||||||
)
|
|
||||||
return push_system_user_util(system_user, [asset], task_name=task_name, username=username)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@tmp_to_root_org()
|
|
||||||
def push_system_users_a_asset(system_users, asset):
|
|
||||||
for system_user in system_users:
|
|
||||||
push_system_user_a_asset_manual(system_user, asset)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@tmp_to_root_org()
|
|
||||||
def push_system_user_to_assets(system_user_id, asset_ids, username=None):
|
|
||||||
"""
|
|
||||||
推送系统用户到指定的若干资产上
|
|
||||||
"""
|
|
||||||
system_user = SystemUser.objects.get(id=system_user_id)
|
|
||||||
assets = get_objects(Asset, asset_ids)
|
|
||||||
task_name = gettext_noop("Push system users to assets: ") + system_user.name
|
|
||||||
|
|
||||||
return push_system_user_util(system_user, assets, task_name, username=username)
|
|
||||||
|
|
||||||
# @shared_task
|
|
||||||
# @register_as_period_task(interval=3600)
|
|
||||||
# @after_app_ready_start
|
|
||||||
# @after_app_shutdown_clean_periodic
|
|
||||||
# def push_system_user_period():
|
|
||||||
# for system_user in SystemUser.objects.all():
|
|
||||||
# push_system_user_related_nodes(system_user)
|
|
|
@ -1,151 +0,0 @@
|
||||||
|
|
||||||
from itertools import groupby
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
from celery import shared_task
|
|
||||||
from django.utils.translation import ugettext as _, gettext_noop
|
|
||||||
|
|
||||||
from assets.models import Asset
|
|
||||||
from common.utils import get_logger
|
|
||||||
from orgs.utils import tmp_to_org, org_aware_func
|
|
||||||
from ..models import SystemUser, Connectivity, Account
|
|
||||||
from . import const
|
|
||||||
from .utils import (
|
|
||||||
clean_ansible_task_hosts, group_asset_by_platform
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
__all__ = [
|
|
||||||
'test_system_user_connectivity_util', 'test_system_user_connectivity_manual',
|
|
||||||
'test_system_user_connectivity_period', 'test_system_user_connectivity_a_asset',
|
|
||||||
'test_system_users_connectivity_a_asset'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def set_assets_accounts_connectivity(system_user, assets, results_summary):
|
|
||||||
asset_ids_ok = set()
|
|
||||||
asset_ids_failed = set()
|
|
||||||
|
|
||||||
asset_hostnames_ok = results_summary.get('contacted', {}).keys()
|
|
||||||
|
|
||||||
for asset in assets:
|
|
||||||
if asset.hostname in asset_hostnames_ok:
|
|
||||||
asset_ids_ok.add(asset.id)
|
|
||||||
else:
|
|
||||||
asset_ids_failed.add(asset.id)
|
|
||||||
|
|
||||||
accounts_ok = Account.objects.filter(asset_id__in=asset_ids_ok, systemuser=system_user)
|
|
||||||
accounts_failed = Account.objects.filter(asset_id__in=asset_ids_failed, systemuser=system_user)
|
|
||||||
|
|
||||||
Account.bulk_set_connectivity(accounts_ok, Connectivity.ok)
|
|
||||||
Account.bulk_set_connectivity(accounts_failed, Connectivity.failed)
|
|
||||||
|
|
||||||
|
|
||||||
@org_aware_func("system_user")
|
|
||||||
def test_system_user_connectivity_util(system_user, assets, task_name):
|
|
||||||
"""
|
|
||||||
Test system cant connect his assets or not.
|
|
||||||
:param system_user:
|
|
||||||
:param assets:
|
|
||||||
:param task_name:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
from ops.utils import update_or_create_ansible_task
|
|
||||||
|
|
||||||
if system_user.username_same_with_user:
|
|
||||||
logger.error(_("Dynamic system user not support test"))
|
|
||||||
return
|
|
||||||
|
|
||||||
# hosts = clean_ansible_task_hosts(assets, system_user=system_user)
|
|
||||||
# TODO: 这里不传递系统用户,因为clean_ansible_task_hosts会通过system_user来判断是否可以推送,
|
|
||||||
# 不符合测试可连接性逻辑, 后面需要优化此逻辑
|
|
||||||
hosts = clean_ansible_task_hosts(assets)
|
|
||||||
if not hosts:
|
|
||||||
return {}
|
|
||||||
platform_hosts_map = {}
|
|
||||||
hosts_sorted = sorted(hosts, 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])
|
|
||||||
|
|
||||||
platform_tasks_map = {
|
|
||||||
"unixlike": const.PING_UNIXLIKE_TASKS,
|
|
||||||
"windows": const.PING_WINDOWS_TASKS
|
|
||||||
}
|
|
||||||
|
|
||||||
results_summary = dict(
|
|
||||||
contacted=defaultdict(dict), dark=defaultdict(dict), success=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def run_task(_tasks, _hosts, _username):
|
|
||||||
old_name = "{}".format(system_user)
|
|
||||||
new_name = "{}({})".format(system_user.name, _username)
|
|
||||||
_task_name = task_name.replace(old_name, new_name)
|
|
||||||
_task, created = update_or_create_ansible_task(
|
|
||||||
task_name=_task_name, hosts=_hosts, tasks=_tasks,
|
|
||||||
pattern='all', options=const.TASK_OPTIONS,
|
|
||||||
run_as=_username, system_user=system_user
|
|
||||||
)
|
|
||||||
raw, summary = _task.run()
|
|
||||||
success = summary.get('success', False)
|
|
||||||
contacted = summary.get('contacted', {})
|
|
||||||
dark = summary.get('dark', {})
|
|
||||||
|
|
||||||
results_summary['success'] &= success
|
|
||||||
results_summary['contacted'].update(contacted)
|
|
||||||
results_summary['dark'].update(dark)
|
|
||||||
|
|
||||||
for platform, _hosts in platform_hosts_map.items():
|
|
||||||
if not _hosts:
|
|
||||||
continue
|
|
||||||
if platform not in ["unixlike", "windows"]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
tasks = platform_tasks_map[platform]
|
|
||||||
print(_("Start test system user connectivity for platform: [{}]").format(platform))
|
|
||||||
print(_("Hosts count: {}").format(len(_hosts)))
|
|
||||||
# 用户名不是动态的,用户名则是一个
|
|
||||||
logger.debug("System user not has special auth")
|
|
||||||
run_task(tasks, _hosts, system_user.username)
|
|
||||||
|
|
||||||
set_assets_accounts_connectivity(system_user, hosts, results_summary)
|
|
||||||
return results_summary
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@org_aware_func("system_user")
|
|
||||||
def test_system_user_connectivity_manual(system_user, asset_ids=None):
|
|
||||||
task_name = gettext_noop("Test system user connectivity: ") + str(system_user)
|
|
||||||
if asset_ids:
|
|
||||||
assets = Asset.objects.filter(id__in=asset_ids)
|
|
||||||
else:
|
|
||||||
assets = system_user.get_related_assets()
|
|
||||||
test_system_user_connectivity_util(system_user, assets, task_name)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
@org_aware_func("system_user")
|
|
||||||
def test_system_user_connectivity_a_asset(system_user, asset):
|
|
||||||
task_name = gettext_noop("Test system user connectivity: ") + "{} => {}".format(
|
|
||||||
system_user, asset
|
|
||||||
)
|
|
||||||
test_system_user_connectivity_util(system_user, [asset], task_name)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
def test_system_users_connectivity_a_asset(system_users, asset):
|
|
||||||
for system_user in system_users:
|
|
||||||
test_system_user_connectivity_a_asset(system_user, asset)
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task(queue="ansible")
|
|
||||||
def test_system_user_connectivity_period():
|
|
||||||
if not const.PERIOD_TASK_ENABLED:
|
|
||||||
logger.debug("Period task disabled, test system user connectivity pass")
|
|
||||||
return
|
|
||||||
queryset_map = SystemUser.objects.all_group_by_org()
|
|
||||||
for org, system_user in queryset_map.items():
|
|
||||||
task_name = gettext_noop("Test system user connectivity period: ") + str(system_user)
|
|
||||||
with tmp_to_org(org):
|
|
||||||
assets = system_user.get_related_assets()
|
|
||||||
test_system_user_connectivity_util(system_user, assets, task_name)
|
|
|
@ -18,24 +18,15 @@ router.register(r'account-secrets', api.AccountSecretsViewSet, 'account-secret')
|
||||||
router.register(r'accounts-history', api.AccountHistoryViewSet, 'account-history')
|
router.register(r'accounts-history', api.AccountHistoryViewSet, 'account-history')
|
||||||
router.register(r'account-history-secrets', api.AccountHistorySecretsViewSet, 'account-history-secret')
|
router.register(r'account-history-secrets', api.AccountHistorySecretsViewSet, 'account-history-secret')
|
||||||
router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
|
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'labels', api.LabelViewSet, 'label')
|
||||||
router.register(r'nodes', api.NodeViewSet, 'node')
|
router.register(r'nodes', api.NodeViewSet, 'node')
|
||||||
router.register(r'domains', api.DomainViewSet, 'domain')
|
router.register(r'domains', api.DomainViewSet, 'domain')
|
||||||
router.register(r'gateways', api.GatewayViewSet, 'gateway')
|
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'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
||||||
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
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-plans', api.AccountBackupPlanViewSet, 'account-backup')
|
||||||
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
|
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
|
||||||
|
|
||||||
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
|
|
||||||
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('assets/<uuid:pk>/gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'),
|
# path('assets/<uuid:pk>/gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'),
|
||||||
|
@ -46,15 +37,6 @@ 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/', 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('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>/auth-info/', api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'),
|
|
||||||
path('system-users/<uuid:pk>/assets/', api.SystemUserAssetsListView.as_view(), name='system-user-assets'),
|
|
||||||
path('system-users/<uuid:pk>/assets/<uuid:asset_id>/auth-info/', api.SystemUserAssetAuthInfoApi.as_view(), name='system-user-asset-auth-info'),
|
|
||||||
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>/temp-auth/', api.SystemUserTempAuthInfoApi.as_view(), name='system-user-asset-temp-info'),
|
|
||||||
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'),
|
|
||||||
|
|
||||||
path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'),
|
path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'),
|
||||||
|
|
||||||
path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
||||||
|
@ -69,14 +51,7 @@ urlpatterns = [
|
||||||
path('nodes/<uuid:pk>/tasks/', api.NodeTaskCreateApi.as_view(), name='node-task-create'),
|
path('nodes/<uuid:pk>/tasks/', api.NodeTaskCreateApi.as_view(), name='node-task-create'),
|
||||||
|
|
||||||
path('gateways/<uuid:pk>/test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
path('gateways/<uuid:pk>/test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
||||||
|
|
||||||
path('cmd-filters/command-confirm/', api.CommandConfirmAPI.as_view(), name='command-confirm'),
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
old_version_urlpatterns = [
|
urlpatterns += router.urls
|
||||||
re_path('(?P<resource>admin-user|system-user|domain|gateway|cmd-filter|asset-user)/.*', capi.redirect_plural_name_api)
|
|
||||||
]
|
|
||||||
|
|
||||||
urlpatterns += router.urls + cmd_filter_router.urls + old_version_urlpatterns
|
|
||||||
|
|
||||||
|
|
|
@ -69,11 +69,6 @@ M2M_NEED_RECORD = {
|
||||||
_('{User} JOINED {UserGroup}'),
|
_('{User} JOINED {UserGroup}'),
|
||||||
_('{User} LEFT {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: (
|
Asset.nodes.through._meta.object_name: (
|
||||||
_('Node and Asset'),
|
_('Node and Asset'),
|
||||||
_('{Node} ADD {Asset}'),
|
_('{Node} ADD {Asset}'),
|
||||||
|
@ -99,11 +94,6 @@ M2M_NEED_RECORD = {
|
||||||
_('{AssetPermission} ADD {Node}'),
|
_('{AssetPermission} ADD {Node}'),
|
||||||
_('{AssetPermission} REMOVE {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: (
|
ApplicationPermission.users.through._meta.object_name: (
|
||||||
_('User application permissions'),
|
_('User application permissions'),
|
||||||
_('{ApplicationPermission} ADD {User}'),
|
_('{ApplicationPermission} ADD {User}'),
|
||||||
|
|
|
@ -20,7 +20,6 @@ from common.decorator import on_transaction_commit
|
||||||
from common.signals import django_ready
|
from common.signals import django_ready
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.utils.connection import RedisPubSub
|
from common.utils.connection import RedisPubSub
|
||||||
from assets.models import CommandFilterRule
|
|
||||||
from users.signals import post_user_leave_org
|
from users.signals import post_user_leave_org
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,7 +140,7 @@ def _clear_users_from_org(org, users):
|
||||||
for m in models:
|
for m in models:
|
||||||
_remove_users(m, users, org)
|
_remove_users(m, users, org)
|
||||||
|
|
||||||
_remove_users(CommandFilterRule, users, org, user_field_name='reviewers')
|
# _remove_users(CommandFilterRule, users, org, user_field_name='reviewers')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
|
|
|
@ -62,20 +62,19 @@ class ValidateUserApplicationPermissionApi(APIView):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
user_id = request.query_params.get('user_id', '')
|
user_id = request.query_params.get('user_id', '')
|
||||||
application_id = request.query_params.get('application_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 = {
|
data = {
|
||||||
'has_permission': False,
|
'has_permission': False,
|
||||||
'expire_at': int(time.time()),
|
'expire_at': int(time.time()),
|
||||||
'actions': []
|
'actions': []
|
||||||
}
|
}
|
||||||
if not all((user_id, application_id, system_user_id)):
|
if not all((user_id, application_id, account)):
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
user = User.objects.get(id=user_id)
|
user = User.objects.get(id=user_id)
|
||||||
application = Application.objects.get(id=application_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, account)
|
||||||
has_perm, actions, expire_at = validate_permission(user, application, system_user)
|
|
||||||
status_code = status.HTTP_200_OK if has_perm else status.HTTP_403_FORBIDDEN
|
status_code = status.HTTP_200_OK if has_perm else status.HTTP_403_FORBIDDEN
|
||||||
data = {
|
data = {
|
||||||
'has_permission': has_perm,
|
'has_permission': has_perm,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from django.db.models import F, Value
|
from django.db.models import F
|
||||||
from django.db.models.functions import Concat
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from orgs.mixins.api import OrgRelationMixin
|
from orgs.mixins.api import OrgRelationMixin
|
||||||
|
@ -15,8 +14,7 @@ from perms.utils.asset.user_permission import UserGrantedAssetsQueryUtils
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetPermissionUserRelationViewSet', 'AssetPermissionUserGroupRelationViewSet',
|
'AssetPermissionUserRelationViewSet', 'AssetPermissionUserGroupRelationViewSet',
|
||||||
'AssetPermissionAssetRelationViewSet', 'AssetPermissionNodeRelationViewSet',
|
'AssetPermissionAssetRelationViewSet', 'AssetPermissionNodeRelationViewSet',
|
||||||
'AssetPermissionSystemUserRelationViewSet', 'AssetPermissionAllAssetListApi',
|
'AssetPermissionAllAssetListApi', 'AssetPermissionAllUserListApi',
|
||||||
'AssetPermissionAllUserListApi',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,21 +115,3 @@ class AssetPermissionNodeRelationViewSet(RelationMixin):
|
||||||
.annotate(node_key=F('node__key'))
|
.annotate(node_key=F('node__key'))
|
||||||
return queryset
|
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
|
|
||||||
|
|
|
@ -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',
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,7 +7,7 @@ from django.db import models
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
from common.db.models import BaseCreateUpdateModel
|
from common.db.models import BaseCreateUpdateModel
|
||||||
from assets.models import Asset, SystemUser, Node, FamilyMixin
|
from assets.models import Asset, Node, FamilyMixin
|
||||||
|
|
||||||
from .base import BasePermission
|
from .base import BasePermission
|
||||||
|
|
||||||
|
@ -23,14 +23,13 @@ logger = logging.getLogger(__name__)
|
||||||
class AssetPermission(BasePermission):
|
class AssetPermission(BasePermission):
|
||||||
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
|
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"))
|
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:
|
class Meta:
|
||||||
unique_together = [('org_id', 'name')]
|
unique_together = [('org_id', 'name')]
|
||||||
verbose_name = _("Asset permission")
|
verbose_name = _("Asset permission")
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
permissions = [
|
permissions = []
|
||||||
]
|
|
||||||
|
|
||||||
@lazyproperty
|
@lazyproperty
|
||||||
def users_amount(self):
|
def users_amount(self):
|
||||||
|
@ -48,16 +47,11 @@ class AssetPermission(BasePermission):
|
||||||
def nodes_amount(self):
|
def nodes_amount(self):
|
||||||
return self.nodes.count()
|
return self.nodes.count()
|
||||||
|
|
||||||
@lazyproperty
|
|
||||||
def system_users_amount(self):
|
|
||||||
return self.system_users.count()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset_with_prefetch(cls):
|
def get_queryset_with_prefetch(cls):
|
||||||
return cls.objects.all().valid().prefetch_related(
|
return cls.objects.all().valid().prefetch_related(
|
||||||
models.Prefetch('nodes', queryset=Node.objects.all().only('key')),
|
models.Prefetch('nodes', queryset=Node.objects.all().only('key')),
|
||||||
models.Prefetch('assets', queryset=Asset.objects.all().only('id')),
|
models.Prefetch('assets', queryset=Asset.objects.all().only('id')),
|
||||||
models.Prefetch('system_users', queryset=SystemUser.objects.all().only('id'))
|
|
||||||
).order_by()
|
).order_by()
|
||||||
|
|
||||||
def get_all_assets(self):
|
def get_all_assets(self):
|
||||||
|
@ -81,10 +75,6 @@ class AssetPermission(BasePermission):
|
||||||
names = [asset.hostname for asset in self.assets.all()]
|
names = [asset.hostname for asset in self.assets.all()]
|
||||||
return names
|
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):
|
def nodes_display(self):
|
||||||
names = [node.full_value for node in self.nodes.all()]
|
names = [node.full_value for node in self.nodes.all()]
|
||||||
return names
|
return names
|
||||||
|
|
|
@ -22,7 +22,6 @@ class AssetPermissionSerializer(BasePermissionSerializer):
|
||||||
user_groups_display = serializers.ListField(child=serializers.CharField(), label=_('User groups display'), required=False)
|
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)
|
assets_display = serializers.ListField(child=serializers.CharField(), label=_('Assets display'), required=False)
|
||||||
nodes_display = serializers.ListField(child=serializers.CharField(), label=_('Nodes 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:
|
class Meta:
|
||||||
model = AssetPermission
|
model = AssetPermission
|
||||||
|
@ -34,9 +33,9 @@ class AssetPermissionSerializer(BasePermissionSerializer):
|
||||||
]
|
]
|
||||||
fields_m2m = [
|
fields_m2m = [
|
||||||
'users', 'users_display', 'user_groups', 'user_groups_display', 'assets',
|
'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',
|
'users_amount', 'user_groups_amount', 'assets_amount',
|
||||||
'nodes_amount', 'system_users_amount',
|
'nodes_amount',
|
||||||
]
|
]
|
||||||
fields = fields_small + fields_m2m
|
fields = fields_small + fields_m2m
|
||||||
read_only_fields = ['created_by', 'date_created', 'from_ticket']
|
read_only_fields = ['created_by', 'date_created', 'from_ticket']
|
||||||
|
@ -48,30 +47,16 @@ class AssetPermissionSerializer(BasePermissionSerializer):
|
||||||
'user_groups_amount': {'label': _('User groups amount')},
|
'user_groups_amount': {'label': _('User groups amount')},
|
||||||
'assets_amount': {'label': _('Assets amount')},
|
'assets_amount': {'label': _('Assets amount')},
|
||||||
'nodes_amount': {'label': _('Nodes amount')},
|
'nodes_amount': {'label': _('Nodes amount')},
|
||||||
'system_users_amount': {'label': _('System users amount')},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" Perform necessary eager loading of data. """
|
||||||
queryset = queryset.prefetch_related(
|
queryset = queryset.prefetch_related(
|
||||||
'users', 'user_groups', 'assets', 'nodes', 'system_users'
|
'users', 'user_groups', 'assets', 'nodes',
|
||||||
)
|
)
|
||||||
return queryset
|
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
|
@staticmethod
|
||||||
def perform_display_create(instance, **kwargs):
|
def perform_display_create(instance, **kwargs):
|
||||||
# 用户
|
# 用户
|
||||||
|
|
|
@ -12,7 +12,6 @@ __all__ = [
|
||||||
'AssetPermissionUserGroupRelationSerializer',
|
'AssetPermissionUserGroupRelationSerializer',
|
||||||
"AssetPermissionAssetRelationSerializer",
|
"AssetPermissionAssetRelationSerializer",
|
||||||
'AssetPermissionNodeRelationSerializer',
|
'AssetPermissionNodeRelationSerializer',
|
||||||
'AssetPermissionSystemUserRelationSerializer',
|
|
||||||
'AssetPermissionAllAssetSerializer',
|
'AssetPermissionAllAssetSerializer',
|
||||||
'AssetPermissionAllUserSerializer',
|
'AssetPermissionAllUserSerializer',
|
||||||
]
|
]
|
||||||
|
@ -99,13 +98,3 @@ class AssetPermissionNodeRelationSerializer(RelationMixin, serializers.ModelSeri
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'node', "node_display",
|
'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'
|
|
||||||
]
|
|
||||||
|
|
|
@ -1,138 +1,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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-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-assets-relations', api.AssetPermissionAssetRelationViewSet, 'asset-permissions-assets-relation')
|
||||||
router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRelationViewSet, 'asset-permissions-nodes-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 = [
|
user_permission_urlpatterns = [
|
||||||
# 统一说明:
|
# 统一说明:
|
||||||
|
|
|
@ -11,11 +11,7 @@ from perms.utils.asset.user_permission import get_user_all_asset_perm_ids
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
def validate_permission(user, asset, system_user, action='connect'):
|
def validate_permission(user, asset, account, action='connect'):
|
||||||
|
|
||||||
if not system_user.protocol in asset.protocols_as_dict.keys():
|
|
||||||
return False, time.time()
|
|
||||||
|
|
||||||
asset_perm_ids = get_user_all_asset_perm_ids(user)
|
asset_perm_ids = get_user_all_asset_perm_ids(user)
|
||||||
|
|
||||||
asset_perm_ids_from_asset = AssetPermission.assets.through.objects.filter(
|
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:
|
for node in nodes:
|
||||||
ancestor_keys = node.get_ancestor_keys(with_self=True)
|
ancestor_keys = node.get_ancestor_keys(with_self=True)
|
||||||
node_keys.update(ancestor_keys)
|
node_keys.update(ancestor_keys)
|
||||||
node_ids = Node.objects.filter(key__in=node_keys).values_list('id', flat=True)
|
node_ids = set(Node.objects.filter(key__in=node_keys).values_list('id', flat=True))
|
||||||
|
|
||||||
node_ids = set(node_ids)
|
|
||||||
|
|
||||||
asset_perm_ids_from_node = AssetPermission.nodes.through.objects.filter(
|
asset_perm_ids_from_node = AssetPermission.nodes.through.objects.filter(
|
||||||
assetpermission_id__in=asset_perm_ids,
|
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 = {*asset_perm_ids_from_asset, *asset_perm_ids_from_node}
|
||||||
|
|
||||||
asset_perm_ids = AssetPermission.system_users.through.objects.filter(
|
asset_perms = AssetPermission.objects\
|
||||||
assetpermission_id__in=asset_perm_ids,
|
.filter(id__in=asset_perm_ids, accounts__contains=account)\
|
||||||
systemuser_id=system_user.id
|
.order_by('-date_expired')
|
||||||
).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')
|
|
||||||
|
|
||||||
if asset_perms:
|
if asset_perms:
|
||||||
actions = set()
|
actions = set()
|
||||||
|
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -47,12 +47,6 @@ class AssetPermissionGenerator(FakeDataGenerator):
|
||||||
relation_name = 'node_id'
|
relation_name = 'node_id'
|
||||||
self.set_relations(perms, through, relation_name, choices)
|
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):
|
def set_relations(self, perms, through, relation_name, choices, choice_count=None):
|
||||||
relations = []
|
relations = []
|
||||||
|
|
||||||
|
@ -79,4 +73,3 @@ class AssetPermissionGenerator(FakeDataGenerator):
|
||||||
self.set_user_groups(created)
|
self.set_user_groups(created)
|
||||||
self.set_assets(created)
|
self.set_assets(created)
|
||||||
self.set_nodes(created)
|
self.set_nodes(created)
|
||||||
self.set_system_users(created)
|
|
||||||
|
|
Loading…
Reference in New Issue