fix: Solve audit job and variable bugs

pull/14453/head
wangruidong 2024-11-13 16:55:24 +08:00 committed by Bryan
parent 1ee57cfda0
commit fcdc2b9510
6 changed files with 101 additions and 94 deletions

View File

@ -66,7 +66,7 @@ class JobsAuditViewSet(OrgModelViewSet):
search_fields = ['creator__name'] search_fields = ['creator__name']
filterset_fields = ['creator__name'] filterset_fields = ['creator__name']
serializer_class = JobsAuditSerializer serializer_class = JobsAuditSerializer
ordering = ['-is_periodic', '-date_created'] ordering = ['-is_periodic', '-date_updated']
http_method_names = ['get', 'options', 'patch'] http_method_names = ['get', 'options', 'patch']
def get_queryset(self): def get_queryset(self):

View File

@ -35,8 +35,14 @@ class JobLogSerializer(JobExecutionSerializer):
class JobsAuditSerializer(JobSerializer): class JobsAuditSerializer(JobSerializer):
material = serializers.ReadOnlyField(label=_("Command"))
summary = serializers.ReadOnlyField(label=_("Summary"))
class Meta(JobSerializer.Meta): class Meta(JobSerializer.Meta):
fields = JobSerializer.Meta.fields read_only_fields = [
"id", 'name', 'args', 'material', 'type', 'crontab', 'interval', 'date_last_run', 'summary', 'created_by'
]
fields = read_only_fields + ['is_periodic']
def validate(self, attrs): def validate(self, attrs):
allowed_fields = {'is_periodic'} allowed_fields = {'is_periodic'}

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-11 19:17+0800\n" "POT-Creation-Date: 2024-11-13 11:11+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -593,7 +593,7 @@ msgstr "结束日期"
#: accounts/models/automations/change_secret.py:44 #: accounts/models/automations/change_secret.py:44
#: assets/models/automations/base.py:113 #: assets/models/automations/base.py:113
#: assets/serializers/automations/base.py:39 audits/models.py:208 #: assets/serializers/automations/base.py:39 audits/models.py:208
#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:239 #: audits/serializers.py:69 ops/models/base.py:49 ops/models/job.py:239
#: terminal/models/applet/applet.py:331 terminal/models/applet/host.py:140 #: terminal/models/applet/applet.py:331 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30 #: terminal/models/component/status.py:30
#: terminal/models/virtualapp/virtualapp.py:99 #: terminal/models/virtualapp/virtualapp.py:99
@ -665,7 +665,7 @@ msgstr "触发方式"
#: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: accounts/models/automations/push_account.py:16 acls/models/base.py:41
#: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81
#: audits/models.py:92 audits/serializers.py:84 #: audits/models.py:92 audits/serializers.py:99
#: authentication/serializers/connect_token_secret.py:119 #: authentication/serializers/connect_token_secret.py:119
#: authentication/templates/authentication/_access_key_modal.html:34 #: authentication/templates/authentication/_access_key_modal.html:34
#: perms/serializers/permission.py:52 perms/serializers/permission.py:74 #: perms/serializers/permission.py:52 perms/serializers/permission.py:74
@ -884,8 +884,8 @@ msgstr "类别"
#: acls/serializers/command_acl.py:19 assets/models/automations/base.py:20 #: acls/serializers/command_acl.py:19 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:96 #: assets/models/cmd_filter.py:74 assets/models/platform.py:96
#: assets/serializers/asset/common.py:146 assets/serializers/platform.py:159 #: assets/serializers/asset/common.py:146 assets/serializers/platform.py:159
#: assets/serializers/platform.py:171 audits/serializers.py:53 #: assets/serializers/platform.py:171 audits/serializers.py:68
#: audits/serializers.py:170 #: audits/serializers.py:185
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:153 #: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:153
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:40 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:40
#: terminal/models/component/storage.py:58 #: terminal/models/component/storage.py:58
@ -958,7 +958,7 @@ msgstr "ID"
#: acls/templates/acls/user_login_reminder.html:8 #: acls/templates/acls/user_login_reminder.html:8
#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54
#: audits/models.py:90 audits/models.py:172 audits/models.py:271 #: audits/models.py:90 audits/models.py:172 audits/models.py:271
#: audits/serializers.py:171 authentication/models/connection_token.py:32 #: audits/serializers.py:186 authentication/models/connection_token.py:32
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16 #: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
#: notifications/models/notification.py:12 #: notifications/models/notification.py:12
#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63
@ -1587,7 +1587,7 @@ msgid "Login city"
msgstr "登录城市" msgstr "登录城市"
#: acls/templates/acls/user_login_reminder.html:11 audits/models.py:197 #: acls/templates/acls/user_login_reminder.html:11 audits/models.py:197
#: audits/models.py:266 audits/serializers.py:68 #: audits/models.py:266 audits/serializers.py:83
msgid "User agent" msgid "User agent"
msgstr "用户代理" msgstr "用户代理"
@ -2821,7 +2821,7 @@ msgid "Finished"
msgstr "结束" msgstr "结束"
#: audits/const.py:46 settings/serializers/terminal.py:6 #: audits/const.py:46 settings/serializers/terminal.py:6
#: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:174 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:182
#: terminal/models/virtualapp/provider.py:14 terminal/serializers/session.py:57 #: terminal/models/virtualapp/provider.py:14 terminal/serializers/session.py:57
#: terminal/serializers/session.py:113 #: terminal/serializers/session.py:113
msgid "Terminal" msgid "Terminal"
@ -2866,7 +2866,7 @@ msgstr "作业审计日志"
msgid "Remote addr" msgid "Remote addr"
msgstr "远端地址" msgstr "远端地址"
#: audits/models.py:61 audits/serializers.py:38 #: audits/models.py:61 audits/serializers.py:53
msgid "Operate" msgid "Operate"
msgstr "操作" msgstr "操作"
@ -2891,12 +2891,12 @@ msgstr "会话"
msgid "File transfer log" msgid "File transfer log"
msgstr "文件传输" msgstr "文件传输"
#: audits/models.py:94 audits/serializers.py:86 #: audits/models.py:94 audits/serializers.py:101
msgid "Resource Type" msgid "Resource Type"
msgstr "资源类型" msgstr "资源类型"
#: audits/models.py:95 audits/models.py:98 audits/models.py:144 #: audits/models.py:95 audits/models.py:98 audits/models.py:144
#: audits/serializers.py:85 labels/serializers.py:46 #: audits/serializers.py:100 labels/serializers.py:46
msgid "Resource" msgid "Resource"
msgstr "资源" msgstr "资源"
@ -2938,7 +2938,7 @@ msgstr "登录方式"
msgid "Login IP" msgid "Login IP"
msgstr "登录 IP" msgstr "登录 IP"
#: audits/models.py:200 audits/serializers.py:52 #: audits/models.py:200 audits/serializers.py:67
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms/profile.py:63 users/models/user/__init__.py:79 #: users/forms/profile.py:63 users/models/user/__init__.py:79
#: users/serializers/profile.py:70 #: users/serializers/profile.py:70
@ -2982,20 +2982,20 @@ msgstr "下线用户会话"
msgid "Creator" msgid "Creator"
msgstr "创建者" msgstr "创建者"
#: audits/serializers.py:69 #: audits/serializers.py:84
msgid "Reason display" msgid "Reason display"
msgstr "原因描述" msgstr "原因描述"
#: audits/serializers.py:70 audits/serializers.py:184 #: audits/serializers.py:85 audits/serializers.py:199
msgid "Auth backend display" msgid "Auth backend display"
msgstr "认证方式" msgstr "认证方式"
#: audits/serializers.py:134 #: audits/serializers.py:149
#, python-format #, python-format
msgid "%s %s this resource" msgid "%s %s this resource"
msgstr "用户 %s %s 了当前资源" msgstr "用户 %s %s 了当前资源"
#: audits/serializers.py:172 authentication/models/connection_token.py:47 #: audits/serializers.py:187 authentication/models/connection_token.py:47
#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80
#: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_application.py:31
#: tickets/models/ticket/apply_asset.py:20 users/models/user/__init__.py:98 #: tickets/models/ticket/apply_asset.py:20 users/models/user/__init__.py:98
@ -4719,7 +4719,7 @@ msgid ""
"File size exceeds maximum limit. Please select a file smaller than {limit}MB" "File size exceeds maximum limit. Please select a file smaller than {limit}MB"
msgstr "文件大小超过最大限制。请选择小于 {limit}MB 的文件。" msgstr "文件大小超过最大限制。请选择小于 {limit}MB 的文件。"
#: ops/api/job.py:231 #: ops/api/job.py:235
msgid "" msgid ""
"The task is being created and cannot be interrupted. Please try again later." "The task is being created and cannot be interrupted. Please try again later."
msgstr "正在创建任务,无法中断,请稍后重试。" msgstr "正在创建任务,无法中断,请稍后重试。"
@ -4971,7 +4971,7 @@ msgstr "其它参数"
msgid "Date published" msgid "Date published"
msgstr "发布日期" msgstr "发布日期"
#: ops/models/celery.py:113 #: ops/models/celery.py:124
msgid "Celery Task Execution" msgid "Celery Task Execution"
msgstr "Celery 任务执行" msgstr "Celery 任务执行"
@ -4992,10 +4992,8 @@ msgid "Parameters define"
msgstr "参数定义" msgstr "参数定义"
#: ops/models/job.py:159 #: ops/models/job.py:159
#, fuzzy
#| msgid "Periodic run"
msgid "Periodic variable" msgid "Periodic variable"
msgstr "周期执行" msgstr "周期执行变量"
#: ops/models/job.py:160 #: ops/models/job.py:160
msgid "Run as" msgid "Run as"
@ -5031,40 +5029,32 @@ msgid "VCS URL"
msgstr "VCS URL" msgstr "VCS URL"
#: ops/models/variable.py:11 #: ops/models/variable.py:11
#, fuzzy
#| msgid "Variable Type"
msgid "Variable name" msgid "Variable name"
msgstr "参数类型" msgstr "变量名"
#: ops/models/variable.py:12 #: ops/models/variable.py:12
msgid "" msgid ""
"The variable name used in the script has a fixed prefix 'jms_' followed by " "The variable name used in the script has a fixed prefix 'jms_' followed by "
"the input variable name. For example, if the variable name is 'name,' the " "the input variable name. For example, if the variable name is 'name,' the "
"final generated environment variable will be 'jms_name'." "final generated environment variable will be 'jms_name'."
msgstr "" msgstr "在脚本使用的变量名称,固定前缀 jms_ + 输入的变量名例如变量名name则最终生成环境变量为 jms_name"
#: ops/models/variable.py:16 #: ops/models/variable.py:16
#, fuzzy
#| msgid "Default"
msgid "Default Value" msgid "Default Value"
msgstr "默认" msgstr "默认"
#: ops/models/variable.py:18 #: ops/models/variable.py:18
#, fuzzy
#| msgid "Variable Type"
msgid "Variable type" msgid "Variable type"
msgstr "参数类型" msgstr "变量类型"
#: ops/models/variable.py:21 ops/serializers/variable.py:23 #: ops/models/variable.py:21 ops/serializers/variable.py:25
msgid "ExtraVars" msgid "ExtraVars"
msgstr "" msgstr "额外参数"
#: ops/models/variable.py:49 ops/serializers/adhoc.py:16 #: ops/models/variable.py:49 ops/serializers/adhoc.py:16
#: ops/serializers/job.py:22 ops/serializers/playbook.py:21 #: ops/serializers/job.py:22 ops/serializers/playbook.py:21
#, fuzzy
#| msgid "Variable Type"
msgid "Variable" msgid "Variable"
msgstr "参数类型" msgstr "变量"
#: ops/notifications.py:20 #: ops/notifications.py:20
msgid "Server performance" msgid "Server performance"
@ -5126,11 +5116,11 @@ msgstr "任务 ID"
msgid "You do not have permission for the current job." msgid "You do not have permission for the current job."
msgstr "你没有当前作业的权限。" msgstr "你没有当前作业的权限。"
#: ops/serializers/variable.py:20 #: ops/serializers/variable.py:22
msgid "Variable Type" msgid "Variable Type"
msgstr "参数类型" msgstr "变量类型"
#: ops/serializers/variable.py:25 #: ops/serializers/variable.py:27
msgid "" msgid ""
"Each item is on a separate line, with each line separated by a colon. The " "Each item is on a separate line, with each line separated by a colon. The "
"part before the colon is the display content, and the part after the colon " "part before the colon is the display content, and the part after the colon "
@ -6586,8 +6576,6 @@ msgid "Vault"
msgstr "启用 Vault" msgstr "启用 Vault"
#: settings/serializers/feature.py:53 #: settings/serializers/feature.py:53
#, fuzzy
#| msgid "Provider"
msgid "Vault provider" msgid "Vault provider"
msgstr "云服务商" msgstr "云服务商"
@ -6611,8 +6599,6 @@ msgstr "挂载点"
#: settings/serializers/feature.py:94 #: settings/serializers/feature.py:94
#: xpack/plugins/cloud/serializers/account_attrs.py:41 #: xpack/plugins/cloud/serializers/account_attrs.py:41
#, fuzzy
#| msgid "Client ID"
msgid "Tenant ID" msgid "Tenant ID"
msgstr "客户端 ID" msgstr "客户端 ID"
@ -7899,7 +7885,7 @@ msgstr "远端地址"
msgid "Application User" msgid "Application User"
msgstr "应用用户" msgstr "应用用户"
#: terminal/models/component/terminal.py:176 #: terminal/models/component/terminal.py:184
msgid "Can view terminal config" msgid "Can view terminal config"
msgstr "可以查看终端配置" msgstr "可以查看终端配置"
@ -8823,7 +8809,7 @@ msgstr "工单快照"
msgid "Please try again" msgid "Please try again"
msgstr "请再次尝试" msgstr "请再次尝试"
#: tickets/models/ticket/general.py:481 #: tickets/models/ticket/general.py:483
msgid "Super ticket" msgid "Super ticket"
msgstr "超级工单" msgstr "超级工单"

View File

@ -1413,5 +1413,6 @@
"setVariable": "Set variable", "setVariable": "Set variable",
"JobsAudit": "Jobs audit", "JobsAudit": "Jobs audit",
"JobList": "Job List", "JobList": "Job List",
"StopJobMsg": "Stop job successfully" "StopJobMsg": "Stop job successfully",
"ExtraArgsFormatError": "Format error, please enter according to the requirements"
} }

View File

@ -1418,5 +1418,6 @@
"setVariable": "设置参数", "setVariable": "设置参数",
"JobsAudit": "作业审计", "JobsAudit": "作业审计",
"JobList": "作业列表", "JobList": "作业列表",
"StopJobMsg": "停止成功" "StopJobMsg": "停止成功",
"ExtraArgsFormatError": "格式错误,请按要求输入"
} }

View File

@ -1,7 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from common.serializers.fields import ReadableHiddenField, LabeledChoiceField, EncryptedField from common.serializers.fields import ReadableHiddenField, LabeledChoiceField, EncryptedField
from common.serializers.mixin import CommonBulkModelSerializer from common.serializers.mixin import CommonBulkModelSerializer
@ -15,6 +17,13 @@ __all__ = [
class VariableSerializer(CommonBulkModelSerializer): class VariableSerializer(CommonBulkModelSerializer):
name = serializers.CharField(max_length=1024, label=_('Name'), required=True)
var_name = serializers.CharField(
max_length=1024, required=True, label=_('Variable name'),
help_text=_("The variable name used in the script has a fixed prefix 'jms_' followed by the input variable "
"name. For example, if the variable name is 'name,' the final generated environment variable will "
"be 'jms_name'.")
)
creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
type = LabeledChoiceField( type = LabeledChoiceField(
choices=FieldType.choices, default=FieldType.text, label=_("Variable Type") choices=FieldType.choices, default=FieldType.text, label=_("Variable Type")
@ -82,64 +91,68 @@ class PlaybookVariableSerializer(VariableSerializer):
fields = VariableSerializer.Meta.fields fields = VariableSerializer.Meta.fields
def create_dynamic_text_choices(options):
"""
动态创建一个 TextChoices 子类`options` 应该是一个列表
格式为 [(value1, display1), (value2, display2), ...]
"""
attrs = {
key.upper(): value for value, key in options
}
attrs['choices'] = options
return type('DynamicTextChoices', (models.TextChoices,), attrs)
class VariableFormDataSerializer(serializers.Serializer): class VariableFormDataSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
request = self.context.get('request') request = self.context.get('request')
if not request: if not request:
return return
params = request.query_params params = request.query_params
if params.get('format') == 'openapi': if params.get('format') == 'openapi':
return return
job = params.get('job') job = params.get('job')
adhoc = params.get('adhoc') adhoc = params.get('adhoc')
playbook = params.get('playbook') playbook = params.get('playbook')
if job:
variables = Variable.objects.filter(job=job).all() if not any([job, adhoc, playbook]):
elif adhoc: raise ValidationError("One of 'job', 'adhoc', or 'playbook' is required.")
variables = Variable.objects.filter(adhoc=adhoc).all()
else: try:
variables = Variable.objects.filter(playbook=playbook).all() variables = Variable.objects.filter(
job=job if job else None,
adhoc=adhoc if adhoc else None,
playbook=playbook if playbook else None
).all()
except ObjectDoesNotExist:
raise ValidationError("Invalid job, adhoc, or playbook ID.")
dynamic_fields = [var.form_data for var in variables] dynamic_fields = [var.form_data for var in variables]
if dynamic_fields:
for field in dynamic_fields: for field in dynamic_fields:
self._add_field(field)
def _add_field(self, field):
field_type = field['type'] field_type = field['type']
required = field['required'] required = field['required']
var_name = field["var_name"] var_name = field["var_name"]
label = field["label"] label = field["label"]
help_text = field['help_text'] help_text = field['help_text']
default = field['default'] default = field.get('default', None)
if field_type == FieldType.text: if field_type == FieldType.text:
self.fields[var_name] = serializers.CharField( self.fields[var_name] = serializers.CharField(
max_length=1024, label=label, help_text=help_text, required=required max_length=1024, label=label, help_text=help_text, required=required
) )
elif field_type == FieldType.select: elif field_type == FieldType.select:
self._add_select_field(field, var_name, required, label, help_text)
if required and default is not None:
self.fields[var_name].default = default
def _add_select_field(self, field, var_name, required, label, help_text):
extra_args = field.get('extra_args', {}) extra_args = field.get('extra_args', {})
options = extra_args.get('options', '').splitlines() options = extra_args.get('options', '').splitlines()
DynamicFieldType = models.TextChoices( try:
'DynamicFieldType', options_data = {option.split(':')[0]: option.split(':')[1] for option in options}
{ except Exception as e:
option.split(':')[0]: option.split(':')[1] for option in raise ValidationError(f"Invalid options format: {str(e)}")
options
} DynamicFieldType = models.TextChoices('DynamicFieldType', options_data)
)
self.fields[var_name] = LabeledChoiceField( self.fields[var_name] = LabeledChoiceField(
choices=DynamicFieldType.choices, required=required, label=label, choices=DynamicFieldType.choices, required=required, label=label,
help_text=help_text help_text=help_text
) )
if required and default is not None:
self.fields[var_name].default = default