Compare commits

..

52 Commits

Author SHA1 Message Date
Eric
66099b9e5d perf: modify url 2024-05-21 15:17:21 +08:00
Eric
eaa052a380 perf: 添加加密配置API 2024-05-21 15:17:21 +08:00
wangruidong
606d2c8933 fix: 关闭ssh client后,sftp,telnet不显示客户端连接方式 2024-05-20 10:02:12 +08:00
feng
a534c496d0 perf: core celery always active 2024-05-16 15:52:11 +08:00
wangruidong
a11097fb5a fix: 定时任务,再次执行报错 2024-05-16 15:48:45 +08:00
feng
d4c1f93ef6 fix: send slack message failed 2024-05-16 15:23:31 +08:00
fit2bot
9168e92669 perf: update poetry lock (#13229)
Co-authored-by: feng <1304903146@qq.com>
2024-05-16 10:27:29 +08:00
fit2bot
bfd030d70f perf: upgrade jms-storage (#13223)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-05-15 14:38:26 +08:00
wangruidong
da0c017c4f fix: ldap定时任务未执行 2024-05-15 14:16:33 +08:00
fit2bot
5ffc0a9665 perf: add new dependencies (#13220)
Co-authored-by: feng <1304903146@qq.com>
2024-05-15 10:13:14 +08:00
fit2bot
10e9026ec7 perf: add gpt 4o (#13218)
Co-authored-by: feng <1304903146@qq.com>
2024-05-14 16:29:41 +08:00
Bai
7c4c0b5924 fix: Fixed ACLs Asset connect select attribute assets while both include labels not matched. 2024-05-14 16:16:00 +08:00
wangruidong
42c3008ec9 perf: 更新ldap相关翻译文件 2024-05-14 15:08:37 +08:00
吴小白
2f6d743cf0 perf: 优化 CI 构建测试 2024-05-14 14:17:02 +08:00
fit2bot
e8faaeb8fb fix: Accounts of ssh key type will no longer export fingerprints. (#13215)
Co-authored-by: feng <1304903146@qq.com>
2024-05-14 13:04:11 +08:00
feng
0ea675f8d6 fix: windows gather account failed 2024-05-13 18:23:40 +08:00
halo
77caa5536f fix: chrome应用加载多个插件不生效问题 2024-05-13 16:30:17 +08:00
fit2bot
52c905832b fix: 账号密钥长度为8192时 刷新账号列表504 (#13196)
Co-authored-by: feng <1304903146@qq.com>
2024-05-11 14:45:29 +08:00
Bai
94ee3169dc perf: While Asset amount (GLOBAL) > 5000 delay (20s) refresh user perm tree 2024-05-10 12:38:31 +08:00
fit2bot
92b6286feb fix: ldap更换OU后无法登录 (#13172)
* fix: ldap更换OU后无法登录

* perf: 翻译

---------

Co-authored-by: wangruidong <940853815@qq.com>
2024-05-08 14:23:20 +08:00
Bai
bce776bb63 fix: 修复 v2 升级到 v3 授权的手动登录系统用户显示空字符串的问题 2024-05-07 14:33:01 +08:00
wangruidong
dc39cbf037 fix: ldap定时任务未执行 2024-05-07 10:27:44 +08:00
Bai
38175d6b57 fix: Fixed csv file export for 0 chars is not appear 2024-04-28 17:56:45 +08:00
wangruidong
7408ed0f03 perf: add XPACKModelFieldsMixin 2024-04-28 15:58:14 +08:00
wangruidong
5135186961 perf: 社区版去掉一些东西 2024-04-28 15:58:14 +08:00
wangruidong
5be399616b fix: 华为交换机执行某些命令报错 2024-04-28 14:17:38 +08:00
wangruidong
46a23afbec perf: 创建、更新用户时MFA选项根据系统设置选项进行动态渲染 2024-04-26 11:35:56 +08:00
Bai
8c4add241d perf: Support django shell run orm output SQL 2024-04-26 10:39:49 +08:00
Bai
feee92daee fix: Fixed issue of v2 to v3 Account missing su_from 2024-04-25 19:13:34 +08:00
Bai
42054c7989 feat: Support asset tree node drag to another one 2024-04-24 18:05:51 +08:00
Aaron3S
9b20b67039 fix: 修复执行快捷命令时 local_connection 没有被正确设置 2024-04-23 19:07:22 +08:00
Bai
2acc84dc69 fix: Adhoc support mariadb with module of mysql 2024-04-23 18:57:08 +08:00
吴小白
3383d0f314 perf: 镜像添加 nc 命令 2024-04-23 16:53:25 +08:00
Bai
c9858b5a84 fix: 修改配置 RECEPTOR_ENABLED=False 默认 2024-04-23 16:52:44 +08:00
Bai
25e21b185f fix: 修改配置 RECEPTOR_ENABLED 2024-04-23 15:03:04 +08:00
Aaron3S
720231f692 feat: 修改 receptor 容器通信地址 2024-04-23 13:12:18 +08:00
jiangweidong
95f29a584e perf: 优化会话过期500问题 2024-04-23 13:11:41 +08:00
Bai
50cbb75b96 perf: 优化 Web 资产详情时根据 autofill 类型返回对应的 spec_info 信息 2024-04-23 13:09:40 +08:00
Bai
d418647774 fix: 修复仪表盘会话排序数量都是 1 的问题 2024-04-22 19:37:45 +08:00
Bai
6b5d4a4810 fix: 修复仪表盘会话排序数量都是 1 的问题 2024-04-22 19:32:42 +08:00
Eric
2cc67634a4 perf: 发布机支持平台连接参数 2024-04-22 16:40:41 +08:00
fit2bot
52922088a9 feat: 优化代码结构,receptor开关,修改为 tcp 通信 (#13078)
* feat: 优化代码结构,receptor开关,修改为 tcp 通信

* fix: 修改导包路径

* fix: 修复错别字

* fix: 修改导包路径

* perf: 优化代码

* fix: 修复任务不执行的问题

* perf: 优化配置项名称

* perf: 优化代码结构

* perf: 优化代码

---------

Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2024-04-22 13:51:52 +08:00
jiangweidong
ef7329a721 perf: 优化频繁发送短信,将后端的频繁发送警告提示到页面上来提醒用户 2024-04-22 13:20:51 +08:00
Bai
ad0bc82539 perf: 优化 HUAWEI 设备判断逻辑 2024-04-22 13:19:32 +08:00
wangruidong
1ecf8534f6 perf: 兼容自定义平台的华为交换机执行命令 2024-04-22 13:19:32 +08:00
feng
94286caec4 fix: 命令输出取消长度限制 2024-04-22 10:31:35 +08:00
wangruidong
d4c8425218 fix: 快捷命令账号选择未按账号数量排序 2024-04-22 10:31:02 +08:00
fit2bot
59f9a4f369 fix: 获取 k8s 树取消异常 返回空 优化错误日志 (#13077)
Co-authored-by: feng <1304903146@qq.com>
2024-04-19 17:41:41 +08:00
Bai
64125051df fix: Org is None not has id attribute 2024-04-19 17:15:30 +08:00
Bai
660572a0ea fix: merge_delay_run 偶尔会出现 (2006, MySQL server has gone away 的报错) 2024-04-19 17:15:30 +08:00
ibuler
c0273dc698 perf: 去掉 js 报错 2024-04-19 11:21:27 +08:00
Bai
2782d4b5f1 fix: 修复 Celery Execution 任务保存失败导致 View 事务回滚的问题(首次登录用户修改密码失败) 2024-04-18 21:21:09 +08:00
78 changed files with 1209 additions and 808 deletions

View File

@@ -1,26 +1,32 @@
name: "Run Build Test"
on:
push:
branches:
- pr@*
- repr@*
paths:
- 'Dockerfile'
- 'Dockerfile-*'
- 'pyproject.toml'
- 'poetry.lock'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/setup-qemu-action@v2
- name: Check Dockerfile
run: |
test -f Dockerfile-ce || cp -f Dockerfile Dockerfile-ce
- uses: docker/setup-buildx-action@v2
- uses: docker/build-push-action@v3
- name: Build CE Image
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: jumpserver/core-ce:test
file: Dockerfile-ce
tags: jumpserver/core-ce:test
platforms: linux/amd64
build-args: |
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
@@ -28,9 +34,22 @@ jobs:
cache-from: type=gha
cache-to: type=gha,mode=max
- uses: LouisBrunner/checks-action@v1.5.0
if: always()
- name: Prepare EE Image
run: |
sed -i 's@^FROM registry.fit2cloud.com@# FROM registry.fit2cloud.com@g' Dockerfile-ee
sed -i 's@^COPY --from=build-xpack@# COPY --from=build-xpack@g' Dockerfile-ee
- name: Build EE Image
uses: docker/build-push-action@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: Check Build
conclusion: ${{ job.status }}
context: .
push: false
file: Dockerfile-ee
tags: jumpserver/core-ee:test
platforms: linux/amd64
build-args: |
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
PIP_JMS_MIRROR=https://pypi.org/simple
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@@ -87,6 +87,7 @@ ARG TOOLS=" \
default-mysql-client \
iputils-ping \
locales \
netcat-openbsd \
nmap \
openssh-client \
patch \

1
GITSHA
View File

@@ -1 +0,0 @@
913c0d140d5fa000c732f2941bb306bb04897d71

View File

@@ -51,8 +51,16 @@ class GatherAccountsManager(AccountBasePlaybookManager):
data = self.generate_data(asset, result)
self.asset_account_info[asset] = data
@staticmethod
def get_nested_info(data, *keys):
for key in keys:
data = data.get(key, {})
if not data:
break
return data
def on_host_success(self, host, result):
info = result.get('debug', {}).get('res', {}).get('info', {})
info = self.get_nested_info(result, 'debug', 'res', 'info')
asset = self.host_asset_mapper.get(host)
if asset and info:
result = self.filter_success_result(asset.type, info)

View File

@@ -431,8 +431,11 @@ class AssetAccountBulkSerializer(
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class Meta(AccountSerializer.Meta):
fields = AccountSerializer.Meta.fields + ['spec_info']
extra_kwargs = {
**AccountSerializer.Meta.extra_kwargs,
'secret': {'write_only': False},
'spec_info': {'label': _('Spec info')},
}

View File

@@ -67,15 +67,14 @@ class BaseAccountSerializer(AuthValidateMixin, ResourceLabelsMixin, BulkOrgResou
fields_mini = ['id', 'name', 'username']
fields_small = fields_mini + [
'secret_type', 'secret', 'passphrase',
'privileged', 'is_active', 'spec_info',
'privileged', 'is_active',
]
fields_other = ['created_by', 'date_created', 'date_updated', 'comment']
fields = fields_small + fields_other + ['labels']
read_only_fields = [
'spec_info', 'date_verified', 'created_by', 'date_created',
'date_verified', 'created_by', 'date_created',
]
extra_kwargs = {
'spec_info': {'label': _('Spec info')},
'username': {'help_text': _(
"Tip: If no username is required for authentication, fill in `null`, "
"If AD account, like `username@domain`"

View File

@@ -35,6 +35,7 @@ class AccountTemplateSerializer(BaseAccountSerializer):
'su_from'
]
extra_kwargs = {
**BaseAccountSerializer.Meta.extra_kwargs,
'secret_strategy': {'help_text': _('Secret generation strategy for account creation')},
'auto_push': {'help_text': _('Whether to automatically push the account to the asset')},
'platforms': {
@@ -64,6 +65,9 @@ class AccountTemplateSerializer(BaseAccountSerializer):
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer):
class Meta(AccountTemplateSerializer.Meta):
fields = AccountTemplateSerializer.Meta.fields + ['spec_info']
extra_kwargs = {
**AccountTemplateSerializer.Meta.extra_kwargs,
'secret': {'write_only': False},
'spec_info': {'label': _('Spec info')},
}

View File

@@ -22,6 +22,7 @@ from orgs.utils import current_org
from rbac.permissions import RBACPermission
from .. import serializers
from ..models import Node
from ..signal_handlers import update_nodes_assets_amount
from ..tasks import (
update_node_assets_hardware_info_manual,
test_node_assets_connectivity_manual,
@@ -94,6 +95,7 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
children = Node.objects.filter(id__in=node_ids)
for node in children:
node.parent = instance
update_nodes_assets_amount.delay(ttl=5, node_ids=(instance.id,))
return Response("OK")

View File

@@ -12,7 +12,8 @@ from sshtunnel import SSHTunnelForwarder
from assets.automations.methods import platform_automation_methods
from common.utils import get_logger, lazyproperty, is_openssh_format_key, ssh_pubkey_gen
from ops.ansible import JMSInventory, SuperPlaybookRunner, DefaultCallback
from ops.ansible import JMSInventory, DefaultCallback, SuperPlaybookRunner
from ops.ansible.interface import interface
logger = get_logger(__name__)
@@ -54,7 +55,9 @@ class SSHTunnelManager:
not_valid.append(k)
else:
local_bind_port = server.local_bind_port
host['ansible_host'] = jms_asset['address'] = host['login_host'] = 'jms_celery'
host['ansible_host'] = jms_asset['address'] = host[
'login_host'] = interface.get_gateway_proxy_host()
host['ansible_port'] = jms_asset['port'] = host['login_port'] = local_bind_port
servers.append(server)
@@ -297,12 +300,16 @@ class BasePlaybookManager:
for host in hosts:
result = cb.host_results.get(host)
if state == 'ok':
self.on_host_success(host, result)
self.on_host_success(host, result.get('ok', ''))
elif state == 'skipped':
pass
else:
error = hosts.get(host)
self.on_host_error(host, error, result)
self.on_host_error(
host, error,
result.get('failures', '')
or result.get('dark', '')
)
def on_runner_failed(self, runner, e):
print("Runner failed: {} {}".format(e, self))

View File

@@ -286,7 +286,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
'label': _('API mode'),
'choices': [
('gpt-3.5-turbo', 'GPT-3.5 Turbo'),
('gpt-3.5-turbo-16k', 'GPT-3.5 Turbo 16K'),
('gpt-3.5-turbo-1106', 'GPT-3.5 Turbo 1106'),
]
}
}
@@ -296,7 +296,8 @@ class Protocol(ChoicesMixin, models.TextChoices):
choices = protocols[cls.chatgpt]['setting']['api_mode']['choices']
choices.extend([
('gpt-4', 'GPT-4'),
('gpt-4-32k', 'GPT-4 32K'),
('gpt-4-turbo', 'GPT-4 Turbo'),
('gpt-4o', 'GPT-4o'),
])
return protocols

View File

@@ -1,6 +1,7 @@
# Generated by Django 3.2.12 on 2022-07-11 06:13
import time
import math
from django.utils import timezone
from itertools import groupby
from django.db import migrations
@@ -40,9 +41,13 @@ def migrate_asset_accounts(apps, schema_editor):
if system_user:
# 更新一次系统用户的认证属性
account_values.update({attr: getattr(system_user, attr, '') for attr in all_attrs})
account_values['created_by'] = str(system_user.id)
account_values['privileged'] = system_user.type == 'admin' \
or system_user.username in ['root', 'Administrator']
if system_user.su_enabled and system_user.su_from:
created_by = f'{str(system_user.id)}::{str(system_user.su_from.username)}'
else:
created_by = str(system_user.id)
account_values['created_by'] = created_by
auth_book_auth = {attr: getattr(auth_book, attr, '') for attr in all_attrs if getattr(auth_book, attr, '')}
# 最终优先使用 auth_book 的认证属性
@@ -117,6 +122,70 @@ def migrate_asset_accounts(apps, schema_editor):
print("\t - histories: {}".format(len(accounts_to_history)))
def update_asset_accounts_su_from(apps, schema_editor):
# Update accounts su_from
print("\n\tStart update asset accounts su_from field")
account_model = apps.get_model('accounts', 'Account')
platform_model = apps.get_model('assets', 'Platform')
asset_model = apps.get_model('assets', 'Asset')
platform_ids = list(platform_model.objects.filter(su_enabled=True).values_list('id', flat=True))
count = 0
step_size = 1000
count_account = 0
while True:
start = time.time()
asset_ids = asset_model.objects \
.filter(platform_id__in=platform_ids) \
.values_list('id', flat=True)[count:count + step_size]
asset_ids = list(asset_ids)
if not asset_ids:
break
count += len(asset_ids)
accounts = list(account_model.objects.filter(asset_id__in=asset_ids))
# {asset_id_account_username: account.id}}
asset_accounts_mapper = {}
for a in accounts:
try:
k = f'{a.asset_id}_{a.username}'
asset_accounts_mapper[k] = str(a.id)
except Exception as e:
pass
update_accounts = []
for a in accounts:
try:
if not a.created_by:
continue
created_by_list = a.created_by.split('::')
if len(created_by_list) != 2:
continue
su_from_username = created_by_list[1]
if not su_from_username:
continue
k = f'{a.asset_id}_{su_from_username}'
su_from_id = asset_accounts_mapper.get(k)
if not su_from_id:
continue
a.su_from_id = su_from_id
update_accounts.append(a)
except Exception as e:
pass
count_account += len(update_accounts)
log_msg = "\t - [{}]: Update accounts su_from: {}-{} {:.2f}s"
try:
account_model.objects.bulk_update(update_accounts, ['su_from_id'])
except Exception as e:
status = 'Failed'
else:
status = 'Success'
print(log_msg.format(status, count_account - len(update_accounts), count_account, time.time() - start))
def migrate_db_accounts(apps, schema_editor):
app_perm_model = apps.get_model('perms', 'ApplicationPermission')
account_model = apps.get_model('accounts', 'Account')
@@ -196,5 +265,6 @@ class Migration(migrations.Migration):
operations = [
migrations.RunPython(migrate_asset_accounts),
migrations.RunPython(update_asset_accounts_su_from),
migrations.RunPython(migrate_db_accounts),
]

View File

@@ -1,8 +1,7 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from assets.const import AllTypes
from assets.const import Protocol
from assets.const import AllTypes, Category, Protocol
from common.db.fields import JsonDictTextField
from common.db.models import JMSBaseModel
@@ -119,6 +118,15 @@ class Platform(LabeledMixin, JMSBaseModel):
)
return linux.id
def is_huawei(self):
if self.category != Category.DEVICE:
return False
if 'huawei' in self.name.lower():
return True
if '华为' in self.name:
return True
return False
def __str__(self):
return self.name

View File

@@ -22,6 +22,36 @@ class WebSpecSerializer(serializers.ModelSerializer):
'submit_selector', 'script'
]
def get_fields(self):
fields = super().get_fields()
if self.is_retrieve():
# 查看 Web 资产详情时
self.pop_fields_if_need(fields)
return fields
def is_retrieve(self):
try:
self.context.get('request').method and self.parent.instance.web
return True
except Exception:
return False
def pop_fields_if_need(self, fields):
fields_script = ['script']
fields_basic = ['username_selector', 'password_selector', 'submit_selector']
autofill = self.parent.instance.web.autofill
pop_fields_mapper = {
FillType.no: fields_script + fields_basic,
FillType.basic: fields_script,
FillType.script: fields_basic,
}
fields_pop = pop_fields_mapper.get(autofill, [])
for f in fields_pop:
fields.pop(f, None)
return fields
category_spec_serializer_map = {
'database': DatabaseSpecSerializer,

View File

@@ -67,5 +67,5 @@ def set_assets_size_to_setting(sender, **kwargs):
if amount > 20000:
settings.ASSET_SIZE = 'large'
elif amount > 2000:
elif amount > 5000:
settings.ASSET_SIZE = 'medium'

View File

@@ -88,8 +88,7 @@ class KubernetesClient:
try:
data = getattr(self, func_name)(*args)
except Exception as e:
logger.error(e)
raise e
logger.error(f'K8S tree get {tp} error: {e}')
if self.server:
self.server.stop()

View File

@@ -9,7 +9,7 @@ from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from common.exceptions import UnexpectError
from common.exceptions import JMSException, UnexpectError
from common.utils import get_logger
from users.models.user import User
from .. import errors
@@ -50,7 +50,10 @@ class MFASendCodeApi(AuthMixin, CreateAPIView):
mfa_type = serializer.validated_data['type']
if not username:
user = self.get_user_from_session()
try:
user = self.get_user_from_session()
except errors.SessionEmptyError as e:
raise ValidationError({'error': e})
else:
user = self.get_user_from_db(username)
@@ -61,6 +64,8 @@ class MFASendCodeApi(AuthMixin, CreateAPIView):
try:
mfa_backend.send_challenge()
except JMSException:
raise
except Exception as e:
raise UnexpectError(str(e))

View File

@@ -204,12 +204,14 @@ class ConnectionToken(JMSOrgBaseModel):
host, account, lock_key = bulk_get(host_account, ('host', 'account', 'lock_key'))
gateway = host.domain.select_gateway() if host.domain else None
platform = host.platform
data = {
'id': lock_key,
'applet': applet,
'host': host,
'gateway': gateway,
'platform': platform,
'account': account,
'remote_app_option': self.get_remote_app_option()
}

View File

@@ -161,6 +161,7 @@ class ConnectTokenAppletOptionSerializer(serializers.Serializer):
host = _ConnectionTokenAssetSerializer(read_only=True)
account = _ConnectionTokenAccountSerializer(read_only=True)
gateway = _ConnectionTokenGatewaySerializer(read_only=True)
platform = _ConnectionTokenPlatformSerializer(read_only=True)
remote_app_option = serializers.JSONField(read_only=True)

View File

@@ -1,6 +1,6 @@
from contextlib import contextmanager
from django.db import connections
from django.db import connections, transaction
from django.utils.encoding import force_str
from common.utils import get_logger, signer, crypto
@@ -58,6 +58,17 @@ def safe_db_connection():
close_old_connections()
@contextmanager
def open_db_connection(alias='default'):
connection = transaction.get_connection(alias)
try:
connection.connect()
with transaction.atomic():
yield connection
finally:
connection.close()
class Encryptor:
def __init__(self, value):
self.value = force_str(value)

View File

@@ -12,6 +12,7 @@ from functools import wraps
from django.db import transaction
from .utils import logger
from .db.utils import open_db_connection
def on_transaction_commit(func):
@@ -146,7 +147,9 @@ ignore_err_exceptions = (
def _run_func_with_org(key, org, func, *args, **kwargs):
from orgs.utils import set_current_org
try:
with transaction.atomic():
with open_db_connection() as conn:
# 保证执行时使用的是新的 connection 数据库连接
# 避免出现 MySQL server has gone away 的情况
set_current_org(org)
func(*args, **kwargs)
except Exception as e:
@@ -201,6 +204,8 @@ def merge_delay_run(ttl=5, key=None):
def delay(func, *args, **kwargs):
from orgs.utils import get_current_org
# 每次调用 delay 时可以指定本次调用的 ttl
current_ttl = kwargs.pop('ttl', ttl)
suffix_key_func = key if key else default_suffix_key
org = get_current_org()
func_name = f'{func.__module__}_{func.__name__}'
@@ -217,7 +222,7 @@ def merge_delay_run(ttl=5, key=None):
else:
cache_kwargs[k] = cache_kwargs[k].union(v)
_loop_debouncer_func_args_cache[cache_key] = cache_kwargs
run_debouncer_func(cache_key, org, ttl, func, *args, **cache_kwargs)
run_debouncer_func(cache_key, org, current_ttl, func, *args, **cache_kwargs)
def apply(func, sync=False, *args, **kwargs):
if sync:

1
apps/common/drf/const.py Normal file
View File

@@ -0,0 +1 @@
CSV_FILE_ESCAPE_CHARS = ['=', '@', '0']

View File

@@ -4,13 +4,25 @@
import chardet
import unicodecsv
from common.utils import lazyproperty
from .base import BaseFileParser
from ..const import CSV_FILE_ESCAPE_CHARS
class CSVFileParser(BaseFileParser):
media_type = 'text/csv'
@lazyproperty
def match_escape_chars(self):
chars = []
for c in CSV_FILE_ESCAPE_CHARS:
dq_char = '"{}'.format(c)
sg_char = "'{}".format(c)
chars.append(dq_char)
chars.append(sg_char)
return tuple(chars)
@staticmethod
def _universal_newlines(stream):
"""
@@ -18,6 +30,14 @@ class CSVFileParser(BaseFileParser):
"""
for line in stream.splitlines():
yield line
def __parse_row(self, row):
row_escape = []
for d in row:
if isinstance(d, str) and d.strip().startswith(self.match_escape_chars):
d = d.lstrip("'").lstrip('"')
row_escape.append(d)
return row_escape
def generate_rows(self, stream_data):
detect_result = chardet.detect(stream_data)
@@ -25,4 +45,5 @@ class CSVFileParser(BaseFileParser):
lines = self._universal_newlines(stream_data)
csv_reader = unicodecsv.reader(lines, encoding=encoding)
for row in csv_reader:
row = self.__parse_row(row)
yield row

View File

@@ -6,31 +6,36 @@ import unicodecsv
from six import BytesIO
from .base import BaseFileRenderer
from ..const import CSV_FILE_ESCAPE_CHARS
class CSVFileRenderer(BaseFileRenderer):
media_type = 'text/csv'
format = 'csv'
writer = None
buffer = None
escape_chars = tuple(CSV_FILE_ESCAPE_CHARS)
def initial_writer(self):
csv_buffer = BytesIO()
csv_buffer.write(codecs.BOM_UTF8)
csv_writer = unicodecsv.writer(csv_buffer, encoding='utf-8')
self.buffer = csv_buffer
self.writer = csv_writer
def write_row(self, row):
def __render_row(self, row):
row_escape = []
for d in row:
if isinstance(d, str) and d.strip().startswith(('=', '@')):
if isinstance(d, str) and d.strip().startswith(self.escape_chars):
d = "'{}".format(d)
row_escape.append(d)
else:
row_escape.append(d)
self.writer.writerow(row_escape)
row_escape.append(d)
return row_escape
def write_row(self, row):
row = self.__render_row(row)
self.writer.writerow(row)
def get_rendered_value(self):
value = self.buffer.getvalue()

View File

@@ -27,7 +27,7 @@ class SlackRenderer(mistune.HTMLRenderer):
def strong(self, text):
return '*' + text + '*'
def list(self, text, **kwargs):
def list(self, text, *args, **kwargs):
lines = text.split('\n')
for i, line in enumerate(lines):
if not line:
@@ -128,7 +128,7 @@ class Slack:
def send_text(self, user_ids, msg_body):
body = self.convert_to_markdown(msg_body)
logger.info(f'Slack send text: user_ids={user_ids} msg={body}')
logger.info(f'Slack send text: user_ids={user_ids}')
for user_id in user_ids:
body['channel'] = user_id
try:

View File

@@ -5,6 +5,7 @@ if sys.version_info.major >= 3 and sys.version_info.minor >= 10:
from collections.abc import Iterable
else:
from collections import Iterable
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import NOT_PROVIDED
from django.utils.translation import gettext_lazy as _
@@ -42,6 +43,17 @@ class SecretReadableMixin(serializers.Serializer):
if 'write_only' not in field_extra_kwargs:
continue
serializer_field.write_only = field_extra_kwargs['write_only']
self.remove_spec_info_field()
def remove_spec_info_field(self):
request = self.context.get('request')
if not request:
return
_format = request.query_params.get('format')
if _format not in ['csv', 'xlsx']:
return
self.fields.pop('spec_info', None)
class BulkSerializerMixin(object):
@@ -264,6 +276,14 @@ class SizedModelFieldsMixin(BaseDynamicFieldsPlugin):
return fields_to_drop
class XPACKModelFieldsMixin(BaseDynamicFieldsPlugin):
def get_exclude_field_names(self):
if settings.XPACK_LICENSE_IS_VALID:
return set()
fields_xpack = set(getattr(self.serializer.Meta, 'fields_xpack', set()))
return fields_xpack
class DefaultValueFieldsMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -302,7 +322,7 @@ class DynamicFieldsMixin:
"""
可以控制显示不同的字段mini 最少small 不包含关系
"""
dynamic_fields_plugins = [QueryFieldsMixin, SizedModelFieldsMixin]
dynamic_fields_plugins = [QueryFieldsMixin, SizedModelFieldsMixin, XPACKModelFieldsMixin]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@@ -30,14 +30,14 @@ class SendAndVerifyCodeUtil(object):
self.other_args = kwargs
def gen_and_send_async(self):
ttl = self.__ttl()
if ttl > 0:
logger.warning('Send sms too frequently, delay {}'.format(ttl))
raise CodeSendTooFrequently(ttl)
return send_async.apply_async(kwargs={"sender": self}, priority=100)
def gen_and_send(self):
ttl = self.__ttl()
if ttl > 0:
logger.error('Send sms too frequently, delay {}'.format(ttl))
raise CodeSendTooFrequently(ttl)
try:
if not self.code:
self.code = self.__generate()

View File

@@ -187,14 +187,14 @@ class DatesLoginMetricMixin:
def get_dates_login_times_assets(self):
assets = self.sessions_queryset.values("asset") \
.annotate(total=Count("asset", distinct=True)) \
.annotate(total=Count("asset")) \
.annotate(last=Cast(Max("date_start"), output_field=CharField())) \
.order_by("-total")
return list(assets[:10])
def get_dates_login_times_users(self):
users = self.sessions_queryset.values("user_id") \
.annotate(total=Count("user_id", distinct=True)) \
.annotate(total=Count("user_id")) \
.annotate(user=Max('user')) \
.annotate(last=Cast(Max("date_start"), output_field=CharField())) \
.order_by("-total")

View File

@@ -617,8 +617,10 @@ class Config(dict):
'TICKET_APPLY_ASSET_SCOPE': 'all',
# Ansible Receptor
'ANSIBLE_RECEPTOR_ENABLE': True,
'ANSIBLE_RECEPTOR_SOCK_PATH': '{}/data/share/control.sock'.format(PROJECT_DIR)
'RECEPTOR_ENABLED': False,
'ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST': 'jms_celery',
'ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS': 'receptor:7521'
}
old_config_map = {

View File

@@ -8,7 +8,7 @@ __all__ = ['BASE_DIR', 'PROJECT_DIR', 'VERSION', 'CONFIG']
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR)
VERSION = 'v3.10.8'
VERSION = '2.0.0'
CONFIG = ConfigManager.load_user_config()

View File

@@ -232,5 +232,6 @@ FILE_UPLOAD_SIZE_LIMIT_MB = CONFIG.FILE_UPLOAD_SIZE_LIMIT_MB
TICKET_APPLY_ASSET_SCOPE = CONFIG.TICKET_APPLY_ASSET_SCOPE
# Ansible Receptor
ANSIBLE_RECEPTOR_ENABLE = CONFIG.ANSIBLE_RECEPTOR_ENABLE
ANSIBLE_RECEPTOR_SOCK_PATH = CONFIG.ANSIBLE_RECEPTOR_SOCK_PATH
RECEPTOR_ENABLED = CONFIG.RECEPTOR_ENABLED
ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST = CONFIG.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST
ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS = CONFIG.ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS

View File

@@ -17,7 +17,7 @@ LOGGING = {
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
'format': '%(levelname)s %(asctime)s %(pathname)s:%(lineno)d %(message)s'
},
'main': {
'datefmt': '%Y-%m-%d %H:%M:%S',
@@ -136,6 +136,12 @@ LOGGING = {
}
}
if CONFIG.DEBUG_DEV:
LOGGING['loggers']['django.db'] = {
'handlers': ['console', 'file'],
'level': 'DEBUG'
}
SYSLOG_ENABLE = CONFIG.SYSLOG_ENABLE
if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2:

View File

@@ -57,7 +57,7 @@ class LabeledMixin(models.Model):
resources = resources.filter(label_id__in=label_ids) \
.values('res_id') \
.order_by('res_id') \
.annotate(count=Count('res_id', distinct=True)) \
.annotate(count=Count('res_id')) \
.values('res_id', 'count') \
.filter(count=len(label_ids))
return resources

26
apps/libs/process/ssh.py Normal file
View File

@@ -0,0 +1,26 @@
import logging
import psutil
from psutil import NoSuchProcess
logger = logging.getLogger(__name__)
def _should_kill(process):
return process.pid != 1 and process.name() == 'ssh'
def kill_ansible_ssh_process(pid):
try:
process = psutil.Process(pid)
except NoSuchProcess as e:
logger.error(f"No such process: {e}")
return
for child in process.children(recursive=True):
if not _should_kill(child):
return
try:
child.kill()
except Exception as e:
logger.error(f"Failed to kill process {child.pid}: {e}")

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b796ec6f93e1a3855473cc5e4a0bd2ecf44b33ce59b07146fa0e83fd16cd7b46
size 177281
oid sha256:e4baadc170ded5134bed55533c4c04e694be6ea7b8e151d80c1092728e26a75b
size 177500

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-18 17:25+0800\n"
"POT-Creation-Date: 2024-05-08 14:11+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"
@@ -36,7 +36,7 @@ msgstr "成功: %s、失敗: %s、合計: %s"
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
#: settings/serializers/msg.py:35 terminal/serializers/storage.py:123
#: terminal/serializers/storage.py:142 users/forms/profile.py:21
#: users/serializers/user.py:109
#: users/serializers/user.py:110
#: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/cloud/serializers/account_attrs.py:28
@@ -384,7 +384,7 @@ msgstr "アカウントバックアップ計画"
#: accounts/models/automations/backup_account.py:119
#: assets/models/automations/base.py:115 audits/models.py:65
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:238
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:241
#: ops/templates/ops/celery_task_log.html:75
#: perms/models/asset_permission.py:78
#: settings/templates/ldap/_msg_import_ldap_user.html:5
@@ -473,7 +473,7 @@ msgstr "SSHキープッシュ方式"
#: accounts/models/automations/gather_account.py:58
#: accounts/serializers/account/backup.py:41
#: accounts/serializers/automations/change_secret.py:58
#: settings/serializers/auth/ldap.py:90
#: settings/serializers/auth/ldap.py:91
msgid "Recipient"
msgstr "受信者"
@@ -495,14 +495,14 @@ msgstr "開始日"
#: accounts/models/automations/change_secret.py:42
#: assets/models/automations/base.py:116 ops/models/base.py:56
#: ops/models/celery.py:89 ops/models/job.py:239
#: ops/models/celery.py:89 ops/models/job.py:242
#: terminal/models/applet/host.py:142
msgid "Date finished"
msgstr "終了日"
#: accounts/models/automations/change_secret.py:44
#: assets/models/automations/base.py:113 audits/models.py:208
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:230
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:233
#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30
#: terminal/models/virtualapp/virtualapp.py:99
@@ -624,14 +624,14 @@ msgstr "パスワードルール"
#: assets/models/_user.py:22 assets/models/asset/common.py:93
#: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21
#: assets/models/domain.py:19 assets/models/group.py:17
#: assets/models/label.py:18 assets/models/platform.py:16
#: assets/models/platform.py:95 assets/serializers/asset/common.py:149
#: assets/models/label.py:18 assets/models/platform.py:15
#: assets/models/platform.py:94 assets/serializers/asset/common.py:149
#: assets/serializers/platform.py:118 assets/serializers/platform.py:228
#: authentication/backends/passkey/models.py:10
#: authentication/serializers/connect_token_secret.py:113
#: authentication/serializers/connect_token_secret.py:168 labels/models.py:11
#: authentication/serializers/connect_token_secret.py:169 labels/models.py:11
#: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15
#: ops/models/celery.py:80 ops/models/job.py:139 ops/models/playbook.py:28
#: ops/models/celery.py:80 ops/models/job.py:142 ops/models/playbook.py:28
#: ops/serializers/job.py:18 orgs/models.py:82
#: perms/models/asset_permission.py:61 rbac/models/role.py:29
#: settings/models.py:33 settings/models.py:181 settings/serializers/msg.py:89
@@ -658,7 +658,7 @@ msgstr "特権アカウント"
#: authentication/serializers/connect_token_secret.py:117
#: terminal/models/applet/applet.py:40
#: terminal/models/component/endpoint.py:120
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:173
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:174
msgid "Is active"
msgstr "アクティブです。"
@@ -774,7 +774,7 @@ msgid "Exist policy"
msgstr "アカウントの存在ポリシー"
#: accounts/serializers/account/account.py:193 applications/models.py:11
#: assets/models/label.py:21 assets/models/platform.py:96
#: assets/models/label.py:21 assets/models/platform.py:95
#: assets/serializers/asset/common.py:125 assets/serializers/cagegory.py:12
#: assets/serializers/platform.py:140 assets/serializers/platform.py:229
#: perms/serializers/user_permission.py:26 settings/models.py:35
@@ -786,11 +786,11 @@ msgstr "カテゴリ"
#: accounts/serializers/automations/base.py:55 acls/models/command_acl.py:24
#: acls/serializers/command_acl.py:19 applications/models.py:14
#: assets/models/_user.py:50 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:97
#: assets/models/cmd_filter.py:74 assets/models/platform.py:96
#: assets/serializers/asset/common.py:126 assets/serializers/platform.py:120
#: assets/serializers/platform.py:139 audits/serializers.py:53
#: audits/serializers.py:170
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:147
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:150
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:39
#: terminal/models/component/storage.py:57
#: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29
@@ -826,7 +826,7 @@ msgstr "編集済み"
#: assets/models/automations/base.py:19
#: assets/serializers/automations/base.py:20
#: authentication/api/connection_token.py:410 ops/models/base.py:17
#: ops/models/job.py:149 ops/serializers/job.py:19
#: ops/models/job.py:152 ops/serializers/job.py:19
#: terminal/templates/terminal/_msg_command_execute_alert.html:16
msgid "Assets"
msgstr "資産"
@@ -958,7 +958,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默
#: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27
#: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88
#: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26
#: ops/models/job.py:155 ops/models/playbook.py:31 rbac/models/role.py:37
#: ops/models/job.py:158 ops/models/playbook.py:31 rbac/models/role.py:37
#: settings/models.py:38 terminal/models/applet/applet.py:45
#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143
#: terminal/models/component/endpoint.py:25
@@ -1377,15 +1377,15 @@ msgstr ""
msgid "Number required"
msgstr "必要な数"
#: assets/api/node.py:57
#: assets/api/node.py:58
msgid "You can't update the root node name"
msgstr "ルートノード名を更新できません"
#: assets/api/node.py:64
#: assets/api/node.py:65
msgid "You can't delete the root node ({})"
msgstr "ルートノード ({}) を削除できません。"
#: assets/api/node.py:67
#: assets/api/node.py:68
msgid "Deletion failed and the node contains assets"
msgstr "削除に失敗し、ノードにアセットが含まれています。"
@@ -1397,11 +1397,11 @@ msgstr "同じレベルのノード名を同じにすることはできません
msgid "App assets"
msgstr "アプリ資産"
#: assets/automations/base/manager.py:188
#: assets/automations/base/manager.py:191
msgid "{} disabled"
msgstr "{} 無効"
#: assets/automations/base/manager.py:251
#: assets/automations/base/manager.py:254
msgid " - Platform {} ansible disabled"
msgstr " - プラットフォーム {} ansible 無効"
@@ -1689,7 +1689,7 @@ msgstr "SSHパブリックキー"
#: assets/models/_user.py:28 assets/models/automations/base.py:114
#: assets/models/cmd_filter.py:41 assets/models/group.py:19
#: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54
#: ops/models/job.py:237 users/models/user.py:1058
#: ops/models/job.py:240 users/models/user.py:1058
msgid "Date created"
msgstr "作成された日付"
@@ -1778,7 +1778,7 @@ msgstr "システムユーザーに一致できます"
msgid "Cloud"
msgstr "クラウド サービス"
#: assets/models/asset/common.py:94 assets/models/platform.py:17
#: assets/models/asset/common.py:94 assets/models/platform.py:16
#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72
#: settings/serializers/msg.py:32 terminal/serializers/storage.py:133
#: xpack/plugins/cloud/serializers/account_attrs.py:73
@@ -1789,7 +1789,7 @@ msgstr "ポート"
msgid "Address"
msgstr "アドレス"
#: assets/models/asset/common.py:161 assets/models/platform.py:126
#: assets/models/asset/common.py:161 assets/models/platform.py:134
#: authentication/backends/passkey/models.py:12
#: authentication/serializers/connect_token_secret.py:118
#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:325
@@ -1858,7 +1858,7 @@ msgstr "証明書チェックを無視"
msgid "Proxy"
msgstr "プロキシー"
#: assets/models/automations/base.py:22 ops/models/job.py:233
#: assets/models/automations/base.py:22 ops/models/job.py:236
#: settings/serializers/auth/sms.py:103
msgid "Parameters"
msgstr "パラメータ"
@@ -1929,7 +1929,7 @@ msgstr "ゲートウェイ"
msgid "Asset group"
msgstr "資産グループ"
#: assets/models/group.py:31 assets/models/platform.py:20
#: assets/models/group.py:31 assets/models/platform.py:19
#: assets/serializers/platform.py:121
#: xpack/plugins/cloud/providers/nutanix.py:30
msgid "Default"
@@ -1986,133 +1986,133 @@ msgstr "親キー"
msgid "Can match node"
msgstr "ノードを一致させることができます"
#: assets/models/platform.py:18
#: assets/models/platform.py:17
msgid "Primary"
msgstr "主要"
#: assets/models/platform.py:19
#: assets/models/platform.py:18
msgid "Required"
msgstr "必要"
#: assets/models/platform.py:21
#: assets/models/platform.py:20
msgid "Public"
msgstr "開ける"
#: assets/models/platform.py:22 assets/serializers/platform.py:49
#: assets/models/platform.py:21 assets/serializers/platform.py:49
#: settings/serializers/settings.py:95
#: users/templates/users/reset_password.html:29
msgid "Setting"
msgstr "設定"
#: assets/models/platform.py:39 audits/const.py:56
#: assets/models/platform.py:38 audits/const.py:56
#: authentication/backends/passkey/models.py:11 settings/models.py:37
#: terminal/serializers/applet_host.py:33
msgid "Enabled"
msgstr "有効化"
#: assets/models/platform.py:40
#: assets/models/platform.py:39
msgid "Ansible config"
msgstr "Ansible 構成"
#: assets/models/platform.py:42 assets/serializers/platform.py:33
#: assets/models/platform.py:41 assets/serializers/platform.py:33
msgid "Ping enabled"
msgstr "アセット ディスカバリを有効にする"
#: assets/models/platform.py:43 assets/serializers/platform.py:34
#: assets/models/platform.py:42 assets/serializers/platform.py:34
msgid "Ping method"
msgstr "資産検出方法"
#: assets/models/platform.py:44
#: assets/models/platform.py:43
msgid "Ping params"
msgstr "資産検出パラメータ"
#: assets/models/platform.py:46 assets/models/platform.py:70
#: assets/models/platform.py:45 assets/models/platform.py:69
#: assets/serializers/platform.py:35
msgid "Gather facts enabled"
msgstr "資産情報の収集を有効にする"
#: assets/models/platform.py:48 assets/models/platform.py:72
#: assets/models/platform.py:47 assets/models/platform.py:71
#: assets/serializers/platform.py:36
msgid "Gather facts method"
msgstr "情報収集の方法"
#: assets/models/platform.py:50 assets/models/platform.py:74
#: assets/models/platform.py:49 assets/models/platform.py:73
msgid "Gather facts params"
msgstr "情報収集パラメータ"
#: assets/models/platform.py:52 assets/serializers/platform.py:39
#: assets/models/platform.py:51 assets/serializers/platform.py:39
msgid "Change secret enabled"
msgstr "パスワードの変更が有効"
#: assets/models/platform.py:54 assets/serializers/platform.py:40
#: assets/models/platform.py:53 assets/serializers/platform.py:40
msgid "Change secret method"
msgstr "パスワード変更モード"
#: assets/models/platform.py:56
#: assets/models/platform.py:55
msgid "Change secret params"
msgstr "パスワード変更パラメータ"
#: assets/models/platform.py:58 assets/serializers/platform.py:41
#: assets/models/platform.py:57 assets/serializers/platform.py:41
msgid "Push account enabled"
msgstr "アカウントのプッシュを有効にする"
#: assets/models/platform.py:60 assets/serializers/platform.py:42
#: assets/models/platform.py:59 assets/serializers/platform.py:42
msgid "Push account method"
msgstr "アカウントプッシュ方式"
#: assets/models/platform.py:62
#: assets/models/platform.py:61
msgid "Push account params"
msgstr "アカウントプッシュパラメータ"
#: assets/models/platform.py:64 assets/serializers/platform.py:37
#: assets/models/platform.py:63 assets/serializers/platform.py:37
msgid "Verify account enabled"
msgstr "アカウントの確認をオンにする"
#: assets/models/platform.py:66 assets/serializers/platform.py:38
#: assets/models/platform.py:65 assets/serializers/platform.py:38
msgid "Verify account method"
msgstr "アカウント認証方法"
#: assets/models/platform.py:68
#: assets/models/platform.py:67
msgid "Verify account params"
msgstr "アカウント認証パラメータ"
#: assets/models/platform.py:76
#: assets/models/platform.py:75
msgid "Remove account enabled"
msgstr "アカウントを開いて削除"
#: assets/models/platform.py:78
#: assets/models/platform.py:77
msgid "Remove account method"
msgstr "アカウントの削除方法"
#: assets/models/platform.py:80
#: assets/models/platform.py:79
msgid "Remove account params"
msgstr "アカウント削除パラメータ"
#: assets/models/platform.py:98 tickets/models/ticket/general.py:298
#: assets/models/platform.py:97 tickets/models/ticket/general.py:298
msgid "Meta"
msgstr "メタ"
#: assets/models/platform.py:99 labels/models.py:13
#: assets/models/platform.py:98 labels/models.py:13
msgid "Internal"
msgstr "ビルトイン"
#: assets/models/platform.py:103 assets/serializers/platform.py:138
#: assets/models/platform.py:102 assets/serializers/platform.py:138
msgid "Charset"
msgstr "シャーセット"
#: assets/models/platform.py:105 assets/serializers/platform.py:167
#: assets/models/platform.py:104 assets/serializers/platform.py:167
msgid "Domain enabled"
msgstr "ドメインを有効にする"
#: assets/models/platform.py:107 assets/serializers/platform.py:166
#: assets/models/platform.py:106 assets/serializers/platform.py:166
msgid "Su enabled"
msgstr "アカウントの切り替えを有効にする"
#: assets/models/platform.py:108 assets/serializers/platform.py:144
#: assets/models/platform.py:107 assets/serializers/platform.py:144
msgid "Su method"
msgstr "アカウントの切り替え方法"
#: assets/models/platform.py:109 assets/serializers/platform.py:147
#: assets/models/platform.py:108 assets/serializers/platform.py:147
msgid "Custom fields"
msgstr "カスタムフィールド"
@@ -2620,8 +2620,8 @@ msgid "Offline user session"
msgstr "オフラインユーザセッション"
#: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:148
#: ops/models/job.py:236 ops/models/playbook.py:30
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:151
#: ops/models/job.py:239 ops/models/playbook.py:30
#: terminal/models/session/sharing.py:25
msgid "Creator"
msgstr "作成者"
@@ -2754,7 +2754,7 @@ msgstr "ACL アクションは拒否です: {}({})"
msgid "ACL action is review"
msgstr "ACL アクションはレビューです"
#: authentication/api/mfa.py:59
#: authentication/api/mfa.py:62
msgid "Current user not support mfa type: {}"
msgstr "現在のユーザーはmfaタイプをサポートしていません: {}"
@@ -3211,11 +3211,11 @@ msgstr "ユーザーなしまたは期限切れのユーザー"
msgid "No asset or inactive asset"
msgstr "アセットがないか、有効化されていないアセット"
#: authentication/models/connection_token.py:272
#: authentication/models/connection_token.py:274
msgid "Can view super connection token secret"
msgstr "スーパー接続トークンのシークレットを表示できます"
#: authentication/models/connection_token.py:274
#: authentication/models/connection_token.py:276
msgid "Super connection token"
msgstr "スーパー接続トークン"
@@ -3259,17 +3259,17 @@ msgstr "コンポーネント"
msgid "Expired now"
msgstr "すぐに期限切れ"
#: authentication/serializers/connect_token_secret.py:169
#: authentication/serializers/connect_token_secret.py:170
#: terminal/models/virtualapp/virtualapp.py:25
msgid "Image name"
msgstr "ミラー名"
#: authentication/serializers/connect_token_secret.py:170
#: authentication/serializers/connect_token_secret.py:171
#: terminal/models/virtualapp/virtualapp.py:27
msgid "Image port"
msgstr "ミラーポート"
#: authentication/serializers/connect_token_secret.py:171
#: authentication/serializers/connect_token_secret.py:172
#: terminal/models/virtualapp/virtualapp.py:26
msgid "Image protocol"
msgstr "ミラープロトコル"
@@ -3292,7 +3292,7 @@ msgstr "アクション"
#: authentication/serializers/connection_token.py:42
#: perms/serializers/permission.py:40 perms/serializers/permission.py:60
#: users/serializers/user.py:100 users/serializers/user.py:177
#: users/serializers/user.py:101 users/serializers/user.py:178
msgid "Is expired"
msgstr "期限切れです"
@@ -3306,8 +3306,8 @@ msgid "Access IP"
msgstr "Access IP"
#: authentication/serializers/token.py:92 perms/serializers/permission.py:39
#: perms/serializers/permission.py:61 users/serializers/user.py:101
#: users/serializers/user.py:174
#: perms/serializers/permission.py:61 users/serializers/user.py:102
#: users/serializers/user.py:175
msgid "Is valid"
msgstr "有効です"
@@ -3984,7 +3984,7 @@ msgstr "間違ったデータ タイプです。リストにする必要があ
msgid "Invalid choice: {}"
msgstr "無効なオプション: {}"
#: common/serializers/mixin.py:397 labels/apps.py:8
#: common/serializers/mixin.py:406 labels/apps.py:8
msgid "Labels"
msgstr "ラベル"
@@ -4118,15 +4118,15 @@ msgstr "システムメッセージ"
msgid "Publish the station message"
msgstr "投稿サイトニュース"
#: ops/ansible/inventory.py:106 ops/models/job.py:63
#: ops/ansible/inventory.py:107 ops/models/job.py:65
msgid "No account available"
msgstr "利用可能なアカウントがありません"
#: ops/ansible/inventory.py:285
#: ops/ansible/inventory.py:286
msgid "Ansible disabled"
msgstr "Ansible 無効"
#: ops/ansible/inventory.py:301
#: ops/ansible/inventory.py:302
msgid "Skip hosts below:"
msgstr "次のホストをスキップします: "
@@ -4247,7 +4247,7 @@ msgstr "VCS"
msgid "Adhoc"
msgstr "コマンド#コマンド#"
#: ops/const.py:39 ops/models/job.py:146
#: ops/const.py:39 ops/models/job.py:149
msgid "Playbook"
msgstr "Playbook"
@@ -4336,11 +4336,11 @@ msgstr "定期的または定期的に設定を行う必要があります"
msgid "Pattern"
msgstr "パターン"
#: ops/models/adhoc.py:23 ops/models/job.py:143
#: ops/models/adhoc.py:23 ops/models/job.py:146
msgid "Module"
msgstr "モジュール"
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:141
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:144
#: terminal/models/component/task.py:14
msgid "Args"
msgstr "アルグ"
@@ -4359,12 +4359,12 @@ msgstr "最後の実行"
msgid "Date last run"
msgstr "最終実行日"
#: ops/models/base.py:51 ops/models/job.py:234
#: ops/models/base.py:51 ops/models/job.py:237
#: xpack/plugins/cloud/models.py:198
msgid "Result"
msgstr "結果"
#: ops/models/base.py:52 ops/models/job.py:235
#: ops/models/base.py:52 ops/models/job.py:238
msgid "Summary"
msgstr "概要"
@@ -4397,43 +4397,43 @@ msgstr "発売日"
msgid "Celery Task Execution"
msgstr "Celery タスク実行"
#: ops/models/job.py:144
#: ops/models/job.py:147
msgid "Chdir"
msgstr "実行ディレクトリ"
#: ops/models/job.py:145
#: ops/models/job.py:148
msgid "Timeout (Seconds)"
msgstr "タイムアウト(秒)"
#: ops/models/job.py:150
#: ops/models/job.py:153
msgid "Use Parameter Define"
msgstr "パラメータ定義を使用する"
#: ops/models/job.py:151
#: ops/models/job.py:154
msgid "Parameters define"
msgstr "パラメータ定義"
#: ops/models/job.py:152
#: ops/models/job.py:155
msgid "Runas"
msgstr "ユーザーとして実行"
#: ops/models/job.py:154
#: ops/models/job.py:157
msgid "Runas policy"
msgstr "ユーザー ポリシー"
#: ops/models/job.py:218
#: ops/models/job.py:221
msgid "Job"
msgstr "ジョブ#ジョブ#"
#: ops/models/job.py:241
#: ops/models/job.py:244
msgid "Material"
msgstr "Material"
#: ops/models/job.py:243
#: ops/models/job.py:246
msgid "Material Type"
msgstr "Material を選択してオプションを設定します。"
#: ops/models/job.py:540
#: ops/models/job.py:544
msgid "Job Execution"
msgstr "ジョブ実行"
@@ -5237,16 +5237,19 @@ msgstr "User DN キャッシュの有効期限 (秒)"
#: settings/serializers/auth/ldap.py:84
msgid ""
"Caching the User DN obtained during user login authentication can "
"effectivelyimprove the speed of user authentication., 0 means no cache"
"effectivelyimprove the speed of user authentication., 0 means no cache<br>If "
"the user OU structure has been adjusted, click Submit to clear the user DN "
"cache"
msgstr ""
"ユーザーログイン認証時に取得したユーザー DN をキャッシュすることで、ユーザー"
"認証の速度を効果的に向上させることができます"
"認証の速度を効果的に向上させることができます<br>ユーザー OU 構造が調整された"
"場合、送信をクリックしてユーザー DN キャッシュをクリアします"
#: settings/serializers/auth/ldap.py:88
#: settings/serializers/auth/ldap.py:89
msgid "Search paged size (piece)"
msgstr "ページサイズを検索 (じょう)"
#: settings/serializers/auth/ldap.py:93
#: settings/serializers/auth/ldap.py:94
msgid "Enable LDAP auth"
msgstr "LDAP認証の有効化"
@@ -8099,7 +8102,7 @@ msgstr "強制有効"
msgid "Lark"
msgstr ""
#: users/models/user.py:826 users/serializers/user.py:175
#: users/models/user.py:826 users/serializers/user.py:176
msgid "Is service account"
msgstr "サービスアカウントです"
@@ -8111,7 +8114,7 @@ msgstr "アバター"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:834 users/serializers/user.py:111
#: users/models/user.py:834 users/serializers/user.py:112
msgid "Phone"
msgstr "電話"
@@ -8122,7 +8125,7 @@ msgstr "OTP 秘密"
# msgid "Private key"
# msgstr "ssh秘密鍵"
#: users/models/user.py:852 users/serializers/profile.py:128
#: users/serializers/user.py:172
#: users/serializers/user.py:173
msgid "Is first login"
msgstr "最初のログインです"
@@ -8296,71 +8299,71 @@ msgstr "パスワードがセキュリティルールと一致しない"
msgid "The new password cannot be the last {} passwords"
msgstr "新しいパスワードを最後の {} 個のパスワードにすることはできません"
#: users/serializers/user.py:44
#: users/serializers/user.py:45
msgid "System roles"
msgstr "システムの役割"
#: users/serializers/user.py:48
#: users/serializers/user.py:49
msgid "Org roles"
msgstr "組織ロール"
#: users/serializers/user.py:51
#: users/serializers/user.py:52
msgid "Organizations and roles"
msgstr "そしきとやくわり"
#: users/serializers/user.py:93
#: users/serializers/user.py:94
msgid "Password strategy"
msgstr "パスワード戦略"
#: users/serializers/user.py:95
#: users/serializers/user.py:96
msgid "MFA enabled"
msgstr "MFA有効化"
#: users/serializers/user.py:97
#: users/serializers/user.py:98
msgid "MFA force enabled"
msgstr "MFAフォース有効化"
#: users/serializers/user.py:99
#: users/serializers/user.py:100
msgid "Login blocked"
msgstr "ログインがロックされました"
#: users/serializers/user.py:102 users/serializers/user.py:181
#: users/serializers/user.py:103 users/serializers/user.py:182
msgid "Is OTP bound"
msgstr "仮想MFAがバインドされているか"
#: users/serializers/user.py:103
#: users/serializers/user.py:104
msgid "Super Administrator"
msgstr "スーパーアドミニストレーター"
#: users/serializers/user.py:104
#: users/serializers/user.py:105
msgid "Organization Administrator"
msgstr "組織管理者"
#: users/serializers/user.py:106
#: users/serializers/user.py:107
msgid "Can public key authentication"
msgstr "公開鍵認証が可能"
#: users/serializers/user.py:176
#: users/serializers/user.py:177
msgid "Is org admin"
msgstr "組織管理者です"
#: users/serializers/user.py:178
#: users/serializers/user.py:179
msgid "Avatar url"
msgstr "アバターURL"
#: users/serializers/user.py:182
#: users/serializers/user.py:183
msgid "MFA level"
msgstr "MFA レベル"
#: users/serializers/user.py:304
#: users/serializers/user.py:305
msgid "Select users"
msgstr "ユーザーの選択"
#: users/serializers/user.py:305
#: users/serializers/user.py:306
msgid "For security, only list several users"
msgstr "セキュリティのために、複数のユーザーのみをリストします"
#: users/serializers/user.py:338
#: users/serializers/user.py:339
msgid "name not unique"
msgstr "名前が一意ではない"

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:baa5feb7e592b529b0a3d5695f236d7542f069f97c236def1ebf9c99f9ea75f8
size 145065
oid sha256:08667579241592ecacd1baf330147a720a9e41171444b925f90778613a7e1d9a
size 145230

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-18 17:25+0800\n"
"POT-Creation-Date: 2024-05-08 14:11+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"
@@ -35,7 +35,7 @@ msgstr "成功: %s, 失败: %s, 总数: %s"
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
#: settings/serializers/msg.py:35 terminal/serializers/storage.py:123
#: terminal/serializers/storage.py:142 users/forms/profile.py:21
#: users/serializers/user.py:109
#: users/serializers/user.py:110
#: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/cloud/serializers/account_attrs.py:28
@@ -383,7 +383,7 @@ msgstr "账号备份计划"
#: accounts/models/automations/backup_account.py:119
#: assets/models/automations/base.py:115 audits/models.py:65
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:238
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:241
#: ops/templates/ops/celery_task_log.html:75
#: perms/models/asset_permission.py:78
#: settings/templates/ldap/_msg_import_ldap_user.html:5
@@ -472,7 +472,7 @@ msgstr "SSH 密钥推送方式"
#: accounts/models/automations/gather_account.py:58
#: accounts/serializers/account/backup.py:41
#: accounts/serializers/automations/change_secret.py:58
#: settings/serializers/auth/ldap.py:90
#: settings/serializers/auth/ldap.py:91
msgid "Recipient"
msgstr "收件人"
@@ -494,14 +494,14 @@ msgstr "开始日期"
#: accounts/models/automations/change_secret.py:42
#: assets/models/automations/base.py:116 ops/models/base.py:56
#: ops/models/celery.py:89 ops/models/job.py:239
#: ops/models/celery.py:89 ops/models/job.py:242
#: terminal/models/applet/host.py:142
msgid "Date finished"
msgstr "结束日期"
#: accounts/models/automations/change_secret.py:44
#: assets/models/automations/base.py:113 audits/models.py:208
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:230
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:233
#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30
#: terminal/models/virtualapp/virtualapp.py:99
@@ -623,14 +623,14 @@ msgstr "密码规则"
#: assets/models/_user.py:22 assets/models/asset/common.py:93
#: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21
#: assets/models/domain.py:19 assets/models/group.py:17
#: assets/models/label.py:18 assets/models/platform.py:16
#: assets/models/platform.py:95 assets/serializers/asset/common.py:149
#: assets/models/label.py:18 assets/models/platform.py:15
#: assets/models/platform.py:94 assets/serializers/asset/common.py:149
#: assets/serializers/platform.py:118 assets/serializers/platform.py:228
#: authentication/backends/passkey/models.py:10
#: authentication/serializers/connect_token_secret.py:113
#: authentication/serializers/connect_token_secret.py:168 labels/models.py:11
#: authentication/serializers/connect_token_secret.py:169 labels/models.py:11
#: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15
#: ops/models/celery.py:80 ops/models/job.py:139 ops/models/playbook.py:28
#: ops/models/celery.py:80 ops/models/job.py:142 ops/models/playbook.py:28
#: ops/serializers/job.py:18 orgs/models.py:82
#: perms/models/asset_permission.py:61 rbac/models/role.py:29
#: settings/models.py:33 settings/models.py:181 settings/serializers/msg.py:89
@@ -657,7 +657,7 @@ msgstr "特权账号"
#: authentication/serializers/connect_token_secret.py:117
#: terminal/models/applet/applet.py:40
#: terminal/models/component/endpoint.py:120
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:173
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:174
msgid "Is active"
msgstr "激活"
@@ -772,7 +772,7 @@ msgid "Exist policy"
msgstr "账号存在策略"
#: accounts/serializers/account/account.py:193 applications/models.py:11
#: assets/models/label.py:21 assets/models/platform.py:96
#: assets/models/label.py:21 assets/models/platform.py:95
#: assets/serializers/asset/common.py:125 assets/serializers/cagegory.py:12
#: assets/serializers/platform.py:140 assets/serializers/platform.py:229
#: perms/serializers/user_permission.py:26 settings/models.py:35
@@ -784,11 +784,11 @@ msgstr "类别"
#: accounts/serializers/automations/base.py:55 acls/models/command_acl.py:24
#: acls/serializers/command_acl.py:19 applications/models.py:14
#: assets/models/_user.py:50 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:97
#: assets/models/cmd_filter.py:74 assets/models/platform.py:96
#: assets/serializers/asset/common.py:126 assets/serializers/platform.py:120
#: assets/serializers/platform.py:139 audits/serializers.py:53
#: audits/serializers.py:170
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:147
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:150
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:39
#: terminal/models/component/storage.py:57
#: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29
@@ -824,7 +824,7 @@ msgstr "已修改"
#: assets/models/automations/base.py:19
#: assets/serializers/automations/base.py:20
#: authentication/api/connection_token.py:410 ops/models/base.py:17
#: ops/models/job.py:149 ops/serializers/job.py:19
#: ops/models/job.py:152 ops/serializers/job.py:19
#: terminal/templates/terminal/_msg_command_execute_alert.html:16
msgid "Assets"
msgstr "资产"
@@ -956,7 +956,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认
#: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27
#: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88
#: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26
#: ops/models/job.py:155 ops/models/playbook.py:31 rbac/models/role.py:37
#: ops/models/job.py:158 ops/models/playbook.py:31 rbac/models/role.py:37
#: settings/models.py:38 terminal/models/applet/applet.py:45
#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143
#: terminal/models/component/endpoint.py:25
@@ -1367,15 +1367,15 @@ msgstr "不能直接创建资产, 你应该创建主机或其他资产"
msgid "Number required"
msgstr "需要为数字"
#: assets/api/node.py:57
#: assets/api/node.py:58
msgid "You can't update the root node name"
msgstr "不能修改根节点名称"
#: assets/api/node.py:64
#: assets/api/node.py:65
msgid "You can't delete the root node ({})"
msgstr "不能删除根节点 ({})"
#: assets/api/node.py:67
#: assets/api/node.py:68
msgid "Deletion failed and the node contains assets"
msgstr "删除失败,节点包含资产"
@@ -1387,11 +1387,11 @@ msgstr "同级别节点名字不能重复"
msgid "App assets"
msgstr "资产管理"
#: assets/automations/base/manager.py:188
#: assets/automations/base/manager.py:191
msgid "{} disabled"
msgstr "{} 已禁用"
#: assets/automations/base/manager.py:251
#: assets/automations/base/manager.py:254
msgid " - Platform {} ansible disabled"
msgstr " - 平台 {} Ansible 已禁用, 无法执行任务"
@@ -1679,7 +1679,7 @@ msgstr "SSH公钥"
#: assets/models/_user.py:28 assets/models/automations/base.py:114
#: assets/models/cmd_filter.py:41 assets/models/group.py:19
#: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54
#: ops/models/job.py:237 users/models/user.py:1058
#: ops/models/job.py:240 users/models/user.py:1058
msgid "Date created"
msgstr "创建日期"
@@ -1768,7 +1768,7 @@ msgstr "可以匹配系统用户"
msgid "Cloud"
msgstr "云服务"
#: assets/models/asset/common.py:94 assets/models/platform.py:17
#: assets/models/asset/common.py:94 assets/models/platform.py:16
#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72
#: settings/serializers/msg.py:32 terminal/serializers/storage.py:133
#: xpack/plugins/cloud/serializers/account_attrs.py:73
@@ -1779,7 +1779,7 @@ msgstr "端口"
msgid "Address"
msgstr "地址"
#: assets/models/asset/common.py:161 assets/models/platform.py:126
#: assets/models/asset/common.py:161 assets/models/platform.py:134
#: authentication/backends/passkey/models.py:12
#: authentication/serializers/connect_token_secret.py:118
#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:325
@@ -1848,7 +1848,7 @@ msgstr "忽略证书校验"
msgid "Proxy"
msgstr "代理"
#: assets/models/automations/base.py:22 ops/models/job.py:233
#: assets/models/automations/base.py:22 ops/models/job.py:236
#: settings/serializers/auth/sms.py:103
msgid "Parameters"
msgstr "参数"
@@ -1919,7 +1919,7 @@ msgstr "网关"
msgid "Asset group"
msgstr "资产组"
#: assets/models/group.py:31 assets/models/platform.py:20
#: assets/models/group.py:31 assets/models/platform.py:19
#: assets/serializers/platform.py:121
#: xpack/plugins/cloud/providers/nutanix.py:30
msgid "Default"
@@ -1976,133 +1976,133 @@ msgstr "ssh私钥"
msgid "Can match node"
msgstr "可以匹配节点"
#: assets/models/platform.py:18
#: assets/models/platform.py:17
msgid "Primary"
msgstr "主要的"
#: assets/models/platform.py:19
#: assets/models/platform.py:18
msgid "Required"
msgstr "必须的"
#: assets/models/platform.py:21
#: assets/models/platform.py:20
msgid "Public"
msgstr "开放的"
#: assets/models/platform.py:22 assets/serializers/platform.py:49
#: assets/models/platform.py:21 assets/serializers/platform.py:49
#: settings/serializers/settings.py:95
#: users/templates/users/reset_password.html:29
msgid "Setting"
msgstr "设置"
#: assets/models/platform.py:39 audits/const.py:56
#: assets/models/platform.py:38 audits/const.py:56
#: authentication/backends/passkey/models.py:11 settings/models.py:37
#: terminal/serializers/applet_host.py:33
msgid "Enabled"
msgstr "启用"
#: assets/models/platform.py:40
#: assets/models/platform.py:39
msgid "Ansible config"
msgstr "Ansible 配置"
#: assets/models/platform.py:42 assets/serializers/platform.py:33
#: assets/models/platform.py:41 assets/serializers/platform.py:33
msgid "Ping enabled"
msgstr "启用资产探活"
#: assets/models/platform.py:43 assets/serializers/platform.py:34
#: assets/models/platform.py:42 assets/serializers/platform.py:34
msgid "Ping method"
msgstr "资产探活方式"
#: assets/models/platform.py:44
#: assets/models/platform.py:43
msgid "Ping params"
msgstr "资产探活参数"
#: assets/models/platform.py:46 assets/models/platform.py:70
#: assets/models/platform.py:45 assets/models/platform.py:69
#: assets/serializers/platform.py:35
msgid "Gather facts enabled"
msgstr "启用收集资产信息"
#: assets/models/platform.py:48 assets/models/platform.py:72
#: assets/models/platform.py:47 assets/models/platform.py:71
#: assets/serializers/platform.py:36
msgid "Gather facts method"
msgstr "收集信息方式"
#: assets/models/platform.py:50 assets/models/platform.py:74
#: assets/models/platform.py:49 assets/models/platform.py:73
msgid "Gather facts params"
msgstr "收集信息参数"
#: assets/models/platform.py:52 assets/serializers/platform.py:39
#: assets/models/platform.py:51 assets/serializers/platform.py:39
msgid "Change secret enabled"
msgstr "启用改密"
#: assets/models/platform.py:54 assets/serializers/platform.py:40
#: assets/models/platform.py:53 assets/serializers/platform.py:40
msgid "Change secret method"
msgstr "改密方式"
#: assets/models/platform.py:56
#: assets/models/platform.py:55
msgid "Change secret params"
msgstr "改密参数"
#: assets/models/platform.py:58 assets/serializers/platform.py:41
#: assets/models/platform.py:57 assets/serializers/platform.py:41
msgid "Push account enabled"
msgstr "启用账号推送"
#: assets/models/platform.py:60 assets/serializers/platform.py:42
#: assets/models/platform.py:59 assets/serializers/platform.py:42
msgid "Push account method"
msgstr "账号推送方式"
#: assets/models/platform.py:62
#: assets/models/platform.py:61
msgid "Push account params"
msgstr "账号推送参数"
#: assets/models/platform.py:64 assets/serializers/platform.py:37
#: assets/models/platform.py:63 assets/serializers/platform.py:37
msgid "Verify account enabled"
msgstr "开启账号验证"
#: assets/models/platform.py:66 assets/serializers/platform.py:38
#: assets/models/platform.py:65 assets/serializers/platform.py:38
msgid "Verify account method"
msgstr "账号验证方式"
#: assets/models/platform.py:68
#: assets/models/platform.py:67
msgid "Verify account params"
msgstr "账号验证参数"
#: assets/models/platform.py:76
#: assets/models/platform.py:75
msgid "Remove account enabled"
msgstr "开启账号移除"
#: assets/models/platform.py:78
#: assets/models/platform.py:77
msgid "Remove account method"
msgstr "账号移除方式"
#: assets/models/platform.py:80
#: assets/models/platform.py:79
msgid "Remove account params"
msgstr "账号移除参数"
#: assets/models/platform.py:98 tickets/models/ticket/general.py:298
#: assets/models/platform.py:97 tickets/models/ticket/general.py:298
msgid "Meta"
msgstr "元数据"
#: assets/models/platform.py:99 labels/models.py:13
#: assets/models/platform.py:98 labels/models.py:13
msgid "Internal"
msgstr "内置"
#: assets/models/platform.py:103 assets/serializers/platform.py:138
#: assets/models/platform.py:102 assets/serializers/platform.py:138
msgid "Charset"
msgstr "编码"
#: assets/models/platform.py:105 assets/serializers/platform.py:167
#: assets/models/platform.py:104 assets/serializers/platform.py:167
msgid "Domain enabled"
msgstr "启用网域"
#: assets/models/platform.py:107 assets/serializers/platform.py:166
#: assets/models/platform.py:106 assets/serializers/platform.py:166
msgid "Su enabled"
msgstr "启用账号切换"
#: assets/models/platform.py:108 assets/serializers/platform.py:144
#: assets/models/platform.py:107 assets/serializers/platform.py:144
msgid "Su method"
msgstr "账号切换方式"
#: assets/models/platform.py:109 assets/serializers/platform.py:147
#: assets/models/platform.py:108 assets/serializers/platform.py:147
msgid "Custom fields"
msgstr "自定义属性"
@@ -2601,8 +2601,8 @@ msgid "Offline user session"
msgstr "下线用户会话"
#: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:148
#: ops/models/job.py:236 ops/models/playbook.py:30
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:151
#: ops/models/job.py:239 ops/models/playbook.py:30
#: terminal/models/session/sharing.py:25
msgid "Creator"
msgstr "创建者"
@@ -2733,7 +2733,7 @@ msgstr "ACL 动作是拒绝: {}({})"
msgid "ACL action is review"
msgstr "ACL 动作是复核"
#: authentication/api/mfa.py:59
#: authentication/api/mfa.py:62
msgid "Current user not support mfa type: {}"
msgstr "当前用户不支持 MFA 类型: {}"
@@ -3177,11 +3177,11 @@ msgstr "没有用户或用户失效"
msgid "No asset or inactive asset"
msgstr "没有资产或资产未激活"
#: authentication/models/connection_token.py:272
#: authentication/models/connection_token.py:274
msgid "Can view super connection token secret"
msgstr "可以查看超级连接令牌密文"
#: authentication/models/connection_token.py:274
#: authentication/models/connection_token.py:276
msgid "Super connection token"
msgstr "超级连接令牌"
@@ -3225,17 +3225,17 @@ msgstr "组件"
msgid "Expired now"
msgstr "立刻过期"
#: authentication/serializers/connect_token_secret.py:169
#: authentication/serializers/connect_token_secret.py:170
#: terminal/models/virtualapp/virtualapp.py:25
msgid "Image name"
msgstr "镜像名称"
#: authentication/serializers/connect_token_secret.py:170
#: authentication/serializers/connect_token_secret.py:171
#: terminal/models/virtualapp/virtualapp.py:27
msgid "Image port"
msgstr "镜像端口"
#: authentication/serializers/connect_token_secret.py:171
#: authentication/serializers/connect_token_secret.py:172
#: terminal/models/virtualapp/virtualapp.py:26
msgid "Image protocol"
msgstr "镜像协议"
@@ -3258,7 +3258,7 @@ msgstr "动作"
#: authentication/serializers/connection_token.py:42
#: perms/serializers/permission.py:40 perms/serializers/permission.py:60
#: users/serializers/user.py:100 users/serializers/user.py:177
#: users/serializers/user.py:101 users/serializers/user.py:178
msgid "Is expired"
msgstr "已过期"
@@ -3272,8 +3272,8 @@ msgid "Access IP"
msgstr "IP 白名单"
#: authentication/serializers/token.py:92 perms/serializers/permission.py:39
#: perms/serializers/permission.py:61 users/serializers/user.py:101
#: users/serializers/user.py:174
#: perms/serializers/permission.py:61 users/serializers/user.py:102
#: users/serializers/user.py:175
msgid "Is valid"
msgstr "是否有效"
@@ -3936,7 +3936,7 @@ msgstr "错误的数据类型,应该是列表"
msgid "Invalid choice: {}"
msgstr "无效选项: {}"
#: common/serializers/mixin.py:397 labels/apps.py:8
#: common/serializers/mixin.py:406 labels/apps.py:8
msgid "Labels"
msgstr "标签管理"
@@ -4067,15 +4067,15 @@ msgstr "系统信息"
msgid "Publish the station message"
msgstr "发布站内消息"
#: ops/ansible/inventory.py:106 ops/models/job.py:63
#: ops/ansible/inventory.py:107 ops/models/job.py:65
msgid "No account available"
msgstr "无可用账号"
#: ops/ansible/inventory.py:285
#: ops/ansible/inventory.py:286
msgid "Ansible disabled"
msgstr "Ansible 已禁用"
#: ops/ansible/inventory.py:301
#: ops/ansible/inventory.py:302
msgid "Skip hosts below:"
msgstr "跳过以下主机: "
@@ -4191,7 +4191,7 @@ msgstr "VCS"
msgid "Adhoc"
msgstr "命令"
#: ops/const.py:39 ops/models/job.py:146
#: ops/const.py:39 ops/models/job.py:149
msgid "Playbook"
msgstr "Playbook"
@@ -4280,11 +4280,11 @@ msgstr "需要周期或定期设置"
msgid "Pattern"
msgstr "模式"
#: ops/models/adhoc.py:23 ops/models/job.py:143
#: ops/models/adhoc.py:23 ops/models/job.py:146
msgid "Module"
msgstr "模块"
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:141
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:144
#: terminal/models/component/task.py:14
msgid "Args"
msgstr "参数"
@@ -4303,12 +4303,12 @@ msgstr "最后执行"
msgid "Date last run"
msgstr "最后运行日期"
#: ops/models/base.py:51 ops/models/job.py:234
#: ops/models/base.py:51 ops/models/job.py:237
#: xpack/plugins/cloud/models.py:198
msgid "Result"
msgstr "结果"
#: ops/models/base.py:52 ops/models/job.py:235
#: ops/models/base.py:52 ops/models/job.py:238
msgid "Summary"
msgstr "汇总"
@@ -4341,43 +4341,43 @@ msgstr "发布日期"
msgid "Celery Task Execution"
msgstr "Celery 任务执行"
#: ops/models/job.py:144
#: ops/models/job.py:147
msgid "Chdir"
msgstr "运行目录"
#: ops/models/job.py:145
#: ops/models/job.py:148
msgid "Timeout (Seconds)"
msgstr "超时时间 (秒)"
#: ops/models/job.py:150
#: ops/models/job.py:153
msgid "Use Parameter Define"
msgstr "使用参数定义"
#: ops/models/job.py:151
#: ops/models/job.py:154
msgid "Parameters define"
msgstr "参数定义"
#: ops/models/job.py:152
#: ops/models/job.py:155
msgid "Runas"
msgstr "运行用户"
#: ops/models/job.py:154
#: ops/models/job.py:157
msgid "Runas policy"
msgstr "用户策略"
#: ops/models/job.py:218
#: ops/models/job.py:221
msgid "Job"
msgstr "作业"
#: ops/models/job.py:241
#: ops/models/job.py:244
msgid "Material"
msgstr "Material"
#: ops/models/job.py:243
#: ops/models/job.py:246
msgid "Material Type"
msgstr "Material 类型"
#: ops/models/job.py:540
#: ops/models/job.py:544
msgid "Job Execution"
msgstr "作业执行"
@@ -5177,14 +5177,18 @@ msgstr "User DN 缓存超时时间 (秒)"
#: settings/serializers/auth/ldap.py:84
msgid ""
"Caching the User DN obtained during user login authentication can "
"effectivelyimprove the speed of user authentication., 0 means no cache"
msgstr "对用户登录认证时查询出的 User DN 进行缓存,可以有效提高用户认证的速度"
"effectivelyimprove the speed of user authentication., 0 means no cache<br>If "
"the user OU structure has been adjusted, click Submit to clear the user DN "
"cache"
msgstr ""
"对用户登录认证时查询出的 User DN 进行缓存,可以有效提高用户认证的速度<br>如果"
"用户 OU 架构有调整,点击提交即可清除用户 DN 缓存"
#: settings/serializers/auth/ldap.py:88
#: settings/serializers/auth/ldap.py:89
msgid "Search paged size (piece)"
msgstr "搜索分页数量 (条)"
#: settings/serializers/auth/ldap.py:93
#: settings/serializers/auth/ldap.py:94
msgid "Enable LDAP auth"
msgstr "启用 LDAP 认证"
@@ -7985,7 +7989,7 @@ msgstr "强制启用"
msgid "Lark"
msgstr ""
#: users/models/user.py:826 users/serializers/user.py:175
#: users/models/user.py:826 users/serializers/user.py:176
msgid "Is service account"
msgstr "服务账号"
@@ -7997,7 +8001,7 @@ msgstr "头像"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:834 users/serializers/user.py:111
#: users/models/user.py:834 users/serializers/user.py:112
msgid "Phone"
msgstr "手机"
@@ -8008,7 +8012,7 @@ msgstr "OTP 密钥"
# msgid "Private key"
# msgstr "ssh私钥"
#: users/models/user.py:852 users/serializers/profile.py:128
#: users/serializers/user.py:172
#: users/serializers/user.py:173
msgid "Is first login"
msgstr "首次登录"
@@ -8178,71 +8182,71 @@ msgstr "密码不满足安全规则"
msgid "The new password cannot be the last {} passwords"
msgstr "新密码不能是最近 {} 次的密码"
#: users/serializers/user.py:44
#: users/serializers/user.py:45
msgid "System roles"
msgstr "系统角色"
#: users/serializers/user.py:48
#: users/serializers/user.py:49
msgid "Org roles"
msgstr "组织角色"
#: users/serializers/user.py:51
#: users/serializers/user.py:52
msgid "Organizations and roles"
msgstr "组织和角色"
#: users/serializers/user.py:93
#: users/serializers/user.py:94
msgid "Password strategy"
msgstr "密码策略"
#: users/serializers/user.py:95
#: users/serializers/user.py:96
msgid "MFA enabled"
msgstr "MFA 已启用"
#: users/serializers/user.py:97
#: users/serializers/user.py:98
msgid "MFA force enabled"
msgstr "强制 MFA"
#: users/serializers/user.py:99
#: users/serializers/user.py:100
msgid "Login blocked"
msgstr "登录被锁定"
#: users/serializers/user.py:102 users/serializers/user.py:181
#: users/serializers/user.py:103 users/serializers/user.py:182
msgid "Is OTP bound"
msgstr "是否绑定了虚拟 MFA"
#: users/serializers/user.py:103
#: users/serializers/user.py:104
msgid "Super Administrator"
msgstr "超级管理员"
#: users/serializers/user.py:104
#: users/serializers/user.py:105
msgid "Organization Administrator"
msgstr "组织管理员"
#: users/serializers/user.py:106
#: users/serializers/user.py:107
msgid "Can public key authentication"
msgstr "可以使用公钥认证"
#: users/serializers/user.py:176
#: users/serializers/user.py:177
msgid "Is org admin"
msgstr "组织管理员"
#: users/serializers/user.py:178
#: users/serializers/user.py:179
msgid "Avatar url"
msgstr "头像路径"
#: users/serializers/user.py:182
#: users/serializers/user.py:183
msgid "MFA level"
msgstr "MFA 级别"
#: users/serializers/user.py:304
#: users/serializers/user.py:305
msgid "Select users"
msgstr "选择用户"
#: users/serializers/user.py:305
#: users/serializers/user.py:306
msgid "For security, only list several users"
msgstr "为了安全,仅列出几个用户"
#: users/serializers/user.py:338
#: users/serializers/user.py:339
msgid "name not unique"
msgstr "名称重复"

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5d73b2f2320bf2be7b4223de27c095e6b69de1b85d21722a0589709bd5315620
size 145170
oid sha256:93fed3f6a027f645520852f40c5f7722a5be579a3a8bb7b7029e3d0bc9794056
size 145341

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-18 17:25+0800\n"
"POT-Creation-Date: 2024-05-08 14:11+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"
@@ -37,7 +37,7 @@ msgstr "成功: %s, 失敗: %s, 總數: %s"
#: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47
#: settings/serializers/msg.py:35 terminal/serializers/storage.py:123
#: terminal/serializers/storage.py:142 users/forms/profile.py:21
#: users/serializers/user.py:109
#: users/serializers/user.py:110
#: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_verify.html:18
#: xpack/plugins/cloud/serializers/account_attrs.py:28
@@ -385,7 +385,7 @@ msgstr "帳號備份計劃"
#: accounts/models/automations/backup_account.py:119
#: assets/models/automations/base.py:115 audits/models.py:65
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:238
#: ops/models/base.py:55 ops/models/celery.py:88 ops/models/job.py:241
#: ops/templates/ops/celery_task_log.html:75
#: perms/models/asset_permission.py:78
#: settings/templates/ldap/_msg_import_ldap_user.html:5
@@ -474,7 +474,7 @@ msgstr "SSH 金鑰推送方式"
#: accounts/models/automations/gather_account.py:58
#: accounts/serializers/account/backup.py:41
#: accounts/serializers/automations/change_secret.py:58
#: settings/serializers/auth/ldap.py:90
#: settings/serializers/auth/ldap.py:91
msgid "Recipient"
msgstr "收件人"
@@ -496,14 +496,14 @@ msgstr "開始日期"
#: accounts/models/automations/change_secret.py:42
#: assets/models/automations/base.py:116 ops/models/base.py:56
#: ops/models/celery.py:89 ops/models/job.py:239
#: ops/models/celery.py:89 ops/models/job.py:242
#: terminal/models/applet/host.py:142
msgid "Date finished"
msgstr "結束日期"
#: accounts/models/automations/change_secret.py:44
#: assets/models/automations/base.py:113 audits/models.py:208
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:230
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:233
#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30
#: terminal/models/virtualapp/virtualapp.py:99
@@ -625,14 +625,14 @@ msgstr "密碼規則"
#: assets/models/_user.py:22 assets/models/asset/common.py:93
#: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21
#: assets/models/domain.py:19 assets/models/group.py:17
#: assets/models/label.py:18 assets/models/platform.py:16
#: assets/models/platform.py:95 assets/serializers/asset/common.py:149
#: assets/models/label.py:18 assets/models/platform.py:15
#: assets/models/platform.py:94 assets/serializers/asset/common.py:149
#: assets/serializers/platform.py:118 assets/serializers/platform.py:228
#: authentication/backends/passkey/models.py:10
#: authentication/serializers/connect_token_secret.py:113
#: authentication/serializers/connect_token_secret.py:168 labels/models.py:11
#: authentication/serializers/connect_token_secret.py:169 labels/models.py:11
#: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15
#: ops/models/celery.py:80 ops/models/job.py:139 ops/models/playbook.py:28
#: ops/models/celery.py:80 ops/models/job.py:142 ops/models/playbook.py:28
#: ops/serializers/job.py:18 orgs/models.py:82
#: perms/models/asset_permission.py:61 rbac/models/role.py:29
#: settings/models.py:33 settings/models.py:181 settings/serializers/msg.py:89
@@ -659,7 +659,7 @@ msgstr "特權帳號"
#: authentication/serializers/connect_token_secret.py:117
#: terminal/models/applet/applet.py:40
#: terminal/models/component/endpoint.py:120
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:173
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:174
msgid "Is active"
msgstr "啟用"
@@ -774,7 +774,7 @@ msgid "Exist policy"
msgstr "帳號存在策略"
#: accounts/serializers/account/account.py:193 applications/models.py:11
#: assets/models/label.py:21 assets/models/platform.py:96
#: assets/models/label.py:21 assets/models/platform.py:95
#: assets/serializers/asset/common.py:125 assets/serializers/cagegory.py:12
#: assets/serializers/platform.py:140 assets/serializers/platform.py:229
#: perms/serializers/user_permission.py:26 settings/models.py:35
@@ -786,11 +786,11 @@ msgstr "類別"
#: accounts/serializers/automations/base.py:55 acls/models/command_acl.py:24
#: acls/serializers/command_acl.py:19 applications/models.py:14
#: assets/models/_user.py:50 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:97
#: assets/models/cmd_filter.py:74 assets/models/platform.py:96
#: assets/serializers/asset/common.py:126 assets/serializers/platform.py:120
#: assets/serializers/platform.py:139 audits/serializers.py:53
#: audits/serializers.py:170
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:147
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:150
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:39
#: terminal/models/component/storage.py:57
#: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29
@@ -826,7 +826,7 @@ msgstr "已修改"
#: assets/models/automations/base.py:19
#: assets/serializers/automations/base.py:20
#: authentication/api/connection_token.py:410 ops/models/base.py:17
#: ops/models/job.py:149 ops/serializers/job.py:19
#: ops/models/job.py:152 ops/serializers/job.py:19
#: terminal/templates/terminal/_msg_command_execute_alert.html:16
msgid "Assets"
msgstr "資產"
@@ -958,7 +958,7 @@ msgstr "關聯平台,可配置推送參數,如果不關聯,將使用默認
#: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27
#: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88
#: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26
#: ops/models/job.py:155 ops/models/playbook.py:31 rbac/models/role.py:37
#: ops/models/job.py:158 ops/models/playbook.py:31 rbac/models/role.py:37
#: settings/models.py:38 terminal/models/applet/applet.py:45
#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143
#: terminal/models/component/endpoint.py:25
@@ -1369,15 +1369,15 @@ msgstr "不能直接創建資產, 你應該創建主機或其他資產"
msgid "Number required"
msgstr "需要為數字"
#: assets/api/node.py:57
#: assets/api/node.py:58
msgid "You can't update the root node name"
msgstr "不能修改根節點名稱"
#: assets/api/node.py:64
#: assets/api/node.py:65
msgid "You can't delete the root node ({})"
msgstr "不能刪除根節點 ({})"
#: assets/api/node.py:67
#: assets/api/node.py:68
msgid "Deletion failed and the node contains assets"
msgstr "刪除失敗,節點包含資產"
@@ -1389,11 +1389,11 @@ msgstr "同級別節點名字不能重複"
msgid "App assets"
msgstr "資產管理"
#: assets/automations/base/manager.py:188
#: assets/automations/base/manager.py:191
msgid "{} disabled"
msgstr "{} 已禁用"
#: assets/automations/base/manager.py:251
#: assets/automations/base/manager.py:254
msgid " - Platform {} ansible disabled"
msgstr " - 平台 {} Ansible 已禁用, 無法執行任務"
@@ -1681,7 +1681,7 @@ msgstr "SSH公鑰"
#: assets/models/_user.py:28 assets/models/automations/base.py:114
#: assets/models/cmd_filter.py:41 assets/models/group.py:19
#: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54
#: ops/models/job.py:237 users/models/user.py:1058
#: ops/models/job.py:240 users/models/user.py:1058
msgid "Date created"
msgstr "創建日期"
@@ -1770,7 +1770,7 @@ msgstr "可以匹配系統用戶"
msgid "Cloud"
msgstr "雲服務"
#: assets/models/asset/common.py:94 assets/models/platform.py:17
#: assets/models/asset/common.py:94 assets/models/platform.py:16
#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72
#: settings/serializers/msg.py:32 terminal/serializers/storage.py:133
#: xpack/plugins/cloud/serializers/account_attrs.py:73
@@ -1781,7 +1781,7 @@ msgstr "埠"
msgid "Address"
msgstr "地址"
#: assets/models/asset/common.py:161 assets/models/platform.py:126
#: assets/models/asset/common.py:161 assets/models/platform.py:134
#: authentication/backends/passkey/models.py:12
#: authentication/serializers/connect_token_secret.py:118
#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:325
@@ -1850,7 +1850,7 @@ msgstr "忽略證書校驗"
msgid "Proxy"
msgstr "代理"
#: assets/models/automations/base.py:22 ops/models/job.py:233
#: assets/models/automations/base.py:22 ops/models/job.py:236
#: settings/serializers/auth/sms.py:103
msgid "Parameters"
msgstr "參數"
@@ -1921,7 +1921,7 @@ msgstr "網關"
msgid "Asset group"
msgstr "資產組"
#: assets/models/group.py:31 assets/models/platform.py:20
#: assets/models/group.py:31 assets/models/platform.py:19
#: assets/serializers/platform.py:121
#: xpack/plugins/cloud/providers/nutanix.py:30
msgid "Default"
@@ -1978,133 +1978,133 @@ msgstr "ssh私鑰"
msgid "Can match node"
msgstr "可以匹配節點"
#: assets/models/platform.py:18
#: assets/models/platform.py:17
msgid "Primary"
msgstr "主要的"
#: assets/models/platform.py:19
#: assets/models/platform.py:18
msgid "Required"
msgstr "必須的"
#: assets/models/platform.py:21
#: assets/models/platform.py:20
msgid "Public"
msgstr "開放的"
#: assets/models/platform.py:22 assets/serializers/platform.py:49
#: assets/models/platform.py:21 assets/serializers/platform.py:49
#: settings/serializers/settings.py:95
#: users/templates/users/reset_password.html:29
msgid "Setting"
msgstr "設置"
#: assets/models/platform.py:39 audits/const.py:56
#: assets/models/platform.py:38 audits/const.py:56
#: authentication/backends/passkey/models.py:11 settings/models.py:37
#: terminal/serializers/applet_host.py:33
msgid "Enabled"
msgstr "啟用"
#: assets/models/platform.py:40
#: assets/models/platform.py:39
msgid "Ansible config"
msgstr "Ansible 配置"
#: assets/models/platform.py:42 assets/serializers/platform.py:33
#: assets/models/platform.py:41 assets/serializers/platform.py:33
msgid "Ping enabled"
msgstr "啟用資產探活"
#: assets/models/platform.py:43 assets/serializers/platform.py:34
#: assets/models/platform.py:42 assets/serializers/platform.py:34
msgid "Ping method"
msgstr "資產探活方式"
#: assets/models/platform.py:44
#: assets/models/platform.py:43
msgid "Ping params"
msgstr "資產探活參數"
#: assets/models/platform.py:46 assets/models/platform.py:70
#: assets/models/platform.py:45 assets/models/platform.py:69
#: assets/serializers/platform.py:35
msgid "Gather facts enabled"
msgstr "啟用收集資產資訊"
#: assets/models/platform.py:48 assets/models/platform.py:72
#: assets/models/platform.py:47 assets/models/platform.py:71
#: assets/serializers/platform.py:36
msgid "Gather facts method"
msgstr "收集資訊方式"
#: assets/models/platform.py:50 assets/models/platform.py:74
#: assets/models/platform.py:49 assets/models/platform.py:73
msgid "Gather facts params"
msgstr "收集資訊參數"
#: assets/models/platform.py:52 assets/serializers/platform.py:39
#: assets/models/platform.py:51 assets/serializers/platform.py:39
msgid "Change secret enabled"
msgstr "啟用改密"
#: assets/models/platform.py:54 assets/serializers/platform.py:40
#: assets/models/platform.py:53 assets/serializers/platform.py:40
msgid "Change secret method"
msgstr "改密方式"
#: assets/models/platform.py:56
#: assets/models/platform.py:55
msgid "Change secret params"
msgstr "改密參數"
#: assets/models/platform.py:58 assets/serializers/platform.py:41
#: assets/models/platform.py:57 assets/serializers/platform.py:41
msgid "Push account enabled"
msgstr "啟用帳號推送"
#: assets/models/platform.py:60 assets/serializers/platform.py:42
#: assets/models/platform.py:59 assets/serializers/platform.py:42
msgid "Push account method"
msgstr "帳號推送方式"
#: assets/models/platform.py:62
#: assets/models/platform.py:61
msgid "Push account params"
msgstr "帳號推送參數"
#: assets/models/platform.py:64 assets/serializers/platform.py:37
#: assets/models/platform.py:63 assets/serializers/platform.py:37
msgid "Verify account enabled"
msgstr "開啟帳號驗證"
#: assets/models/platform.py:66 assets/serializers/platform.py:38
#: assets/models/platform.py:65 assets/serializers/platform.py:38
msgid "Verify account method"
msgstr "帳號驗證方式"
#: assets/models/platform.py:68
#: assets/models/platform.py:67
msgid "Verify account params"
msgstr "帳號驗證參數"
#: assets/models/platform.py:76
#: assets/models/platform.py:75
msgid "Remove account enabled"
msgstr "開啟帳號移除"
#: assets/models/platform.py:78
#: assets/models/platform.py:77
msgid "Remove account method"
msgstr "帳號移除方式"
#: assets/models/platform.py:80
#: assets/models/platform.py:79
msgid "Remove account params"
msgstr "帳號移除參數"
#: assets/models/platform.py:98 tickets/models/ticket/general.py:298
#: assets/models/platform.py:97 tickets/models/ticket/general.py:298
msgid "Meta"
msgstr "元數據"
#: assets/models/platform.py:99 labels/models.py:13
#: assets/models/platform.py:98 labels/models.py:13
msgid "Internal"
msgstr "內建"
#: assets/models/platform.py:103 assets/serializers/platform.py:138
#: assets/models/platform.py:102 assets/serializers/platform.py:138
msgid "Charset"
msgstr "編碼"
#: assets/models/platform.py:105 assets/serializers/platform.py:167
#: assets/models/platform.py:104 assets/serializers/platform.py:167
msgid "Domain enabled"
msgstr "啟用網域"
#: assets/models/platform.py:107 assets/serializers/platform.py:166
#: assets/models/platform.py:106 assets/serializers/platform.py:166
msgid "Su enabled"
msgstr "啟用帳號切換"
#: assets/models/platform.py:108 assets/serializers/platform.py:144
#: assets/models/platform.py:107 assets/serializers/platform.py:144
msgid "Su method"
msgstr "帳號切換方式"
#: assets/models/platform.py:109 assets/serializers/platform.py:147
#: assets/models/platform.py:108 assets/serializers/platform.py:147
msgid "Custom fields"
msgstr "自訂屬性"
@@ -2603,8 +2603,8 @@ msgid "Offline user session"
msgstr "下線用戶會話"
#: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:148
#: ops/models/job.py:236 ops/models/playbook.py:30
#: ops/models/base.py:53 ops/models/celery.py:86 ops/models/job.py:151
#: ops/models/job.py:239 ops/models/playbook.py:30
#: terminal/models/session/sharing.py:25
msgid "Creator"
msgstr "創建者"
@@ -2735,7 +2735,7 @@ msgstr "ACL 動作是拒絕: {}({})"
msgid "ACL action is review"
msgstr "ACL 動作是覆核"
#: authentication/api/mfa.py:59
#: authentication/api/mfa.py:62
msgid "Current user not support mfa type: {}"
msgstr "當前用戶不支持 MFA 類型: {}"
@@ -3179,11 +3179,11 @@ msgstr "沒有用戶或用戶失效"
msgid "No asset or inactive asset"
msgstr "沒有資產或資產未啟用"
#: authentication/models/connection_token.py:272
#: authentication/models/connection_token.py:274
msgid "Can view super connection token secret"
msgstr "可以查看超級連接令牌密文"
#: authentication/models/connection_token.py:274
#: authentication/models/connection_token.py:276
msgid "Super connection token"
msgstr "超級連接令牌"
@@ -3227,17 +3227,17 @@ msgstr "組件"
msgid "Expired now"
msgstr "立刻過期"
#: authentication/serializers/connect_token_secret.py:169
#: authentication/serializers/connect_token_secret.py:170
#: terminal/models/virtualapp/virtualapp.py:25
msgid "Image name"
msgstr "鏡像名稱"
#: authentication/serializers/connect_token_secret.py:170
#: authentication/serializers/connect_token_secret.py:171
#: terminal/models/virtualapp/virtualapp.py:27
msgid "Image port"
msgstr "鏡像埠"
#: authentication/serializers/connect_token_secret.py:171
#: authentication/serializers/connect_token_secret.py:172
#: terminal/models/virtualapp/virtualapp.py:26
msgid "Image protocol"
msgstr "鏡像協議"
@@ -3260,7 +3260,7 @@ msgstr "動作"
#: authentication/serializers/connection_token.py:42
#: perms/serializers/permission.py:40 perms/serializers/permission.py:60
#: users/serializers/user.py:100 users/serializers/user.py:177
#: users/serializers/user.py:101 users/serializers/user.py:178
msgid "Is expired"
msgstr "已過期"
@@ -3274,8 +3274,8 @@ msgid "Access IP"
msgstr "IP 白名單"
#: authentication/serializers/token.py:92 perms/serializers/permission.py:39
#: perms/serializers/permission.py:61 users/serializers/user.py:101
#: users/serializers/user.py:174
#: perms/serializers/permission.py:61 users/serializers/user.py:102
#: users/serializers/user.py:175
msgid "Is valid"
msgstr "是否有效"
@@ -3938,7 +3938,7 @@ msgstr "錯誤的數據類型,應該是列表"
msgid "Invalid choice: {}"
msgstr "無效選項: {}"
#: common/serializers/mixin.py:397 labels/apps.py:8
#: common/serializers/mixin.py:406 labels/apps.py:8
msgid "Labels"
msgstr "標籤管理"
@@ -4068,15 +4068,15 @@ msgstr "系統資訊"
msgid "Publish the station message"
msgstr "發布站內消息"
#: ops/ansible/inventory.py:106 ops/models/job.py:63
#: ops/ansible/inventory.py:107 ops/models/job.py:65
msgid "No account available"
msgstr "無可用帳號"
#: ops/ansible/inventory.py:285
#: ops/ansible/inventory.py:286
msgid "Ansible disabled"
msgstr "Ansible 已禁用"
#: ops/ansible/inventory.py:301
#: ops/ansible/inventory.py:302
msgid "Skip hosts below:"
msgstr "跳過以下主機: "
@@ -4192,7 +4192,7 @@ msgstr "VCS"
msgid "Adhoc"
msgstr "命令"
#: ops/const.py:39 ops/models/job.py:146
#: ops/const.py:39 ops/models/job.py:149
msgid "Playbook"
msgstr "Playbook"
@@ -4281,11 +4281,11 @@ msgstr "需要週期或定期設置"
msgid "Pattern"
msgstr "模式"
#: ops/models/adhoc.py:23 ops/models/job.py:143
#: ops/models/adhoc.py:23 ops/models/job.py:146
msgid "Module"
msgstr "模組"
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:141
#: ops/models/adhoc.py:24 ops/models/celery.py:81 ops/models/job.py:144
#: terminal/models/component/task.py:14
msgid "Args"
msgstr "參數"
@@ -4304,12 +4304,12 @@ msgstr "最後執行"
msgid "Date last run"
msgstr "最後運行日期"
#: ops/models/base.py:51 ops/models/job.py:234
#: ops/models/base.py:51 ops/models/job.py:237
#: xpack/plugins/cloud/models.py:198
msgid "Result"
msgstr "結果"
#: ops/models/base.py:52 ops/models/job.py:235
#: ops/models/base.py:52 ops/models/job.py:238
msgid "Summary"
msgstr "匯總"
@@ -4342,43 +4342,43 @@ msgstr "發布日期"
msgid "Celery Task Execution"
msgstr "Celery 任務執行"
#: ops/models/job.py:144
#: ops/models/job.py:147
msgid "Chdir"
msgstr "運行目錄"
#: ops/models/job.py:145
#: ops/models/job.py:148
msgid "Timeout (Seconds)"
msgstr "超時時間 (秒)"
#: ops/models/job.py:150
#: ops/models/job.py:153
msgid "Use Parameter Define"
msgstr "使用參數定義"
#: ops/models/job.py:151
#: ops/models/job.py:154
msgid "Parameters define"
msgstr "參數定義"
#: ops/models/job.py:152
#: ops/models/job.py:155
msgid "Runas"
msgstr "運行用戶"
#: ops/models/job.py:154
#: ops/models/job.py:157
msgid "Runas policy"
msgstr "用戶策略"
#: ops/models/job.py:218
#: ops/models/job.py:221
msgid "Job"
msgstr "作業"
#: ops/models/job.py:241
#: ops/models/job.py:244
msgid "Material"
msgstr "Material"
#: ops/models/job.py:243
#: ops/models/job.py:246
msgid "Material Type"
msgstr "Material 類型"
#: ops/models/job.py:540
#: ops/models/job.py:544
msgid "Job Execution"
msgstr "作業執行"
@@ -5178,15 +5178,18 @@ msgstr "快取逾時時間 (秒)"
#: settings/serializers/auth/ldap.py:84
msgid ""
"Caching the User DN obtained during user login authentication can "
"effectivelyimprove the speed of user authentication., 0 means no cache"
"effectivelyimprove the speed of user authentication., 0 means no cache<br>If "
"the user OU structure has been adjusted, click Submit to clear the user DN "
"cache"
msgstr ""
"對於使用者登錄認證時查詢的使用者 DN 進行快取,可以有效提高使用者認證的速度"
"<br>如果使用者 OU 架構已調整,請點擊提交以清除使用者 DN 快取"
#: settings/serializers/auth/ldap.py:88
#: settings/serializers/auth/ldap.py:89
msgid "Search paged size (piece)"
msgstr "搜索分頁數量 (條)"
#: settings/serializers/auth/ldap.py:93
#: settings/serializers/auth/ldap.py:94
msgid "Enable LDAP auth"
msgstr "啟用 LDAP 認證"
@@ -7987,7 +7990,7 @@ msgstr "強制啟用"
msgid "Lark"
msgstr ""
#: users/models/user.py:826 users/serializers/user.py:175
#: users/models/user.py:826 users/serializers/user.py:176
msgid "Is service account"
msgstr "服務帳號"
@@ -7999,7 +8002,7 @@ msgstr "頭像"
msgid "Wechat"
msgstr "微信"
#: users/models/user.py:834 users/serializers/user.py:111
#: users/models/user.py:834 users/serializers/user.py:112
msgid "Phone"
msgstr "手機"
@@ -8010,7 +8013,7 @@ msgstr "OTP 金鑰"
# msgid "Private key"
# msgstr "ssh私鑰"
#: users/models/user.py:852 users/serializers/profile.py:128
#: users/serializers/user.py:172
#: users/serializers/user.py:173
msgid "Is first login"
msgstr "首次登錄"
@@ -8180,71 +8183,71 @@ msgstr "密碼不滿足安全規則"
msgid "The new password cannot be the last {} passwords"
msgstr "新密碼不能是最近 {} 次的密碼"
#: users/serializers/user.py:44
#: users/serializers/user.py:45
msgid "System roles"
msgstr "系統角色"
#: users/serializers/user.py:48
#: users/serializers/user.py:49
msgid "Org roles"
msgstr "組織角色"
#: users/serializers/user.py:51
#: users/serializers/user.py:52
msgid "Organizations and roles"
msgstr "組織和角色"
#: users/serializers/user.py:93
#: users/serializers/user.py:94
msgid "Password strategy"
msgstr "密碼策略"
#: users/serializers/user.py:95
#: users/serializers/user.py:96
msgid "MFA enabled"
msgstr "MFA 已啟用"
#: users/serializers/user.py:97
#: users/serializers/user.py:98
msgid "MFA force enabled"
msgstr "強制 MFA"
#: users/serializers/user.py:99
#: users/serializers/user.py:100
msgid "Login blocked"
msgstr "登錄被鎖定"
#: users/serializers/user.py:102 users/serializers/user.py:181
#: users/serializers/user.py:103 users/serializers/user.py:182
msgid "Is OTP bound"
msgstr "是否綁定了虛擬 MFA"
#: users/serializers/user.py:103
#: users/serializers/user.py:104
msgid "Super Administrator"
msgstr "超級管理員"
#: users/serializers/user.py:104
#: users/serializers/user.py:105
msgid "Organization Administrator"
msgstr "組織管理員"
#: users/serializers/user.py:106
#: users/serializers/user.py:107
msgid "Can public key authentication"
msgstr "可以使用公鑰認證"
#: users/serializers/user.py:176
#: users/serializers/user.py:177
msgid "Is org admin"
msgstr "組織管理員"
#: users/serializers/user.py:178
#: users/serializers/user.py:179
msgid "Avatar url"
msgstr "頭像路徑"
#: users/serializers/user.py:182
#: users/serializers/user.py:183
msgid "MFA level"
msgstr "MFA 級別"
#: users/serializers/user.py:304
#: users/serializers/user.py:305
msgid "Select users"
msgstr "選擇用戶"
#: users/serializers/user.py:305
#: users/serializers/user.py:306
msgid "For security, only list several users"
msgstr "為了安全,僅列出幾個用戶"
#: users/serializers/user.py:338
#: users/serializers/user.py:339
msgid "name not unique"
msgstr "名稱重複"

View File

@@ -2,5 +2,7 @@
from .callback import *
from .inventory import *
from .runner import *
from .runners import *
from .exceptions import *
from .runner import *
from .interface import *

View File

@@ -35,10 +35,10 @@ class DefaultCallback:
@property
def host_results(self):
results = {}
results = defaultdict(dict)
for state, hosts in self.result.items():
for host, items in hosts.items():
results[host] = items
results[host][state] = items
return results
def is_success(self):
@@ -165,4 +165,4 @@ class DefaultCallback:
def write_pid(self, pid):
pid_filepath = os.path.join(self.private_data_dir, 'local.pid')
with open(pid_filepath, 'w') as f:
f.write(str(pid))
f.write(str(pid))

View File

@@ -4,6 +4,20 @@ from functools import wraps
from settings.api import settings
__all__ = ["WorkPostRunCleaner", "cleanup_post_run"]
class WorkPostRunCleaner:
@property
def clean_dir(self):
raise NotImplemented
def clean_post_run(self):
if settings.DEBUG_DEV:
return
if self.clean_dir and os.path.exists(self.clean_dir):
shutil.rmtree(self.clean_dir)
def cleanup_post_run(func):
def get_instance(*args):
@@ -22,15 +36,3 @@ def cleanup_post_run(func):
instance.clean_post_run()
return wrapper
class WorkPostRunCleaner:
@property
def clean_dir(self):
raise NotImplemented
def clean_post_run(self):
if settings.DEBUG_DEV:
return
if self.clean_dir and os.path.exists(self.clean_dir):
shutil.rmtree(self.clean_dir)

View File

@@ -0,0 +1,5 @@
__all__ = ['CommandInBlackListException']
class CommandInBlackListException(Exception):
pass

View File

@@ -0,0 +1,46 @@
from django.conf import settings
from django.utils.functional import LazyObject
from ops.ansible import AnsibleReceptorRunner, AnsibleNativeRunner
from ops.ansible.runners.base import BaseRunner
__all__ = ['interface']
class _LazyRunnerInterface(LazyObject):
def _setup(self):
self._wrapped = self.make_interface()
@staticmethod
def make_interface():
runner_type = AnsibleReceptorRunner \
if settings.RECEPTOR_ENABLED else AnsibleNativeRunner
gateway_host = settings.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST \
if settings.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST else '127.0.0.1'
return RunnerInterface(runner_type=runner_type, gateway_proxy_host=gateway_host)
interface = _LazyRunnerInterface()
class RunnerInterface:
def __init__(self, runner_type, gateway_proxy_host='127.0.0.1'):
if not issubclass(runner_type, BaseRunner):
raise TypeError(f'{runner_type} can not cast to {BaseRunner}')
self._runner_type = runner_type
self._gateway_proxy_host = gateway_proxy_host
def get_gateway_proxy_host(self):
return self._gateway_proxy_host
def get_runner_type(self):
return self._runner_type
def kill_process(self, pid):
return self._runner_type.kill_precess(pid)
def run(self, **kwargs):
runner_type = self.get_runner_type()
runner = runner_type(**kwargs)
return runner.run()

View File

@@ -5,6 +5,7 @@ import re
from collections import defaultdict
from django.utils.translation import gettext as _
from assets.const.category import Category
__all__ = ['JMSInventory']
@@ -124,9 +125,9 @@ class JMSInventory:
else:
host.update(self.make_account_ansible_vars(account, path_dir))
if platform.name == 'Huawei':
if platform.is_huawei():
host['ansible_connection'] = 'network_cli'
host['ansible_network_os'] = 'asa'
host['ansible_network_os'] = 'ce'
if gateway:
ansible_connection = host.get('ansible_connection', 'ssh')

View File

@@ -1,147 +0,0 @@
import concurrent.futures
import os
import queue
import socket
from django.conf import settings
import ansible_runner
from receptorctl import ReceptorControl
from ops.ansible.cleaner import WorkPostRunCleaner, cleanup_post_run
class ReceptorCtl:
@property
def ctl(self):
return ReceptorControl(settings.ANSIBLE_RECEPTOR_SOCK_PATH)
def cancel(self, unit_id):
return self.ctl.simple_command("work cancel {}".format(unit_id))
def nodes(self):
return self.ctl.simple_command("status").get("Advertisements", None)
def submit_work(self,
worktype,
payload,
node=None,
tlsclient=None,
ttl=None,
signwork=False,
params=None, ):
return self.ctl.submit_work(worktype, payload, node, tlsclient, ttl, signwork, params)
def get_work_results(self, unit_id, startpos=0, return_socket=False, return_sockfile=True):
return self.ctl.get_work_results(unit_id, startpos, return_socket, return_sockfile)
def kill_process(self, pid):
submit_result = self.submit_work(worktype="kill", node="primary", payload=str(pid))
unit_id = submit_result["unitid"]
result_socket, result_file = self.get_work_results(unit_id=unit_id, return_sockfile=True,
return_socket=True)
while not result_socket.close():
buf = result_file.read()
if not buf:
break
print(buf.decode('utf8'))
receptor_ctl = ReceptorCtl()
def run(**kwargs):
receptor_runner = AnsibleReceptorRunner(**kwargs)
return receptor_runner.run()
class AnsibleReceptorRunner(WorkPostRunCleaner):
def __init__(self, **kwargs):
self.runner_params = kwargs
self.unit_id = None
self.clean_workspace = kwargs.pop("clean_workspace", True)
def write_unit_id(self):
if not self.unit_id:
return
private_dir = self.runner_params.get("private_data_dir", "")
with open(os.path.join(private_dir, "local.unitid"), "w") as f:
f.write(self.unit_id)
f.flush()
@property
def clean_dir(self):
if not self.clean_workspace:
return None
return self.runner_params.get("private_data_dir", None)
@cleanup_post_run
def run(self):
input, output = socket.socketpair()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
transmitter_future = executor.submit(self.transmit, input)
result = receptor_ctl.submit_work(payload=output.makefile('rb'),
node='primary', worktype='ansible-runner')
input.close()
output.close()
self.unit_id = result['unitid']
self.write_unit_id()
transmitter_future.result()
result_file = receptor_ctl.get_work_results(self.unit_id, return_sockfile=True)
stdout_queue = queue.Queue()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
processor_future = executor.submit(self.processor, result_file, stdout_queue)
while not processor_future.done() or \
not stdout_queue.empty():
msg = stdout_queue.get()
if msg is None:
break
print(msg)
return processor_future.result()
def transmit(self, _socket):
try:
ansible_runner.run(
streamer='transmit',
_output=_socket.makefile('wb'),
**self.runner_params
)
finally:
_socket.shutdown(socket.SHUT_WR)
def processor(self, _result_file, stdout_queue):
try:
original_event_handler = self.runner_params.pop("event_handler", None)
original_status_handler = self.runner_params.pop("status_handler", None)
def event_handler(data, **kwargs):
stdout = data.get('stdout', '')
if stdout:
stdout_queue.put(stdout)
if original_event_handler:
original_event_handler(data, **kwargs)
def status_handler(data, **kwargs):
private_data_dir = self.runner_params.get("private_data_dir", None)
if private_data_dir:
data["private_data_dir"] = private_data_dir
if original_status_handler:
original_status_handler(data, **kwargs)
return ansible_runner.interface.run(
quite=True,
streamer='process',
_input=_result_file,
event_handler=event_handler,
status_handler=status_handler,
**self.runner_params,
)
finally:
stdout_queue.put(None)

View File

@@ -1,47 +1,31 @@
import logging
import os
import shutil
import uuid
import ansible_runner
from django.conf import settings
from django.utils._os import safe_join
from django.utils.functional import LazyObject
from .interface import interface
from .callback import DefaultCallback
from .receptor import receptor_runner
from .exception import CommandInBlackListException
from ..utils import get_ansible_log_verbosity
logger = logging.getLogger(__file__)
class CommandInBlackListException(Exception):
pass
class AnsibleWrappedRunner(LazyObject):
def _setup(self):
self._wrapped = self.get_runner()
@staticmethod
def get_runner():
if settings.ANSIBLE_RECEPTOR_ENABLE and settings.ANSIBLE_RECEPTOR_SOCK_PATH:
return receptor_runner
return ansible_runner
runner = AnsibleWrappedRunner()
__all__ = ['AdHocRunner', 'PlaybookRunner', 'SuperPlaybookRunner', 'UploadFileRunner']
class AdHocRunner:
cmd_modules_choices = ('shell', 'raw', 'command', 'script', 'win_shell')
need_local_connection_modules_choices = ("mysql", "postgresql", "sqlserver", "huawei")
def __init__(self, inventory, module, module_args='', pattern='*', project_dir='/tmp/', extra_vars={},
def __init__(self, inventory, job_module, module, module_args='', pattern='*', project_dir='/tmp/',
extra_vars=None,
dry_run=False, timeout=-1):
if extra_vars is None:
extra_vars = {}
self.id = uuid.uuid4()
self.inventory = inventory
self.pattern = pattern
self.module = module
self.job_module = job_module
self.module_args = module_args
self.project_dir = project_dir
self.cb = DefaultCallback()
@@ -49,8 +33,7 @@ class AdHocRunner:
self.extra_vars = extra_vars
self.dry_run = dry_run
self.timeout = timeout
# enable local connection
self.extra_vars.update({"LOCAL_CONNECTION_ENABLED": "1"})
self.envs = {}
def check_module(self):
if self.module not in self.cmd_modules_choices:
@@ -59,8 +42,13 @@ class AdHocRunner:
raise CommandInBlackListException(
"Command is rejected by black list: {}".format(self.module_args.split()[0]))
def set_local_connection(self):
if self.job_module in self.need_local_connection_modules_choices:
self.envs.update({"LOCAL_CONNECTION_ENABLED": "1"})
def run(self, verbosity=0, **kwargs):
self.check_module()
self.set_local_connection()
verbosity = get_ansible_log_verbosity(verbosity)
if not os.path.exists(self.project_dir):
@@ -69,9 +57,10 @@ class AdHocRunner:
if os.path.exists(private_env):
shutil.rmtree(private_env)
runner.run(
interface.run(
timeout=self.timeout if self.timeout > 0 else None,
extravars=self.extra_vars,
envvars=self.envs,
host_pattern=self.pattern,
private_data_dir=self.project_dir,
inventory=self.inventory,
@@ -112,7 +101,7 @@ class PlaybookRunner:
if os.path.exists(private_env):
shutil.rmtree(private_env)
runner.run(
interface.run(
private_data_dir=self.project_dir,
inventory=self.inventory,
playbook=self.playbook,
@@ -144,7 +133,7 @@ class UploadFileRunner:
def run(self, verbosity=0, **kwargs):
verbosity = get_ansible_log_verbosity(verbosity)
runner.run(
interface.run(
private_data_dir=self.project_dir,
host_pattern="*",
inventory=self.inventory,
@@ -160,11 +149,3 @@ class UploadFileRunner:
except OSError as e:
print(f"del upload tmp dir {self.src_paths} failed! {e}")
return self.cb
class CommandRunner(AdHocRunner):
def __init__(self, inventory, command, pattern='*', project_dir='/tmp/'):
super().__init__(inventory, 'shell', command, pattern, project_dir)
def run(self, verbosity=0, **kwargs):
return super().run(verbosity, **kwargs)

View File

@@ -0,0 +1,3 @@
from .base import *
from .native import *
from .receptor import *

View File

@@ -0,0 +1,42 @@
from ops.ansible.cleaner import WorkPostRunCleaner, cleanup_post_run
class BaseRunner(WorkPostRunCleaner):
def __init__(self, **kwargs):
self.runner_params = kwargs
self.clean_workspace = kwargs.pop("clean_workspace", True)
@classmethod
def kill_precess(cls, pid):
return NotImplementedError
@property
def clean_dir(self):
if not self.clean_workspace:
return None
return self.private_data_dir
@property
def private_data_dir(self):
return self.runner_params.get('private_data_dir', None)
def get_event_handler(self):
_event_handler = self.runner_params.pop("event_handler", None)
return _event_handler
def get_status_handler(self):
_status_handler = self.runner_params.pop("status_handler", None)
if not _status_handler:
return
def _handler(data, **kwargs):
if self.private_data_dir:
data["private_data_dir"] = self.private_data_dir
_status_handler(data, **kwargs)
return _handler
def run(self):
raise NotImplementedError()

View File

@@ -0,0 +1,24 @@
import ansible_runner
from libs.process.ssh import kill_ansible_ssh_process
from ops.ansible.cleaner import cleanup_post_run
from ops.ansible.runners.base import BaseRunner
__all__ = ['AnsibleNativeRunner']
class AnsibleNativeRunner(BaseRunner):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def kill_precess(cls, pid):
return kill_ansible_ssh_process(pid)
@cleanup_post_run
def run(self):
ansible_runner.run(
event_handler=self.get_event_handler(),
status_handler=self.get_status_handler(),
**self.runner_params,
)

View File

@@ -0,0 +1,100 @@
import concurrent.futures
import os
import queue
import socket
import ansible_runner
from ops.ansible.cleaner import cleanup_post_run
from ops.ansible.runners.receptorctl.receptorctl import ReceptorCtl
from ops.ansible.runners.base import BaseRunner
__all__ = ['AnsibleReceptorRunner']
receptor_ctl = ReceptorCtl()
class AnsibleReceptorRunner(BaseRunner):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.unit_id = None
self.stdout_queue = None
@classmethod
def kill_precess(cls, pid):
return receptor_ctl.kill_process(pid)
def write_unit_id(self):
if not self.unit_id:
return
private_dir = self.runner_params.get("private_data_dir", "")
with open(os.path.join(private_dir, "local.unitid"), "w") as f:
f.write(self.unit_id)
f.flush()
@cleanup_post_run
def run(self):
input, output = socket.socketpair()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
transmitter_future = executor.submit(self.transmit, input)
result = receptor_ctl.submit_work(payload=output.makefile('rb'),
node='primary', worktype='ansible-runner')
input.close()
output.close()
self.unit_id = result['unitid']
self.write_unit_id()
transmitter_future.result()
result_file = receptor_ctl.get_work_results(self.unit_id, return_sockfile=True)
self.stdout_queue = queue.Queue()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
processor_future = executor.submit(self.processor, result_file)
while not processor_future.done() or \
not self.stdout_queue.empty():
msg = self.stdout_queue.get()
if msg is None:
break
print(msg)
return processor_future.result()
def transmit(self, _socket):
try:
ansible_runner.run(
streamer='transmit',
_output=_socket.makefile('wb'),
**self.runner_params
)
finally:
_socket.shutdown(socket.SHUT_WR)
def get_event_handler(self):
_event_handler = super().get_event_handler()
def _handler(data, **kwargs):
stdout = data.get('stdout', '')
if stdout:
self.stdout_queue.put(stdout)
_event_handler(data, **kwargs)
return _handler
def processor(self, _result_file):
try:
return ansible_runner.interface.run(
quite=True,
streamer='process',
_input=_result_file,
event_handler=self.get_event_handler(),
status_handler=self.get_status_handler(),
**self.runner_params,
)
finally:
self.stdout_queue.put(None)

View File

@@ -0,0 +1,38 @@
from django.conf import settings
from receptorctl import ReceptorControl
class ReceptorCtl:
@property
def ctl(self):
return ReceptorControl("tcp://{}".format(settings.ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS))
def cancel(self, unit_id):
return self.ctl.simple_command("work cancel {}".format(unit_id))
def nodes(self):
return self.ctl.simple_command("status").get("Advertisements", None)
def submit_work(self,
worktype,
payload,
node=None,
tlsclient=None,
ttl=None,
signwork=False,
params=None, ):
return self.ctl.submit_work(worktype, payload, node, tlsclient, ttl, signwork, params)
def get_work_results(self, unit_id, startpos=0, return_socket=False, return_sockfile=True):
return self.ctl.get_work_results(unit_id, startpos, return_socket, return_sockfile)
def kill_process(self, pid):
submit_result = self.submit_work(worktype="kill", node="primary", payload=str(pid))
unit_id = submit_result["unitid"]
result_socket, result_file = self.get_work_results(unit_id=unit_id, return_sockfile=True,
return_socket=True)
while not result_socket.close():
buf = result_file.read()
if not buf:
break
print(buf.decode('utf8'))

View File

@@ -262,6 +262,8 @@ class CeleryTaskExecutionViewSet(CommonApiMixin, viewsets.ModelViewSet):
msg = _("Task {} not found").format(execution.name)
raise JMSException(code='task_not_found_error', detail=msg)
try:
execution.kwargs.pop('__current_lang', None)
execution.kwargs.pop('__current_org_id', None)
t = task.delay(*execution.args, **execution.kwargs)
except TypeError:
msg = _("Task {} args or kwargs error").format(execution.name)

View File

@@ -304,6 +304,6 @@ class UsernameHintsAPI(APIView):
.filter(username__icontains=query) \
.filter(asset__in=assets) \
.values('username') \
.annotate(total=Count('username', distinct=True)) \
.order_by('total', '-username')[:10]
.annotate(total=Count('username')) \
.order_by('-total', '-username')[:10]
return Response(data=top_accounts)

View File

@@ -22,8 +22,10 @@ from acls.models import CommandFilterACL
from assets.models import Asset
from assets.automations.base.manager import SSHTunnelManager
from common.db.encoder import ModelJSONFieldEncoder
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner
from ops.ansible.receptor import receptor_runner
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, UploadFileRunner
"""stop all ssh child processes of the given ansible process pid."""
from ops.ansible.exception import CommandInBlackListException
from ops.mixin import PeriodTaskModelMixin
from ops.variables import *
from ops.const import Types, RunasPolicies, JobStatus, JobModules
@@ -65,6 +67,7 @@ class JMSPermedInventory(JMSInventory):
protocol_supported_modules_mapping = {
'mysql': ['mysql'],
'mariadb': ['mysql'],
'postgresql': ['postgresql'],
'sqlserver': ['sqlserver'],
'ssh': ['shell', 'python', 'win_shell', 'raw', 'huawei'],
@@ -75,7 +78,7 @@ class JMSPermedInventory(JMSInventory):
host['error'] = "Module {} is not suitable for this asset".format(self.module)
return host
if protocol.name in ('mysql', 'postgresql', 'sqlserver'):
if protocol.name in ('mariadb', 'mysql', 'postgresql', 'sqlserver'):
host['login_host'] = asset.address
host['login_port'] = protocol.port
host['login_user'] = account.username
@@ -331,6 +334,7 @@ class JobExecution(JMSOrgBaseModel):
runner = AdHocRunner(
self.inventory_path,
self.job.module,
module,
timeout=self.current_job.timeout,
module_args=args,

View File

@@ -1,4 +1,3 @@
import ast
import json
import time
@@ -17,9 +16,9 @@ from common.signals import django_ready
from common.utils.connection import RedisPubSub
from jumpserver.utils import get_current_request
from orgs.utils import get_current_org_id, set_current_org
from .ansible.receptor.receptor_runner import receptor_ctl
from .celery import app
from .models import CeleryTaskExecution, CeleryTask, Job
from .ansible.runner import interface
logger = get_logger(__name__)
@@ -167,7 +166,7 @@ def subscribe_stop_job_execution(sender, **kwargs):
def on_stop(pid):
logger.info(f"Stop job execution {pid} start")
receptor_ctl.kill_process(pid)
interface.kill_process(pid)
job_execution_stop_pub_sub.subscribe(on_stop)

View File

@@ -144,10 +144,10 @@ def clean_job_execution_period():
del_res = JobExecution.objects.filter(date_created__lt=expired_day).delete()
logger.info(f"clean job_execution db record success! delete {days} days {del_res[0]} records")
@shared_task
def longtime_add(x, y):
print('long time task begins')
time.sleep(50)
print('long time task finished')
return x + y
# 测试使用,注释隐藏
# @shared_task
# def longtime_add(x, y):
# print('long time task begins')
# time.sleep(50)
# print('long time task finished')
# return x + y

View File

@@ -87,6 +87,8 @@ class OrgResourceStatisticsRefreshUtil:
if not cache_field_name:
return
org = getattr(instance, 'org', None)
if not org:
return
cache_field_name = tuple(cache_field_name)
cls.refresh_org_fields.delay(org_fields=((org, cache_field_name),))

View File

@@ -16,7 +16,7 @@ def migrate_system_user_to_accounts(apps, schema_editor):
count += len(asset_permissions)
updated = []
for asset_permission in asset_permissions:
asset_permission.accounts = [s.username for s in asset_permission.system_users.all()]
asset_permission.accounts = [s.username for s in asset_permission.system_users.all() if s.username.strip()]
updated.append(asset_permission)
asset_permission_model.objects.bulk_update(updated, ['accounts'])

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
#
import re
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponse
from django.views.static import serve
from rest_framework import generics
@@ -168,6 +170,13 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
if hasattr(serializer, 'post_save'):
serializer.post_save()
self.send_signal(serializer)
if self.request.query_params.get('category') == 'ldap':
self.clean_ldap_user_dn_cache()
@staticmethod
def clean_ldap_user_dn_cache():
del_count = cache.delete_pattern('django_auth_ldap.user_dn.*')
logger.debug(f'clear LDAP user_dn_cache count={del_count}')
class SettingsLogoApi(APIView):

View File

@@ -82,7 +82,8 @@ class LDAPSettingSerializer(serializers.Serializer):
required=False, label=_('User DN cache timeout (s)'),
help_text=_(
'Caching the User DN obtained during user login authentication can effectively'
'improve the speed of user authentication., 0 means no cache'
'improve the speed of user authentication., 0 means no cache<br>'
'If the user OU structure has been adjusted, click Submit to clear the user DN cache'
)
)
AUTH_LDAP_SEARCH_PAGED_SIZE = serializers.IntegerField(required=False, label=_('Search paged size (piece)'))

View File

@@ -22,6 +22,7 @@ class PrivateSettingSerializer(PublicSettingSerializer):
AUTH_LDAP_SYNC_ORG_IDS = serializers.ListField()
SECURITY_MAX_IDLE_TIME = serializers.IntegerField()
SECURITY_VIEW_AUTH_NEED_MFA = serializers.BooleanField()
SECURITY_MFA_AUTH = serializers.IntegerField()
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField()
SECURITY_COMMAND_EXECUTION = serializers.BooleanField()
SECURITY_COMMAND_BLACKLIST = serializers.ListField()
@@ -41,6 +42,7 @@ class PrivateSettingSerializer(PublicSettingSerializer):
AUTH_DINGTALK = serializers.BooleanField()
AUTH_FEISHU = serializers.BooleanField()
AUTH_LARK = serializers.BooleanField()
AUTH_SLACK = serializers.BooleanField()
AUTH_TEMP_TOKEN = serializers.BooleanField()
TERMINAL_RAZOR_ENABLED = serializers.BooleanField()

View File

@@ -9,7 +9,7 @@ from common.utils import get_logger
from common.utils.timezone import local_now_display
from ops.celery.decorator import after_app_ready_start
from ops.celery.utils import (
create_or_update_celery_periodic_tasks, disable_celery_periodic_task
create_or_update_celery_periodic_tasks, delete_celery_periodic_task
)
from orgs.models import Organization
from settings.notifications import LDAPImportMessage
@@ -69,7 +69,7 @@ def import_ldap_user_periodic():
if not settings.AUTH_LDAP:
return
task_name = 'import_ldap_user_periodic'
disable_celery_periodic_task(task_name)
delete_celery_periodic_task(task_name)
if not settings.AUTH_LDAP_SYNC_IS_PERIODIC:
return
@@ -86,8 +86,7 @@ def import_ldap_user_periodic():
task_name: {
'task': import_ldap_user.name,
'interval': interval,
'crontab': crontab,
'enabled': True,
'crontab': crontab
}
}
create_or_update_celery_periodic_tasks(tasks)

View File

@@ -42,7 +42,10 @@
$.fn.select2.defaults.set('language', getUserLang())
const md = window.markdownit();
const markdownContent = document.querySelector('script[type="text/markdown"]').textContent;
document.getElementById('markdown-output').innerHTML = md.render(markdownContent);
const markdownRef = document.getElementById('markdown-output')
if (markdownRef) {
markdownRef.innerHTML = md.render(markdownContent);
}
});
</script>

View File

@@ -118,11 +118,18 @@
})
}
function onError (responseText, responseJson, status) {
setTimeout(function () {
toastr.error(responseJson.detail || responseJson.error);
});
};
requestApi({
url: url,
method: "POST",
body: JSON.stringify(data),
success: onSuccess,
error: onError,
flash_message: false
})
}

View File

@@ -1,24 +1,26 @@
# -*- coding: utf-8 -*-
#
import logging
from django.db.models import Q
from django.conf import settings
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework as filters
from rest_framework import generics
from rest_framework import status
from rest_framework.views import APIView, Response
from django_filters import rest_framework as filters
from common.drf.filters import BaseFilterSet
from common.api import JMSBulkModelViewSet
from common.drf.filters import BaseFilterSet
from common.exceptions import JMSException
from common.permissions import WithBootstrapToken
from common.permissions import WithBootstrapToken, IsServiceAccount
from jumpserver.conf import ConfigCrypto
from terminal import serializers
from terminal.models import Terminal
__all__ = [
'TerminalViewSet', 'TerminalConfig',
'TerminalRegistrationApi',
'TerminalRegistrationApi', 'EncryptedTerminalConfig'
]
logger = logging.getLogger(__file__)
@@ -89,3 +91,17 @@ class TerminalRegistrationApi(generics.CreateAPIView):
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
return super().create(request, *args, **kwargs)
class EncryptedTerminalConfig(generics.CreateAPIView):
serializer_class = serializers.EncryptedConfigSerializer
permission_classes = [IsServiceAccount]
http_method_names = ['post']
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
encrypt_key = serializer.validated_data['secret_encrypt_key']
encrypted_value = serializer.validated_data['encrypted_value']
config_crypto = ConfigCrypto(encrypt_key)
value = config_crypto.decrypt(encrypted_value)
return Response(data={'value': value}, status=200)

View File

@@ -247,8 +247,7 @@ class AppletApplication(BaseApplication):
if protocol_setting and protocol_setting.safe_mode:
# 加载 extensions
extension_paths = load_extensions()
for extension_path in extension_paths:
self._chrome_options.add_argument('--load-extension={}'.format(extension_path))
self._chrome_options.add_argument('--load-extension={}'.format(','.join(extension_paths)))
@wrapper_progress_bar
def run(self):

View File

@@ -253,8 +253,9 @@ class ConnectMethodUtil:
def _filter_disable_protocols_connect_methods(cls, methods):
# 过滤一些特殊的协议方式
if not getattr(settings, 'TERMINAL_KOKO_SSH_ENABLED'):
protocol = Protocol.ssh
methods[protocol] = [m for m in methods[protocol] if m['type'] != 'native']
disable_ssh_client_protocols = [Protocol.ssh, Protocol.sftp, Protocol.telnet]
for protocol in disable_ssh_client_protocols:
methods[protocol] = [m for m in methods[protocol] if m['type'] != 'native']
return methods
@classmethod

View File

@@ -96,9 +96,9 @@ class Terminal(StorageMixin, TerminalStatusMixin, JMSBaseModel):
@property
def is_active(self):
if self.user and self.user.is_active:
return True
return False
user_active = self.user and self.user.is_active
type_active = self.type in [TypeChoices.core, TypeChoices.celery]
return user_active or type_active
@is_active.setter
def is_active(self, active):

View File

@@ -70,7 +70,7 @@ class SessionCommandSerializerMixin(serializers.Serializer):
id = serializers.UUIDField(read_only=True)
# 限制 64 字符,不能直接迁移成 128 字符,命令表数据量会比较大
account = serializers.CharField(label=_("Account "))
output = serializers.CharField(max_length=2048, allow_blank=True, label=_("Output"))
output = serializers.CharField(allow_blank=True, label=_("Output"))
timestamp = serializers.IntegerField(label=_('Timestamp'))
timestamp_display = serializers.DateTimeField(read_only=True, label=_('Datetime'))
remote_addr = serializers.CharField(read_only=True, label=_('Remote Address'))

View File

@@ -147,3 +147,8 @@ class ConnectMethodSerializer(serializers.Serializer):
type = serializers.CharField(max_length=128)
endpoint_protocol = serializers.CharField(max_length=128)
component = serializers.CharField(max_length=128)
class EncryptedConfigSerializer(serializers.Serializer):
secret_encrypt_key = serializers.CharField(max_length=128)
encrypted_value = serializers.CharField(max_length=128)

View File

@@ -54,6 +54,7 @@ urlpatterns = [
# components
path('components/metrics/', api.ComponentsMetricsAPIView.as_view(), name='components-metrics'),
path('components/connect-methods/', api.ConnectMethodListApi.as_view(), name='connect-methods'),
path('encrypted-config/', api.EncryptedTerminalConfig.as_view(), name='encrypted-terminal-config'),
]
urlpatterns += router.urls

View File

@@ -3,6 +3,7 @@
from functools import partial
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@@ -123,11 +124,12 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
fields_write_only = [
"password", "public_key",
]
# xpack 包含的字段
fields_xpack = ["wecom_id", "dingtalk_id", "feishu_id", "lark_id", "slack_id"]
# small 指的是 不需要计算的直接能从一张表中获取到的数据
fields_small = fields_mini + fields_write_only + [
"email", "wechat", "phone", "mfa_level", "source",
"wecom_id", "dingtalk_id", "feishu_id", "lark_id",
"slack_id", "created_by", "updated_by", "comment", # 通用字段
*fields_xpack, "created_by", "updated_by", "comment", # 通用字段
]
fields_date = [
"date_expired", "date_joined", "last_login",
@@ -156,8 +158,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
read_only_fields = [
"date_joined", "last_login", "created_by",
"is_first_login", "wecom_id", "dingtalk_id",
"feishu_id", "lark_id", "date_api_key_last_used",
"is_first_login", *fields_xpack, "date_api_key_last_used",
]
fields_only_root_org = ["orgs_roles"]
disallow_self_update_fields = ["is_active", "system_roles", "org_roles"]
@@ -181,7 +182,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
"is_otp_secret_key_bound": {"label": _("Is OTP bound")},
'mfa_level': {'label': _("MFA level")},
}
def get_fields(self):
fields = super().get_fields()
self.pop_fields_if_need(fields)
@@ -192,7 +193,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
if not current_org.is_root():
for f in self.Meta.fields_only_root_org:
fields.pop(f, None)
def validate_password(self, password):
password_strategy = self.initial_data.get("password_strategy")
if self.instance is None and password_strategy != PasswordStrategy.custom:

274
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
[[package]]
name = "adal"
@@ -2836,8 +2836,14 @@ files = [
[package.dependencies]
google-auth = ">=2.14.1,<3.0.dev0"
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
grpcio = [
{version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""},
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
]
grpcio-status = [
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""},
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
]
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0"
requests = ">=2.18.0,<3.0.0.dev0"
@@ -3148,25 +3154,15 @@ reference = "tsinghua"
[[package]]
name = "httpcore"
version = "1.0.4"
description = "A minimal low-level HTTP client."
version = "1.0.5"
description = ""
optional = false
python-versions = ">=3.8"
python-versions = "*"
files = [
{file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
{file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
{file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
]
[package.dependencies]
certifi = "*"
h11 = ">=0.13,<0.15"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<0.25.0)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -3579,12 +3575,12 @@ reference = "tsinghua"
[[package]]
name = "jms-storage"
version = "0.0.57"
version = "0.0.58"
description = "Jumpserver storage python sdk tools"
optional = false
python-versions = "*"
files = [
{file = "jms-storage-0.0.57.tar.gz", hash = "sha256:765fdd0ebfa0965eb37d820b0bce2d26254126523c3f263eb7aab6a7d45253ff"},
{file = "jms-storage-0.0.58.tar.gz", hash = "sha256:9508864c5b65b2628291c39e3eeccedffd77448545a0359a4cb12c6435592e62"},
]
[package.dependencies]
@@ -3919,6 +3915,16 @@ files = [
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
@@ -4166,7 +4172,6 @@ files = [
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
{file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
]
@@ -4448,27 +4453,15 @@ reference = "tsinghua"
[[package]]
name = "openai"
version = "1.13.3"
description = "The official Python library for the openai API"
version = "1.29.0"
description = ""
optional = false
python-versions = ">=3.7.1"
python-versions = "*"
files = [
{file = "openai-1.13.3-py3-none-any.whl", hash = "sha256:5769b62abd02f350a8dd1a3a242d8972c947860654466171d60fb0972ae0a41c"},
{file = "openai-1.13.3.tar.gz", hash = "sha256:ff6c6b3bc7327e715e4b3592a923a5a1c7519ff5dd764a83d69f633d49e77a7b"},
{file = "openai-1.29.0-py3-none-any.whl", hash = "sha256:c61cd12376c84362d406341f9e2f9a9d6b81c082b133b44484dc0f43954496b1"},
{file = "openai-1.29.0.tar.gz", hash = "sha256:d5a769f485610cff8bae14343fa45a8b1d346be3d541fa5b28ccd040dbc8baf8"},
]
[package.dependencies]
anyio = ">=3.5.0,<5"
distro = ">=1.7.0,<2"
httpx = ">=0.23.0,<1"
pydantic = ">=1.9.0,<3"
sniffio = "*"
tqdm = ">4"
typing-extensions = ">=4.7,<5"
[package.extras]
datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -5363,23 +5356,15 @@ reference = "tsinghua"
[[package]]
name = "pydantic"
version = "2.6.3"
description = "Data validation using Python type hints"
version = "2.7.1"
description = ""
optional = false
python-versions = ">=3.8"
python-versions = "*"
files = [
{file = "pydantic-2.6.3-py3-none-any.whl", hash = "sha256:72c6034df47f46ccdf81869fddb81aade68056003900a8724a4f160700016a2a"},
{file = "pydantic-2.6.3.tar.gz", hash = "sha256:e07805c4c7f5c6826e33a1d4c9d47950d7eaf34868e2690f8594d2e30241f11f"},
{file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"},
{file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"},
]
[package.dependencies]
annotated-types = ">=0.4.0"
pydantic-core = "2.16.3"
typing-extensions = ">=4.6.1"
[package.extras]
email = ["email-validator (>=2.0.0)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -5387,95 +5372,92 @@ reference = "tsinghua"
[[package]]
name = "pydantic-core"
version = "2.16.3"
version = "2.18.2"
description = ""
optional = false
python-versions = ">=3.8"
python-versions = "*"
files = [
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"},
{file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"},
{file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"},
{file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"},
{file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"},
{file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"},
{file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"},
{file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"},
{file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"},
{file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"},
{file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"},
{file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"},
{file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"},
{file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"},
{file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"},
{file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"},
{file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"},
{file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"},
{file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"},
{file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"},
{file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"},
{file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"},
{file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"},
{file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"},
{file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"},
{file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"},
{file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"},
{file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"},
{file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"},
{file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"},
{file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"},
{file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"},
{file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"},
{file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"},
{file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"},
{file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"},
{file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"},
{file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"},
{file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"},
{file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"},
{file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"},
{file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"},
{file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"},
{file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"},
{file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"},
{file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"},
{file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"},
{file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"},
{file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"},
{file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"},
{file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"},
{file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"},
{file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"},
{file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"},
{file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"},
{file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"},
{file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"},
{file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"},
{file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"},
{file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"},
{file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
@@ -5809,9 +5791,11 @@ files = [
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:049f2e3de919e8e02504780a21ebbf235e21ca8ed5c7538c5b6e705aa6c43d8c"},
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd86d8e3e346e34f3f03d12e333747b53a1daa74374a727f4714d5b82ee0dd5"},
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:508226a0df7cb6faeda9f8e84e85743690ca427d7b27af9a73d75fcf0c1eef6e"},
{file = "pymssql-2.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:47859887adeaf184766b5e0bc845dd23611f3808f9521552063bb36eabc10092"},
{file = "pymssql-2.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d873e553374d5b1c57fe1c43bb75e3bcc2920678db1ef26f6bfed396c7d21b30"},
{file = "pymssql-2.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf31b8b76634c826a91f9999e15b7bfb0c051a0f53b319fd56481a67e5b903bb"},
{file = "pymssql-2.2.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:821945c2214fe666fd456c61e09a29a00e7719c9e136c801bffb3a254e9c579b"},
{file = "pymssql-2.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:cc85b609b4e60eac25fa38bbac1ff854fd2c2a276e0ca4a3614c6f97efb644bb"},
{file = "pymssql-2.2.8-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ebe7f64d5278d807f14bea08951e02512bfbc6219fd4d4f15bb45ded885cf3d4"},
{file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:253af3d39fc0235627966817262d5c4c94ad09dcbea59664748063470048c29c"},
{file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9d109df536dc5f7dd851a88d285a4c9cb12a9314b621625f4f5ab1197eb312"},
@@ -5827,11 +5811,13 @@ files = [
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3906993300650844ec140aa58772c0f5f3e9e9d5709c061334fd1551acdcf066"},
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7309c7352e4a87c9995c3183ebfe0ff4135e955bb759109637673c61c9f0ca8d"},
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b8d603cc1ec7ae585c5a409a1d45e8da067970c79dd550d45c238ae0aa0f79f"},
{file = "pymssql-2.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:293cb4d0339e221d877d6b19a1905082b658f0100a1e2ccc9dda10de58938901"},
{file = "pymssql-2.2.8-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:895041edd002a2e91d8a4faf0906b6fbfef29d9164bc6beb398421f5927fa40e"},
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6b2d9c6d38a416c6f2db36ff1cd8e69f9a5387a46f9f4f612623192e0c9404b1"},
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63d6f25cf40fe6a03c49be2d4d337858362b8ab944d6684c268e4990807cf0c"},
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c83ad3ad20951f3a94894b354fa5fa9666dcd5ebb4a635dad507c7d1dd545833"},
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3933f7f082be74698eea835df51798dab9bc727d94d3d280bffc75ab9265f890"},
{file = "pymssql-2.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:de313375b90b0f554058992f35c4a4beb3f6ec2f5912d8cd6afb649f95b03a9f"},
{file = "pymssql-2.2.8.tar.gz", hash = "sha256:9baefbfbd07d0142756e2dfcaa804154361ac5806ab9381350aad4e780c3033e"},
]
@@ -6320,6 +6306,7 @@ files = [
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
@@ -6327,8 +6314,15 @@ files = [
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
@@ -6345,6 +6339,7 @@ files = [
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
@@ -6352,6 +6347,7 @@ files = [
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
@@ -7065,13 +7061,13 @@ reference = "tsinghua"
[[package]]
name = "tqdm"
version = "4.66.2"
version = "4.66.4"
description = "Fast, Extensible Progress Meter"
optional = false
python-versions = ">=3.7"
files = [
{file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
{file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
{file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"},
{file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"},
]
[package.dependencies]
@@ -7909,4 +7905,4 @@ reference = "tsinghua"
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "2924fdefd09ae7c8656999f0d3212cb317fbfba2d603fdfbd11d0fdfb66b3523"
content-hash = "63da026ae0d9d5e6a62b25d265a50c95666e05f60cdae798c2e0443c93544e3a"

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "jumpserver"
version = "v3.10.8"
version = "v3.9"
description = "广受欢迎的开源堡垒机"
authors = ["ibuler <ibuler@qq.com>"]
license = "GPLv3"
@@ -47,7 +47,7 @@ pynacl = "1.5.0"
python-dateutil = "2.8.2"
pyyaml = "6.0.1"
requests = "2.31.0"
jms-storage = "0.0.57"
jms-storage = "^0.0.58"
simplejson = "3.19.1"
six = "1.16.0"
sshtunnel = "0.4.0"
@@ -144,12 +144,18 @@ ua-parser = "^0.18.0"
user-agents = "^2.2.0"
django-cors-headers = "^4.3.0"
mistune = "2.0.3"
openai = "^1.3.7"
openai = "^1.29.0"
xlsxwriter = "^3.1.9"
exchangelib = "^5.1.0"
xmlsec = "^1.3.13"
lxml = "4.9.3"
receptorctl = "^1.4.5"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
annotated-types = "^0.6.0"
httpx = "^0.27.0"
distro = "1.9.0"
tqdm = "4.66.4"
[tool.poetry.group.xpack.dependencies]

View File

@@ -9,8 +9,7 @@ import os
import signal
import tempfile
import psutil
from psutil import NoSuchProcess
from apps.libs.process.ssh import kill_ansible_ssh_process
ANSIBLE_RUNNER_COMMAND = "ansible-runner"
@@ -22,6 +21,8 @@ DEFAULT_SHARE_DIR = os.path.join(PROJECT_DIR, "data", "share")
DEFAULT_ANSIBLE_MODULES_DIR = os.path.join(APPS_DIR, "libs", "ansible", "modules")
DEFAULT_CONTROL_SOCK_PATH = os.path.join(DEFAULT_SHARE_DIR, "control.sock")
DEFAULT_TCP_LISTEN_ADDRESS = "0.0.0.0:7521"
logger = logging.getLogger(__name__)
os.chdir(APPS_DIR)
@@ -34,9 +35,10 @@ class ReceptorService:
'receptor',
'--local-only',
'--node', 'id=primary',
'--log-level', 'level=Error',
'--control-service',
'service=control',
'filename={}'.format(DEFAULT_CONTROL_SOCK_PATH),
'tcplisten={}'.format(DEFAULT_TCP_LISTEN_ADDRESS),
'--work-command',
'worktype={}'.format(ANSIBLE_RUNNER_COMMAND),
'command={}'.format(ANSIBLE_RUNNER_COMMAND),
@@ -49,6 +51,7 @@ class ReceptorService:
'allowruntimeparams=true'
]
@staticmethod
def before_start():
os.makedirs(os.path.join(DEFAULT_SHARE_DIR), exist_ok=True)
@@ -72,7 +75,6 @@ class ReceptorService:
print("\n- PID file is corrupted, starting Receptor...")
os.remove(self.pid_file)
os.environ.update({'LOCAL_CONNECTION_ENABLED': '1'})
os.environ.setdefault('ANSIBLE_LIBRARY', DEFAULT_ANSIBLE_MODULES_DIR)
os.environ.update({'PYTHONPATH': APPS_DIR})
process = subprocess.Popen(self.receptor_command)
@@ -141,29 +143,12 @@ def kill_progress_tree(pid=None):
try:
pid_input = input()
pid = int(pid_input)
logger.info("progress {} will be kill".format(pid))
kill_ansible_ssh_process(pid)
except Exception as e:
logger.error(e)
return
logger.info("progress {} will be kill".format(pid))
try:
current_process = psutil.Process(pid)
except NoSuchProcess as e:
logger.error(e)
return
children = current_process.children(recursive=True)
for child in children:
if child.pid == 1:
continue
if child.name() != 'ssh':
continue
try:
child.kill()
except Exception as e:
logger.error(e)
if __name__ == '__main__':
parser = argparse.ArgumentParser(