mirror of https://github.com/jumpserver/jumpserver
commit
e4ac73896f
|
@ -1,35 +0,0 @@
|
|||
---
|
||||
name: 需求建议
|
||||
about: 提出针对本项目的想法和建议
|
||||
title: "[Feature] 需求标题"
|
||||
labels: 类型:需求
|
||||
assignees:
|
||||
- ibuler
|
||||
- baijiangjie
|
||||
---
|
||||
|
||||
## 注意
|
||||
_针对过于简单的需求描述不予考虑。请确保提供足够的细节和信息以支持功能的开发和实现。_
|
||||
|
||||
## 功能名称
|
||||
[在这里输入功能的名称或标题]
|
||||
|
||||
## 功能描述
|
||||
[在这里描述该功能的详细内容,包括其作用、目的和所需的功能]
|
||||
|
||||
## 用户故事(可选)
|
||||
[如果适用,可以提供用户故事来更好地理解该功能的使用场景和用户期望]
|
||||
|
||||
## 功能要求
|
||||
- [要求1:描述该功能的具体要求,如界面设计、交互逻辑等]
|
||||
- [要求2:描述该功能的另一个具体要求]
|
||||
- [以此类推,列出所有相关的功能要求]
|
||||
|
||||
## 示例或原型(可选)
|
||||
[如果有的话,提供该功能的示例或原型图以更好地说明功能的实现方式]
|
||||
|
||||
## 优先级
|
||||
[描述该功能的优先级,如高、中、低,或使用数字等其他标识]
|
||||
|
||||
## 备注(可选)
|
||||
[在这里添加任何其他相关信息或备注]
|
|
@ -0,0 +1,72 @@
|
|||
name: '🐛 Bug Report'
|
||||
description: 'Report an Bug'
|
||||
title: '[Bug] '
|
||||
labels: ['🐛 Bug']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: 'Product Version'
|
||||
description: The versions prior to v2.28 (inclusive) are no longer supported.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Product Edition'
|
||||
options:
|
||||
- label: 'Community Edition'
|
||||
- label: 'Enterprise Edition'
|
||||
- label: 'Enterprise Trial Edition'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Installation Method'
|
||||
options:
|
||||
- label: 'Online Installation (One-click command installation)'
|
||||
- label: 'Offline Package Installation'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: 'Source Code'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Environment Information'
|
||||
description: Please provide a clear and concise description outlining your environment information.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🐛 Bug Description'
|
||||
description:
|
||||
Please provide a clear and concise description of the defect. If the issue is complex, please provide detailed explanations. <br/>
|
||||
Unclear descriptions will not be processed. Please ensure you provide enough detail and information to support replicating and fixing the defect.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Recurrence Steps'
|
||||
description: Please provide a clear and concise description outlining how to reproduce the issue.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Expected Behavior'
|
||||
description: Please provide a clear and concise description of what you expect to happen.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Additional Information'
|
||||
description: Please add any additional background information about the issue here.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Attempted Solutions'
|
||||
description: If you have already attempted to solve the issue, please list the solutions you have tried here.
|
|
@ -0,0 +1,72 @@
|
|||
name: '🐛 反馈缺陷'
|
||||
description: '反馈一个缺陷'
|
||||
title: '[Bug] '
|
||||
labels: ['🐛 Bug']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: '产品版本'
|
||||
description: 不再支持 v2.28(含)之前的版本。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '版本类型'
|
||||
options:
|
||||
- label: '社区版'
|
||||
- label: '企业版'
|
||||
- label: '企业试用版'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '安装方式'
|
||||
options:
|
||||
- label: '在线安装 (一键命令安装)'
|
||||
- label: '离线包安装'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: '源码安装'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '环境信息'
|
||||
description: 请提供一个清晰且简洁的描述,说明你的环境信息。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🐛 缺陷描述'
|
||||
description: |
|
||||
请提供一个清晰且简洁的缺陷描述,如果问题比较复杂,也请详细说明。<br/>
|
||||
针对不清晰的描述信息将不予处理。请确保提供足够的细节和信息,以支持对缺陷进行复现和修复。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '复现步骤'
|
||||
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '期望结果'
|
||||
description: 请提供一个清晰且简洁的描述,说明你期望发生什么。
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '补充信息'
|
||||
description: 在这里添加关于问题的任何其他背景信息。
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '尝试过的解决方案'
|
||||
description: 如果你已经尝试解决问题,请在此列出你尝试过的解决方案。
|
|
@ -0,0 +1,56 @@
|
|||
name: '⭐️ Feature Request'
|
||||
description: 'Suggest an idea'
|
||||
title: '[Feature] '
|
||||
labels: ['⭐️ Feature Request']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
- ibuler
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: 'Product Version'
|
||||
description: The versions prior to v2.28 (inclusive) are no longer supported.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Product Edition'
|
||||
options:
|
||||
- label: 'Community Edition'
|
||||
- label: 'Enterprise Edition'
|
||||
- label: 'Enterprise Trial Edition'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Installation Method'
|
||||
options:
|
||||
- label: 'Online Installation (One-click command installation)'
|
||||
- label: 'Offline Package Installation'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: 'Source Code'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '⭐️ Feature Description'
|
||||
description: |
|
||||
Please add a clear and concise description of the problem you aim to solve with this feature request.<br/>
|
||||
Unclear descriptions will not be processed.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Proposed Solution'
|
||||
description: Please provide a clear and concise description of the solution you desire.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Additional Information'
|
||||
description: Please add any additional background information about the issue here.
|
|
@ -0,0 +1,56 @@
|
|||
name: '⭐️ 功能需求'
|
||||
description: '提出需求或建议'
|
||||
title: '[Feature] '
|
||||
labels: ['⭐️ Feature Request']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
- ibuler
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: '产品版本'
|
||||
description: 不再支持 v2.28(含)之前的版本。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '版本类型'
|
||||
options:
|
||||
- label: '社区版'
|
||||
- label: '企业版'
|
||||
- label: '企业试用版'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '安装方式'
|
||||
options:
|
||||
- label: '在线安装 (一键命令安装)'
|
||||
- label: '离线包安装'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: '源码安装'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '⭐️ 需求描述'
|
||||
description: |
|
||||
请添加一个清晰且简洁的问题描述,阐述你希望通过这个功能需求解决的问题。<br/>
|
||||
针对不清晰的描述信息将不予处理。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '解决方案'
|
||||
description: 请清晰且简洁地描述你想要的解决方案。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '补充信息'
|
||||
description: 在这里添加关于问题的任何其他背景信息。
|
|
@ -0,0 +1,60 @@
|
|||
name: '🤔 Question'
|
||||
description: 'Pose a question'
|
||||
title: '[Question] '
|
||||
labels: ['🤔 Question']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: 'Product Version'
|
||||
description: The versions prior to v2.28 (inclusive) are no longer supported.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Product Edition'
|
||||
options:
|
||||
- label: 'Community Edition'
|
||||
- label: 'Enterprise Edition'
|
||||
- label: 'Enterprise Trial Edition'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 'Installation Method'
|
||||
options:
|
||||
- label: 'Online Installation (One-click command installation)'
|
||||
- label: 'Offline Package Installation'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: 'Source Code'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Environment Information'
|
||||
description: Please provide a clear and concise description outlining your environment information.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🤔 Question Description'
|
||||
description: |
|
||||
Please provide a clear and concise description of the defect. If the issue is complex, please provide detailed explanations. <br/>
|
||||
Unclear descriptions will not be processed.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Expected Behavior'
|
||||
description: Please provide a clear and concise description of what you expect to happen.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 'Additional Information'
|
||||
description: Please add any additional background information about the issue here.
|
|
@ -0,0 +1,61 @@
|
|||
name: '🤔 问题咨询'
|
||||
description: '提出一个问题'
|
||||
title: '[Question] '
|
||||
labels: ['🤔 Question']
|
||||
assignees:
|
||||
- baijiangjie
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: '产品版本'
|
||||
description: 不再支持 v2.28(含)之前的版本。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '版本类型'
|
||||
options:
|
||||
- label: '社区版'
|
||||
- label: '企业版'
|
||||
- label: '企业试用版'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: '安装方式'
|
||||
options:
|
||||
- label: '在线安装 (一键命令安装)'
|
||||
- label: '离线包安装'
|
||||
- label: 'All-in-One'
|
||||
- label: '1Panel'
|
||||
- label: 'Kubernetes'
|
||||
- label: '源码安装'
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '环境信息'
|
||||
description: 请在此详细描述你的环境信息,如操作系统、浏览器和部署架构等。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🤔 问题描述'
|
||||
description: |
|
||||
请提供一个清晰且简洁的问题描述,如果问题比较复杂,也请详细说明。<br/>
|
||||
针对不清晰的描述信息将不予处理。
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '期望结果'
|
||||
description: 请提供一个清晰且简洁的描述,说明你期望发生什么。
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '补充信息'
|
||||
description: 在这里添加关于问题的任何其他背景信息。
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
name: Bug 提交
|
||||
about: 提交产品缺陷帮助我们更好的改进
|
||||
title: "[Bug] Bug 标题"
|
||||
labels: 类型:Bug
|
||||
assignees:
|
||||
- baijiangjie
|
||||
---
|
||||
|
||||
## 注意
|
||||
**JumpServer 版本( v2.28 之前的版本不再支持 )** <br>
|
||||
_针对过于简单的 Bug 描述不予考虑。请确保提供足够的细节和信息以支持 Bug 的复现和修复。_
|
||||
|
||||
## 当前使用的 JumpServer 版本 (必填)
|
||||
[在这里输入当前使用的 JumpServer 的版本号]
|
||||
|
||||
## 使用的版本类型 (必填)
|
||||
- [ ] 社区版
|
||||
- [ ] 企业版
|
||||
- [ ] 企业试用版
|
||||
|
||||
|
||||
## 版本安装方式 (必填)
|
||||
- [ ] 在线安装 (一键命令)
|
||||
- [ ] 离线安装 (下载离线包)
|
||||
- [ ] All-in-One
|
||||
- [ ] 1Panel 安装
|
||||
- [ ] Kubernetes 安装
|
||||
- [ ] 源码安装
|
||||
|
||||
## Bug 描述 (详细)
|
||||
[在这里描述 Bug 的详细情况,包括其影响和出现的具体情况]
|
||||
|
||||
## 复现步骤
|
||||
1. [描述如何复现 Bug 的第一步]
|
||||
2. [描述如何复现 Bug 的第二步]
|
||||
3. [以此类推,列出所有复现 Bug 所需的步骤]
|
||||
|
||||
## 期望行为
|
||||
[描述 Bug 出现时期望的系统行为或结果]
|
||||
|
||||
## 实际行为
|
||||
[描述实际上发生了什么,以及 Bug 出现的具体情况]
|
||||
|
||||
## 系统环境
|
||||
- 操作系统:[例如:Windows 10, macOS Big Sur]
|
||||
- 浏览器/应用版本:[如果适用,请提供相关版本信息]
|
||||
- 其他相关环境信息:[如果有其他相关环境信息,请在此处提供]
|
||||
|
||||
## 附加信息(可选)
|
||||
[在这里添加任何其他相关信息,如截图、错误信息等]
|
|
@ -1,50 +0,0 @@
|
|||
---
|
||||
name: 问题咨询
|
||||
about: 提出针对本项目安装部署、使用及其他方面的相关问题
|
||||
title: "[Question] 问题标题"
|
||||
labels: 类型:提问
|
||||
assignees:
|
||||
- baijiangjie
|
||||
---
|
||||
## 注意
|
||||
**请描述您的问题.** <br>
|
||||
**JumpServer 版本( v2.28 之前的版本不再支持 )** <br>
|
||||
_针对过于简单的 Bug 描述不予考虑。请确保提供足够的细节和信息以支持 Bug 的复现和修复。_
|
||||
|
||||
## 当前使用的 JumpServer 版本 (必填)
|
||||
[在这里输入当前使用的 JumpServer 的版本号]
|
||||
|
||||
## 使用的版本类型 (必填)
|
||||
- [ ] 社区版
|
||||
- [ ] 企业版
|
||||
- [ ] 企业试用版
|
||||
|
||||
|
||||
## 版本安装方式 (必填)
|
||||
- [ ] 在线安装 (一键命令)
|
||||
- [ ] 离线安装 (下载离线包)
|
||||
- [ ] All-in-One
|
||||
- [ ] 1Panel 安装
|
||||
- [ ] Kubernetes 安装
|
||||
- [ ] 源码安装
|
||||
|
||||
## 问题描述 (详细)
|
||||
[在这里描述你遇到的问题]
|
||||
|
||||
## 背景信息
|
||||
- 操作系统:[例如:Windows 10, macOS Big Sur]
|
||||
- 浏览器/应用版本:[如果适用,请提供相关版本信息]
|
||||
- 其他相关环境信息:[如果有其他相关环境信息,请在此处提供]
|
||||
|
||||
## 具体问题
|
||||
[在这里详细描述你的问题,包括任何相关细节或错误信息]
|
||||
|
||||
## 尝试过的解决方法
|
||||
[如果你已经尝试过解决问题,请在这里列出你已经尝试过的解决方法]
|
||||
|
||||
## 预期结果
|
||||
[描述你期望的解决方案或结果]
|
||||
|
||||
## 我们的期望
|
||||
[描述你希望我们提供的帮助或支持]
|
||||
|
|
@ -12,7 +12,9 @@ jobs:
|
|||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'close-issues'
|
||||
labels: '状态:待反馈'
|
||||
labels: '⏳ Pending feedback'
|
||||
inactive-day: 30
|
||||
body: |
|
||||
You haven't provided feedback for over 30 days.
|
||||
We will close this issue. If you have any further needs, you can reopen it or submit a new issue.
|
||||
您超过 30 天未反馈信息,我们将关闭该 issue,如有需求您可以重新打开或者提交新的 issue。
|
||||
|
|
|
@ -13,4 +13,4 @@ jobs:
|
|||
if: ${{ !github.event.issue.pull_request }}
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
labels: '状态:待处理,状态:待反馈'
|
||||
labels: '🔔 Pending processing,⏳ Pending feedback'
|
|
@ -13,13 +13,13 @@ jobs:
|
|||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
labels: '状态:待处理'
|
||||
labels: '🔔 Pending processing'
|
||||
|
||||
- name: Remove require reply label
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
labels: '状态:待反馈'
|
||||
labels: '⏳ Pending feedback'
|
||||
|
||||
add-label-if-is-member:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -55,11 +55,11 @@ jobs:
|
|||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
labels: '状态:待反馈'
|
||||
labels: '⏳ Pending feedback'
|
||||
|
||||
- name: Remove require handle label
|
||||
if: contains(steps.member_names.outputs.data, github.event.comment.user.login)
|
||||
uses: actions-cool/issues-helper@v2
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
labels: '状态:待处理'
|
||||
labels: '🔔 Pending processing'
|
||||
|
|
|
@ -13,4 +13,4 @@ jobs:
|
|||
if: ${{ !github.event.issue.pull_request }}
|
||||
with:
|
||||
actions: 'add-labels'
|
||||
labels: '状态:待处理'
|
||||
labels: '🔔 Pending processing'
|
|
@ -10,3 +10,4 @@ jobs:
|
|||
- uses: jumpserver/action-generic-handler@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
|
||||
I18N_TOKEN: ${{ secrets.I18N_TOKEN }}
|
||||
|
|
|
@ -4,6 +4,4 @@
|
|||
- name: "Remove account"
|
||||
ansible.windows.win_user:
|
||||
name: "{{ account.username }}"
|
||||
state: absent
|
||||
purge: yes
|
||||
force: yes
|
||||
state: absent
|
|
@ -225,7 +225,7 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize
|
|||
fields = BaseAccountSerializer.Meta.fields + [
|
||||
'su_from', 'asset', 'version',
|
||||
'source', 'source_id', 'connectivity',
|
||||
] + AccountCreateUpdateSerializerMixin.Meta.fields
|
||||
] + list(set(AccountCreateUpdateSerializerMixin.Meta.fields) - {'params'})
|
||||
read_only_fields = BaseAccountSerializer.Meta.read_only_fields + [
|
||||
'connectivity'
|
||||
]
|
||||
|
|
|
@ -126,7 +126,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
|||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||
if not self.instance or not include_assets:
|
||||
return Asset.objects.none()
|
||||
if self.instance.is_org_root():
|
||||
if not self.request.GET.get('search') and self.instance.is_org_root():
|
||||
return Asset.objects.none()
|
||||
if query_all:
|
||||
assets = self.instance.get_all_assets()
|
||||
|
|
|
@ -8,6 +8,7 @@ class DatabaseTypes(BaseType):
|
|||
ORACLE = 'oracle', 'Oracle'
|
||||
SQLSERVER = 'sqlserver', 'SQLServer'
|
||||
DB2 = 'db2', 'DB2'
|
||||
DAMENG = 'dameng', 'Dameng'
|
||||
CLICKHOUSE = 'clickhouse', 'ClickHouse'
|
||||
MONGODB = 'mongodb', 'MongoDB'
|
||||
REDIS = 'redis', 'Redis'
|
||||
|
@ -55,6 +56,15 @@ class DatabaseTypes(BaseType):
|
|||
'change_secret_enabled': False,
|
||||
'push_account_enabled': False,
|
||||
},
|
||||
cls.DAMENG: {
|
||||
'ansible_enabled': False,
|
||||
'ping_enabled': False,
|
||||
'gather_facts_enabled': False,
|
||||
'gather_accounts_enabled': False,
|
||||
'verify_account_enabled': False,
|
||||
'change_secret_enabled': False,
|
||||
'push_account_enabled': False,
|
||||
},
|
||||
cls.CLICKHOUSE: {
|
||||
'ansible_enabled': False,
|
||||
'ping_enabled': False,
|
||||
|
@ -84,6 +94,7 @@ class DatabaseTypes(BaseType):
|
|||
cls.ORACLE: [{'name': 'Oracle'}],
|
||||
cls.SQLSERVER: [{'name': 'SQLServer'}],
|
||||
cls.DB2: [{'name': 'DB2'}],
|
||||
cls.DAMENG: [{'name': 'Dameng'}],
|
||||
cls.CLICKHOUSE: [{'name': 'ClickHouse'}],
|
||||
cls.MONGODB: [{'name': 'MongoDB'}],
|
||||
cls.REDIS: [
|
||||
|
|
|
@ -23,6 +23,7 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||
postgresql = 'postgresql', 'PostgreSQL'
|
||||
sqlserver = 'sqlserver', 'SQLServer'
|
||||
db2 = 'db2', 'DB2'
|
||||
dameng = 'dameng', 'Dameng'
|
||||
clickhouse = 'clickhouse', 'ClickHouse'
|
||||
redis = 'redis', 'Redis'
|
||||
mongodb = 'mongodb', 'MongoDB'
|
||||
|
@ -185,6 +186,12 @@ class Protocol(ChoicesMixin, models.TextChoices):
|
|||
'secret_types': ['password'],
|
||||
'xpack': True,
|
||||
},
|
||||
cls.dameng: {
|
||||
'port': 5236,
|
||||
'required': True,
|
||||
'secret_types': ['password'],
|
||||
'xpack': True,
|
||||
},
|
||||
cls.clickhouse: {
|
||||
'port': 9000,
|
||||
'required': True,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 4.1.10 on 2023-10-07 06:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def add_dameng_platform(apps, schema_editor):
|
||||
platform_cls = apps.get_model('assets', 'Platform')
|
||||
automation_cls = apps.get_model('assets', 'PlatformAutomation')
|
||||
platform, _ = platform_cls.objects.update_or_create(
|
||||
name='Dameng', defaults={
|
||||
'name': 'Dameng', 'category': 'database',
|
||||
'internal': True, 'type': 'dameng',
|
||||
'domain_enabled': True, 'su_enabled': False,
|
||||
'su_method': None, 'comment': 'Dameng', 'created_by': 'System',
|
||||
'updated_by': 'System', 'custom_fields': []
|
||||
}
|
||||
)
|
||||
platform.protocols.update_or_create(name='dameng', defaults={
|
||||
'name': 'dameng', 'port': 5236, 'primary': True, 'setting': {}
|
||||
})
|
||||
automation_cls.objects.update_or_create(platform=platform, defaults={'ansible_enabled': False})
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('assets', '0127_automation_remove_account'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_dameng_platform)
|
||||
]
|
|
@ -123,7 +123,7 @@ class AutomationExecution(OrgModelMixin):
|
|||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ('-date_start',)
|
||||
ordering = ('org_id', '-date_start',)
|
||||
verbose_name = _('Automation task execution')
|
||||
|
||||
@property
|
||||
|
|
|
@ -381,6 +381,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
|
|||
|
||||
|
||||
class DetailMixin(serializers.Serializer):
|
||||
accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts'))
|
||||
spec_info = MethodSerializer(label=_('Spec info'), read_only=True)
|
||||
gathered_info = MethodSerializer(label=_('Gathered info'), read_only=True)
|
||||
auto_config = serializers.DictField(read_only=True, label=_('Auto info'))
|
||||
|
@ -395,7 +396,7 @@ class DetailMixin(serializers.Serializer):
|
|||
def get_field_names(self, declared_fields, info):
|
||||
names = super().get_field_names(declared_fields, info)
|
||||
names.extend([
|
||||
'gathered_info', 'spec_info', 'auto_config',
|
||||
'accounts', 'gathered_info', 'spec_info', 'auto_config',
|
||||
])
|
||||
return names
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class OperateLogStore(object):
|
|||
resource_map = {
|
||||
'Asset permission': lambda k, v: ActionChoices.display(int(v)) if k == 'Actions' else v
|
||||
}
|
||||
return resource_map.get(resource_type, lambda k, v: v)
|
||||
return resource_map.get(resource_type, lambda k, v: _(v))
|
||||
|
||||
@classmethod
|
||||
def convert_diff_friendly(cls, op_log):
|
||||
|
|
|
@ -37,6 +37,9 @@ class ActionChoices(TextChoices):
|
|||
approve = 'approve', _('Approve')
|
||||
close = 'close', _('Close')
|
||||
|
||||
# Custom action
|
||||
finished = 'finished', _('Finished')
|
||||
|
||||
|
||||
class LoginTypeChoices(TextChoices):
|
||||
web = "W", _("Web")
|
||||
|
|
|
@ -58,7 +58,7 @@ class OperatorLogHandler(metaclass=Singleton):
|
|||
return
|
||||
|
||||
key = '%s_%s' % (self.CACHE_KEY, instance_id)
|
||||
cache.set(key, instance_dict, 3 * 60)
|
||||
cache.set(key, instance_dict, 3)
|
||||
|
||||
def get_instance_dict_from_cache(self, instance_id):
|
||||
if instance_id is None:
|
||||
|
|
|
@ -257,6 +257,8 @@ class UserLoginLog(models.Model):
|
|||
|
||||
|
||||
class UserSession(models.Model):
|
||||
_OPERATE_LOG_ACTION = {'delete': ActionChoices.finished}
|
||||
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
ip = models.GenericIPAddressField(verbose_name=_("Login IP"))
|
||||
key = models.CharField(max_length=128, verbose_name=_("Session key"))
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
import uuid
|
||||
|
||||
from django.apps import apps
|
||||
from django.db.models.signals import post_save, pre_save, m2m_changed, pre_delete
|
||||
from django.db.models.signals import (
|
||||
pre_delete, pre_save, m2m_changed, post_delete, post_save
|
||||
)
|
||||
from django.dispatch import receiver
|
||||
from django.utils import translation
|
||||
|
||||
|
@ -94,7 +96,7 @@ def signal_of_operate_log_whether_continue(
|
|||
return condition
|
||||
|
||||
|
||||
@receiver(pre_save)
|
||||
@receiver([pre_save, pre_delete])
|
||||
def on_object_pre_create_or_update(
|
||||
sender, instance=None, raw=False, using=None, update_fields=None, **kwargs
|
||||
):
|
||||
|
@ -103,6 +105,7 @@ def on_object_pre_create_or_update(
|
|||
)
|
||||
if not ok:
|
||||
return
|
||||
|
||||
with translation.override('en'):
|
||||
# users.PrivateToken Model 没有 id 有 pk字段
|
||||
instance_id = getattr(instance, 'id', getattr(instance, 'pk', None))
|
||||
|
@ -145,7 +148,7 @@ def on_object_created_or_update(
|
|||
)
|
||||
|
||||
|
||||
@receiver(pre_delete)
|
||||
@receiver(post_delete)
|
||||
def on_object_delete(sender, instance=None, **kwargs):
|
||||
ok = signal_of_operate_log_whether_continue(sender, instance, False)
|
||||
if not ok:
|
||||
|
@ -153,9 +156,15 @@ def on_object_delete(sender, instance=None, **kwargs):
|
|||
|
||||
with translation.override('en'):
|
||||
resource_type = sender._meta.verbose_name
|
||||
action = getattr(sender, '_OPERATE_LOG_ACTION', {})
|
||||
action = action.get('delete', ActionChoices.delete)
|
||||
instance_id = getattr(instance, 'id', getattr(instance, 'pk', None))
|
||||
log_id, before = get_instance_dict_from_cache(instance_id)
|
||||
if not log_id:
|
||||
log_id, before = None, model_to_dict(instance)
|
||||
create_or_update_operate_log(
|
||||
ActionChoices.delete, resource_type,
|
||||
resource=instance, before=model_to_dict(instance)
|
||||
action, resource_type, log_id=log_id,
|
||||
resource=instance, before=before,
|
||||
)
|
||||
|
||||
|
||||
|
@ -166,7 +175,7 @@ def on_django_start_set_operate_log_monitor_models(sender, **kwargs):
|
|||
'django_celery_beat', 'contenttypes', 'sessions', 'auth',
|
||||
}
|
||||
exclude_models = {
|
||||
'UserPasswordHistory', 'ContentType',
|
||||
'UserPasswordHistory', 'ContentType', 'Asset',
|
||||
'MessageContent', 'SiteMessage',
|
||||
'PlatformAutomation', 'PlatformProtocol', 'Protocol',
|
||||
'HistoricalAccount', 'GatheredUser', 'ApprovalRule',
|
||||
|
@ -178,13 +187,15 @@ def on_django_start_set_operate_log_monitor_models(sender, **kwargs):
|
|||
'PermedAsset', 'PermedAccount', 'MenuPermission',
|
||||
'Permission', 'TicketSession', 'ApplyLoginTicket',
|
||||
'ApplyCommandTicket', 'ApplyLoginAssetTicket',
|
||||
'FavoriteAsset',
|
||||
'FavoriteAsset', 'ChangeSecretRecord'
|
||||
}
|
||||
include_models = {'UserSession'}
|
||||
for i, app in enumerate(apps.get_models(), 1):
|
||||
app_name = app._meta.app_label
|
||||
model_name = app._meta.object_name
|
||||
if app_name in exclude_apps or \
|
||||
model_name in exclude_models or \
|
||||
model_name.endswith('Execution'):
|
||||
continue
|
||||
if model_name not in include_models:
|
||||
continue
|
||||
MODELS_NEED_RECORD.add(model_name)
|
||||
|
|
|
@ -49,9 +49,15 @@ def _get_instance_field_value(
|
|||
continue
|
||||
|
||||
value = getattr(instance, f.name, None) or getattr(instance, f.attname, None)
|
||||
if not isinstance(value, bool) and not value:
|
||||
if not isinstance(value, (bool, int)) and not value:
|
||||
continue
|
||||
|
||||
choices = getattr(f, 'choices', []) or []
|
||||
for c_value, c_label in choices:
|
||||
if c_value == value:
|
||||
value = c_label
|
||||
break
|
||||
|
||||
if getattr(f, 'primary_key', False):
|
||||
f.verbose_name = 'id'
|
||||
elif isinstance(value, list):
|
||||
|
|
|
@ -4,7 +4,6 @@ from django.contrib import auth
|
|||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentication.utils import build_absolute_uri
|
||||
from authentication.views.mixins import FlashMessageMixin
|
||||
|
@ -55,11 +54,7 @@ class OAuth2AuthCallbackView(View, FlashMessageMixin):
|
|||
logger.debug(log_prompt.format('Process authenticate'))
|
||||
user = authenticate(code=callback_params['code'], request=request)
|
||||
|
||||
if err_msg := getattr(request, 'error_message', ''):
|
||||
login_url = reverse('authentication:login') + '?admin=1'
|
||||
return self.get_failed_response(login_url, title=_('Authentication failed'), msg=err_msg)
|
||||
|
||||
if user and user.is_valid:
|
||||
if user:
|
||||
logger.debug(log_prompt.format('Login: {}'.format(user)))
|
||||
auth.login(self.request, user)
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
|
@ -68,8 +63,7 @@ class OAuth2AuthCallbackView(View, FlashMessageMixin):
|
|||
)
|
||||
|
||||
logger.debug(log_prompt.format('Redirect'))
|
||||
# OAuth2 服务端认证成功, 但是用户被禁用了, 这时候需要调用服务端的logout
|
||||
redirect_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT
|
||||
redirect_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT or '/'
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from django.conf import settings
|
||||
from django.contrib.auth import user_logged_in
|
||||
from django.contrib.auth import user_logged_in, BACKEND_SESSION_KEY
|
||||
from django.core.cache import cache
|
||||
from django.dispatch import receiver
|
||||
from django_cas_ng.signals import cas_user_authenticated
|
||||
|
@ -20,8 +20,9 @@ def on_user_auth_login_success(sender, user, request, **kwargs):
|
|||
and user.mfa_enabled \
|
||||
and not request.session.get('auth_mfa'):
|
||||
request.session['auth_mfa_required'] = 1
|
||||
auth_backend = request.session.get('auth_backend', request.session.get(BACKEND_SESSION_KEY))
|
||||
if not request.session.get("auth_third_party_done") and \
|
||||
request.session.get('auth_backend') in AUTHENTICATION_BACKENDS_THIRD_PARTY:
|
||||
auth_backend in AUTHENTICATION_BACKENDS_THIRD_PARTY:
|
||||
request.session['auth_third_party_required'] = 1
|
||||
|
||||
user_session_id = request.session.get('user_session_id')
|
||||
|
|
|
@ -249,6 +249,8 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView):
|
|||
def form_valid(self, form):
|
||||
if not self.request.session.test_cookie_worked():
|
||||
form.add_error(None, _("Login timeout, please try again."))
|
||||
# 当 session 过期后,刷新浏览器重新提交依旧会报错,所以需要重新设置 test_cookie
|
||||
self.request.session.set_test_cookie()
|
||||
return self.form_invalid(form)
|
||||
|
||||
# https://docs.djangoproject.com/en/3.1/topics/http/sessions/#setting-test-cookies
|
||||
|
|
|
@ -14,9 +14,13 @@ from uuid import UUID
|
|||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models import QuerySet as DJQuerySet
|
||||
from elasticsearch import Elasticsearch
|
||||
from elasticsearch.helpers import bulk
|
||||
from elasticsearch.exceptions import RequestError, NotFoundError
|
||||
from elasticsearch7 import Elasticsearch
|
||||
from elasticsearch7.helpers import bulk
|
||||
from elasticsearch7.exceptions import RequestError, SSLError
|
||||
from elasticsearch7.exceptions import NotFoundError as NotFoundError7
|
||||
|
||||
from elasticsearch8.exceptions import NotFoundError as NotFoundError8
|
||||
from elasticsearch8.exceptions import BadRequestError
|
||||
|
||||
from common.utils.common import lazyproperty
|
||||
from common.utils import get_logger
|
||||
|
@ -36,9 +40,82 @@ class NotSupportElasticsearch8(JMSException):
|
|||
default_detail = _('Not Support Elasticsearch8')
|
||||
|
||||
|
||||
class ES(object):
|
||||
def __init__(self, config, properties, keyword_fields, exact_fields=None, match_fields=None):
|
||||
class InvalidElasticsearchSSL(JMSException):
|
||||
default_code = 'invalid_elasticsearch_SSL'
|
||||
default_detail = _(
|
||||
'Connection failed: Self-signed certificate used. Please check server certificate configuration')
|
||||
|
||||
|
||||
class ESClient(object):
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
version = get_es_client_version(**kwargs)
|
||||
if version == 6:
|
||||
return ESClientV6(*args, **kwargs)
|
||||
if version == 7:
|
||||
return ESClientV7(*args, **kwargs)
|
||||
elif version == 8:
|
||||
return ESClientV8(*args, **kwargs)
|
||||
raise ValueError('Unsupported ES_VERSION %r' % version)
|
||||
|
||||
|
||||
class ESClientBase(object):
|
||||
@classmethod
|
||||
def get_properties(cls, data, index):
|
||||
return data[index]['mappings']['properties']
|
||||
|
||||
@classmethod
|
||||
def get_mapping(cls, properties):
|
||||
return {'mappings': {'properties': properties}}
|
||||
|
||||
|
||||
class ESClientV7(ESClientBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
from elasticsearch7 import Elasticsearch
|
||||
self.es = Elasticsearch(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_sort(cls, field, direction):
|
||||
return f'{field}:{direction}'
|
||||
|
||||
|
||||
class ESClientV6(ESClientV7):
|
||||
|
||||
@classmethod
|
||||
def get_properties(cls, data, index):
|
||||
return data[index]['mappings']['data']['properties']
|
||||
|
||||
@classmethod
|
||||
def get_mapping(cls, properties):
|
||||
return {'mappings': {'data': {'properties': properties}}}
|
||||
|
||||
|
||||
class ESClientV8(ESClientBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
from elasticsearch8 import Elasticsearch
|
||||
self.es = Elasticsearch(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_sort(cls, field, direction):
|
||||
return {field: {'order': direction}}
|
||||
|
||||
|
||||
def get_es_client_version(**kwargs):
|
||||
try:
|
||||
es = Elasticsearch(**kwargs)
|
||||
info = es.info()
|
||||
version = int(info['version']['number'].split('.')[0])
|
||||
return version
|
||||
except SSLError:
|
||||
raise InvalidElasticsearchSSL
|
||||
except Exception:
|
||||
raise InvalidElasticsearch
|
||||
|
||||
|
||||
class ES(object):
|
||||
|
||||
def __init__(self, config, properties, keyword_fields, exact_fields=None, match_fields=None):
|
||||
self.version = 7
|
||||
self.config = config
|
||||
hosts = self.config.get('HOSTS')
|
||||
kwargs = self.config.get('OTHER', {})
|
||||
|
@ -46,7 +123,8 @@ class ES(object):
|
|||
ignore_verify_certs = kwargs.pop('IGNORE_VERIFY_CERTS', False)
|
||||
if ignore_verify_certs:
|
||||
kwargs['verify_certs'] = None
|
||||
self.es = Elasticsearch(hosts=hosts, max_retries=0, **kwargs)
|
||||
self.client = ESClient(hosts=hosts, max_retries=0, **kwargs)
|
||||
self.es = self.client.es
|
||||
self.index_prefix = self.config.get('INDEX') or 'jumpserver'
|
||||
self.is_index_by_date = bool(self.config.get('INDEX_BY_DATE', False))
|
||||
|
||||
|
@ -83,26 +161,14 @@ class ES(object):
|
|||
if not self.ping(timeout=2):
|
||||
return False
|
||||
|
||||
info = self.es.info()
|
||||
version = info['version']['number'].split('.')[0]
|
||||
|
||||
if version == '8':
|
||||
raise NotSupportElasticsearch8
|
||||
|
||||
try:
|
||||
# 获取索引信息,如果没有定义,直接返回
|
||||
data = self.es.indices.get_mapping(index=self.index)
|
||||
except NotFoundError:
|
||||
except (NotFoundError8, NotFoundError7):
|
||||
return False
|
||||
|
||||
try:
|
||||
if version == '6':
|
||||
# 检测索引是不是新的类型 es6
|
||||
properties = data[self.index]['mappings']['data']['properties']
|
||||
else:
|
||||
# 检测索引是不是新的类型 es7 default index type: _doc
|
||||
properties = data[self.index]['mappings']['properties']
|
||||
|
||||
properties = self.client.get_properties(data=data, index=self.index)
|
||||
for keyword in self.keyword_fields:
|
||||
if not properties[keyword]['type'] == 'keyword':
|
||||
break
|
||||
|
@ -118,12 +184,7 @@ class ES(object):
|
|||
|
||||
def _ensure_index_exists(self):
|
||||
try:
|
||||
info = self.es.info()
|
||||
version = info['version']['number'].split('.')[0]
|
||||
if version == '6':
|
||||
mappings = {'mappings': {'data': {'properties': self.properties}}}
|
||||
else:
|
||||
mappings = {'mappings': {'properties': self.properties}}
|
||||
mappings = self.client.get_mapping(self.properties)
|
||||
|
||||
if self.is_index_by_date:
|
||||
mappings['aliases'] = {
|
||||
|
@ -132,7 +193,7 @@ class ES(object):
|
|||
|
||||
try:
|
||||
self.es.indices.create(index=self.index, body=mappings)
|
||||
except RequestError as e:
|
||||
except (RequestError, BadRequestError) as e:
|
||||
if e.error == 'resource_already_exists_exception':
|
||||
logger.warning(e)
|
||||
else:
|
||||
|
@ -175,11 +236,15 @@ class ES(object):
|
|||
|
||||
def _filter(self, query: dict, from_=None, size=None, sort=None):
|
||||
body = self.get_query_body(**query)
|
||||
|
||||
data = self.es.search(
|
||||
index=self.query_index, body=body,
|
||||
from_=from_, size=size, sort=sort
|
||||
)
|
||||
search_params = {
|
||||
'index': self.query_index,
|
||||
'body': body,
|
||||
'from_': from_,
|
||||
'size': size
|
||||
}
|
||||
if sort is not None:
|
||||
search_params['sort'] = sort
|
||||
data = self.es.search(**search_params)
|
||||
|
||||
source_data = []
|
||||
for item in data['hits']['hits']:
|
||||
|
@ -367,7 +432,7 @@ class QuerySet(DJQuerySet):
|
|||
else:
|
||||
direction = 'asc'
|
||||
field = field.lstrip('-+')
|
||||
sort = f'{field}:{direction}'
|
||||
sort = self._storage.client.get_sort(field, direction)
|
||||
return sort
|
||||
|
||||
def __execute(self):
|
||||
|
|
|
@ -74,9 +74,13 @@ class DateTimeMixin:
|
|||
query = {f'{query_field}__gte': t}
|
||||
return qs.filter(**query)
|
||||
|
||||
@lazyproperty
|
||||
def users(self):
|
||||
return self.org.get_members()
|
||||
|
||||
def get_logs_queryset(self, queryset, query_params):
|
||||
query = {}
|
||||
users = self.org.get_members()
|
||||
users = self.users
|
||||
if not self.org.is_root():
|
||||
if query_params == 'username':
|
||||
query = {
|
||||
|
@ -100,6 +104,13 @@ class DateTimeMixin:
|
|||
queryset = self.get_logs_queryset(qs, 'username')
|
||||
return queryset
|
||||
|
||||
@lazyproperty
|
||||
def user_login_logs_on_the_system_queryset(self):
|
||||
qs = UserLoginLog.objects.all()
|
||||
qs = self.get_logs_queryset_filter(qs, 'datetime')
|
||||
queryset = qs.filter(username__in=construct_userlogin_usernames(self.users))
|
||||
return queryset
|
||||
|
||||
@lazyproperty
|
||||
def password_change_logs_queryset(self):
|
||||
qs = PasswordChangeLog.objects.all()
|
||||
|
@ -141,6 +152,7 @@ class DatesLoginMetricMixin:
|
|||
ftp_logs_queryset: FTPLog.objects
|
||||
job_logs_queryset: JobLog.objects
|
||||
login_logs_queryset: UserLoginLog.objects
|
||||
user_login_logs_on_the_system_queryset: UserLoginLog.objects
|
||||
operate_logs_queryset: OperateLog.objects
|
||||
password_change_logs_queryset: PasswordChangeLog.objects
|
||||
|
||||
|
@ -159,31 +171,48 @@ class DatesLoginMetricMixin:
|
|||
query = {f'{field_name}__range': self.date_start_end}
|
||||
return queryset.filter(**query)
|
||||
|
||||
def get_date_metrics(self, queryset, field_name, count_field):
|
||||
def get_date_metrics(self, queryset, field_name, count_fields):
|
||||
queryset = self.filter_date_start_end(queryset, field_name)
|
||||
queryset = queryset.values_list(field_name, count_field)
|
||||
|
||||
date_group_map = defaultdict(set)
|
||||
for datetime, count_field in queryset:
|
||||
if not isinstance(count_fields, (list, tuple)):
|
||||
count_fields = [count_fields]
|
||||
|
||||
values_list = [field_name] + list(count_fields)
|
||||
queryset = queryset.values_list(*values_list)
|
||||
|
||||
date_group_map = defaultdict(lambda: defaultdict(set))
|
||||
|
||||
for row in queryset:
|
||||
datetime = row[0]
|
||||
date_str = str(datetime.date())
|
||||
date_group_map[date_str].add(count_field)
|
||||
for idx, count_field in enumerate(count_fields):
|
||||
date_group_map[date_str][count_field].add(row[idx + 1])
|
||||
|
||||
return [
|
||||
len(date_group_map.get(str(d), set()))
|
||||
for d in self.dates_list
|
||||
]
|
||||
date_metrics_dict = defaultdict(list)
|
||||
for field in count_fields:
|
||||
for date_str in self.dates_list:
|
||||
count = len(date_group_map.get(str(date_str), {}).get(field, set()))
|
||||
date_metrics_dict[field].append(count)
|
||||
|
||||
return date_metrics_dict
|
||||
|
||||
def get_dates_metrics_total_count_active_users_and_assets(self):
|
||||
date_metrics_dict = self.get_date_metrics(
|
||||
Session.objects, 'date_start', ('user_id', 'asset_id')
|
||||
)
|
||||
return date_metrics_dict.get('user_id', []), date_metrics_dict.get('asset_id', [])
|
||||
|
||||
def get_dates_metrics_total_count_login(self):
|
||||
return self.get_date_metrics(UserLoginLog.objects, 'datetime', 'id')
|
||||
|
||||
def get_dates_metrics_total_count_active_users(self):
|
||||
return self.get_date_metrics(Session.objects, 'date_start', 'user_id')
|
||||
|
||||
def get_dates_metrics_total_count_active_assets(self):
|
||||
return self.get_date_metrics(Session.objects, 'date_start', 'asset_id')
|
||||
date_metrics_dict = self.get_date_metrics(
|
||||
UserLoginLog.objects, 'datetime', 'id'
|
||||
)
|
||||
return date_metrics_dict.get('id', [])
|
||||
|
||||
def get_dates_metrics_total_count_sessions(self):
|
||||
return self.get_date_metrics(Session.objects, 'date_start', 'id')
|
||||
date_metrics_dict = self.get_date_metrics(
|
||||
Session.objects, 'date_start', 'id'
|
||||
)
|
||||
return date_metrics_dict.get('id', [])
|
||||
|
||||
def get_dates_login_times_assets(self):
|
||||
assets = self.sessions_queryset.values("asset") \
|
||||
|
@ -224,7 +253,7 @@ class DatesLoginMetricMixin:
|
|||
|
||||
@lazyproperty
|
||||
def user_login_amount(self):
|
||||
return self.login_logs_queryset.values('username').distinct().count()
|
||||
return self.user_login_logs_on_the_system_queryset.values('username').distinct().count()
|
||||
|
||||
@lazyproperty
|
||||
def operate_logs_amount(self):
|
||||
|
@ -412,11 +441,13 @@ class IndexApi(DateTimeMixin, DatesLoginMetricMixin, APIView):
|
|||
})
|
||||
|
||||
if _all or query_params.get('dates_metrics'):
|
||||
user_data, asset_data = self.get_dates_metrics_total_count_active_users_and_assets()
|
||||
login_data = self.get_dates_metrics_total_count_login()
|
||||
data.update({
|
||||
'dates_metrics_date': self.get_dates_metrics_date(),
|
||||
'dates_metrics_total_count_login': self.get_dates_metrics_total_count_login(),
|
||||
'dates_metrics_total_count_active_users': self.get_dates_metrics_total_count_active_users(),
|
||||
'dates_metrics_total_count_active_assets': self.get_dates_metrics_total_count_active_assets(),
|
||||
'dates_metrics_total_count_login': login_data,
|
||||
'dates_metrics_total_count_active_users': user_data,
|
||||
'dates_metrics_total_count_active_assets': asset_data,
|
||||
})
|
||||
|
||||
if _all or query_params.get('dates_login_times_top10_assets'):
|
||||
|
|
|
@ -489,7 +489,7 @@ class Config(dict):
|
|||
# 安全配置
|
||||
'SECURITY_MFA_AUTH': 0, # 0 不开启 1 全局开启 2 管理员开启
|
||||
'SECURITY_MFA_AUTH_ENABLED_FOR_THIRD_PARTY': True,
|
||||
'SECURITY_COMMAND_EXECUTION': True,
|
||||
'SECURITY_COMMAND_EXECUTION': False,
|
||||
'SECURITY_COMMAND_BLACKLIST': [
|
||||
'reboot', 'shutdown', 'poweroff', 'halt', 'dd', 'half', 'top'
|
||||
],
|
||||
|
@ -619,7 +619,9 @@ class Config(dict):
|
|||
# Ansible Receptor
|
||||
'RECEPTOR_ENABLED': False,
|
||||
'ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST': 'jms_celery',
|
||||
'ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS': 'receptor:7521'
|
||||
'ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS': 'receptor:7521',
|
||||
|
||||
'FILE_UPLOAD_TEMP_DIR': None
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -138,7 +138,6 @@ INSTALLED_APPS = [
|
|||
'rbac.apps.RBACConfig',
|
||||
'labels.apps.LabelsConfig',
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
'drf_yasg',
|
||||
'django_cas_ng',
|
||||
'channels',
|
||||
|
@ -320,6 +319,8 @@ PRIVATE_STORAGE_AUTH_FUNCTION = 'jumpserver.rewriting.storage.permissions.allow_
|
|||
PRIVATE_STORAGE_INTERNAL_URL = '/private-media/'
|
||||
PRIVATE_STORAGE_SERVER = 'jumpserver.rewriting.storage.servers.StaticFileServer'
|
||||
|
||||
FILE_UPLOAD_TEMP_DIR = CONFIG.FILE_UPLOAD_TEMP_DIR
|
||||
|
||||
# Use django-bootstrap-form to format template, input max width arg
|
||||
# BOOTSTRAP_COLUMN_COUNT = 11
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e4baadc170ded5134bed55533c4c04e694be6ea7b8e151d80c1092728e26a75b
|
||||
size 177500
|
||||
oid sha256:52990de6b508e55b8b5f4a70f86c567410c5cf217ca312847f65178393d81b19
|
||||
size 177824
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:08667579241592ecacd1baf330147a720a9e41171444b925f90778613a7e1d9a
|
||||
size 145230
|
||||
oid sha256:144d439f8f3c96d00b1744de34b8a2a22b891f88ccb4b3c9669ad7273ecd08be
|
||||
size 145525
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:93fed3f6a027f645520852f40c5f7722a5be579a3a8bb7b7029e3d0bc9794056
|
||||
size 145341
|
||||
oid sha256:17da592df8b280d501a3b579c6a249b080bf07fbee34520a2012d8936d96ca14
|
||||
size 145636
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -57,21 +57,22 @@ def register_as_period_task(
|
|||
task = '{func.__module__}.{func.__name__}'.format(func=func)
|
||||
_name = name if name else task
|
||||
add_register_period_task({
|
||||
_name: {
|
||||
'task': task,
|
||||
'interval': interval,
|
||||
'crontab': crontab,
|
||||
'args': args,
|
||||
'kwargs': kwargs if kwargs else {},
|
||||
'enabled': True,
|
||||
'description': description
|
||||
}
|
||||
_name: {
|
||||
'task': task,
|
||||
'interval': interval,
|
||||
'crontab': crontab,
|
||||
'args': args,
|
||||
'kwargs': kwargs if kwargs else {},
|
||||
'description': description
|
||||
}
|
||||
})
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorate
|
||||
|
||||
|
||||
|
@ -85,6 +86,7 @@ def after_app_ready_start(func):
|
|||
@wraps(func)
|
||||
def decorate(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return decorate
|
||||
|
||||
|
||||
|
@ -98,4 +100,5 @@ def after_app_shutdown_clean_periodic(func):
|
|||
@wraps(func)
|
||||
def decorate(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return decorate
|
||||
|
|
|
@ -80,6 +80,9 @@ def create_or_update_celery_periodic_tasks(tasks):
|
|||
description=detail.get('description') or '',
|
||||
last_run_at=last_run_at,
|
||||
)
|
||||
enabled = detail.get('enabled')
|
||||
if enabled is not None:
|
||||
defaults["enabled"] = enabled
|
||||
task = PeriodicTask.objects.update_or_create(
|
||||
defaults=defaults, name=name,
|
||||
)
|
||||
|
|
|
@ -215,7 +215,8 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
|
|||
return "{}:{}:{}".format(self.org.name, self.creator.name, self.playbook.name)
|
||||
|
||||
def create_execution(self):
|
||||
return self.executions.create(job_version=self.version, material=self.material, job_type=Types[self.type].value)
|
||||
return self.executions.create(job_version=self.version, material=self.material, job_type=Types[self.type].value,
|
||||
creator=self.creator)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Job")
|
||||
|
|
|
@ -140,6 +140,9 @@ def task_sent_handler(headers=None, body=None, **kwargs):
|
|||
args = []
|
||||
kwargs = {}
|
||||
|
||||
# 不要保存__current_lang和__current_org_id参数,防止系统任务中点击再次执行报错
|
||||
kwargs.pop('__current_lang', None)
|
||||
kwargs.pop('__current_org_id', None)
|
||||
data = {
|
||||
'id': i,
|
||||
'name': task,
|
||||
|
|
|
@ -7,6 +7,7 @@ from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
|||
|
||||
from common.db.utils import close_old_connections
|
||||
from common.utils import get_logger
|
||||
from rbac.builtin import BuiltinRole
|
||||
from .ansible.utils import get_ansible_task_log_path
|
||||
from .celery.utils import get_celery_task_log_path
|
||||
from .const import CELERY_LOG_MAGIC_MARK
|
||||
|
@ -48,13 +49,30 @@ class TaskLogWebsocket(AsyncJsonWebsocketConsumer):
|
|||
else:
|
||||
return None
|
||||
|
||||
@sync_to_async
|
||||
def get_current_user_role_ids(self, user):
|
||||
roles = user.system_roles.all() | user.org_roles.all()
|
||||
user_role_ids = set(map(str, roles.values_list('id', flat=True)))
|
||||
return user_role_ids
|
||||
|
||||
async def receive_json(self, content, **kwargs):
|
||||
task_id = content.get('task')
|
||||
task = await self.get_task(task_id)
|
||||
if not task:
|
||||
await self.send_json({'message': 'Task not found', 'task': task_id})
|
||||
return
|
||||
if task.name in self.user_tasks and task.creator != self.scope['user']:
|
||||
|
||||
admin_auditor_role_ids = {
|
||||
BuiltinRole.system_admin.id,
|
||||
BuiltinRole.system_auditor.id,
|
||||
BuiltinRole.org_admin.id,
|
||||
BuiltinRole.org_auditor.id
|
||||
}
|
||||
user = self.scope['user']
|
||||
user_role_ids = await self.get_current_user_role_ids(user)
|
||||
has_admin_auditor_role = bool(admin_auditor_role_ids & user_role_ids)
|
||||
|
||||
if not has_admin_auditor_role and task.name in self.user_tasks and task.creator != user:
|
||||
await self.send_json({'message': 'No permission', 'task': task_id})
|
||||
return
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from rest_framework.serializers import ValidationError
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -45,7 +45,7 @@ class OrgManager(models.Manager):
|
|||
for obj in objs:
|
||||
if org.is_root():
|
||||
if not obj.org_id:
|
||||
raise ValidationError('Please save in a org')
|
||||
raise ValidationError(_('Please save in a org'))
|
||||
else:
|
||||
obj.org_id = org.id
|
||||
return super().bulk_create(objs, batch_size, ignore_conflicts)
|
||||
|
@ -70,7 +70,7 @@ class OrgModelMixin(models.Model):
|
|||
# raise ...
|
||||
if org.is_root():
|
||||
if not self.org_id:
|
||||
raise ValidationError('Please save in a org')
|
||||
raise ValidationError(_('Please save in a org'))
|
||||
else:
|
||||
self.org_id = org.id
|
||||
return super().save(*args, **kwargs)
|
||||
|
@ -119,4 +119,3 @@ class OrgModelMixin(models.Model):
|
|||
class JMSOrgBaseModel(JMSBaseModel, OrgModelMixin):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ def migrate_system_user_to_accounts(apps, schema_editor):
|
|||
bulk_size = 10000
|
||||
while True:
|
||||
asset_permissions = asset_permission_model.objects \
|
||||
.prefetch_related('system_users')[count:bulk_size]
|
||||
.prefetch_related('system_users')[count:count+bulk_size]
|
||||
if not asset_permissions:
|
||||
break
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@ logger = get_logger(__file__)
|
|||
class LDAPImportMessage(UserMessage):
|
||||
def __init__(self, user, extra_kwargs):
|
||||
super().__init__(user)
|
||||
self.orgs = extra_kwargs.pop('orgs', [])
|
||||
self.end_time = extra_kwargs.pop('end_time', '')
|
||||
self.start_time = extra_kwargs.pop('start_time', '')
|
||||
self.time_start_display = extra_kwargs.pop('time_start_display', '')
|
||||
self.new_users = extra_kwargs.pop('new_users', [])
|
||||
self.errors = extra_kwargs.pop('errors', [])
|
||||
self.cost_time = extra_kwargs.pop('cost_time', '')
|
||||
self.orgs = extra_kwargs.get('orgs', [])
|
||||
self.end_time = extra_kwargs.get('end_time', '')
|
||||
self.start_time = extra_kwargs.get('start_time', '')
|
||||
self.time_start_display = extra_kwargs.get('time_start_display', '')
|
||||
self.new_users = extra_kwargs.get('new_users', [])
|
||||
self.errors = extra_kwargs.get('errors', [])
|
||||
self.cost_time = extra_kwargs.get('cost_time', '')
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
subject = _('Notification of Synchronized LDAP User Task Results')
|
||||
|
|
|
@ -93,7 +93,10 @@ class LDAPSettingSerializer(serializers.Serializer):
|
|||
|
||||
AUTH_LDAP = serializers.BooleanField(required=False, label=_('Enable LDAP auth'))
|
||||
|
||||
@staticmethod
|
||||
def post_save():
|
||||
def post_save(self):
|
||||
keys = ['AUTH_LDAP_SYNC_IS_PERIODIC', 'AUTH_LDAP_SYNC_INTERVAL', 'AUTH_LDAP_SYNC_CRONTAB']
|
||||
kwargs = {k: self.validated_data[k] for k in keys if k in self.validated_data}
|
||||
if not kwargs:
|
||||
return
|
||||
from settings.tasks import import_ldap_user_periodic
|
||||
import_ldap_user_periodic()
|
||||
import_ldap_user_periodic(**kwargs)
|
||||
|
|
|
@ -9,7 +9,7 @@ from common.utils import get_logger
|
|||
from common.utils.timezone import local_now_display
|
||||
from ops.celery.decorator import after_app_ready_start
|
||||
from ops.celery.utils import (
|
||||
create_or_update_celery_periodic_tasks, delete_celery_periodic_task
|
||||
create_or_update_celery_periodic_tasks, disable_celery_periodic_task
|
||||
)
|
||||
from orgs.models import Organization
|
||||
from settings.notifications import LDAPImportMessage
|
||||
|
@ -65,20 +65,15 @@ def import_ldap_user():
|
|||
|
||||
@shared_task(verbose_name=_('Registration periodic import ldap user task'))
|
||||
@after_app_ready_start
|
||||
def import_ldap_user_periodic():
|
||||
if not settings.AUTH_LDAP:
|
||||
return
|
||||
def import_ldap_user_periodic(**kwargs):
|
||||
task_name = 'import_ldap_user_periodic'
|
||||
delete_celery_periodic_task(task_name)
|
||||
if not settings.AUTH_LDAP_SYNC_IS_PERIODIC:
|
||||
return
|
||||
|
||||
interval = settings.AUTH_LDAP_SYNC_INTERVAL
|
||||
interval = kwargs.get('AUTH_LDAP_SYNC_INTERVAL', settings.AUTH_LDAP_SYNC_INTERVAL)
|
||||
enabled = kwargs.get('AUTH_LDAP_SYNC_IS_PERIODIC', settings.AUTH_LDAP_SYNC_IS_PERIODIC)
|
||||
crontab = kwargs.get('AUTH_LDAP_SYNC_CRONTAB', settings.AUTH_LDAP_SYNC_CRONTAB)
|
||||
if isinstance(interval, int):
|
||||
interval = interval * 3600
|
||||
else:
|
||||
interval = None
|
||||
crontab = settings.AUTH_LDAP_SYNC_CRONTAB
|
||||
if crontab:
|
||||
# 优先使用 crontab
|
||||
interval = None
|
||||
|
@ -86,7 +81,8 @@ def import_ldap_user_periodic():
|
|||
task_name: {
|
||||
'task': import_ldap_user.name,
|
||||
'interval': interval,
|
||||
'crontab': crontab
|
||||
'crontab': crontab,
|
||||
'enabled': enabled
|
||||
}
|
||||
}
|
||||
create_or_update_celery_periodic_tasks(tasks)
|
||||
|
|
|
@ -60,7 +60,7 @@ class AppletHostDeploymentViewSet(viewsets.ModelViewSet):
|
|||
queryset = AppletHostDeployment.objects.all()
|
||||
filterset_fields = ['host', ]
|
||||
rbac_perms = (
|
||||
('applets', 'terminal.view_AppletHostDeployment'),
|
||||
('applets', 'terminal.view_applethostdeployment'),
|
||||
('uninstall', 'terminal.change_applethost'),
|
||||
)
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class AppletMethod:
|
|||
from .models import Applet, AppletHost
|
||||
|
||||
methods = defaultdict(list)
|
||||
has_applet_hosts = AppletHost.objects.all().exists()
|
||||
has_applet_hosts = AppletHost.objects.filter(is_active=True).exists()
|
||||
applets = Applet.objects.filter(is_active=True)
|
||||
for applet in applets:
|
||||
for protocol in applet.protocols:
|
||||
|
@ -166,7 +166,8 @@ class ConnectMethodUtil:
|
|||
'support': [
|
||||
Protocol.mysql, Protocol.postgresql,
|
||||
Protocol.oracle, Protocol.sqlserver,
|
||||
Protocol.mariadb, Protocol.db2
|
||||
Protocol.mariadb, Protocol.db2,
|
||||
Protocol.dameng
|
||||
],
|
||||
'match': 'm2m'
|
||||
},
|
||||
|
@ -253,8 +254,9 @@ class ConnectMethodUtil:
|
|||
def _filter_disable_protocols_connect_methods(cls, methods):
|
||||
# 过滤一些特殊的协议方式
|
||||
if not getattr(settings, 'TERMINAL_KOKO_SSH_ENABLED'):
|
||||
protocol = Protocol.ssh
|
||||
methods[protocol] = [m for m in methods[protocol] if m['type'] != 'native']
|
||||
disable_ssh_client_protocols = [Protocol.ssh, Protocol.sftp, Protocol.telnet]
|
||||
for protocol in disable_ssh_client_protocols:
|
||||
methods[protocol] = [m for m in methods[protocol] if m['type'] != 'native']
|
||||
return methods
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -176,7 +176,7 @@ class Applet(JMSBaseModel):
|
|||
label_value = spec_label.label.value
|
||||
matched = [host for host in hosts if host.name == label_value]
|
||||
if matched:
|
||||
return matched[0]
|
||||
return random.choice(matched)
|
||||
|
||||
hosts = [h for h in hosts if h.auto_create_accounts]
|
||||
prefer_key = self.host_prefer_key_tpl.format(user.id)
|
||||
|
|
|
@ -75,6 +75,7 @@ class AuthMixin:
|
|||
if self.can_update_ssh_key():
|
||||
self.public_key = public_key
|
||||
self.save()
|
||||
post_user_change_password.send(self.__class__, user=self)
|
||||
|
||||
def can_update_password(self):
|
||||
return self.is_local
|
||||
|
|
|
@ -17,6 +17,7 @@ from orgs.utils import current_org
|
|||
from rbac.builtin import BuiltinRole
|
||||
from rbac.models import OrgRoleBinding, SystemRoleBinding, Role
|
||||
from rbac.permissions import RBACPermission
|
||||
from users.signals import post_user_change_password
|
||||
from ..const import PasswordStrategy
|
||||
from ..models import User
|
||||
|
||||
|
@ -268,6 +269,8 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
|
|||
instance = self.save_and_set_custom_m2m_fields(
|
||||
validated_data, save_handler, created=False
|
||||
)
|
||||
if validated_data.get('public_key'):
|
||||
post_user_change_password.send(instance.__class__, user=instance)
|
||||
return instance
|
||||
|
||||
def create(self, validated_data):
|
||||
|
@ -275,6 +278,8 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa
|
|||
instance = self.save_and_set_custom_m2m_fields(
|
||||
validated_data, save_handler, created=True
|
||||
)
|
||||
if validated_data.get('public_key'):
|
||||
post_user_change_password.send(instance.__class__, user=instance)
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
{% block custom_foot_js %}
|
||||
<script type="text/javascript" src="{% static 'js/pwstrength-bootstrap.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/plugins/jsencrypt/jsencrypt.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/plugins/cryptojs/crypto-js.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/plugins/buffer/buffer.min.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// 密码强度校验
|
||||
|
@ -76,7 +79,8 @@ $(document).ready(function () {
|
|||
checkPasswordRules(password, minLength);
|
||||
})
|
||||
|
||||
$("form").submit(function(){
|
||||
$("form").submit(function(event){
|
||||
event.preventDefault()
|
||||
// Let's find the input to check
|
||||
var ids = ['id_new_password', 'id_confirm_password']
|
||||
for (id of ids) {
|
||||
|
@ -87,6 +91,7 @@ $(document).ready(function () {
|
|||
passwordRef.val(value)
|
||||
}
|
||||
}
|
||||
this.submit();
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "adal"
|
||||
|
@ -2138,28 +2138,6 @@ type = "legacy"
|
|||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "django-rest-swagger"
|
||||
version = "2.2.0"
|
||||
description = "Swagger UI for Django REST Framework 3.5+"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "django-rest-swagger-2.2.0.tar.gz", hash = "sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b"},
|
||||
{file = "django_rest_swagger-2.2.0-py2.py3-none-any.whl", hash = "sha256:b039b0288bab4665cd45dc5d16f94b13911bc4ad0ed55f74ad3b90aa31c87c17"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
coreapi = ">=2.3.0"
|
||||
djangorestframework = ">=3.5.4"
|
||||
openapi-codec = ">=1.3.1"
|
||||
simplejson = "*"
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "django-simple-captcha"
|
||||
version = "0.5.18"
|
||||
|
@ -2389,22 +2367,45 @@ url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
|||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "elasticsearch"
|
||||
version = "7.8.0"
|
||||
description = "Python client for Elasticsearch"
|
||||
name = "elastic-transport"
|
||||
version = "8.13.1"
|
||||
description = "Transport classes and utilities shared among Python Elastic client libraries"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "elasticsearch-7.8.0-py2.py3-none-any.whl", hash = "sha256:6fb566dd23b91b5871ce12212888674b4cf33374e92b71b1080916c931e44dcb"},
|
||||
{file = "elasticsearch-7.8.0.tar.gz", hash = "sha256:e637d8cf4e27e279b5ff8ca8edc0c086f4b5df4bf2b48e2f950b7833aca3a792"},
|
||||
{file = "elastic_transport-8.13.1-py3-none-any.whl", hash = "sha256:5d4bb6b8e9d74a9c16de274e91a5caf65a3a8d12876f1e99152975e15b2746fe"},
|
||||
{file = "elastic_transport-8.13.1.tar.gz", hash = "sha256:16339d392b4bbe86ad00b4bdeecff10edf516d32bc6c16053846625f2c6ea250"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
urllib3 = ">=1.21.1"
|
||||
urllib3 = ">=1.26.2,<3"
|
||||
|
||||
[package.extras]
|
||||
async = ["aiohttp (>=3,<4)", "yarl"]
|
||||
develop = ["aiohttp", "furo", "httpx", "mock", "opentelemetry-api", "opentelemetry-sdk", "orjson", "pytest", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "pytest-mock", "requests", "respx", "sphinx (>2)", "sphinx-autodoc-typehints", "trustme"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "elasticsearch7"
|
||||
version = "7.17.9"
|
||||
description = "Python client for Elasticsearch"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
|
||||
files = [
|
||||
{file = "elasticsearch7-7.17.9-py2.py3-none-any.whl", hash = "sha256:24cfa00438dd1c0328f4c61e064bcfd4bbf5ff7684e2ec49cc46efbb7598b055"},
|
||||
{file = "elasticsearch7-7.17.9.tar.gz", hash = "sha256:4868965d7d6af948c6f31510523f610e9b81299acd2fd35325e46090d786584d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
urllib3 = ">=1.21.1,<2"
|
||||
|
||||
[package.extras]
|
||||
async = ["aiohttp (>=3,<4)"]
|
||||
develop = ["black", "coverage", "jinja2", "mock", "pytest", "pytest-cov", "pyyaml", "requests (>=2.0.0,<3.0.0)", "sphinx (<1.7)", "sphinx-rtd-theme"]
|
||||
docs = ["sphinx (<1.7)", "sphinx-rtd-theme"]
|
||||
requests = ["requests (>=2.4.0,<3.0.0)"]
|
||||
|
@ -2414,6 +2415,31 @@ type = "legacy"
|
|||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "elasticsearch8"
|
||||
version = "8.13.2"
|
||||
description = "Python client for Elasticsearch"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "elasticsearch8-8.13.2-py3-none-any.whl", hash = "sha256:1691496d9a39b5504f768e2a3000574f0e9c684842b449f692ffc364a2171758"},
|
||||
{file = "elasticsearch8-8.13.2.tar.gz", hash = "sha256:ae5f08a15f24b7af0025290f303c7777d781ebedb23c1805a46114ea0f918d0e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
elastic-transport = ">=8.13,<9"
|
||||
|
||||
[package.extras]
|
||||
async = ["aiohttp (>=3,<4)"]
|
||||
orjson = ["orjson (>=3)"]
|
||||
requests = ["requests (>=2.4.0,!=2.32.2,<3.0.0)"]
|
||||
vectorstore-mmr = ["numpy (>=1)", "simsimd (>=3)"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "tsinghua"
|
||||
|
||||
[[package]]
|
||||
name = "enum-compat"
|
||||
version = "0.0.3"
|
||||
|
@ -2836,14 +2862,8 @@ files = [
|
|||
[package.dependencies]
|
||||
google-auth = ">=2.14.1,<3.0.dev0"
|
||||
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
|
||||
grpcio = [
|
||||
{version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
grpcio-status = [
|
||||
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0"
|
||||
requests = ">=2.18.0,<3.0.0.dev0"
|
||||
|
||||
|
@ -3575,12 +3595,12 @@ reference = "tsinghua"
|
|||
|
||||
[[package]]
|
||||
name = "jms-storage"
|
||||
version = "0.0.58"
|
||||
version = "0.0.59"
|
||||
description = "Jumpserver storage python sdk tools"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "jms-storage-0.0.58.tar.gz", hash = "sha256:9508864c5b65b2628291c39e3eeccedffd77448545a0359a4cb12c6435592e62"},
|
||||
{file = "jms-storage-0.0.59.tar.gz", hash = "sha256:62171d5182f4ab774dbe4204aed8dabc37926869067e2ed4dd52afab1df8d1d3"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -3592,7 +3612,6 @@ certifi = "2023.7.22"
|
|||
chardet = "5.1.0"
|
||||
crcmod = "1.7"
|
||||
docutils = "0.20.1"
|
||||
elasticsearch = "7.8.0"
|
||||
esdk-obs-python = "3.21.4"
|
||||
idna = "3.4"
|
||||
oss2 = "2.18.1"
|
||||
|
@ -3915,16 +3934,6 @@ files = [
|
|||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
|
@ -4172,6 +4181,7 @@ files = [
|
|||
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
|
||||
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
|
||||
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
|
||||
{file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
|
||||
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
|
||||
]
|
||||
|
||||
|
@ -5791,11 +5801,9 @@ files = [
|
|||
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:049f2e3de919e8e02504780a21ebbf235e21ca8ed5c7538c5b6e705aa6c43d8c"},
|
||||
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd86d8e3e346e34f3f03d12e333747b53a1daa74374a727f4714d5b82ee0dd5"},
|
||||
{file = "pymssql-2.2.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:508226a0df7cb6faeda9f8e84e85743690ca427d7b27af9a73d75fcf0c1eef6e"},
|
||||
{file = "pymssql-2.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:47859887adeaf184766b5e0bc845dd23611f3808f9521552063bb36eabc10092"},
|
||||
{file = "pymssql-2.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d873e553374d5b1c57fe1c43bb75e3bcc2920678db1ef26f6bfed396c7d21b30"},
|
||||
{file = "pymssql-2.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf31b8b76634c826a91f9999e15b7bfb0c051a0f53b319fd56481a67e5b903bb"},
|
||||
{file = "pymssql-2.2.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:821945c2214fe666fd456c61e09a29a00e7719c9e136c801bffb3a254e9c579b"},
|
||||
{file = "pymssql-2.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:cc85b609b4e60eac25fa38bbac1ff854fd2c2a276e0ca4a3614c6f97efb644bb"},
|
||||
{file = "pymssql-2.2.8-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ebe7f64d5278d807f14bea08951e02512bfbc6219fd4d4f15bb45ded885cf3d4"},
|
||||
{file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:253af3d39fc0235627966817262d5c4c94ad09dcbea59664748063470048c29c"},
|
||||
{file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9d109df536dc5f7dd851a88d285a4c9cb12a9314b621625f4f5ab1197eb312"},
|
||||
|
@ -5811,13 +5819,11 @@ files = [
|
|||
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3906993300650844ec140aa58772c0f5f3e9e9d5709c061334fd1551acdcf066"},
|
||||
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7309c7352e4a87c9995c3183ebfe0ff4135e955bb759109637673c61c9f0ca8d"},
|
||||
{file = "pymssql-2.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b8d603cc1ec7ae585c5a409a1d45e8da067970c79dd550d45c238ae0aa0f79f"},
|
||||
{file = "pymssql-2.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:293cb4d0339e221d877d6b19a1905082b658f0100a1e2ccc9dda10de58938901"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:895041edd002a2e91d8a4faf0906b6fbfef29d9164bc6beb398421f5927fa40e"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6b2d9c6d38a416c6f2db36ff1cd8e69f9a5387a46f9f4f612623192e0c9404b1"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63d6f25cf40fe6a03c49be2d4d337858362b8ab944d6684c268e4990807cf0c"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c83ad3ad20951f3a94894b354fa5fa9666dcd5ebb4a635dad507c7d1dd545833"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3933f7f082be74698eea835df51798dab9bc727d94d3d280bffc75ab9265f890"},
|
||||
{file = "pymssql-2.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:de313375b90b0f554058992f35c4a4beb3f6ec2f5912d8cd6afb649f95b03a9f"},
|
||||
{file = "pymssql-2.2.8.tar.gz", hash = "sha256:9baefbfbd07d0142756e2dfcaa804154361ac5806ab9381350aad4e780c3033e"},
|
||||
]
|
||||
|
||||
|
@ -6306,7 +6312,6 @@ files = [
|
|||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
|
@ -6314,15 +6319,8 @@ files = [
|
|||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
|
@ -6339,7 +6337,6 @@ files = [
|
|||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
|
@ -6347,7 +6344,6 @@ files = [
|
|||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
|
@ -7905,4 +7901,4 @@ reference = "tsinghua"
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "63da026ae0d9d5e6a62b25d265a50c95666e05f60cdae798c2e0443c93544e3a"
|
||||
content-hash = "54280c08e758b0767dbf7dd249a0d68f4a16c6de69a86723f00b2793c6bfd186"
|
||||
|
|
|
@ -47,7 +47,7 @@ pynacl = "1.5.0"
|
|||
python-dateutil = "2.8.2"
|
||||
pyyaml = "6.0.1"
|
||||
requests = "2.31.0"
|
||||
jms-storage = "^0.0.58"
|
||||
jms-storage = "^0.0.59"
|
||||
simplejson = "3.19.1"
|
||||
six = "1.16.0"
|
||||
sshtunnel = "0.4.0"
|
||||
|
@ -83,7 +83,6 @@ django-bootstrap3 = "23.4"
|
|||
django-filter = "23.2"
|
||||
django-formtools = "2.4.1"
|
||||
django-ranged-response = "0.2.0"
|
||||
django-rest-swagger = "2.2.0"
|
||||
django-simple-captcha = "0.5.18"
|
||||
django-timezone-field = "5.1"
|
||||
djangorestframework = "3.14.0"
|
||||
|
@ -156,6 +155,9 @@ annotated-types = "^0.6.0"
|
|||
httpx = "^0.27.0"
|
||||
distro = "1.9.0"
|
||||
tqdm = "4.66.4"
|
||||
elasticsearch7 = "7.17.9"
|
||||
elasticsearch8 = "8.13.2"
|
||||
|
||||
|
||||
|
||||
[tool.poetry.group.xpack.dependencies]
|
||||
|
|
Loading…
Reference in New Issue