Compare commits

...

20 Commits
v4.1 ... v3.4.2

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 VERSION
@@ -8,7 +8,7 @@ WORKDIR /opt/jumpserver
ADD . .
RUN cd utils && bash -ixeu build.sh
FROM python:3.9-slim-buster
FROM python:3.9-slim-bullseye
ARG TARGETARCH
MAINTAINER JumpServer Team <ibuler@qq.com>

1
GITSHA Normal file
View File

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

View File

@@ -1,11 +1,12 @@
- hosts: custom
gather_facts: no
vars:
ansible_shell_type: sh
ansible_connection: local
tasks:
- name: Verify account
ssh_ping:
- name: Verify account (pyfreerdp)
rdp_ping:
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"

View File

@@ -4,7 +4,7 @@
ansible_connection: local
tasks:
- name: Verify account
- name: Verify account (paramiko)
ssh_ping:
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"

View File

@@ -1,5 +1,4 @@
# Generated by Django 3.2.17 on 2023-06-06 10:57
from collections import defaultdict
from django.db import migrations, models
@@ -8,17 +7,20 @@ import common.db.fields
def migrate_users_login_acls(apps, schema_editor):
login_acl_model = apps.get_model('acls', 'LoginACL')
name_used = defaultdict(int)
for login_acl in login_acl_model.objects.all():
name = login_acl.name
if name_used[name] > 0:
login_acl.name += "_{}".format(name_used[name])
name_used[name] += 1
name_used = []
login_acls = []
for login_acl in login_acl_model.objects.all().select_related('user'):
name = '{}_{}'.format(login_acl.name, login_acl.user.username)
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 = {
"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):

View File

@@ -82,7 +82,7 @@ class AssetFilterSet(BaseFilterSet):
@staticmethod
def filter_protocols(queryset, name, value):
value = value.split(',')
return queryset.filter(protocols__name__in=value)
return queryset.filter(protocols__name__in=value).distinct()
@staticmethod
def filter_labels(queryset, name, value):
@@ -91,7 +91,7 @@ class AssetFilterSet(BaseFilterSet):
queryset = queryset.filter(labels__name=n, labels__value=v)
else:
q = Q(labels__name__contains=value) | Q(labels__value__contains=value)
queryset = queryset.filter(q)
queryset = queryset.filter(q).distinct()
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
tasks:
- name: Test asset connection
- name: Test asset connection (paramiko)
ssh_ping:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"

View File

@@ -2,6 +2,7 @@
import django.db
from django.db import migrations, models
import common.db.fields
@@ -118,7 +119,7 @@ class Migration(migrations.Migration):
primary_key=True, serialize=False, to='assets.asset')),
],
options={
'verbose_name': 'Host',
'verbose_name': 'Host',
},
),
migrations.CreateModel(

View File

@@ -137,6 +137,24 @@ def migrate_to_nodes(apps, *args):
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):
dependencies = [
('assets', '0097_auto_20220426_1558'),
@@ -146,5 +164,6 @@ class Migration(migrations.Migration):
operations = [
migrations.RunPython(migrate_database_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 assets.const import AllTypes
def migrate_automation_push_account_params(apps, schema_editor):
platform_automation_model = apps.get_model('assets', 'PlatformAutomation')
platform_automation_methods = AllTypes.get_automation_methods()
methods_id_data_map = {
i['id']: None if i['params_serializer'] is None else i['params_serializer']({}).data
for i in platform_automation_methods
if i['method'] == 'push_account'
'push_account_aix': {'sudo': '/bin/whoami', 'shell': '/bin/bash', 'home': '', 'groups': ''},
'push_account_posix': {'sudo': '/bin/whoami', 'shell': '/bin/bash', 'home': '', 'groups': ''},
'push_account_local_windows': {'groups': 'Users,Remote Desktop Users'},
}
automation_objs = []
for automation in platform_automation_model.objects.all():

View File

@@ -206,15 +206,14 @@ class Asset(NodesRelationMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseMode
@lazyproperty
def auto_config(self):
platform = self.platform
automation = self.platform.automation
auto_config = {
'su_enabled': platform.su_enabled,
'domain_enabled': platform.domain_enabled,
'ansible_enabled': False
}
automation = getattr(self.platform, 'automation', None)
if not automation:
return auto_config
auto_config.update(model_to_dict(automation))
return auto_config

View File

@@ -42,7 +42,7 @@ def _get_instance_field_value(
if getattr(f, 'attname', None) in model_need_continue_fields:
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:
continue

View File

@@ -328,13 +328,13 @@ class RelatedManager:
q = Q()
if isinstance(val, str):
val = [val]
if ['*'] in val:
return Q()
for ip in val:
if not ip:
continue
try:
if ip == '*':
return Q()
elif '/' in ip:
if '/' in ip:
network = ipaddress.ip_network(ip)
ips = network.hosts()
q |= Q(**{"{}__in".format(name): ips})
@@ -378,7 +378,7 @@ class RelatedManager:
if match == 'ip_in':
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)
q = Q(**{lookup: val})
elif match == 'regex':
@@ -470,9 +470,9 @@ class JSONManyToManyDescriptor:
continue
if rule_match == 'in':
res &= value in rule_value
res &= value in rule_value or '*' in rule_value
elif rule_match == 'exact':
res &= value == rule_value
res &= value == rule_value or rule_value == '*'
elif rule_match == 'contains':
res &= rule_value in value
elif rule_match == 'startswith':
@@ -499,7 +499,7 @@ class JSONManyToManyDescriptor:
elif rule['match'] == 'ip_in':
if isinstance(rule_value, str):
rule_value = [rule_value]
res &= contains_ip(value, rule_value)
res &= '*' in rule_value or contains_ip(value, rule_value)
elif rule['match'] == 'm2m':
if isinstance(value, Manager):
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):
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):
data = {k: field_info.get(k) for k in fields_name}
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['required'] = field_info.get('required', True)
data['required'] = True
data = set_default_by_type(field_type, data, field_info)
data = set_default_if_need(data, i)
if data.get('default', None) is not None:
data['required'] = False
field_name = data.pop('name')
field_class = type_field_map.get(field_type, serializers.CharField)
serializer_fields[field_name] = field_class(**data)

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a76aa384867a4732eb7d2365515a1a972502ebadcba4de8236c1dcb3c5c7fdd2
size 145757
oid sha256:cd4fb6a0396c8636f8a36645354a5102790c020d73cdeb1f0e1d1f1b34ea39e9
size 145760

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -28,7 +28,7 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。"
#: authentication/confirm/password.py:9 authentication/forms.py:32
#: authentication/templates/authentication/login.html:274
#: 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/user_password_verify.html:18
#: 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/label.py:22
#: 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"
msgstr "アクティブです。"
@@ -1281,7 +1281,7 @@ msgstr "資産ハードウェア情報の収集"
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
msgid "Custom info"
msgstr "自动化信息"
msgstr "カスタム属性"
#: assets/models/asset/common.py:335
msgid "Can refresh asset hardware info"
@@ -1645,8 +1645,8 @@ msgstr "プロトコルが必要です: {}"
msgid "Default database"
msgstr "デフォルト・データベース"
#: assets/serializers/asset/database.py:28 common/db/fields.py:570
#: common/db/fields.py:575 common/serializers/fields.py:104
#: assets/serializers/asset/database.py:28 common/db/fields.py:579
#: common/db/fields.py:584 common/serializers/fields.py:104
#: tickets/serializers/ticket/common.py:58
#: xpack/plugins/cloud/serializers/account_attrs.py:56
#: xpack/plugins/cloud/serializers/account_attrs.py:79
@@ -2654,7 +2654,7 @@ msgstr "アクション"
#: authentication/serializers/connection_token.py:44
#: 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"
msgstr "期限切れです"
@@ -2680,8 +2680,8 @@ msgid "The {} cannot be empty"
msgstr "{} 空にしてはならない"
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
#: perms/serializers/permission.py:58 users/serializers/user.py:98
#: users/serializers/user.py:171
#: perms/serializers/permission.py:58 users/serializers/user.py:97
#: users/serializers/user.py:170
msgid "Is valid"
msgstr "有効です"
@@ -3116,7 +3116,7 @@ msgstr "テキストフィールドへのマーシャルデータ"
msgid "Encrypt field using Secret Key"
msgstr "Secret Keyを使用したフィールドの暗号化"
#: common/db/fields.py:558
#: common/db/fields.py:567
msgid ""
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
@@ -3126,19 +3126,19 @@ msgstr ""
"'type''ids''ids':[]}或 #タイプ:属性、属性:[名前ip、照合正確、"
"値1.1.1.1"
#: common/db/fields.py:565
#: common/db/fields.py:574
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
msgstr "無効なタイプです。all、ids、またはattrsでなければなりません"
#: common/db/fields.py:568
#: common/db/fields.py:577
msgid "Invalid ids for ids, should be a list"
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"
msgstr "無効な属性、dictリストでなければなりません"
#: common/db/fields.py:580
#: common/db/fields.py:589
msgid "Invalid attrs, should be has name and value"
msgstr "名前と値が必要な無効な属性"
@@ -3654,7 +3654,7 @@ msgstr "Material"
msgid "Material Type"
msgstr "Material を選択してオプションを設定します。"
#: ops/models/job.py:460
#: ops/models/job.py:461
msgid "Job Execution"
msgstr "ジョブ実行"
@@ -6711,7 +6711,7 @@ msgstr "公開キー"
msgid "Force enable"
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"
msgstr "サービスアカウントです"
@@ -6723,7 +6723,7 @@ msgstr "アバター"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:773 users/serializers/user.py:109
#: users/models/user.py:773 users/serializers/user.py:108
msgid "Phone"
msgstr "電話"
@@ -6740,7 +6740,7 @@ msgid "Secret key"
msgstr "秘密キー"
#: users/models/user.py:794 users/serializers/profile.py:149
#: users/serializers/user.py:169
#: users/serializers/user.py:168
msgid "Is first login"
msgstr "最初のログインです"
@@ -6823,55 +6823,55 @@ msgstr "新しいパスワードを最後の {} 個のパスワードにする
msgid "The newly set password is inconsistent"
msgstr "新しく設定されたパスワードが一致しない"
#: users/serializers/user.py:43
#: users/serializers/user.py:42
msgid "System roles"
msgstr "システムの役割"
#: users/serializers/user.py:47
#: users/serializers/user.py:46
msgid "Org roles"
msgstr "組織ロール"
#: users/serializers/user.py:90
#: users/serializers/user.py:89
msgid "Password strategy"
msgstr "パスワード戦略"
#: users/serializers/user.py:92
#: users/serializers/user.py:91
msgid "MFA enabled"
msgstr "MFA有効化"
#: users/serializers/user.py:94
#: users/serializers/user.py:93
msgid "MFA force enabled"
msgstr "MFAフォース有効化"
#: users/serializers/user.py:96
#: users/serializers/user.py:95
msgid "Login blocked"
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"
msgstr "仮想MFAがバインドされているか"
#: users/serializers/user.py:101
#: users/serializers/user.py:100
msgid "Can public key authentication"
msgstr "公開鍵認証が可能"
#: users/serializers/user.py:174
#: users/serializers/user.py:173
msgid "Avatar url"
msgstr "アバターURL"
#: users/serializers/user.py:178
#: users/serializers/user.py:177
msgid "MFA level"
msgstr "MFA レベル"
#: users/serializers/user.py:284
#: users/serializers/user.py:283
msgid "Select users"
msgstr "ユーザーの選択"
#: users/serializers/user.py:285
#: users/serializers/user.py:284
msgid "For security, only list several users"
msgstr "セキュリティのために、複数のユーザーのみをリストします"
#: users/serializers/user.py:318
#: users/serializers/user.py:317
msgid "name not unique"
msgstr "名前が一意ではない"

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f32e327dd50762b76d209f80b7de470df6faf5242af383dd6152b0c7ea5a7974
oid sha256:0efb248e80873f34d20f0fc3d4dd5c5a346048cb683c2b6bda3df939697fc52c
size 119261

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\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"
"Last-Translator: ibuler <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/templates/authentication/login.html:274
#: 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/user_password_verify.html:18
#: 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/label.py:22
#: 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"
msgstr "激活"
@@ -1274,7 +1274,7 @@ msgstr "收集资产硬件信息"
#: assets/models/asset/common.py:159 assets/serializers/asset/custom.py:14
msgid "Custom info"
msgstr "自动化信息"
msgstr "自定义属性"
#: assets/models/asset/common.py:335
msgid "Can refresh asset hardware info"
@@ -1636,8 +1636,8 @@ msgstr "协议是必填的: {}"
msgid "Default database"
msgstr "默认数据库"
#: assets/serializers/asset/database.py:28 common/db/fields.py:570
#: common/db/fields.py:575 common/serializers/fields.py:104
#: assets/serializers/asset/database.py:28 common/db/fields.py:579
#: common/db/fields.py:584 common/serializers/fields.py:104
#: tickets/serializers/ticket/common.py:58
#: xpack/plugins/cloud/serializers/account_attrs.py:56
#: xpack/plugins/cloud/serializers/account_attrs.py:79
@@ -2629,7 +2629,7 @@ msgstr "动作"
#: authentication/serializers/connection_token.py:44
#: 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"
msgstr "已过期"
@@ -2653,8 +2653,8 @@ msgid "The {} cannot be empty"
msgstr "{} 不能为空"
#: authentication/serializers/token.py:79 perms/serializers/permission.py:37
#: perms/serializers/permission.py:58 users/serializers/user.py:98
#: users/serializers/user.py:171
#: perms/serializers/permission.py:58 users/serializers/user.py:97
#: users/serializers/user.py:170
msgid "Is valid"
msgstr "是否有效"
@@ -3081,7 +3081,7 @@ msgstr "编码数据为 text"
msgid "Encrypt field using Secret Key"
msgstr "加密的字段"
#: common/db/fields.py:558
#: common/db/fields.py:567
msgid ""
"Invalid JSON data for JSONManyToManyField, should be like {'type': 'all'} or "
"{'type': 'ids', 'ids': []} or {'type': 'attrs', 'attrs': [{'name': 'ip', "
@@ -3091,19 +3091,19 @@ msgstr ""
"{'type': 'attrs', 'attrs': [{'name': 'ip', 'match': 'exact', 'value': "
"'1.1.1.1'}}"
#: common/db/fields.py:565
#: common/db/fields.py:574
msgid "Invalid type, should be \"all\", \"ids\" or \"attrs\""
msgstr "无效类型,应为 all、ids 或 attrs"
#: common/db/fields.py:568
#: common/db/fields.py:577
msgid "Invalid ids for ids, should be a list"
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"
msgstr "无效的属性应为dict列表"
#: common/db/fields.py:580
#: common/db/fields.py:589
msgid "Invalid attrs, should be has name and value"
msgstr "无效属性,应具有名称和值"
@@ -3612,7 +3612,7 @@ msgstr "Material"
msgid "Material Type"
msgstr "Material 类型"
#: ops/models/job.py:460
#: ops/models/job.py:461
msgid "Job Execution"
msgstr "作业执行"
@@ -6618,7 +6618,7 @@ msgstr "SSH公钥"
msgid "Force enable"
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"
msgstr "服务账号"
@@ -6630,7 +6630,7 @@ msgstr "头像"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:773 users/serializers/user.py:109
#: users/models/user.py:773 users/serializers/user.py:108
msgid "Phone"
msgstr "手机"
@@ -6647,7 +6647,7 @@ msgid "Secret key"
msgstr "Secret key"
#: users/models/user.py:794 users/serializers/profile.py:149
#: users/serializers/user.py:169
#: users/serializers/user.py:168
msgid "Is first login"
msgstr "首次登录"
@@ -6730,55 +6730,55 @@ msgstr "新密码不能是最近 {} 次的密码"
msgid "The newly set password is inconsistent"
msgstr "两次密码不一致"
#: users/serializers/user.py:43
#: users/serializers/user.py:42
msgid "System roles"
msgstr "系统角色"
#: users/serializers/user.py:47
#: users/serializers/user.py:46
msgid "Org roles"
msgstr "组织角色"
#: users/serializers/user.py:90
#: users/serializers/user.py:89
msgid "Password strategy"
msgstr "密码策略"
#: users/serializers/user.py:92
#: users/serializers/user.py:91
msgid "MFA enabled"
msgstr "MFA 已启用"
#: users/serializers/user.py:94
#: users/serializers/user.py:93
msgid "MFA force enabled"
msgstr "强制 MFA"
#: users/serializers/user.py:96
#: users/serializers/user.py:95
msgid "Login blocked"
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"
msgstr "是否绑定了虚拟 MFA"
#: users/serializers/user.py:101
#: users/serializers/user.py:100
msgid "Can public key authentication"
msgstr "可以使用公钥认证"
#: users/serializers/user.py:174
#: users/serializers/user.py:173
msgid "Avatar url"
msgstr "头像路径"
#: users/serializers/user.py:178
#: users/serializers/user.py:177
msgid "MFA level"
msgstr "MFA 级别"
#: users/serializers/user.py:284
#: users/serializers/user.py:283
msgid "Select users"
msgstr "选择用户"
#: users/serializers/user.py:285
#: users/serializers/user.py:284
msgid "For security, only list several users"
msgstr "为了安全,仅列出几个用户"
#: users/serializers/user.py:318
#: users/serializers/user.py:317
msgid "name not unique"
msgstr "名称重复"

View File

@@ -194,7 +194,7 @@ class Message(metaclass=MessageType):
return self.markdown_msg
def get_feishu_msg(self) -> dict:
return self.text_msg
return self.markdown_msg
def get_email_msg(self) -> dict:
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)
if created:
obj.post_insert_to_db(sub)
logger.info(
f'Create SystemMsgSubscription: package={app_config.module.__package__} type={message_type}')
logger.info(f'Create MsgSubscription: package={app_config.module.__package__} type={message_type}')
except ModuleNotFoundError:
pass

View File

@@ -109,21 +109,21 @@ class DefaultCallback:
pass
def playbook_on_stats(self, event_data, **kwargs):
failed = []
error_func = lambda err, task_detail: err + f"{task_detail[0]}: {task_detail[1]['stderr']};"
for tp in ['dark', 'failures']:
for host, tasks in self.result[tp].items():
failed.append(host)
error = reduce(error_func, tasks.items(), '').strip(';')
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():
ignore_errors = reduce(error_func, tasks.items(), '').strip(';')
if host in failed:
if host in failures:
self.summary['failures'][host] += {ignore_errors}
self.summary['ok'] = list(set(self.result['ok'].keys()) - set(failed))
self.summary['skipped'] = list(set(self.result['skipped'].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(dark_or_failures))
def playbook_on_include(self, event_data, **kwargs):
pass

View File

@@ -39,10 +39,6 @@ import pyfreerdp
from typing import NamedTuple
from ansible.module_utils.basic import AnsibleModule
from ops.ansible.modules_utils.custom_common import (
common_argument_spec
)
# =========================================
# Module execution.
@@ -55,6 +51,18 @@ class Param(NamedTuple):
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():
options = common_argument_spec()
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.translation import ugettext_lazy as _
from assets.const import Protocol
from assets.models import Asset
from common.utils import get_object_or_none, lazyproperty
from orgs.mixins.models import OrgModelMixin
from terminal.backends import get_multi_command_storage
from terminal.const import SessionType
from terminal.const import SessionType, TerminalType
from users.models import User
@@ -112,6 +111,7 @@ class Session(OrgModelMixin):
return rel_path
except:
pass
@property
def asset_obj(self):
return Asset.objects.get(id=self.asset_id)
@@ -132,10 +132,7 @@ class Session(OrgModelMixin):
if self.type != SessionType.normal:
# 会话监控仅支持 normal不支持 tunnel 和 command
return False
if self.protocol in [
Protocol.ssh, Protocol.vnc, Protocol.rdp,
Protocol.telnet, Protocol.k8s
]:
if self.terminal.type in [TerminalType.lion, TerminalType.koko]:
return True
else:
return False

View File

@@ -1,7 +1,6 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from assets.const import Protocol
from common.serializers.fields import LabeledChoiceField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .terminal import TerminalSmallSerializer
@@ -14,11 +13,9 @@ __all__ = [
]
class SessionSerializer(BulkOrgResourceModelSerializer):
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(
choices=SessionType.choices, label=_("Type"), default=SessionType.normal
)
@@ -33,7 +30,7 @@ class SessionSerializer(BulkOrgResourceModelSerializer):
"user", "asset", "user_id", "asset_id", 'account', 'account_id',
"protocol", 'type', "login_from", "remote_addr",
"is_success", "is_finished", "has_replay", "has_command",
"date_start", "date_end", "comment"
"date_start", "date_end", "comment", "terminal_display"
]
fields_fk = ["terminal", ]
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()