mirror of https://github.com/jumpserver/jumpserver
perf: add check account
parent
4c3cfcbf23
commit
90cbb4be23
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -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 *
|
||||
|
|
|
@ -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'))
|
|
@ -1,4 +1,5 @@
|
|||
from .base import *
|
||||
from .change_secret import *
|
||||
from .check_accounts import *
|
||||
from .gather_accounts import *
|
||||
from .push_account import *
|
||||
|
|
|
@ -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
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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": "创建账号推送",
|
||||
|
|
Loading…
Reference in New Issue