perf: add check account

pull/14345/head
ibuler 2024-10-22 17:30:20 +08:00
parent 4c3cfcbf23
commit 90cbb4be23
13 changed files with 286 additions and 5 deletions

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
#
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.models import AccountCheckAutomation
from accounts.models import AccountRisk
from orgs.mixins.api import OrgBulkModelViewSet
from .base import AutomationExecutionViewSet
__all__ = [
'CheckAccountsAutomationViewSet', 'CheckAccountExecutionViewSet',
'AccountRiskViewSet'
]
class CheckAccountsAutomationViewSet(OrgBulkModelViewSet):
model = AccountCheckAutomation
filterset_fields = ('name',)
search_fields = filterset_fields
serializer_class = serializers.CheckAccountsAutomationSerializer
class CheckAccountExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_gatheraccountsexecution"),
("retrieve", "accounts.view_gatheraccountsexecution"),
("create", "accounts.add_gatheraccountsexecution"),
)
tp = AutomationTypes.gather_accounts
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(automation__type=self.tp)
return queryset
class AccountRiskViewSet(OrgBulkModelViewSet):
model = AccountRisk
search_fields = ('username',)
filterset_class = AccountRiskFilterSet
serializer_classes = {
'default': serializers.AccountRiskSerializer,
}
rbac_perms = {
'sync_accounts': 'assets.add_AccountRisk',
}
@action(methods=['post'], detail=False, url_path='sync-accounts')
def sync_accounts(self, request, *args, **kwargs):
gathered_account_ids = request.data.get('gathered_account_ids')
gathered_accounts = self.model.objects.filter(id__in=gathered_account_ids)
self.model.sync_accounts(gathered_accounts)
return Response(status=status.HTTP_201_CREATED)

View File

@ -27,18 +27,21 @@ class AutomationTypes(models.TextChoices):
remove_account = 'remove_account', _('Remove account')
gather_accounts = 'gather_accounts', _('Gather accounts')
verify_gateway_account = 'verify_gateway_account', _('Verify gateway account')
check_account = 'check_account', _('Check account')
@classmethod
def get_type_model(cls, tp):
from accounts.models import (
PushAccountAutomation, ChangeSecretAutomation,
VerifyAccountAutomation, GatherAccountsAutomation,
AccountCheckAutomation,
)
type_model_dict = {
cls.push_account: PushAccountAutomation,
cls.change_secret: ChangeSecretAutomation,
cls.verify_account: VerifyAccountAutomation,
cls.gather_accounts: GatherAccountsAutomation,
cls.check_account: AccountCheckAutomation,
}
return type_model_dict.get(tp)

View File

@ -25,4 +25,11 @@ class Migration(migrations.Migration):
verbose_name="Start Datetime",
),
),
migrations.AddField(
model_name="account",
name="date_last_access",
field=models.DateTimeField(
blank=True, null=True, verbose_name="Date last access"
),
),
]

View File

@ -0,0 +1,112 @@
# Generated by Django 4.1.13 on 2024-10-22 06:36
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
("assets", "0006_baseautomation_start_time"),
("accounts", "0005_account_secret_reset"),
]
operations = [
migrations.CreateModel(
name="AccountCheckAutomation",
fields=[
(
"baseautomation_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="assets.baseautomation",
),
),
],
options={
"verbose_name": "Gather account automation",
},
bases=("accounts.accountbaseautomation",),
),
migrations.CreateModel(
name="AccountRisk",
fields=[
(
"created_by",
models.CharField(
blank=True, max_length=128, null=True, verbose_name="Created by"
),
),
(
"updated_by",
models.CharField(
blank=True, max_length=128, null=True, verbose_name="Updated by"
),
),
(
"date_created",
models.DateTimeField(
auto_now_add=True, null=True, verbose_name="Date created"
),
),
(
"date_updated",
models.DateTimeField(auto_now=True, verbose_name="Date updated"),
),
(
"comment",
models.TextField(blank=True, default="", verbose_name="Comment"),
),
(
"id",
models.UUIDField(
default=uuid.uuid4, primary_key=True, serialize=False
),
),
(
"org_id",
models.CharField(
blank=True,
db_index=True,
default="",
max_length=36,
verbose_name="Organization",
),
),
(
"risk",
models.CharField(
choices=[
("zombie", "Zombie"),
("ghost", "Ghost"),
("weak_password", "Weak password"),
("long_time_no_change", "Long time no change"),
],
max_length=128,
verbose_name="Risk",
),
),
(
"confirmed",
models.BooleanField(default=False, verbose_name="Confirmed"),
),
(
"account",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="risks",
to="accounts.account",
verbose_name="Account",
),
),
],
options={
"abstract": False,
},
),
]

View File

@ -3,4 +3,5 @@ from .backup_account import *
from .change_secret import *
from .gather_account import *
from .push_account import *
from .scan_account import *
from .verify_account import *

View File

@ -0,0 +1,47 @@
from django.db import models
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
from common.const import Trigger
from orgs.mixins.models import JMSOrgBaseModel
from .base import AccountBaseAutomation
from ...const import AutomationTypes
__all__ = ['AccountCheckAutomation', 'AccountRisk']
class AccountCheckAutomation(AccountBaseAutomation):
def get_register_task(self):
from ...tasks import check_accounts_task
name = "check_accounts_task_period_{}".format(str(self.id)[:8])
task = check_accounts_task.name
args = (str(self.id), Trigger.timing)
kwargs = {}
return name, task, args, kwargs
def to_attr_json(self):
attr_json = super().to_attr_json()
attr_json.update({
})
return attr_json
def save(self, *args, **kwargs):
self.type = AutomationTypes.check_account
super().save(*args, **kwargs)
class Meta:
verbose_name = _('Gather account automation')
class RiskChoice(TextChoices):
zombie = 'zombie', _('Zombie') # 好久没登录的账号
ghost = 'ghost', _('Ghost') # 未被纳管的账号
weak_password = 'weak_password', _('Weak password')
longtime_no_change = 'long_time_no_change', _('Long time no change')
class AccountRisk(JMSOrgBaseModel):
account = models.ForeignKey('Account', on_delete=models.CASCADE, related_name='risks', verbose_name=_('Account'))
risk = models.CharField(max_length=128, verbose_name=_('Risk'), choices=RiskChoice.choices)
confirmed = models.BooleanField(default=False, verbose_name=_('Confirmed'))

View File

@ -1,4 +1,5 @@
from .base import *
from .change_secret import *
from .check_accounts import *
from .gather_accounts import *
from .push_account import *

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
from accounts.const import AutomationTypes
from accounts.models import AccountCheckAutomation
from common.utils import get_logger
from .base import BaseAutomationSerializer
logger = get_logger(__file__)
__all__ = [
'CheckAccountsAutomationSerializer',
]
class CheckAccountsAutomationSerializer(BaseAutomationSerializer):
class Meta:
model = AccountCheckAutomation
read_only_fields = BaseAutomationSerializer.Meta.read_only_fields
fields = BaseAutomationSerializer.Meta.fields \
+ [] + read_only_fields
extra_kwargs = BaseAutomationSerializer.Meta.extra_kwargs
@property
def model_type(self):
return AutomationTypes.check_account

View File

@ -3,5 +3,6 @@ from .backup_account import *
from .gather_accounts import *
from .push_account import *
from .remove_account import *
from .scan_account import *
from .template import *
from .verify_account import *

View File

@ -0,0 +1,19 @@
from celery import shared_task
from django.utils.translation import gettext_lazy as _
from common.utils import get_logger
logger = get_logger(__file__)
__all__ = [
'check_accounts_task',
]
@shared_task(
queue="ansible",
verbose_name=_('Scan accounts'),
activity_callback=lambda self, node_ids, task_name=None, *args, **kwargs: (node_ids, None),
description=_("Unused")
)
def check_accounts_task(node_ids, task_name=None):
pass

View File

@ -238,6 +238,10 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
auto_config.update(model_to_dict(automation))
return auto_config
@lazyproperty
def accounts_amount(self):
return self.accounts.count()
def get_target_ip(self):
return self.address

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
from django.db.models import F
from django.db.models import F, Count
from django.db.transaction import atomic
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@ -148,6 +148,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Accounts'))
nodes_display = NodeDisplaySerializer(read_only=False, required=False, label=_("Node path"))
platform = ObjectRelatedField(queryset=Platform.objects, required=True, label=_('Platform'), attrs=('id', 'name', 'type'))
accounts_amount = serializers.IntegerField(read_only=True, label=_('Accounts amount'))
_accounts = None
class Meta:
@ -160,7 +161,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
'nodes_display', 'accounts',
]
read_only_fields = [
'category', 'type', 'connectivity', 'auto_config',
'accounts_amount', 'category', 'type', 'connectivity', 'auto_config',
'date_verified', 'created_by', 'date_created', 'date_updated',
]
fields = fields_small + fields_fk + fields_m2m + read_only_fields
@ -228,7 +229,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
queryset = queryset.prefetch_related('domain', 'nodes', 'protocols', ) \
.prefetch_related('platform', 'platform__automation') \
.annotate(category=F("platform__category")) \
.annotate(type=F("platform__type"))
.annotate(type=F("platform__type")) \
.annotate(assets_amount=Count('accounts'))
if queryset.model is Asset:
queryset = queryset.prefetch_related('labels__label', 'labels')
else:

View File

@ -7,7 +7,6 @@
"Accept": "同意",
"AccessIP": "IP 白名单",
"AccessKey": "访问密钥",
"Account": "账号信息",
"AccountBackup": "账号备份",
"AccountBackupCreate": "创建账号备份",
"AccountBackupDetail": "账号备份详情",
@ -22,7 +21,8 @@
"AccountDiscoverTaskCreate": "创建账号发现任务",
"AccountDiscoverTaskList": "账号发现任务",
"AccountDiscoverTaskUpdate": "更新账号发现任务",
"AccountList": "账号",
"Account": "账号",
"AccountList": "账号列表",
"AccountPolicy": "账号策略",
"AccountPolicyHelpText": "创建时对于不符合要求的账号,如:密钥类型不合规,唯一键约束,可选择以上策略。",
"AccountPushCreate": "创建账号推送",