mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 08:32:48 +00:00
Compare commits
20 Commits
revert-162
...
v3.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dadad2069 | ||
|
|
33db6de372 | ||
|
|
0bea545b6f | ||
|
|
424066b38f | ||
|
|
13d895b22e | ||
|
|
d5c51a4f0e | ||
|
|
7c965706d4 | ||
|
|
f59499f77e | ||
|
|
98536cbc24 | ||
|
|
7c29e60a82 | ||
|
|
9e68d7d1a0 | ||
|
|
77b7134404 | ||
|
|
fb7a839c16 | ||
|
|
e80855068e | ||
|
|
5832246e5f | ||
|
|
dbef45e23b | ||
|
|
dedc42d775 | ||
|
|
f64073f48f | ||
|
|
70754ad748 | ||
|
|
3158980057 |
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.9-slim-buster as stage-build
|
FROM python:3.9-slim-bullseye as stage-build
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
@@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
|
|||||||
ADD . .
|
ADD . .
|
||||||
RUN cd utils && bash -ixeu build.sh
|
RUN cd utils && bash -ixeu build.sh
|
||||||
|
|
||||||
FROM python:3.9-slim-buster
|
FROM python:3.9-slim-bullseye
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
MAINTAINER JumpServer Team <ibuler@qq.com>
|
MAINTAINER JumpServer Team <ibuler@qq.com>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
- hosts: custom
|
- hosts: custom
|
||||||
gather_facts: no
|
gather_facts: no
|
||||||
vars:
|
vars:
|
||||||
|
ansible_shell_type: sh
|
||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Verify account
|
- name: Verify account (pyfreerdp)
|
||||||
ssh_ping:
|
rdp_ping:
|
||||||
login_host: "{{ jms_asset.address }}"
|
login_host: "{{ jms_asset.address }}"
|
||||||
login_port: "{{ jms_asset.port }}"
|
login_port: "{{ jms_asset.port }}"
|
||||||
login_user: "{{ account.username }}"
|
login_user: "{{ account.username }}"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Verify account
|
- name: Verify account (paramiko)
|
||||||
ssh_ping:
|
ssh_ping:
|
||||||
login_host: "{{ jms_asset.address }}"
|
login_host: "{{ jms_asset.address }}"
|
||||||
login_port: "{{ jms_asset.port }}"
|
login_port: "{{ jms_asset.port }}"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# Generated by Django 3.2.17 on 2023-06-06 10:57
|
# Generated by Django 3.2.17 on 2023-06-06 10:57
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
@@ -8,17 +7,20 @@ import common.db.fields
|
|||||||
|
|
||||||
def migrate_users_login_acls(apps, schema_editor):
|
def migrate_users_login_acls(apps, schema_editor):
|
||||||
login_acl_model = apps.get_model('acls', 'LoginACL')
|
login_acl_model = apps.get_model('acls', 'LoginACL')
|
||||||
name_used = defaultdict(int)
|
|
||||||
|
|
||||||
for login_acl in login_acl_model.objects.all():
|
name_used = []
|
||||||
name = login_acl.name
|
login_acls = []
|
||||||
if name_used[name] > 0:
|
for login_acl in login_acl_model.objects.all().select_related('user'):
|
||||||
login_acl.name += "_{}".format(name_used[name])
|
name = '{}_{}'.format(login_acl.name, login_acl.user.username)
|
||||||
name_used[name] += 1
|
if name.lower() in name_used:
|
||||||
|
name += '_{}'.format(str(login_acl.user_id)[:4])
|
||||||
|
name_used.append(name.lower())
|
||||||
|
login_acl.name = name
|
||||||
login_acl.users = {
|
login_acl.users = {
|
||||||
"type": "ids", "ids": [str(login_acl.user_id)]
|
"type": "ids", "ids": [str(login_acl.user_id)]
|
||||||
}
|
}
|
||||||
login_acl.save()
|
login_acls.append(login_acl)
|
||||||
|
login_acl_model.objects.bulk_update(login_acls, ['name', 'users'])
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class AssetFilterSet(BaseFilterSet):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def filter_protocols(queryset, name, value):
|
def filter_protocols(queryset, name, value):
|
||||||
value = value.split(',')
|
value = value.split(',')
|
||||||
return queryset.filter(protocols__name__in=value)
|
return queryset.filter(protocols__name__in=value).distinct()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def filter_labels(queryset, name, value):
|
def filter_labels(queryset, name, value):
|
||||||
@@ -91,7 +91,7 @@ class AssetFilterSet(BaseFilterSet):
|
|||||||
queryset = queryset.filter(labels__name=n, labels__value=v)
|
queryset = queryset.filter(labels__name=n, labels__value=v)
|
||||||
else:
|
else:
|
||||||
q = Q(labels__name__contains=value) | Q(labels__value__contains=value)
|
q = Q(labels__name__contains=value) | Q(labels__value__contains=value)
|
||||||
queryset = queryset.filter(q)
|
queryset = queryset.filter(q).distinct()
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
apps/assets/automations/ping/custom/rdp/main.yml
Normal file
15
apps/assets/automations/ping/custom/rdp/main.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
- hosts: custom
|
||||||
|
gather_facts: no
|
||||||
|
vars:
|
||||||
|
ansible_shell_type: sh
|
||||||
|
ansible_connection: local
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Test asset connection (pyfreerdp)
|
||||||
|
rdp_ping:
|
||||||
|
login_user: "{{ jms_account.username }}"
|
||||||
|
login_password: "{{ jms_account.secret }}"
|
||||||
|
login_host: "{{ jms_asset.address }}"
|
||||||
|
login_port: "{{ jms_asset.port }}"
|
||||||
|
login_secret_type: "{{ jms_account.secret_type }}"
|
||||||
|
login_private_key_path: "{{ jms_account.private_key_path }}"
|
||||||
13
apps/assets/automations/ping/custom/rdp/manifest.yml
Normal file
13
apps/assets/automations/ping/custom/rdp/manifest.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
id: ping_by_rdp
|
||||||
|
name: "{{ 'Ping by pyfreerdp' | trans }}"
|
||||||
|
category:
|
||||||
|
- device
|
||||||
|
- host
|
||||||
|
type:
|
||||||
|
- windows
|
||||||
|
method: ping
|
||||||
|
i18n:
|
||||||
|
Ping by pyfreerdp:
|
||||||
|
zh: 使用 Python 模块 pyfreerdp 测试主机可连接性
|
||||||
|
en: Ping by pyfreerdp module
|
||||||
|
ja: Pyfreerdpモジュールを使用してホストにPingする
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
ansible_connection: local
|
ansible_connection: local
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Test asset connection
|
- name: Test asset connection (paramiko)
|
||||||
ssh_ping:
|
ssh_ping:
|
||||||
login_user: "{{ jms_account.username }}"
|
login_user: "{{ jms_account.username }}"
|
||||||
login_password: "{{ jms_account.secret }}"
|
login_password: "{{ jms_account.secret }}"
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import django.db
|
import django.db
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
import common.db.fields
|
import common.db.fields
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ class Migration(migrations.Migration):
|
|||||||
primary_key=True, serialize=False, to='assets.asset')),
|
primary_key=True, serialize=False, to='assets.asset')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Host',
|
'verbose_name': 'Host',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
|||||||
@@ -137,6 +137,24 @@ def migrate_to_nodes(apps, *args):
|
|||||||
parent.save()
|
parent.save()
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_ori_host_to_devices(apps, *args):
|
||||||
|
device_model = apps.get_model('assets', 'Device')
|
||||||
|
asset_model = apps.get_model('assets', 'Asset')
|
||||||
|
host_model = apps.get_model('assets', 'Host')
|
||||||
|
hosts_need_migrate_to_device = host_model.objects.filter(asset_ptr__platform__category='device')
|
||||||
|
assets = asset_model.objects.filter(id__in=hosts_need_migrate_to_device.values_list('asset_ptr_id', flat=True))
|
||||||
|
assets_map = {asset.id: asset for asset in assets}
|
||||||
|
|
||||||
|
for host in hosts_need_migrate_to_device:
|
||||||
|
asset = assets_map.get(host.asset_ptr_id)
|
||||||
|
if not asset:
|
||||||
|
continue
|
||||||
|
device = device_model(asset_ptr_id=asset.id)
|
||||||
|
device.__dict__.update(asset.__dict__)
|
||||||
|
device.save()
|
||||||
|
host.delete(keep_parents=True)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('assets', '0097_auto_20220426_1558'),
|
('assets', '0097_auto_20220426_1558'),
|
||||||
@@ -146,5 +164,6 @@ class Migration(migrations.Migration):
|
|||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(migrate_database_to_asset),
|
migrations.RunPython(migrate_database_to_asset),
|
||||||
migrations.RunPython(migrate_cloud_to_asset),
|
migrations.RunPython(migrate_cloud_to_asset),
|
||||||
migrations.RunPython(migrate_to_nodes)
|
migrations.RunPython(migrate_to_nodes),
|
||||||
|
migrations.RunPython(migrate_ori_host_to_devices),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,16 +2,13 @@
|
|||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
from assets.const import AllTypes
|
|
||||||
|
|
||||||
|
|
||||||
def migrate_automation_push_account_params(apps, schema_editor):
|
def migrate_automation_push_account_params(apps, schema_editor):
|
||||||
platform_automation_model = apps.get_model('assets', 'PlatformAutomation')
|
platform_automation_model = apps.get_model('assets', 'PlatformAutomation')
|
||||||
platform_automation_methods = AllTypes.get_automation_methods()
|
|
||||||
methods_id_data_map = {
|
methods_id_data_map = {
|
||||||
i['id']: None if i['params_serializer'] is None else i['params_serializer']({}).data
|
'push_account_aix': {'sudo': '/bin/whoami', 'shell': '/bin/bash', 'home': '', 'groups': ''},
|
||||||
for i in platform_automation_methods
|
'push_account_posix': {'sudo': '/bin/whoami', 'shell': '/bin/bash', 'home': '', 'groups': ''},
|
||||||
if i['method'] == 'push_account'
|
'push_account_local_windows': {'groups': 'Users,Remote Desktop Users'},
|
||||||
}
|
}
|
||||||
automation_objs = []
|
automation_objs = []
|
||||||
for automation in platform_automation_model.objects.all():
|
for automation in platform_automation_model.objects.all():
|
||||||
|
|||||||
@@ -206,15 +206,14 @@ class Asset(NodesRelationMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseMode
|
|||||||
@lazyproperty
|
@lazyproperty
|
||||||
def auto_config(self):
|
def auto_config(self):
|
||||||
platform = self.platform
|
platform = self.platform
|
||||||
automation = self.platform.automation
|
|
||||||
auto_config = {
|
auto_config = {
|
||||||
'su_enabled': platform.su_enabled,
|
'su_enabled': platform.su_enabled,
|
||||||
'domain_enabled': platform.domain_enabled,
|
'domain_enabled': platform.domain_enabled,
|
||||||
'ansible_enabled': False
|
'ansible_enabled': False
|
||||||
}
|
}
|
||||||
|
automation = getattr(self.platform, 'automation', None)
|
||||||
if not automation:
|
if not automation:
|
||||||
return auto_config
|
return auto_config
|
||||||
|
|
||||||
auto_config.update(model_to_dict(automation))
|
auto_config.update(model_to_dict(automation))
|
||||||
return auto_config
|
return auto_config
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def _get_instance_field_value(
|
|||||||
if getattr(f, 'attname', None) in model_need_continue_fields:
|
if getattr(f, 'attname', None) in model_need_continue_fields:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
value = getattr(instance, f.name) or getattr(instance, f.attname)
|
value = getattr(instance, f.name, None) or getattr(instance, f.attname, None)
|
||||||
if not isinstance(value, bool) and not value:
|
if not isinstance(value, bool) and not value:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -328,13 +328,13 @@ class RelatedManager:
|
|||||||
q = Q()
|
q = Q()
|
||||||
if isinstance(val, str):
|
if isinstance(val, str):
|
||||||
val = [val]
|
val = [val]
|
||||||
|
if ['*'] in val:
|
||||||
|
return Q()
|
||||||
for ip in val:
|
for ip in val:
|
||||||
if not ip:
|
if not ip:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
if ip == '*':
|
if '/' in ip:
|
||||||
return Q()
|
|
||||||
elif '/' in ip:
|
|
||||||
network = ipaddress.ip_network(ip)
|
network = ipaddress.ip_network(ip)
|
||||||
ips = network.hosts()
|
ips = network.hosts()
|
||||||
q |= Q(**{"{}__in".format(name): ips})
|
q |= Q(**{"{}__in".format(name): ips})
|
||||||
@@ -378,7 +378,7 @@ class RelatedManager:
|
|||||||
|
|
||||||
if match == 'ip_in':
|
if match == 'ip_in':
|
||||||
q = cls.get_ip_in_q(name, val)
|
q = cls.get_ip_in_q(name, val)
|
||||||
elif match in ("exact", "contains", "startswith", "endswith", "gte", "lte", "gt", "lt"):
|
elif match in ("contains", "startswith", "endswith", "gte", "lte", "gt", "lt"):
|
||||||
lookup = "{}__{}".format(name, match)
|
lookup = "{}__{}".format(name, match)
|
||||||
q = Q(**{lookup: val})
|
q = Q(**{lookup: val})
|
||||||
elif match == 'regex':
|
elif match == 'regex':
|
||||||
@@ -470,9 +470,9 @@ class JSONManyToManyDescriptor:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if rule_match == 'in':
|
if rule_match == 'in':
|
||||||
res &= value in rule_value
|
res &= value in rule_value or '*' in rule_value
|
||||||
elif rule_match == 'exact':
|
elif rule_match == 'exact':
|
||||||
res &= value == rule_value
|
res &= value == rule_value or rule_value == '*'
|
||||||
elif rule_match == 'contains':
|
elif rule_match == 'contains':
|
||||||
res &= rule_value in value
|
res &= rule_value in value
|
||||||
elif rule_match == 'startswith':
|
elif rule_match == 'startswith':
|
||||||
@@ -499,7 +499,7 @@ class JSONManyToManyDescriptor:
|
|||||||
elif rule['match'] == 'ip_in':
|
elif rule['match'] == 'ip_in':
|
||||||
if isinstance(rule_value, str):
|
if isinstance(rule_value, str):
|
||||||
rule_value = [rule_value]
|
rule_value = [rule_value]
|
||||||
res &= contains_ip(value, rule_value)
|
res &= '*' in rule_value or contains_ip(value, rule_value)
|
||||||
elif rule['match'] == 'm2m':
|
elif rule['match'] == 'm2m':
|
||||||
if isinstance(value, Manager):
|
if isinstance(value, Manager):
|
||||||
value = value.values_list('id', flat=True)
|
value = value.values_list('id', flat=True)
|
||||||
|
|||||||
@@ -44,19 +44,21 @@ def set_default_by_type(tp, data, field_info):
|
|||||||
|
|
||||||
def create_serializer_class(serializer_name, fields_info):
|
def create_serializer_class(serializer_name, fields_info):
|
||||||
serializer_fields = {}
|
serializer_fields = {}
|
||||||
fields_name = ['name', 'label', 'default', 'type', 'help_text']
|
fields_name = ['name', 'label', 'default', 'required', 'type', 'help_text']
|
||||||
|
|
||||||
for i, field_info in enumerate(fields_info):
|
for i, field_info in enumerate(fields_info):
|
||||||
data = {k: field_info.get(k) for k in fields_name}
|
data = {k: field_info.get(k) for k in fields_name}
|
||||||
field_type = data.pop('type', 'str')
|
field_type = data.pop('type', 'str')
|
||||||
|
|
||||||
if data.get('default') is None:
|
# 用户定义 default 和 required 可能会冲突, 所以要处理一下
|
||||||
|
default = data.get('default', '')
|
||||||
|
if default not in ['', None]:
|
||||||
|
data['required'] = False
|
||||||
|
else:
|
||||||
data.pop('default', None)
|
data.pop('default', None)
|
||||||
data['required'] = field_info.get('required', True)
|
data['required'] = True
|
||||||
data = set_default_by_type(field_type, data, field_info)
|
data = set_default_by_type(field_type, data, field_info)
|
||||||
data = set_default_if_need(data, i)
|
data = set_default_if_need(data, i)
|
||||||
if data.get('default', None) is not None:
|
|
||||||
data['required'] = False
|
|
||||||
field_name = data.pop('name')
|
field_name = data.pop('name')
|
||||||
field_class = type_field_map.get(field_type, serializers.CharField)
|
field_class = type_field_map.get(field_type, serializers.CharField)
|
||||||
serializer_fields[field_name] = field_class(**data)
|
serializer_fields[field_name] = field_class(**data)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a76aa384867a4732eb7d2365515a1a972502ebadcba4de8236c1dcb3c5c7fdd2
|
oid sha256:cd4fb6a0396c8636f8a36645354a5102790c020d73cdeb1f0e1d1f1b34ea39e9
|
||||||
size 145757
|
size 145760
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-14 20:40+0800\n"
|
"POT-Creation-Date: 2023-06-15 15:35+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -28,7 +28,7 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。"
|
|||||||
#: authentication/confirm/password.py:9 authentication/forms.py:32
|
#: authentication/confirm/password.py:9 authentication/forms.py:32
|
||||||
#: authentication/templates/authentication/login.html:274
|
#: authentication/templates/authentication/login.html:274
|
||||||
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
|
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
|
||||||
#: users/forms/profile.py:22 users/serializers/user.py:105
|
#: users/forms/profile.py:22 users/serializers/user.py:104
|
||||||
#: users/templates/users/_msg_user_created.html:13
|
#: users/templates/users/_msg_user_created.html:13
|
||||||
#: users/templates/users/user_password_verify.html:18
|
#: users/templates/users/user_password_verify.html:18
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:28
|
#: xpack/plugins/cloud/serializers/account_attrs.py:28
|
||||||
@@ -505,7 +505,7 @@ msgstr "特権アカウント"
|
|||||||
#: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39
|
#: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39
|
||||||
#: assets/models/label.py:22
|
#: assets/models/label.py:22
|
||||||
#: authentication/serializers/connect_token_secret.py:114
|
#: authentication/serializers/connect_token_secret.py:114
|
||||||
#: terminal/models/applet/applet.py:39 users/serializers/user.py:170
|
#: terminal/models/applet/applet.py:39 users/serializers/user.py:169
|
||||||
msgid "Is active"
|
msgid "Is active"
|
||||||
msgstr "アクティブです。"
|
msgstr "アクティブです。"
|
||||||
|
|
||||||
@@ -1281,7 +1281,7 @@ msgstr "資産ハードウェア情報の収集"
|
|||||||
|
|
||||||
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
|
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
|
||||||
msgid "Custom info"
|
msgid "Custom info"
|
||||||
msgstr "自动化信息"
|
msgstr "カスタム属性"
|
||||||
|
|
||||||
#: assets/models/asset/common.py:335
|
#: assets/models/asset/common.py:335
|
||||||
msgid "Can refresh asset hardware info"
|
msgid "Can refresh asset hardware info"
|
||||||
@@ -1645,8 +1645,8 @@ msgstr "プロトコルが必要です: {}"
|
|||||||
msgid "Default database"
|
msgid "Default database"
|
||||||
msgstr "デフォルト・データベース"
|
msgstr "デフォルト・データベース"
|
||||||
|
|
||||||
#: assets/serializers/asset/database.py:28 common/db/fields.py:570
|
#: assets/serializers/asset/database.py:28 common/db/fields.py:579
|
||||||
#: common/db/fields.py:575 common/serializers/fields.py:104
|
#: common/db/fields.py:584 common/serializers/fields.py:104
|
||||||
#: tickets/serializers/ticket/common.py:58
|
#: tickets/serializers/ticket/common.py:58
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:56
|
#: xpack/plugins/cloud/serializers/account_attrs.py:56
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:79
|
#: xpack/plugins/cloud/serializers/account_attrs.py:79
|
||||||
@@ -2654,7 +2654,7 @@ msgstr "アクション"
|
|||||||
|
|
||||||
#: authentication/serializers/connection_token.py:44
|
#: authentication/serializers/connection_token.py:44
|
||||||
#: perms/serializers/permission.py:38 perms/serializers/permission.py:57
|
#: perms/serializers/permission.py:38 perms/serializers/permission.py:57
|
||||||
#: users/serializers/user.py:97 users/serializers/user.py:173
|
#: users/serializers/user.py:96 users/serializers/user.py:172
|
||||||
msgid "Is expired"
|
msgid "Is expired"
|
||||||
msgstr "期限切れです"
|
msgstr "期限切れです"
|
||||||
|
|
||||||
@@ -2680,8 +2680,8 @@ msgid "The {} cannot be empty"
|
|||||||
msgstr "{} 空にしてはならない"
|
msgstr "{} 空にしてはならない"
|
||||||
|
|
||||||
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
|
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
|
||||||
#: perms/serializers/permission.py:58 users/serializers/user.py:98
|
#: perms/serializers/permission.py:58 users/serializers/user.py:97
|
||||||
#: users/serializers/user.py:171
|
#: users/serializers/user.py:170
|
||||||
msgid "Is valid"
|
msgid "Is valid"
|
||||||
msgstr "有効です"
|
msgstr "有効です"
|
||||||
|
|
||||||
@@ -3116,7 +3116,7 @@ msgstr "テキストフィールドへのマーシャルデータ"
|
|||||||
msgid "Encrypt field using Secret Key"
|
msgid "Encrypt field using Secret Key"
|
||||||
msgstr "Secret Keyを使用したフィールドの暗号化"
|
msgstr "Secret Keyを使用したフィールドの暗号化"
|
||||||
|
|
||||||
#: common/db/fields.py:558
|
#: common/db/fields.py:567
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
|
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
|
||||||
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
|
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
|
||||||
@@ -3126,19 +3126,19 @@ msgstr ""
|
|||||||
"{'type':'ids','ids':[]}或 #タイプ:属性、属性:[#名前:ip、照合:正確、"
|
"{'type':'ids','ids':[]}或 #タイプ:属性、属性:[#名前:ip、照合:正確、"
|
||||||
"値:1.1.1.1}"
|
"値:1.1.1.1}"
|
||||||
|
|
||||||
#: common/db/fields.py:565
|
#: common/db/fields.py:574
|
||||||
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
|
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
|
||||||
msgstr "無効なタイプです。all、ids、またはattrsでなければなりません"
|
msgstr "無効なタイプです。all、ids、またはattrsでなければなりません"
|
||||||
|
|
||||||
#: common/db/fields.py:568
|
#: common/db/fields.py:577
|
||||||
msgid "Invalid ids for ids, should be a list"
|
msgid "Invalid ids for ids, should be a list"
|
||||||
msgstr "無効なID、リストでなければなりません"
|
msgstr "無効なID、リストでなければなりません"
|
||||||
|
|
||||||
#: common/db/fields.py:573 common/db/fields.py:578
|
#: common/db/fields.py:582 common/db/fields.py:587
|
||||||
msgid "Invalid attrs, should be a list of dict"
|
msgid "Invalid attrs, should be a list of dict"
|
||||||
msgstr "無効な属性、dictリストでなければなりません"
|
msgstr "無効な属性、dictリストでなければなりません"
|
||||||
|
|
||||||
#: common/db/fields.py:580
|
#: common/db/fields.py:589
|
||||||
msgid "Invalid attrs, should be has name and value"
|
msgid "Invalid attrs, should be has name and value"
|
||||||
msgstr "名前と値が必要な無効な属性"
|
msgstr "名前と値が必要な無効な属性"
|
||||||
|
|
||||||
@@ -3654,7 +3654,7 @@ msgstr "Material"
|
|||||||
msgid "Material Type"
|
msgid "Material Type"
|
||||||
msgstr "Material を選択してオプションを設定します。"
|
msgstr "Material を選択してオプションを設定します。"
|
||||||
|
|
||||||
#: ops/models/job.py:460
|
#: ops/models/job.py:461
|
||||||
msgid "Job Execution"
|
msgid "Job Execution"
|
||||||
msgstr "ジョブ実行"
|
msgstr "ジョブ実行"
|
||||||
|
|
||||||
@@ -6711,7 +6711,7 @@ msgstr "公開キー"
|
|||||||
msgid "Force enable"
|
msgid "Force enable"
|
||||||
msgstr "強制有効"
|
msgstr "強制有効"
|
||||||
|
|
||||||
#: users/models/user.py:765 users/serializers/user.py:172
|
#: users/models/user.py:765 users/serializers/user.py:171
|
||||||
msgid "Is service account"
|
msgid "Is service account"
|
||||||
msgstr "サービスアカウントです"
|
msgstr "サービスアカウントです"
|
||||||
|
|
||||||
@@ -6723,7 +6723,7 @@ msgstr "アバター"
|
|||||||
msgid "Wechat"
|
msgid "Wechat"
|
||||||
msgstr "微信"
|
msgstr "微信"
|
||||||
|
|
||||||
#: users/models/user.py:773 users/serializers/user.py:109
|
#: users/models/user.py:773 users/serializers/user.py:108
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "電話"
|
msgstr "電話"
|
||||||
|
|
||||||
@@ -6740,7 +6740,7 @@ msgid "Secret key"
|
|||||||
msgstr "秘密キー"
|
msgstr "秘密キー"
|
||||||
|
|
||||||
#: users/models/user.py:794 users/serializers/profile.py:149
|
#: users/models/user.py:794 users/serializers/profile.py:149
|
||||||
#: users/serializers/user.py:169
|
#: users/serializers/user.py:168
|
||||||
msgid "Is first login"
|
msgid "Is first login"
|
||||||
msgstr "最初のログインです"
|
msgstr "最初のログインです"
|
||||||
|
|
||||||
@@ -6823,55 +6823,55 @@ msgstr "新しいパスワードを最後の {} 個のパスワードにする
|
|||||||
msgid "The newly set password is inconsistent"
|
msgid "The newly set password is inconsistent"
|
||||||
msgstr "新しく設定されたパスワードが一致しない"
|
msgstr "新しく設定されたパスワードが一致しない"
|
||||||
|
|
||||||
#: users/serializers/user.py:43
|
#: users/serializers/user.py:42
|
||||||
msgid "System roles"
|
msgid "System roles"
|
||||||
msgstr "システムの役割"
|
msgstr "システムの役割"
|
||||||
|
|
||||||
#: users/serializers/user.py:47
|
#: users/serializers/user.py:46
|
||||||
msgid "Org roles"
|
msgid "Org roles"
|
||||||
msgstr "組織ロール"
|
msgstr "組織ロール"
|
||||||
|
|
||||||
#: users/serializers/user.py:90
|
#: users/serializers/user.py:89
|
||||||
msgid "Password strategy"
|
msgid "Password strategy"
|
||||||
msgstr "パスワード戦略"
|
msgstr "パスワード戦略"
|
||||||
|
|
||||||
#: users/serializers/user.py:92
|
#: users/serializers/user.py:91
|
||||||
msgid "MFA enabled"
|
msgid "MFA enabled"
|
||||||
msgstr "MFA有効化"
|
msgstr "MFA有効化"
|
||||||
|
|
||||||
#: users/serializers/user.py:94
|
#: users/serializers/user.py:93
|
||||||
msgid "MFA force enabled"
|
msgid "MFA force enabled"
|
||||||
msgstr "MFAフォース有効化"
|
msgstr "MFAフォース有効化"
|
||||||
|
|
||||||
#: users/serializers/user.py:96
|
#: users/serializers/user.py:95
|
||||||
msgid "Login blocked"
|
msgid "Login blocked"
|
||||||
msgstr "ログインがロックされました"
|
msgstr "ログインがロックされました"
|
||||||
|
|
||||||
#: users/serializers/user.py:99 users/serializers/user.py:177
|
#: users/serializers/user.py:98 users/serializers/user.py:176
|
||||||
msgid "Is OTP bound"
|
msgid "Is OTP bound"
|
||||||
msgstr "仮想MFAがバインドされているか"
|
msgstr "仮想MFAがバインドされているか"
|
||||||
|
|
||||||
#: users/serializers/user.py:101
|
#: users/serializers/user.py:100
|
||||||
msgid "Can public key authentication"
|
msgid "Can public key authentication"
|
||||||
msgstr "公開鍵認証が可能"
|
msgstr "公開鍵認証が可能"
|
||||||
|
|
||||||
#: users/serializers/user.py:174
|
#: users/serializers/user.py:173
|
||||||
msgid "Avatar url"
|
msgid "Avatar url"
|
||||||
msgstr "アバターURL"
|
msgstr "アバターURL"
|
||||||
|
|
||||||
#: users/serializers/user.py:178
|
#: users/serializers/user.py:177
|
||||||
msgid "MFA level"
|
msgid "MFA level"
|
||||||
msgstr "MFA レベル"
|
msgstr "MFA レベル"
|
||||||
|
|
||||||
#: users/serializers/user.py:284
|
#: users/serializers/user.py:283
|
||||||
msgid "Select users"
|
msgid "Select users"
|
||||||
msgstr "ユーザーの選択"
|
msgstr "ユーザーの選択"
|
||||||
|
|
||||||
#: users/serializers/user.py:285
|
#: users/serializers/user.py:284
|
||||||
msgid "For security, only list several users"
|
msgid "For security, only list several users"
|
||||||
msgstr "セキュリティのために、複数のユーザーのみをリストします"
|
msgstr "セキュリティのために、複数のユーザーのみをリストします"
|
||||||
|
|
||||||
#: users/serializers/user.py:318
|
#: users/serializers/user.py:317
|
||||||
msgid "name not unique"
|
msgid "name not unique"
|
||||||
msgstr "名前が一意ではない"
|
msgstr "名前が一意ではない"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f32e327dd50762b76d209f80b7de470df6faf5242af383dd6152b0c7ea5a7974
|
oid sha256:0efb248e80873f34d20f0fc3d4dd5c5a346048cb683c2b6bda3df939697fc52c
|
||||||
size 119261
|
size 119261
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-14 20:40+0800\n"
|
"POT-Creation-Date: 2023-06-15 15:35+0800\n"
|
||||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||||
@@ -27,7 +27,7 @@ msgstr "参数 'action' 必须是 [{}]"
|
|||||||
#: authentication/confirm/password.py:9 authentication/forms.py:32
|
#: authentication/confirm/password.py:9 authentication/forms.py:32
|
||||||
#: authentication/templates/authentication/login.html:274
|
#: authentication/templates/authentication/login.html:274
|
||||||
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
|
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
|
||||||
#: users/forms/profile.py:22 users/serializers/user.py:105
|
#: users/forms/profile.py:22 users/serializers/user.py:104
|
||||||
#: users/templates/users/_msg_user_created.html:13
|
#: users/templates/users/_msg_user_created.html:13
|
||||||
#: users/templates/users/user_password_verify.html:18
|
#: users/templates/users/user_password_verify.html:18
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:28
|
#: xpack/plugins/cloud/serializers/account_attrs.py:28
|
||||||
@@ -504,7 +504,7 @@ msgstr "特权账号"
|
|||||||
#: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39
|
#: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39
|
||||||
#: assets/models/label.py:22
|
#: assets/models/label.py:22
|
||||||
#: authentication/serializers/connect_token_secret.py:114
|
#: authentication/serializers/connect_token_secret.py:114
|
||||||
#: terminal/models/applet/applet.py:39 users/serializers/user.py:170
|
#: terminal/models/applet/applet.py:39 users/serializers/user.py:169
|
||||||
msgid "Is active"
|
msgid "Is active"
|
||||||
msgstr "激活"
|
msgstr "激活"
|
||||||
|
|
||||||
@@ -1274,7 +1274,7 @@ msgstr "收集资产硬件信息"
|
|||||||
|
|
||||||
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
|
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
|
||||||
msgid "Custom info"
|
msgid "Custom info"
|
||||||
msgstr "自动化信息"
|
msgstr "自定义属性"
|
||||||
|
|
||||||
#: assets/models/asset/common.py:335
|
#: assets/models/asset/common.py:335
|
||||||
msgid "Can refresh asset hardware info"
|
msgid "Can refresh asset hardware info"
|
||||||
@@ -1636,8 +1636,8 @@ msgstr "协议是必填的: {}"
|
|||||||
msgid "Default database"
|
msgid "Default database"
|
||||||
msgstr "默认数据库"
|
msgstr "默认数据库"
|
||||||
|
|
||||||
#: assets/serializers/asset/database.py:28 common/db/fields.py:570
|
#: assets/serializers/asset/database.py:28 common/db/fields.py:579
|
||||||
#: common/db/fields.py:575 common/serializers/fields.py:104
|
#: common/db/fields.py:584 common/serializers/fields.py:104
|
||||||
#: tickets/serializers/ticket/common.py:58
|
#: tickets/serializers/ticket/common.py:58
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:56
|
#: xpack/plugins/cloud/serializers/account_attrs.py:56
|
||||||
#: xpack/plugins/cloud/serializers/account_attrs.py:79
|
#: xpack/plugins/cloud/serializers/account_attrs.py:79
|
||||||
@@ -2629,7 +2629,7 @@ msgstr "动作"
|
|||||||
|
|
||||||
#: authentication/serializers/connection_token.py:44
|
#: authentication/serializers/connection_token.py:44
|
||||||
#: perms/serializers/permission.py:38 perms/serializers/permission.py:57
|
#: perms/serializers/permission.py:38 perms/serializers/permission.py:57
|
||||||
#: users/serializers/user.py:97 users/serializers/user.py:173
|
#: users/serializers/user.py:96 users/serializers/user.py:172
|
||||||
msgid "Is expired"
|
msgid "Is expired"
|
||||||
msgstr "已过期"
|
msgstr "已过期"
|
||||||
|
|
||||||
@@ -2653,8 +2653,8 @@ msgid "The {} cannot be empty"
|
|||||||
msgstr "{} 不能为空"
|
msgstr "{} 不能为空"
|
||||||
|
|
||||||
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
|
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
|
||||||
#: perms/serializers/permission.py:58 users/serializers/user.py:98
|
#: perms/serializers/permission.py:58 users/serializers/user.py:97
|
||||||
#: users/serializers/user.py:171
|
#: users/serializers/user.py:170
|
||||||
msgid "Is valid"
|
msgid "Is valid"
|
||||||
msgstr "是否有效"
|
msgstr "是否有效"
|
||||||
|
|
||||||
@@ -3081,7 +3081,7 @@ msgstr "编码数据为 text"
|
|||||||
msgid "Encrypt field using Secret Key"
|
msgid "Encrypt field using Secret Key"
|
||||||
msgstr "加密的字段"
|
msgstr "加密的字段"
|
||||||
|
|
||||||
#: common/db/fields.py:558
|
#: common/db/fields.py:567
|
||||||
msgid ""
|
msgid ""
|
||||||
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
|
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
|
||||||
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
|
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
|
||||||
@@ -3091,19 +3091,19 @@ msgstr ""
|
|||||||
"{'type': 'attrs', 'attrs': [{'name': 'ip', 'match': 'exact', 'value': "
|
"{'type': 'attrs', 'attrs': [{'name': 'ip', 'match': 'exact', 'value': "
|
||||||
"'1.1.1.1'}}"
|
"'1.1.1.1'}}"
|
||||||
|
|
||||||
#: common/db/fields.py:565
|
#: common/db/fields.py:574
|
||||||
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
|
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
|
||||||
msgstr "无效类型,应为 all、ids 或 attrs"
|
msgstr "无效类型,应为 all、ids 或 attrs"
|
||||||
|
|
||||||
#: common/db/fields.py:568
|
#: common/db/fields.py:577
|
||||||
msgid "Invalid ids for ids, should be a list"
|
msgid "Invalid ids for ids, should be a list"
|
||||||
msgstr "无效的ID,应为列表"
|
msgstr "无效的ID,应为列表"
|
||||||
|
|
||||||
#: common/db/fields.py:573 common/db/fields.py:578
|
#: common/db/fields.py:582 common/db/fields.py:587
|
||||||
msgid "Invalid attrs, should be a list of dict"
|
msgid "Invalid attrs, should be a list of dict"
|
||||||
msgstr "无效的属性,应为dict列表"
|
msgstr "无效的属性,应为dict列表"
|
||||||
|
|
||||||
#: common/db/fields.py:580
|
#: common/db/fields.py:589
|
||||||
msgid "Invalid attrs, should be has name and value"
|
msgid "Invalid attrs, should be has name and value"
|
||||||
msgstr "无效属性,应具有名称和值"
|
msgstr "无效属性,应具有名称和值"
|
||||||
|
|
||||||
@@ -3612,7 +3612,7 @@ msgstr "Material"
|
|||||||
msgid "Material Type"
|
msgid "Material Type"
|
||||||
msgstr "Material 类型"
|
msgstr "Material 类型"
|
||||||
|
|
||||||
#: ops/models/job.py:460
|
#: ops/models/job.py:461
|
||||||
msgid "Job Execution"
|
msgid "Job Execution"
|
||||||
msgstr "作业执行"
|
msgstr "作业执行"
|
||||||
|
|
||||||
@@ -6618,7 +6618,7 @@ msgstr "SSH公钥"
|
|||||||
msgid "Force enable"
|
msgid "Force enable"
|
||||||
msgstr "强制启用"
|
msgstr "强制启用"
|
||||||
|
|
||||||
#: users/models/user.py:765 users/serializers/user.py:172
|
#: users/models/user.py:765 users/serializers/user.py:171
|
||||||
msgid "Is service account"
|
msgid "Is service account"
|
||||||
msgstr "服务账号"
|
msgstr "服务账号"
|
||||||
|
|
||||||
@@ -6630,7 +6630,7 @@ msgstr "头像"
|
|||||||
msgid "Wechat"
|
msgid "Wechat"
|
||||||
msgstr "微信"
|
msgstr "微信"
|
||||||
|
|
||||||
#: users/models/user.py:773 users/serializers/user.py:109
|
#: users/models/user.py:773 users/serializers/user.py:108
|
||||||
msgid "Phone"
|
msgid "Phone"
|
||||||
msgstr "手机"
|
msgstr "手机"
|
||||||
|
|
||||||
@@ -6647,7 +6647,7 @@ msgid "Secret key"
|
|||||||
msgstr "Secret key"
|
msgstr "Secret key"
|
||||||
|
|
||||||
#: users/models/user.py:794 users/serializers/profile.py:149
|
#: users/models/user.py:794 users/serializers/profile.py:149
|
||||||
#: users/serializers/user.py:169
|
#: users/serializers/user.py:168
|
||||||
msgid "Is first login"
|
msgid "Is first login"
|
||||||
msgstr "首次登录"
|
msgstr "首次登录"
|
||||||
|
|
||||||
@@ -6730,55 +6730,55 @@ msgstr "新密码不能是最近 {} 次的密码"
|
|||||||
msgid "The newly set password is inconsistent"
|
msgid "The newly set password is inconsistent"
|
||||||
msgstr "两次密码不一致"
|
msgstr "两次密码不一致"
|
||||||
|
|
||||||
#: users/serializers/user.py:43
|
#: users/serializers/user.py:42
|
||||||
msgid "System roles"
|
msgid "System roles"
|
||||||
msgstr "系统角色"
|
msgstr "系统角色"
|
||||||
|
|
||||||
#: users/serializers/user.py:47
|
#: users/serializers/user.py:46
|
||||||
msgid "Org roles"
|
msgid "Org roles"
|
||||||
msgstr "组织角色"
|
msgstr "组织角色"
|
||||||
|
|
||||||
#: users/serializers/user.py:90
|
#: users/serializers/user.py:89
|
||||||
msgid "Password strategy"
|
msgid "Password strategy"
|
||||||
msgstr "密码策略"
|
msgstr "密码策略"
|
||||||
|
|
||||||
#: users/serializers/user.py:92
|
#: users/serializers/user.py:91
|
||||||
msgid "MFA enabled"
|
msgid "MFA enabled"
|
||||||
msgstr "MFA 已启用"
|
msgstr "MFA 已启用"
|
||||||
|
|
||||||
#: users/serializers/user.py:94
|
#: users/serializers/user.py:93
|
||||||
msgid "MFA force enabled"
|
msgid "MFA force enabled"
|
||||||
msgstr "强制 MFA"
|
msgstr "强制 MFA"
|
||||||
|
|
||||||
#: users/serializers/user.py:96
|
#: users/serializers/user.py:95
|
||||||
msgid "Login blocked"
|
msgid "Login blocked"
|
||||||
msgstr "登录被锁定"
|
msgstr "登录被锁定"
|
||||||
|
|
||||||
#: users/serializers/user.py:99 users/serializers/user.py:177
|
#: users/serializers/user.py:98 users/serializers/user.py:176
|
||||||
msgid "Is OTP bound"
|
msgid "Is OTP bound"
|
||||||
msgstr "是否绑定了虚拟 MFA"
|
msgstr "是否绑定了虚拟 MFA"
|
||||||
|
|
||||||
#: users/serializers/user.py:101
|
#: users/serializers/user.py:100
|
||||||
msgid "Can public key authentication"
|
msgid "Can public key authentication"
|
||||||
msgstr "可以使用公钥认证"
|
msgstr "可以使用公钥认证"
|
||||||
|
|
||||||
#: users/serializers/user.py:174
|
#: users/serializers/user.py:173
|
||||||
msgid "Avatar url"
|
msgid "Avatar url"
|
||||||
msgstr "头像路径"
|
msgstr "头像路径"
|
||||||
|
|
||||||
#: users/serializers/user.py:178
|
#: users/serializers/user.py:177
|
||||||
msgid "MFA level"
|
msgid "MFA level"
|
||||||
msgstr "MFA 级别"
|
msgstr "MFA 级别"
|
||||||
|
|
||||||
#: users/serializers/user.py:284
|
#: users/serializers/user.py:283
|
||||||
msgid "Select users"
|
msgid "Select users"
|
||||||
msgstr "选择用户"
|
msgstr "选择用户"
|
||||||
|
|
||||||
#: users/serializers/user.py:285
|
#: users/serializers/user.py:284
|
||||||
msgid "For security, only list several users"
|
msgid "For security, only list several users"
|
||||||
msgstr "为了安全,仅列出几个用户"
|
msgstr "为了安全,仅列出几个用户"
|
||||||
|
|
||||||
#: users/serializers/user.py:318
|
#: users/serializers/user.py:317
|
||||||
msgid "name not unique"
|
msgid "name not unique"
|
||||||
msgstr "名称重复"
|
msgstr "名称重复"
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class Message(metaclass=MessageType):
|
|||||||
return self.markdown_msg
|
return self.markdown_msg
|
||||||
|
|
||||||
def get_feishu_msg(self) -> dict:
|
def get_feishu_msg(self) -> dict:
|
||||||
return self.text_msg
|
return self.markdown_msg
|
||||||
|
|
||||||
def get_email_msg(self) -> dict:
|
def get_email_msg(self) -> dict:
|
||||||
return self.html_msg_with_sign
|
return self.html_msg_with_sign
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ def create_system_messages(app_config: AppConfig, **kwargs):
|
|||||||
sub, created = SystemMsgSubscription.objects.get_or_create(message_type=message_type)
|
sub, created = SystemMsgSubscription.objects.get_or_create(message_type=message_type)
|
||||||
if created:
|
if created:
|
||||||
obj.post_insert_to_db(sub)
|
obj.post_insert_to_db(sub)
|
||||||
logger.info(
|
logger.info(f'Create MsgSubscription: package={app_config.module.__package__} type={message_type}')
|
||||||
f'Create SystemMsgSubscription: package={app_config.module.__package__} type={message_type}')
|
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -109,21 +109,21 @@ class DefaultCallback:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def playbook_on_stats(self, event_data, **kwargs):
|
def playbook_on_stats(self, event_data, **kwargs):
|
||||||
failed = []
|
|
||||||
error_func = lambda err, task_detail: err + f"{task_detail[0]}: {task_detail[1]['stderr']};"
|
error_func = lambda err, task_detail: err + f"{task_detail[0]}: {task_detail[1]['stderr']};"
|
||||||
for tp in ['dark', 'failures']:
|
for tp in ['dark', 'failures']:
|
||||||
for host, tasks in self.result[tp].items():
|
for host, tasks in self.result[tp].items():
|
||||||
failed.append(host)
|
|
||||||
error = reduce(error_func, tasks.items(), '').strip(';')
|
error = reduce(error_func, tasks.items(), '').strip(';')
|
||||||
self.summary[tp][host] = error
|
self.summary[tp][host] = error
|
||||||
|
failures = list(self.result['failures'].keys())
|
||||||
|
dark_or_failures = list(self.result['dark'].keys()) + failures
|
||||||
|
|
||||||
for host, tasks in self.result.get('ignored', {}).items():
|
for host, tasks in self.result.get('ignored', {}).items():
|
||||||
ignore_errors = reduce(error_func, tasks.items(), '').strip(';')
|
ignore_errors = reduce(error_func, tasks.items(), '').strip(';')
|
||||||
if host in failed:
|
if host in failures:
|
||||||
self.summary['failures'][host] += {ignore_errors}
|
self.summary['failures'][host] += {ignore_errors}
|
||||||
|
|
||||||
self.summary['ok'] = list(set(self.result['ok'].keys()) - set(failed))
|
self.summary['ok'] = list(set(self.result['ok'].keys()) - set(dark_or_failures))
|
||||||
self.summary['skipped'] = list(set(self.result['skipped'].keys()) - set(failed))
|
self.summary['skipped'] = list(set(self.result['skipped'].keys()) - set(dark_or_failures))
|
||||||
|
|
||||||
def playbook_on_include(self, event_data, **kwargs):
|
def playbook_on_include(self, event_data, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -39,10 +39,6 @@ import pyfreerdp
|
|||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
from ops.ansible.modules_utils.custom_common import (
|
|
||||||
common_argument_spec
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================
|
# =========================================
|
||||||
# Module execution.
|
# Module execution.
|
||||||
@@ -55,6 +51,18 @@ class Param(NamedTuple):
|
|||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
def common_argument_spec():
|
||||||
|
options = dict(
|
||||||
|
login_host=dict(type='str', required=False, default='localhost'),
|
||||||
|
login_port=dict(type='int', required=False, default=22),
|
||||||
|
login_user=dict(type='str', required=False, default='root'),
|
||||||
|
login_password=dict(type='str', required=False, no_log=True),
|
||||||
|
login_secret_type=dict(type='str', required=False, default='password'),
|
||||||
|
login_private_key_path=dict(type='str', required=False, no_log=True),
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
options = common_argument_spec()
|
options = common_argument_spec()
|
||||||
module = AnsibleModule(argument_spec=options, supports_check_mode=True)
|
module = AnsibleModule(argument_spec=options, supports_check_mode=True)
|
||||||
|
|||||||
@@ -10,12 +10,11 @@ from django.db import models
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from assets.const import Protocol
|
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from common.utils import get_object_or_none, lazyproperty
|
from common.utils import get_object_or_none, lazyproperty
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from terminal.backends import get_multi_command_storage
|
from terminal.backends import get_multi_command_storage
|
||||||
from terminal.const import SessionType
|
from terminal.const import SessionType, TerminalType
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
@@ -112,6 +111,7 @@ class Session(OrgModelMixin):
|
|||||||
return rel_path
|
return rel_path
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asset_obj(self):
|
def asset_obj(self):
|
||||||
return Asset.objects.get(id=self.asset_id)
|
return Asset.objects.get(id=self.asset_id)
|
||||||
@@ -132,10 +132,7 @@ class Session(OrgModelMixin):
|
|||||||
if self.type != SessionType.normal:
|
if self.type != SessionType.normal:
|
||||||
# 会话监控仅支持 normal,不支持 tunnel 和 command
|
# 会话监控仅支持 normal,不支持 tunnel 和 command
|
||||||
return False
|
return False
|
||||||
if self.protocol in [
|
if self.terminal.type in [TerminalType.lion, TerminalType.koko]:
|
||||||
Protocol.ssh, Protocol.vnc, Protocol.rdp,
|
|
||||||
Protocol.telnet, Protocol.k8s
|
|
||||||
]:
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from assets.const import Protocol
|
|
||||||
from common.serializers.fields import LabeledChoiceField
|
from common.serializers.fields import LabeledChoiceField
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from .terminal import TerminalSmallSerializer
|
from .terminal import TerminalSmallSerializer
|
||||||
@@ -14,11 +13,9 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SessionSerializer(BulkOrgResourceModelSerializer):
|
class SessionSerializer(BulkOrgResourceModelSerializer):
|
||||||
org_id = serializers.CharField(allow_blank=True)
|
org_id = serializers.CharField(allow_blank=True)
|
||||||
protocol = serializers.ChoiceField(choices=Protocol.choices, label=_("Protocol"))
|
protocol = serializers.CharField(max_length=128, label=_("Protocol"))
|
||||||
type = LabeledChoiceField(
|
type = LabeledChoiceField(
|
||||||
choices=SessionType.choices, label=_("Type"), default=SessionType.normal
|
choices=SessionType.choices, label=_("Type"), default=SessionType.normal
|
||||||
)
|
)
|
||||||
@@ -33,7 +30,7 @@ class SessionSerializer(BulkOrgResourceModelSerializer):
|
|||||||
"user", "asset", "user_id", "asset_id", 'account', 'account_id',
|
"user", "asset", "user_id", "asset_id", 'account', 'account_id',
|
||||||
"protocol", 'type', "login_from", "remote_addr",
|
"protocol", 'type', "login_from", "remote_addr",
|
||||||
"is_success", "is_finished", "has_replay", "has_command",
|
"is_success", "is_finished", "has_replay", "has_command",
|
||||||
"date_start", "date_end", "comment"
|
"date_start", "date_end", "comment", "terminal_display"
|
||||||
]
|
]
|
||||||
fields_fk = ["terminal", ]
|
fields_fk = ["terminal", ]
|
||||||
fields_custom = ["can_replay", "can_join", "can_terminate"]
|
fields_custom = ["can_replay", "can_join", "can_terminate"]
|
||||||
|
|||||||
37
utils/clean_host_to_device.py
Normal file
37
utils/clean_host_to_device.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
|
if os.path.exists('../apps'):
|
||||||
|
sys.path.insert(0, '../apps')
|
||||||
|
elif os.path.exists('./apps'):
|
||||||
|
sys.path.insert(0, './apps')
|
||||||
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from assets.models import Asset as asset_model, Host as host_model, Device as device_model
|
||||||
|
from orgs.models import Organization
|
||||||
|
|
||||||
|
|
||||||
|
def clean_host():
|
||||||
|
root = Organization.root()
|
||||||
|
root.change_to()
|
||||||
|
|
||||||
|
devices = host_model.objects.filter(platform__category='device')
|
||||||
|
assets = asset_model.objects.filter(id__in=devices.values_list('asset_ptr_id', flat=True))
|
||||||
|
assets_map = {asset.id: asset for asset in assets}
|
||||||
|
|
||||||
|
for host in devices:
|
||||||
|
asset = assets_map.get(host.asset_ptr_id)
|
||||||
|
if not asset:
|
||||||
|
continue
|
||||||
|
device = device_model(asset_ptr_id=asset.id)
|
||||||
|
device.__dict__.update(asset.__dict__)
|
||||||
|
device.save()
|
||||||
|
host.delete(keep_parents=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
clean_host()
|
||||||
Reference in New Issue
Block a user