Compare commits

...

20 Commits

Author SHA1 Message Date
fit2bot
5dadad2069 feat: Update v3.4.2 2023-06-30 11:17:35 +08:00
Eric
33db6de372 fix: 修复自定义远程应用的连接问题 2023-06-27 14:42:35 +08:00
feng
0bea545b6f fix: 修复自动化任务原子性error 导致整个任务失败问题 2023-06-25 18:20:28 +08:00
jiangweidong
424066b38f perf: 优化飞书接收到的工单审批的连接无法点击的问题 2023-06-25 11:08:56 +08:00
Eric
13d895b22e fix: 修复远程应用会话无法监控的问题 2023-06-21 12:05:19 +08:00
Bai
d5c51a4f0e fix: 修复迁移文件时触发信号记录操作日志导致迁移失败的问题 2023-06-21 11:02:14 +08:00
Eric
7c965706d4 perf: 修复 terminal 显示问题 2023-06-20 16:33:43 +08:00
feng
f59499f77e fix: 修改 push_account_params 数据迁移逻辑,不在导入公共方法生成数据 2023-06-19 18:23:23 +08:00
ibuler
98536cbc24 fix: 修改原来 platform 为 device 时,导致的 asset 类型不对 2023-06-19 17:54:17 +08:00
老广
7c29e60a82 Merge pull request #10757 from jumpserver/pr@v3.4@fix_custom_asset_detail_error
perf: 修复自定义资产详情没有 auto_config 的问题
2023-06-16 18:48:41 +08:00
ibuler
9e68d7d1a0 perf: 修复自定义资产详情没有 auto_config 的问题 2023-06-16 10:45:00 +00:00
ibuler
77b7134404 perf: 优化自定义 platform field 2023-06-16 17:02:46 +08:00
老广
fb7a839c16 Merge pull request #10755 from jumpserver/pr@v3.4@fix_permed_asset_duplicate
fix: 修复授权资产根据协议搜索重复的问题
2023-06-16 16:53:21 +08:00
ibuler
e80855068e fix: 修复授权资产根据协议搜索重复的问题 2023-06-16 08:44:53 +00:00
ibuler
5832246e5f perf: 修改 login acls 迁移冲突问题
perf: 修改 login acls 迁移,避免冲突
2023-06-16 13:58:51 +08:00
ibuler
dbef45e23b perf: 修复 acl 迁移后无法使用 2023-06-15 18:46:33 +08:00
feng
dedc42d775 feat: 添加自动化任务rdp ping 2023-06-15 18:46:04 +08:00
老广
f64073f48f Merge pull request #10739 from jumpserver/pr@v3.4@pr@dev@change_base_image
perf: 修改基础镜像版本
2023-06-15 17:36:33 +08:00
ibuler
70754ad748 perf: 修改基础镜像版本 2023-06-15 17:33:04 +08:00
fit2bot
3158980057 perf: 修改翻译 (#10734)
Co-authored-by: feng <1304903146@qq.com>
2023-06-15 15:41:25 +08:00
28 changed files with 215 additions and 127 deletions

View File

@@ -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
GITSHA Normal file
View File

@@ -0,0 +1 @@
33db6de372ad445ebf5e7d9843c2409abdafaf14

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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):

View File

@@ -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

View 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 }}"

View 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する

View File

@@ -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 }}"

View File

@@ -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(

View File

@@ -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),
] ]

View File

@@ -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():

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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 "名前が一意ではない"

View File

@@ -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

View File

@@ -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 "名称重复"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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"]

View 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()