mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-16 17:12:53 +00:00
Compare commits
40 Commits
pr@dev@ter
...
v4.10.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
000bb100cd | ||
|
|
36f3071eed | ||
|
|
15259fc10c | ||
|
|
f31994fdcd | ||
|
|
71766418bb | ||
|
|
a9399dd709 | ||
|
|
d0cb9e5432 | ||
|
|
558188da90 | ||
|
|
ad5460dab8 | ||
|
|
4d37dca0de | ||
|
|
2ca4002624 | ||
|
|
053d640e4c | ||
|
|
f3acc28ded | ||
|
|
25987545db | ||
|
|
6720ecc6e0 | ||
|
|
0b3a7bb020 | ||
|
|
56373e362b | ||
|
|
02fc045370 | ||
|
|
e4ac73896f | ||
|
|
1518f792d6 | ||
|
|
67277dd622 | ||
|
|
82e7f020ea | ||
|
|
f20b9e01ab | ||
|
|
8cf8a3701b | ||
|
|
7ba24293d1 | ||
|
|
f10114c9ed | ||
|
|
cf31cbfb07 | ||
|
|
0edad24d5d | ||
|
|
1f1c1a9157 | ||
|
|
6c9d271ae1 | ||
|
|
6ff852e225 | ||
|
|
baa75dc735 | ||
|
|
8a9f0436b8 | ||
|
|
a9620a3cbe | ||
|
|
769e7dc8a0 | ||
|
|
2a70449411 | ||
|
|
8df720f19e | ||
|
|
dabbb45f6e | ||
|
|
ce24c1c3fd | ||
|
|
3c54c82ce9 |
@@ -1,4 +1,4 @@
|
||||
FROM jumpserver/core-base:20250827_025554 AS stage-build
|
||||
FROM jumpserver/core-base:20250819_064003 AS stage-build
|
||||
|
||||
ARG VERSION
|
||||
|
||||
@@ -33,7 +33,6 @@ ARG TOOLS=" \
|
||||
default-libmysqlclient-dev \
|
||||
openssh-client \
|
||||
sshpass \
|
||||
nmap \
|
||||
bubblewrap"
|
||||
|
||||
ARG APT_MIRROR=http://deb.debian.org
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<a name="readme-top"></a>
|
||||
<a href="https://jumpserver.com" target="_blank"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
|
||||
|
||||
## An open-source PAM platform (Bastion Host)
|
||||
## An open-source PAM tool (Bastion Host)
|
||||
|
||||
[![][license-shield]][license-link]
|
||||
[![][docs-shield]][docs-link]
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
## What is JumpServer?
|
||||
|
||||
JumpServer is an open-source Privileged Access Management (PAM) platform that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.
|
||||
JumpServer is an open-source Privileged Access Management (PAM) tool that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.
|
||||
|
||||
|
||||
<picture>
|
||||
|
||||
@@ -190,7 +190,6 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
|
||||
rbac_perms = {
|
||||
'GET': 'accounts.view_accountsecret',
|
||||
}
|
||||
queryset = Account.history.model.objects.none()
|
||||
|
||||
@lazyproperty
|
||||
def account(self) -> Account:
|
||||
|
||||
@@ -12,8 +12,6 @@ class VirtualAccountViewSet(OrgBulkModelViewSet):
|
||||
filterset_fields = ('alias',)
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, "swagger_fake_view", False):
|
||||
return VirtualAccount.objects.none()
|
||||
return VirtualAccount.get_or_init_queryset()
|
||||
|
||||
def get_object(self, ):
|
||||
|
||||
@@ -41,7 +41,6 @@ class AutomationAssetsListApi(generics.ListAPIView):
|
||||
|
||||
class AutomationRemoveAssetApi(generics.UpdateAPIView):
|
||||
model = BaseAutomation
|
||||
queryset = BaseAutomation.objects.all()
|
||||
serializer_class = serializers.UpdateAssetSerializer
|
||||
http_method_names = ['patch']
|
||||
|
||||
@@ -60,7 +59,6 @@ class AutomationRemoveAssetApi(generics.UpdateAPIView):
|
||||
|
||||
class AutomationAddAssetApi(generics.UpdateAPIView):
|
||||
model = BaseAutomation
|
||||
queryset = BaseAutomation.objects.all()
|
||||
serializer_class = serializers.UpdateAssetSerializer
|
||||
http_method_names = ['patch']
|
||||
|
||||
|
||||
@@ -154,10 +154,12 @@ class ChangSecretAddAssetApi(AutomationAddAssetApi):
|
||||
model = ChangeSecretAutomation
|
||||
serializer_class = serializers.ChangeSecretUpdateAssetSerializer
|
||||
|
||||
|
||||
class ChangSecretNodeAddRemoveApi(AutomationNodeAddRemoveApi):
|
||||
model = ChangeSecretAutomation
|
||||
serializer_class = serializers.ChangeSecretUpdateNodeSerializer
|
||||
|
||||
|
||||
class ChangeSecretStatusViewSet(OrgBulkModelViewSet):
|
||||
perm_model = ChangeSecretAutomation
|
||||
filterset_class = ChangeSecretStatusFilterSet
|
||||
|
||||
@@ -62,8 +62,7 @@ class ChangeSecretDashboardApi(APIView):
|
||||
status_counts = defaultdict(lambda: defaultdict(int))
|
||||
|
||||
for date_finished, status in results:
|
||||
dt_local = timezone.localtime(date_finished)
|
||||
date_str = str(dt_local.date())
|
||||
date_str = str(date_finished.date())
|
||||
if status == ChangeSecretRecordStatusChoice.failed:
|
||||
status_counts[date_str]['failed'] += 1
|
||||
elif status == ChangeSecretRecordStatusChoice.success:
|
||||
|
||||
@@ -150,9 +150,6 @@ class CheckAccountEngineViewSet(JMSModelViewSet):
|
||||
http_method_names = ['get', 'options']
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, "swagger_fake_view", False):
|
||||
return CheckAccountEngine.objects.none()
|
||||
|
||||
return CheckAccountEngine.get_default_engines()
|
||||
|
||||
def filter_queryset(self, queryset: list):
|
||||
|
||||
@@ -63,10 +63,12 @@ class PushAccountRemoveAssetApi(AutomationRemoveAssetApi):
|
||||
model = PushAccountAutomation
|
||||
serializer_class = serializers.PushAccountUpdateAssetSerializer
|
||||
|
||||
|
||||
class PushAccountAddAssetApi(AutomationAddAssetApi):
|
||||
model = PushAccountAutomation
|
||||
serializer_class = serializers.PushAccountUpdateAssetSerializer
|
||||
|
||||
|
||||
class PushAccountNodeAddRemoveApi(AutomationNodeAddRemoveApi):
|
||||
model = PushAccountAutomation
|
||||
serializer_class = serializers.PushAccountUpdateNodeSerializer
|
||||
@@ -113,18 +113,6 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager):
|
||||
if host.get('error'):
|
||||
return host
|
||||
|
||||
inventory_hosts = []
|
||||
if asset.type == HostTypes.WINDOWS:
|
||||
if self.secret_type == SecretType.SSH_KEY:
|
||||
host['error'] = _("Windows does not support SSH key authentication")
|
||||
return host
|
||||
|
||||
if self.secret_strategy == SecretStrategy.custom:
|
||||
new_secret = self.execution.snapshot['secret']
|
||||
if '>' in new_secret or '^' in new_secret:
|
||||
host['error'] = _("Windows password cannot contain special characters like > ^")
|
||||
return host
|
||||
|
||||
host['ssh_params'] = {}
|
||||
|
||||
accounts = self.get_accounts(account)
|
||||
@@ -142,6 +130,11 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager):
|
||||
if asset.type == HostTypes.WINDOWS:
|
||||
accounts = accounts.filter(secret_type=SecretType.PASSWORD)
|
||||
|
||||
inventory_hosts = []
|
||||
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
|
||||
print(f'Windows {asset} does not support ssh key push')
|
||||
return inventory_hosts
|
||||
|
||||
for account in accounts:
|
||||
h = deepcopy(host)
|
||||
h['name'] += '(' + account.username + ')' # To distinguish different accounts
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
|
||||
tasks:
|
||||
- name: Test SQLServer connection
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
register: db_info
|
||||
@@ -25,53 +23,45 @@
|
||||
var: info
|
||||
|
||||
- name: Check whether SQLServer User exist
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "SELECT 1 from sys.sql_logins WHERE name='{{ account.username }}';"
|
||||
when: db_info is succeeded
|
||||
register: user_exist
|
||||
|
||||
- name: Change SQLServer password
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "ALTER LOGIN {{ account.username }} WITH PASSWORD = '{{ account.secret }}', DEFAULT_DATABASE = {{ jms_asset.spec_info.db_name }}; select @@version"
|
||||
ignore_errors: true
|
||||
when: user_exist.query_results[0] | length != 0
|
||||
|
||||
- name: Add SQLServer user
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "CREATE LOGIN {{ account.username }} WITH PASSWORD = '{{ account.secret }}', DEFAULT_DATABASE = {{ jms_asset.spec_info.db_name }}; CREATE USER {{ account.username }} FOR LOGIN {{ account.username }}; select @@version"
|
||||
ignore_errors: true
|
||||
when: user_exist.query_results[0] | length == 0
|
||||
|
||||
- name: Verify password
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ account.username }}"
|
||||
login_password: "{{ account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
when: check_conn_after_change
|
||||
|
||||
@@ -9,7 +9,7 @@ from accounts.const import (
|
||||
AutomationTypes, SecretStrategy, ChangeSecretRecordStatusChoice
|
||||
)
|
||||
from accounts.models import ChangeSecretRecord
|
||||
from accounts.notifications import ChangeSecretExecutionTaskMsg
|
||||
from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretReportMsg
|
||||
from accounts.serializers import ChangeSecretRecordBackUpSerializer
|
||||
from common.utils import get_logger
|
||||
from common.utils.file import encrypt_and_compress_zip_file
|
||||
@@ -94,6 +94,10 @@ class ChangeSecretManager(BaseChangeSecretPushManager):
|
||||
if not recipients:
|
||||
return
|
||||
|
||||
context = self.get_report_context()
|
||||
for user in recipients:
|
||||
ChangeSecretReportMsg(user, context).publish()
|
||||
|
||||
if not records:
|
||||
return
|
||||
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
|
||||
tasks:
|
||||
- name: Test SQLServer connection
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT
|
||||
l.name,
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
|
||||
tasks:
|
||||
- name: Test SQLServer connection
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
register: db_info
|
||||
@@ -25,55 +23,47 @@
|
||||
var: info
|
||||
|
||||
- name: Check whether SQLServer User exist
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "SELECT 1 from sys.sql_logins WHERE name='{{ account.username }}';"
|
||||
when: db_info is succeeded
|
||||
register: user_exist
|
||||
|
||||
- name: Change SQLServer password
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "ALTER LOGIN {{ account.username }} WITH PASSWORD = '{{ account.secret }}', DEFAULT_DATABASE = {{ jms_asset.spec_info.db_name }}; select @@version"
|
||||
ignore_errors: true
|
||||
when: user_exist.query_results[0] | length != 0
|
||||
register: change_info
|
||||
|
||||
- name: Add SQLServer user
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "CREATE LOGIN [{{ account.username }}] WITH PASSWORD = '{{ account.secret }}'; CREATE USER [{{ account.username }}] FOR LOGIN [{{ account.username }}]; select @@version"
|
||||
ignore_errors: true
|
||||
when: user_exist.query_results[0] | length == 0
|
||||
register: change_info
|
||||
|
||||
- name: Verify password
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ account.username }}"
|
||||
login_password: "{{ account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
when: check_conn_after_change
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
|
||||
tasks:
|
||||
- name: "Remove account"
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: "{{ jms_asset.spec_info.db_name }}"
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: "DROP LOGIN {{ account.username }}; select @@version"
|
||||
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
|
||||
tasks:
|
||||
- name: Verify account
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ account.username }}"
|
||||
login_password: "{{ account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError
|
||||
from azure.identity import ClientSecretCredential
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
|
||||
from common.utils import get_logger
|
||||
|
||||
@@ -11,9 +14,6 @@ __all__ = ['AZUREVaultClient']
|
||||
class AZUREVaultClient(object):
|
||||
|
||||
def __init__(self, vault_url, tenant_id, client_id, client_secret):
|
||||
from azure.identity import ClientSecretCredential
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
|
||||
authentication_endpoint = 'https://login.microsoftonline.com/' \
|
||||
if ('azure.net' in vault_url) else 'https://login.chinacloudapi.cn/'
|
||||
|
||||
@@ -23,8 +23,6 @@ class AZUREVaultClient(object):
|
||||
self.client = SecretClient(vault_url=vault_url, credential=credentials)
|
||||
|
||||
def is_active(self):
|
||||
from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError
|
||||
|
||||
try:
|
||||
self.client.set_secret('jumpserver', '666')
|
||||
except (ResourceNotFoundError, ClientAuthenticationError) as e:
|
||||
@@ -34,8 +32,6 @@ class AZUREVaultClient(object):
|
||||
return True, ''
|
||||
|
||||
def get(self, name, version=None):
|
||||
from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError
|
||||
|
||||
try:
|
||||
secret = self.client.get_secret(name, version)
|
||||
return secret.value
|
||||
|
||||
@@ -132,7 +132,7 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
return self.asset.platform
|
||||
|
||||
@lazyproperty
|
||||
def alias(self) -> str:
|
||||
def alias(self):
|
||||
"""
|
||||
别称,因为有虚拟账号,@INPUT @MANUAL @USER, 否则为 id
|
||||
"""
|
||||
@@ -140,13 +140,13 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
return self.username
|
||||
return str(self.id)
|
||||
|
||||
def is_virtual(self) -> bool:
|
||||
def is_virtual(self):
|
||||
"""
|
||||
不要用 username 去判断,因为可能是构造的 account 对象,设置了同名账号的用户名,
|
||||
"""
|
||||
return self.alias.startswith('@')
|
||||
|
||||
def is_ds_account(self) -> bool:
|
||||
def is_ds_account(self):
|
||||
if self.is_virtual():
|
||||
return ''
|
||||
if not self.asset.is_directory_service:
|
||||
@@ -160,7 +160,7 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
return self.asset.ds
|
||||
|
||||
@lazyproperty
|
||||
def ds_domain(self) -> str:
|
||||
def ds_domain(self):
|
||||
"""这个不能去掉,perm_account 会动态设置这个值,以更改 full_username"""
|
||||
if self.is_virtual():
|
||||
return ''
|
||||
@@ -172,17 +172,17 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
|
||||
return '@' in self.username or '\\' in self.username
|
||||
|
||||
@property
|
||||
def full_username(self) -> str:
|
||||
def full_username(self):
|
||||
if not self.username_has_domain() and self.ds_domain:
|
||||
return '{}@{}'.format(self.username, self.ds_domain)
|
||||
return self.username
|
||||
|
||||
@lazyproperty
|
||||
def has_secret(self) -> bool:
|
||||
def has_secret(self):
|
||||
return bool(self.secret)
|
||||
|
||||
@lazyproperty
|
||||
def versions(self) -> int:
|
||||
def versions(self):
|
||||
return self.history.count()
|
||||
|
||||
def get_su_from_accounts(self):
|
||||
|
||||
@@ -33,7 +33,7 @@ class IntegrationApplication(JMSOrgBaseModel):
|
||||
return qs.filter(*query)
|
||||
|
||||
@property
|
||||
def accounts_amount(self) -> int:
|
||||
def accounts_amount(self):
|
||||
return self.get_accounts().count()
|
||||
|
||||
@property
|
||||
|
||||
@@ -75,11 +75,11 @@ class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
|
||||
return bool(self.secret)
|
||||
|
||||
@property
|
||||
def has_username(self) -> bool:
|
||||
def has_username(self):
|
||||
return bool(self.username)
|
||||
|
||||
@property
|
||||
def spec_info(self) -> dict:
|
||||
def spec_info(self):
|
||||
data = {}
|
||||
if self.secret_type != SecretType.SSH_KEY:
|
||||
return data
|
||||
@@ -87,13 +87,13 @@ class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
|
||||
return data
|
||||
|
||||
@property
|
||||
def password(self) -> str:
|
||||
def password(self):
|
||||
if self.secret_type == SecretType.PASSWORD:
|
||||
return self.secret
|
||||
return None
|
||||
|
||||
@property
|
||||
def private_key(self) -> str:
|
||||
def private_key(self):
|
||||
if self.secret_type == SecretType.SSH_KEY:
|
||||
return self.secret
|
||||
return None
|
||||
@@ -110,7 +110,7 @@ class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
|
||||
return None
|
||||
|
||||
@property
|
||||
def ssh_key_fingerprint(self) -> str:
|
||||
def ssh_key_fingerprint(self):
|
||||
if self.public_key:
|
||||
public_key = self.public_key
|
||||
elif self.private_key:
|
||||
|
||||
@@ -56,7 +56,7 @@ class VaultModelMixin(models.Model):
|
||||
__secret = None
|
||||
|
||||
@property
|
||||
def secret(self) -> str:
|
||||
def secret(self):
|
||||
if self.__secret:
|
||||
return self.__secret
|
||||
from accounts.backends import vault_client
|
||||
|
||||
@@ -18,11 +18,11 @@ class VirtualAccount(JMSOrgBaseModel):
|
||||
verbose_name = _('Virtual account')
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
def name(self):
|
||||
return self.get_alias_display()
|
||||
|
||||
@property
|
||||
def username(self) -> str:
|
||||
def username(self):
|
||||
usernames_map = {
|
||||
AliasAccount.INPUT: _("Manual input"),
|
||||
AliasAccount.USER: _("Same with user"),
|
||||
@@ -32,7 +32,7 @@ class VirtualAccount(JMSOrgBaseModel):
|
||||
return usernames_map.get(self.alias, '')
|
||||
|
||||
@property
|
||||
def comment(self) -> str:
|
||||
def comment(self):
|
||||
comments_map = {
|
||||
AliasAccount.INPUT: _('Non-asset account, Input username/password on connect'),
|
||||
AliasAccount.USER: _('The account username name same with user on connect'),
|
||||
|
||||
@@ -253,8 +253,6 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize
|
||||
'source_id': {'required': False, 'allow_null': True},
|
||||
}
|
||||
fields_unimport_template = ['params']
|
||||
# 手动判断唯一性校验
|
||||
validators = []
|
||||
|
||||
@classmethod
|
||||
def setup_eager_loading(cls, queryset):
|
||||
@@ -265,21 +263,6 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize
|
||||
)
|
||||
return queryset
|
||||
|
||||
def validate(self, attrs):
|
||||
instance = getattr(self, "instance", None)
|
||||
if instance:
|
||||
return super().validate(attrs)
|
||||
|
||||
field_errors = {}
|
||||
for _fields in Account._meta.unique_together:
|
||||
lookup = {field: attrs.get(field) for field in _fields}
|
||||
if Account.objects.filter(**lookup).exists():
|
||||
verbose_names = ', '.join([str(Account._meta.get_field(f).verbose_name) for f in _fields])
|
||||
msg_template = _('Account already exists. Field(s): {fields} must be unique.')
|
||||
field_errors[_fields[0]] = msg_template.format(fields=verbose_names)
|
||||
raise serializers.ValidationError(field_errors)
|
||||
return attrs
|
||||
|
||||
|
||||
class AccountDetailSerializer(AccountSerializer):
|
||||
has_secret = serializers.BooleanField(label=_("Has secret"), read_only=True)
|
||||
@@ -473,8 +456,6 @@ class AssetAccountBulkSerializer(
|
||||
|
||||
|
||||
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
|
||||
spec_info = serializers.DictField(label=_('Spec info'), read_only=True)
|
||||
|
||||
class Meta(AccountSerializer.Meta):
|
||||
fields = AccountSerializer.Meta.fields + ['spec_info']
|
||||
extra_kwargs = {
|
||||
@@ -489,7 +470,6 @@ class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
|
||||
|
||||
class AccountHistorySerializer(serializers.ModelSerializer):
|
||||
secret_type = LabeledChoiceField(choices=SecretType.choices, label=_('Secret type'))
|
||||
secret = serializers.CharField(label=_('Secret'), read_only=True)
|
||||
id = serializers.IntegerField(label=_('ID'), source='history_id', read_only=True)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -70,8 +70,6 @@ class AuthValidateMixin(serializers.Serializer):
|
||||
class BaseAccountSerializer(
|
||||
AuthValidateMixin, ResourceLabelsMixin, BulkOrgResourceModelSerializer
|
||||
):
|
||||
spec_info = serializers.DictField(label=_('Spec info'), read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = BaseAccount
|
||||
fields_mini = ["id", "name", "username"]
|
||||
|
||||
@@ -130,7 +130,7 @@ class ChangeSecretRecordSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = fields
|
||||
|
||||
@staticmethod
|
||||
def get_is_success(obj) -> bool:
|
||||
def get_is_success(obj):
|
||||
return obj.status == ChangeSecretRecordStatusChoice.success
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ class ChangeSecretRecordBackUpSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = fields
|
||||
|
||||
@staticmethod
|
||||
def get_asset(instance) -> str:
|
||||
def get_asset(instance):
|
||||
return str(instance.asset)
|
||||
|
||||
@staticmethod
|
||||
@@ -165,7 +165,7 @@ class ChangeSecretRecordBackUpSerializer(serializers.ModelSerializer):
|
||||
return str(instance.account)
|
||||
|
||||
@staticmethod
|
||||
def get_is_success(obj) -> str:
|
||||
def get_is_success(obj):
|
||||
if obj.status == ChangeSecretRecordStatusChoice.success.value:
|
||||
return _("Success")
|
||||
return _("Failed")
|
||||
@@ -196,9 +196,9 @@ class ChangeSecretAccountSerializer(serializers.ModelSerializer):
|
||||
read_only_fields = fields
|
||||
|
||||
@staticmethod
|
||||
def get_meta(obj) -> dict:
|
||||
def get_meta(obj):
|
||||
return account_secret_task_status.get(str(obj.id))
|
||||
|
||||
@staticmethod
|
||||
def get_ttl(obj) -> int:
|
||||
def get_ttl(obj):
|
||||
return account_secret_task_status.get_ttl(str(obj.id))
|
||||
|
||||
@@ -69,7 +69,7 @@ class AssetRiskSerializer(serializers.Serializer):
|
||||
risk_summary = serializers.SerializerMethodField()
|
||||
|
||||
@staticmethod
|
||||
def get_risk_summary(obj) -> dict:
|
||||
def get_risk_summary(obj):
|
||||
summary = {}
|
||||
for risk in RiskChoice.choices:
|
||||
summary[f"{risk[0]}_count"] = obj.get(f"{risk[0]}_count", 0)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{% load i18n %}
|
||||
|
||||
<h3>{% trans 'Task name' %}: {{ name }}</h3>
|
||||
<h3>{% trans 'Task execution id' %}: {{ execution_id }}</h3>
|
||||
<p>{% trans 'Respectful' %} {{ recipient }}</p>
|
||||
<p>{% trans 'Hello! The following is the failure of changing the password of your assets or pushing the account. Please check and handle it in time.' %}</p>
|
||||
<table style="width: 100%; border-collapse: collapse; max-width: 100%; text-align: left; margin-top: 20px;">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr style="background-color: #f2f2f2;">
|
||||
<th style="border: 1px solid #ddd; padding: 10px;">{% trans 'Asset' %}</th>
|
||||
<th style="border: 1px solid #ddd; padding: 10px;">{% trans 'Account' %}</th>
|
||||
<th style="border: 1px solid #ddd; padding: 10px;">{% trans 'Error' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asset_name, account_username, error in asset_account_errors %}
|
||||
<tr>
|
||||
<td style="border: 1px solid #ddd; padding: 10px;">{{ asset_name }}</td>
|
||||
<td style="border: 1px solid #ddd; padding: 10px;">{{ account_username }}</td>
|
||||
<td style="border: 1px solid #ddd; padding: 10px;">
|
||||
<div style="
|
||||
max-width: 90%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;"
|
||||
title="{{ error }}"
|
||||
>
|
||||
{{ error }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,4 +1,4 @@
|
||||
from common.serializers.mixin import CommonBulkModelSerializer
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from .base import BaseUserAssetAccountACLSerializer as BaseSerializer
|
||||
from ..const import ActionChoices
|
||||
from ..models import ConnectMethodACL
|
||||
@@ -6,15 +6,16 @@ from ..models import ConnectMethodACL
|
||||
__all__ = ["ConnectMethodACLSerializer"]
|
||||
|
||||
|
||||
class ConnectMethodACLSerializer(BaseSerializer, CommonBulkModelSerializer):
|
||||
class ConnectMethodACLSerializer(BaseSerializer, BulkOrgResourceModelSerializer):
|
||||
class Meta(BaseSerializer.Meta):
|
||||
model = ConnectMethodACL
|
||||
fields = [
|
||||
i for i in BaseSerializer.Meta.fields + ['connect_methods']
|
||||
if i not in ['assets', 'accounts', 'org_id']
|
||||
if i not in ['assets', 'accounts']
|
||||
]
|
||||
action_choices_exclude = BaseSerializer.Meta.action_choices_exclude + [
|
||||
ActionChoices.review,
|
||||
ActionChoices.accept,
|
||||
ActionChoices.notice,
|
||||
ActionChoices.face_verify,
|
||||
ActionChoices.face_online,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from common.serializers import CommonBulkModelSerializer
|
||||
from common.serializers import MethodSerializer
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from .base import BaseUserACLSerializer
|
||||
from .rules import RuleSerializer
|
||||
from ..const import ActionChoices
|
||||
@@ -12,12 +12,12 @@ __all__ = ["LoginACLSerializer"]
|
||||
common_help_text = _("With * indicating a match all. ")
|
||||
|
||||
|
||||
class LoginACLSerializer(BaseUserACLSerializer, CommonBulkModelSerializer):
|
||||
class LoginACLSerializer(BaseUserACLSerializer, BulkOrgResourceModelSerializer):
|
||||
rules = MethodSerializer(label=_('Rule'))
|
||||
|
||||
class Meta(BaseUserACLSerializer.Meta):
|
||||
model = LoginACL
|
||||
fields = list((set(BaseUserACLSerializer.Meta.fields) | {'rules'}) - {'org_id'})
|
||||
fields = BaseUserACLSerializer.Meta.fields + ['rules', ]
|
||||
action_choices_exclude = [
|
||||
ActionChoices.warning,
|
||||
ActionChoices.notify_and_warn,
|
||||
|
||||
@@ -16,7 +16,6 @@ class CategoryViewSet(ListModelMixin, JMSGenericViewSet):
|
||||
'types': TypeSerializer,
|
||||
}
|
||||
permission_classes = (IsValidUser,)
|
||||
default_limit = None
|
||||
|
||||
def get_queryset(self):
|
||||
return AllTypes.categories()
|
||||
|
||||
@@ -14,7 +14,6 @@ class FavoriteAssetViewSet(BulkModelViewSet):
|
||||
serializer_class = FavoriteAssetSerializer
|
||||
permission_classes = (IsValidUser,)
|
||||
filterset_fields = ['asset']
|
||||
default_limit = None
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
with tmp_to_root_org():
|
||||
|
||||
@@ -7,7 +7,7 @@ from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from assets.const import AllTypes
|
||||
from assets.models import Platform, Node, Asset, PlatformProtocol, PlatformAutomation
|
||||
from assets.models import Platform, Node, Asset, PlatformProtocol
|
||||
from assets.serializers import PlatformSerializer, PlatformProtocolSerializer, PlatformListSerializer
|
||||
from common.api import JMSModelViewSet
|
||||
from common.permissions import IsValidUser
|
||||
@@ -43,7 +43,6 @@ class AssetPlatformViewSet(JMSModelViewSet):
|
||||
'ops_methods': 'assets.view_platform',
|
||||
'filter_nodes_assets': 'assets.view_platform',
|
||||
}
|
||||
default_limit = None
|
||||
|
||||
def get_queryset(self):
|
||||
# 因为没有走分页逻辑,所以需要这里 prefetch
|
||||
@@ -113,7 +112,6 @@ class PlatformProtocolViewSet(JMSModelViewSet):
|
||||
|
||||
class PlatformAutomationMethodsApi(generics.ListAPIView):
|
||||
permission_classes = (IsValidUser,)
|
||||
queryset = PlatformAutomation.objects.none()
|
||||
|
||||
@staticmethod
|
||||
def automation_methods():
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from assets import serializers
|
||||
from assets.const import Protocol
|
||||
from common.permissions import IsValidUser
|
||||
from assets.models import Protocol
|
||||
|
||||
__all__ = ['ProtocolListApi']
|
||||
|
||||
|
||||
@@ -161,7 +161,6 @@ class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
|
||||
'GET': 'assets.view_asset',
|
||||
'list': 'assets.view_asset',
|
||||
}
|
||||
queryset = Node.objects.none()
|
||||
|
||||
def get_assets(self):
|
||||
key = self.request.query_params.get('key')
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
|
||||
tasks:
|
||||
- name: Test SQLServer connection
|
||||
mssql_script:
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ jms_account.username }}"
|
||||
login_password: "{{ jms_account.secret }}"
|
||||
login_host: "{{ jms_asset.address }}"
|
||||
login_port: "{{ jms_asset.port }}"
|
||||
name: '{{ jms_asset.spec_info.db_name }}'
|
||||
encryption: "{{ jms_asset.encryption | default(None) }}"
|
||||
tds_version: "{{ jms_asset.tds_version | default(None) }}"
|
||||
script: |
|
||||
SELECT @@version
|
||||
|
||||
@@ -250,12 +250,6 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
||||
'default': False,
|
||||
'label': _('Auth username')
|
||||
},
|
||||
'enable_cluster_mode': {
|
||||
'type': 'bool',
|
||||
'default': False,
|
||||
'label': _('Enable cluster mode'),
|
||||
'help_text': _('Enable if this Redis instance is part of a cluster')
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class Protocol(models.Model):
|
||||
return protocols[0] if len(protocols) > 0 else {}
|
||||
|
||||
@property
|
||||
def setting(self) -> dict:
|
||||
def setting(self):
|
||||
if self._setting is not None:
|
||||
return self._setting
|
||||
return self.asset_platform_protocol.get('setting', {})
|
||||
@@ -122,7 +122,7 @@ class Protocol(models.Model):
|
||||
self._setting = value
|
||||
|
||||
@property
|
||||
def public(self) -> bool:
|
||||
def public(self):
|
||||
return self.asset_platform_protocol.get('public', True)
|
||||
|
||||
|
||||
@@ -210,7 +210,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
return self.category == const.Category.DS and hasattr(self, 'ds')
|
||||
|
||||
@lazyproperty
|
||||
def spec_info(self) -> dict:
|
||||
def spec_info(self):
|
||||
instance = getattr(self, self.category, None)
|
||||
if not instance:
|
||||
return {}
|
||||
@@ -240,7 +240,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
return info
|
||||
|
||||
@lazyproperty
|
||||
def auto_config(self) -> dict:
|
||||
def auto_config(self):
|
||||
platform = self.platform
|
||||
auto_config = {
|
||||
'su_enabled': platform.su_enabled,
|
||||
@@ -343,11 +343,11 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
|
||||
return names
|
||||
|
||||
@lazyproperty
|
||||
def type(self) -> str:
|
||||
def type(self):
|
||||
return self.platform.type
|
||||
|
||||
@lazyproperty
|
||||
def category(self) -> str:
|
||||
def category(self):
|
||||
return self.platform.category
|
||||
|
||||
def is_category(self, category):
|
||||
|
||||
@@ -573,7 +573,7 @@ class Node(JMSOrgBaseModel, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
|
||||
return not self.__gt__(other)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
def name(self):
|
||||
return self.value
|
||||
|
||||
def computed_full_value(self):
|
||||
|
||||
@@ -25,7 +25,7 @@ class PlatformProtocol(models.Model):
|
||||
return '{}/{}'.format(self.name, self.port)
|
||||
|
||||
@property
|
||||
def secret_types(self) -> list:
|
||||
def secret_types(self):
|
||||
return Protocol.settings().get(self.name, {}).get('secret_types', ['password'])
|
||||
|
||||
@lazyproperty
|
||||
|
||||
@@ -147,7 +147,6 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
|
||||
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'), default=())
|
||||
accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Accounts'))
|
||||
nodes_display = NodeDisplaySerializer(read_only=False, required=False, label=_("Node path"))
|
||||
auto_config = serializers.DictField(read_only=True, label=_('Auto info'))
|
||||
platform = ObjectRelatedField(queryset=Platform.objects, required=True, label=_('Platform'),
|
||||
attrs=('id', 'name', 'type'))
|
||||
accounts_amount = serializers.IntegerField(read_only=True, label=_('Accounts amount'))
|
||||
@@ -426,18 +425,6 @@ class DetailMixin(serializers.Serializer):
|
||||
gathered_info = MethodSerializer(label=_('Gathered info'), read_only=True)
|
||||
auto_config = serializers.DictField(read_only=True, label=_('Auto info'))
|
||||
|
||||
@staticmethod
|
||||
def get_auto_config(obj) -> dict:
|
||||
return obj.auto_config
|
||||
|
||||
@staticmethod
|
||||
def get_gathered_info(obj) -> dict:
|
||||
return obj.gathered_info
|
||||
|
||||
@staticmethod
|
||||
def get_spec_info(obj) -> dict:
|
||||
return obj.spec_info
|
||||
|
||||
def get_instance(self):
|
||||
request = self.context.get('request')
|
||||
if not self.instance and UUID_PATTERN.findall(request.path):
|
||||
|
||||
@@ -19,13 +19,11 @@ __all__ = [
|
||||
class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSerializer):
|
||||
assets = ObjectRelatedField(many=True, required=False, queryset=Asset.objects, label=_('Assets'))
|
||||
nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes'))
|
||||
executed_amount = serializers.IntegerField(read_only=True, label=_('Executed amount'))
|
||||
|
||||
class Meta:
|
||||
read_only_fields = [
|
||||
'date_created', 'date_updated', 'created_by',
|
||||
'periodic_display', 'executed_amount', 'type',
|
||||
'last_execution_date',
|
||||
'periodic_display', 'executed_amount', 'type', 'last_execution_date'
|
||||
]
|
||||
mini_fields = [
|
||||
'id', 'name', 'type', 'is_periodic', 'interval',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import uuid
|
||||
from datetime import timedelta, datetime
|
||||
from datetime import timedelta
|
||||
from importlib import import_module
|
||||
|
||||
from django.conf import settings
|
||||
@@ -40,7 +40,7 @@ __all__ = [
|
||||
|
||||
class JobLog(JobExecution):
|
||||
@property
|
||||
def creator_name(self) -> str:
|
||||
def creator_name(self):
|
||||
return self.creator.name
|
||||
|
||||
class Meta:
|
||||
@@ -232,7 +232,7 @@ class UserLoginLog(models.Model):
|
||||
return '%s(%s)' % (self.username, self.city)
|
||||
|
||||
@property
|
||||
def backend_display(self) -> str:
|
||||
def backend_display(self):
|
||||
return gettext(self.backend)
|
||||
|
||||
@classmethod
|
||||
@@ -258,7 +258,7 @@ class UserLoginLog(models.Model):
|
||||
return login_logs
|
||||
|
||||
@property
|
||||
def reason_display(self) -> str:
|
||||
def reason_display(self):
|
||||
from authentication.errors import reason_choices, old_reason_choices
|
||||
|
||||
reason = reason_choices.get(self.reason)
|
||||
@@ -300,15 +300,15 @@ class UserSession(models.Model):
|
||||
return '%s(%s)' % (self.user, self.ip)
|
||||
|
||||
@property
|
||||
def backend_display(self) -> str:
|
||||
def backend_display(self):
|
||||
return gettext(self.backend)
|
||||
|
||||
@property
|
||||
def is_active(self) -> bool:
|
||||
def is_active(self):
|
||||
return user_session_manager.check_active(self.key)
|
||||
|
||||
@property
|
||||
def date_expired(self) -> datetime:
|
||||
def date_expired(self):
|
||||
session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore
|
||||
session_store = session_store_cls(session_key=self.key)
|
||||
cache_key = session_store.cache_key
|
||||
|
||||
@@ -119,11 +119,11 @@ class OperateLogSerializer(BulkOrgResourceModelSerializer):
|
||||
fields = fields_small
|
||||
|
||||
@staticmethod
|
||||
def get_resource_type(instance) -> str:
|
||||
def get_resource_type(instance):
|
||||
return _(instance.resource_type)
|
||||
|
||||
@staticmethod
|
||||
def get_resource(instance) -> str:
|
||||
def get_resource(instance):
|
||||
return i18n_trans(instance.resource)
|
||||
|
||||
|
||||
@@ -147,11 +147,11 @@ class ActivityUnionLogSerializer(serializers.Serializer):
|
||||
r_type = serializers.CharField(read_only=True)
|
||||
|
||||
@staticmethod
|
||||
def get_timestamp(obj) -> str:
|
||||
def get_timestamp(obj):
|
||||
return as_current_tz(obj['datetime']).strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@staticmethod
|
||||
def get_content(obj) -> str:
|
||||
def get_content(obj):
|
||||
if not obj['r_detail']:
|
||||
action = obj['r_action'].replace('_', ' ').capitalize()
|
||||
ctn = _('%s %s this resource') % (obj['r_user'], _(action).lower())
|
||||
@@ -160,7 +160,7 @@ class ActivityUnionLogSerializer(serializers.Serializer):
|
||||
return ctn
|
||||
|
||||
@staticmethod
|
||||
def get_detail_url(obj) -> str:
|
||||
def get_detail_url(obj):
|
||||
detail_url = ''
|
||||
detail_id, obj_type = obj['r_detail_id'], obj['r_type']
|
||||
if not detail_id:
|
||||
@@ -210,7 +210,7 @@ class UserSessionSerializer(serializers.ModelSerializer):
|
||||
"backend_display": {"label": _("Auth backend display")},
|
||||
}
|
||||
|
||||
def get_is_current_user_session(self, obj) -> bool:
|
||||
def get_is_current_user_session(self, obj):
|
||||
request = self.context.get('request')
|
||||
if not request:
|
||||
return False
|
||||
|
||||
@@ -618,8 +618,6 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
|
||||
|
||||
token_id = request.data.get('id') or ''
|
||||
token = ConnectionToken.get_typed_connection_token(token_id)
|
||||
if not token:
|
||||
raise PermissionDenied('Token {} is not valid'.format(token))
|
||||
token.is_valid()
|
||||
serializer = self.get_serializer(instance=token)
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ class TempTokenAuthBackend(JMSBaseAuthBackend):
|
||||
return settings.AUTH_TEMP_TOKEN
|
||||
|
||||
def authenticate(self, request, username='', password=''):
|
||||
tokens = self.model.objects.filter(username=username).order_by('-date_created')[:500]
|
||||
token = next((t for t in tokens if t.secret == password), None)
|
||||
|
||||
token = self.model.objects.filter(username=username, secret=password).first()
|
||||
if not token:
|
||||
return None
|
||||
if not token.is_valid:
|
||||
|
||||
@@ -4,24 +4,6 @@ import authentication.models.access_key
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
old_access_key_secrets_mapper = {}
|
||||
|
||||
def fetch_access_key_secrets(apps, schema_editor):
|
||||
AccessKey = apps.get_model("authentication", "AccessKey")
|
||||
|
||||
for id, secret in AccessKey.objects.all().values_list('id', 'secret'):
|
||||
old_access_key_secrets_mapper[str(id)] = secret
|
||||
|
||||
|
||||
def save_access_key_secrets(apps, schema_editor):
|
||||
AccessKey = apps.get_model("authentication", "AccessKey")
|
||||
aks = AccessKey.objects.filter(id__in=list(old_access_key_secrets_mapper.keys()))
|
||||
for ak in aks:
|
||||
old_value = old_access_key_secrets_mapper.get(str(ak.id))
|
||||
if not old_value:
|
||||
continue
|
||||
ak.secret = old_value
|
||||
ak.save(update_fields=["secret"])
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -30,7 +12,6 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(fetch_access_key_secrets),
|
||||
migrations.AlterField(
|
||||
model_name="accesskey",
|
||||
name="secret",
|
||||
@@ -46,5 +27,4 @@ class Migration(migrations.Migration):
|
||||
verbose_name="Secret"
|
||||
),
|
||||
),
|
||||
migrations.RunPython(save_access_key_secrets),
|
||||
]
|
||||
|
||||
@@ -4,7 +4,6 @@ from datetime import timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
@@ -77,10 +76,7 @@ class ConnectionToken(JMSOrgBaseModel):
|
||||
|
||||
@classmethod
|
||||
def get_typed_connection_token(cls, token_id):
|
||||
try:
|
||||
token = get_object_or_404(cls, id=token_id)
|
||||
except ValidationError:
|
||||
return None
|
||||
token = get_object_or_404(cls, id=token_id)
|
||||
|
||||
if token.type == ConnectionTokenType.ADMIN.value:
|
||||
token = AdminConnectionToken.objects.get(id=token_id)
|
||||
@@ -89,11 +85,11 @@ class ConnectionToken(JMSOrgBaseModel):
|
||||
return token
|
||||
|
||||
@property
|
||||
def is_expired(self) -> bool:
|
||||
def is_expired(self):
|
||||
return self.date_expired < timezone.now()
|
||||
|
||||
@property
|
||||
def expire_time(self) -> int:
|
||||
def expire_time(self):
|
||||
interval = self.date_expired - timezone.now()
|
||||
seconds = interval.total_seconds()
|
||||
if seconds < 0:
|
||||
@@ -165,7 +161,7 @@ class ConnectionToken(JMSOrgBaseModel):
|
||||
def expire_at(self):
|
||||
return self.permed_account.date_expired.timestamp()
|
||||
|
||||
def is_valid(self) -> bool:
|
||||
def is_valid(self):
|
||||
if not self.is_active:
|
||||
error = _('Connection token inactive')
|
||||
raise PermissionDenied(error)
|
||||
|
||||
@@ -20,10 +20,9 @@ class UserConfirmation(permissions.BasePermission):
|
||||
if not settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||
return True
|
||||
|
||||
session = getattr(request, 'session', {})
|
||||
confirm_level = session.get('CONFIRM_LEVEL')
|
||||
confirm_type = session.get('CONFIRM_TYPE')
|
||||
confirm_time = session.get('CONFIRM_TIME')
|
||||
confirm_level = request.session.get('CONFIRM_LEVEL')
|
||||
confirm_type = request.session.get('CONFIRM_TYPE')
|
||||
confirm_time = request.session.get('CONFIRM_TIME')
|
||||
|
||||
ttl = self.get_ttl(confirm_type)
|
||||
now = int(time.time())
|
||||
|
||||
@@ -60,7 +60,7 @@ class _ConnectionTokenAccountSerializer(serializers.ModelSerializer):
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_su_from(account) -> dict:
|
||||
def get_su_from(account):
|
||||
if not hasattr(account, 'asset'):
|
||||
return {}
|
||||
su_enabled = account.asset.platform.su_enabled
|
||||
|
||||
@@ -6,7 +6,6 @@ from common.serializers import CommonModelSerializer
|
||||
from common.serializers.fields import EncryptedField
|
||||
from perms.serializers.permission import ActionChoicesField
|
||||
from ..models import ConnectionToken, AdminConnectionToken
|
||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||
|
||||
__all__ = [
|
||||
'ConnectionTokenSerializer', 'SuperConnectionTokenSerializer',
|
||||
@@ -14,7 +13,7 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
|
||||
class ConnectionTokenSerializer(CommonModelSerializer):
|
||||
expire_time = serializers.IntegerField(read_only=True, label=_('Expired time'))
|
||||
input_secret = EncryptedField(
|
||||
label=_("Input secret"), max_length=40960, required=False, allow_blank=True
|
||||
@@ -61,7 +60,7 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
|
||||
validated_data['remote_addr'] = get_request_ip(request)
|
||||
return super().create(validated_data)
|
||||
|
||||
def get_from_ticket_info(self, instance) -> dict:
|
||||
def get_from_ticket_info(self, instance):
|
||||
if not instance.from_ticket:
|
||||
return {}
|
||||
user = self.get_request_user()
|
||||
|
||||
@@ -46,7 +46,7 @@ class BearerTokenSerializer(serializers.Serializer):
|
||||
user = UserProfileSerializer(read_only=True)
|
||||
|
||||
@staticmethod
|
||||
def get_keyword(obj) -> str:
|
||||
def get_keyword(obj):
|
||||
return 'Bearer'
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import datetime
|
||||
import os
|
||||
from typing import Callable
|
||||
from urllib.parse import urlparse, urlsplit, urlunsplit, urlencode
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import BACKEND_SESSION_KEY
|
||||
@@ -155,18 +155,9 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
|
||||
auth_name, redirect_url = auth_method['name'], auth_method['url']
|
||||
next_url = request.GET.get('next') or '/'
|
||||
next_url = safe_next_url(next_url, request=request)
|
||||
query_string = request.GET.urlencode()
|
||||
redirect_url = '{}?next={}&{}'.format(redirect_url, next_url, query_string)
|
||||
|
||||
merged_qs_items = dict(request.GET.lists())
|
||||
merged_qs_items.pop('next', None)
|
||||
|
||||
merged = {}
|
||||
for k, v_list in merged_qs_items.items():
|
||||
merged[k] = v_list if len(v_list) > 1 else (v_list[0] if v_list else '')
|
||||
|
||||
merged['next'] = next_url
|
||||
query = urlencode(merged, doseq=True)
|
||||
u = urlsplit(redirect_url)
|
||||
redirect_url = urlunsplit((u.scheme, u.netloc, u.path, query, u.fragment))
|
||||
if settings.LOGIN_REDIRECT_MSG_ENABLED:
|
||||
message_data = {
|
||||
'title': _('Redirecting'),
|
||||
@@ -174,7 +165,7 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
|
||||
'redirect_url': redirect_url,
|
||||
'interval': 3,
|
||||
'has_cancel': True,
|
||||
'cancel_url': reverse('authentication:login') + f'?admin=1&{query}'
|
||||
'cancel_url': reverse('authentication:login') + f'?admin=1&{query_string}'
|
||||
}
|
||||
redirect_url = FlashMessageUtil.gen_message_url(message_data)
|
||||
return redirect_url
|
||||
|
||||
@@ -143,9 +143,11 @@ class EncryptMixin:
|
||||
if value is None:
|
||||
return value
|
||||
|
||||
encryptor = Encryptor(value)
|
||||
plain_value = encryptor.decrypt()
|
||||
plain_value = Encryptor(value).decrypt()
|
||||
|
||||
# 如果解密失败,则使用原来的值
|
||||
if not plain_value:
|
||||
plain_value = value
|
||||
# 可能和Json mix,所以要先解密,再json
|
||||
sp = super()
|
||||
if hasattr(sp, "from_db_value"):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from contextlib import contextmanager
|
||||
import base64
|
||||
|
||||
from django.db import connections, transaction, connection
|
||||
from django.utils.encoding import force_str
|
||||
@@ -103,54 +102,6 @@ class Encryptor:
|
||||
def __init__(self, value):
|
||||
self.value = force_str(value)
|
||||
|
||||
def is_encrypted_data(self):
|
||||
"""
|
||||
检测数据是否为加密格式
|
||||
返回 True 表示是加密数据,False 表示是原始数据
|
||||
"""
|
||||
if not self.value:
|
||||
return False
|
||||
|
||||
# 检测 base64 编码格式 (crypto.encrypt 的输出)
|
||||
try:
|
||||
# 尝试不同的 base64 解码方式
|
||||
# 1. 标准 base64
|
||||
try:
|
||||
base64.b64decode(self.value)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. URL-safe base64
|
||||
try:
|
||||
# 添加必要的填充
|
||||
missing_padding = len(self.value) % 4
|
||||
if missing_padding:
|
||||
padded_value = self.value + '=' * (4 - missing_padding)
|
||||
else:
|
||||
padded_value = self.value
|
||||
base64.urlsafe_b64decode(padded_value)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 检测 AES GCM 格式 (固定72字符metadata)
|
||||
if len(self.value) > 72:
|
||||
try:
|
||||
# 前72字符应该是3个24字符的base64编码
|
||||
metadata = self.value[:72]
|
||||
for i in range(0, 72, 24):
|
||||
part = metadata[i:i+24]
|
||||
base64.b64decode(part)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def decrypt(self):
|
||||
plain_value = crypto.decrypt(self.value)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models import Model
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
from rest_framework.fields import ChoiceField, empty
|
||||
|
||||
from common.db.fields import TreeChoices, JSONManyToManyField as ModelJSONManyToManyField
|
||||
from common.utils import decrypt_password, is_uuid
|
||||
@@ -53,7 +53,7 @@ class EncryptedField(serializers.CharField):
|
||||
return decrypt_password(value)
|
||||
|
||||
|
||||
class LabeledChoiceField(serializers.ChoiceField):
|
||||
class LabeledChoiceField(ChoiceField):
|
||||
def to_representation(self, key):
|
||||
if key is None:
|
||||
return key
|
||||
@@ -180,122 +180,6 @@ class ObjectRelatedField(serializers.RelatedField):
|
||||
except (TypeError, ValueError):
|
||||
self.fail("incorrect_type", data_type=type(pk).__name__)
|
||||
|
||||
def get_schema(self):
|
||||
"""
|
||||
为 drf-spectacular 提供 OpenAPI schema
|
||||
"""
|
||||
# 获取字段的基本信息
|
||||
field_type = 'array' if self.many else 'object'
|
||||
|
||||
if field_type == 'array':
|
||||
# 如果是多对多关系
|
||||
return {
|
||||
'type': 'array',
|
||||
'items': self._get_openapi_item_schema(),
|
||||
'description': getattr(self, 'help_text', ''),
|
||||
'title': getattr(self, 'label', ''),
|
||||
}
|
||||
else:
|
||||
# 如果是一对一关系
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': self._get_openapi_properties_schema(),
|
||||
'description': getattr(self, 'help_text', ''),
|
||||
'title': getattr(self, 'label', ''),
|
||||
}
|
||||
|
||||
def _get_openapi_item_schema(self):
|
||||
"""
|
||||
获取数组项的 OpenAPI schema
|
||||
"""
|
||||
return self._get_openapi_object_schema()
|
||||
|
||||
def _get_openapi_object_schema(self):
|
||||
"""
|
||||
获取对象的 OpenAPI schema
|
||||
"""
|
||||
properties = {}
|
||||
|
||||
# 动态分析 attrs 中的属性类型
|
||||
for attr in self.attrs:
|
||||
# 尝试从 queryset 的 model 中获取字段信息
|
||||
field_type = self._infer_field_type(attr)
|
||||
properties[attr] = {
|
||||
'type': field_type,
|
||||
'description': f'{attr} field'
|
||||
}
|
||||
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': properties,
|
||||
'required': ['id'] if 'id' in self.attrs else []
|
||||
}
|
||||
|
||||
def _infer_field_type(self, attr_name):
|
||||
"""
|
||||
智能推断字段类型
|
||||
"""
|
||||
try:
|
||||
# 如果有 queryset,尝试从 model 中获取字段信息
|
||||
if hasattr(self, 'queryset') and self.queryset is not None:
|
||||
model = self.queryset.model
|
||||
if hasattr(model, '_meta') and hasattr(model._meta, 'fields'):
|
||||
field = model._meta.get_field(attr_name)
|
||||
if field:
|
||||
return self._map_django_field_type(field)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 如果没有 queryset 或无法获取字段信息,使用启发式规则
|
||||
return self._heuristic_field_type(attr_name)
|
||||
|
||||
def _map_django_field_type(self, field):
|
||||
"""
|
||||
将 Django 字段类型映射到 OpenAPI 类型
|
||||
"""
|
||||
field_type = type(field).__name__
|
||||
|
||||
# 整数类型
|
||||
if 'Integer' in field_type or 'BigInteger' in field_type or 'SmallInteger' in field_type:
|
||||
return 'integer'
|
||||
# 浮点数类型
|
||||
elif 'Float' in field_type or 'Decimal' in field_type:
|
||||
return 'number'
|
||||
# 布尔类型
|
||||
elif 'Boolean' in field_type:
|
||||
return 'boolean'
|
||||
# 日期时间类型
|
||||
elif 'DateTime' in field_type or 'Date' in field_type or 'Time' in field_type:
|
||||
return 'string'
|
||||
# 文件类型
|
||||
elif 'File' in field_type or 'Image' in field_type:
|
||||
return 'string'
|
||||
# 其他类型默认为字符串
|
||||
else:
|
||||
return 'string'
|
||||
|
||||
def _heuristic_field_type(self, attr_name):
|
||||
"""
|
||||
启发式推断字段类型
|
||||
"""
|
||||
# 基于属性名的启发式规则
|
||||
|
||||
if attr_name in ['is_active', 'enabled', 'visible'] or attr_name.startswith('is_'):
|
||||
return 'boolean'
|
||||
elif attr_name in ['count', 'number', 'size', 'amount']:
|
||||
return 'integer'
|
||||
elif attr_name in ['price', 'rate', 'percentage']:
|
||||
return 'number'
|
||||
else:
|
||||
# 默认返回字符串类型
|
||||
return 'string'
|
||||
|
||||
def _get_openapi_properties_schema(self):
|
||||
"""
|
||||
获取对象属性的 OpenAPI schema
|
||||
"""
|
||||
return self._get_openapi_object_schema()['properties']
|
||||
|
||||
|
||||
class TreeChoicesField(serializers.MultipleChoiceField):
|
||||
def __init__(self, choice_cls, **kwargs):
|
||||
@@ -354,23 +238,6 @@ class BitChoicesField(TreeChoicesField):
|
||||
value |= name_value_map[name]
|
||||
return value
|
||||
|
||||
def get_schema(self):
|
||||
"""
|
||||
为 drf-spectacular 提供 OpenAPI schema
|
||||
"""
|
||||
return {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'value': {'type': 'string'},
|
||||
'label': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'description': getattr(self, 'help_text', ''),
|
||||
'title': getattr(self, 'label', ''),
|
||||
}
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
"""
|
||||
备注:
|
||||
|
||||
@@ -185,9 +185,3 @@ def check_migrations_file_prefix_conflict(*args, **kwargs):
|
||||
print(f'{msg_left}{msg_right1}\n{msg_right2}\n')
|
||||
|
||||
print('=' * 80)
|
||||
|
||||
|
||||
@receiver(django_ready)
|
||||
def clear_response_cache(sender, **kwargs):
|
||||
from django.core.cache import cache
|
||||
cache.delete_pattern('views.decorators.cache.cache_page:*')
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
import os
|
||||
|
||||
from azure.storage.blob import BlobServiceClient
|
||||
|
||||
from .base import ObjectStorage
|
||||
|
||||
|
||||
class AzureStorage(ObjectStorage):
|
||||
|
||||
def __init__(self, config):
|
||||
from azure.storage.blob import BlobServiceClient
|
||||
|
||||
self.account_name = config.get("ACCOUNT_NAME", None)
|
||||
self.account_key = config.get("ACCOUNT_KEY", None)
|
||||
self.container_name = config.get("CONTAINER_NAME", None)
|
||||
|
||||
@@ -10,7 +10,7 @@ import socket
|
||||
import time
|
||||
import uuid
|
||||
from collections import OrderedDict
|
||||
from functools import wraps, cached_property
|
||||
from functools import wraps
|
||||
from itertools import chain
|
||||
|
||||
import html2text
|
||||
@@ -246,19 +246,17 @@ def dict_get_any(d, keys):
|
||||
return None
|
||||
|
||||
|
||||
# class lazyproperty:
|
||||
# def __init__(self, func):
|
||||
# self.func = func
|
||||
class lazyproperty:
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
|
||||
# def __get__(self, instance, cls):
|
||||
# if instance is None:
|
||||
# return self
|
||||
# else:
|
||||
# value = self.func(instance)
|
||||
# setattr(instance, self.func.__name__, value)
|
||||
# return value
|
||||
|
||||
lazyproperty = cached_property
|
||||
def __get__(self, instance, cls):
|
||||
if instance is None:
|
||||
return self
|
||||
else:
|
||||
value = self.func(instance)
|
||||
setattr(instance, self.func.__name__, value)
|
||||
return value
|
||||
|
||||
|
||||
def get_disk_usage(path):
|
||||
|
||||
@@ -47,6 +47,7 @@ class Subscription:
|
||||
self.ch = pb.ch
|
||||
self.sub = sub
|
||||
self.unsubscribed = False
|
||||
logger.info(f"Subscribed to channel: {sub}")
|
||||
|
||||
def _handle_msg(self, _next, error, complete):
|
||||
"""
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"SessionLockedMessage": "This session is locked by %s, please wait for the session to be unlocked.",
|
||||
"SessionUnlockedMessage": "This session has been unlocked by %s.",
|
||||
"ShowProperties": "Show properties",
|
||||
"StopHotKey": "Stop (Ctrl + D)",
|
||||
"StopHotKey": "Stop (Ctrl + C)",
|
||||
"Submit": "Submit",
|
||||
"Total": "Total",
|
||||
"Type": "Type",
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"SessionLockedMessage": "此会话已被 %s 锁定,无法继续执行命令",
|
||||
"SessionUnlockedMessage": "此会话已被 %s 解锁,可以继续执行命令",
|
||||
"ShowProperties": "属性",
|
||||
"StopHotKey": "停止 (Ctrl + D)",
|
||||
"StopHotKey": "停止 (Ctrl + C)",
|
||||
"Submit": "提交",
|
||||
"Total": "总计",
|
||||
"Type": "类型",
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -199,7 +199,7 @@ msgstr ""
|
||||
msgid "Skip"
|
||||
msgstr ""
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
@@ -572,8 +572,8 @@ msgstr ""
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1015,7 +1015,7 @@ msgstr ""
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1183,13 +1183,13 @@ msgstr ""
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130 ops/models/job.py:155
|
||||
#: authentication/serializers/connect_token_secret.py:128 ops/models/job.py:155
|
||||
#: perms/serializers/user_permission.py:28 terminal/models/applet/applet.py:40
|
||||
#: terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1248,7 +1248,7 @@ msgid "Spec info"
|
||||
msgstr ""
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1263,7 +1263,7 @@ msgstr ""
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1812,7 +1812,7 @@ msgstr ""
|
||||
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99 audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2461,7 +2461,7 @@ msgstr ""
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr ""
|
||||
@@ -2482,7 +2482,7 @@ msgstr ""
|
||||
msgid "Gathered info"
|
||||
msgstr ""
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr ""
|
||||
|
||||
@@ -2654,7 +2654,7 @@ msgstr ""
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2663,7 +2663,7 @@ msgstr ""
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -2977,7 +2977,7 @@ msgid "Disk total"
|
||||
msgstr ""
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr ""
|
||||
|
||||
@@ -3261,7 +3261,7 @@ msgstr ""
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@@ -3287,7 +3287,7 @@ msgstr ""
|
||||
msgid "Rename dir"
|
||||
msgstr ""
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3296,7 +3296,7 @@ msgstr ""
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
@@ -4112,7 +4112,7 @@ msgid "Input secret"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4222,38 +4222,38 @@ msgstr ""
|
||||
msgid "binding reminder"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "Builtin"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr ""
|
||||
@@ -6136,27 +6136,27 @@ msgstr ""
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "System Admin"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "System Auditor"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "System Component"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "Organization Admin"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "Organization Auditor"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "Organization user"
|
||||
|
||||
@@ -6192,30 +6192,6 @@ msgstr ""
|
||||
msgid "Can view System Tools"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr ""
|
||||
@@ -6333,28 +6309,24 @@ msgstr ""
|
||||
msgid "Job audit"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "Organizations"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr ""
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr ""
|
||||
|
||||
@@ -6382,22 +6354,6 @@ msgstr ""
|
||||
msgid "Account automation report"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:44
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr ""
|
||||
@@ -7627,11 +7583,11 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr ""
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr ""
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9309,7 +9265,7 @@ msgstr ""
|
||||
msgid "Body"
|
||||
msgstr ""
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr ""
|
||||
@@ -9318,7 +9274,7 @@ msgstr ""
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr ""
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -224,7 +224,7 @@ msgstr "Plantilla"
|
||||
msgid "Skip"
|
||||
msgstr "Omitir"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
@@ -599,8 +599,8 @@ msgstr "Actividad de cuenta"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1054,7 +1054,7 @@ msgstr "Reglas de contraseña"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1239,13 +1239,13 @@ msgstr "Categoría"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1304,7 +1304,7 @@ msgid "Spec info"
|
||||
msgstr "Información especial"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1320,7 +1320,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1924,7 +1924,7 @@ msgstr ""
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2608,7 +2608,7 @@ msgstr "Dirección"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "Plataforma"
|
||||
@@ -2629,7 +2629,7 @@ msgstr "Nodo"
|
||||
msgid "Gathered info"
|
||||
msgstr "Recopilar información sobre hardware de activos"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "Atributos personalizados"
|
||||
|
||||
@@ -2801,7 +2801,7 @@ msgstr "Sistema"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2810,7 +2810,7 @@ msgstr "Valor"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3146,7 +3146,7 @@ msgid "Disk total"
|
||||
msgstr "Tamaño del disco"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "Sistema operativo"
|
||||
|
||||
@@ -3454,7 +3454,7 @@ msgstr "Eliminar directorio"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
@@ -3480,7 +3480,7 @@ msgstr "Descargar"
|
||||
msgid "Rename dir"
|
||||
msgstr "Mapear directorio"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3489,7 +3489,7 @@ msgstr "Ver"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "Crear"
|
||||
|
||||
@@ -4361,7 +4361,7 @@ msgid "Input secret"
|
||||
msgstr "Contraseña personalizada"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4471,38 +4471,38 @@ msgstr "Recordatorio de inicio de sesión remoto"
|
||||
msgid "binding reminder"
|
||||
msgstr "Recordatorio de vinculación"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "Incorporado"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "Opciones"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "Componentes"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "Dominio"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "Expiración inmediata"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "Nombre de imagen"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "Puerto de imagen"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "Protocolo de imagen"
|
||||
@@ -6535,27 +6535,27 @@ msgstr "{} debe tener al menos un rol del sistema"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "Administrador del sistema"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "Auditor del sistema"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "Componente del sistema"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "Administrador de la organización"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "Auditor de la organización"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "Usuario de la organización"
|
||||
|
||||
@@ -6591,30 +6591,6 @@ msgstr "Puede ver la gestión de archivos"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "Puede ver las herramientas del sistema"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "Puedes consultar el informe de inicio de sesión de usuarios"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "puedes consultar el informe de cambio de contraseña de usuarios"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "puedes consultar el informe de estadísticas de activos"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "puedes consultar el informe de actividades de activos"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "puedes consultar el informe de estadísticas de cuentas"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "puedes consultar el informe de automatización de cuentas"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "Tipo de contenido"
|
||||
@@ -6734,28 +6710,24 @@ msgstr "Licencia"
|
||||
msgid "Job audit"
|
||||
msgstr "Auditoría de acciones"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "Informe"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "Gestión de organizaciones"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "Comentarios de órdenes de trabajo"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "Orden de trabajo"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "Configuración general"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "Ver árbol de autorizaciones"
|
||||
|
||||
@@ -6783,26 +6755,6 @@ msgstr "Informe de estadísticas de cuentas"
|
||||
msgid "Account automation report"
|
||||
msgstr "Informe de automatización de cuentas"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "Consola"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "Registro de cambio de clave"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "Chat AI no está habilitado"
|
||||
@@ -8266,11 +8218,11 @@ msgstr ""
|
||||
"completar la autenticación"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "Activar código de verificación para inicio de sesión"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr ""
|
||||
"Habilitar el código de verificación para prevenir el inicio de sesión de "
|
||||
"bots"
|
||||
@@ -10092,7 +10044,7 @@ msgstr "Nombre de usuario"
|
||||
msgid "Body"
|
||||
msgstr "Contenido"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "Nivel de aprobación"
|
||||
@@ -10101,7 +10053,7 @@ msgstr "Nivel de aprobación"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "Información de aprobación de la orden de trabajo"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "Proceso de la orden de trabajo"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -202,7 +202,7 @@ msgstr "テンプレート"
|
||||
msgid "Skip"
|
||||
msgstr "スキップ"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "更新"
|
||||
@@ -590,8 +590,8 @@ msgstr "アカウントの活動"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1038,7 +1038,7 @@ msgstr "パスワードルール"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1209,13 +1209,13 @@ msgstr "カテゴリ"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1274,7 +1274,7 @@ msgid "Spec info"
|
||||
msgstr "特別情報"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1290,7 +1290,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1835,7 +1835,7 @@ msgstr "1-100、低い値は最初に一致します"
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2492,7 +2492,7 @@ msgstr "アドレス"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "プラットフォーム"
|
||||
@@ -2513,7 +2513,7 @@ msgstr "ノード"
|
||||
msgid "Gathered info"
|
||||
msgstr "資産ハードウェア情報の収集"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "カスタム属性"
|
||||
|
||||
@@ -2685,7 +2685,7 @@ msgstr "システム"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2694,7 +2694,7 @@ msgstr "値"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3019,7 +3019,7 @@ msgid "Disk total"
|
||||
msgstr "ディスクの合計"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "OS"
|
||||
|
||||
@@ -3303,7 +3303,7 @@ msgstr "Rmdir"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "削除"
|
||||
|
||||
@@ -3329,7 +3329,7 @@ msgstr "ダウンロード"
|
||||
msgid "Rename dir"
|
||||
msgstr "マップディレクトリ"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3338,7 +3338,7 @@ msgstr "表示"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "作成"
|
||||
|
||||
@@ -4155,7 +4155,7 @@ msgid "Input secret"
|
||||
msgstr "カスタムパスワード"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4265,38 +4265,38 @@ msgstr "異なる都市ログインのリマインダー"
|
||||
msgid "binding reminder"
|
||||
msgstr "バインディングリマインダー"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "ビルトイン"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "オプション"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "コンポーネント"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "ドメイン"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "すぐに期限切れ"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "ミラー名"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "ミラーポート"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "ミラープロトコル"
|
||||
@@ -6184,27 +6184,27 @@ msgstr "{} 少なくとも1つのシステムロール"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "システム管理者"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "システム監査人"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "システムコンポーネント"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "組織管理者"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "監査員を組織する"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "組織ユーザー"
|
||||
|
||||
@@ -6240,30 +6240,6 @@ msgstr "ファイルマネージャを表示できます"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "システムツールを表示できます"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "ユーザーのログインレポートを確認できます"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "ユーザーのパスワード変更レポートを確認できます"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "資産統計レポートを確認できます"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "資産活動レポートを確認できます"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "アカウント統計レポートを確認できます"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "アカウント自動化レポートを確認できます"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "コンテンツタイプ"
|
||||
@@ -6381,28 +6357,24 @@ msgstr "ライセンス"
|
||||
msgid "Job audit"
|
||||
msgstr "作業監査"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "レポート"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "アプリ組織"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "チケットコメント"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "チケット"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "共通設定"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "権限ツリーの表示"
|
||||
|
||||
@@ -6430,26 +6402,6 @@ msgstr "アカウント統計報告"
|
||||
msgid "Account automation report"
|
||||
msgstr "アカウント自動化報告"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "Console"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "パスワード レコードの変更"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "チャットAIがオンになっていない"
|
||||
@@ -7727,11 +7679,11 @@ msgid ""
|
||||
msgstr "パスワードと追加コードは、検証のためにサードパーティの認証システムに送信されます"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "ログインcaptchaの有効化"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "Captchaを有効にしてロボット認証を防止する"
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9434,7 +9386,7 @@ msgstr "ユーザー表示名"
|
||||
msgid "Body"
|
||||
msgstr "ボディ"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "レベルを承認する"
|
||||
@@ -9443,7 +9395,7 @@ msgstr "レベルを承認する"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "チケットフロー承認ルール"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "チケットの流れ"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -202,7 +202,7 @@ msgstr "템플릿"
|
||||
msgid "Skip"
|
||||
msgstr "건너뛰기"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "업데이트"
|
||||
@@ -575,8 +575,8 @@ msgstr "계정 활동"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1023,7 +1023,7 @@ msgstr "암호 규칙"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1195,13 +1195,13 @@ msgstr "카테고리"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1260,7 +1260,7 @@ msgid "Spec info"
|
||||
msgstr "특별 정보"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1276,7 +1276,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1825,7 +1825,7 @@ msgstr "우선순위 선택 범위는 1-100 (숫자가 작을수록 더 우선)"
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2484,7 +2484,7 @@ msgstr "주소"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "플랫폼"
|
||||
@@ -2505,7 +2505,7 @@ msgstr "노드"
|
||||
msgid "Gathered info"
|
||||
msgstr "자산 하드웨어 정보 수집"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "사용자 정의 속성"
|
||||
|
||||
@@ -2677,7 +2677,7 @@ msgstr "시스템"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2686,7 +2686,7 @@ msgstr "값"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3008,7 +3008,7 @@ msgid "Disk total"
|
||||
msgstr "하드디스크 크기"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "운영 체제"
|
||||
|
||||
@@ -3294,7 +3294,7 @@ msgstr "디렉터리 삭제"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "삭제"
|
||||
|
||||
@@ -3320,7 +3320,7 @@ msgstr "다운로드"
|
||||
msgid "Rename dir"
|
||||
msgstr "디렉터리 매핑"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3329,7 +3329,7 @@ msgstr "조회"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "생성"
|
||||
|
||||
@@ -4154,7 +4154,7 @@ msgid "Input secret"
|
||||
msgstr "비밀번호 사용자 정의"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4264,38 +4264,38 @@ msgstr "이전 로그인 알림"
|
||||
msgid "binding reminder"
|
||||
msgstr "바인딩 알림"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "내장된"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "옵션"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "구성 요소"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "도메인"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "즉시 만료"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "이미지 이름"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "이미지 포트"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "이미지 프로토콜"
|
||||
@@ -6206,27 +6206,27 @@ msgstr "{} 최소한 하나의 시스템 역할이 필요합니다."
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "시스템 관리자"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "시스템 감사자"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "시스템 구성 요소"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "조직 관리자"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "조직 감사자"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "조직 사용자"
|
||||
|
||||
@@ -6276,30 +6276,6 @@ msgstr ""
|
||||
msgid "Can view System Tools"
|
||||
msgstr "시스템 도구를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "사용자 로그인 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "사용자 비밀번호 변경 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "자산 통계 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "자산 활동 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "계정 통계 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "계정 자동화 보고서를 확인할 수 있습니다"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "내용 유형"
|
||||
@@ -6417,28 +6393,24 @@ msgstr "라이센스"
|
||||
msgid "Job audit"
|
||||
msgstr "작업 감사"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "보고서"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "조직 관리"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "작업 댓글"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "작업"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "일반 설정"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "권한 트리 보기"
|
||||
|
||||
@@ -6466,26 +6438,6 @@ msgstr "계정 통계 보고서"
|
||||
msgid "Account automation report"
|
||||
msgstr "계정 자동화 보고서"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "콘솔"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "비밀번호 변경 기록"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "채팅 AI가 활성화되어 있지 않습니다"
|
||||
@@ -7789,11 +7741,11 @@ msgstr ""
|
||||
"인증을 완료해야 합니다."
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "로그인 인증 코드 활성화"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "인증 코드를 활성화하여 로봇 로그인을 차단합니다."
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9499,7 +9451,7 @@ msgstr "사용자 표시 이름"
|
||||
msgid "Body"
|
||||
msgstr "내용"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "승인 수준"
|
||||
@@ -9508,7 +9460,7 @@ msgstr "승인 수준"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "작업 요청 승인 정보"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "작업 요청 프로세스"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -202,7 +202,7 @@ msgstr "Modelo"
|
||||
msgid "Skip"
|
||||
msgstr "Pular"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "Atualizar"
|
||||
@@ -578,8 +578,8 @@ msgstr "Atividade da conta"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1046,7 +1046,7 @@ msgstr "Regras de senha"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1228,13 +1228,13 @@ msgstr "Categoria"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1293,7 +1293,7 @@ msgid "Spec info"
|
||||
msgstr "Informações especiais"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1309,7 +1309,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1900,7 +1900,7 @@ msgstr ""
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2577,7 +2577,7 @@ msgstr "Endereço"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "Plataforma"
|
||||
@@ -2598,7 +2598,7 @@ msgstr "Node"
|
||||
msgid "Gathered info"
|
||||
msgstr "Coletar informações do hardware do ativo"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "Propriedades personalizadas"
|
||||
|
||||
@@ -2770,7 +2770,7 @@ msgstr "Sistema"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2779,7 +2779,7 @@ msgstr "Valor"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3111,7 +3111,7 @@ msgid "Disk total"
|
||||
msgstr "Tamanho do disco rígido"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "Sistema operacional"
|
||||
|
||||
@@ -3417,7 +3417,7 @@ msgstr "Excluir diretório"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "Excluir"
|
||||
|
||||
@@ -3443,7 +3443,7 @@ msgstr "Baixar"
|
||||
msgid "Rename dir"
|
||||
msgstr "Mapeamento de Diretórios"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3452,7 +3452,7 @@ msgstr "Visualizar"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "Criar"
|
||||
|
||||
@@ -4300,7 +4300,7 @@ msgid "Input secret"
|
||||
msgstr "Senha personalizada"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4410,38 +4410,38 @@ msgstr "Alerta de login remoto"
|
||||
msgid "binding reminder"
|
||||
msgstr "Alerta de vinculação"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "Incorporado"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "Opções"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "Componentes"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "Domínio da Web"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "Expiração Imediata"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "Nome da Imagem"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "Porta da Imagem"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "Protocolo da Imagem"
|
||||
@@ -6428,27 +6428,27 @@ msgstr "{} tem pelo menos um papel de sistema"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "Administrador do sistema"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "Auditor do sistema"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "Componentes do sistema"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "Administrador da organização"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "Auditor da organização"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "Organizar usuários"
|
||||
|
||||
@@ -6484,30 +6484,6 @@ msgstr "Pode visualizar a gestão de arquivos"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "Pode visualizar as ferramentas do sistema"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "Pode visualizar o relatório de login do usuário"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "Pode visualizar o relatório de alteração de senha do usuário"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "Pode visualizar o relatório de estatísticas de ativos"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "Pode visualizar o relatório de atividades de ativos"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "Pode visualizar o relatório de estatísticas de contas"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "Pode visualizar o relatório de automação de contas."
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "Tipo de conteúdo"
|
||||
@@ -6627,28 +6603,24 @@ msgstr "Licença"
|
||||
msgid "Job audit"
|
||||
msgstr "Auditoria de Tarefas"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "Relatório"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "Gerenciamento de Organização"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "Comentários de Ordem de Serviço"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "Ordem de Serviço"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "Configurações Gerais"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "Visualizar Árvore de Autorização"
|
||||
|
||||
@@ -6676,26 +6648,6 @@ msgstr "Relatório de Estatísticas de Contas"
|
||||
msgid "Account automation report"
|
||||
msgstr "Relatório de Automação de Contas"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "Console"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "Registro de alteração de senha"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "Chat AI Não Ativado"
|
||||
@@ -8079,11 +8031,11 @@ msgstr ""
|
||||
"de terceiros exigem senha + 6 números para completar a autenticação"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "Habilitar código de verificação de login"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "Ligar o código de verificação para prevenir o login de robôs"
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9869,7 +9821,7 @@ msgstr "Nome de exibição do usuário"
|
||||
msgid "Body"
|
||||
msgstr "Conteúdo"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "Nível de aprovação"
|
||||
@@ -9878,7 +9830,7 @@ msgstr "Nível de aprovação"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "Informações de aprovação da ordem de serviço"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "Fluxo de Action da ordem de serviço"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+0800\n"
|
||||
"PO-Revision-Date: 2025-06-11 11:51+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -204,7 +204,7 @@ msgstr "Шаблон"
|
||||
msgid "Skip"
|
||||
msgstr "Пропустить"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "Обновить"
|
||||
@@ -579,8 +579,8 @@ msgstr "Активность аккаунта"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1033,7 +1033,7 @@ msgstr "Правила паролей"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1215,13 +1215,13 @@ msgstr "Категория"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1280,7 +1280,7 @@ msgid "Spec info"
|
||||
msgstr "Специальная информация"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1296,7 +1296,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1877,7 +1877,7 @@ msgstr "1-100 (меньшее число - выше приоритет)"
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2553,7 +2553,7 @@ msgstr "Адрес"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "Платформа"
|
||||
@@ -2574,7 +2574,7 @@ msgstr "Папки"
|
||||
msgid "Gathered info"
|
||||
msgstr "Собранные данные"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "Пользовательские атрибуты"
|
||||
|
||||
@@ -2746,7 +2746,7 @@ msgstr "Система"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2755,7 +2755,7 @@ msgstr "Значение"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3090,7 +3090,7 @@ msgid "Disk total"
|
||||
msgstr "Размер диска"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "ОС"
|
||||
|
||||
@@ -3399,7 +3399,7 @@ msgstr "Удалить каталог"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "Удалить"
|
||||
|
||||
@@ -3425,7 +3425,7 @@ msgstr "Скачать"
|
||||
msgid "Rename dir"
|
||||
msgstr "Сопоставление каталога"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3434,7 +3434,7 @@ msgstr "Просмотр"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "Создать"
|
||||
|
||||
@@ -4280,7 +4280,7 @@ msgid "Input secret"
|
||||
msgstr "Введите пароль"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4390,38 +4390,38 @@ msgstr "Уведомление о входе из другого местопо
|
||||
msgid "binding reminder"
|
||||
msgstr "Уведомление о привязке"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "Встроенный"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "Параметры"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "Компоненты"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "Зона"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "Истекает немедленно"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "Название образа"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "Порт образа"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "Протокол образа"
|
||||
@@ -6402,27 +6402,27 @@ msgstr "{} хотя бы одна системная роль"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "Системный администратор"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "Системный аудитор"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "Системные компоненты"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "Администратор организации"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "Аудитор организации"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "Пользователь организации"
|
||||
|
||||
@@ -6458,30 +6458,6 @@ msgstr "Просмотр файлового менеджера"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "Просмотр системных инструментов"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "Можно просматривать отчеты о входе пользователей"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "Можно просматривать отчеты о смене пароля пользователей"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "Можно просматривать отчеты о статистике активов"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "Можно просматривать отчеты о действиях с активами"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "Можно просматривать отчеты о статистике аккаунтов"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "Можно просматривать отчеты о автоматизации аккаунтов"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "Тип содержимого"
|
||||
@@ -6601,28 +6577,24 @@ msgstr "Лицензия"
|
||||
msgid "Job audit"
|
||||
msgstr "Аудит заданий"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "Отчёт"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "Управление организациями"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "Комментарии к заявке"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "Заявка"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "Общие настройки"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "Просмотр дерева разрешений"
|
||||
|
||||
@@ -6650,26 +6622,6 @@ msgstr "Отчет о статистике аккаунтов"
|
||||
msgid "Account automation report"
|
||||
msgstr "Автоматизированный отчет по аккаунтам"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "Консоль"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "Запись о смене секрета"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "Чат AI не включен"
|
||||
@@ -8046,11 +7998,11 @@ msgstr ""
|
||||
"для проверки"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "Проверочный код (captcha) при входе"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "Включите проверочный код, чтобы предотвратить аутентификацию роботов"
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9834,7 +9786,7 @@ msgstr "Имя пользователя"
|
||||
msgid "Body"
|
||||
msgstr "Содержимое"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "Уровень одобрения"
|
||||
@@ -9843,7 +9795,7 @@ msgstr "Уровень одобрения"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "Правило утверждения потока заявок"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "Поток заявок"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -201,7 +201,7 @@ msgstr "模板"
|
||||
msgid "Skip"
|
||||
msgstr "跳过"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "更新"
|
||||
@@ -574,8 +574,8 @@ msgstr "账号活动"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1017,7 +1017,7 @@ msgstr "密码规则"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1194,13 +1194,13 @@ msgstr "类别"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130 ops/models/job.py:155
|
||||
#: authentication/serializers/connect_token_secret.py:128 ops/models/job.py:155
|
||||
#: perms/serializers/user_permission.py:28 terminal/models/applet/applet.py:40
|
||||
#: terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1259,7 +1259,7 @@ msgid "Spec info"
|
||||
msgstr "特殊信息"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1274,7 +1274,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1841,7 +1841,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)"
|
||||
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99 audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2501,7 +2501,7 @@ msgstr "地址"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "平台"
|
||||
@@ -2522,7 +2522,7 @@ msgstr "节点"
|
||||
msgid "Gathered info"
|
||||
msgstr "收集资产硬件信息"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "自定义属性"
|
||||
|
||||
@@ -2696,7 +2696,7 @@ msgstr "系统"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2705,7 +2705,7 @@ msgstr "值"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3034,7 +3034,7 @@ msgid "Disk total"
|
||||
msgstr "硬盘大小"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "操作系统"
|
||||
|
||||
@@ -3323,7 +3323,7 @@ msgstr "删除目录"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
@@ -3349,7 +3349,7 @@ msgstr "下载"
|
||||
msgid "Rename dir"
|
||||
msgstr "映射目录"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3358,7 +3358,7 @@ msgstr "查看"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "创建"
|
||||
|
||||
@@ -4181,7 +4181,7 @@ msgid "Input secret"
|
||||
msgstr "自定义密码"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4291,38 +4291,38 @@ msgstr "异地登录提醒"
|
||||
msgid "binding reminder"
|
||||
msgstr "绑定提醒"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "内置的"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "选项"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "组件"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "网域"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "立刻过期"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "镜像名称"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "镜像端口"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "镜像协议"
|
||||
@@ -6239,27 +6239,27 @@ msgstr "{} 至少有一个系统角色"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "系统管理员"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "系统审计员"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "系统组件"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "组织管理员"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "组织审计员"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "组织用户"
|
||||
|
||||
@@ -6295,30 +6295,6 @@ msgstr "可以查看文件管理"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "可以查看系统工具"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "可以查看用户登录报告"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "可以查看用户改密报告"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "可以查看资产统计报告"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "可以查看资产活动报告"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "可以查看账号统计报告"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "可以查看账号自动化报告"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "内容类型"
|
||||
@@ -6436,28 +6412,24 @@ msgstr "许可证"
|
||||
msgid "Job audit"
|
||||
msgstr "作业审计"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "报表"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "组织管理"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "工单评论"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "工单"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "一般设置"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "查看授权树"
|
||||
|
||||
@@ -6485,26 +6457,6 @@ msgstr "账号统计报告"
|
||||
msgid "Account automation report"
|
||||
msgstr "账号自动化报告"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "控制台"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "改密记录"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "聊天 AI 没有开启"
|
||||
@@ -7523,9 +7475,7 @@ msgstr "登录到邮件服务器的用户名。这通常是你的邮件地址"
|
||||
msgid ""
|
||||
"Password to use for the email server. It is used in conjunction with "
|
||||
"`Account` when authenticating to the email server"
|
||||
msgstr ""
|
||||
"用于电子邮件服务器的密码。在向电子邮件服务器进行身份验证时,它与`账号`一起使"
|
||||
"用"
|
||||
msgstr "用于电子邮件服务器的密码。在向电子邮件服务器进行身份验证时,它与`账号`一起使用"
|
||||
|
||||
#: settings/serializers/msg.py:41
|
||||
msgid "Sender"
|
||||
@@ -7789,11 +7739,11 @@ msgstr ""
|
||||
"码+6位数字 完成认证"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "启用登录验证码"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "开启验证码,防止机器人登录"
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9518,7 +9468,7 @@ msgstr "用户显示名称"
|
||||
msgid "Body"
|
||||
msgstr "内容"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "审批级别"
|
||||
@@ -9527,7 +9477,7 @@ msgstr "审批级别"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "工单批准信息"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "工单流程"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-08-22 18:01+0800\n"
|
||||
"POT-Creation-Date: 2025-08-21 11:01+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"
|
||||
@@ -202,7 +202,7 @@ msgstr "模板"
|
||||
msgid "Skip"
|
||||
msgstr "跳過"
|
||||
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:291
|
||||
#: accounts/const/account.py:33 audits/const.py:24 rbac/tree.py:283
|
||||
#: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6
|
||||
msgid "Update"
|
||||
msgstr "更新"
|
||||
@@ -575,8 +575,8 @@ msgstr "帳號活動"
|
||||
#: assets/serializers/platform.py:283
|
||||
#: authentication/backends/passkey/models.py:10
|
||||
#: authentication/models/ssh_key.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
#: authentication/serializers/connect_token_secret.py:174 labels/models.py:11
|
||||
#: authentication/serializers/connect_token_secret.py:115
|
||||
#: authentication/serializers/connect_token_secret.py:172 labels/models.py:11
|
||||
#: ops/mixin.py:32 ops/models/adhoc.py:19 ops/models/celery.py:15
|
||||
#: ops/models/celery.py:81 ops/models/job.py:147 ops/models/playbook.py:28
|
||||
#: ops/models/variable.py:9 ops/serializers/job.py:20
|
||||
@@ -1023,7 +1023,7 @@ msgstr "密碼規則"
|
||||
|
||||
#: accounts/models/base.py:69 assets/models/automations/base.py:28
|
||||
#: assets/models/cmd_filter.py:39 assets/models/label.py:22
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: terminal/models/applet/applet.py:41
|
||||
#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:257
|
||||
msgid "Is active"
|
||||
@@ -1195,13 +1195,13 @@ msgstr "類別"
|
||||
#: assets/serializers/platform.py:160 assets/serializers/platform.py:172
|
||||
#: audits/serializers.py:76 audits/serializers.py:196
|
||||
#: authentication/models/connection_token.py:66
|
||||
#: authentication/serializers/connect_token_secret.py:130
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: ops/models/job.py:155 perms/serializers/user_permission.py:28
|
||||
#: terminal/models/applet/applet.py:40 terminal/models/component/storage.py:58
|
||||
#: terminal/models/component/storage.py:152 terminal/serializers/applet.py:30
|
||||
#: terminal/serializers/session.py:33 terminal/serializers/storage.py:281
|
||||
#: terminal/serializers/storage.py:294 tickets/models/comment.py:26
|
||||
#: tickets/models/flow.py:43 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/flow.py:42 tickets/models/ticket/apply_application.py:16
|
||||
#: tickets/models/ticket/general.py:276 tickets/serializers/flow.py:25
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type"
|
||||
@@ -1260,7 +1260,7 @@ msgid "Spec info"
|
||||
msgstr "特殊資訊"
|
||||
|
||||
#: accounts/serializers/account/account.py:473
|
||||
#: authentication/serializers/connect_token_secret.py:164
|
||||
#: authentication/serializers/connect_token_secret.py:162
|
||||
#: authentication/templates/authentication/_access_key_modal.html:30
|
||||
#: perms/models/perm_node.py:21 users/serializers/group.py:33
|
||||
msgid "ID"
|
||||
@@ -1276,7 +1276,7 @@ msgstr "ID"
|
||||
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
|
||||
#: notifications/models/notification.py:12
|
||||
#: perms/api/user_permission/mixin.py:58 perms/models/asset_permission.py:63
|
||||
#: rbac/builtin.py:133 rbac/models/rolebinding.py:49
|
||||
#: rbac/builtin.py:127 rbac/models/rolebinding.py:49
|
||||
#: rbac/serializers/rolebinding.py:17 terminal/backends/command/models.py:16
|
||||
#: terminal/models/session/session.py:27 terminal/models/session/sharing.py:34
|
||||
#: terminal/notifications.py:157 terminal/notifications.py:217
|
||||
@@ -1817,7 +1817,7 @@ msgstr "優先度可選範圍為 1-100 (數值越小越優先)"
|
||||
#: acls/models/base.py:41 acls/serializers/base.py:57
|
||||
#: assets/models/cmd_filter.py:81 audits/models.py:99
|
||||
#: audits/serializers.py:107
|
||||
#: authentication/serializers/connect_token_secret.py:123
|
||||
#: authentication/serializers/connect_token_secret.py:121
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: perms/serializers/permission.py:63 perms/serializers/permission.py:85
|
||||
#: terminal/backends/command/models.py:24
|
||||
@@ -2472,7 +2472,7 @@ msgstr "地址"
|
||||
#: assets/models/asset/common.py:169 assets/models/platform.py:155
|
||||
#: assets/serializers/asset/common.py:150
|
||||
#: authentication/backends/passkey/models.py:12
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: perms/serializers/user_permission.py:26 xpack/plugins/cloud/models.py:398
|
||||
msgid "Platform"
|
||||
msgstr "系統平台"
|
||||
@@ -2493,7 +2493,7 @@ msgstr "節點"
|
||||
msgid "Gathered info"
|
||||
msgstr "收集資產硬體資訊"
|
||||
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:15
|
||||
#: assets/models/asset/common.py:184 assets/serializers/asset/custom.py:14
|
||||
msgid "Custom info"
|
||||
msgstr "自訂屬性"
|
||||
|
||||
@@ -2667,7 +2667,7 @@ msgstr "系統"
|
||||
#: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18
|
||||
#: assets/serializers/cagegory.py:24
|
||||
#: authentication/models/connection_token.py:34
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: authentication/serializers/connect_token_secret.py:127
|
||||
#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:40
|
||||
#: users/models/preference.py:13
|
||||
msgid "Value"
|
||||
@@ -2676,7 +2676,7 @@ msgstr "值"
|
||||
#: assets/models/label.py:40 assets/serializers/cagegory.py:10
|
||||
#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23
|
||||
#: assets/serializers/platform.py:159
|
||||
#: authentication/serializers/connect_token_secret.py:128
|
||||
#: authentication/serializers/connect_token_secret.py:126
|
||||
#: common/serializers/common.py:85 labels/serializers.py:45
|
||||
#: settings/serializers/msg.py:90 xpack/plugins/cloud/models.py:403
|
||||
msgid "Label"
|
||||
@@ -3000,7 +3000,7 @@ msgid "Disk total"
|
||||
msgstr "硬碟大小"
|
||||
|
||||
#: assets/serializers/asset/info/gathered.py:16
|
||||
#: authentication/serializers/connect_token_secret.py:119
|
||||
#: authentication/serializers/connect_token_secret.py:117
|
||||
msgid "OS"
|
||||
msgstr "操作系統"
|
||||
|
||||
@@ -3282,7 +3282,7 @@ msgstr "刪除目錄"
|
||||
|
||||
#: audits/const.py:14 audits/const.py:25
|
||||
#: authentication/templates/authentication/_access_key_modal.html:65
|
||||
#: rbac/tree.py:292
|
||||
#: rbac/tree.py:284
|
||||
msgid "Delete"
|
||||
msgstr "刪除"
|
||||
|
||||
@@ -3308,7 +3308,7 @@ msgstr "下載"
|
||||
msgid "Rename dir"
|
||||
msgstr "映射目錄"
|
||||
|
||||
#: audits/const.py:23 rbac/tree.py:290 terminal/api/session/session.py:285
|
||||
#: audits/const.py:23 rbac/tree.py:282 terminal/api/session/session.py:285
|
||||
#: terminal/templates/terminal/_msg_command_warning.html:18
|
||||
#: terminal/templates/terminal/_msg_session_sharing.html:10
|
||||
#: xpack/plugins/cloud/manager.py:102
|
||||
@@ -3317,7 +3317,7 @@ msgstr "查看"
|
||||
|
||||
#: audits/const.py:26
|
||||
#: authentication/templates/authentication/_access_key_modal.html:22
|
||||
#: rbac/tree.py:289
|
||||
#: rbac/tree.py:281
|
||||
msgid "Create"
|
||||
msgstr "創建"
|
||||
|
||||
@@ -4131,7 +4131,7 @@ msgid "Input secret"
|
||||
msgstr "自訂密碼"
|
||||
|
||||
#: authentication/models/connection_token.py:46
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
#: authentication/serializers/connect_token_secret.py:116
|
||||
#: terminal/models/applet/applet.py:43
|
||||
#: terminal/models/virtualapp/virtualapp.py:24
|
||||
#: terminal/serializers/session.py:31 terminal/serializers/session.py:58
|
||||
@@ -4241,38 +4241,38 @@ msgstr "異地登錄提醒"
|
||||
msgid "binding reminder"
|
||||
msgstr "綁定提醒"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:120
|
||||
#: authentication/serializers/connect_token_secret.py:118
|
||||
msgid "Is builtin"
|
||||
msgstr "內建的"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:124
|
||||
#: authentication/serializers/connect_token_secret.py:122
|
||||
msgid "Options"
|
||||
msgstr "選項"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:131
|
||||
#: authentication/serializers/connect_token_secret.py:129
|
||||
#: ops/notifications.py:19 rbac/tree.py:63
|
||||
msgid "Component"
|
||||
msgstr "組件"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
#: authentication/serializers/connect_token_secret.py:138
|
||||
msgid "Domain"
|
||||
msgstr "網域"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:142
|
||||
#: authentication/serializers/connect_token_secret.py:140
|
||||
msgid "Expired now"
|
||||
msgstr "立刻過期"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: authentication/serializers/connect_token_secret.py:173
|
||||
#: terminal/models/virtualapp/virtualapp.py:25
|
||||
msgid "Image name"
|
||||
msgstr "鏡像名稱"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:176
|
||||
#: authentication/serializers/connect_token_secret.py:174
|
||||
#: terminal/models/virtualapp/virtualapp.py:27
|
||||
msgid "Image port"
|
||||
msgstr "鏡像埠"
|
||||
|
||||
#: authentication/serializers/connect_token_secret.py:177
|
||||
#: authentication/serializers/connect_token_secret.py:175
|
||||
#: terminal/models/virtualapp/virtualapp.py:26
|
||||
msgid "Image protocol"
|
||||
msgstr "鏡像協議"
|
||||
@@ -6158,27 +6158,27 @@ msgstr "{} 至少有一個系統角色"
|
||||
msgid "App RBAC"
|
||||
msgstr "RBAC"
|
||||
|
||||
#: rbac/builtin.py:124
|
||||
#: rbac/builtin.py:118
|
||||
msgid "SystemAdmin"
|
||||
msgstr "系統管理員"
|
||||
|
||||
#: rbac/builtin.py:127
|
||||
#: rbac/builtin.py:121
|
||||
msgid "SystemAuditor"
|
||||
msgstr "系統審計員"
|
||||
|
||||
#: rbac/builtin.py:130
|
||||
#: rbac/builtin.py:124
|
||||
msgid "SystemComponent"
|
||||
msgstr "系統組件"
|
||||
|
||||
#: rbac/builtin.py:136
|
||||
#: rbac/builtin.py:130
|
||||
msgid "OrgAdmin"
|
||||
msgstr "組織管理員"
|
||||
|
||||
#: rbac/builtin.py:139
|
||||
#: rbac/builtin.py:133
|
||||
msgid "OrgAuditor"
|
||||
msgstr "組織審計員"
|
||||
|
||||
#: rbac/builtin.py:142
|
||||
#: rbac/builtin.py:136
|
||||
msgid "OrgUser"
|
||||
msgstr "組織用戶"
|
||||
|
||||
@@ -6214,30 +6214,6 @@ msgstr "文件管理"
|
||||
msgid "Can view System Tools"
|
||||
msgstr "可以查看系統工具"
|
||||
|
||||
#: rbac/models/menu.py:22
|
||||
msgid "Can view user login report"
|
||||
msgstr "可以查看使用者登入報告"
|
||||
|
||||
#: rbac/models/menu.py:23
|
||||
msgid "Can view user change password report"
|
||||
msgstr "可以查看使用者變更密碼報告"
|
||||
|
||||
#: rbac/models/menu.py:24
|
||||
msgid "Can view asset statistics report"
|
||||
msgstr "可以查看資產統計報告"
|
||||
|
||||
#: rbac/models/menu.py:25
|
||||
msgid "Can view asset activity report"
|
||||
msgstr "可以查看資產活動報告"
|
||||
|
||||
#: rbac/models/menu.py:26
|
||||
msgid "Can view account statistics report"
|
||||
msgstr "可以查看帳號統計報告"
|
||||
|
||||
#: rbac/models/menu.py:27
|
||||
msgid "Can view account automation report"
|
||||
msgstr "可以查看帳號自動化報告"
|
||||
|
||||
#: rbac/models/permission.py:18
|
||||
msgid "ContentType"
|
||||
msgstr "內容類型"
|
||||
@@ -6355,28 +6331,24 @@ msgstr "許可證"
|
||||
msgid "Job audit"
|
||||
msgstr "作業審核"
|
||||
|
||||
#: rbac/tree.py:71
|
||||
msgid "Report"
|
||||
msgstr "報表"
|
||||
|
||||
#: rbac/tree.py:181
|
||||
#: rbac/tree.py:173
|
||||
msgid "App organizations"
|
||||
msgstr "組織管理"
|
||||
|
||||
#: rbac/tree.py:182
|
||||
#: rbac/tree.py:174
|
||||
msgid "Ticket comment"
|
||||
msgstr "工單評論"
|
||||
|
||||
#: rbac/tree.py:183 settings/serializers/feature.py:174
|
||||
#: rbac/tree.py:175 settings/serializers/feature.py:174
|
||||
#: settings/serializers/feature.py:176 tickets/models/ticket/general.py:310
|
||||
msgid "Ticket"
|
||||
msgstr "工單管理"
|
||||
|
||||
#: rbac/tree.py:184
|
||||
#: rbac/tree.py:176
|
||||
msgid "Common setting"
|
||||
msgstr "一般設定"
|
||||
|
||||
#: rbac/tree.py:185
|
||||
#: rbac/tree.py:177
|
||||
msgid "View permission tree"
|
||||
msgstr "查看授權樹"
|
||||
|
||||
@@ -6404,26 +6376,6 @@ msgstr "賬號統計報告"
|
||||
msgid "Account automation report"
|
||||
msgstr "賬號自動化報告"
|
||||
|
||||
#: reports/views.py:44
|
||||
#, fuzzy
|
||||
#| msgid "Console"
|
||||
msgid "ConsoleDashboard"
|
||||
msgstr "控制台"
|
||||
|
||||
#: reports/views.py:48
|
||||
msgid "AuditsDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:52
|
||||
msgid "PamDashboard"
|
||||
msgstr ""
|
||||
|
||||
#: reports/views.py:56
|
||||
#, fuzzy
|
||||
#| msgid "Change secret record"
|
||||
msgid "ChangeSecretDashboard"
|
||||
msgstr "改密記錄"
|
||||
|
||||
#: settings/api/chat.py:41
|
||||
msgid "Chat AI is not enabled"
|
||||
msgstr "聊天 AI 沒有開啟"
|
||||
@@ -7676,11 +7628,11 @@ msgid ""
|
||||
msgstr "密碼和附加碼一併發送給第三方認證系統進行校驗, 如:有的第三方認證系統,需要 密碼+6位數字 完成認證"
|
||||
|
||||
#: settings/serializers/security.py:169
|
||||
msgid "Login CAPTCHA"
|
||||
msgid "Login captcha"
|
||||
msgstr "啟用登入驗證碼"
|
||||
|
||||
#: settings/serializers/security.py:170
|
||||
msgid "Enable CAPTCHA to prevent robot authentication"
|
||||
msgid "Enable captcha to prevent robot authentication"
|
||||
msgstr "開啟驗證碼,防止機器人登錄"
|
||||
|
||||
#: settings/serializers/security.py:173
|
||||
@@ -9360,7 +9312,7 @@ msgstr "用戶顯示名稱"
|
||||
msgid "Body"
|
||||
msgstr "內容"
|
||||
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:48
|
||||
#: tickets/models/flow.py:21 tickets/models/flow.py:47
|
||||
#: tickets/models/ticket/general.py:45
|
||||
msgid "Approve level"
|
||||
msgstr "審批級別"
|
||||
@@ -9369,7 +9321,7 @@ msgstr "審批級別"
|
||||
msgid "Ticket flow approval rule"
|
||||
msgstr "工單批准資訊"
|
||||
|
||||
#: tickets/models/flow.py:53
|
||||
#: tickets/models/flow.py:52
|
||||
msgid "Ticket flow"
|
||||
msgstr "工單流程"
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"Aliyun": "Alibaba cloud",
|
||||
"All": "All",
|
||||
"AllAccountTip": "All accounts already added on the asset",
|
||||
"AllAccounts": "All accounts",
|
||||
"AllAccounts": "All existing accounts",
|
||||
"AllClickRead": "Mark all as read",
|
||||
"AllMembers": "All members",
|
||||
"AllowInvalidCert": "Ignore certificate check",
|
||||
@@ -575,7 +575,6 @@
|
||||
"Exclude": "Does not include",
|
||||
"ExcludeAsset": "Skipped assets",
|
||||
"ExcludeSymbol": "Exclude char",
|
||||
"ExcludeAccount": "Exclude accounts",
|
||||
"ExecCloudSyncErrorMsg": "The cloud account configuration is incomplete, please update and try again.",
|
||||
"Execute": "Execute",
|
||||
"ExecuteAfterSaving": "Execute after saving",
|
||||
|
||||
@@ -574,7 +574,6 @@
|
||||
"Exclude": "不包含",
|
||||
"ExcludeAsset": "跳过的资产",
|
||||
"ExcludeSymbol": "排除字符",
|
||||
"ExcludeAccount": "排除账号",
|
||||
"ExecCloudSyncErrorMsg": "云账号配置不完整,请更新后重试",
|
||||
"Execute": "执行",
|
||||
"ExecuteAfterSaving": "保存后执行",
|
||||
@@ -1625,8 +1624,5 @@
|
||||
"removeWarningMsg": "你确定要移除",
|
||||
"setVariable": "设置参数",
|
||||
"userId": "用户ID",
|
||||
"userName": "用户名",
|
||||
"ExportAsPDF": "导出 PDF",
|
||||
"EMailReport": "发送邮件报告",
|
||||
"Print": "打印"
|
||||
"userName": "用户名"
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
from .aggregate import *
|
||||
from .dashboard import IndexApi
|
||||
from .health import PrometheusMetricsApi, HealthCheckView
|
||||
from .search import GlobalSearchView
|
||||
@@ -1,6 +1,6 @@
|
||||
# views.py
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.views import APIView
|
||||
|
||||
@@ -27,49 +27,47 @@ object_params = [
|
||||
class ResourceDetailApi(ProxyMixin, APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="get_resource_detail",
|
||||
summary="Get resource detail",
|
||||
parameters=object_params,
|
||||
description="""
|
||||
operation_summary="Get resource detail",
|
||||
manual_parameters=object_params,
|
||||
operation_description="""
|
||||
Get resource detail.
|
||||
{resource} is the resource name, GET /api/v1/resources/ to get full supported resource.
|
||||
""",
|
||||
)
|
||||
|
||||
""", )
|
||||
def get(self, request, resource, pk=None):
|
||||
return self._proxy(request, resource, pk=pk, action='retrieve')
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="delete_resource",
|
||||
summary="Delete the resource",
|
||||
parameters=object_params,
|
||||
description="Delete the resource, and can not be restored",
|
||||
operation_summary="Delete the resource ",
|
||||
manual_parameters=object_params,
|
||||
operation_description="Delete the resource, and can not be restored",
|
||||
)
|
||||
def delete(self, request, resource, pk=None):
|
||||
return self._proxy(request, resource, pk, action='destroy')
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="update_resource",
|
||||
summary="Update the resource property",
|
||||
parameters=object_params,
|
||||
description="""
|
||||
operation_summary="Update the resource property",
|
||||
manual_parameters=object_params,
|
||||
operation_description="""
|
||||
Update the resource property, all property will be update,
|
||||
{resource} is the resource name, GET /api/v1/resources/ to get full supported resource.
|
||||
|
||||
OPTION /api/v1/resources/{resource}/{id}/?action=put to get field type and helptext.
|
||||
""",
|
||||
)
|
||||
""")
|
||||
def put(self, request, resource, pk=None):
|
||||
return self._proxy(request, resource, pk, action='update')
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="partial_update_resource",
|
||||
summary="Update the resource property",
|
||||
parameters=object_params,
|
||||
description="""
|
||||
operation_summary="Update the resource property",
|
||||
manual_parameters=object_params,
|
||||
operation_description="""
|
||||
Partial update the resource property, only request property will be update,
|
||||
OPTION /api/v1/resources/{resource}/{id}/?action=patch to get field type and helptext.
|
||||
""",
|
||||
)
|
||||
""")
|
||||
def patch(self, request, resource, pk=None):
|
||||
return self._proxy(request, resource, pk, action='partial_update')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# views.py
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from rest_framework.views import APIView
|
||||
|
||||
@@ -50,34 +51,32 @@ list_schema = {
|
||||
}
|
||||
}
|
||||
|
||||
from drf_spectacular.openapi import OpenApiResponse, OpenApiExample
|
||||
list_response = openapi.Response("Resource list response", schema=openapi.Schema(**list_schema))
|
||||
|
||||
|
||||
class ResourceListApi(ProxyMixin, APIView):
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="get_resource_list",
|
||||
summary="Get resource list",
|
||||
parameters=list_params,
|
||||
responses={200: OpenApiResponse(description="Resource list response")},
|
||||
description="""
|
||||
operation_summary="Get resource list",
|
||||
manual_parameters=list_params,
|
||||
responses={200: list_response},
|
||||
operation_description="""
|
||||
Get resource list, you should set the resource name in the url.
|
||||
OPTIONS /api/v1/resources/{resource}/?action=get to get every type resource's field type and help text.
|
||||
""",
|
||||
)
|
||||
""", )
|
||||
# ↓↓↓ Swagger 自动文档 ↓↓↓
|
||||
def get(self, request, resource):
|
||||
return self._proxy(request, resource)
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="create_resource_by_type",
|
||||
summary="Create resource",
|
||||
parameters=create_params,
|
||||
description="""
|
||||
operation_summary="Create resource",
|
||||
manual_parameters=create_params,
|
||||
operation_description="""
|
||||
Create resource,
|
||||
OPTIONS /api/v1/resources/{resource}/?action=post to get every resource type field type and helptext, and
|
||||
you will know how to create it.
|
||||
""",
|
||||
)
|
||||
""")
|
||||
def post(self, request, resource, pk=None):
|
||||
if not resource:
|
||||
resource = request.data.pop('resource', '')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# views.py
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework import serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
@@ -25,10 +25,10 @@ class ResourceTypeResourceSerializer(serializers.Serializer):
|
||||
class ResourceTypeListApi(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
@swagger_auto_schema(
|
||||
operation_id="get_supported_resources",
|
||||
summary="Get-all-support-resources",
|
||||
description="Get all support resources, name, path, verbose_name description",
|
||||
operation_summary="Get-all-support-resources",
|
||||
operation_description="Get all support resources, name, path, verbose_name description",
|
||||
responses={200: ResourceTypeResourceSerializer(many=True)}, # Specify the response serializer
|
||||
)
|
||||
def get(self, request):
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Dict
|
||||
|
||||
from django.urls import URLPattern
|
||||
from django.urls import URLResolver
|
||||
from drf_spectacular.utils import OpenApiParameter
|
||||
from drf_yasg import openapi
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
@@ -114,8 +114,8 @@ def extract_resource_paths(urlpatterns, prefix='/api/v1/') -> Dict[str, Dict[str
|
||||
|
||||
|
||||
def param_dic_to_param(d):
|
||||
return OpenApiParameter(
|
||||
name=d['name'], location=d['in'],
|
||||
return openapi.Parameter(
|
||||
d['name'], d['in'],
|
||||
description=d['description'], type=d['type'], required=d.get('required', False)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import re
|
||||
|
||||
from django.contrib.postgres.search import SearchVector
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
|
||||
class GlobalSearchView(APIView):
|
||||
limits = 5
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_models(self):
|
||||
from users.models import User, UserGroup
|
||||
from assets.models import Asset
|
||||
from accounts.models import Account
|
||||
from perms.models import AssetPermission
|
||||
return [
|
||||
[User, ['name', 'username']],
|
||||
[UserGroup, ['name', 'comment']],
|
||||
[Asset, ['name', 'address']],
|
||||
[Account, ['name', 'username']],
|
||||
[AssetPermission, ['name', 'comment']],
|
||||
]
|
||||
|
||||
def search_model(self, model, fields, keyword):
|
||||
queryset = model.objects.all()
|
||||
if hasattr(model, 'get_queryset'):
|
||||
queryset = model.get_queryset()
|
||||
|
||||
if settings.DB_ENGINE == 'postgres':
|
||||
qs = model.objects.annotate(
|
||||
search=SearchVector(*fields),
|
||||
).filter(search=keyword)
|
||||
else:
|
||||
q = Q()
|
||||
for field in fields:
|
||||
q |= Q(**{field + '__icontains': keyword})
|
||||
qs = queryset.filter(q)
|
||||
return qs[:self.limits]
|
||||
|
||||
def get_result(self, model, fields, item, keyword):
|
||||
d = {
|
||||
"id": item.id, "name": item.name,
|
||||
"model": model.__name__, "model_label": model._meta.verbose_name,
|
||||
}
|
||||
content = ""
|
||||
value_list = [item.name]
|
||||
|
||||
for field in fields:
|
||||
field_label = model._meta.get_field(field).verbose_name
|
||||
value = getattr(item, field)
|
||||
|
||||
if value in value_list:
|
||||
continue
|
||||
value_list.append(value)
|
||||
|
||||
if content:
|
||||
continue
|
||||
content += f"{field_label}: {value} "
|
||||
|
||||
display = str(item).replace(item.name, '').replace('(', '').replace(')', '')
|
||||
if display not in value:
|
||||
content += f" {display} "
|
||||
|
||||
d["content"] = content
|
||||
return d
|
||||
|
||||
def get(self, request):
|
||||
q = request.query_params.get("q", "").strip()
|
||||
models = self.get_models()
|
||||
results = []
|
||||
|
||||
for model, fields in models:
|
||||
perm = model._meta.app_label + '.' + 'view_' + model._meta.model_name
|
||||
if not request.user.has_perm(perm):
|
||||
continue
|
||||
qs = self.search_model(model, fields, q)
|
||||
for item in qs:
|
||||
d = self.get_result(model, fields, item, q)
|
||||
results.append(d)
|
||||
|
||||
return Response(results)
|
||||
@@ -693,7 +693,6 @@ class Config(dict):
|
||||
|
||||
# API 分页
|
||||
'MAX_LIMIT_PER_PAGE': 10000,
|
||||
'DEFAULT_PAGE_SIZE': 10,
|
||||
|
||||
'LIMIT_SUPER_PRIV': False,
|
||||
|
||||
|
||||
@@ -15,15 +15,6 @@ class MaxLimitOffsetPagination(LimitOffsetPagination):
|
||||
def paginate_queryset(self, queryset, request, view=None):
|
||||
if view and hasattr(view, 'page_max_limit'):
|
||||
self.max_limit = view.page_max_limit
|
||||
|
||||
# 自定义的 api view,就默认不约束分页了
|
||||
if getattr(view, 'action', None) != 'list' and not getattr(view, 'default_limit', None):
|
||||
self.default_limit = None
|
||||
|
||||
if view and hasattr(view, 'page_default_limit'):
|
||||
self.default_limit = view.page_default_limit
|
||||
if view and hasattr(view, 'default_limit'):
|
||||
self.default_limit = view.default_limit
|
||||
|
||||
return super().paginate_queryset(queryset, request, view)
|
||||
|
||||
|
||||
@@ -138,8 +138,7 @@ INSTALLED_APPS = [
|
||||
'labels.apps.LabelsConfig',
|
||||
'reports.apps.ReportsConfig',
|
||||
'rest_framework',
|
||||
'drf_spectacular',
|
||||
'drf_spectacular_sidecar',
|
||||
'drf_yasg',
|
||||
'django_cas_ng',
|
||||
'channels',
|
||||
'django_filters',
|
||||
|
||||
@@ -225,7 +225,7 @@ SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key'
|
||||
OPERATE_LOG_ELASTICSEARCH_CONFIG = CONFIG.OPERATE_LOG_ELASTICSEARCH_CONFIG
|
||||
|
||||
MAX_LIMIT_PER_PAGE = CONFIG.MAX_LIMIT_PER_PAGE
|
||||
DEFAULT_PAGE_SIZE = CONFIG.DEAFULT_LIMIT_PER_PAGE
|
||||
DEFAULT_PAGE_SIZE = CONFIG.MAX_LIMIT_PER_PAGE
|
||||
PERM_TREE_REGEN_INTERVAL = CONFIG.PERM_TREE_REGEN_INTERVAL
|
||||
|
||||
# Magnus DB Port
|
||||
|
||||
@@ -49,44 +49,21 @@ REST_FRAMEWORK = {
|
||||
'DEFAULT_PAGINATION_CLASS': 'jumpserver.rewriting.pagination.MaxLimitOffsetPagination',
|
||||
'PAGE_SIZE': CONFIG.DEFAULT_PAGE_SIZE,
|
||||
'EXCEPTION_HANDLER': 'common.drf.exc_handlers.common_exception_handler',
|
||||
'DEFAULT_SCHEMA_CLASS': 'jumpserver.views.schema.CustomAutoSchema',
|
||||
}
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'TITLE': 'JumpServer API Docs',
|
||||
'DESCRIPTION': 'JumpServer Restful api docs',
|
||||
'VERSION': 'v1',
|
||||
'LICENSE': {
|
||||
'name': 'GPLv3 License',
|
||||
'url': 'https://www.gnu.org/licenses/gpl-3.0.html',
|
||||
SWAGGER_SETTINGS = {
|
||||
'DEFAULT_AUTO_SCHEMA_CLASS': 'jumpserver.views.swagger.CustomSwaggerAutoSchema',
|
||||
'USE_SESSION_AUTH': True,
|
||||
'SECURITY_DEFINITIONS': {
|
||||
'Bearer': {
|
||||
'type': 'apiKey',
|
||||
'name': 'Authorization',
|
||||
'in': 'header'
|
||||
}
|
||||
},
|
||||
'CONTACT': {
|
||||
'name': 'JumpServer',
|
||||
'url': 'https://jumpserver.org',
|
||||
'email': 'support@jumpserver.org',
|
||||
},
|
||||
"SERVE_INCLUDE_SCHEMA": False,
|
||||
'SERVE_PUBLIC': True,
|
||||
'BASE_PATH': '/api/v1/',
|
||||
'SCHEMA_PATH_PREFIX': '/api/v1/',
|
||||
'SWAGGER_UI_DIST': 'SIDECAR',
|
||||
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
||||
'SWAGGER_UI_OAUTH2_REDIRECT_URL': 'SIDECAR',
|
||||
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
|
||||
'DEFAULT_GENERATOR_CLASS': 'jumpserver.views.schema.CustomSchemaGenerator',
|
||||
'SWAGGER_UI_SETTINGS': {
|
||||
'persistAuthorization': True,
|
||||
'displayOperationId': True,
|
||||
},
|
||||
# 添加自定义字段扩展
|
||||
'SERIALIZER_EXTENSIONS': [
|
||||
'jumpserver.views.schema.ObjectRelatedFieldExtension',
|
||||
'jumpserver.views.schema.LabeledChoiceFieldExtension',
|
||||
'jumpserver.views.schema.BitChoicesFieldExtension',
|
||||
'jumpserver.views.schema.LabelRelatedFieldExtension',
|
||||
],
|
||||
'SECURITY': [{'Bearer': []}],
|
||||
'DEFAULT_INFO': 'jumpserver.views.swagger.api_info',
|
||||
}
|
||||
|
||||
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
|
||||
CAPTCHA_IMAGE_SIZE = (180, 38)
|
||||
CAPTCHA_FOREGROUND_COLOR = '#001100'
|
||||
|
||||
@@ -38,7 +38,6 @@ api_v1 = resource_api + [
|
||||
path('resources/', api.ResourceTypeListApi.as_view(), name='resource-list'),
|
||||
path('resources/<str:resource>/', api.ResourceListApi.as_view()),
|
||||
path('resources/<str:resource>/<str:pk>/', api.ResourceDetailApi.as_view()),
|
||||
path('search/', api.GlobalSearchView.as_view()),
|
||||
]
|
||||
|
||||
app_view_patterns = [
|
||||
@@ -95,10 +94,9 @@ cache_kwargs = {
|
||||
}
|
||||
# docs 路由
|
||||
urlpatterns += [
|
||||
path('api/swagger.json', views.get_swagger_view(ui='json', **cache_kwargs), name='schema-json'),
|
||||
path('api/swagger.yaml', views.get_swagger_view(ui='yaml', **cache_kwargs), name='schema'),
|
||||
re_path('api/docs/?', views.get_swagger_view(ui='swagger', **cache_kwargs), name="docs"),
|
||||
re_path('api/redoc/?', views.get_swagger_view(ui='redoc', **cache_kwargs), name='redoc'),
|
||||
path('api/swagger.<format>', views.get_swagger_view().without_ui(**cache_kwargs), name='schema-json'),
|
||||
re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', **cache_kwargs), name="docs"),
|
||||
re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', **cache_kwargs), name='redoc'),
|
||||
]
|
||||
|
||||
if os.environ.get('DEBUG_TOOLBAR', False):
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
import re
|
||||
|
||||
from drf_spectacular.openapi import AutoSchema
|
||||
from drf_spectacular.generators import SchemaGenerator
|
||||
|
||||
|
||||
class CustomSchemaGenerator(SchemaGenerator):
|
||||
from_mcp = False
|
||||
|
||||
def get_schema(self, request=None, public=False):
|
||||
self.from_mcp = request.query_params.get('mcp') or request.path.endswith('swagger.json')
|
||||
return super().get_schema(request, public)
|
||||
|
||||
|
||||
class CustomAutoSchema(AutoSchema):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.from_mcp = kwargs.get('from_mcp', False)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def map_parsers(self):
|
||||
return ['application/json']
|
||||
|
||||
def map_renderers(self, *args, **kwargs):
|
||||
return ['application/json']
|
||||
|
||||
def get_tags(self):
|
||||
operation_keys = self._tokenize_path()
|
||||
if len(operation_keys) == 1:
|
||||
return []
|
||||
tags = ['_'.join(operation_keys[:2])]
|
||||
return tags
|
||||
|
||||
def get_operation(self, path, *args, **kwargs):
|
||||
if path.endswith('render-to-json/'):
|
||||
return None
|
||||
# if not path.startswith('/api/v1/users'):
|
||||
# return None
|
||||
operation = super().get_operation(path, *args, **kwargs)
|
||||
if not operation:
|
||||
return operation
|
||||
|
||||
if not operation.get('summary', ''):
|
||||
operation['summary'] = operation.get('operationId')
|
||||
|
||||
return operation
|
||||
|
||||
def get_operation_id(self):
|
||||
tokenized_path = self._tokenize_path()
|
||||
# replace dashes as they can be problematic later in code generation
|
||||
tokenized_path = [t.replace('-', '_') for t in tokenized_path]
|
||||
|
||||
action = ''
|
||||
if hasattr(self.view, 'action'):
|
||||
action = self.view.action
|
||||
|
||||
if not action:
|
||||
if self.method == 'GET' and self._is_list_view():
|
||||
action = 'list'
|
||||
else:
|
||||
action = self.method_mapping[self.method.lower()]
|
||||
|
||||
if action == "bulk_destroy":
|
||||
action = "bulk_delete"
|
||||
|
||||
if not tokenized_path:
|
||||
tokenized_path.append('root')
|
||||
|
||||
if re.search(r'<drf_format_suffix\w*:\w+>', self.path_regex):
|
||||
tokenized_path.append('formatted')
|
||||
|
||||
return '_'.join(tokenized_path + [action])
|
||||
|
||||
def get_filter_parameters(self):
|
||||
if not self.should_filter():
|
||||
return []
|
||||
|
||||
fields = []
|
||||
if hasattr(self.view, 'get_filter_backends'):
|
||||
backends = self.view.get_filter_backends()
|
||||
elif hasattr(self.view, 'filter_backends'):
|
||||
backends = self.view.filter_backends
|
||||
else:
|
||||
backends = []
|
||||
for filter_backend in backends:
|
||||
fields += self.probe_inspectors(
|
||||
self.filter_inspectors, 'get_filter_parameters', filter_backend()
|
||||
) or []
|
||||
return fields
|
||||
|
||||
def get_auth(self):
|
||||
return [{'Bearer': []}]
|
||||
|
||||
def get_operation_security(self):
|
||||
"""
|
||||
重写操作安全配置,统一使用 Bearer token
|
||||
"""
|
||||
return [{'Bearer': []}]
|
||||
|
||||
def get_components_security_schemes(self):
|
||||
"""
|
||||
重写安全方案定义,避免认证类解析错误
|
||||
"""
|
||||
return {
|
||||
'Bearer': {
|
||||
'type': 'http',
|
||||
'scheme': 'bearer',
|
||||
'bearerFormat': 'JWT',
|
||||
'description': 'JWT token for API authentication'
|
||||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def exclude_some_paths(path):
|
||||
# 这里可以对 paths 进行处理
|
||||
excludes = [
|
||||
'/report/', '/render-to-json/', '/suggestions/',
|
||||
'executions', 'automations', 'change-secret-records',
|
||||
'change-secret-dashboard', '/copy-to-assets/',
|
||||
'/move-to-assets/', 'dashboard', 'index', 'countries',
|
||||
'/resources/cache/', 'profile/mfa', 'profile/password',
|
||||
'profile/permissions', 'prometheus', 'constraints'
|
||||
]
|
||||
for p in excludes:
|
||||
if path.find(p) >= 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def exclude_some_app_model(self, path):
|
||||
parts = path.split('/')
|
||||
if len(parts) < 5:
|
||||
return False
|
||||
|
||||
apps = []
|
||||
if self.from_mcp:
|
||||
apps = [
|
||||
'ops', 'tickets', 'authentication',
|
||||
'settings', 'xpack', 'terminal', 'rbac',
|
||||
'notifications', 'promethues', 'acls'
|
||||
]
|
||||
|
||||
app_name = parts[3]
|
||||
if app_name in apps:
|
||||
return True
|
||||
models = []
|
||||
model = parts[4]
|
||||
if self.from_mcp:
|
||||
models = [
|
||||
'users', 'user-groups', 'users-groups-relations', 'assets', 'hosts', 'devices', 'databases',
|
||||
'webs', 'clouds', 'gpts', 'ds', 'customs', 'platforms', 'nodes', 'zones', 'gateways',
|
||||
'protocol-settings', 'labels', 'virtual-accounts', 'gathered-accounts', 'account-templates',
|
||||
'account-template-secrets', 'account-backups', 'account-backup-executions',
|
||||
'change-secret-automations', 'change-secret-executions', 'change-secret-records',
|
||||
'gather-account-automations', 'gather-account-executions', 'push-account-automations',
|
||||
'push-account-executions', 'push-account-records', 'check-account-automations',
|
||||
'check-account-executions', 'account-risks', 'integration-apps', 'asset-permissions',
|
||||
'asset-permissions-users-relations', 'asset-permissions-user-groups-relations',
|
||||
'asset-permissions-assets-relations', 'asset-permissions-nodes-relations', 'terminal-status',
|
||||
'terminals', 'tasks', 'status', 'replay-storages', 'command-storages', 'session-sharing-records',
|
||||
'endpoints', 'endpoint-rules', 'applets', 'applet-hosts', 'applet-publications',
|
||||
'applet-host-deployments', 'virtual-apps', 'app-providers', 'virtual-app-publications',
|
||||
'celery-period-tasks', 'task-executions', 'adhocs', 'playbooks', 'variables', 'ftp-logs',
|
||||
'login-logs', 'operate-logs', 'password-change-logs', 'job-logs', 'jobs', 'user-sessions',
|
||||
'service-access-logs', 'chatai-prompts', 'super-connection-tokens', 'flows',
|
||||
'apply-assets', 'apply-nodes', 'login-acls', 'login-asset-acls', 'command-filter-acls',
|
||||
'command-groups', 'connect-method-acls', 'system-msg-subscriptions', 'roles', 'role-bindings',
|
||||
'system-roles', 'system-role-bindings', 'org-roles', 'org-role-bindings', 'content-types',
|
||||
'labeled-resources', 'account-backup-plans', 'account-check-engines', 'account-secrets',
|
||||
'change-secret', 'integration-applications', 'push-account', 'directories', 'connection-token',
|
||||
'groups', 'accounts', 'resource-types', 'favorite-assets', 'activities', 'platform-automation-methods',
|
||||
]
|
||||
if model in models:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_excluded(self):
|
||||
if self.exclude_some_paths(self.path):
|
||||
return True
|
||||
if self.exclude_some_app_model(self.path):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_operation(self, path, *args, **kwargs):
|
||||
operation = super().get_operation(path, *args, **kwargs)
|
||||
if not operation:
|
||||
return operation
|
||||
|
||||
operation_id = operation.get('operationId')
|
||||
if 'bulk' in operation_id:
|
||||
return None
|
||||
|
||||
if not operation.get('summary', ''):
|
||||
operation['summary'] = operation.get('operationId')
|
||||
|
||||
exclude_operations = [
|
||||
'orgs_orgs_read', 'orgs_orgs_update', 'orgs_orgs_delete',
|
||||
'orgs_orgs_create', 'orgs_orgs_partial_update',
|
||||
]
|
||||
if operation_id in exclude_operations:
|
||||
return None
|
||||
return operation
|
||||
|
||||
# 添加自定义字段的 OpenAPI 扩展
|
||||
from drf_spectacular.extensions import OpenApiSerializerFieldExtension
|
||||
from drf_spectacular.openapi import AutoSchema
|
||||
from drf_spectacular.plumbing import build_basic_type
|
||||
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField, BitChoicesField
|
||||
|
||||
|
||||
class ObjectRelatedFieldExtension(OpenApiSerializerFieldExtension):
|
||||
"""
|
||||
为 ObjectRelatedField 提供 OpenAPI schema
|
||||
"""
|
||||
target_class = ObjectRelatedField
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
field = self.target
|
||||
|
||||
# 获取字段的基本信息
|
||||
field_type = 'array' if field.many else 'object'
|
||||
|
||||
if field_type == 'array':
|
||||
# 如果是多对多关系
|
||||
return {
|
||||
'type': 'array',
|
||||
'items': self._get_openapi_item_schema(field),
|
||||
'description': getattr(field, 'help_text', ''),
|
||||
'title': getattr(field, 'label', ''),
|
||||
}
|
||||
else:
|
||||
# 如果是一对一关系
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': self._get_openapi_properties_schema(field),
|
||||
'description': getattr(field, 'help_text', ''),
|
||||
'title': getattr(field, 'label', ''),
|
||||
}
|
||||
|
||||
def _get_openapi_item_schema(self, field):
|
||||
"""
|
||||
获取数组项的 OpenAPI schema
|
||||
"""
|
||||
return self._get_openapi_object_schema(field)
|
||||
|
||||
def _get_openapi_object_schema(self, field):
|
||||
"""
|
||||
获取对象的 OpenAPI schema
|
||||
"""
|
||||
properties = {}
|
||||
|
||||
# 动态分析 attrs 中的属性类型
|
||||
for attr in field.attrs:
|
||||
# 尝试从 queryset 的 model 中获取字段信息
|
||||
field_type = self._infer_field_type(field, attr)
|
||||
properties[attr] = {
|
||||
'type': field_type,
|
||||
'description': f'{attr} field'
|
||||
}
|
||||
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': properties,
|
||||
'required': ['id'] if 'id' in field.attrs else []
|
||||
}
|
||||
|
||||
def _infer_field_type(self, field, attr_name):
|
||||
"""
|
||||
智能推断字段类型
|
||||
"""
|
||||
try:
|
||||
# 如果有 queryset,尝试从 model 中获取字段信息
|
||||
if hasattr(field, 'queryset') and field.queryset is not None:
|
||||
model = field.queryset.model
|
||||
if hasattr(model, '_meta') and hasattr(model._meta, 'fields'):
|
||||
model_field = model._meta.get_field(attr_name)
|
||||
if model_field:
|
||||
return self._map_django_field_type(model_field)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 如果没有 queryset 或无法获取字段信息,使用启发式规则
|
||||
return self._heuristic_field_type(attr_name)
|
||||
|
||||
def _map_django_field_type(self, model_field):
|
||||
"""
|
||||
将 Django 字段类型映射到 OpenAPI 类型
|
||||
"""
|
||||
field_type = type(model_field).__name__
|
||||
|
||||
# 整数类型
|
||||
if 'Integer' in field_type or 'BigInteger' in field_type or 'SmallInteger' in field_type:
|
||||
return 'integer'
|
||||
# 浮点数类型
|
||||
elif 'Float' in field_type or 'Decimal' in field_type:
|
||||
return 'number'
|
||||
# 布尔类型
|
||||
elif 'Boolean' in field_type:
|
||||
return 'boolean'
|
||||
# 日期时间类型
|
||||
elif 'DateTime' in field_type or 'Date' in field_type or 'Time' in field_type:
|
||||
return 'string'
|
||||
# 文件类型
|
||||
elif 'File' in field_type or 'Image' in field_type:
|
||||
return 'string'
|
||||
# 其他类型默认为字符串
|
||||
else:
|
||||
return 'string'
|
||||
|
||||
def _heuristic_field_type(self, attr_name):
|
||||
"""
|
||||
启发式推断字段类型
|
||||
"""
|
||||
# 基于属性名的启发式规则
|
||||
|
||||
if attr_name in ['is_active', 'enabled', 'visible'] or attr_name.startswith('is_'):
|
||||
return 'boolean'
|
||||
elif attr_name in ['count', 'number', 'size', 'amount']:
|
||||
return 'integer'
|
||||
elif attr_name in ['price', 'rate', 'percentage']:
|
||||
return 'number'
|
||||
else:
|
||||
# 默认返回字符串类型
|
||||
return 'string'
|
||||
|
||||
def _get_openapi_properties_schema(self, field):
|
||||
"""
|
||||
获取对象属性的 OpenAPI schema
|
||||
"""
|
||||
return self._get_openapi_object_schema(field)['properties']
|
||||
|
||||
|
||||
class LabeledChoiceFieldExtension(OpenApiSerializerFieldExtension):
|
||||
"""
|
||||
为 LabeledChoiceField 提供 OpenAPI schema
|
||||
"""
|
||||
target_class = LabeledChoiceField
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
field = self.target
|
||||
|
||||
if getattr(field, 'many', False):
|
||||
return {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'value': {'type': 'string'},
|
||||
'label': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'description': getattr(field, 'help_text', ''),
|
||||
'title': getattr(field, 'label', ''),
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'value': {'type': 'string'},
|
||||
'label': {'type': 'string'}
|
||||
},
|
||||
'description': getattr(field, 'help_text', ''),
|
||||
'title': getattr(field, 'label', ''),
|
||||
}
|
||||
|
||||
|
||||
class BitChoicesFieldExtension(OpenApiSerializerFieldExtension):
|
||||
"""
|
||||
为 BitChoicesField 提供 OpenAPI schema
|
||||
"""
|
||||
target_class = BitChoicesField
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
field = self.target
|
||||
|
||||
return {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'value': {'type': 'string'},
|
||||
'label': {'type': 'string'}
|
||||
}
|
||||
},
|
||||
'description': getattr(field, 'help_text', ''),
|
||||
'title': getattr(field, 'label', ''),
|
||||
}
|
||||
|
||||
|
||||
class LabelRelatedFieldExtension(OpenApiSerializerFieldExtension):
|
||||
"""
|
||||
为 LabelRelatedField 提供 OpenAPI schema
|
||||
"""
|
||||
target_class = 'common.serializers.fields.LabelRelatedField'
|
||||
|
||||
def map_serializer_field(self, auto_schema, direction):
|
||||
field = self.target
|
||||
|
||||
# LabelRelatedField 返回一个包含 id, name, value, color 的对象
|
||||
return {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'string',
|
||||
'description': 'Label ID'
|
||||
},
|
||||
'name': {
|
||||
'type': 'string',
|
||||
'description': 'Label name'
|
||||
},
|
||||
'value': {
|
||||
'type': 'string',
|
||||
'description': 'Label value'
|
||||
},
|
||||
'color': {
|
||||
'type': 'string',
|
||||
'description': 'Label color'
|
||||
}
|
||||
},
|
||||
'required': ['id', 'name', 'value'],
|
||||
'description': getattr(field, 'help_text', 'Label information'),
|
||||
'title': getattr(field, 'label', 'Label'),
|
||||
}
|
||||
@@ -1,57 +1,176 @@
|
||||
import os
|
||||
|
||||
from drf_spectacular.views import (
|
||||
SpectacularSwaggerView, SpectacularRedocView,
|
||||
SpectacularYAMLAPIView, SpectacularJSONAPIView
|
||||
)
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.utils.decorators import method_decorator
|
||||
from rest_framework.response import Response
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.generators import OpenAPISchemaGenerator
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||
from drf_yasg.views import get_schema_view
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class SwaggerUI(LoginRequiredMixin, SpectacularSwaggerView):
|
||||
pass
|
||||
class CustomSchemaGenerator(OpenAPISchemaGenerator):
|
||||
from_mcp = False
|
||||
|
||||
def get_schema(self, request=None, public=False):
|
||||
self.from_mcp = request.query_params.get('mcp') or request.path.endswith('swagger.json')
|
||||
return super().get_schema(request, public)
|
||||
|
||||
class Redoc(LoginRequiredMixin, SpectacularRedocView):
|
||||
pass
|
||||
|
||||
|
||||
class SchemeMixin:
|
||||
def get(self, request, *args, **kwargs):
|
||||
schema = super().get(request, *args, **kwargs).data
|
||||
host = request.get_host()
|
||||
schema['servers'] = [
|
||||
{"url": f"https://{host}", "description": "HTTPS Server"},
|
||||
{"url": f"http://{host}", "description": "HTTP Server"},
|
||||
@staticmethod
|
||||
def exclude_some_paths(path):
|
||||
# 这里可以对 paths 进行处理
|
||||
excludes = [
|
||||
'/report/', '/render-to-json/', '/suggestions/',
|
||||
'executions', 'automations', 'change-secret-records',
|
||||
'change-secret-dashboard', '/copy-to-assets/',
|
||||
'/move-to-assets/', 'dashboard', 'index', 'countries',
|
||||
'/resources/cache/', 'profile/mfa', 'profile/password',
|
||||
'profile/permissions', 'prometheus', 'constraints'
|
||||
]
|
||||
if request.scheme == 'http':
|
||||
schema['servers'] = schema['servers'][::-1]
|
||||
for p in excludes:
|
||||
if path.find(p) >= 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
schema['components']['securitySchemes'] = {
|
||||
'Bearer': {
|
||||
'type': 'http',
|
||||
'scheme': 'bearer',
|
||||
'bearerFormat': 'JWT',
|
||||
}
|
||||
}
|
||||
return Response(schema)
|
||||
def exclude_some_app_model(self, path):
|
||||
parts = path.split('/')
|
||||
if len(parts) < 5:
|
||||
return False
|
||||
|
||||
@method_decorator(cache_page(60 * 5,), name="dispatch")
|
||||
class JsonApi(SchemeMixin, SpectacularJSONAPIView):
|
||||
pass
|
||||
apps = []
|
||||
if self.from_mcp:
|
||||
apps = [
|
||||
'ops', 'tickets', 'authentication',
|
||||
'settings', 'xpack', 'terminal', 'rbac',
|
||||
'notifications', 'promethues', 'acls'
|
||||
]
|
||||
|
||||
@method_decorator(cache_page(60 * 5,), name="dispatch")
|
||||
class YamlApi(SchemeMixin, SpectacularYAMLAPIView):
|
||||
pass
|
||||
app_name = parts[3]
|
||||
if app_name in apps:
|
||||
return True
|
||||
models = []
|
||||
model = parts[4]
|
||||
if self.from_mcp:
|
||||
models = [
|
||||
'users', 'user-groups', 'users-groups-relations', 'assets', 'hosts', 'devices', 'databases',
|
||||
'webs', 'clouds', 'gpts', 'ds', 'customs', 'platforms', 'nodes', 'zones', 'gateways',
|
||||
'protocol-settings', 'labels', 'virtual-accounts', 'gathered-accounts', 'account-templates',
|
||||
'account-template-secrets', 'account-backups', 'account-backup-executions',
|
||||
'change-secret-automations', 'change-secret-executions', 'change-secret-records',
|
||||
'gather-account-automations', 'gather-account-executions', 'push-account-automations',
|
||||
'push-account-executions', 'push-account-records', 'check-account-automations',
|
||||
'check-account-executions', 'account-risks', 'integration-apps', 'asset-permissions',
|
||||
'asset-permissions-users-relations', 'asset-permissions-user-groups-relations',
|
||||
'asset-permissions-assets-relations', 'asset-permissions-nodes-relations', 'terminal-status',
|
||||
'terminals', 'tasks', 'status', 'replay-storages', 'command-storages', 'session-sharing-records',
|
||||
'endpoints', 'endpoint-rules', 'applets', 'applet-hosts', 'applet-publications',
|
||||
'applet-host-deployments', 'virtual-apps', 'app-providers', 'virtual-app-publications',
|
||||
'celery-period-tasks', 'task-executions', 'adhocs', 'playbooks', 'variables', 'ftp-logs',
|
||||
'login-logs', 'operate-logs', 'password-change-logs', 'job-logs', 'jobs', 'user-sessions',
|
||||
'service-access-logs', 'chatai-prompts', 'super-connection-tokens', 'flows',
|
||||
'apply-assets', 'apply-nodes', 'login-acls', 'login-asset-acls', 'command-filter-acls',
|
||||
'command-groups', 'connect-method-acls', 'system-msg-subscriptions', 'roles', 'role-bindings',
|
||||
'system-roles', 'system-role-bindings', 'org-roles', 'org-role-bindings', 'content-types',
|
||||
'labeled-resources', 'account-backup-plans', 'account-check-engines', 'account-secrets',
|
||||
'change-secret', 'integration-applications', 'push-account', 'directories', 'connection-token',
|
||||
'groups', 'accounts', 'resource-types', 'favorite-assets', 'activities', 'platform-automation-methods',
|
||||
]
|
||||
if model in models:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_operation(self, view, path, prefix, method, components, request):
|
||||
# 这里可以对 path 进行处理
|
||||
if self.exclude_some_paths(path):
|
||||
return None
|
||||
if self.exclude_some_app_model(path):
|
||||
return None
|
||||
operation = super().get_operation(view, path, prefix, method, components, request)
|
||||
operation_id = operation.get('operationId')
|
||||
if 'bulk' in operation_id:
|
||||
return None
|
||||
exclude_operations = [
|
||||
'orgs_orgs_read', 'orgs_orgs_update', 'orgs_orgs_delete', 'orgs_orgs_create',
|
||||
'orgs_orgs_partial_update',
|
||||
]
|
||||
if operation_id in exclude_operations:
|
||||
return None
|
||||
return operation
|
||||
|
||||
|
||||
def get_swagger_view(ui=None, **kwargs):
|
||||
if ui == 'swagger':
|
||||
return SwaggerUI.as_view(url_name='schema')
|
||||
elif ui == 'redoc':
|
||||
return Redoc.as_view(url_name='schema')
|
||||
elif ui == 'json':
|
||||
return JsonApi.as_view()
|
||||
elif ui == 'yaml':
|
||||
return YamlApi.as_view()
|
||||
class CustomSwaggerAutoSchema(SwaggerAutoSchema):
|
||||
def get_tags(self, operation_keys):
|
||||
if len(operation_keys) > 2:
|
||||
return [operation_keys[0] + '_' + operation_keys[1]]
|
||||
return super().get_tags(operation_keys)
|
||||
|
||||
def get_operation_id(self, operation_keys):
|
||||
action = ''
|
||||
dump_keys = [k for k in operation_keys]
|
||||
if hasattr(self.view, 'action'):
|
||||
action = self.view.action
|
||||
if action == "bulk_destroy":
|
||||
action = "bulk_delete"
|
||||
if dump_keys[-2] == "children":
|
||||
if self.path.find('id') < 0:
|
||||
dump_keys.insert(-2, "root")
|
||||
if dump_keys[0] == "perms" and dump_keys[1] == "users":
|
||||
if self.path.find('{id}') < 0:
|
||||
dump_keys.insert(2, "my")
|
||||
if action.replace('bulk_', '') == dump_keys[-1]:
|
||||
dump_keys[-1] = action
|
||||
return super().get_operation_id(tuple(dump_keys))
|
||||
|
||||
def get_operation(self, operation_keys):
|
||||
operation = super().get_operation(operation_keys)
|
||||
if not getattr(operation, 'summary', ''):
|
||||
operation.summary = operation.operation_id
|
||||
return operation
|
||||
|
||||
def get_filter_parameters(self):
|
||||
if not self.should_filter():
|
||||
return []
|
||||
|
||||
fields = []
|
||||
if hasattr(self.view, 'get_filter_backends'):
|
||||
backends = self.view.get_filter_backends()
|
||||
elif hasattr(self.view, 'filter_backends'):
|
||||
backends = self.view.filter_backends
|
||||
else:
|
||||
backends = []
|
||||
for filter_backend in backends:
|
||||
fields += self.probe_inspectors(self.filter_inspectors, 'get_filter_parameters', filter_backend()) or []
|
||||
return fields
|
||||
|
||||
|
||||
api_info = openapi.Info(
|
||||
title="JumpServer API Docs",
|
||||
default_version='v1',
|
||||
description="JumpServer Restful api docs",
|
||||
terms_of_service="https://www.jumpserver.com",
|
||||
contact=openapi.Contact(email="support@lxware.hk"),
|
||||
license=openapi.License(name="GPLv3 License"),
|
||||
)
|
||||
|
||||
|
||||
def get_swagger_view():
|
||||
from ..urls import api_v1
|
||||
from django.urls import path, include
|
||||
patterns = [
|
||||
path('api/v1/', include(api_v1))
|
||||
]
|
||||
|
||||
with_auth = os.environ.get('DOC_AUTH', '1') == '1'
|
||||
if with_auth:
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
public = False
|
||||
else:
|
||||
permission_classes = []
|
||||
public = True
|
||||
|
||||
schema_view = get_schema_view(
|
||||
api_info,
|
||||
public=public,
|
||||
patterns=patterns,
|
||||
generator_class=CustomSchemaGenerator,
|
||||
permission_classes=permission_classes
|
||||
)
|
||||
return schema_view
|
||||
|
||||
@@ -20,11 +20,11 @@ class Label(JMSOrgBaseModel):
|
||||
verbose_name = _("Tag")
|
||||
|
||||
@lazyproperty
|
||||
def res_count(self) -> int:
|
||||
def res_count(self):
|
||||
return self.labeled_resources.count()
|
||||
|
||||
@lazyproperty
|
||||
def display_name(self) -> str:
|
||||
def display_name(self):
|
||||
return "{}:{}".format(self.name, self.value)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -73,5 +73,5 @@ class ContentTypeResourceSerializer(serializers.Serializer):
|
||||
name = serializers.SerializerMethodField()
|
||||
|
||||
@staticmethod
|
||||
def get_name(obj) -> str:
|
||||
def get_name(obj):
|
||||
return str(obj)
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# Modified from ansible_collections.community.general.mssql_script
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r"""
|
||||
module: mssql_script
|
||||
|
||||
short_description: Execute SQL scripts on a MSSQL database
|
||||
|
||||
version_added: "1.0.0"
|
||||
|
||||
description:
|
||||
- Execute SQL scripts on a MSSQL database.
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: partial
|
||||
details:
|
||||
- The script is not be executed in check mode.
|
||||
diff_mode:
|
||||
support: none
|
||||
|
||||
options:
|
||||
name:
|
||||
description: Database to run script against.
|
||||
aliases: [db]
|
||||
default: ''
|
||||
type: str
|
||||
login_user:
|
||||
description: The username used to authenticate with.
|
||||
type: str
|
||||
login_password:
|
||||
description: The password used to authenticate with.
|
||||
type: str
|
||||
login_host:
|
||||
description: Host running the database.
|
||||
type: str
|
||||
required: true
|
||||
login_port:
|
||||
description: Port of the MSSQL server. Requires O(login_host) be defined as well.
|
||||
default: 1433
|
||||
type: int
|
||||
script:
|
||||
description:
|
||||
- The SQL script to be executed.
|
||||
- Script can contain multiple SQL statements. Multiple Batches can be separated by V(GO) command.
|
||||
- Each batch must return at least one result set.
|
||||
required: true
|
||||
type: str
|
||||
transaction:
|
||||
description:
|
||||
- If transactional mode is requested, start a transaction and commit the change only if the script succeed. Otherwise,
|
||||
rollback the transaction.
|
||||
- If transactional mode is not requested (default), automatically commit the change.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 8.4.0
|
||||
output:
|
||||
description:
|
||||
- With V(default) each row is returned as a list of values. See RV(query_results).
|
||||
- Output format V(dict) returns dictionary with the column names as keys. See RV(query_results_dict).
|
||||
- V(dict) requires named columns to be returned by each query otherwise an error is thrown.
|
||||
choices: ["dict", "default"]
|
||||
default: 'default'
|
||||
type: str
|
||||
params:
|
||||
description: |-
|
||||
Parameters passed to the script as SQL parameters.
|
||||
(Query V('SELECT %(name\)s"') with V(example: '{"name": "John Doe"}).)'.
|
||||
type: dict
|
||||
notes:
|
||||
- Requires the pymssql Python package on the remote host. For Ubuntu, this is as easy as C(pip install pymssql) (See M(ansible.builtin.pip)).
|
||||
requirements:
|
||||
- pymssql
|
||||
|
||||
author:
|
||||
- Kris Budde (@kbudde)
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
- name: Check DB connection
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
db: master
|
||||
script: "SELECT 1"
|
||||
|
||||
- name: Query with parameter
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
script: |
|
||||
SELECT name, state_desc FROM sys.databases WHERE name = %(dbname)s
|
||||
params:
|
||||
dbname: msdb
|
||||
register: result_params
|
||||
- assert:
|
||||
that:
|
||||
- result_params.query_results[0][0][0][0] == 'msdb'
|
||||
- result_params.query_results[0][0][0][1] == 'ONLINE'
|
||||
|
||||
- name: Query within a transaction
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
script: |
|
||||
UPDATE sys.SomeTable SET desc = 'some_table_desc' WHERE name = %(dbname)s
|
||||
UPDATE sys.AnotherTable SET desc = 'another_table_desc' WHERE name = %(dbname)s
|
||||
transaction: true
|
||||
params:
|
||||
dbname: msdb
|
||||
|
||||
- name: two batches with default output
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
script: |
|
||||
SELECT 'Batch 0 - Select 0'
|
||||
SELECT 'Batch 0 - Select 1'
|
||||
GO
|
||||
SELECT 'Batch 1 - Select 0'
|
||||
register: result_batches
|
||||
- assert:
|
||||
that:
|
||||
- result_batches.query_results | length == 2 # two batch results
|
||||
- result_batches.query_results[0] | length == 2 # two selects in first batch
|
||||
- result_batches.query_results[0][0] | length == 1 # one row in first select
|
||||
- result_batches.query_results[0][0][0] | length == 1 # one column in first row
|
||||
- result_batches.query_results[0][0][0][0] == 'Batch 0 - Select 0' # each row contains a list of values.
|
||||
|
||||
- name: two batches with dict output
|
||||
community.general.mssql_script:
|
||||
login_user: "{{ mssql_login_user }}"
|
||||
login_password: "{{ mssql_login_password }}"
|
||||
login_host: "{{ mssql_host }}"
|
||||
login_port: "{{ mssql_port }}"
|
||||
output: dict
|
||||
script: |
|
||||
SELECT 'Batch 0 - Select 0' as b0s0
|
||||
SELECT 'Batch 0 - Select 1' as b0s1
|
||||
GO
|
||||
SELECT 'Batch 1 - Select 0' as b1s0
|
||||
register: result_batches_dict
|
||||
- assert:
|
||||
that:
|
||||
- result_batches_dict.query_results_dict | length == 2 # two batch results
|
||||
- result_batches_dict.query_results_dict[0] | length == 2 # two selects in first batch
|
||||
- result_batches_dict.query_results_dict[0][0] | length == 1 # one row in first select
|
||||
- result_batches_dict.query_results_dict[0][0][0]['b0s0'] == 'Batch 0 - Select 0' # column 'b0s0' of first row
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
query_results:
|
||||
description: List of batches (queries separated by V(GO) keyword).
|
||||
type: list
|
||||
elements: list
|
||||
returned: success and O(output=default)
|
||||
sample:
|
||||
[
|
||||
[
|
||||
[
|
||||
[
|
||||
"Batch 0 - Select 0"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"Batch 0 - Select 1"
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
[
|
||||
"Batch 1 - Select 0"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
contains:
|
||||
queries:
|
||||
description:
|
||||
- List of result sets of each query.
|
||||
- If a query returns no results, the results of this and all the following queries are not included in the output.
|
||||
- Use the V(GO) keyword in O(script) to separate queries.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
rows:
|
||||
description: List of rows returned by query.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
column_value:
|
||||
description:
|
||||
- List of column values.
|
||||
- Any non-standard JSON type is converted to string.
|
||||
type: list
|
||||
example: ["Batch 0 - Select 0"]
|
||||
returned: success, if output is default
|
||||
query_results_dict:
|
||||
description: List of batches (queries separated by V(GO) keyword).
|
||||
type: list
|
||||
elements: list
|
||||
returned: success and O(output=dict)
|
||||
sample:
|
||||
[
|
||||
[
|
||||
[
|
||||
[
|
||||
"Batch 0 - Select 0"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"Batch 0 - Select 1"
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
[
|
||||
"Batch 1 - Select 0"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
contains:
|
||||
queries:
|
||||
description:
|
||||
- List of result sets of each query.
|
||||
- If a query returns no results, the results of this and all the following queries are not included in the output.
|
||||
Use V(GO) keyword to separate queries.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
rows:
|
||||
description: List of rows returned by query.
|
||||
type: list
|
||||
elements: list
|
||||
contains:
|
||||
column_dict:
|
||||
description:
|
||||
- Dictionary of column names and values.
|
||||
- Any non-standard JSON type is converted to string.
|
||||
type: dict
|
||||
example: {"col_name": "Batch 0 - Select 0"}
|
||||
returned: success, if output is dict
|
||||
"""
|
||||
|
||||
import pymssql
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import json
|
||||
|
||||
|
||||
def clean_output(o):
|
||||
return str(o)
|
||||
|
||||
|
||||
def main():
|
||||
module_args = dict(
|
||||
name=dict(aliases=['db'], default=''),
|
||||
login_user=dict(type='str', required=False, default=None),
|
||||
login_password=dict(no_log=True),
|
||||
login_host=dict(required=True),
|
||||
login_port=dict(type='int', default=1433),
|
||||
script=dict(required=True),
|
||||
output=dict(default='default', choices=['dict', 'default']),
|
||||
params=dict(type='dict'),
|
||||
transaction=dict(type='bool', default=True),
|
||||
tds_version=dict(type='str', required=False, default=None),
|
||||
encryption=dict(type='str', required=False, default=None)
|
||||
)
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
db = module.params['name']
|
||||
login_user = module.params['login_user']
|
||||
login_password = module.params['login_password']
|
||||
login_host = module.params['login_host']
|
||||
login_port = module.params['login_port']
|
||||
script = module.params['script']
|
||||
output = module.params['output']
|
||||
sql_params = module.params['params']
|
||||
transaction = module.params['transaction']
|
||||
# TODO 待 ansible 官方支持这两个参数
|
||||
tds_version = module.params['tds_version'] or None
|
||||
encryption = module.params['encryption'] or None
|
||||
|
||||
login_querystring = login_host
|
||||
if login_port != 1433:
|
||||
login_querystring = "%s:%s" % (login_host, login_port)
|
||||
|
||||
if login_user is not None and login_password is None:
|
||||
module.fail_json(
|
||||
msg="when supplying login_user argument, login_password must also be provided")
|
||||
|
||||
try:
|
||||
conn = pymssql.connect(
|
||||
user=login_user, password=login_password, host=login_querystring,
|
||||
database=db, encryption=encryption, tds_version=tds_version)
|
||||
cursor = conn.cursor()
|
||||
except Exception as e:
|
||||
if "Unknown database" in str(e):
|
||||
errno, errstr = e.args
|
||||
module.fail_json(msg="ERROR: %s %s" % (errno, errstr))
|
||||
else:
|
||||
module.fail_json(
|
||||
msg="unable to connect, check login_user and login_password are correct, or alternatively check your "
|
||||
"@sysconfdir@/freetds.conf / ${HOME}/.freetds.conf")
|
||||
|
||||
# If transactional mode is requested, start a transaction
|
||||
conn.autocommit(not transaction)
|
||||
|
||||
query_results_key = 'query_results'
|
||||
if output == 'dict':
|
||||
cursor = conn.cursor(as_dict=True)
|
||||
query_results_key = 'query_results_dict'
|
||||
|
||||
# Process the script into batches
|
||||
queries = []
|
||||
current_batch = []
|
||||
for statement in script.splitlines(True):
|
||||
# Ignore the Byte Order Mark, if found
|
||||
if statement.strip() == '\uFEFF':
|
||||
continue
|
||||
|
||||
# Assume each 'GO' is on its own line but may have leading/trailing whitespace
|
||||
# and be of mixed-case
|
||||
if statement.strip().upper() != 'GO':
|
||||
current_batch.append(statement)
|
||||
else:
|
||||
queries.append(''.join(current_batch))
|
||||
current_batch = []
|
||||
if len(current_batch) > 0:
|
||||
queries.append(''.join(current_batch))
|
||||
|
||||
result['changed'] = True
|
||||
if module.check_mode:
|
||||
module.exit_json(**result)
|
||||
|
||||
query_results = []
|
||||
for query in queries:
|
||||
# Catch and exit on any bad query errors
|
||||
try:
|
||||
cursor.execute(query, sql_params)
|
||||
qry_result = []
|
||||
rows = cursor.fetchall()
|
||||
while rows:
|
||||
qry_result.append(rows)
|
||||
rows = cursor.fetchall()
|
||||
query_results.append(qry_result)
|
||||
except Exception as e:
|
||||
# We know we executed the statement so this error just means we have no resultset
|
||||
# which is ok (eg UPDATE/INSERT)
|
||||
if (
|
||||
type(e).__name__ == 'OperationalError' and
|
||||
str(e) == 'Statement not executed or executed statement has no resultset'
|
||||
):
|
||||
query_results.append([])
|
||||
else:
|
||||
# Rollback transaction before failing the module in case of error
|
||||
if transaction:
|
||||
conn.rollback()
|
||||
error_msg = '%s: %s' % (type(e).__name__, str(e))
|
||||
module.fail_json(msg="query failed", query=query, error=error_msg, **result)
|
||||
|
||||
# Commit transaction before exiting the module in case of no error
|
||||
if transaction:
|
||||
conn.commit()
|
||||
|
||||
# ensure that the result is json serializable
|
||||
qry_results = json.loads(json.dumps(query_results, default=clean_output))
|
||||
|
||||
result[query_results_key] = qry_results
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -45,7 +45,7 @@ class SystemMsgSubscription(JMSBaseModel):
|
||||
self.message_type_label = msg_label
|
||||
|
||||
@property
|
||||
def receivers(self) -> list:
|
||||
def receivers(self):
|
||||
from notifications.backends import BACKEND
|
||||
|
||||
users = [user for user in self.users.all()]
|
||||
|
||||
@@ -6,7 +6,6 @@ from notifications.models import SystemMsgSubscription, UserMsgSubscription
|
||||
|
||||
class SystemMsgSubscriptionSerializer(BulkModelSerializer):
|
||||
receive_backends = serializers.ListField(child=serializers.CharField())
|
||||
message_type_label = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SystemMsgSubscription
|
||||
|
||||
@@ -8,7 +8,7 @@ from ..models import MessageContent
|
||||
class SenderMixin(ModelSerializer):
|
||||
sender = serializers.SerializerMethodField()
|
||||
|
||||
def get_sender(self, site_msg) -> str:
|
||||
def get_sender(self, site_msg):
|
||||
sender = site_msg.sender
|
||||
if sender:
|
||||
return str(sender)
|
||||
@@ -28,15 +28,13 @@ class MessageContentSerializer(SenderMixin, ModelSerializer):
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_message(site_msg) -> str:
|
||||
def get_message(site_msg):
|
||||
markdown = convert_html_to_markdown(site_msg.message)
|
||||
return markdown
|
||||
|
||||
|
||||
class SiteMessageSerializer(SenderMixin, ModelSerializer):
|
||||
content = MessageContentSerializer(read_only=True)
|
||||
has_read = serializers.BooleanField(read_only=True)
|
||||
read_at = serializers.DateTimeField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = MessageContent
|
||||
|
||||
@@ -112,21 +112,12 @@ class JMSInventory:
|
||||
|
||||
@staticmethod
|
||||
def make_protocol_setting_vars(host, protocols):
|
||||
# 针对 ssh sqlserver 协议的特殊处理
|
||||
# 针对 ssh 协议的特殊处理
|
||||
for p in protocols:
|
||||
if p.name == 'ssh':
|
||||
if hasattr(p, 'setting'):
|
||||
setting = getattr(p, 'setting')
|
||||
host['jms_asset']['old_ssh_version'] = setting.get('old_ssh_version', False)
|
||||
if p.name == 'sqlserver':
|
||||
if hasattr(p, 'setting'):
|
||||
setting = getattr(p, 'setting')
|
||||
encryption = setting.get('encrypt', True)
|
||||
version = setting.get('version', ">=2014")
|
||||
if version == '<2014':
|
||||
host['jms_asset']['tds_version'] = '7.0'
|
||||
if not encryption:
|
||||
host['jms_asset']['encryption'] = 'off'
|
||||
host['old_ssh_version'] = setting.get('old_ssh_version', False)
|
||||
|
||||
def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway, path_dir,
|
||||
ansible_config):
|
||||
@@ -235,6 +226,7 @@ class JMSInventory:
|
||||
})
|
||||
|
||||
self.make_protocol_setting_vars(host, protocols)
|
||||
|
||||
protocols = host['jms_asset']['protocols']
|
||||
host['jms_asset'].update({f"{p['name']}_port": p['port'] for p in protocols})
|
||||
if host['jms_account'] and tp == 'oracle':
|
||||
|
||||
@@ -19,7 +19,7 @@ class CeleryTask(models.Model):
|
||||
__state = None
|
||||
|
||||
@property
|
||||
def meta(self) -> dict:
|
||||
def meta(self):
|
||||
task = app.tasks.get(self.name, None)
|
||||
return {
|
||||
"comment": getattr(task, 'verbose_name', None),
|
||||
@@ -28,7 +28,7 @@ class CeleryTask(models.Model):
|
||||
}
|
||||
|
||||
@property
|
||||
def summary(self) -> dict:
|
||||
def summary(self):
|
||||
if self.__summary is not None:
|
||||
return self.__summary
|
||||
executions = CeleryTaskExecution.objects.filter(name=self.name)
|
||||
@@ -53,7 +53,7 @@ class CeleryTask(models.Model):
|
||||
return color
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
def state(self):
|
||||
if self.__state is not None:
|
||||
return self.__state
|
||||
last_five_executions = CeleryTaskExecution.objects.filter(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta, datetime
|
||||
from datetime import timedelta
|
||||
|
||||
import sys
|
||||
from celery import current_task
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
@@ -177,11 +177,11 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
|
||||
return self.executions.first()
|
||||
|
||||
@property
|
||||
def date_last_run(self) -> datetime:
|
||||
def date_last_run(self):
|
||||
return self.last_execution.date_created if self.last_execution else None
|
||||
|
||||
@property
|
||||
def summary(self) -> dict:
|
||||
def summary(self):
|
||||
summary = {
|
||||
"total": self.executions.count(),
|
||||
"success": self.executions.filter(status=JobStatus.success).count(),
|
||||
@@ -189,7 +189,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
|
||||
return summary
|
||||
|
||||
@property
|
||||
def average_time_cost(self) -> int:
|
||||
def average_time_cost(self):
|
||||
return self.last_execution.time_cost if self.last_execution else 0
|
||||
|
||||
def get_register_task(self):
|
||||
@@ -207,7 +207,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
|
||||
user=self.creator, module=self.module)
|
||||
|
||||
@property
|
||||
def material(self) -> str:
|
||||
def material(self):
|
||||
if self.type == 'adhoc':
|
||||
return "{}:{}".format(self.module, self.args)
|
||||
if self.type == 'playbook':
|
||||
@@ -271,7 +271,7 @@ class JobExecution(JMSOrgBaseModel):
|
||||
db_module_name_map = {
|
||||
'mysql': 'community.mysql.mysql_query',
|
||||
'postgresql': 'community.postgresql.postgresql_query',
|
||||
'sqlserver': 'mssql_script',
|
||||
'sqlserver': 'community.general.mssql_script',
|
||||
}
|
||||
extra_query_token_map = {
|
||||
'sqlserver': 'script'
|
||||
@@ -292,10 +292,7 @@ class JobExecution(JMSOrgBaseModel):
|
||||
"login_user={{login_user}} " \
|
||||
"login_password={{login_password}} " \
|
||||
"login_port={{login_port}} " \
|
||||
"%s={{login_db}} " % login_db_token
|
||||
if module == 'mssql_script':
|
||||
login_args += "encryption={{jms_asset.encryption | default(None) }} " \
|
||||
"tds_version={{jms_asset.tds_version | default(None) }} "
|
||||
"%s={{login_db}}" % login_db_token
|
||||
shell = "{} {}=\"{}\" ".format(login_args, query_token, self.current_job.args)
|
||||
return module, shell
|
||||
|
||||
@@ -377,7 +374,7 @@ class JobExecution(JMSOrgBaseModel):
|
||||
return str(self.id).split('-')[-1]
|
||||
|
||||
@property
|
||||
def time_cost(self) -> int:
|
||||
def time_cost(self):
|
||||
if not self.date_start:
|
||||
return 0
|
||||
if self.is_finished:
|
||||
@@ -385,25 +382,25 @@ class JobExecution(JMSOrgBaseModel):
|
||||
return (timezone.now() - self.date_start).total_seconds()
|
||||
|
||||
@property
|
||||
def timedelta(self) -> timedelta:
|
||||
def timedelta(self):
|
||||
if self.date_start and self.date_finished:
|
||||
return self.date_finished - self.date_start
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_finished(self) -> bool:
|
||||
def is_finished(self):
|
||||
return self.status in [JobStatus.success, JobStatus.failed, JobStatus.timeout]
|
||||
|
||||
@property
|
||||
def is_success(self) -> bool:
|
||||
def is_success(self):
|
||||
return self.status == JobStatus.success
|
||||
|
||||
@property
|
||||
def inventory_path(self) -> str:
|
||||
def inventory_path(self):
|
||||
return os.path.join(self.private_dir, 'inventory', 'hosts')
|
||||
|
||||
@property
|
||||
def private_dir(self) -> str:
|
||||
def private_dir(self):
|
||||
uniq = self.date_created.strftime('%Y%m%d_%H%M%S') + '_' + self.short_id
|
||||
job_name = self.current_job.name if self.current_job.name else 'instant'
|
||||
return os.path.join(settings.ANSIBLE_DIR, job_name, uniq)
|
||||
@@ -453,8 +450,6 @@ class JobExecution(JMSOrgBaseModel):
|
||||
"input": self.material,
|
||||
"risk_level": RiskLevelChoices.reject,
|
||||
"user": self.creator,
|
||||
'org_id': self.org_id,
|
||||
'_org_name': self.org_name,
|
||||
}).publish_async()
|
||||
raise Exception("command is rejected by ACL")
|
||||
elif acl.is_action(CommandFilterACL.ActionChoices.warning):
|
||||
@@ -493,8 +488,6 @@ class JobExecution(JMSOrgBaseModel):
|
||||
"input": self.material,
|
||||
"risk_level": RiskLevelChoices.reject,
|
||||
"user": self.creator,
|
||||
'org_id': self.org_id,
|
||||
'_org_name': self.org_name,
|
||||
}).publish_async()
|
||||
raise CommandInBlackListException(
|
||||
"Command is rejected by black list: {}".format(self.current_job.args))
|
||||
|
||||
@@ -32,7 +32,7 @@ class Variable(JMSBaseModel):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def form_data(self) -> dict:
|
||||
def form_data(self):
|
||||
return {
|
||||
'var_name': self.var_name,
|
||||
'label': self.name,
|
||||
|
||||
@@ -42,8 +42,8 @@ class VariableSerializer(CommonBulkModelSerializer):
|
||||
model = Variable
|
||||
read_only_fields = ["id", "date_created", "date_updated", "created_by", "creator"]
|
||||
fields = read_only_fields + [
|
||||
"name", "var_name", "type", 'required', 'default_value', 'tips',
|
||||
'adhoc', 'playbook', 'job', 'form_data', 'extra_args'
|
||||
"name", "var_name", "type", 'required', 'default_value', 'tips', 'adhoc', 'playbook', 'job', 'form_data',
|
||||
'extra_args'
|
||||
]
|
||||
|
||||
def validate(self, attrs):
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
from rest_framework.serializers import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
|
||||
from common.db.models import JMSBaseModel
|
||||
from common.utils import get_logger, lazyproperty
|
||||
from ..models import Organization
|
||||
@@ -81,7 +80,7 @@ class OrgModelMixin(models.Model):
|
||||
return Organization.get_instance(self.org_id)
|
||||
|
||||
@property
|
||||
def org_name(self) -> str:
|
||||
def org_name(self):
|
||||
return self.org.name
|
||||
|
||||
@property
|
||||
|
||||
@@ -168,17 +168,17 @@ class Organization(OrgRoleMixin, JMSBaseModel):
|
||||
name = settings.GLOBAL_ORG_DISPLAY_NAME or cls.ROOT_NAME
|
||||
return cls(id=cls.ROOT_ID, name=name, builtin=True)
|
||||
|
||||
def is_root(self) -> bool:
|
||||
def is_root(self):
|
||||
return self.id == self.ROOT_ID
|
||||
|
||||
def is_default(self) -> bool:
|
||||
def is_default(self):
|
||||
return str(self.id) == self.DEFAULT_ID
|
||||
|
||||
def is_system(self) -> bool:
|
||||
def is_system(self):
|
||||
return str(self.id) == self.SYSTEM_ID
|
||||
|
||||
@property
|
||||
def internal(self) -> bool:
|
||||
def internal(self):
|
||||
return str(self.id) in self.INTERNAL_IDS
|
||||
|
||||
def change_to(self):
|
||||
|
||||
@@ -6,7 +6,6 @@ from django.db.models import F
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics
|
||||
|
||||
from users.models import User
|
||||
from accounts.serializers import AccountSerializer
|
||||
from orgs.mixins.api import OrgRelationMixin, OrgBulkModelViewSet
|
||||
from orgs.utils import current_org
|
||||
@@ -53,7 +52,6 @@ class AssetPermissionAllUserListApi(generics.ListAPIView):
|
||||
serializer_class = serializers.AssetPermissionAllUserSerializer
|
||||
filterset_fields = ("username", "name")
|
||||
search_fields = filterset_fields
|
||||
queryset = User.objects.none()
|
||||
|
||||
def get_queryset(self):
|
||||
pk = self.kwargs.get("pk")
|
||||
|
||||
@@ -26,19 +26,16 @@ class CurrentAssetPermission(object):
|
||||
|
||||
|
||||
class RelationMixin(BulkSerializerMixin, serializers.Serializer):
|
||||
assetpermission_display = serializers.SerializerMethodField()
|
||||
assetpermission_display = serializers.ReadOnlyField()
|
||||
|
||||
def get_field_names(self, declared_fields, info):
|
||||
fields = super().get_field_names(declared_fields, info)
|
||||
fields.extend(['assetpermission', "assetpermission_display"])
|
||||
return fields
|
||||
|
||||
def get_assetpermission_display(self, obj) -> str:
|
||||
return str(obj.assetpermission)
|
||||
|
||||
|
||||
class AssetPermissionUserRelationSerializer(RelationMixin, serializers.ModelSerializer):
|
||||
user_display = serializers.SerializerMethodField()
|
||||
user_display = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = AssetPermission.users.through
|
||||
@@ -46,9 +43,6 @@ class AssetPermissionUserRelationSerializer(RelationMixin, serializers.ModelSeri
|
||||
'id', 'user', 'user_display',
|
||||
]
|
||||
|
||||
def get_user_display(self, obj) -> str:
|
||||
return str(obj.user)
|
||||
|
||||
|
||||
class AssetPermissionAllUserSerializer(serializers.Serializer):
|
||||
user = serializers.UUIDField(read_only=True, source='id')
|
||||
@@ -58,12 +52,12 @@ class AssetPermissionAllUserSerializer(serializers.Serializer):
|
||||
only_fields = ['id', 'username', 'name']
|
||||
|
||||
@staticmethod
|
||||
def get_user_display(obj) -> str:
|
||||
def get_user_display(obj):
|
||||
return str(obj)
|
||||
|
||||
|
||||
class AssetPermissionUserGroupRelationSerializer(RelationMixin, serializers.ModelSerializer):
|
||||
usergroup_display = serializers.SerializerMethodField()
|
||||
usergroup_display = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = AssetPermission.user_groups.through
|
||||
@@ -71,12 +65,9 @@ class AssetPermissionUserGroupRelationSerializer(RelationMixin, serializers.Mode
|
||||
'id', 'usergroup', "usergroup_display",
|
||||
]
|
||||
|
||||
def get_usergroup_display(self, obj) -> str:
|
||||
return str(obj.usergroup)
|
||||
|
||||
|
||||
class AssetPermissionAssetRelationSerializer(RelationMixin, serializers.ModelSerializer):
|
||||
asset_display = serializers.SerializerMethodField()
|
||||
asset_display = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = AssetPermission.assets.through
|
||||
@@ -84,9 +75,6 @@ class AssetPermissionAssetRelationSerializer(RelationMixin, serializers.ModelSer
|
||||
'id', "asset", "asset_display",
|
||||
]
|
||||
|
||||
def get_asset_display(self, obj) -> str:
|
||||
return str(obj.asset)
|
||||
|
||||
|
||||
class AssetPermissionAllAssetSerializer(serializers.Serializer):
|
||||
asset = serializers.UUIDField(read_only=True, source='id')
|
||||
@@ -96,7 +84,7 @@ class AssetPermissionAllAssetSerializer(serializers.Serializer):
|
||||
only_fields = ['id', 'name', 'address']
|
||||
|
||||
@staticmethod
|
||||
def get_asset_display(obj) -> str:
|
||||
def get_asset_display(obj):
|
||||
return str(obj)
|
||||
|
||||
|
||||
@@ -108,6 +96,3 @@ class AssetPermissionNodeRelationSerializer(RelationMixin, serializers.ModelSeri
|
||||
fields = [
|
||||
'id', 'node', "node_display",
|
||||
]
|
||||
|
||||
def get_node_display(self, obj) -> str:
|
||||
return str(obj.node)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user