mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 08:32:48 +00:00
Compare commits
17 Commits
pr@dev@ter
...
v3.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ec4f2c4d6 | ||
|
|
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 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,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 }}"
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
tasks:
|
||||
- name: Test asset connection
|
||||
- name: Test asset connection (paramiko)
|
||||
ssh_ping:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
@@ -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(
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a76aa384867a4732eb7d2365515a1a972502ebadcba4de8236c1dcb3c5c7fdd2
|
||||
size 145757
|
||||
oid sha256:cd4fb6a0396c8636f8a36645354a5102790c020d73cdeb1f0e1d1f1b34ea39e9
|
||||
size 145760
|
||||
|
||||
@@ -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 "名前が一意ではない"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f32e327dd50762b76d209f80b7de470df6faf5242af383dd6152b0c7ea5a7974
|
||||
oid sha256:0efb248e80873f34d20f0fc3d4dd5c5a346048cb683c2b6bda3df939697fc52c
|
||||
size 119261
|
||||
|
||||
@@ -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 "名称重复"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,7 +33,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"]
|
||||
|
||||
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