jumpserver/apps/tickets/models/ticket/general.py

466 lines
16 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
#
import json
from typing import Callable
from django.db import models
from django.db.models import Q
from django.db.models.fields import related
from django.db.utils import IntegrityError
from django.forms import model_to_dict
from django.utils.translation import ugettext_lazy as _
from common.db.encoder import ModelJSONFieldEncoder
from common.db.models import JMSBaseModel
from common.exceptions import JMSException
from common.utils.timezone import as_current_tz
v3.0.0-rc1 (#9322) * perf:automation * pref: 修改账号推送 * perf: 修改 assets * perf: 修改 accounts * feat: 优化代码 * fix: 修复 ObjectRelatedField 获取 value attr 时先判断是否有 attr 属性 * perf: 增加翻译 * feat: 增加部分翻译 * feat: 去除无用列 * perf: ticket remove app * fix: 修复创建账号备份任务失败的问题 * perf: 添加 accounts app * perf: ticket type serializer (#9252) Co-authored-by: feng <1304903146@qq.com> * perf: ticket * perf: 修改 accounts api * perf: 优化 AssetPermissionSerializer fields 顺序 * perf: 修改 accounts * feat: 限制常用用户名api返回长度 * feat: 限制常用用户名api返回长度 * perf: 修改 LoginAssetACL 序列类,增加 users_username_group, accounts_username_group... 字段 * perf: 修改 CommandFilterACLSerializer 增加 command_groups_amount 字段 * perf: 修改rbac API啥的 (#9254) * perf: migrate * perf: 修改 AssetPermedSerializer domain 字段类型 * perf: 放开push account 权限位 * perf: 修改 accounts * perf: 修改 LoginACLSerializer 字段类型 * pref: 修改数据库 migrations * perf: filter asset systemuser * perf: 修改 SessionSerializer 字段类型 * pref: 修改 applet host * perf: 修改 SessionCommandSerializer 字段类型 * perf: 修改 accounts import * perf: 修改 celery datetime * perf: 修改 asset serializer * pref: 修改 labeled field * feat: 修改翻译 * perf: 修改 JobSerializer 字段类型 * feat: 支持使用 ws 发送终断任务 * perf: add AccessTokenAuthentication * perf: 修改 BaseStorageSerializer 字段类型 * perf: 修改 AppletHostSerializer 字段类型 * perf: signal event * perf: asset types automations (#9259) Co-authored-by: feng <1304903146@qq.com> * perf: 修改下载 rdp 文件时返回的 address 地址信息为空的问题 * perf: 修改 AssetSerializer.accounts.secret 为 write_only; 修改 DomainWithGatewaySerializer.gateways 返回 account 信息及 secret 字段; * perf: automation 干库 (#9260) Co-authored-by: feng <1304903146@qq.com> * perf: account push api * feat: 修改迁移文件 * feat: 删除无用代码 * feat: 优化部分资源无操作日志 * perf: 修改 account * perf: perm tree * perf: asset serializers retrieve * perf: 格式化代码 * perf: AutomationExecution (#9268) Co-authored-by: feng <1304903146@qq.com> * perf: AssetDetailSerializer 和 Asset Model 添加 specific_info 字段; * perf: 修改账号推送 * feat: handle ws heartbeat status * perf: k8s tree (#9269) Co-authored-by: feng <1304903146@qq.com> * perf: 修改账号推送 * perf: 修改 asset detail serializer * fix: 修复 windows 不能运行 powershell 命令的问题 * feat: 支持按照资源时间线查看操作活动 * feat: 翻译 * feat: 优化操作日志 * perf: asset clone * fix: 错误的修改改回去 * perf: create asset account * feat: 增加task 刷新续传功能 * fix: applet host deloypment filter host * perf: 修改了 common 结构,和 push accounts * perf: 整理 common 结构 * perf: 修改 const import * perf: 修改 allow bulk destroy * fix: applet host search fileds * perf: applet bulk delete * fix: applet list 404 * perf: 修改 common view * feat: 增加一些翻译, 修复 playbook 上传的错误 * fix: 修改错别字 * perf: 修改 applets status * perf: 修改网关 api * perf: automateion (#9281) Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> * perf: 失效 connect methods 当 applet 删除 或者 host 删除 * perf: 网关账号的密码类型改成 LabelField * perf: chrome applet script * perf: verify code ttl (#9282) Co-authored-by: feng <1304903146@qq.com> * perf: database ping * perf: ws * perf: 修改网关创建 * perf: account task org (#9285) Co-authored-by: feng <1304903146@qq.com> * perf: asset test api * perf: port 添加 account * pref: 修改 db mapper permission * fix: db port mapper list api * perf: account change secret (#9286) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 setup_eager_loading * perf: SecretStrategy * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * pref: web database 信号转发 * perf: account push automation * perf: push filter account * perf: 修改 publish 版本 * perf: 修改网关 * fix: 修改资产 Specific 信息中 JSONField 字段返回 json.loads 对象 * feat: 远程应用内置Navicat Premium 16 * feat: 更新下载链接 * feat: 整理代码格式 * perf: 修改 terminal point * perf: update chrome applet script * fix: 资产 specific 获取 JSONField 时, 判断值的类型不为 list, dict * perf: domain (#9292) Co-authored-by: feng <1304903146@qq.com> * perf: 优化 endpoint 监听端口,仅 oracle 动态 * perf: 修改翻译 * perf: 修改文案 * perf: 修改缺失的翻译 * perf: 修改 endpoint help text * feat: 还原格式 * feat: 去掉基类 * feat: 增加特权账号字段 * perf: decode content * fix: check pid * perf: 修改 smart endpoint * perf: 修改 endpoint mysql default port * feat: 优化 * perf: 修改 endpoint mysql default port * perf: gateway test (#9295) Co-authored-by: feng <1304903146@qq.com> * perf: migrate * perf: 修改 endpoint mysql default port * fix: 修复获取任务执行结果死循环 * feat: 作业审计日志增加字段 * fix: add on_transaction_commit task post save * perf: gateway (#9297) Co-authored-by: feng <1304903146@qq.com> * feat: 过滤 jumpserver 自动产生的用户 * fix: 修复ops节点选择的问题 * fix: 修改 统一 connection-token 和 command 的 review API 返回数据 from_ticket_info * perf: change secret (#9298) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 db port manager * perf: 修改 db port manager * perf: add celery log mark * perf: remove debug log data * fix: navicat use manual type * fix: remove navicate download url * perf: push_account_enabled (#9301) Co-authored-by: feng <1304903146@qq.com> * fix: 修改navicat启动程序MD5值 * perf: push account (#9303) Co-authored-by: feng <1304903146@qq.com> * feat: Redis/MongoDB 支持SSL * fix: 修改授权规则过滤字段 node_name,node_id; 修复获取授权节点下的资产为空的问题; * perf: push account button (#9305) Co-authored-by: feng <1304903146@qq.com> * perf: account push * fix: 修复获取 /user//assets/tree/ 返回用户授权的所有资产 * perf: asset ping (#9307) Co-authored-by: feng <1304903146@qq.com> * perf: asset enabled_info * perf: 优化activity记录都保存至operatelog中 * feat: 远程应用navicat支持试用版连接 * perf: 优化迁移文件 * perf: 修改资产列表 API category type 字段 choices 根据 category 进行返回 * fix * perf: 修改账号列表 API 解决根据 node_id asset_id 搜索账号列表无效的问题 * fix: navicat dba账号登录 * perf: 优化navicat连接 * perf: 修改账号列表 Model Manager 继承自 OrgManager,解决组织过滤问题 * perf: 修改账号列表 Filter 支持根据 platform,category,type 字段搜索 * perf: change secret email (#9312) Co-authored-by: feng <1304903146@qq.com> * feat: 保证认证信息一定清理 * perf: add mariadb * perf: 修改资产类型树数量统计资产或账号 * perf: applet chrome quit * perf: 优化关闭欢迎页面 * fix * perf: executed amount * perf: 修改 built-in applet installation * perf: 修改资产列表增加标签搜索 * perf: 修改资产列表增加标签搜索 * perf: account task automation (#9319) Co-authored-by: feng <1304903146@qq.com> * perf: account trigger * perf: 修改系统设置文案:批量命令执行 -> 作业中心 * perf: 优化migrate (#9320) Co-authored-by: feng <1304903146@qq.com> * perf: 修改资产节点树 API,支持搜索资产、节点 * perf: audit dashboard (#9321) Co-authored-by: feng <1304903146@qq.com> * fix: 修改 has_perm 权限判断兼容 list 和 str 类型 * perf: 修改一些换行 * perf: 修改 ansible config * fix: oracle依赖文件地址错误 (#9324) * perf: ansible mudules * perf: 修改 runner host cwd Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Aaron3S <chenyang@fit2cloud.com> Co-authored-by: Bai <baijiangjie@gmail.com> Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> Co-authored-by: Eric <xplzv@126.com> Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com> Co-authored-by: jiangweidong <80373698+Hi-JWD@users.noreply.github.com>
2023-01-16 11:02:09 +00:00
from common.utils import reverse
from orgs.models import Organization
from orgs.utils import tmp_to_org
from tickets.const import (
TicketType, TicketStatus, TicketState,
TicketLevel, StepState, StepStatus
)
from tickets.errors import AlreadyClosed
from tickets.handlers import get_ticket_handler
from ..flow import TicketFlow
2022-11-17 10:04:38 +00:00
__all__ = [
'Ticket', 'TicketStep', 'TicketAssignee',
'SuperTicket', 'SubTicketManager'
2022-11-17 10:04:38 +00:00
]
class TicketStep(JMSBaseModel):
ticket = models.ForeignKey(
'Ticket', related_name='ticket_steps',
on_delete=models.CASCADE, verbose_name='Ticket'
)
level = models.SmallIntegerField(
default=TicketLevel.one, choices=TicketLevel.choices,
verbose_name=_('Approve level')
)
state = models.CharField(
max_length=64, choices=StepState.choices,
default=StepState.pending, verbose_name=_("State")
)
status = models.CharField(
max_length=16, choices=StepStatus.choices,
default=StepStatus.pending
)
def change_state(self, state, processor):
if state != StepState.closed:
assignees = self.ticket_assignees.filter(assignee=processor)
if not assignees:
raise PermissionError('Only assignees can do this')
assignees.update(state=state)
self.status = StepStatus.closed
self.state = state
self.save(update_fields=['state', 'status'])
def set_active(self):
self.status = StepStatus.active
self.save(update_fields=['status'])
def next(self):
kwargs = dict(ticket=self.ticket, level=self.level + 1, status=StepStatus.pending)
return self.__class__.objects.filter(**kwargs).first()
@property
def processor(self):
processor = self.ticket_assignees.exclude(state=StepState.pending).first()
return processor.assignee if processor else None
class Meta:
verbose_name = _("Ticket step")
class TicketAssignee(JMSBaseModel):
assignee = models.ForeignKey(
'users.User', related_name='ticket_assignees',
on_delete=models.CASCADE, verbose_name='Assignee'
)
state = models.CharField(
choices=TicketState.choices, max_length=64,
default=TicketState.pending
)
step = models.ForeignKey(
'tickets.TicketStep', related_name='ticket_assignees',
on_delete=models.CASCADE
)
class Meta:
verbose_name = _('Ticket assignee')
def __str__(self):
return '{0.assignee.name}({0.assignee.username})_{0.step}'.format(self)
class StatusMixin:
State = TicketState
Status = TicketStatus
state: str
status: str
applicant_id: str
applicant: models.ForeignKey
current_step: TicketStep
save: Callable
create_process_steps_by_flow: Callable
create_process_steps_by_assignees: Callable
assignees: Callable
set_serial_num: Callable
set_rel_snapshot: Callable
approval_step: int
handler: None
flow: TicketFlow
ticket_steps: models.Manager
def is_state(self, state: TicketState):
return self.state == state
def is_status(self, status: TicketStatus):
return self.status == status
def _open(self):
self.set_serial_num()
self.set_rel_snapshot()
self._change_state_by_applicant(TicketState.pending)
def open(self):
self.create_process_steps_by_flow()
self._open()
def open_by_system(self, assignees):
self.create_process_steps_by_assignees(assignees)
self._open()
def approve(self, processor):
self.set_rel_snapshot()
self._change_state(StepState.approved, processor)
def reject(self, processor):
self._change_state(StepState.rejected, processor)
def reopen(self):
self._change_state_by_applicant(TicketState.reopen)
def close(self):
self._change_state(TicketState.closed, self.applicant)
def _change_state_by_applicant(self, state):
if state == TicketState.closed:
self.status = TicketStatus.closed
elif state in [TicketState.reopen, TicketState.pending]:
self.status = TicketStatus.open
else:
raise ValueError("Not supported state: {}".format(state))
self.state = state
self.save(update_fields=['state', 'status'])
self.handler.on_change_state(state)
def _change_state(self, state, processor):
if self.is_status(self.Status.closed):
raise AlreadyClosed
current_step = self.current_step
current_step.change_state(state, processor)
self._finish_or_next(current_step, state)
def _finish_or_next(self, current_step, state):
next_step = current_step.next()
# 提前结束,或者最后一步
if state in [TicketState.rejected, TicketState.closed] or not next_step:
self.state = state
self.status = Ticket.Status.closed
self.save(update_fields=['state', 'status'])
self.handler.on_step_state_change(current_step, state)
else:
self.handler.on_step_state_change(current_step, state)
next_step.set_active()
self.approval_step += 1
self.save(update_fields=['approval_step'])
@property
def process_map(self):
process_map = []
for step in self.ticket_steps.all():
processor_id = ''
assignee_ids = []
processor_display = ''
assignees_display = []
state = step.state
for i in step.ticket_assignees.all().prefetch_related('assignee'):
assignee_id = i.assignee_id
assignee_display = str(i.assignee)
if state != StepState.pending and state == i.state:
processor_id = assignee_id
processor_display = assignee_display
if state == StepState.closed:
processor_id = self.applicant_id
processor_display = str(self.applicant)
assignee_ids.append(assignee_id)
assignees_display.append(assignee_display)
step_info = {
'state': state,
'assignees': assignee_ids,
2022-11-17 11:20:54 +00:00
'processor': processor_id,
'approval_level': step.level,
'assignees_display': assignees_display,
'approval_date': str(step.date_updated),
'processor_display': processor_display
}
process_map.append(step_info)
return process_map
def exclude_applicant(self, assignees, applicant=None):
applicant = applicant if applicant else self.applicant
if len(assignees) != 1:
assignees = set(assignees) - {applicant, }
return list(assignees)
def create_process_steps_by_flow(self):
org_id = self.flow.org_id
flow_rules = self.flow.rules.order_by('level')
for rule in flow_rules:
assignees = rule.get_assignees(org_id=org_id)
assignees = self.exclude_applicant(assignees, self.applicant)
2022-11-17 11:20:54 +00:00
step = TicketStep.objects.create(ticket=self, level=rule.level)
step_assignees = [TicketAssignee(step=step, assignee=user) for user in assignees]
TicketAssignee.objects.bulk_create(step_assignees)
def create_process_steps_by_assignees(self, assignees):
step = TicketStep.objects.create(ticket=self, level=1)
2022-11-17 11:20:54 +00:00
assignees = self.exclude_applicant(assignees, self.applicant)
ticket_assignees = [TicketAssignee(step=step, assignee=user) for user in assignees]
TicketAssignee.objects.bulk_create(ticket_assignees)
@property
def current_step(self):
return self.ticket_steps.filter(level=self.approval_step).first()
@property
def current_assignees(self):
ticket_assignees = self.current_step.ticket_assignees.all()
return [i.assignee for i in ticket_assignees]
@property
def processor(self):
v3.0.0-rc1 (#9322) * perf:automation * pref: 修改账号推送 * perf: 修改 assets * perf: 修改 accounts * feat: 优化代码 * fix: 修复 ObjectRelatedField 获取 value attr 时先判断是否有 attr 属性 * perf: 增加翻译 * feat: 增加部分翻译 * feat: 去除无用列 * perf: ticket remove app * fix: 修复创建账号备份任务失败的问题 * perf: 添加 accounts app * perf: ticket type serializer (#9252) Co-authored-by: feng <1304903146@qq.com> * perf: ticket * perf: 修改 accounts api * perf: 优化 AssetPermissionSerializer fields 顺序 * perf: 修改 accounts * feat: 限制常用用户名api返回长度 * feat: 限制常用用户名api返回长度 * perf: 修改 LoginAssetACL 序列类,增加 users_username_group, accounts_username_group... 字段 * perf: 修改 CommandFilterACLSerializer 增加 command_groups_amount 字段 * perf: 修改rbac API啥的 (#9254) * perf: migrate * perf: 修改 AssetPermedSerializer domain 字段类型 * perf: 放开push account 权限位 * perf: 修改 accounts * perf: 修改 LoginACLSerializer 字段类型 * pref: 修改数据库 migrations * perf: filter asset systemuser * perf: 修改 SessionSerializer 字段类型 * pref: 修改 applet host * perf: 修改 SessionCommandSerializer 字段类型 * perf: 修改 accounts import * perf: 修改 celery datetime * perf: 修改 asset serializer * pref: 修改 labeled field * feat: 修改翻译 * perf: 修改 JobSerializer 字段类型 * feat: 支持使用 ws 发送终断任务 * perf: add AccessTokenAuthentication * perf: 修改 BaseStorageSerializer 字段类型 * perf: 修改 AppletHostSerializer 字段类型 * perf: signal event * perf: asset types automations (#9259) Co-authored-by: feng <1304903146@qq.com> * perf: 修改下载 rdp 文件时返回的 address 地址信息为空的问题 * perf: 修改 AssetSerializer.accounts.secret 为 write_only; 修改 DomainWithGatewaySerializer.gateways 返回 account 信息及 secret 字段; * perf: automation 干库 (#9260) Co-authored-by: feng <1304903146@qq.com> * perf: account push api * feat: 修改迁移文件 * feat: 删除无用代码 * feat: 优化部分资源无操作日志 * perf: 修改 account * perf: perm tree * perf: asset serializers retrieve * perf: 格式化代码 * perf: AutomationExecution (#9268) Co-authored-by: feng <1304903146@qq.com> * perf: AssetDetailSerializer 和 Asset Model 添加 specific_info 字段; * perf: 修改账号推送 * feat: handle ws heartbeat status * perf: k8s tree (#9269) Co-authored-by: feng <1304903146@qq.com> * perf: 修改账号推送 * perf: 修改 asset detail serializer * fix: 修复 windows 不能运行 powershell 命令的问题 * feat: 支持按照资源时间线查看操作活动 * feat: 翻译 * feat: 优化操作日志 * perf: asset clone * fix: 错误的修改改回去 * perf: create asset account * feat: 增加task 刷新续传功能 * fix: applet host deloypment filter host * perf: 修改了 common 结构,和 push accounts * perf: 整理 common 结构 * perf: 修改 const import * perf: 修改 allow bulk destroy * fix: applet host search fileds * perf: applet bulk delete * fix: applet list 404 * perf: 修改 common view * feat: 增加一些翻译, 修复 playbook 上传的错误 * fix: 修改错别字 * perf: 修改 applets status * perf: 修改网关 api * perf: automateion (#9281) Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> * perf: 失效 connect methods 当 applet 删除 或者 host 删除 * perf: 网关账号的密码类型改成 LabelField * perf: chrome applet script * perf: verify code ttl (#9282) Co-authored-by: feng <1304903146@qq.com> * perf: database ping * perf: ws * perf: 修改网关创建 * perf: account task org (#9285) Co-authored-by: feng <1304903146@qq.com> * perf: asset test api * perf: port 添加 account * pref: 修改 db mapper permission * fix: db port mapper list api * perf: account change secret (#9286) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 setup_eager_loading * perf: SecretStrategy * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * pref: web database 信号转发 * perf: account push automation * perf: push filter account * perf: 修改 publish 版本 * perf: 修改网关 * fix: 修改资产 Specific 信息中 JSONField 字段返回 json.loads 对象 * feat: 远程应用内置Navicat Premium 16 * feat: 更新下载链接 * feat: 整理代码格式 * perf: 修改 terminal point * perf: update chrome applet script * fix: 资产 specific 获取 JSONField 时, 判断值的类型不为 list, dict * perf: domain (#9292) Co-authored-by: feng <1304903146@qq.com> * perf: 优化 endpoint 监听端口,仅 oracle 动态 * perf: 修改翻译 * perf: 修改文案 * perf: 修改缺失的翻译 * perf: 修改 endpoint help text * feat: 还原格式 * feat: 去掉基类 * feat: 增加特权账号字段 * perf: decode content * fix: check pid * perf: 修改 smart endpoint * perf: 修改 endpoint mysql default port * feat: 优化 * perf: 修改 endpoint mysql default port * perf: gateway test (#9295) Co-authored-by: feng <1304903146@qq.com> * perf: migrate * perf: 修改 endpoint mysql default port * fix: 修复获取任务执行结果死循环 * feat: 作业审计日志增加字段 * fix: add on_transaction_commit task post save * perf: gateway (#9297) Co-authored-by: feng <1304903146@qq.com> * feat: 过滤 jumpserver 自动产生的用户 * fix: 修复ops节点选择的问题 * fix: 修改 统一 connection-token 和 command 的 review API 返回数据 from_ticket_info * perf: change secret (#9298) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 db port manager * perf: 修改 db port manager * perf: add celery log mark * perf: remove debug log data * fix: navicat use manual type * fix: remove navicate download url * perf: push_account_enabled (#9301) Co-authored-by: feng <1304903146@qq.com> * fix: 修改navicat启动程序MD5值 * perf: push account (#9303) Co-authored-by: feng <1304903146@qq.com> * feat: Redis/MongoDB 支持SSL * fix: 修改授权规则过滤字段 node_name,node_id; 修复获取授权节点下的资产为空的问题; * perf: push account button (#9305) Co-authored-by: feng <1304903146@qq.com> * perf: account push * fix: 修复获取 /user//assets/tree/ 返回用户授权的所有资产 * perf: asset ping (#9307) Co-authored-by: feng <1304903146@qq.com> * perf: asset enabled_info * perf: 优化activity记录都保存至operatelog中 * feat: 远程应用navicat支持试用版连接 * perf: 优化迁移文件 * perf: 修改资产列表 API category type 字段 choices 根据 category 进行返回 * fix * perf: 修改账号列表 API 解决根据 node_id asset_id 搜索账号列表无效的问题 * fix: navicat dba账号登录 * perf: 优化navicat连接 * perf: 修改账号列表 Model Manager 继承自 OrgManager,解决组织过滤问题 * perf: 修改账号列表 Filter 支持根据 platform,category,type 字段搜索 * perf: change secret email (#9312) Co-authored-by: feng <1304903146@qq.com> * feat: 保证认证信息一定清理 * perf: add mariadb * perf: 修改资产类型树数量统计资产或账号 * perf: applet chrome quit * perf: 优化关闭欢迎页面 * fix * perf: executed amount * perf: 修改 built-in applet installation * perf: 修改资产列表增加标签搜索 * perf: 修改资产列表增加标签搜索 * perf: account task automation (#9319) Co-authored-by: feng <1304903146@qq.com> * perf: account trigger * perf: 修改系统设置文案:批量命令执行 -> 作业中心 * perf: 优化migrate (#9320) Co-authored-by: feng <1304903146@qq.com> * perf: 修改资产节点树 API,支持搜索资产、节点 * perf: audit dashboard (#9321) Co-authored-by: feng <1304903146@qq.com> * fix: 修改 has_perm 权限判断兼容 list 和 str 类型 * perf: 修改一些换行 * perf: 修改 ansible config * fix: oracle依赖文件地址错误 (#9324) * perf: ansible mudules * perf: 修改 runner host cwd Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Aaron3S <chenyang@fit2cloud.com> Co-authored-by: Bai <baijiangjie@gmail.com> Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> Co-authored-by: Eric <xplzv@126.com> Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com> Co-authored-by: jiangweidong <80373698+Hi-JWD@users.noreply.github.com>
2023-01-16 11:02:09 +00:00
""" 返回最后一步的处理人 """
return self.current_step.processor
def has_current_assignee(self, assignee):
return self.ticket_steps.filter(
2022-11-17 11:20:54 +00:00
level=self.approval_step,
ticket_assignees__assignee=assignee,
).exists()
def has_all_assignee(self, assignee):
return self.ticket_steps.filter(ticket_assignees__assignee=assignee).exists()
@property
def handler(self):
return get_ticket_handler(ticket=self)
class Ticket(StatusMixin, JMSBaseModel):
title = models.CharField(max_length=256, verbose_name=_('Title'))
type = models.CharField(
max_length=64, choices=TicketType.choices,
default=TicketType.general, verbose_name=_('Type')
)
state = models.CharField(
max_length=16, choices=TicketState.choices,
default=TicketState.pending, verbose_name=_('State')
)
status = models.CharField(
max_length=16, choices=TicketStatus.choices,
default=TicketStatus.open, verbose_name=_('Status')
)
# 申请人
applicant = models.ForeignKey(
2022-11-17 10:04:38 +00:00
'users.User', related_name='applied_tickets', null=True,
on_delete=models.SET_NULL, verbose_name=_("Applicant")
)
flow = models.ForeignKey(
2022-11-17 10:04:38 +00:00
'TicketFlow', related_name='tickets', null=True,
on_delete=models.SET_NULL, verbose_name=_('TicketFlow')
)
approval_step = models.SmallIntegerField(
default=TicketLevel.one, choices=TicketLevel.choices, verbose_name=_('Approval step')
)
2022-11-17 10:04:38 +00:00
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
rel_snapshot = models.JSONField(verbose_name=_('Relation snapshot'), default=dict)
2022-11-17 10:04:38 +00:00
serial_num = models.CharField(_('Serial number'), max_length=128, unique=True, null=True)
meta = models.JSONField(encoder=ModelJSONFieldEncoder, default=dict, verbose_name=_("Meta"))
org_id = models.CharField(
max_length=36, blank=True, default='', verbose_name=_('Organization'), db_index=True
)
class Meta:
ordering = ('-date_created',)
verbose_name = _('Ticket')
def __str__(self):
return '{}({})'.format(self.title, self.applicant)
@property
def spec_ticket(self):
attr = self.type.replace('_', '') + 'ticket'
return getattr(self, attr)
# TODO 先单独处理一下
@property
def org_name(self):
org = Organization.get_instance(self.org_id)
return org.name
def is_type(self, tp: TicketType):
return self.type == tp
@classmethod
def get_user_related_tickets(cls, user):
queries = Q(applicant=user) | Q(ticket_steps__ticket_assignees__assignee=user)
2022-11-17 11:20:54 +00:00
tickets = cls.objects.filter(queries).distinct()
return tickets
def get_current_ticket_flow_approve(self):
return self.flow.rules.filter(level=self.approval_step).first()
@classmethod
def all(cls):
return cls.objects.all()
def set_rel_snapshot(self, save=True):
rel_fields = set()
m2m_fields = set()
excludes = ['ticket_ptr_id', 'ticket_ptr', 'flow_id', 'flow', 'applicant_id']
for name, field in self._meta._forward_fields_map.items():
if name in excludes:
continue
if isinstance(field, related.RelatedField):
rel_fields.add(name)
if isinstance(field, related.ManyToManyField):
m2m_fields.add(name)
snapshot = {}
with tmp_to_org(self.org_id):
for field in rel_fields:
value = getattr(self, field)
if field in m2m_fields:
value = [str(v) for v in value.all()]
else:
value = str(value) if value else ''
snapshot[field] = value
self.rel_snapshot.update(snapshot)
if save:
self.save(update_fields=('rel_snapshot',))
def get_next_serial_num(self):
date_created = as_current_tz(self.date_created)
date_prefix = date_created.strftime('%Y%m%d')
ticket = Ticket.objects.all().select_for_update().filter(
serial_num__startswith=date_prefix
).order_by('-date_created').first()
last_num = 0
if ticket:
last_num = ticket.serial_num[8:]
last_num = int(last_num)
num = '%04d' % (last_num + 1)
return '{}{}'.format(date_prefix, num)
def set_serial_num(self):
if self.serial_num:
return
try:
self.serial_num = self.get_next_serial_num()
self.save(update_fields=('serial_num',))
except IntegrityError as e:
if e.args[0] == 1062:
# 虽然做了 `select_for_update` 但是每天的第一条工单仍可能造成冲突
# 但概率小,这里只报错,用户重新提交即可
raise JMSException(detail=_('Please try again'), code='please_try_again')
raise e
def get_field_display(self, name, field, data: dict):
value = data.get(name)
if hasattr(self, f'get_{name}_display'):
value = getattr(self, f'get_{name}_display')()
elif isinstance(field, related.ForeignKey):
value = self.rel_snapshot[name]
elif isinstance(field, related.ManyToManyField):
if isinstance(self.rel_snapshot[name], str):
value = self.rel_snapshot[name]
elif isinstance(self.rel_snapshot[name], list):
value = ','.join(self.rel_snapshot[name])
elif isinstance(value, list):
value = ', '.join(value)
return value
def get_local_snapshot(self):
2022-11-17 11:20:54 +00:00
snapshot = {}
excludes = ['ticket_ptr']
fields = self._meta._forward_fields_map
json_data = json.dumps(model_to_dict(self), cls=ModelJSONFieldEncoder)
data = json.loads(json_data)
local_fields = self._meta.local_fields + self._meta.local_many_to_many
item_names = [field.name for field in local_fields if field.name not in excludes]
for name in item_names:
field = fields[name]
value = self.get_field_display(name, field, data)
snapshot[field.verbose_name] = value
return snapshot
v3.0.0-rc1 (#9322) * perf:automation * pref: 修改账号推送 * perf: 修改 assets * perf: 修改 accounts * feat: 优化代码 * fix: 修复 ObjectRelatedField 获取 value attr 时先判断是否有 attr 属性 * perf: 增加翻译 * feat: 增加部分翻译 * feat: 去除无用列 * perf: ticket remove app * fix: 修复创建账号备份任务失败的问题 * perf: 添加 accounts app * perf: ticket type serializer (#9252) Co-authored-by: feng <1304903146@qq.com> * perf: ticket * perf: 修改 accounts api * perf: 优化 AssetPermissionSerializer fields 顺序 * perf: 修改 accounts * feat: 限制常用用户名api返回长度 * feat: 限制常用用户名api返回长度 * perf: 修改 LoginAssetACL 序列类,增加 users_username_group, accounts_username_group... 字段 * perf: 修改 CommandFilterACLSerializer 增加 command_groups_amount 字段 * perf: 修改rbac API啥的 (#9254) * perf: migrate * perf: 修改 AssetPermedSerializer domain 字段类型 * perf: 放开push account 权限位 * perf: 修改 accounts * perf: 修改 LoginACLSerializer 字段类型 * pref: 修改数据库 migrations * perf: filter asset systemuser * perf: 修改 SessionSerializer 字段类型 * pref: 修改 applet host * perf: 修改 SessionCommandSerializer 字段类型 * perf: 修改 accounts import * perf: 修改 celery datetime * perf: 修改 asset serializer * pref: 修改 labeled field * feat: 修改翻译 * perf: 修改 JobSerializer 字段类型 * feat: 支持使用 ws 发送终断任务 * perf: add AccessTokenAuthentication * perf: 修改 BaseStorageSerializer 字段类型 * perf: 修改 AppletHostSerializer 字段类型 * perf: signal event * perf: asset types automations (#9259) Co-authored-by: feng <1304903146@qq.com> * perf: 修改下载 rdp 文件时返回的 address 地址信息为空的问题 * perf: 修改 AssetSerializer.accounts.secret 为 write_only; 修改 DomainWithGatewaySerializer.gateways 返回 account 信息及 secret 字段; * perf: automation 干库 (#9260) Co-authored-by: feng <1304903146@qq.com> * perf: account push api * feat: 修改迁移文件 * feat: 删除无用代码 * feat: 优化部分资源无操作日志 * perf: 修改 account * perf: perm tree * perf: asset serializers retrieve * perf: 格式化代码 * perf: AutomationExecution (#9268) Co-authored-by: feng <1304903146@qq.com> * perf: AssetDetailSerializer 和 Asset Model 添加 specific_info 字段; * perf: 修改账号推送 * feat: handle ws heartbeat status * perf: k8s tree (#9269) Co-authored-by: feng <1304903146@qq.com> * perf: 修改账号推送 * perf: 修改 asset detail serializer * fix: 修复 windows 不能运行 powershell 命令的问题 * feat: 支持按照资源时间线查看操作活动 * feat: 翻译 * feat: 优化操作日志 * perf: asset clone * fix: 错误的修改改回去 * perf: create asset account * feat: 增加task 刷新续传功能 * fix: applet host deloypment filter host * perf: 修改了 common 结构,和 push accounts * perf: 整理 common 结构 * perf: 修改 const import * perf: 修改 allow bulk destroy * fix: applet host search fileds * perf: applet bulk delete * fix: applet list 404 * perf: 修改 common view * feat: 增加一些翻译, 修复 playbook 上传的错误 * fix: 修改错别字 * perf: 修改 applets status * perf: 修改网关 api * perf: automateion (#9281) Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> * perf: 失效 connect methods 当 applet 删除 或者 host 删除 * perf: 网关账号的密码类型改成 LabelField * perf: chrome applet script * perf: verify code ttl (#9282) Co-authored-by: feng <1304903146@qq.com> * perf: database ping * perf: ws * perf: 修改网关创建 * perf: account task org (#9285) Co-authored-by: feng <1304903146@qq.com> * perf: asset test api * perf: port 添加 account * pref: 修改 db mapper permission * fix: db port mapper list api * perf: account change secret (#9286) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 setup_eager_loading * perf: SecretStrategy * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * feat: 修改 ConnectionToken Create API 支持校验 ACL 逻辑 * pref: web database 信号转发 * perf: account push automation * perf: push filter account * perf: 修改 publish 版本 * perf: 修改网关 * fix: 修改资产 Specific 信息中 JSONField 字段返回 json.loads 对象 * feat: 远程应用内置Navicat Premium 16 * feat: 更新下载链接 * feat: 整理代码格式 * perf: 修改 terminal point * perf: update chrome applet script * fix: 资产 specific 获取 JSONField 时, 判断值的类型不为 list, dict * perf: domain (#9292) Co-authored-by: feng <1304903146@qq.com> * perf: 优化 endpoint 监听端口,仅 oracle 动态 * perf: 修改翻译 * perf: 修改文案 * perf: 修改缺失的翻译 * perf: 修改 endpoint help text * feat: 还原格式 * feat: 去掉基类 * feat: 增加特权账号字段 * perf: decode content * fix: check pid * perf: 修改 smart endpoint * perf: 修改 endpoint mysql default port * feat: 优化 * perf: 修改 endpoint mysql default port * perf: gateway test (#9295) Co-authored-by: feng <1304903146@qq.com> * perf: migrate * perf: 修改 endpoint mysql default port * fix: 修复获取任务执行结果死循环 * feat: 作业审计日志增加字段 * fix: add on_transaction_commit task post save * perf: gateway (#9297) Co-authored-by: feng <1304903146@qq.com> * feat: 过滤 jumpserver 自动产生的用户 * fix: 修复ops节点选择的问题 * fix: 修改 统一 connection-token 和 command 的 review API 返回数据 from_ticket_info * perf: change secret (#9298) Co-authored-by: feng <1304903146@qq.com> * perf: 修改 db port manager * perf: 修改 db port manager * perf: add celery log mark * perf: remove debug log data * fix: navicat use manual type * fix: remove navicate download url * perf: push_account_enabled (#9301) Co-authored-by: feng <1304903146@qq.com> * fix: 修改navicat启动程序MD5值 * perf: push account (#9303) Co-authored-by: feng <1304903146@qq.com> * feat: Redis/MongoDB 支持SSL * fix: 修改授权规则过滤字段 node_name,node_id; 修复获取授权节点下的资产为空的问题; * perf: push account button (#9305) Co-authored-by: feng <1304903146@qq.com> * perf: account push * fix: 修复获取 /user//assets/tree/ 返回用户授权的所有资产 * perf: asset ping (#9307) Co-authored-by: feng <1304903146@qq.com> * perf: asset enabled_info * perf: 优化activity记录都保存至operatelog中 * feat: 远程应用navicat支持试用版连接 * perf: 优化迁移文件 * perf: 修改资产列表 API category type 字段 choices 根据 category 进行返回 * fix * perf: 修改账号列表 API 解决根据 node_id asset_id 搜索账号列表无效的问题 * fix: navicat dba账号登录 * perf: 优化navicat连接 * perf: 修改账号列表 Model Manager 继承自 OrgManager,解决组织过滤问题 * perf: 修改账号列表 Filter 支持根据 platform,category,type 字段搜索 * perf: change secret email (#9312) Co-authored-by: feng <1304903146@qq.com> * feat: 保证认证信息一定清理 * perf: add mariadb * perf: 修改资产类型树数量统计资产或账号 * perf: applet chrome quit * perf: 优化关闭欢迎页面 * fix * perf: executed amount * perf: 修改 built-in applet installation * perf: 修改资产列表增加标签搜索 * perf: 修改资产列表增加标签搜索 * perf: account task automation (#9319) Co-authored-by: feng <1304903146@qq.com> * perf: account trigger * perf: 修改系统设置文案:批量命令执行 -> 作业中心 * perf: 优化migrate (#9320) Co-authored-by: feng <1304903146@qq.com> * perf: 修改资产节点树 API,支持搜索资产、节点 * perf: audit dashboard (#9321) Co-authored-by: feng <1304903146@qq.com> * fix: 修改 has_perm 权限判断兼容 list 和 str 类型 * perf: 修改一些换行 * perf: 修改 ansible config * fix: oracle依赖文件地址错误 (#9324) * perf: ansible mudules * perf: 修改 runner host cwd Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Aaron3S <chenyang@fit2cloud.com> Co-authored-by: Bai <baijiangjie@gmail.com> Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com> Co-authored-by: Eric <xplzv@126.com> Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com> Co-authored-by: jiangweidong <80373698+Hi-JWD@users.noreply.github.com>
2023-01-16 11:02:09 +00:00
def get_extra_info_of_review(self, user=None):
if user and user.is_service_account:
url_ticket_status = reverse(
view_name='api-tickets:super-ticket-status', kwargs={'pk': str(self.id)}
)
check_ticket_api = {'method': 'GET', 'url': url_ticket_status}
close_ticket_api = {'method': 'DELETE', 'url': url_ticket_status}
else:
url_ticket_status = reverse(
view_name='api-tickets:ticket-detail', kwargs={'pk': str(self.id)}
)
url_ticket_close = reverse(
view_name='api-tickets:ticket-close', kwargs={'pk': str(self.id)}
)
check_ticket_api = {'method': 'GET', 'url': url_ticket_status}
close_ticket_api = {'method': 'PUT', 'url': url_ticket_close}
url_ticket_detail_external = reverse(
view_name='api-tickets:ticket-detail',
kwargs={'pk': str(self.id)},
external=True,
api_to_ui=True
)
ticket_assignees = self.current_step.ticket_assignees.all()
return {
'check_ticket_api': check_ticket_api,
'close_ticket_api': close_ticket_api,
'ticket_detail_page_url': '{url}?type={type}'.format(
url=url_ticket_detail_external, type=self.type
),
'assignees': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees]
}
class SuperTicket(Ticket):
class Meta:
proxy = True
verbose_name = _("Super ticket")
class SubTicketManager(models.Manager):
pass