Browse Source

feat: 改密计划支持数据库改密 (#6709)

* feat: 改密计划支持数据库改密

* fix: 将数据库账户信息不保存在资产信息里,保存到自己的存储中

* perf: 早餐村

* perf: 修改account

* perf: 修改app和系统用户

* perf: 优化系统用户和应用关系

* fix: 修复oracle不可连接问题

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Michael Bai <baijiangjie@gmail.com>
pull/6787/head
fit2cloud-jiangweidong 3 years ago committed by GitHub
parent
commit
905014d441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      Dockerfile
  2. 87
      apps/applications/api/account.py
  3. 5
      apps/applications/api/application.py
  4. 76
      apps/applications/migrations/0010_appaccount_historicalappaccount.py
  5. 40
      apps/applications/migrations/0011_auto_20210826_1759.py
  6. 1
      apps/applications/models/__init__.py
  7. 88
      apps/applications/models/account.py
  8. 93
      apps/applications/serializers/application.py
  9. 6
      apps/assets/api/system_user_relation.py
  10. 3
      apps/assets/models/authbook.py
  11. 4
      apps/assets/models/user.py
  12. 13
      apps/assets/serializers/system_user.py
  13. 2
      apps/common/management/commands/services/services/base.py
  14. BIN
      apps/locale/zh/LC_MESSAGES/django.mo
  15. 517
      apps/locale/zh/LC_MESSAGES/django.po
  16. 4
      apps/perms/api/application/user_group_permission.py
  17. 4
      apps/perms/api/application/user_permission/user_permission_applications.py
  18. 6
      apps/perms/serializers/application/user_permission.py
  19. 3
      apps/perms/signals_handler/__init__.py
  20. 104
      apps/perms/signals_handler/app_permission.py
  21. 113
      apps/perms/signals_handler/asset_permission.py
  22. 3
      requirements/requirements.txt

6
Dockerfile

@ -40,6 +40,12 @@ COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config
RUN mkdir -p /opt/jumpserver/oracle/
ADD https://f2c-north-rel.oss-cn-qingdao.aliyuncs.com/2.0/north/jumpserver/instantclient-basiclite-linux.x64-21.1.0.0.0.tar /opt/jumpserver/oracle/
RUN tar xvf /opt/jumpserver/oracle/instantclient-basiclite-linux.x64-21.1.0.0.0.tar -C /opt/jumpserver/oracle/
RUN sh -c "echo /opt/jumpserver/oracle/instantclient_21_1 > /etc/ld.so.conf.d/oracle-instantclient.conf"
RUN ldconfig
RUN echo > config.yml
VOLUME /opt/jumpserver/data
VOLUME /opt/jumpserver/logs

87
apps/applications/api/account.py

@ -2,74 +2,57 @@
#
from django_filters import rest_framework as filters
from django.db.models import F, Value, CharField
from django.db.models.functions import Concat
from django.http import Http404
from django.db.models import F, Q
from common.drf.filters import BaseFilterSet
from common.drf.api import JMSModelViewSet
from common.utils import unique
from perms.models import ApplicationPermission
from common.drf.api import JMSBulkModelViewSet
from ..models import Account
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
from .. import serializers
class AccountFilterSet(BaseFilterSet):
username = filters.CharFilter(field_name='username')
app = filters.CharFilter(field_name='applications', lookup_expr='exact')
app_name = filters.CharFilter(field_name='app_name', lookup_expr='exact')
type = filters.CharFilter(field_name='type', lookup_expr='exact')
category = filters.CharFilter(field_name='category', lookup_expr='exact')
app_display = filters.CharFilter(field_name='app_display', lookup_expr='exact')
class Meta:
model = ApplicationPermission
fields = ['type', 'category']
class ApplicationAccountViewSet(JMSModelViewSet):
permission_classes = (IsOrgAdmin, )
search_fields = ['username', 'app_name']
model = Account
fields = ['app', 'systemuser']
@property
def qs(self):
qs = super().qs
qs = self.filter_username(qs)
return qs
def filter_username(self, qs):
username = self.get_query_param('username')
if not username:
return qs
qs = qs.filter(Q(username=username) | Q(systemuser__username=username)).distinct()
return qs
class ApplicationAccountViewSet(JMSBulkModelViewSet):
model = Account
search_fields = ['username', 'app_display']
filterset_class = AccountFilterSet
filterset_fields = ['username', 'app_name', 'type', 'category']
serializer_class = serializers.ApplicationAccountSerializer
http_method_names = ['get', 'put', 'patch', 'options']
filterset_fields = ['username', 'app_display', 'type', 'category', 'app']
serializer_class = serializers.AppAccountSerializer
permission_classes = (IsOrgAdmin,)
def get_queryset(self):
queryset = ApplicationPermission.objects\
.exclude(system_users__isnull=True) \
.exclude(applications__isnull=True) \
.annotate(uid=Concat(
'applications', Value('_'), 'system_users', output_field=CharField()
)) \
.annotate(systemuser=F('system_users')) \
.annotate(systemuser_display=F('system_users__name')) \
.annotate(username=F('system_users__username')) \
.annotate(password=F('system_users__password')) \
.annotate(app=F('applications')) \
.annotate(app_name=F("applications__name")) \
.values('username', 'password', 'systemuser', 'systemuser_display',
'app', 'app_name', 'category', 'type', 'uid', 'org_id')
return queryset
def get_object(self):
obj = self.get_queryset().filter(
uid=self.kwargs['pk']
).first()
if not obj:
raise Http404()
return obj
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset_list = unique(queryset, key=lambda x: (x['app'], x['systemuser']))
return queryset_list
@staticmethod
def filter_spm_queryset(resource_ids, queryset):
queryset = queryset.filter(uid__in=resource_ids)
queryset = Account.objects.all() \
.annotate(type=F('app__type')) \
.annotate(app_display=F('app__name')) \
.annotate(systemuser_display=F('systemuser__name')) \
.annotate(category=F('app__category'))
return queryset
class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
serializer_class = serializers.ApplicationAccountSecretSerializer
serializer_class = serializers.AppAccountSecretSerializer
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
http_method_names = ['get', 'options']

5
apps/applications/api/application.py

@ -23,9 +23,8 @@ class ApplicationViewSet(OrgBulkModelViewSet):
search_fields = ('name', 'type', 'category')
permission_classes = (IsOrgAdminOrAppUser,)
serializer_classes = {
'default': serializers.ApplicationSerializer,
'get_tree': TreeNodeSerializer,
'suggestion': serializers.MiniApplicationSerializer
'default': serializers.AppSerializer,
'get_tree': TreeNodeSerializer
}
@action(methods=['GET'], detail=False, url_path='tree')

76
apps/applications/migrations/0010_appaccount_historicalappaccount.py

@ -0,0 +1,76 @@
# Generated by Django 3.1.12 on 2021-08-26 09:07
import assets.models.base
import common.fields.model
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import simple_history.models
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('assets', '0076_delete_assetuser'),
('applications', '0009_applicationuser'),
]
operations = [
migrations.CreateModel(
name='HistoricalAccount',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('version', models.IntegerField(default=1, verbose_name='Version')),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('app', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='applications.application', verbose_name='Database')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('systemuser', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='assets.systemuser', verbose_name='System user')),
],
options={
'verbose_name': 'historical Account',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='Account',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('version', models.IntegerField(default=1, verbose_name='Version')),
('app', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.application', verbose_name='Database')),
('systemuser', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.systemuser', verbose_name='System user')),
],
options={
'verbose_name': 'Account',
'unique_together': {('username', 'app', 'systemuser')},
},
bases=(models.Model, assets.models.base.AuthMixin),
),
]

40
apps/applications/migrations/0011_auto_20210826_1759.py

@ -0,0 +1,40 @@
# Generated by Django 3.1.12 on 2021-08-26 09:59
from django.db import migrations, transaction
from django.db.models import F
def migrate_app_account(apps, schema_editor):
db_alias = schema_editor.connection.alias
app_perm_model = apps.get_model("perms", "ApplicationPermission")
app_account_model = apps.get_model("applications", 'Account')
queryset = app_perm_model.objects \
.exclude(system_users__isnull=True) \
.exclude(applications__isnull=True) \
.annotate(systemuser=F('system_users')) \
.annotate(app=F('applications')) \
.values('app', 'systemuser', 'org_id')
accounts = []
for p in queryset:
if not p['app']:
continue
account = app_account_model(
app_id=p['app'], systemuser_id=p['systemuser'],
version=1, org_id=p['org_id']
)
accounts.append(account)
app_account_model.objects.using(db_alias).bulk_create(accounts, ignore_conflicts=True)
class Migration(migrations.Migration):
dependencies = [
('applications', '0010_appaccount_historicalappaccount'),
]
operations = [
migrations.RunPython(migrate_app_account)
]

1
apps/applications/models/__init__.py

@ -1 +1,2 @@
from .application import *
from .account import *

88
apps/applications/models/account.py

@ -0,0 +1,88 @@
from django.db import models
from simple_history.models import HistoricalRecords
from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty
from assets.models.base import BaseUser
class Account(BaseUser):
app = models.ForeignKey('applications.Application', on_delete=models.CASCADE, null=True, verbose_name=_('Database'))
systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user"))
version = models.IntegerField(default=1, verbose_name=_('Version'))
history = HistoricalRecords()
auth_attrs = ['username', 'password', 'private_key', 'public_key']
class Meta:
verbose_name = _('Account')
unique_together = [('username', 'app', 'systemuser')]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.auth_snapshot = {}
def get_or_systemuser_attr(self, attr):
val = getattr(self, attr, None)
if val:
return val
if self.systemuser:
return getattr(self.systemuser, attr, '')
return ''
def load_auth(self):
for attr in self.auth_attrs:
value = self.get_or_systemuser_attr(attr)
self.auth_snapshot[attr] = [getattr(self, attr), value]
setattr(self, attr, value)
def unload_auth(self):
if not self.systemuser:
return
for attr, values in self.auth_snapshot.items():
origin_value, loaded_value = values
current_value = getattr(self, attr, '')
if current_value == loaded_value:
setattr(self, attr, origin_value)
def save(self, *args, **kwargs):
self.unload_auth()
instance = super().save(*args, **kwargs)
self.load_auth()
return instance
@lazyproperty
def category(self):
return self.app.category
@lazyproperty
def type(self):
return self.app.type
@lazyproperty
def app_display(self):
return self.systemuser.name
@property
def username_display(self):
return self.get_or_systemuser_attr('username') or ''
@lazyproperty
def systemuser_display(self):
if not self.systemuser:
return ''
return str(self.systemuser)
@property
def smart_name(self):
username = self.username_display
if self.app:
app = str(self.app)
else:
app = '*'
return '{}@{}'.format(username, app)
def __str__(self):
return self.smart_name

93
apps/applications/serializers/application.py

@ -4,20 +4,23 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.models import Organization
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.serializers.base import AuthSerializerMixin
from common.drf.serializers import MethodSerializer
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
from .attrs import (
category_serializer_classes_mapping,
type_serializer_classes_mapping
)
from .. import models
from .. import const
__all__ = [
'ApplicationSerializer', 'ApplicationSerializerMixin', 'MiniApplicationSerializer',
'ApplicationAccountSerializer', 'ApplicationAccountSecretSerializer'
'AppSerializer', 'AppSerializerMixin',
'AppAccountSerializer', 'AppAccountSecretSerializer'
]
class ApplicationSerializerMixin(serializers.Serializer):
class AppSerializerMixin(serializers.Serializer):
attrs = MethodSerializer()
def get_attrs_serializer(self):
@ -45,8 +48,14 @@ class ApplicationSerializerMixin(serializers.Serializer):
serializer = serializer_class
return serializer
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
return super().update(instance, validated_data)
class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSerializer):
class AppSerializer(AppSerializerMixin, BulkOrgResourceModelSerializer):
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category display'))
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
@ -69,48 +78,54 @@ class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSeri
return _attrs
class ApplicationAccountSerializer(serializers.Serializer):
id = serializers.ReadOnlyField(label=_("Id"), source='uid')
username = serializers.ReadOnlyField(label=_("Username"))
password = serializers.CharField(write_only=True, label=_("Password"))
systemuser = serializers.ReadOnlyField(label=_('System user'))
systemuser_display = serializers.ReadOnlyField(label=_("System user display"))
app = serializers.ReadOnlyField(label=_('App'))
app_name = serializers.ReadOnlyField(label=_("Application name"), read_only=True)
class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True)
category_display = serializers.SerializerMethodField(label=_('Category display'))
type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
type_display = serializers.SerializerMethodField(label=_('Type display'))
uid = serializers.ReadOnlyField(label=_("Union id"))
org_id = serializers.ReadOnlyField(label=_("Organization"))
org_name = serializers.SerializerMethodField(label=_("Org name"))
category_mapper = dict(const.AppCategory.choices)
type_mapper = dict(const.AppType.choices)
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
class Meta:
model = models.Account
fields_mini = ['id', 'username', 'version']
fields_write_only = ['password', 'private_key']
fields_fk = ['systemuser', 'systemuser_display', 'app', 'app_display']
fields = fields_mini + fields_fk + fields_write_only + [
'type', 'type_display', 'category', 'category_display',
]
extra_kwargs = {
'username': {'default': '', 'required': False},
'password': {'write_only': True},
'app_display': {'label': _('Application display')}
}
use_model_bulk_create = True
model_bulk_create_kwargs = {
'ignore_conflicts': True
}
def get_category_display(self, obj):
return self.category_mapper.get(obj['category'])
return self.category_mapper.get(obj.category)
def get_type_display(self, obj):
return self.type_mapper.get(obj['type'])
@staticmethod
def get_org_name(obj):
org = Organization.get_instance(obj['org_id'])
return org.name
class ApplicationAccountSecretSerializer(ApplicationAccountSerializer):
password = serializers.CharField(write_only=False, label=_("Password"))
class MiniApplicationSerializer(serializers.ModelSerializer):
class Meta:
model = models.Application
fields = ApplicationSerializer.Meta.fields_mini
return self.type_mapper.get(obj.type)
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('systemuser', 'app')
return queryset
def to_representation(self, instance):
instance.load_auth()
return super().to_representation(instance)
class AppAccountSecretSerializer(AppAccountSerializer):
class Meta(AppAccountSerializer.Meta):
extra_kwargs = {
'password': {'write_only': False},
'private_key': {'write_only': False},
'public_key': {'write_only': False},
}

6
apps/assets/api/system_user_relation.py

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.db.models import F, Value
from django.db.models import F, Value, Model
from django.db.models.signals import m2m_changed
from django.db.models.functions import Concat
@ -13,13 +13,15 @@ from .. import models, serializers
__all__ = [
'SystemUserAssetRelationViewSet', 'SystemUserNodeRelationViewSet',
'SystemUserUserRelationViewSet',
'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():

3
apps/assets/models/authbook.py

@ -16,7 +16,6 @@ class AuthBook(BaseUser, AbsConnectivity):
systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user"))
version = models.IntegerField(default=1, verbose_name=_('Version'))
history = HistoricalRecords()
_systemuser_display = ''
auth_attrs = ['username', 'password', 'private_key', 'public_key']
@ -64,8 +63,6 @@ class AuthBook(BaseUser, AbsConnectivity):
@lazyproperty
def systemuser_display(self):
if self._systemuser_display:
return self._systemuser_display
if not self.systemuser:
return ''
return str(self.systemuser)

4
apps/assets/models/user.py

@ -73,6 +73,10 @@ class ProtocolMixin:
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 AuthMixin:
username_same_with_user: bool

13
apps/assets/serializers/system_user.py

@ -14,7 +14,7 @@ __all__ = [
'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer',
'SystemUserNodeRelationSerializer', 'SystemUserTaskSerializer',
'SystemUserUserRelationSerializer', 'SystemUserWithAuthInfoSerializer',
'SystemUserTempAuthSerializer',
'SystemUserTempAuthSerializer', 'RelationMixin',
]
@ -31,12 +31,12 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
fields_mini = ['id', 'name', 'username']
fields_write_only = ['password', 'public_key', 'private_key']
fields_small = fields_mini + fields_write_only + [
'type', 'type_display', 'protocol', 'login_mode', 'login_mode_display',
'priority', 'sudo', 'shell', 'sftp_root', 'token', 'ssh_key_fingerprint',
'home', 'system_groups', 'ad_domain',
'token', 'ssh_key_fingerprint',
'type', 'type_display', 'protocol', 'is_asset_protocol',
'login_mode', 'login_mode_display', 'priority',
'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain',
'username_same_with_user', 'auto_push', 'auto_generate_key',
'date_created', 'date_updated',
'comment', 'created_by',
'date_created', 'date_updated', 'comment', 'created_by',
]
fields_m2m = ['cmd_filters', 'assets_amount']
fields = fields_small + fields_m2m
@ -53,6 +53,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'login_mode_display': {'label': _('Login mode display')},
'created_by': {'read_only': True},
'ad_domain': {'required': False, 'allow_blank': True, 'label': _('Ad domain')},
'is_asset_protocol': {'label': _('Is asset protocol')}
}
def validate_auto_push(self, value):

2
apps/common/management/commands/services/services/base.py

@ -160,7 +160,7 @@ class BaseService(object):
if self.process:
try:
self.process.wait(1) # 不wait,子进程可能无法回收
except subprocess.TimeoutExpired:
except:
pass
if self.is_running:

BIN
apps/locale/zh/LC_MESSAGES/django.mo

Binary file not shown.

517
apps/locale/zh/LC_MESSAGES/django.po

@ -35,12 +35,12 @@ msgid "Name"
msgstr "名称"
#: acls/models/base.py:27 assets/models/cmd_filter.py:54
#: assets/models/user.py:203
#: assets/models/user.py:207
msgid "Priority"
msgstr "优先级"
#: acls/models/base.py:28 assets/models/cmd_filter.py:54
#: assets/models/user.py:203
#: assets/models/user.py:207
msgid "1-100, the lower the value will be match first"
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
@ -51,8 +51,6 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
msgid "Active"
msgstr "激活中"
# msgid "Date created"
# msgstr "创建日期"
#: acls/models/base.py:32 applications/models/application.py:179
#: assets/models/asset.py:144 assets/models/asset.py:220
#: assets/models/base.py:180 assets/models/cluster.py:29
@ -62,7 +60,7 @@ msgstr "激活中"
#: orgs/models.py:27 perms/models/base.py:53 settings/models.py:34
#: terminal/models/storage.py:26 terminal/models/terminal.py:114
#: tickets/models/ticket.py:71 users/models/group.py:16
#: users/models/user.py:590 xpack/plugins/change_auth_plan/models.py:88
#: users/models/user.py:590 xpack/plugins/change_auth_plan/models/base.py:41
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:113
#: xpack/plugins/gathered_user/models.py:26
msgid "Comment"
@ -114,20 +112,16 @@ msgstr "用户"
msgid "Login confirm"
msgstr "登录复核"
#: acls/models/login_asset_acl.py:21
msgid "System User"
msgstr "系统用户"
#: acls/models/login_asset_acl.py:22
#: applications/serializers/attrs/application_category/remote_app.py:37
#: assets/models/asset.py:357 assets/models/authbook.py:15
#: assets/models/gathered_user.py:14 assets/serializers/system_user.py:200
#: assets/models/gathered_user.py:14 assets/serializers/system_user.py:201
#: audits/models.py:38 perms/models/asset_permission.py:99
#: templates/index.html:82 terminal/backends/command/models.py:19
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:40
#: users/templates/users/user_asset_permission.html:40
#: users/templates/users/user_asset_permission.html:70
#: xpack/plugins/change_auth_plan/models.py:315
#: xpack/plugins/change_auth_plan/models/asset.py:195
#: xpack/plugins/cloud/models.py:217
msgid "Asset"
msgstr "资产"
@ -173,7 +167,6 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: acls/serializers/login_asset_acl.py:17
#: acls/serializers/login_asset_acl.py:51
#: applications/serializers/application.py:74
#: applications/serializers/attrs/application_type/chrome.py:20
#: applications/serializers/attrs/application_type/custom.py:21
#: applications/serializers/attrs/application_type/mysql_workbench.py:30
@ -182,8 +175,8 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17
#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:555
#: users/templates/users/_select_user_modal.html:14
#: xpack/plugins/change_auth_plan/models.py:51
#: xpack/plugins/change_auth_plan/models.py:311
#: xpack/plugins/change_auth_plan/models/asset.py:35
#: xpack/plugins/change_auth_plan/models/asset.py:191
#: xpack/plugins/cloud/serializers.py:67
msgid "Username"
msgstr "用户名"
@ -212,7 +205,7 @@ msgid ""
msgstr "格式为逗号分隔的字符串, * 表示匹配所有. 可选的协议有: {}"
#: acls/serializers/login_asset_acl.py:55 assets/models/asset.py:184
#: assets/models/domain.py:63 assets/models/user.py:204
#: assets/models/domain.py:63 assets/models/user.py:208
#: terminal/serializers/session.py:30 terminal/serializers/storage.py:69
msgid "Protocol"
msgstr "协议"
@ -234,9 +227,11 @@ msgstr "所有复核人都不属于组织 `{}`"
msgid "My applications"
msgstr "我的应用"
#: applications/const.py:8
#: applications/const.py:8 applications/models/account.py:10
#: applications/serializers/attrs/application_category/db.py:14
#: applications/serializers/attrs/application_type/mysql_workbench.py:26
#: xpack/plugins/change_auth_plan/models/app.py:32
#: xpack/plugins/change_auth_plan/models/app.py:139
msgid "Database"
msgstr "数据库"
@ -248,25 +243,44 @@ msgstr "远程应用"
msgid "Custom"
msgstr "自定义"
#: applications/models/account.py:11 assets/models/authbook.py:16
#: assets/models/user.py:281 audits/models.py:39
#: perms/models/application_permission.py:31
#: perms/models/asset_permission.py:101 templates/_nav.html:45
#: terminal/backends/command/models.py:20
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42
#: users/templates/users/_granted_assets.html:27
#: users/templates/users/user_asset_permission.html:42
#: users/templates/users/user_asset_permission.html:76
#: users/templates/users/user_asset_permission.html:159
#: users/templates/users/user_database_app_permission.html:40
#: users/templates/users/user_database_app_permission.html:67
#: xpack/plugins/change_auth_plan/models/app.py:36
msgid "System user"
msgstr "系统用户"
#: applications/models/application.py:50 templates/_nav.html:60
msgid "Applications"
msgstr "应用管理"
#: applications/models/application.py:168
#: applications/serializers/application.py:80 assets/models/label.py:21
#: applications/serializers/application.py:82 assets/models/label.py:21
#: perms/models/application_permission.py:20
#: perms/serializers/application/user_permission.py:33
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:20
#: xpack/plugins/change_auth_plan/models/app.py:25
msgid "Category"
msgstr "类别"
#: applications/models/application.py:171
#: applications/serializers/application.py:82 assets/models/cmd_filter.py:53
#: assets/models/user.py:202 perms/models/application_permission.py:23
#: applications/serializers/application.py:84 assets/models/cmd_filter.py:53
#: assets/models/user.py:206 perms/models/application_permission.py:23
#: perms/serializers/application/user_permission.py:34
#: terminal/models/storage.py:55 terminal/models/storage.py:116
#: tickets/models/flow.py:50 tickets/models/ticket.py:48
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:27
#: xpack/plugins/change_auth_plan/models/app.py:28
#: xpack/plugins/change_auth_plan/models/app.py:148
msgid "Type"
msgstr "类型"
@ -279,15 +293,15 @@ msgstr "网域"
msgid "Attrs"
msgstr ""
#: applications/serializers/application.py:50
#: applications/serializers/application.py:81 assets/serializers/label.py:13
#: applications/serializers/application.py:59
#: applications/serializers/application.py:83 assets/serializers/label.py:13
#: perms/serializers/application/permission.py:18
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:24
msgid "Category display"
msgstr "类别名称"
#: applications/serializers/application.py:51
#: applications/serializers/application.py:83
#: applications/serializers/application.py:60
#: applications/serializers/application.py:85
#: assets/serializers/system_user.py:26 audits/serializers.py:29
#: perms/serializers/application/permission.py:19
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:31
@ -300,46 +314,6 @@ msgstr "类型名称"
msgid "Id"
msgstr ""
#: applications/serializers/application.py:75
#: applications/serializers/application.py:110
#: applications/serializers/attrs/application_type/chrome.py:23
#: applications/serializers/attrs/application_type/custom.py:25
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
#: applications/serializers/attrs/application_type/vmware_client.py:30
#: assets/models/base.py:177 audits/signals_handler.py:63
#: authentication/forms.py:22
#: authentication/templates/authentication/login.html:164
#: settings/serializers/auth/ldap.py:44 users/forms/profile.py:21
#: users/templates/users/user_otp_check_password.html:13
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/change_auth_plan/models.py:72
#: xpack/plugins/change_auth_plan/models.py:207
#: xpack/plugins/change_auth_plan/models.py:318
#: xpack/plugins/cloud/serializers.py:69
msgid "Password"
msgstr "密码"
#: applications/serializers/application.py:76 assets/models/authbook.py:16
#: assets/models/user.py:277 audits/models.py:39
#: perms/models/application_permission.py:31
#: perms/models/asset_permission.py:101 templates/_nav.html:45
#: terminal/backends/command/models.py:20
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42
#: users/templates/users/_granted_assets.html:27
#: users/templates/users/user_asset_permission.html:42
#: users/templates/users/user_asset_permission.html:76
#: users/templates/users/user_asset_permission.html:159
#: users/templates/users/user_database_app_permission.html:40
#: users/templates/users/user_database_app_permission.html:67
msgid "System user"
msgstr "系统用户"
#: applications/serializers/application.py:77 assets/serializers/account.py:31
#: assets/serializers/account.py:52
msgid "System user display"
msgstr "系统用户名称"
#: applications/serializers/application.py:78
msgid "App"
msgstr "应用"
@ -348,21 +322,6 @@ msgstr "应用"
msgid "Application name"
msgstr "应用名称"
#: applications/serializers/application.py:84
msgid "Union id"
msgstr "联合ID"
#: applications/serializers/application.py:85 orgs/mixins/models.py:46
#: orgs/mixins/serializers.py:25 orgs/models.py:37 orgs/models.py:432
#: orgs/serializers.py:106 tickets/serializers/ticket/ticket.py:77
msgid "Organization"
msgstr "组织"
#: applications/serializers/application.py:86 assets/serializers/asset.py:98
#: assets/serializers/system_user.py:217 orgs/mixins/serializers.py:26
msgid "Org name"
msgstr "组织名称"
#: applications/serializers/attrs/application_category/cloud.py:9
#: assets/models/cluster.py:40
msgid "Cluster"
@ -400,6 +359,24 @@ msgstr "该字段是必填项。"
msgid "Target URL"
msgstr "目标URL"
#: applications/serializers/attrs/application_type/chrome.py:23
#: applications/serializers/attrs/application_type/custom.py:25
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
#: applications/serializers/attrs/application_type/vmware_client.py:30
#: assets/models/base.py:177 audits/signals_handler.py:63
#: authentication/forms.py:22
#: authentication/templates/authentication/login.html:164
#: settings/serializers/settings.py:95 users/forms/profile.py:21
#: users/templates/users/user_otp_check_password.html:13
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/change_auth_plan/models/base.py:39
#: xpack/plugins/change_auth_plan/models/base.py:114
#: xpack/plugins/change_auth_plan/models/base.py:182
#: xpack/plugins/cloud/serializers.py:69
msgid "Password"
msgstr "密码"
#: applications/serializers/attrs/application_type/custom.py:13
msgid "Operating parameter"
msgstr "运行参数"
@ -452,9 +429,9 @@ msgstr "系统平台"
msgid "Protocols"
msgstr "协议组"
#: assets/models/asset.py:189 assets/models/user.py:194
#: assets/models/asset.py:189 assets/models/user.py:198
#: perms/models/asset_permission.py:100
#: xpack/plugins/change_auth_plan/models.py:60
#: xpack/plugins/change_auth_plan/models/asset.py:44
#: xpack/plugins/gathered_user/models.py:24
msgid "Nodes"
msgstr "节点"
@ -466,7 +443,7 @@ msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:193 assets/models/cluster.py:19
#: assets/models/user.py:191 assets/models/user.py:326 templates/_nav.html:44
#: assets/models/user.py:195 assets/models/user.py:330 templates/_nav.html:44
msgid "Admin user"
msgstr "特权用户"
@ -543,13 +520,12 @@ msgstr "标签管理"
#: assets/models/cmd_filter.py:67 assets/models/group.py:21
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25
#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:598
#: users/serializers/group.py:33 xpack/plugins/change_auth_plan/models.py:92
#: users/serializers/group.py:33
#: xpack/plugins/change_auth_plan/models/base.py:45
#: xpack/plugins/cloud/models.py:119 xpack/plugins/gathered_user/models.py:30
msgid "Created by"
msgstr "创建者"
# msgid "Created by"
# msgstr "创建者"
#: assets/models/asset.py:219 assets/models/base.py:181
#: assets/models/cluster.py:26 assets/models/domain.py:27
#: assets/models/gathered_user.py:19 assets/models/group.py:22
@ -589,15 +565,15 @@ msgstr "可连接性"
msgid "Date verified"
msgstr "校验日期"
#: assets/models/base.py:178 xpack/plugins/change_auth_plan/models.py:82
#: xpack/plugins/change_auth_plan/models.py:214
#: xpack/plugins/change_auth_plan/models.py:325
#: assets/models/base.py:178 xpack/plugins/change_auth_plan/models/asset.py:54
#: xpack/plugins/change_auth_plan/models/asset.py:126
#: xpack/plugins/change_auth_plan/models/asset.py:202
msgid "SSH private key"
msgstr "SSH密钥"
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models.py:85
#: xpack/plugins/change_auth_plan/models.py:210
#: xpack/plugins/change_auth_plan/models.py:321
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:57
#: xpack/plugins/change_auth_plan/models/asset.py:122
#: xpack/plugins/change_auth_plan/models/asset.py:198
msgid "SSH public key"
msgstr "SSH公钥"
@ -649,7 +625,7 @@ msgstr "系统"
msgid "Default Cluster"
msgstr "默认Cluster"
#: assets/models/cmd_filter.py:33 assets/models/user.py:209
#: assets/models/cmd_filter.py:33 assets/models/user.py:213
msgid "Command filter"
msgstr "命令过滤器"
@ -751,7 +727,7 @@ msgstr "全称"
msgid "Parent key"
msgstr "ssh私钥"
#: assets/models/node.py:559 assets/serializers/system_user.py:199
#: assets/models/node.py:559 assets/serializers/system_user.py:200
#: users/templates/users/user_asset_permission.html:41
#: users/templates/users/user_asset_permission.html:73
#: users/templates/users/user_asset_permission.html:158
@ -759,65 +735,65 @@ msgstr "ssh私钥"
msgid "Node"
msgstr "节点"
#: assets/models/user.py:185
#: assets/models/user.py:189
msgid "Automatic managed"
msgstr "托管密码"
#: assets/models/user.py:186
#: assets/models/user.py:190
msgid "Manually input"
msgstr "手动输入"
#: assets/models/user.py:190
#: assets/models/user.py:194
msgid "Common user"
msgstr "普通用户"
#: assets/models/user.py:193
#: assets/models/user.py:197
msgid "Username same with user"
msgstr "用户名与用户相同"
#: assets/models/user.py:196 assets/serializers/domain.py:28
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:56
#: assets/models/user.py:200 assets/serializers/domain.py:28
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models/asset.py:40
msgid "Assets"
msgstr "资产"
#: assets/models/user.py:200 templates/_nav.html:17
#: assets/models/user.py:204 templates/_nav.html:17
#: users/views/profile/pubkey.py:37
msgid "Users"
msgstr "用户管理"
#: assets/models/user.py:201
#: assets/models/user.py:205
msgid "User groups"
msgstr "用户组"
#: assets/models/user.py:205
#: assets/models/user.py:209
msgid "Auto push"
msgstr "自动推送"
#: assets/models/user.py:206
#: assets/models/user.py:210
msgid "Sudo"
msgstr "Sudo"
#: assets/models/user.py:207
#: assets/models/user.py:211
msgid "Shell"
msgstr "Shell"
#: assets/models/user.py:208
#: assets/models/user.py:212
msgid "Login mode"
msgstr "认证方式"
#: assets/models/user.py:210
#: assets/models/user.py:214
msgid "SFTP Root"
msgstr "SFTP根路径"
#: assets/models/user.py:211 authentication/models.py:94
#: assets/models/user.py:215 authentication/models.py:94
msgid "Token"
msgstr ""
#: assets/models/user.py:212
#: assets/models/user.py:216
msgid "Home"
msgstr "家目录"
#: assets/models/user.py:213
#: assets/models/user.py:217
msgid "System groups"
msgstr "用户组"
@ -826,6 +802,10 @@ msgstr "用户组"
msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
#: assets/serializers/account.py:31 assets/serializers/account.py:52
msgid "System user display"
msgstr "系统用户名称"
#: assets/serializers/asset.py:20
msgid "Protocol format should {}/{}"
msgstr "协议格式 {}/{}"
@ -846,6 +826,11 @@ msgstr "节点名称"
msgid "Hardware info"
msgstr "硬件信息"
#: assets/serializers/asset.py:98 assets/serializers/system_user.py:218
#: orgs/mixins/serializers.py:26
msgid "Org name"
msgstr "用户名"
#: assets/serializers/asset.py:99
msgid "Admin user display"
msgstr "特权用户名称"
@ -890,7 +875,7 @@ msgstr "密钥指纹"
msgid "Nodes amount"
msgstr "节点数量"
#: assets/serializers/system_user.py:53 assets/serializers/system_user.py:201
#: assets/serializers/system_user.py:53 assets/serializers/system_user.py:202
msgid "Login mode display"
msgstr "认证方式名称"
@ -898,27 +883,31 @@ msgstr "认证方式名称"
msgid "Ad domain"
msgstr "Ad 网域"
#: assets/serializers/system_user.py:95
#: assets/serializers/system_user.py:56
msgid "Is asset protocol"
msgstr ""
#: assets/serializers/system_user.py:96
msgid "Username same with user with protocol {} only allow 1"
msgstr "用户名和用户相同的一种协议只允许存在一个"
#: assets/serializers/system_user.py:109
#: assets/serializers/system_user.py:110
msgid "* Automatic login mode must fill in the username."
msgstr "自动登录模式,必须填写用户名"
#: assets/serializers/system_user.py:123
#: assets/serializers/system_user.py:124
msgid "Path should starts with /"
msgstr "路径应该以 / 开头"
#: assets/serializers/system_user.py:148
#: assets/serializers/system_user.py:149
msgid "Password or private key required"
msgstr "密码或密钥密码需要一个"
#: assets/serializers/system_user.py:216
#: assets/serializers/system_user.py:217
msgid "System user name"
msgstr "系统用户名称"
#: assets/serializers/system_user.py:226
#: assets/serializers/system_user.py:227
msgid "Asset hostname"
msgstr "资产主机名"
@ -1092,8 +1081,8 @@ msgstr "成功"
#: terminal/models/session.py:52
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:53
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:45
#: xpack/plugins/change_auth_plan/models.py:194
#: xpack/plugins/change_auth_plan/models.py:340
#: xpack/plugins/change_auth_plan/models/base.py:105
#: xpack/plugins/change_auth_plan/models/base.py:189
#: xpack/plugins/gathered_user/models.py:76
msgid "Date start"
msgstr "开始日期"
@ -2123,7 +2112,7 @@ msgid "Regularly perform"
msgstr "定期执行"
#: ops/mixin.py:106 ops/mixin.py:147
#: xpack/plugins/change_auth_plan/serializers.py:60
#: xpack/plugins/change_auth_plan/serializers/base.py:42
msgid "Periodic perform"
msgstr "定时执行"
@ -2202,8 +2191,8 @@ msgstr "开始时间"
msgid "End time"
msgstr "完成时间"
#: ops/models/adhoc.py:246 xpack/plugins/change_auth_plan/models.py:197
#: xpack/plugins/change_auth_plan/models.py:343
#: ops/models/adhoc.py:246 xpack/plugins/change_auth_plan/models/base.py:108
#: xpack/plugins/change_auth_plan/models/base.py:190
#: xpack/plugins/gathered_user/models.py:79
msgid "Time"
msgstr "时间"
@ -2285,6 +2274,12 @@ msgstr "当前组织 ({}) 不能被删除"
msgid "The organization have resource ({}) cannot be deleted"
msgstr "组织存在资源 ({}) 不能被删除"
#: orgs/mixins/models.py:46 orgs/mixins/serializers.py:25 orgs/models.py:37
#: orgs/models.py:432 orgs/serializers.py:106
#: tickets/serializers/ticket/ticket.py:77
msgid "Organization"
msgstr "组织审计员"
#: orgs/models.py:17 users/const.py:12
msgid "Organization administrator"
msgstr "组织管理员"
@ -2935,6 +2930,7 @@ msgstr "仅管理员"
msgid "Global MFA auth"
msgstr "全局启用 MFA 认证"
#: settings/serializers/security.py:33
msgid "Limit the number of login failures"
msgstr "限制登录失败次数"
@ -2947,6 +2943,7 @@ msgstr "禁止登录时间间隔"
msgid ""
"Unit: minute, If the user has failed to log in for a limited number of "
"times, no login is allowed during this time interval."
msgstr "单位:分, 当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
#: settings/serializers/security.py:45
@ -4311,7 +4308,7 @@ msgstr "工单受理人"
#: tickets/models/ticket.py:45
msgid "Title"
msgstr "标题"
msgstr ""
#: tickets/models/ticket.py:53
msgid "State"
@ -4624,8 +4621,9 @@ msgstr "两次密码不一致"
msgid "Is first login"
msgstr "首次登录"
#: users/serializers/user.py:22 xpack/plugins/change_auth_plan/models.py:65
#: xpack/plugins/change_auth_plan/serializers.py:33
#: users/serializers/user.py:22
#: xpack/plugins/change_auth_plan/models/base.py:32
#: xpack/plugins/change_auth_plan/serializers/base.py:24
msgid "Password strategy"
msgstr "密码策略"
@ -5263,84 +5261,111 @@ msgstr "* 新密码不能是最近 {} 次的密码"
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: xpack/plugins/change_auth_plan/api/asset.py:83
#: xpack/plugins/change_auth_plan/api/asset.py:141
msgid "The parameter 'action' must be [{}]"
msgstr ""
#: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:100
#: xpack/plugins/change_auth_plan/models.py:201
#: xpack/plugins/change_auth_plan/models/asset.py:63
#: xpack/plugins/change_auth_plan/models/asset.py:119
msgid "Change auth plan"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:40
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models/app.py:41
#: xpack/plugins/change_auth_plan/models/app.py:90
msgid "Database Change auth plan"
msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:41
msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models/app.py:94
#: xpack/plugins/change_auth_plan/models/app.py:146
msgid "Database Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:42
msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models/app.py:142
msgid "SystemUser"
msgstr "系统用户"
#: xpack/plugins/change_auth_plan/models.py:46
#: xpack/plugins/change_auth_plan/models/app.py:151
msgid "Database Change auth plan task"
msgstr "改密计划任务"
#: xpack/plugins/change_auth_plan/models/asset.py:30
msgid "Append SSH KEY"
msgstr "追加新密钥"
#: xpack/plugins/change_auth_plan/models.py:47
#: xpack/plugins/change_auth_plan/models/asset.py:31
msgid "Empty and append SSH KEY"
msgstr "清空所有密钥再追加新密钥"
#: xpack/plugins/change_auth_plan/models.py:48
#: xpack/plugins/change_auth_plan/models/asset.py:32
msgid "Empty current user and append SSH KEY"
msgstr "清空当前账号密钥再追加新密钥"
#: xpack/plugins/change_auth_plan/models.py:69
msgid "Password rules"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:78
#: xpack/plugins/change_auth_plan/serializers.py:35
#: xpack/plugins/change_auth_plan/models/asset.py:50
#: xpack/plugins/change_auth_plan/serializers/asset.py:34
msgid "SSH Key strategy"
msgstr "SSH Key 策略"
#: xpack/plugins/change_auth_plan/models.py:189
#: xpack/plugins/change_auth_plan/models/asset.py:130
#: xpack/plugins/change_auth_plan/models/asset.py:206
msgid "Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models/asset.py:213
msgid "Change auth plan task"
msgstr "改密计划任务"
#: xpack/plugins/change_auth_plan/models/base.py:24
msgid "Custom password"
msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models/base.py:25
msgid "All assets use the same random password"
msgstr "使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models/base.py:26
msgid "All assets use different random password"
msgstr "使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models/base.py:36
msgid "Password rules"
msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models/base.py:100
msgid "Manual trigger"
msgstr "手动触发"
#: xpack/plugins/change_auth_plan/models.py:190
#: xpack/plugins/change_auth_plan/models/base.py:101
msgid "Timing trigger"
msgstr "定时触发"
#: xpack/plugins/change_auth_plan/models.py:204
#: xpack/plugins/change_auth_plan/models/base.py:111
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:218
#: xpack/plugins/change_auth_plan/serializers.py:166
#: xpack/plugins/change_auth_plan/models/base.py:118
#: xpack/plugins/change_auth_plan/serializers/base.py:70
msgid "Trigger mode"
msgstr "触发模式"
#: xpack/plugins/change_auth_plan/models.py:223
#: xpack/plugins/change_auth_plan/models.py:329
msgid "Change auth plan execution"
msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:302
#: xpack/plugins/change_auth_plan/models/base.py:173
msgid "Ready"
msgstr "准备"
#: xpack/plugins/change_auth_plan/models.py:303
#: xpack/plugins/change_auth_plan/models/base.py:174
msgid "Preflight check"
msgstr "改密前的校验"
#: xpack/plugins/change_auth_plan/models.py:304
#: xpack/plugins/change_auth_plan/models/base.py:175
msgid "Change auth"
msgstr "执行改密"
#: xpack/plugins/change_auth_plan/models.py:305
#: xpack/plugins/change_auth_plan/models/base.py:176
msgid "Verify auth"
msgstr "验证密码/密钥"
#: xpack/plugins/change_auth_plan/models.py:306
#: xpack/plugins/change_auth_plan/models/base.py:177
msgid "Keep auth"
msgstr "保存密码/密钥"
@ -5348,55 +5373,51 @@ msgstr "保存密码/密钥"
msgid "Step"
msgstr "步骤"
#: xpack/plugins/change_auth_plan/models.py:350
msgid "Change auth plan task"
msgstr "改密计划任务"
#: xpack/plugins/change_auth_plan/serializers.py:29
#: xpack/plugins/change_auth_plan/serializers/asset.py:31
msgid "Change Password"
msgstr "修改密码"
#: xpack/plugins/change_auth_plan/serializers.py:30
#: xpack/plugins/change_auth_plan/serializers/asset.py:32
msgid "Change SSH Key"
msgstr "修改密钥"
#: xpack/plugins/change_auth_plan/serializers.py:61
msgid "Run times"
msgstr "执行次数"
#: xpack/plugins/change_auth_plan/serializers.py:79
#: xpack/plugins/change_auth_plan/serializers/asset.py:65
msgid "Require password strategy perform setting"
msgstr "需要密码策略执行设置"
#: xpack/plugins/change_auth_plan/serializers.py:82
#: xpack/plugins/change_auth_plan/serializers/asset.py:68
msgid "Require password perform setting"
msgstr "需要密码执行设置"
#: xpack/plugins/change_auth_plan/serializers.py:85
#: xpack/plugins/change_auth_plan/serializers/asset.py:71
msgid "Require password rule perform setting"
msgstr "需要密码规则执行设置"
#: xpack/plugins/change_auth_plan/serializers.py:97
#: xpack/plugins/change_auth_plan/serializers/asset.py:87
msgid "Require ssh key strategy or ssh key perform setting"
msgstr "需要ssh密钥策略或ssh密钥执行设置"
#: xpack/plugins/change_auth_plan/serializers/base.py:43
msgid "Run times"
msgstr "执行次数"
#: xpack/plugins/change_auth_plan/serializers/base.py:54
msgid "* Please enter the correct password length"
msgstr "* 请输入正确的密码长度"
#: xpack/plugins/change_auth_plan/serializers.py:100
#: xpack/plugins/change_auth_plan/serializers/base.py:57
msgid "* Password length range 6-30 bits"
msgstr "* 密码长度范围 6-30 位"
#: xpack/plugins/change_auth_plan/serializers.py:118
msgid "Require ssh key strategy or ssh key perform setting"
msgstr "需要ssh密钥策略或ssh密钥执行设置"
#: xpack/plugins/change_auth_plan/utils.py:485
#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:248
msgid "Invalid/incorrect password"
msgstr "无效/错误 密码"
#: xpack/plugins/change_auth_plan/utils.py:487
#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:250
msgid "Failed to connect to the host"
msgstr "连接主机失败"
#: xpack/plugins/change_auth_plan/utils.py:489
#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:252
msgid "Data could not be sent to remote"
msgstr "无法将数据发送到远程"
@ -5492,10 +5513,6 @@ msgstr "云服务商"
msgid "Cloud account"
msgstr "云账号"
#: xpack/plugins/cloud/models.py:82 xpack/plugins/cloud/serializers.py:207
msgid "Account"
msgstr "账户"
#: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers.py:179
msgid "Regions"
msgstr "地域"
@ -5816,135 +5833,3 @@ msgstr "旗舰版"
#: xpack/plugins/license/models.py:77
msgid "Community edition"
msgstr "社区版"
#~ msgid "Approval level"
#~ msgstr "同意"
#~ msgid "Login challenge enabled"
#~ msgstr "登录页面开启CHALLENGE输入框"
#~ msgid "Login captcha enabled"
#~ msgstr "登录页面开启验证码"
#~ msgid "Insecure command level"
#~ msgstr "不安全命令等级"
#~ msgid "Encrypt password"
#~ msgstr "密码加密"
#~ msgid "Create User"
#~ msgstr "创建用户"
#~ msgid "Apply attr to use"
#~ msgstr "申请可用属性"
#~ msgid "User login only in users"
#~ msgstr "仅在用户列表中用户认证"
#~ msgid "Approved applications"
#~ msgstr "批准的应用"
#~ msgid "Approved system users"
#~ msgstr "批准的系统用户"
#~ msgid "Approved date expired"
#~ msgstr "批准的失效日期"
#~ msgid "Applied IP group"
#~ msgstr "申请的IP组"
#~ msgid "Approved assets"
#~ msgstr "批准的资产"
#~ msgid "Approved actions"
#~ msgstr "批准的动作"
#~ msgid "Ticket action"
#~ msgstr "工单动作"
#~ msgid "Ticket processor"
#~ msgstr "工单处理人"
#~ msgid "Processor display"
#~ msgstr "处理人名称"
#~ msgid "Application group"
#~ msgstr "应用组"
#~ msgid "System user group"
#~ msgstr "系统用户组"
#~ msgid "Permission name"
#~ msgstr "授权名称"
#~ msgid "Approve applications"
#~ msgstr "批准的应用"
#~ msgid "No `Application` are found under Organization `{}`"
#~ msgstr "在组织 `{}` 下没有发现 `应用`"
#~ msgid "No `SystemUser` are found under Organization `{}`"
#~ msgstr "在组织 `{}` 下没有发现 `系统用户`"
#~ msgid "IP group"
#~ msgstr "IP组"
#~ msgid "Hostname group"
#~ msgstr "主机名组"
#~ msgid "Approve assets"
#~ msgstr "批准的资产"
#~ msgid "No `Asset` are found under Organization `{}`"
#~ msgstr "在组织 `{}` 下没有发现 `资产`"
#~ msgid "Action display"
#~ msgstr "动作名称"
#~ msgid "None of the assignees belong to Organization `{}` admins"
#~ msgstr "所有受理人都不属于组织 `{}` 下的管理员"
#~ msgid "* Please enter custom password"
#~ msgstr "* 请输入自定义密码"
#~ msgid "FeiShu Error, Please contact your system administrator"
#~ msgstr "飞书错误,请联系系统管理员"
#~ msgid "Category(Display)"
#~ msgstr "类别 (显示名称)"
#~ msgid "Type(Dispaly)"
#~ msgstr "类型 (显示名称)"
#~ msgid "Users name"
#~ msgstr "用户名"
#~ msgid "User groups name"
#~ msgstr "用户组名称"
#~ msgid "Assets name"
#~ msgstr "资产名称"
#~ msgid "System users name"
#~ msgstr "系统用户名称"
#~ msgid "Admin user MFA auth"
#~ msgstr "所有管理员启用 MFA"
#~ msgid "Admin user enable MFA"
#~ msgstr "强制管理员启用 MFA"
#~ msgid "Password update"
#~ msgstr "密码更新"
#~ msgid "All user enable MFA"
#~ msgstr "强制所有用户启用 MFA"
#~ msgid "Application category"
#~ msgstr "应用类别"
#~ msgid "Application type"
#~ msgstr "应用类型"
#~ msgid "Trigger"
#~ msgstr "触发"

4
apps/perms/api/application/user_group_permission.py

@ -19,8 +19,8 @@ class UserGroupGrantedApplicationsApi(CommonApiMixin, ListAPIView):
获取用户组直接授权的应用
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.ApplicationGrantedSerializer
only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields
serializer_class = serializers.AppGrantedSerializer
only_fields = serializers.AppGrantedSerializer.Meta.only_fields
filterset_fields = ['id', 'name', 'category', 'type', 'comment']
search_fields = ['name', 'comment']

4
apps/perms/api/application/user_permission/user_permission_applications.py

@ -24,8 +24,8 @@ __all__ = [
class AllGrantedApplicationsMixin(CommonApiMixin, ListAPIView):
only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields
serializer_class = serializers.ApplicationGrantedSerializer
only_fields = serializers.AppGrantedSerializer.Meta.only_fields
serializer_class = serializers.AppGrantedSerializer
filterset_fields = {
'id': ['exact'],
'name': ['exact'],

6
apps/perms/serializers/application/user_permission.py

@ -6,10 +6,10 @@ from django.utils.translation import ugettext_lazy as _
from assets.models import SystemUser
from applications.models import Application
from applications.serializers import ApplicationSerializerMixin
from applications.serializers import AppSerializerMixin
__all__ = [
'ApplicationGrantedSerializer', 'ApplicationSystemUserSerializer'
'AppGrantedSerializer', 'ApplicationSystemUserSerializer'
]
@ -26,7 +26,7 @@ class ApplicationSystemUserSerializer(serializers.ModelSerializer):
read_only_fields = fields
class ApplicationGrantedSerializer(ApplicationSerializerMixin, serializers.ModelSerializer):
class AppGrantedSerializer(AppSerializerMixin, serializers.ModelSerializer):
"""
被授权应用的数据结构
"""

3
apps/perms/signals_handler/__init__.py

@ -1,2 +1,3 @@
from . import common
from . import asset_permission
from . import app_permission
from . import refresh_perms

104
apps/perms/signals_handler/app_permission.py

@ -0,0 +1,104 @@
import itertools
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from users.models import User, UserGroup
from assets.models import SystemUser
from applications.models import Application
from common.utils import get_logger
from common.exceptions import M2MReverseNotAllowed
from common.decorator import on_transaction_commit
from common.const.signals import POST_ADD
from perms.models import ApplicationPermission
from applications.models import Account as AppAccount
logger = get_logger(__file__)
@receiver(m2m_changed, sender=ApplicationPermission.applications.through)
@on_transaction_commit
def on_app_permission_applications_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission applications change signal received")
system_users = instance.system_users.all()
set_remote_app_asset_system_users_if_need(instance, system_users=system_users)
apps = Application.objects.filter(pk__in=pk_set)
set_app_accounts(apps, system_users)
def set_app_accounts(apps, system_users):
for app, system_user in itertools.product(apps, system_users):
AppAccount.objects.get_or_create(
defaults={'app': app, 'systemuser': system_user},
app=app, systemuser=system_user
)
def set_remote_app_asset_system_users_if_need(instance: ApplicationPermission, system_users=None,
users=None, groups=None):
if not instance.category_remote_app:
return
attrs = instance.applications.all().values_list('attrs', flat=True)
asset_ids = [attr['asset'] for attr in attrs if attr.get('asset')]
if not asset_ids:
return
system_users = system_users or instance.system_users.all()
for system_user in system_users:
system_user.assets.add(*asset_ids)
if system_user.username_same_with_user:
users = users or instance.users.all()
groups = groups or instance.user_groups.all()
system_user.groups.add(*groups)
system_user.users.add(*users)
@receiver(m2m_changed, sender=ApplicationPermission.system_users.through)
@on_transaction_commit
def on_app_permission_system_users_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission system_users change signal received")
system_users = SystemUser.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, system_users=system_users)
apps = instance.applications.all()
set_app_accounts(apps, system_users)
@receiver(m2m_changed, sender=ApplicationPermission.users.through)
@on_transaction_commit
def on_app_permission_users_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission users change signal received")
users = User.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, users=users)
@receiver(m2m_changed, sender=ApplicationPermission.user_groups.through)
@on_transaction_commit
def on_app_permission_user_groups_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission user groups change signal received")
groups = UserGroup.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, groups=groups)

113
apps/perms/signals_handler/common.py → apps/perms/signals_handler/asset_permission.py

@ -3,19 +3,20 @@
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from users.models import User, UserGroup
from users.models import User
from assets.models import SystemUser
from applications.models import Application
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, ApplicationPermission
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 关联的动态系统用户相关联
@ -39,12 +40,13 @@ def on_user_groups_change(sender, instance, action, reverse, pk_set, **kwargs):
@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()
@ -55,12 +57,13 @@ def on_permission_nodes_changed(instance, action, reverse, pk_set, model, **kwar
@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)
@ -71,33 +74,38 @@ def on_permission_assets_changed(instance, action, reverse, pk_set, model, **kwa
@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)
users = instance.users.all().values_list('id', flat=True)
groups = instance.user_groups.all().values_list('id', flat=True)
for system_user in system_users:
system_user.nodes.add(*tuple(nodes))
system_user.assets.add(*tuple(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()
@ -109,13 +117,13 @@ def on_asset_permission_users_changed(instance, action, reverse, pk_set, model,
@receiver(m2m_changed, sender=AssetPermission.user_groups.through)
def on_asset_permission_user_groups_changed(instance, action, pk_set, model,
reverse, **kwargs):
@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()
@ -126,87 +134,6 @@ def on_asset_permission_user_groups_changed(instance, action, pk_set, model,
system_user.groups.add(*tuple(groups))
@receiver(m2m_changed, sender=ApplicationPermission.system_users.through)
def on_application_permission_system_users_changed(sender, instance: ApplicationPermission, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if not instance.category_remote_app:
return
if action != POST_ADD:
return
system_users = SystemUser.objects.filter(pk__in=pk_set)
logger.debug("Application permission system_users change signal received")
attrs = instance.applications.all().values_list('attrs', flat=True)
asset_ids = [attr['asset'] for attr in attrs if attr.get('asset')]
if not asset_ids:
return
for system_user in system_users:
system_user.assets.add(*asset_ids)
if system_user.username_same_with_user:
user_ids = instance.users.all().values_list('id', flat=True)
group_ids = instance.user_groups.all().values_list('id', flat=True)
system_user.groups.add(*group_ids)
system_user.users.add(*user_ids)
@receiver(m2m_changed, sender=ApplicationPermission.users.through)
def on_application_permission_users_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if not instance.category_remote_app:
return
if action != POST_ADD:
return
logger.debug("Application permission users change signal received")
user_ids = User.objects.filter(pk__in=pk_set).values_list('id', flat=True)
system_users = instance.system_users.all()
for system_user in system_users:
if system_user.username_same_with_user:
system_user.users.add(*user_ids)
@receiver(m2m_changed, sender=ApplicationPermission.user_groups.through)
def on_application_permission_user_groups_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if not instance.category_remote_app:
return
if action != POST_ADD:
return
logger.debug("Application permission user groups change signal received")
group_ids = UserGroup.objects.filter(pk__in=pk_set).values_list('id', flat=True)
system_users = instance.system_users.all()
for system_user in system_users:
if system_user.username_same_with_user:
system_user.groups.add(*group_ids)
@receiver(m2m_changed, sender=ApplicationPermission.applications.through)
def on_application_permission_applications_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if not instance.category_remote_app:
return
if action != POST_ADD:
return
attrs = Application.objects.filter(id__in=pk_set).values_list('attrs', flat=True)
asset_ids = [attr['asset'] for attr in attrs if attr.get('asset')]
if not asset_ids:
return
system_users = instance.system_users.all()
for system_user in system_users:
system_user.assets.add(*asset_ids)

3
requirements/requirements.txt

@ -115,3 +115,6 @@ azure-mgmt-subscription==1.0.0
qingcloud-sdk==1.2.12
django-simple-history==3.0.0
google-cloud-compute==0.5.0
PyMySQL==1.0.2
cx-Oracle==8.2.1
psycopg2-binary==2.9.1

Loading…
Cancel
Save