Compare commits

...

558 Commits

Author SHA1 Message Date
ibuler
bb5b390d1d perf: 修复 middleware 导致的内存增长 2022-06-25 10:34:39 +08:00
Jiangjie.Bai
16c01733f1 fix: 修改 private_key 的序列类长度为 16384 2022-06-24 19:24:40 +08:00
feng626
b1b63445db fix: 修复confirm接口刚登录时可以跳过权限访问资源 2022-06-23 16:51:46 +08:00
Jiangjie.Bai
fca15eae7f fix: 修复post方法调用AuthBook接口时500的问题 2022-06-22 17:10:20 +08:00
ibuler
2c63b56f62 perf: 修改 redis scan counter 2022-06-20 19:39:29 +08:00
Jiangjie.Bai
ea5e56b33e fix: 修复es类型的命令存储更新忽略证书字段不成功的问题 2022-06-20 14:47:10 +08:00
ibuler
e4819ffe11 pref: 去掉 django-redis-cache 依赖 2022-06-20 14:12:22 +08:00
Eric
c34302325f fix:修复es日期索引忽略证书的问题 2022-06-20 14:06:35 +08:00
Jiangjie.Bai
3b5ee06535 Merge pull request #8430 from jumpserver/pr@v2.23@fix_ticketaction
fix: 修复工单自定义搜索时500的问题
2022-06-17 15:25:03 +08:00
Jiangjie.Bai
8e5edfd179 fix: 修复工单自定义搜索时500的问题 2022-06-17 07:22:15 +00:00
Jiangjie.Bai
d3355ab0ec Merge pull request #8427 from jumpserver/dev
v2.23.0 rc6
2022-06-16 18:12:44 +08:00
Jiangjie.Bai
81598a5264 perf: 推送系统用户用户名提示信息 2022-06-16 18:04:03 +08:00
feng626
298f6ba41d fix: 修改翻译 2022-06-16 18:03:16 +08:00
feng626
8e43e9ee2b fix: 授权过期通知 2022-06-16 17:52:33 +08:00
Jiangjie.Bai
adc8a8f7d3 fix: 修改翻译 2022-06-16 17:10:21 +08:00
Jiangjie.Bai
1e3da50979 fix: 修复会话加入记录更新失败的问题 2022-06-16 16:50:51 +08:00
Jiangjie.Bai
7ac385d64c Merge pull request #8420 from jumpserver/dev
v2.23.0 rc5
2022-06-16 15:46:40 +08:00
Jiangjie.Bai
2be74c4b84 fix: 修复命令列表模糊搜索报错500的问题
fix: 修复命令列表模糊搜索报错500的问题
2022-06-16 13:45:24 +08:00
feng626
75a72fb182 fix: user confirm bug 2022-06-16 11:31:27 +08:00
Jiangjie.Bai
4c2274b14e fix: 修改翻译 2022-06-16 11:19:26 +08:00
feng626
a024f26768 fix: 授权过期消息提示 2022-06-16 11:19:26 +08:00
Jiangjie.Bai
2898c35970 Merge pull request #8411 from jumpserver/dev
v2.23.0 rc4
2022-06-15 19:38:17 +08:00
Jiangjie.Bai
62f5662bd0 fix: 修复openid用户登录时默认邮件后缀使用配置项 2022-06-15 19:33:26 +08:00
ibuler
0fe221019a pref: 优化没有获取到节点的问题 2022-06-15 19:33:26 +08:00
ibuler
d745314aa1 perf: 优化签名认证 2022-06-15 19:33:26 +08:00
feng626
153fad9ac7 feat: add client linux arm64 version 2022-06-15 19:33:26 +08:00
Jiangjie.Bai
0792c7ec49 fix: 修改推送系统用户提示文案 2022-06-15 19:33:26 +08:00
fit2bot
e617697553 fix: 修复授权过期通知bug (#8404)
Co-authored-by: feng626 <1304903146@qq.com>
2022-06-15 19:33:26 +08:00
fit2bot
9dc7da3595 perf: 优化 apt (#8398)
* pref: 修改 oracle lib path

* perf: 优化 apt

Co-authored-by: ibuler <ibuler@qq.com>
2022-06-15 19:33:26 +08:00
Jiangjie.Bai
f7f4d3a42e fix: 过滤系统用户密码过滤ansible不支持的字符 2022-06-15 19:33:26 +08:00
feng626
70fcbfe883 perf: 授权过期通知 2022-06-15 19:33:26 +08:00
Jiangjie.Bai
9e16b79abe fix: 修复openid用户登录时默认邮件后缀使用配置项 2022-06-15 19:32:36 +08:00
ibuler
8c839784fb pref: 优化没有获取到节点的问题 2022-06-15 15:31:33 +08:00
ibuler
10adb4e6b7 perf: 优化签名认证 2022-06-15 15:30:51 +08:00
feng626
75c011f1c5 feat: add client linux arm64 version 2022-06-15 15:30:13 +08:00
Jiangjie.Bai
a882ca0d51 fix: 修改推送系统用户提示文案 2022-06-15 15:20:08 +08:00
fit2bot
e0a2d03f44 fix: 修复授权过期通知bug (#8404)
Co-authored-by: feng626 <1304903146@qq.com>
2022-06-15 15:01:56 +08:00
fit2bot
2414f34a5a perf: 优化 apt (#8398)
* pref: 修改 oracle lib path

* perf: 优化 apt

Co-authored-by: ibuler <ibuler@qq.com>
2022-06-14 19:59:00 +08:00
Jiangjie.Bai
2aebfa51b2 fix: 过滤系统用户密码过滤ansible不支持的字符 2022-06-14 18:49:35 +08:00
feng626
f91bfedc50 perf: 授权过期通知 2022-06-14 18:33:49 +08:00
Jiangjie.Bai
68aad56bad Merge pull request #8379 from jumpserver/dev
v2.23.0-rc3
2022-06-13 17:42:31 +08:00
ibuler
556ce0a146 perf: 继续优化一波 2022-06-13 16:46:14 +08:00
Jiangjie.Bai
95f8b12912 fix: 修复部分 password encrypted field extra kwargs 参数不生效问题 2022-06-13 16:44:01 +08:00
fit2bot
25ae790f7d fix: 修改client 版本 (#8375)
Co-authored-by: feng626 <1304903146@qq.com>
2022-06-13 15:45:10 +08:00
ibuler
0464b1a9e6 perf: 优化迁移 rbac 速度
perf: migrate
2022-06-13 15:18:15 +08:00
Jiangjie.Bai
3755f8f33a fix: 修复推送动态用户 comment 中包含空格导致推送失败的问题 2022-06-13 14:54:29 +08:00
Jiangjie.Bai
85b2ec2e6a Merge pull request #8362 from jumpserver/dev
v2.23.0-rc2
2022-06-10 19:12:17 +08:00
Jiangjie.Bai
9d1e94d3c2 fix: 修复手动登录系统用户连接RemoteApp应用获取不到认证信息的问题 2022-06-10 18:35:39 +08:00
Jiangjie.Bai
be75edcb41 Merge pull request #8353 from jumpserver/dev
v2.23.0-rc1
2022-06-09 17:40:10 +08:00
ibuler
a5c6ba6cd6 perf: 优化 perm app node 2022-06-09 10:48:48 +08:00
fit2bot
81ef614820 fix: relogin重置MFA_VERIFY_TIME (#8348)
Co-authored-by: feng626 <1304903146@qq.com>
2022-06-08 19:32:50 +08:00
ibuler
c6949b4f68 perf: 去掉 remote app 的加密 2022-06-08 10:04:14 +08:00
fit2bot
a5acdb9f60 perf: 统一校验当前用户api (#8324)
Co-authored-by: feng626 <1304903146@qq.com>
2022-06-07 19:26:07 +08:00
Jiangjie.Bai
2366f02d10 feat: 添加组件类型 razor 并替换 XRDP_ENABLED 2022-06-07 13:43:53 +08:00
Jiangjie.Bai
dade0cadda feat: 克隆角色权限 2022-06-06 16:13:12 +08:00
ibuler
e096244e75 pref: app tree 添加 icon 2022-06-06 14:00:34 +08:00
Jiangjie.Bai
3bc307d666 perf: 设置Connection Token 默认最少5分钟 (#8331) 2022-06-01 18:00:22 +08:00
Jiangjie.Bai
810c500402 feat: 添加配置项 CONNECTION_TOKEN_EXPIRATION 2022-05-31 18:23:48 +08:00
fit2bot
6c0d0c3e92 feat: OIDC 用户添加属性映射值 (#8327)
* feat: OIDC 用户添加属性映射值

* feat: OIDC 用户添加属性映射值

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-05-31 16:09:31 +08:00
Jiangjie.Bai
af1150bb86 feat: OIDC 用户添加属性映射值 2022-05-31 16:03:39 +08:00
ibuler
f7cbcc46f4 perf: 升级 ansible version 2022-05-31 16:01:21 +08:00
ibuler
327c6beab4 fix: 修复假数据构造 2022-05-30 16:39:47 +08:00
ibuler
196663f205 perf: 修改生成假数据 2022-05-30 16:03:13 +08:00
Jiangjie.Bai
15423291cc fix: 修复ldap用户登录时用户组不设置 2022-05-30 16:02:50 +08:00
ibuler
021635b850 perf: 优化 readme 2022-05-30 15:09:04 +08:00
老广
992c1407b6 Update README.md (#8316)
* Update README.md

* Update README.md

* Update README.md

* Update README.md
2022-05-30 14:51:29 +08:00
Chayim I. Kirshen
1322106c91 bumping redis-py to 4.3.1 (latest) 2022-05-30 13:30:25 +08:00
Jiangjie.Bai
42202bd528 fix: 修改 public settings API公告字段类型为 dict 2022-05-27 17:24:09 +08:00
fit2bot
b24d2f628a perf: update download (#8304)
Co-authored-by: feng626 <1304903146@qq.com>
2022-05-27 14:14:47 +08:00
fit2bot
041302d5d2 fix: 修复获取 city 时可能的报错 (#8294)
Co-authored-by: ibuler <ibuler@qq.com>
2022-05-24 12:31:16 +08:00
feng626
a08dd5ee72 fix: 修复用户更新自己密码 url 不准确问题 2022-05-24 11:16:13 +08:00
ibuler
09ef72a4a8 fix: 修复 Migrations 错误 2022-05-24 11:01:26 +08:00
ibuler
26cf64ad2d perf: 修改 i18 2022-05-20 11:41:33 +08:00
ibuler
0a04f0f351 perf: 下载 ip 数据库 2022-05-20 10:03:13 +08:00
fit2bot
1029556902 perf: remote app 字段也加密 (#8274)
* perf: remote app 字段也加密

* perf: 修改一些加密字段

Co-authored-by: ibuler <ibuler@qq.com>
2022-05-20 10:01:41 +08:00
Jiangjie.Bai
c41fc54380 Merge pull request #8271 from jumpserver/dev
v2.22.0-rc4
2022-05-18 20:21:35 +08:00
feng626
c2fbe5c75a fix: 不支持es8 提示 2022-05-18 20:20:54 +08:00
feng626
99e1b2cf92 fix: 不支持es8 提示 2022-05-18 20:14:31 +08:00
Jiangjie.Bai
33090c4cdf Merge pull request #8268 from jumpserver/dev
v2.22.0-rc4
2022-05-18 19:49:11 +08:00
fit2bot
c8d7c7c56f fix: 修复oidc认证不区分大小写 (#8267)
Co-authored-by: feng626 <1304903146@qq.com>
2022-05-18 18:32:53 +08:00
ibuler
aa7540045b feat: 添加 session guard 2022-05-18 14:55:58 +08:00
ibuler
e5f4b8000e stash 2022-05-18 14:55:58 +08:00
ibuler
44ffd09924 fix: 修复可能的 decode error 2022-05-18 10:17:15 +08:00
ibuler
fe3059c1fd fix: 修复获取密码失败 2022-05-17 23:50:30 +08:00
Jiangjie.Bai
b76920a4bf fix: 修改组织资源统计时 org 为None的问题 2022-05-17 22:11:42 +08:00
ibuler
b5ac5c5670 perf: domain gateway 也添加 2022-05-17 21:36:40 +08:00
ibuler
c3c0f87c01 perf: domain gateway 也添加 2022-05-17 21:32:31 +08:00
Jiangjie.Bai
d672122c79 Merge pull request #8260 from jumpserver/dev
v2.22.0-rc3
2022-05-17 21:14:05 +08:00
fit2bot
0c71190337 fix: 修改 EncryptedField 字段的 write_only 属性 (#8259)
* fix: 修改 EncryptedField 字段的 write_only 属性

fix: 修改 EncryptedField 字段的 write_only 属性

* fix: 修改 EncryptedField 字段的 write_only 属性

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-05-17 21:12:59 +08:00
feng626
14710e9c9e feat: 工单审批人中去除申请人 2022-05-17 20:56:53 +08:00
ibuler
7eec50804c perf: 优化 encrypted field 2022-05-17 20:04:46 +08:00
Jiangjie.Bai
0fc5a33983 fix: 修复企业微信、钉钉、飞书登录跳转问题 2022-05-17 18:57:49 +08:00
fit2bot
07779c5a7a perf: 工单启用 (#8254)
Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-05-17 18:57:04 +08:00
fit2bot
d675b1d4fc fix: k8s token 解密 (#8252)
Co-authored-by: feng626 <1304903146@qq.com>
2022-05-17 16:53:15 +08:00
Jiangjie.Bai
514fa9cf0a Merge pull request #8250 from jumpserver/dev
v2.22.0-rc2
2022-05-17 15:10:59 +08:00
ibuler
2c73611cb4 fix: 修复公告不显示的问题 2022-05-17 11:30:37 +08:00
ibuler
83571718e9 perf: 修改版本 2022-05-16 20:02:10 +08:00
ibuler
521ec0245b fix: ipdb 版本 2022-05-16 20:02:10 +08:00
jiangweidong
e80b6936a2 perf: 兼容AWS上redis[ssl]无证书无法部署的问题 2022-05-16 18:03:47 +08:00
Jiangjie.Bai
2c4f937e0b fix: 解决LDAP同步用户仪表盘总数没有刷新的问题 2022-05-16 17:52:49 +08:00
Jiangjie.Bai
2a5497de14 fix: 修改工单审批文案 2022-05-16 17:52:27 +08:00
feng626
d87dc7cbd6 fix: import ipdb 2022-05-16 17:51:37 +08:00
fit2bot
3b253e276c perf: 优化翻译 (#8244)
Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-05-16 17:50:28 +08:00
feng626
525538e775 fix: 修复密码密钥翻译问题 2022-05-16 17:48:28 +08:00
ibuler
2a8f8dd709 perf: 优化使用两个 ip 库 2022-05-16 15:24:38 +08:00
ibuler
1e6e59d815 perf: 添加 ipdb 2022-05-16 15:24:38 +08:00
ibuler
475678e29b fix: 修复密码 write only 2022-05-16 12:19:52 +08:00
Jiangjie.Bai
7f52675bd3 Merge pull request #8229 from jumpserver/dev
v2.22.0 rc1
2022-05-12 17:02:01 +08:00
fit2bot
6409b7deee feat: Endpoint添加Redis Port (#8225)
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-05-12 14:47:35 +08:00
ibuler
4f37b2b920 perf: 优化 setting 读取,避免遗漏 2022-05-12 11:52:44 +08:00
fit2bot
c692eed3c6 perf: 修改client 版本 (#8223)
Co-authored-by: feng626 <1304903146@qq.com>
2022-05-12 10:59:19 +08:00
ibuler
dab8828b03 perf: 优化 setting 获取 2022-05-11 17:57:33 +08:00
ibuler
d692188a34 perf: 修改 i18n 2022-05-11 16:03:26 +08:00
Jiangjie.Bai
bc8df72603 fix: 修改创建更新用户的密码字段 2022-05-11 16:02:56 +08:00
Jiangjie.Bai
bf466a1ba2 feat: LDAP同步用户支持组织 2022-05-11 11:09:30 +08:00
fit2bot
aff5b0035d perf: 优化加密 (#8206)
* perf: 优化加密

* perf: 优化加密

* perf: 优化加密传输

Co-authored-by: ibuler <ibuler@qq.com>
2022-05-10 17:28:10 +08:00
jiangweidong
b44fa64994 perf: 企业微信、钉钉工单审批增加拒绝功能 (#8208)
* perf: 工单直接审批增加拒绝功能

* feat: 翻译

* perf: 修改动作名词

* perf: 修改翻译
2022-05-10 16:30:25 +08:00
ibuler
094446c548 chore: 去掉一个workflow 2022-05-10 10:37:01 +08:00
jiangweidong
64eda5f28b perf: 命令存储ES可根据日期动态建立索引 (#8180)
* perf: 命令存储ES可根据日期动态建立索引

* perf: 优化合并字段

* feat: 修改逻辑
2022-05-09 16:37:31 +08:00
Jiangjie.Bai
ab737ae09b fix: 修复获取类型为null的命令显示不支持的问题
fix: 修复获取类型为null的命令显示不支持的问题
2022-05-07 17:56:50 +08:00
jiangweidong
55e04e8e9f feat: 内置AIX系统,根据系统选择算法加密密码 2022-05-07 16:25:03 +08:00
jiangweidong
5e70a8af15 feat: 支持平台关联算法,支持AIX改密 2022-05-07 16:25:03 +08:00
fit2bot
031077c298 perf: password 等使用 rsa 加密传输 (#8188)
* perf: 修改 model fields 路径

* stash it

* pref: 统一加密方式,密码字段采用 rsa 加密

* pref: 临时密码使用 rsa

* perf: 去掉 debug msg

* perf: 去掉 Debug

* perf: 去掉 debug

* perf: 抽出来

Co-authored-by: ibuler <ibuler@qq.com>
2022-05-07 16:20:12 +08:00
Jiangjie.Bai
3f856e68f0 feat: public settings 区分 public 和 open 2022-05-07 11:09:24 +08:00
fit2bot
56862a965d fix: 修复system-role获取users失败的问题 (#8196)
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-05-07 10:40:12 +08:00
jiangweidong
e151548701 perf: 账号管理中查看密码记录日志 (#8157) 2022-05-05 14:42:09 +08:00
jiangweidong
c56179e9e4 feat: 支持企业微信、钉钉直接审批工单 (#8115) 2022-05-05 13:07:48 +08:00
feng626
d23953932f perf: connection token 分api权限 2022-05-05 11:45:26 +08:00
Jiangjie.Bai
2493647e5c fix: 修复windows执行ansible显示sudo失败的问题 2022-05-05 11:40:12 +08:00
Jiangjie.Bai
00ed7bb025 perf: 优化 OIDC 支持选择认证方式 2022-04-29 14:28:07 +08:00
xiaziheng
b1aadf1ee9 Fix oidc (#8165) 2022-04-29 10:59:29 +08:00
feng626
86e6982383 fix: 组织管理员 添加 view platform perm 2022-04-28 19:10:58 +08:00
halo
dc42d1caa2 perf: 修改ssh_client连接选项翻译 2022-04-28 19:09:44 +08:00
ibuler
cb5d8fa13f fix: 去掉自动生成的map文件 2022-04-26 16:46:47 +08:00
jiangweidong
3a3f7eaf71 feat: 优化SAML2生成的metadata文件内容及属性映射 2022-04-26 10:00:53 +08:00
fit2bot
9804ca5dd0 fix: workbench_orgs 去重 (#8150)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-25 11:38:15 +08:00
老广
034d0e285c Update README.md 2022-04-24 17:47:02 +08:00
feng626
104d672634 perf: client download 2022-04-24 15:09:33 +08:00
ibuler
529e3d12e0 perf: 删除 build 2022-04-24 09:12:29 +08:00
ibuler
978c1f6363 perf: 修改 Dockerfile, 优化构建 2022-04-24 09:12:29 +08:00
ibuler
d25cde1bd5 fix: 修复社区版跳转问题 2022-04-21 22:48:59 +08:00
Jiangjie.Bai
a4be0ff2f3 Merge pull request #8131 from jumpserver/dev
v2.21.0
2022-04-21 18:11:21 +08:00
Jiangjie.Bai
a6d61721dd fix: 修改csrftoken获取问题 2022-04-21 16:17:33 +08:00
fit2bot
c3de7b78c2 fix: 远程应用授权时 有些资产已经不存在了 导致授权失败 (#8127)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-21 15:36:40 +08:00
Jiangjie.Bai
e83d676712 Merge pull request #8119 from jumpserver/dev
v2.21.0-rc6
2022-04-20 20:25:43 +08:00
Jiangjie.Bai
63ee2dd8fb fix: 修复获取权限树权限控制 2022-04-20 20:14:44 +08:00
feng626
74f88d842d fix: 修改replay download perm 2022-04-20 19:14:42 +08:00
fit2bot
e61bae5ee4 perf: 优化权限位 (#8110)
* perf: 优化权限位

* perf: 优化返回的组织

* perf: 保证结果是 ok

* perf: 去掉 distinct

* perf: tree count

Co-authored-by: ibuler <ibuler@qq.com>
2022-04-20 18:50:53 +08:00
fit2bot
b0b379e5a9 fix: del org check ldap (#8114)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-20 16:38:42 +08:00
fit2bot
415521a003 fix: 删除组织时检测ldap同步组织 (#8112)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-20 16:32:33 +08:00
Jiangjie.Bai
c29d133776 feat: 修改LDAP导入定时任务interval/crontab优先级
Signed-off-by: Jiangjie.Bai <bugatti_it@163.com>
2022-04-20 16:31:56 +08:00
fit2bot
d2dd487e2c feat: 修改LDAP导入组织问题 (#8111)
Signed-off-by: Jiangjie.Bai <bugatti_it@163.com>

Co-authored-by: BaiJiangJie <bugatti_it@163.com>
2022-04-20 16:05:33 +08:00
ibuler
f1bd4ea91f perf: 修改 系统级别用户角色的 perms 2022-04-20 11:50:11 +08:00
fit2bot
7647438792 perf: 账号备份log (#8106)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-20 11:18:50 +08:00
Jiangjie.Bai
015ff4b119 Merge pull request #8105 from jumpserver/dev
v2.21.0-rc5
2022-04-20 10:46:27 +08:00
fit2bot
af9248ef7c fix: 还原connection token 逻辑 (#8101)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-20 10:24:08 +08:00
Jiangjie.Bai
c04ab1aab9 Merge pull request #8100 from jumpserver/dev
v2.21.0-rc5
2022-04-19 21:52:51 +08:00
feng626
611a00a5fa fix: 修复super user perm bug 2022-04-19 21:52:01 +08:00
Jiangjie.Bai
57969a4e23 fix: 修改获取smart endpoint的逻辑 2022-04-19 19:50:18 +08:00
Jiangjie.Bai
5f370c1c04 perf: 优化内置系统用户角色权限 2022-04-19 19:19:47 +08:00
Jiangjie.Bai
f026b86a20 fix: 修复获取组织用户 2022-04-19 18:09:07 +08:00
ibuler
0addba7c14 perf: 修改 command 命令执行 2022-04-19 17:21:27 +08:00
ibuler
e4b0ab6a45 perf: 修改命令执行区分组织 2022-04-19 17:21:27 +08:00
Jiangjie.Bai
b4ac24ad6d fix: 修改endpoint/rule权限树位置 2022-04-19 17:21:09 +08:00
halo
500477fad1 fix: ftp日志清理bug 2022-04-19 17:13:17 +08:00
Jiangjie.Bai
3b9cb2a99c fix: 修改翻译临时密码 2022-04-19 16:26:33 +08:00
Jiangjie.Bai
f8fade4cf2 feat: 添加配置项 KoKo SSH Client 方式 2022-04-19 16:16:55 +08:00
Jiangjie.Bai
be2708f83d fix: 修复ajax请求携带csrftoken问题 2022-04-19 14:29:23 +08:00
ibuler
516cb05d69 perf: 修改翻译 2022-04-19 13:33:28 +08:00
老广
714b6b1233 Merge pull request #8085 from jumpserver/dev
v2.21.0-rc5
2022-04-19 13:15:16 +08:00
ibuler
3e3835dc28 perf: 修改用户权限 2022-04-19 10:42:36 +08:00
ibuler
f4ed4e1176 perf: 添加 temp token 排序 2022-04-18 19:55:16 +08:00
feng626
7b2d51f343 fix: 修复角色过滤失败 2022-04-18 19:54:44 +08:00
feng626
fe47e40588 fix: es6 create index fail 2022-04-18 19:44:54 +08:00
fit2bot
4362f8d5af perf: 优化组织 (#8080)
* perf: 优化用户的orgs

* perf: 优化组织

Co-authored-by: ibuler <ibuler@qq.com>
2022-04-18 17:17:23 +08:00
Jiangjie.Bai
6f49d240af Merge pull request #8079 from jumpserver/dev
v2.21.0-rc4
2022-04-18 15:31:02 +08:00
Jiangjie.Bai
3eab621b28 feat: 优化Endpoint迁移逻辑,增加XRDP规则和Endpoint
fix: 修改Endpoint迁移文件
2022-04-18 15:29:40 +08:00
Jiangjie.Bai
afcbe60531 Merge pull request #8076 from jumpserver/dev
v2.21.0-rc3
2022-04-18 11:43:40 +08:00
jiangweidong
548a374c6d fix: 修复部署在没有密码的redis上时,站内信数量不更新问题 2022-04-18 11:39:20 +08:00
feng626
10c146b07d fix: 修复远程应用无资产下载xrdp file 500 问题 2022-04-18 11:38:31 +08:00
fit2bot
a647e73c02 feat: 设置SessionCookieNamePrefix (#8071)
* feat: 设置SessionCookieNamePrefix

* feat: 设置SessionCookieNamePrefix

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-04-15 21:33:15 +08:00
Jiangjie.Bai
7b02777f1e fix: 修改endpoint smart API允许有效用户访问 2022-04-15 16:33:41 +08:00
Jiangjie.Bai
97e59384e0 fix: connection token API 返回有效时间 2022-04-15 16:33:20 +08:00
jiangweidong
70a07539af perf: 优化部分云厂商的redis连接的问题 2022-04-15 10:00:49 +08:00
Jiangjie.Bai
f98c170b8c Merge pull request #8061 from jumpserver/dev
v2.21.0-rc2
2022-04-14 19:51:29 +08:00
fit2bot
0b94d7414a feat: download (#8062)
Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2022-04-14 19:51:10 +08:00
feng626
7aa0c9bf19 feat: download 2022-04-14 15:00:59 +08:00
Jiangjie.Bai
6d8e8856ac fix: 修改命令timestamp_display只读 2022-04-14 14:45:19 +08:00
Jiangjie.Bai
c240a471dc fix: Public Setting 添加 Magnus 2022-04-14 14:16:02 +08:00
Jiangjie.Bai
ea478fc801 fix: Public Setting 添加 Magnus 2022-04-14 12:55:18 +08:00
fit2bot
5127214375 feat: 站内信一键已读 (#8057)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-14 12:18:11 +08:00
Jiangjie.Bai
21c41a6334 Merge pull request #8054 from jumpserver/dev
v2.21.0-rc1
2022-04-13 20:25:47 +08:00
fit2bot
b610d71e11 feat: 添加 临时 password (#8035)
* perf: 添加 template password

* perf: 修改id

* perf: 修改 翻译

* perf: 修改 tmp token

* perf: 修改 token

Co-authored-by: ibuler <ibuler@qq.com>
2022-04-13 20:24:56 +08:00
Jiangjie.Bai
10b033010e feat: 优化命令导出时间戳可读性 2022-04-13 19:50:17 +08:00
fit2bot
c630b11bd5 fix: port str (#8055)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-13 19:48:31 +08:00
ibuler
b0f7c114fc perf: 修改 csrf token domain 2022-04-13 16:08:07 +08:00
fit2bot
72608146cc chore: lgtm (#8048)
* chore: lgtm

* perf: add lgtm

Co-authored-by: ibuler <ibuler@qq.com>
2022-04-12 18:25:28 +08:00
ibuler
3213fe0984 chore: 添加action lgtm 2022-04-12 18:06:42 +08:00
fit2bot
f481463c64 feat: 添加Endpoint (#8041)
* feat: add Endpoint EndpointRule EndpointProtocol model

* feat: add Endpoint EndpointRule EndpointProtocol API

* feat: modify protocols field

* feat: 修改序列类

* feat: 获取connect-url连接地址

* feat: 获取connect-url连接地址

* feat: 优化后台获取smart-endpoint逻辑

* feat: 优化后台获取smart-endpoint逻辑

* feat: 删除配置KOKO、XRDP、MAGNUS

* feat: 删除配置KOKO、XRDP、MAGNUS

* feat: 修改翻译

* feat: 修改smart endpoint

* feat: 修改翻译

* feat: smart API 添加token解析

* feat: 删除 smart serializer

* feat: 修改迁移逻辑

* feat: 解决冲突

* feat: 修改匹配 endpoint

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-04-12 17:45:10 +08:00
ibuler
4cf90df17c perf: 默认角色添加 created by 2022-04-12 16:20:46 +08:00
ibuler
ffd98c6e3f fix: 修改 import 2022-04-12 16:20:46 +08:00
fit2bot
1f8ded49fa feat: 工作台区分组织 (#8040)
* perf: 工作台受组织角色控制

* perf: workspace => workbench

* perf: 修改 workspace codename

Co-authored-by: ibuler <ibuler@qq.com>
2022-04-12 14:25:49 +08:00
fit2bot
7c7d7d52b2 perf: asset number 扩容 (#8045)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-12 14:01:57 +08:00
feng626
f769d5a9bb fix: 修复用户数据不同步问题 2022-04-08 15:47:14 +08:00
feng626
c8758f417d feat: ldap一键导入及设置用户组织 2022-04-06 17:13:34 +08:00
Eric
ef36b2e662 perf: 完善 setting 的动态配置 2022-04-06 16:45:22 +08:00
jiangweidong
fe8527fd07 feat: 修改翻译 2022-04-02 16:04:13 +08:00
feng626
2cb08b4785 fix: user is common user 2022-04-02 16:02:57 +08:00
fit2bot
a936092020 perf: es相关代码格式优化 (#8020)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-02 13:26:18 +08:00
fit2bot
e602bc0341 fix: 修复网关翻译 (#8016)
Co-authored-by: feng626 <1304903146@qq.com>
2022-04-01 16:52:50 +08:00
Jiangjie.Bai
3121b4e3ff feat: 更新翻译 2022-03-31 13:05:55 +08:00
Jiangjie.Bai
eff562505e feat: 更新翻译 2022-03-31 13:05:55 +08:00
Jiangjie.Bai
73cb5e10b4 fix: 添加用户不能自更新字段逻辑 & 修复用户is_active创建失败的问题
fix: 添加用户不能自更新字段逻辑 & 修复用户is_active创建失败的问题

fix: 添加用户不能自更新字段逻辑 & 修复用户is_active创建失败的问题
2022-03-30 19:52:45 +08:00
fit2bot
c58d245636 fix: 修复koko setting (#8005)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-30 19:51:56 +08:00
fit2bot
e7af037513 perf: 修改命令command input 长度问题 (#7996)
* perf: 修改命令command input max_length 1024

* perf: 修改命令command input 长度问题

* perf: 修改命令command input 长度问题

* perf: 修改命令command input 长度问题

* perf: 修改命令command input 长度问题

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-30 19:07:49 +08:00
Jiangjie.Bai
54d1996507 feat: 支持续期Connection Token 2022-03-30 11:20:56 +08:00
dependabot[bot]
71f8b40e21 build(deps): bump paramiko from 2.7.2 to 2.10.1 in /requirements
Bumps [paramiko](https://github.com/paramiko/paramiko) from 2.7.2 to 2.10.1.
- [Release notes](https://github.com/paramiko/paramiko/releases)
- [Changelog](https://github.com/paramiko/paramiko/blob/main/NEWS)
- [Commits](https://github.com/paramiko/paramiko/compare/2.7.2...2.10.1)

---
updated-dependencies:
- dependency-name: paramiko
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 11:06:49 +08:00
ibuler
59342a88c0 perf: 优化各种翻译 2022-03-29 19:33:38 +08:00
fit2bot
b8e6bc932b perf: 添加 mariadb port (#7989)
* perf: 添加 mariadb port

* perf: 优化 mariadb 树上天津更多信息

* perf: remove mixin

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-29 17:22:59 +08:00
Jiangjie.Bai
cddff9fd19 feat: 改密计划支持su切换用户执行
feat: 改密计划支持su切换用户执行

feat: 改密计划支持su切换用户执行

feat: 改密计划支持su切换用户执行

feat: 改密计划支持su切换用户执行

feat: 改密计划支持su切换用户执行

feat: 改密计划支持su切换用户执行
2022-03-29 15:32:33 +08:00
feng626
d856f1364a feat: 拉起ssh api 2022-03-29 13:23:58 +08:00
fit2bot
52709d2efa feat: 企业微信、钉钉 工作台免密登录(飞书已实现) (#7855)
* feat: 添加oauth接口

* feat: 企业微信支持OAuth认证,工作台免密登录

* feat: 钉钉支持OAuth认证,工作台免密登录

* fix: 修复参数错误

Co-authored-by: halo <wuyihuangw@gmail.com>
2022-03-29 13:19:13 +08:00
ibuler
a20de3df16 perf: app tree 添加attrs,luna 使用 2022-03-29 12:44:36 +08:00
ibuler
e303b4f571 perf: 修复 settings patch 问题 2022-03-28 13:40:36 +08:00
jiangweidong
03fdaa03e4 feat: 支持日语 2022-03-28 10:06:54 +08:00
fit2bot
b7b1d81ea0 fix: magenus bug (#7977)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-25 16:46:36 +08:00
fit2bot
e0fdfa52b9 feat: 支持 magnus (#7965)
* feat: 支持 magnus

* perf: 添加 setting 到 api

* perf: 放出 mongodb

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-25 14:45:08 +08:00
ibuler
8718dc6751 pref: 优雅一发 2022-03-25 14:44:33 +08:00
ibuler
9e284f96e5 perf: 修改依赖写法,避免 github 认错 2022-03-25 14:44:33 +08:00
Eric
fc06295d04 perf: 优化 windows rdp 窗口显示 2022-03-25 10:50:34 +08:00
fit2bot
9b73727bbc fix: 修复系统组件绑定角色bug (#7962)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-23 10:56:23 +08:00
Jiangjie.Bai
6bde31cdd0 fix: 修复获取远程应用认证信息问题 2022-03-22 18:54:25 +08:00
ibuler
2721793b8f fix: 修复权限 view 没有 Model 的问题 2022-03-22 16:59:01 +08:00
feng626
2ec0cb8a2c fix: 修复用户绑定角色重大bug 2022-03-22 16:55:53 +08:00
feng626
d01d44b48d fix: api docs 2022-03-22 13:05:44 +08:00
老广
0ef7a9571c Merge pull request #7800 from jumpserver/pr@dev@feat_core_redis_support_ssl
feat: JumpServer支持部署在使用了ssl的redis上
2022-03-22 10:34:32 +08:00
ibuler
54fd1fb0c8 perf: 移动到信号中 2022-03-21 19:27:23 +08:00
ibuler
87c6eec619 perf: 优化 role bingding,优化 is_superuser 2022-03-21 19:27:23 +08:00
jiangweidong
e35fbfc7e9 Update session.py 2022-03-21 19:02:30 +08:00
fit2bot
3345456dc2 fix: 修复wateway api (#7947)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-21 17:53:16 +08:00
Jiangjie.Bai
9ae74120ed fix: 修复用户API权限 2022-03-21 12:00:15 +08:00
Jiangjie.Bai
9e5c132485 fix: 修复用户API权限 2022-03-21 11:58:29 +08:00
fit2bot
5cc2fdae4f fix: 修复api docs打不开的问题 (#7938)
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-21 11:14:49 +08:00
jiangweidong
e993f31b6d feat: 支持纳管百度云资产 (#7921)
Co-authored-by: ibuler <ibuler@qq.com>
2022-03-21 10:39:47 +08:00
Jiangjie.Bai
60edbb36a1 fix: 应用树隐藏mongodb节点 2022-03-18 18:02:15 +08:00
fit2bot
5da1ec55a7 perf: org del ticket perm (#7932)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-18 17:44:20 +08:00
Jiangjie.Bai
b8c083af7e fix: 工单权限位放到sys角色中 2022-03-18 17:36:22 +08:00
Jiangjie.Bai
996621f303 fix: 移除权限dashboard 2022-03-18 17:10:34 +08:00
fit2bot
ec9e5da653 fix: 修复apikey perm (#7918)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-18 15:53:34 +08:00
Jiangjie.Bai
d4e4015d91 fix: 修复去除rolebiding change 权限 2022-03-18 15:38:33 +08:00
Jiangjie.Bai
005dd27701 Merge pull request #7917 from jumpserver/dev
v2.20.0
2022-03-17 19:22:22 +08:00
Jiangjie.Bai
ac6052546a fix: 修改翻译 2022-03-17 19:20:39 +08:00
Jiangjie.Bai
0265adcc72 fix: 修改翻译 2022-03-17 19:20:39 +08:00
Jiangjie.Bai
9654083662 fix: 修改站内信权限位 2022-03-17 19:06:44 +08:00
fit2bot
08ff8fa285 fix: login confirm bug (#7914)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-17 18:50:37 +08:00
Jiangjie.Bai
f82f7eba2b fix: 修改一些权限 2022-03-17 17:51:02 +08:00
Jiangjie.Bai
a8cee26874 fix: 修复资产详情授权用户tab页的权限位 2022-03-17 17:25:20 +08:00
Jiangjie.Bai
8080d36d90 Merge pull request #7911 from jumpserver/dev
v2.20.0-rc6
2022-03-17 17:07:22 +08:00
ibuler
3ed7477057 perf: 修改 default role 2022-03-17 16:53:29 +08:00
ibuler
a3cddd5d34 fix: 修复批量命令的bug 2022-03-17 16:26:22 +08:00
Jiangjie.Bai
26b3c60e5c fix: 删除一些perms相关的权限位 2022-03-17 16:26:00 +08:00
ibuler
b5dea38164 perf: 修改bug 2022-03-17 16:09:51 +08:00
ibuler
7addb881f6 fix: 修复系统用户Perms 2022-03-17 16:09:51 +08:00
fit2bot
9c395b674f fix: command log search (#7906)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-17 15:39:05 +08:00
Jiangjie.Bai
b297ebe973 fix: 修改应用category 2022-03-17 15:37:57 +08:00
fit2bot
5c7bfcff1c perf: 修改 i18n (#7904)
* perf: some perm to xpack

* perf: 修改 i18n

* perf: 修改 i18n

* perf: 修改 i18n

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-03-17 15:23:22 +08:00
fit2bot
76796f249d fix: user match (#7903)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-17 14:47:24 +08:00
Jiangjie.Bai
55a63477ed fix: 去除系统用户资产相关权限 2022-03-17 14:38:19 +08:00
ibuler
5942037d81 perf: some perm to xpack 2022-03-17 14:24:52 +08:00
Jiangjie.Bai
5882b8a682 fix: 去除application secret change perm 2022-03-17 14:24:25 +08:00
fit2bot
34e75099a3 perf: 设置默认的角色,系统用户角色添加权限 (#7898)
* perf: 修改 role handler

* perf: 设置默认的角色,系统用户角色添加权限

* perf: authentication 还是放到系统中吧

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-03-17 14:08:16 +08:00
feng626
8fe84345e4 fix: 工单默认权限 2022-03-17 14:06:34 +08:00
Jiangjie.Bai
a31c3ccc30 fix: 去除application account delete perm 2022-03-17 14:05:08 +08:00
Jiangjie.Bai
e13e34098a fix: 站内信命令告警中的会话地址 2022-03-17 14:05:08 +08:00
feng626
e8653c74cd fix: ticket URL type 2022-03-17 12:35:08 +08:00
feng626
1433c35ff9 fix: test gateway 2022-03-17 12:33:59 +08:00
Jiangjie.Bai
a237b5a63d fix: 去掉terminal task add/delete 权限位 2022-03-17 11:40:54 +08:00
feng626
2587c8693e fix: account_change_pwd 2022-03-17 11:36:48 +08:00
feng626
dfe5e2bce3 fix: 修改rbac permission 2022-03-17 11:36:48 +08:00
Jiangjie.Bai
91a34d1a88 Merge pull request #7888 from jumpserver/dev
v2.20.0-rc5
2022-03-16 20:49:53 +08:00
Jiangjie.Bai
1a05a942c2 fix: 更新资产详情授权用户权限 2022-03-16 20:49:29 +08:00
Jiangjie.Bai
30556023d1 fix: 修复会话录像播放权限 2022-03-16 20:42:38 +08:00
fit2bot
aa022a02c1 fix: 修复用户认证失败的详细信息显示 (#7886)
* fix: 修复用户认证失败的详细信息显示

* fix: 更新授权树翻译

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-03-16 20:41:54 +08:00
fit2bot
433d829c29 fix: 添加profile myorgs (#7887)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-16 20:36:12 +08:00
fit2bot
3b507dc795 perf: 优化英文下树的显示 (#7883)
* perf: 优化英文下树的显示

* perf: 修改翻译

* perf: 修改翻译

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-16 20:27:38 +08:00
Jiangjie.Bai
8233c69038 fix: 修复adhoc权限 2022-03-16 19:26:40 +08:00
ibuler
0fbc548c02 fix: 修复访问 flower 错误 2022-03-16 19:05:06 +08:00
Jiangjie.Bai
aa9ae14e46 fix: 修复view action获取 2022-03-16 18:33:55 +08:00
Jiangjie.Bai
04b35ba520 fix: 修复批量命令权限位 2022-03-16 18:33:13 +08:00
ibuler
580d2cd80b perf: 修改 org admin 2022-03-16 18:26:16 +08:00
ibuler
da9136f7af perf: 修改 org role 2022-03-16 18:26:16 +08:00
ibuler
1ce2706f20 perf: 修改翻译 2022-03-16 18:26:16 +08:00
ibuler
bbdeba3659 perf: 修复 org admin all 2022-03-16 18:26:16 +08:00
feng626
3a26b9d102 fix: 修复工单相应bug 2022-03-16 17:40:51 +08:00
ibuler
ee757e261d perf: 修改写法 2022-03-16 17:14:42 +08:00
ibuler
f41e6db007 perf: sso token 只能超级管理员 2022-03-16 17:14:42 +08:00
ibuler
7eed7b32cc perf: 修复 org role binding 在root组织下看到的可能不对 2022-03-16 17:14:42 +08:00
Jiangjie.Bai
efb26132f6 fix: 修复sso用户登录失败的问题 2022-03-16 17:11:22 +08:00
Jiangjie.Bai
572c5b6925 fix: 修改工单管理权限位 2022-03-16 14:54:50 +08:00
ibuler
8a1cd7e2a9 perf: fix some bug 2022-03-16 14:18:01 +08:00
Jiangjie.Bai
c065f82d30 fix: 排除收集用户执行的更新、删除权限位 2022-03-16 14:17:35 +08:00
ibuler
995c9a6c19 perf: 修复一些bug, rolebingding 找到合适的 2022-03-16 14:08:54 +08:00
Jiangjie.Bai
5ec970fab4 fix: 全局组织受权限位控制 2022-03-16 14:07:00 +08:00
Jiangjie.Bai
166745baf6 Merge pull request #7866 from jumpserver/dev
v2.20.0 rc4
2022-03-15 20:54:40 +08:00
Jiangjie.Bai
d320443c9f fix: 去除change_setting权限位 2022-03-15 20:25:18 +08:00
ibuler
4bfa88f01f perf: 修改 app tree 2022-03-15 20:09:30 +08:00
Jiangjie.Bai
aedd8ba589 fix: 修复查看我的应用翻译 2022-03-15 19:51:59 +08:00
Jiangjie.Bai
172b492bc3 fix: 修复存储测试权限位 2022-03-15 19:41:25 +08:00
Jiangjie.Bai
c4280d259a fix: 修复翻译/权限设置 2022-03-15 19:18:01 +08:00
Jiangjie.Bai
b25ec559bb fix: 修复翻译/权限设置 2022-03-15 19:18:01 +08:00
ibuler
cd46c8c78e perf: 发送验证码,不再提示 2022-03-15 18:47:12 +08:00
ibuler
8839e6293b perf: 修改 mfa 地址 2022-03-15 18:47:12 +08:00
ibuler
4f887b1b11 perf: 优化 tree node icon 2022-03-15 18:46:11 +08:00
fit2bot
90840a4417 fix: 工单创建bug (#7858)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-15 18:22:13 +08:00
fit2bot
a18b9bad0a fix:  修复ticketsession (#7857)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-15 17:32:54 +08:00
feng626
e1a238b778 fix: 删除资产授权缓存接口 2022-03-15 16:55:45 +08:00
吴小白
ee44ae2e12 perf: 优化构建速度 2022-03-15 16:55:10 +08:00
fit2bot
d77e84e6f8 fix: ticket comment bug (#7854)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-15 16:50:51 +08:00
Jiangjie.Bai
2042c7a6e5 fix: 云同步权限显示创建执行 2022-03-15 14:18:37 +08:00
Jiangjie.Bai
40aca26155 fix: 暂时排除会话分享的权限位 2022-03-15 13:13:16 +08:00
Jiangjie.Bai
e18e76002c fix: 暂时排除会话分享的权限位 2022-03-15 13:00:56 +08:00
Jiangjie.Bai
c77f02b295 Merge pull request #7844 from jumpserver/dev
v2.20.0-rc3
2022-03-15 11:37:30 +08:00
Jiangjie.Bai
3924ff0114 fix: 修改查看会话录像权限位 2022-03-15 11:37:09 +08:00
Jiangjie.Bai
6a0264ad3b fix: 删除权限位connect_myasset/myapp 2022-03-15 11:11:37 +08:00
Jiangjie.Bai
2d7349d596 fix: 删除权限位connect_myasset/myapp 2022-03-15 11:11:37 +08:00
feng626
c41a81c8d0 fix: 修改授权权限 2022-03-15 11:06:48 +08:00
feng626
7ba19ab1a1 fix: exclude adhoc change delete 2022-03-14 20:36:02 +08:00
Jiangjie.Bai
72247d1df3 fix: 修复批量命令权限 2022-03-14 20:35:43 +08:00
feng626
faf82d7cfb fix: del connect my asset 2022-03-14 20:34:35 +08:00
feng626
4e8defc647 fix: 修复上传bug 2022-03-14 19:15:12 +08:00
Jiangjie.Bai
2f18208874 fix: 修复资产改密计划权限控制 2022-03-14 19:01:26 +08:00
Jiangjie.Bai
b37e8cdc3f fix: 修复更新资产账号权限控制 2022-03-14 17:51:03 +08:00
Jiangjie.Bai
5b960fc46b fix: 修复测试资产账号权限 2022-03-14 17:51:03 +08:00
fit2bot
df51c82cfd perf: 优化Migration,删掉原来的 content type (#7835)
Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-03-14 17:10:34 +08:00
feng626
e9deb6fc7a fix: app relation api 2022-03-14 17:05:47 +08:00
Jiangjie.Bai
cca49fa9cd fix: 修复授权树显示 2022-03-14 16:47:29 +08:00
Jiangjie.Bai
cfed849175 Merge pull request #7834 from jumpserver/dev
fix: 修复setting perm
2022-03-14 15:53:11 +08:00
feng626
a7cc457f54 fix: 修复setting perm 2022-03-14 15:50:53 +08:00
Jiangjie.Bai
5996cedcd6 Merge pull request #7832 from jumpserver/dev
fix: 修复权限问题
2022-03-14 15:16:51 +08:00
feng626
567c1b0124 fix: node test refresh perm 2022-03-14 14:37:59 +08:00
fit2bot
2da541c127 fix: 修改授权树(账号备份) (#7830)
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-14 14:23:13 +08:00
jiangweidong
794139782f feat: JumpServer支持部署在使用了ssl的redis上,可使用证书连接 2022-03-14 11:35:14 +08:00
Jiangjie.Bai
307b739a03 fix: 修改授权树(收集用户) 2022-03-14 11:34:14 +08:00
feng626
ca5708988a fix: 修复saml2 auth 2022-03-14 11:17:15 +08:00
ibuler
90d84f4d69 perf: 修改消息订阅 2022-03-14 11:16:54 +08:00
feng626
758f418f63 perf: 删除多余权限 2022-03-14 10:48:04 +08:00
Jiangjie.Bai
a64ec8a1d2 Merge pull request #7825 from jumpserver/dev
v2.20.0-rc2
2022-03-14 10:38:35 +08:00
ibuler
60564d1b4f perf: 修改 bug 2022-03-14 10:01:51 +08:00
fit2bot
017710c056 perf: 修改perms (#7822)
* perf: 修改 perm tree

* perf: 修改perms

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-11 21:24:07 +08:00
Jiangjie.Bai
a876a82a76 fix: 从组织移除用户 2022-03-11 21:20:44 +08:00
Jiangjie.Bai
8423ae602f fix: 移除TICKET_ENABLED配置;系统设置API限制权限 2022-03-11 21:02:26 +08:00
Jiangjie.Bai
8e2471c1eb fix: 移除TICKET_ENABLED配置;系统设置API限制权限 2022-03-11 21:02:26 +08:00
Jiangjie.Bai
224a9fbdb3 fix: 修复创建授权时actions为空保存时报错的问题 2022-03-11 21:00:37 +08:00
ibuler
797b184c7f perf: 修改 perm tree 2022-03-11 20:31:38 +08:00
fit2bot
b3632f6531 fix: dingtalk auth perm (#7814)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 19:32:23 +08:00
fit2bot
e3bc54e764 fix: 修复用户组添加用户 (#7809)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 19:31:55 +08:00
fit2bot
f0325c48df fix: 工单权限 (#7808)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 19:31:29 +08:00
fit2bot
416d4bd0c3 fix: 修复tree (#7802)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 17:24:28 +08:00
fit2bot
10c877c120 fix: 修复工单ticket exclude perm (#7799)
* fix: 修复工单ticket exclude perm

* fix: 修复perm tree

Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 13:30:14 +08:00
jiangweidong
f04378eaf8 feat: JumpServer支持部署在使用了ssl的redis上 2022-03-11 12:56:22 +08:00
fit2bot
b644c47173 perf: 排除工单流权限 (#7798)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-11 11:04:21 +08:00
老广
45331dc9e8 Merge pull request #7796 from jumpserver/dev
v2.20.0-rc1
2022-03-10 20:34:18 +08:00
ibuler
8a565b9eef perf: 修改设置 2022-03-10 20:30:41 +08:00
ibuler
4eb7b50b52 perf: 修改设置 2022-03-10 20:30:41 +08:00
Jiangjie.Bai
fd64bd03b4 fix: 修改LDAP配置API权限 2022-03-10 19:03:51 +08:00
Jiangjie.Bai
9c75147179 fix: 修改LDAP配置API权限 2022-03-10 19:03:51 +08:00
fit2bot
147e4cce94 perf: 修改 migrations (#7794)
* perf: 优化 auditor 权限

* perf: 修改 migrations

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-10 18:55:53 +08:00
fit2bot
d1e25e1fef fix: 删除应用/授权应用相关权限 (#7792)
* fix: 删除应用/授权应用相关权限

* fix: 删rbac清除code的一些迁移文件;增加到utils目录下

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-10 18:51:45 +08:00
fit2bot
af2ba07338 fix: exclude (#7791)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-10 18:11:33 +08:00
fit2bot
29b9adb684 perf: 修复一把数据 (#7789)
* fix: 优化 perm exclude

* perf: 修复一把数据

perf: 修复一些数据

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-10 17:40:31 +08:00
fit2bot
64e0860d24 fix: 修复app suggestion perm bug (#7788)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-10 17:24:24 +08:00
ibuler
9934007397 fix: 优化 perm exclude 2022-03-10 16:56:32 +08:00
Jiangjie.Bai
4044a71aea revert: 回滚权限树 2022-03-10 15:27:15 +08:00
fit2bot
9725f0c963 fix: 修复工单流500bug (#7784)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-10 11:26:34 +08:00
Jiangjie.Bai
b017e68a56 Perf: 优化RBAC权限树 (#7782)
* fix: 优化权限树(1)

* fix: 优化权限树(2)

* fix: 优化权限树(3)

* fix: 优化权限树(4)

* fix: 优化权限树(5)

* fix: 优化权限树(添加迁移文件)

* fix: 优化权限树(6)

* fix: 优化权限树(7)

* fix: 优化权限树(8)

* fix: 优化权限树(9)
2022-03-10 11:25:33 +08:00
fit2bot
9ca0eaf7ce fix: 修改校对资产数量permbug (#7777)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-09 15:01:44 +08:00
fit2bot
94e60e180e fix: 修复新建组织用户列表获取到是全局用户bug (#7776)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-09 11:17:28 +08:00
fit2bot
8ed221ea5a fix: org role perm (#7775)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-08 20:22:10 +08:00
fit2bot
42ebb1f82f fix: 删除组织角色时 判断全局绑定用户数量 (#7774)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-08 20:00:19 +08:00
ibuler
9492518773 perf: 修改 redis conn 2022-03-08 18:49:50 +08:00
feng626
1cca9c10fb perf: 修改perm判断逻辑 2022-03-08 18:41:50 +08:00
feng626
c4a6715eb8 fix: 命令存储 or 命令权限 2022-03-08 17:43:26 +08:00
ibuler
4c31b5ec0f perf: 去掉一些 perm 2022-03-08 17:33:06 +08:00
fit2bot
9fd7fa9339 fix: add auditor perm (#7768)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-08 15:53:02 +08:00
feng626
a930f3aab3 fix: 修复创建更新用户给定默认权限 2022-03-08 14:07:02 +08:00
ibuler
5081fb5fe7 perf: 优化perm tree, 并添加缓存 2022-03-08 13:46:25 +08:00
fit2bot
cb072123d6 fix: 修复org admin ticket flow perm (#7765)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-08 12:42:46 +08:00
fit2bot
761265dec5 fix: 修复index api perm (#7761)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-08 10:43:01 +08:00
fit2bot
89de111acc fix: 修复特殊用户过滤 (#7759)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-07 20:09:52 +08:00
ibuler
14327ee398 perf: 优化账号备份api path 2022-03-07 19:56:27 +08:00
fit2bot
1b007c8c5c perf: 修改权限树 (#7757)
* perf: 修改 rbac tree

* perf: 修改权限树

* perf:  修改用户默认权限

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-07 19:02:37 +08:00
fit2bot
3222687aaa fix: 修复应用授权权限bug (#7756)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-07 18:42:22 +08:00
feng626
79994f5ddc fix: 资产授权权限bug 2022-03-07 18:25:06 +08:00
fit2bot
8271492ec1 fix: 修复查看资产密钥bug (#7754)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-07 17:55:35 +08:00
fit2bot
27560793f8 fix: 修复系统用户任务权限bug (#7753)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-07 16:01:02 +08:00
Jiangjie.Bai
615929dd43 fix: 修复可以删除已关联用户角色的问题 2022-03-07 15:05:58 +08:00
feng626
a1c1b128e9 fix: 修复relation_systemuser_perm问题 2022-03-07 14:53:38 +08:00
ibuler
fa2c70c6be perf: 修改 perm node title 2022-03-07 14:39:07 +08:00
Jiangjie.Bai
46e119db1f fix: 后台不限制roles的必填项 2022-03-07 13:56:13 +08:00
ibuler
0afff45bae fix: validate app perm error 2022-03-07 11:42:39 +08:00
fit2bot
31d219524b fix: 修复创建用户roles必填问题 (#7745)
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-07 11:21:25 +08:00
fit2bot
a20884e2ad perf: 修改 rbac tree (#7743)
* perf: 修改 rbac tree

* perf: 修改verbose name

* fix: 修复系统用户

* fix: 还原 xpack

Co-authored-by: ibuler <ibuler@qq.com>
2022-03-07 11:19:03 +08:00
feng626
eb6bddc599 fix: 拆分角色权限树router 2022-03-04 18:13:59 +08:00
Jiangjie.Bai
8a8ed90eef fix: 保留之前的jms_oidc_rp包 2022-03-04 18:12:46 +08:00
Jiangjie.Bai
75825f5baa fix: 删除jms_oidc_rp包中的表jms_oidc_rp_oidcuser 2022-03-04 18:12:46 +08:00
fit2bot
0141fce27d fix: 审计员add工作台 (#7738)
Co-authored-by: feng626 <1304903146@qq.com>
2022-03-04 10:46:45 +08:00
老广
3f9f9351f3 Fix rbac (#7737)
* perf: 修改 rbac role bingding

* fix: suggestion perm

* perf: 修改 requirements

* perf: 修改 rbac

* fix: auditor_perms

Co-authored-by: feng626 <1304903146@qq.com>
2022-03-04 10:16:21 +08:00
ibuler
390b8693df perf: 修改 signal handler 2022-03-03 10:03:06 +08:00
fit2bot
dafc416783 Fix rbac (#7728)
* perf: 重命名 signal handlers

* fix: 修复 ticket processor 问题

* perf: 修改 ticket 处理人api

* fix: 修复创建系统账号bug

* fix: 升级celery_beat==2.2.1和flower==1.0.0;修改celery进程启动参数先后顺序

* perf: 修改 authentication token

* fix: 修复上传权限bug

* fix: 登录页面增加i18n切换;

* fix: 系统角色删除限制

* perf: 修改一下 permissions tree

* perf: 生成 i18n

* perf: 修改一点点

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2022-03-02 20:48:43 +08:00
halo
04e46e4b1c fix: 修复节点创建不能指定id问题,兼容api 2022-03-02 16:24:03 +08:00
fit2bot
ab1024fbf4 perf: 优化 super ticket 的状态 (#7714)
* fix: token 系统用户增加 protocol

* fix: 修复清除orphan session时同时清除对应的 session_task

* perf: 修改 connection token api

* fix: 修复无法获取系统角色绑定的问题

* perf: 增加 db terminal 及 magnus 组件

* perf: 修改 migrations

* fix: 修复AUTHENTICATION_BACKENDS相关的逻辑

* fix: 修改判断backend认证逻辑

* fix: 修复资产账号查看密码跳过mfa

* fix: 修复用户组授权权限错误

* feat: 支持COS对象存储

* feat: 升级依赖 jms_storage==0.0.42

* fix: 修复 koko api 问题

* feat: 修改存储翻译信息

* perf: 修改 ticket 权限

* fix: 修复获取资产授权系统用户 get_queryset

* perf: 抽取 ticket

* perf: 修改 cmd filter 的权限

* fix: 修改 ticket perm

* perf: 修改工单的api

Co-authored-by: Eric <xplzv@126.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: 小冯 <xiaofeng@xiaofengdeMacBook-Pro.local>
Co-authored-by: feng626 <1304903146@qq.com>
2022-02-28 20:22:14 +08:00
Jiangjie.Bai
03afa4f974 Fix rbac (#7713)
* fix: token 系统用户增加 protocol

* fix: 修复清除orphan session时同时清除对应的 session_task

* perf: 修改 connection token api

* fix: 修复无法获取系统角色绑定的问题

* perf: 增加 db terminal 及 magnus 组件

* perf: 修改 migrations

* fix: 修复AUTHENTICATION_BACKENDS相关的逻辑

* fix: 修改判断backend认证逻辑

* fix: 修复资产账号查看密码跳过mfa

* fix: 修复用户组授权权限错误

* feat: 支持COS对象存储

* feat: 升级依赖 jms_storage==0.0.42

* fix: 修复 koko api 问题

* feat: 修改存储翻译信息

* perf: 修改 ticket 权限

* fix: 修复获取资产授权系统用户 get_queryset

* perf: 抽取 ticket

* perf: 修改 cmd filter 的权限

* fix: 修改 ticket perm

* fix: 修复oidc依赖问题

Co-authored-by: Eric <xplzv@126.com>
Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: 小冯 <xiaofeng@xiaofengdeMacBook-Pro.local>
Co-authored-by: feng626 <1304903146@qq.com>
2022-02-28 19:28:58 +08:00
Jiangjie.Bai
edfca5eb24 Fix rbac (#7699)
* perf: 优化 suggesstion

* perf: 修改 migrations

* feat: 添加OIDC认证逻辑

* perf: 修改 backend

* perf: 优化认证backends

* perf: 优化认证backends

* perf: 优化CAS认证, 用户多域名进行访问时回调到各自域名

Co-authored-by: ibuler <ibuler@qq.com>
2022-02-25 19:23:59 +08:00
老广
02ca473492 Fix rbac (#7690)
* perf: 优化 suggesstion

* perf: 修改 migrations
2022-02-24 12:03:40 +08:00
老广
484b75bb53 Merge pull request #7687 from jumpserver/fix_rbac
fix: rbac
2022-02-24 10:06:47 +08:00
xinwen
9c3fd59ef4 fix: 删除用户最近登录的资产接口 2022-02-23 18:35:53 +08:00
feng626
bbf3250161 fix: 优化用户列表接口性能 2022-02-23 18:35:10 +08:00
feng626
8604b9019f fix: 添加迁移文件 2022-02-23 18:32:30 +08:00
Jiangjie.Bai
966b4250b8 Merge branch 'fix_rbac' of github.com:jumpserver/jumpserver into fix_rbac 2022-02-23 18:28:47 +08:00
Jiangjie.Bai
291f2b0e13 fix: 修复用户列表出现服务账号的问题 2022-02-23 18:28:23 +08:00
Eric
a1d15ef206 fix: 修复管理员终断会话API 2022-02-23 17:10:02 +08:00
ibuler
e76eec530f perf: 添加migrations 2022-02-23 17:06:07 +08:00
ibuler
add4d8d2cd fix: 修改上传录像 api 权限 2022-02-23 15:47:12 +08:00
ibuler
c6ece550a9 perf: 修改翻译 2022-02-22 19:53:00 +08:00
xinwen
e3b620089a feat: 个人页面接口 2022-02-22 18:29:49 +08:00
Jiangjie.Bai
64f721875b fix: 修复用户页面点击资产树节点提示对象找不到的问题 2022-02-22 18:29:49 +08:00
ibuler
02d3747c70 perf: 添加监测密码的脚本 2022-02-22 18:29:49 +08:00
feng626
09494193ab fix: 修复批量操作权限bug 2022-02-22 18:29:49 +08:00
feng626
702111f578 fix: 修复系统用户sudo tab权限 2022-02-22 18:29:49 +08:00
feng626
e08db7423f fix: 修复资产账号测试可连接性权限问题 2022-02-22 18:29:49 +08:00
ibuler
0c95faac04 fix: 修复 cache set 2022-02-22 18:29:49 +08:00
fit2bot
534cbf1281 perf: 修改依赖版本 (#7666)
* perf: 修复一些错误权限位

* Pr@fix rbac@fix rbac permissions (#7648)

* fix: 确保每次 migrate 执行更新 role permissions

* perf: 修改 choices

* feat: 兼容apple m1

* perf: 修改 migrations role permissions

* perf: pymysql 导入

* perf: admin 判断

* perf: 修改依赖版本

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2022-02-22 18:29:49 +08:00
fit2bot
abe5fa9036 perf: 修改依赖库版本 (#7661)
* perf: 修复一些错误权限位

* Pr@fix rbac@fix rbac permissions (#7648)

* fix: 确保每次 migrate 执行更新 role permissions

* perf: 修改 choices

* feat: 兼容apple m1

* perf: 修改 migrations role permissions

* perf: pymysql 导入

* perf: admin 判断

* perf: 修改依赖库版本

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2022-02-22 18:29:49 +08:00
feng626
2a2f05e51c fix: 删除组织关联删除工单 工单流 2022-02-22 18:29:49 +08:00
xinwen
f460916e84 fix: swagger 2022-02-22 18:29:49 +08:00
jiangweidong
ad2cb233d7 feat: 支持 MongoDB 数据库的纳管 (#7631) 2022-02-22 10:09:48 +08:00
halo
0dbf035146 perf: 优化代码 2022-02-21 16:52:21 +08:00
halo
ea124fd0db fix: 修复keycloak配置转换为openid不生效问题,持久化到数据库 2022-02-21 16:52:21 +08:00
Jiangjie.Bai
83ff8dbf26 fix: rbac 合并 (#7658)
* perf: 修复一些错误权限位

* Pr@fix rbac@fix rbac permissions (#7648)

* fix: 确保每次 migrate 执行更新 role permissions

* perf: 修改 choices

* feat: 兼容apple m1

* perf: 修改 migrations role permissions

* perf: pymysql 导入

* perf: admin 判断

* fix: 修复消息订阅权限

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
Co-authored-by: feng626 <1304903146@qq.com>
2022-02-21 16:24:03 +08:00
Jiangjie.Bai
783c163324 fix: 修复获取命令过滤规则时按照有组织的资源进行组织过滤 2022-02-19 11:44:03 +08:00
Jiangjie.Bai
3deced4ade fix: 修复登录系统用户temp_auth保存时构建的key和获取时不一致的问题 2022-02-18 20:07:19 +08:00
fit2bot
63de4e1806 perf: 添加 is_org_admin (#7644)
* fix: 修复 org members 的问题

* perf: 修改 org member

* perf: 修改 is sa

* perf: 修改 active

* perf: 修复写法

* perf: is_sa to is_service_account

Co-authored-by: ibuler <ibuler@qq.com>
2022-02-18 16:25:54 +08:00
ibuler
48d0c7b6cc fix: 修复 org members 的问题 2022-02-18 14:37:38 +08:00
ibuler
20cc8a124f perf: 修改connection token domain
perf: 添加 org_id
2022-02-18 14:10:22 +08:00
ibuler
db050e405d fix: 修改 migrations 2022-02-17 23:02:32 +08:00
fit2bot
e259d2a9e9 fix: fix rbac to dev (#7636)
* feat: 添加 RBAC 应用模块

* feat: 添加 RBAC Model、API

* feat: 添加 RBAC Model、API 2

* feat: 添加 RBAC Model、API 3

* feat: 添加 RBAC Model、API 4

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC 整理权限位

* feat: RBAC 整理权限位2

* feat: RBAC 整理权限位2

* feat: RBAC 整理权限位

* feat: RBAC 添加默认角色

* feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定

* feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定

* feat: RBAC 修改用户模块API

* feat: RBAC 添加组织模块迁移文件 & 修改组织模块API

* feat: RBAC 添加组织模块迁移文件 & 修改组织模块API

* feat: RBAC 修改用户角色属性的使用

* feat: RBAC No.1

* xxx

* perf: 暂存

* perf: ...

* perf(rbac): 添加 perms 到 profile serializer 中

* stash

* perf: 使用init

* perf: 修改migrations

* perf: rbac

* stash

* stash

* pref: 修改rbac

* stash it

* stash: 先去修复其他bug

* perf: 修改 role 添加 users

* pref: 修改 RBAC Model

* feat: 添加权限的 tree api

* stash: 暂存一下

* stash: 暂存一下

* perf: 修改 model verbose name

* feat: 添加model各种 verbose name

* perf: 生成 migrations

* perf: 优化权限位

* perf: 添加迁移脚本

* feat: 添加组织角色迁移

* perf: 添加迁移脚本

* stash

* perf: 添加migrateion

* perf: 暂存一下

* perf: 修改rbac

* perf: stash it

* fix: 迁移冲突

* fix: 迁移冲突

* perf: 暂存一下

* perf: 修改 rbac 逻辑

* stash: 暂存一下

* perf: 修改内置角色

* perf: 解决 root 组织的问题

* perf: stash it

* perf: 优化 rbac

* perf: 优化 rolebinding 处理

* perf: 完成用户离开组织的问题

* perf: 暂存一下

* perf: 修改翻译

* perf: 去掉了 IsSuperUser

* perf: IsAppUser 去掉完成

* perf: 修改 connection token 的权限

* perf: 去掉导入的问题

* perf: perms define 格式,修改 app 用户 的全新啊

* perf: 修改 permission

* perf: 去掉一些 org admin

* perf: 去掉部分 org admin

* perf: 再去掉点 org admin role

* perf: 再去掉部分 org admin

* perf: user 角色搜索

* perf: 去掉很多 js

* perf: 添加权限位

* perf: 修改权限

* perf: 去掉一个 todo

* merge: with dev

* fix: 修复冲突

Co-authored-by: Bai <bugatti_it@163.com>
Co-authored-by: Michael Bai <baijiangjie@gmail.com>
Co-authored-by: ibuler <ibuler@qq.com>
2022-02-17 20:13:31 +08:00
Jiangjie.Bai
18c388f3a5 Merge pull request #7629 from jumpserver/dev
v2.19.0-rc3
2022-02-17 11:04:33 +08:00
feng626
b088362ae3 perf: 账号备份性能优化 2022-02-17 11:02:49 +08:00
xinwen
c7f8ebb613 fix: 站内信 ServerPerformanceCheck redis 订阅报错 2022-02-17 10:53:36 +08:00
feng626
de2f9ae687 Merge pull request #7626 from jumpserver/pr@dev@fix_i18n
fix: 翻译
2022-02-16 19:19:55 +08:00
xinwen
a806a2d3e6 fix: 翻译 2022-02-16 19:16:45 +08:00
Jiangjie.Bai
7be76feeb0 Merge pull request #7622 from jumpserver/dev
v2.19.0-rc3
2022-02-16 16:42:19 +08:00
ibuler
f548abcb87 perf: 用户临时密码支持加密传输 2022-02-16 16:31:57 +08:00
xinwen
35c6b581e2 feat: 远程应用支持磁盘挂载 2022-02-16 16:29:06 +08:00
feng626
40b119786b Merge pull request #7621 from jumpserver/pr@dev@ticket_flow
fix: 工单流需要超级管理员配置
2022-02-16 14:47:56 +08:00
feng626
ff9347e344 fix: 工单流需要超级管理员配置 2022-02-16 14:37:26 +08:00
feng626
da0bbcee57 fix: 修复资产账号过滤重复问题 2022-02-16 11:35:53 +08:00
ibuler
824d10ce93 pref: connection token 添加 secret 2022-02-16 11:33:25 +08:00
feng626
1d6dcf9fb5 fix: 修复命令报警翻译 2022-02-15 18:33:52 +08:00
Jiangjie.Bai
3325867f8f fix: 修改LDAP SYNC相关参数设置 2022-02-15 17:57:56 +08:00
xinwen
2f43aeee5d fix: Authbook 修改秘钥不生效 2022-02-15 11:14:51 +08:00
Jiangjie.Bai
764d70ea4f fix: 修改用户组名称翻译 2022-02-14 19:27:41 +08:00
Jiangjie.Bai
ff6dbe67a6 Merge pull request #7610 from jumpserver/dev
v2.19.0-rc2
2022-02-14 18:31:52 +08:00
feng626
7f36958683 perf: 工单流只需组织管理员权限 2022-02-14 18:15:11 +08:00
xinwen
fa5921cd86 fix: 去掉 can_replay 兼容代码 2022-02-14 18:14:37 +08:00
feng626
540679df11 fix: 修复operatelog add remove 翻译问题 2022-02-14 14:04:59 +08:00
fit2bot
597b022905 fix: 修复历史会话文件不能被清理的bug (#7604)
* fix: 修复历史会话文件不能被清理的bug

* perf: 删除调试日志

Co-authored-by: halo <wuyihuangw@gmail.com>
2022-02-14 10:58:30 +08:00
jiangweidong
ab34b9906e perf: 切割一次就可以了 2022-02-10 18:34:09 +08:00
jiangweidong
755fa8efa8 perf: 兼容不同版本间JumpServer获取的SAML2协议用户属性 2022-02-10 18:34:09 +08:00
feng626
889713f00e fix: 修复工单流程设置显示其它组织问题 2022-02-10 14:07:32 +08:00
Jiangjie.Bai
c10436de47 Merge pull request #7589 from jumpserver/dev
v2.19.0-rc1
2022-02-10 11:24:28 +08:00
xinwen
333bb64b8b fix: Authbook 取认证信息bug 2022-02-10 11:20:40 +08:00
Michael Bai
67a49dc5e9 feat: syslog 记录的数据来源于序列类 2022-02-08 19:22:08 +08:00
Michael Bai
8085db7acc feat: 增加系统设置(安全)控制第三方认证用户是否进行MFA认证 2022-02-08 17:48:40 +08:00
Michael Bai
6adeafd1d2 fix: 修复创建命令过滤规则失败的问题 2022-02-08 14:21:43 +08:00
xinwen
1faba95a48 fix: 修复 xrdp 连接资产时会生成用户登录日志 2022-02-08 12:38:27 +08:00
吴小白
ab07091eb8 perf: 优化 quick_start.sh 2022-02-08 12:36:43 +08:00
xinwen
f994f5d776 fix: redis 订阅 bug 2022-02-08 12:35:16 +08:00
ibuler
55fae1667d fix: 修复 redis 连接过多的问题 2022-02-08 12:35:16 +08:00
Michael Bai
b3397c6aeb feat: 命令过滤规则增加忽略大小写选项 2022-02-08 12:31:15 +08:00
老广
8ab4f6f004 docs: 添加 pr 提示说明 2022-02-07 15:39:50 +08:00
老广
5a37540eda docs: 修改贡献提示 2022-02-07 15:39:50 +08:00
老广
1c1d507e34 Create CONTRIBUTING.md 2022-02-07 15:39:50 +08:00
老广
9c40314edc Create CODE_OF_CONDUCT.md 2022-02-07 15:39:22 +08:00
feng626
acdde5a236 fix: 密钥密码翻译 2022-01-27 18:19:44 +08:00
Jiangjie.Bai
37a3566b0e Merge pull request #7540 from jumpserver/dev
v2.18
2022-01-20 13:47:13 +08:00
feng626
a81f6bf97b fix: 应用账号attrs字段问题 2022-01-20 13:20:45 +08:00
Michael Bai
b07f532a71 fix: 修复导出应用账号会包含attrs字段的问题 2022-01-20 12:00:18 +08:00
feng626
46270fd91c fix: authbook load asset queryset bug 2022-01-20 11:03:18 +08:00
Jiangjie.Bai
2b364c1476 Merge pull request #7534 from jumpserver/dev
v2.18.0-rc4
2022-01-19 19:36:59 +08:00
feng626
9e47bf6ec5 fix: 批量命令bug 2022-01-19 19:34:32 +08:00
ibuler
89c7e514eb perf: 修复登录和推送系统用户是密码选择 2022-01-19 19:08:38 +08:00
feng626
83c5344307 fix: 获取命令规则过滤不准 2022-01-19 19:07:43 +08:00
feng626
2beec1a03b Merge pull request #7530 from jumpserver/pr@dev@ssh_passphrase_bug
fix: 修改 修改ssh密钥时报500问题
2022-01-19 11:11:49 +08:00
feng626
d53925d1fb Merge pull request #7529 from jumpserver/pr@dev@ticket_bug
fix: 修复工单审批后审批人与记录人不一致问题
2022-01-19 11:11:34 +08:00
feng626
69d4a0a250 fix: 修改 修改ssh密钥时报500问题 2022-01-19 10:39:17 +08:00
feng626
e33a67daf6 fix: 修复工单审批后审批人与记录人不一致问题 2022-01-19 10:33:41 +08:00
Jiangjie.Bai
2036037675 Merge pull request #7527 from jumpserver/dev
v2.18.0-rc3
2022-01-18 19:35:37 +08:00
fit2bot
0b16d70c0c fix: 修改翻译 (#7526)
* fix: 修改翻译

* fix: 修改保存数据库翻译

* fix: 修复翻译为none问题

Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-01-18 19:31:01 +08:00
Michael Bai
8d6498c8f0 fix: 升级依赖 jms-storage==0.0.41 2022-01-18 17:01:02 +08:00
Michael Bai
59d0f53725 fix: 修改审计日志操作日志动作翻译问题 2022-01-18 15:21:35 +08:00
Michael Bai
518caa4f7a fix: 修改应用账号APP名称的VerboseName 2022-01-18 15:03:23 +08:00
Michael Bai
b8aaa7d249 fix: 修改命令过滤器翻译 2022-01-18 14:27:22 +08:00
Michael Bai
c9f63a3f4a fix: 修改审计日志中的i18n翻译问题 2022-01-18 13:21:31 +08:00
xinwen
ec68fc9562 fix: 绑定三方登录消息翻译 2022-01-18 10:39:35 +08:00
xinwen
561ce53413 fix: 空的 es 存储,指定 timestamp 排序报错 2022-01-18 10:14:51 +08:00
Jiangjie.Bai
6bd597eadd Merge pull request #7511 from jumpserver/dev
v2.18.0-rc2
2022-01-17 19:21:39 +08:00
feng626
005032ea00 fix: 修复账号备份存在外键search 500 bug 2022-01-17 19:21:13 +08:00
Michael Bai
de74477fd6 fix: 修改翻译文件 2022-01-17 19:11:01 +08:00
fit2bot
c43ad981bd perf: 优化写法 (#7498)
* fix: 修复登录页输入 mfa 时不支持 某 mfa 的错误提示

fix tapd 1145454465001008371

* perf: 优化 send code api,避免暴力常识

* perf: 优化写法

* Update mfa.py

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
2022-01-17 19:05:01 +08:00
xinwen
088676d998 fix: 计算工单序号的时候切换到root组织 2022-01-17 16:22:04 +08:00
jiangweidong
106bb9b63d perf: SAML2属性映射去掉域名前缀 2022-01-17 16:06:37 +08:00
ibuler
451e61bb61 perf: 优化 celery 任务中的数据库连接问题
perf: add comment
2022-01-17 16:04:50 +08:00
halo
e1de210585 feat: 创建用户邮件内容支持html标签 2022-01-17 16:03:04 +08:00
xinwen
cce34f4939 fix: 用户登录失败时,登陆日志中的认证方式不准确 2022-01-17 16:02:28 +08:00
fit2bot
def9bedd30 perf: 账号备份优化 (#7503)
* perf: 账号备份优化

* feat: 优化账号备份获取有序备份字段列表

Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Michael Bai <baijiangjie@gmail.com>
2022-01-17 15:58:39 +08:00
feng626
d2b3edffca Merge pull request #7505 from jumpserver/pr@dev@ticket_bug
fix: 修复工单审批后state不准问题
2022-01-17 15:51:49 +08:00
Michael Bai
aea2db84e8 fix: 添加deb依赖: libaio-dev freetds-dev 2022-01-17 13:57:08 +08:00
feng626
6a4ffcc9ad fix: 修复工单审批后state不准问题 2022-01-16 17:49:17 +08:00
fit2bot
4343b6487d fix: 修复登录页输入 mfa 时不支持 某 mfa 的错误提示 (#7495)
* fix: 修复登录页输入 mfa 时不支持 某 mfa 的错误提示

fix tapd 1145454465001008371

* perf: 优化 send code api,避免暴力常识

Co-authored-by: ibuler <ibuler@qq.com>
2022-01-13 19:01:12 +08:00
Michael Bai
ba695c4600 fix: 抽象permissions-serializer的actions字段值设置 2022-01-13 18:58:10 +08:00
Michael Bai
21a34ddc03 feat: 返回License信息 2022-01-13 18:55:44 +08:00
ibuler
145c7952c9 perf: 优化 ops 任务,支持 i18n
- 内置任务名称支持了 i18n,数据库存英文,返回是翻译一下
- 任务执行中,添加 language 上下文
2022-01-13 15:03:48 +08:00
xinwen
43c4c78378 fix: 工单序号添加排序 2022-01-13 14:37:02 +08:00
Michael Bai
620834c817 fix: 修改工单迁移文件warining问题
Any fields that are stored with VARCHAR column types may have their max_length restricted to 255 characters if you are using unique=True for the field.
See: https://docs.djangoproject.com/en/3.1/ref/databases/#mysql-character-fields
2022-01-13 14:36:30 +08:00
Jiangjie.Bai
fbd0b44d4f Merge pull request #7490 from jumpserver/dev
v2.18.0-rc1
2022-01-12 20:58:04 +08:00
xinwen
4b44a54d97 fix: 腾讯验证码测试不通过 2022-01-12 20:57:22 +08:00
Eric
6716acb4ff perf: 更新安全英文版本 2022-01-12 20:56:58 +08:00
Jiangjie.Bai
35722a8466 Merge pull request #7487 from jumpserver/dev
v2.18.0-rc1
2022-01-12 20:56:33 +08:00
feng626
e5659a1d07 fix: 修复翻译及迁移文件 2022-01-12 20:55:58 +08:00
xinwen
414137cd73 fix: 三方认证绑定发通知文案 2022-01-12 20:34:19 +08:00
xinwen
1e3a15a3d0 feat: 三方认证绑定发通知 2022-01-12 20:23:25 +08:00
ibuler
dde7559f47 feat: node api 支持使用 full_value 递归创建 2022-01-12 20:23:16 +08:00
feng626
b0932e5137 feat: 逃生通道 2022-01-12 20:22:36 +08:00
xinwen
5cdc4c3c28 feat: 工单添加序号 2022-01-12 10:38:07 +08:00
ibuler
25a2290804 perf: 优化迁移数据库错误提示 2022-01-10 17:32:01 +08:00
Michael Bai
4405abbedf feat: 2022 2022-01-04 12:54:12 +08:00
halo
3f6c9d519e fix: 修复通知用户过期时间bug 2021-12-31 17:26:23 +08:00
xinwen
bd84edea62 feat: 资产登录工单页面增加监控与中断 2021-12-31 17:23:39 +08:00
feng626
100bfe0304 feat: 新增获取k8s pod namespace container接口 2021-12-31 15:54:59 +08:00
feng626
11fd9a6567 feat: 配置私钥密码 2021-12-31 14:47:01 +08:00
Michael Bai
90299a508c fix: 授权actions serializer 设置default值 2021-12-30 17:25:00 +08:00
Michael Bai
de9516dee5 feat: 应用授权增加Action动作控制 2021-12-30 14:47:58 +08:00
jiangweidong
db01a6d48d fix: 因为admin_user表中有历史数据,导致组织无法删除 2021-12-30 10:48:07 +08:00
halo
c2d271f00b feat: rdp协议新增username表达式,匹配更多特殊字符 2021-12-30 10:01:26 +08:00
halo
90de229631 feat: rdp协议username支持输入中文和$ 2021-12-30 10:01:26 +08:00
xinwen
35a10fdd62 feat: 命令记录接口增加 remote_addr 字段 2021-12-28 18:26:21 +08:00
xinwen
89cad224c5 fix: SSO 限制用户删除 2021-12-28 18:24:32 +08:00
jiangweidong
9b2e1e08d8 feat: 将redis_acl协议合并到redis协议中 2021-12-28 18:15:00 +08:00
jiangweidong
aaaa87dd60 feat: 增加数据库redis的纳管功能 2021-12-28 18:15:00 +08:00
吴小白
24cad76232 perf: 更新基础镜像 2021-12-28 18:12:10 +08:00
吴小白
040f1fcbb8 perf: 添加 quick_start.sh 2021-12-27 12:32:58 +08:00
Eric
d76ef788d2 perf: 修改 Jmservisor 的下载地址名称 2021-12-24 11:35:47 +08:00
feng626
38c870a0b5 perf: 工单流对应受理人懒加载 2021-12-21 18:20:02 +08:00
Michael Bai
ac9e7e9b40 fix: 下载页面添加Jmservisor拉起脚本 2021-12-17 15:54:32 +08:00
Michael Bai
5aed04d58b fix: 修改SAML2.0 Logo 2021-12-17 15:25:15 +08:00
jiangweidong
c05951b6c4 feat: 添加数据库SQLServer改密功能的python依赖库 2021-12-17 11:20:09 +08:00
ibuler
107215aadc workflow: 添加同步 gitee 的 workflow 2021-12-17 11:19:47 +08:00
658 changed files with 21969 additions and 68469 deletions

1
.gitattributes vendored
View File

@@ -1,2 +1,3 @@
*.mmdb filter=lfs diff=lfs merge=lfs -text
*.mo filter=lfs diff=lfs merge=lfs -text
*.ipdb filter=lfs diff=lfs merge=lfs -text

9
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,9 @@
#### What this PR does / why we need it?
#### Summary of your change
#### Please indicate you've done the following:
- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.

View File

@@ -20,6 +20,8 @@ jobs:
run: |
TAG=$(basename ${GITHUB_REF})
VERSION=${TAG/v/}
wget https://raw.githubusercontent.com/jumpserver/installer/master/quick_start.sh
sed -i "s@Version=.*@Version=v${VERSION}@g" quick_start.sh
echo "::set-output name=TAG::$TAG"
echo "::set-output name=VERSION::$VERSION"
- name: Create Release
@@ -31,6 +33,16 @@ jobs:
config-name: release-config.yml
version: ${{ steps.get_version.outputs.TAG }}
tag: ${{ steps.get_version.outputs.TAG }}
- name: Upload Quick Start Script
id: upload-release-quick-start-shell
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./quick_start.sh
asset_name: quick_start.sh
asset_content_type: application/text
build-and-release:
needs: create-realese
@@ -43,4 +55,4 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-realese.outputs.upload_url }}
upload_url: ${{ needs.create-realese.outputs.upload_url }}

23
.github/workflows/sync-gitee.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: 🔀 Sync mirror to Gitee
on:
push:
branches:
- master
- dev
create:
jobs:
mirror:
runs-on: ubuntu-latest
if: github.repository == 'jumpserver/jumpserver'
steps:
- name: mirror
continue-on-error: true
if: github.event_name == 'push' || (github.event_name == 'create' && github.event.ref_type == 'tag')
uses: wearerequired/git-mirror-action@v1
env:
SSH_PRIVATE_KEY: ${{ secrets.GITEE_SSH_PRIVATE_KEY }}
with:
source-repo: 'git@github.com:jumpserver/jumpserver.git'
destination-repo: 'git@gitee.com:jumpserver/jumpserver.git'

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

25
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,25 @@
# Contributing
## Create pull request
PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
This [development guideline](https://docs.jumpserver.org/zh/master/dev/rest_api/) contains information about repository structure, how to setup development environment, how to run it, and more.
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
## Report issues
It is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information.
Before opening any issue, please look up the existing issues to avoid submitting a duplication.
If you find a match, you can "subscribe" to it to get notified on updates. If you have additional helpful information about the issue, please leave a comment.
When reporting issues, always include:
* Which version you are using.
* Steps to reproduce the issue.
* Snapshots or log files if needed
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
replace those parts with "REDACTED" or other strings like "****".

View File

@@ -1,55 +1,91 @@
# 编译代码
FROM python:3.8.6-slim as stage-build
FROM python:3.8-slim
MAINTAINER JumpServer Team <ibuler@qq.com>
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN cd utils && bash -ixeu build.sh
ARG BUILD_DEPENDENCIES=" \
g++ \
make \
pkg-config"
# 构建运行时环境
FROM python:3.8.6-slim
ARG PIP_MIRROR=https://pypi.douban.com/simple
ENV PIP_MIRROR=$PIP_MIRROR
ARG PIP_JMS_MIRROR=https://pypi.douban.com/simple
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
ARG DEPENDENCIES=" \
default-libmysqlclient-dev \
freetds-dev \
libpq-dev \
libffi-dev \
libldap2-dev \
libsasl2-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
libaio-dev \
sshpass"
WORKDIR /opt/jumpserver
ARG TOOLS=" \
curl \
default-mysql-client \
iproute2 \
iputils-ping \
locales \
procps \
redis-tools \
telnet \
vim \
unzip \
wget"
COPY ./requirements/deb_requirements.txt ./requirements/deb_requirements.txt
RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \
&& sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list \
&& apt update \
&& apt -y install telnet iproute2 redis-tools default-mysql-client vim wget curl locales procps \
&& apt -y install $(cat requirements/deb_requirements.txt) \
&& rm -rf /var/lib/apt/lists/* \
&& apt update && sleep 1 && apt update \
&& apt -y install ${BUILD_DEPENDENCIES} \
&& apt -y install ${DEPENDENCIES} \
&& apt -y install ${TOOLS} \
&& localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& sed -i "s@# alias l@alias l@g" ~/.bashrc \
&& echo "set mouse-=a" > ~/.vimrc
&& echo "set mouse-=a" > ~/.vimrc \
&& rm -rf /var/lib/apt/lists/* \
&& mv /bin/sh /bin/sh.bak \
&& ln -s /bin/bash /bin/sh
COPY ./requirements/requirements.txt ./requirements/requirements.txt
ARG TARGETARCH
ARG ORACLE_LIB_MAJOR=19
ARG ORACLE_LIB_MINOR=10
ENV ORACLE_FILE="instantclient-basiclite-linux.${TARGETARCH:-amd64}-${ORACLE_LIB_MAJOR}.${ORACLE_LIB_MINOR}.0.0.0dbru.zip"
RUN mkdir -p /opt/oracle/ \
&& cd /opt/oracle/ \
&& wget https://download.jumpserver.org/files/oracle/${ORACLE_FILE} \
&& unzip instantclient-basiclite-linux.${TARGETARCH-amd64}-19.10.0.0.0dbru.zip \
&& mv instantclient_${ORACLE_LIB_MAJOR}_${ORACLE_LIB_MINOR} instantclient \
&& echo "/opt/oracle/instantclient" > /etc/ld.so.conf.d/oracle-instantclient.conf \
&& ldconfig \
&& rm -f ${ORACLE_FILE}
WORKDIR /tmp/build
COPY ./requirements ./requirements
ARG PIP_MIRROR=https://mirrors.aliyun.com/pypi/simple/
ENV PIP_MIRROR=$PIP_MIRROR
ARG PIP_JMS_MIRROR=https://mirrors.aliyun.com/pypi/simple/
ENV PIP_JMS_MIRROR=$PIP_JMS_MIRROR
# 因为以 jms 或者 jumpserver 开头的 mirror 上可能没有
RUN pip install --upgrade pip==20.2.4 setuptools==49.6.0 wheel==0.34.2 -i ${PIP_MIRROR} \
&& pip install --no-cache-dir $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install --no-cache-dir -r requirements/requirements.txt -i ${PIP_MIRROR} \
&& rm -rf ~/.cache/pip
COPY --from=stage-build /opt/jumpserver/release/jumpserver /opt/jumpserver
RUN mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& mv /bin/sh /bin/sh.bak \
&& ln -s /bin/bash /bin/sh
ARG VERSION
ENV VERSION=$VERSION
RUN mkdir -p /opt/jumpserver/oracle/ \
&& wget https://download.jumpserver.org/public/instantclient-basiclite-linux.x64-21.1.0.0.0.tar > /dev/null \
&& tar xf instantclient-basiclite-linux.x64-21.1.0.0.0.tar -C /opt/jumpserver/oracle/ \
&& echo "/opt/jumpserver/oracle/instantclient_21_1" > /etc/ld.so.conf.d/oracle-instantclient.conf \
&& ldconfig \
&& rm -f instantclient-basiclite-linux.x64-21.1.0.0.0.tar
RUN echo > config.yml
ADD . .
RUN cd utils \
&& bash -ixeu build.sh \
&& mv ../release/jumpserver /opt/jumpserver \
&& rm -rf /tmp/build \
&& echo > /opt/jumpserver/config.yml
WORKDIR /opt/jumpserver
VOLUME /opt/jumpserver/data
VOLUME /opt/jumpserver/logs

View File

@@ -1,10 +1,13 @@
<p align="center"><a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a></p>
<p align="center">
<a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
</p>
<h3 align="center">多云环境下更好用的堡垒机</h3>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://shields.io/github/downloads/jumpserver/jumpserver/total"><img src="https://shields.io/github/downloads/jumpserver/jumpserver/total" alt=" release"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Codacy"></a>
<a href="https://github.com/jumpserver/jumpserver/commits"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jumpserver/jumpserver.svg" /></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
@@ -15,7 +18,7 @@
JumpServer 是全球首款开源的堡垒机,使用 GPLv3 开源协议,是符合 4A 规范的运维安全审计系统。
JumpServer 使用 Python 开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 方案,交互界面美观、用户体验好。
JumpServer 使用 Python 开发,配备了业界领先的 Web Terminal 方案,交互界面美观、用户体验好。
JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向扩展,无资产数量及并发限制。
@@ -28,9 +31,9 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
- 开源: 零门槛,线上快速获取和安装;
- 分布式: 轻松支持大规模并发访问;
- 无插件: 仅需浏览器,极致的 Web Terminal 使用体验;
- 多租户: 一套系统,多个子公司或部门同时使用;
- 多云支持: 一套系统,同时管理不同云上面的资产;
- 云端存储: 审计录像云端存储,永不丢失;
- 多租户: 一套系统,多个子公司和部门同时使用;
- 多应用支持: 数据库Windows远程应用Kubernetes。
### UI 展示
@@ -55,12 +58,15 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
- [手动安装](https://github.com/jumpserver/installer)
### 组件项目
- [Lina](https://github.com/jumpserver/lina) JumpServer Web UI 项目
- [Luna](https://github.com/jumpserver/luna) JumpServer Web Terminal 项目
- [KoKo](https://github.com/jumpserver/koko) JumpServer 字符协议 Connector 项目,替代原来 Python 版本的 [Coco](https://github.com/jumpserver/coco)
- [Lion](https://github.com/jumpserver/lion-release) JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/)
- [Clients](https://github.com/jumpserver/clients) JumpServer 客户端 项目
- [Installer](https://github.com/jumpserver/installer) JumpServer 安装包 项目
| 项目 | 状态 | 描述 |
| --------------------------------------------------------------------------- | ------------------- | ---------------------------------------- |
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI 项目 |
| [Luna](https://github.com/jumpserver/luna) | <a href="https://github.com/jumpserver/luna/releases"><img alt="Luna release" src="https://img.shields.io/github/release/jumpserver/luna.svg" /></a> | JumpServer Web Terminal 项目 |
| [KoKo](https://github.com/jumpserver/koko) | <a href="https://github.com/jumpserver/koko/releases"><img alt="Koko release" src="https://img.shields.io/github/release/jumpserver/koko.svg" /></a> | JumpServer 字符协议 Connector 项目,替代原来 Python 版本的 [Coco](https://github.com/jumpserver/coco) |
| [Lion](https://github.com/jumpserver/lion-release) | <a href="https://github.com/jumpserver/lion-release/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion-release.svg" /></a> | JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/) |
| [Magnus](https://github.com/jumpserver/magnus-release) | <a href="https://github.com/jumpserver/magnus-release/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/magnus-release.svg" /> | JumpServer 数据库代理 Connector 项目 |
| [Clients](https://github.com/jumpserver/clients) | <a href="https://github.com/jumpserver/clients/releases"><img alt="Clients release" src="https://img.shields.io/github/release/jumpserver/clients.svg" /> | JumpServer 客户端 项目 |
| [Installer](https://github.com/jumpserver/installer)| <a href="https://github.com/jumpserver/installer/releases"><img alt="Installer release" src="https://img.shields.io/github/release/jumpserver/installer.svg" /> | JumpServer 安装包 项目 |
### 社区
@@ -75,27 +81,13 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
感谢以下贡献者,让 JumpServer 更加完善
<a href="https://github.com/jumpserver/jumpserver/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/jumpserver" />
</a>
<a href="https://github.com/jumpserver/koko/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/koko" />
</a>
<a href="https://github.com/jumpserver/lina/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/lina" />
</a>
<a href="https://github.com/jumpserver/luna/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/luna" />
</a>
<a href="https://github.com/jumpserver/jumpserver/graphs/contributors"><img src="https://opencollective.com/jumpserver/contributors.svg?width=890&button=false" /></a>
### 致谢
- [Apache Guacamole](https://guacamole.apache.org/) Web页面连接 RDP, SSH, VNC协议设备JumpServer 图形化组件 Lion 依赖
- [OmniDB](https://omnidb.org/) Web页面连接使用数据库JumpServer Web数据库依赖
- [Apache Guacamole](https://guacamole.apache.org/) Web页面连接 RDP, SSH, VNC 协议设备JumpServer 图形化组件 Lion 依赖
- [OmniDB](https://omnidb.org/) Web 页面连接使用数据库JumpServer Web 数据库依赖
### JumpServer 企业版
@@ -103,14 +95,14 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
### 案例研究
- [JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [携程 JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [小红书的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)
- [JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [携程 JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [小红书的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)
### 安全说明
@@ -124,11 +116,10 @@ JumpServer是一款安全产品请参考 [基本安全建议](https://docs.ju
### License & Copyright
Copyright (c) 2014-2021 飞致云 FIT2CLOUD, All rights reserved.
Copyright (c) 2014-2022 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View File

@@ -85,7 +85,7 @@ If you find a security problem, please contact us directly
- 400-052-0755
### License & Copyright
Copyright (c) 2014-2021 FIT2CLOUD Tech, Inc., All rights reserved.
Copyright (c) 2014-2022 FIT2CLOUD Tech, Inc., All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

View File

@@ -7,3 +7,14 @@ JumpServer 是一款正在成长的安全产品, 请参考 [基本安全建议
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
# Security Policy
JumpServer is a security product, The installation and development should follow our security tips.
## Reporting a Vulnerability
All security bugs should be reported to the contact as below:
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755

View File

@@ -1,20 +1,14 @@
from common.permissions import IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember
from common.drf.api import JMSBulkModelViewSet
from ..models import LoginACL
from .. import serializers
from ..filters import LoginAclFilter
__all__ = ['LoginACLViewSet', ]
__all__ = ['LoginACLViewSet']
class LoginACLViewSet(JMSBulkModelViewSet):
queryset = LoginACL.objects.all()
filterset_class = LoginAclFilter
search_fields = ('name',)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.LoginACLSerializer
def get_permissions(self):
if self.action in ["retrieve", "list"]:
self.permission_classes = (IsOrgAdmin, HasQueryParamsUserAndIsCurrentOrgMember)
return super().get_permissions()

View File

@@ -1,5 +1,4 @@
from orgs.mixins.api import OrgBulkModelViewSet
from common.permissions import IsOrgAdmin
from .. import models, serializers
@@ -10,5 +9,4 @@ class LoginAssetACLViewSet(OrgBulkModelViewSet):
model = models.LoginAssetACL
filterset_fields = ('name', )
search_fields = filterset_fields
permission_classes = (IsOrgAdmin, )
serializer_class = serializers.LoginAssetACLSerializer

View File

@@ -1,19 +1,23 @@
from rest_framework.response import Response
from rest_framework.generics import CreateAPIView
from common.permissions import IsAppUser
from common.utils import reverse, lazyproperty
from orgs.utils import tmp_to_org
from tickets.api import GenericTicketStatusRetrieveCloseAPI
from ..models import LoginAssetACL
from .. import serializers
__all__ = ['LoginAssetCheckAPI', 'LoginAssetConfirmStatusAPI']
__all__ = ['LoginAssetCheckAPI']
class LoginAssetCheckAPI(CreateAPIView):
permission_classes = (IsAppUser,)
serializer_class = serializers.LoginAssetCheckSerializer
model = LoginAssetACL
rbac_perms = {
'POST': 'tickets.add_superticket'
}
def get_queryset(self):
return LoginAssetACL.objects.all()
def create(self, request, *args, **kwargs):
is_need_confirm, response_data = self.check_if_need_confirm()
@@ -46,7 +50,7 @@ class LoginAssetCheckAPI(CreateAPIView):
org_id=self.serializer.org.id
)
confirm_status_url = reverse(
view_name='api-acls:login-asset-confirm-status',
view_name='api-tickets:super-ticket-status',
kwargs={'pk': str(ticket.id)}
)
ticket_detail_url = reverse(
@@ -60,7 +64,8 @@ class LoginAssetCheckAPI(CreateAPIView):
'check_confirm_status': {'method': 'GET', 'url': confirm_status_url},
'close_confirm': {'method': 'DELETE', 'url': confirm_status_url},
'ticket_detail_url': ticket_detail_url,
'reviewers': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees]
'reviewers': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees],
'ticket_id': str(ticket.id)
}
return data
@@ -70,6 +75,3 @@ class LoginAssetCheckAPI(CreateAPIView):
serializer.is_valid(raise_exception=True)
return serializer
class LoginAssetConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
pass

View File

@@ -1,5 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class AclsConfig(AppConfig):
name = 'acls'
verbose_name = _('Acls')

View File

@@ -0,0 +1,21 @@
# Generated by Django 3.1.13 on 2021-11-30 02:37
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('acls', '0002_auto_20210926_1047'),
]
operations = [
migrations.AlterModelOptions(
name='loginacl',
options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login acl'},
),
migrations.AlterModelOptions(
name='loginassetacl',
options={'ordering': ('priority', '-date_updated', 'name'), 'verbose_name': 'Login asset acl'},
),
]

View File

@@ -123,6 +123,8 @@ class LoginACL(BaseACL):
'org_id': Organization.ROOT_ID,
}
ticket = Ticket.objects.create(**data)
ticket.create_process_map_and_node(self.reviewers.all())
ticket.open(self.user)
applicant = self.user
assignees = self.reviewers.all()
ticket.create_process_map_and_node(assignees, applicant)
ticket.open(applicant)
return ticket

View File

@@ -97,7 +97,7 @@ class LoginAssetACL(BaseACL, OrgModelMixin):
'org_id': org_id,
}
ticket = Ticket.objects.create(**data)
ticket.create_process_map_and_node(assignees)
ticket.create_process_map_and_node(assignees, user)
ticket.open(applicant=user)
return ticket

View File

@@ -12,7 +12,6 @@ router.register(r'login-asset-acls', api.LoginAssetACLViewSet, 'login-asset-acl'
urlpatterns = [
path('login-asset/check/', api.LoginAssetCheckAPI.as_view(), name='login-asset-check'),
path('login-asset-confirm/<uuid:pk>/status/', api.LoginAssetConfirmStatusAPI.as_view(), name='login-asset-confirm-status')
]
urlpatterns += router.urls

View File

@@ -1,4 +1,3 @@
from .application import *
from .account import *
from .mixin import *
from .remote_app import *

View File

@@ -6,8 +6,11 @@ from django.db.models import F, Q
from common.drf.filters import BaseFilterSet
from common.drf.api import JMSBulkModelViewSet
from common.mixins import RecordViewLogMixin
from rbac.permissions import RBACPermission
from assets.models import SystemUser
from ..models import Account
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
from ..hands import NeedMFAVerify
from .. import serializers
@@ -31,7 +34,8 @@ class AccountFilterSet(BaseFilterSet):
username = self.get_query_param('username')
if not username:
return qs
qs = qs.filter(Q(username=username) | Q(systemuser__username=username)).distinct()
q = Q(username=username) | Q(systemuser__username=username)
qs = qs.filter(q).distinct()
return qs
@@ -41,18 +45,21 @@ class ApplicationAccountViewSet(JMSBulkModelViewSet):
filterset_class = AccountFilterSet
filterset_fields = ['username', 'app_display', 'type', 'category', 'app']
serializer_class = serializers.AppAccountSerializer
permission_classes = (IsOrgAdmin,)
def get_queryset(self):
queryset = Account.objects.all() \
.annotate(type=F('app__type')) \
.annotate(app_display=F('app__name')) \
.annotate(systemuser_display=F('systemuser__name')) \
.annotate(category=F('app__category'))
queryset = Account.get_queryset()
return queryset
class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
class SystemUserAppRelationViewSet(ApplicationAccountViewSet):
perm_model = SystemUser
class ApplicationAccountSecretViewSet(RecordViewLogMixin, ApplicationAccountViewSet):
serializer_class = serializers.AppAccountSecretSerializer
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
permission_classes = [RBACPermission, NeedMFAVerify]
http_method_names = ['get', 'options']
rbac_perms = {
'retrieve': 'applications.view_applicationaccountsecret',
'list': 'applications.view_applicationaccountsecret',
}

View File

@@ -1,13 +1,12 @@
# coding: utf-8
#
from django.shortcuts import get_object_or_404
from orgs.mixins.api import OrgBulkModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from common.tree import TreeNodeSerializer
from common.mixins.api import SuggestionMixin
from ..hands import IsOrgAdminOrAppUser
from .. import serializers
from ..models import Application
@@ -18,16 +17,19 @@ class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
model = Application
filterset_fields = {
'name': ['exact'],
'category': ['exact'],
'category': ['exact', 'in'],
'type': ['exact', 'in'],
}
search_fields = ('name', 'type', 'category')
permission_classes = (IsOrgAdminOrAppUser,)
serializer_classes = {
'default': serializers.AppSerializer,
'get_tree': TreeNodeSerializer,
'suggestion': serializers.MiniAppSerializer
}
rbac_perms = {
'get_tree': 'applications.view_application',
'match': 'applications.match_application'
}
@action(methods=['GET'], detail=False, url_path='tree')
def get_tree(self, request, *args, **kwargs):

View File

@@ -1,55 +0,0 @@
from django.utils.translation import ugettext as _
from common.tree import TreeNode
from orgs.models import Organization
from ..models import Application
__all__ = ['SerializeApplicationToTreeNodeMixin']
class SerializeApplicationToTreeNodeMixin:
@staticmethod
def filter_organizations(applications):
organization_ids = set(applications.values_list('org_id', flat=True))
organizations = [Organization.get_instance(org_id) for org_id in organization_ids]
organizations.sort(key=lambda x: x.name)
return organizations
@staticmethod
def create_root_node():
name = _('My applications')
node = TreeNode(**{
'id': 'applications',
'name': name,
'title': name,
'pId': '',
'open': True,
'isParent': True,
'meta': {
'type': 'root'
}
})
return node
def serialize_applications_with_org(self, applications):
if not applications:
return []
root_node = self.create_root_node()
tree_nodes = [root_node]
organizations = self.filter_organizations(applications)
for i, org in enumerate(organizations):
# 组织节点
org_node = org.as_tree_node(pid=root_node.id)
tree_nodes.append(org_node)
org_applications = applications.filter(org_id=org.id)
count = org_applications.count()
org_node.name += '({})'.format(count)
# 各应用节点
apps_nodes = Application.create_tree_nodes(
queryset=org_applications, root_node=org_node,
show_empty=False
)
tree_nodes += apps_nodes
return tree_nodes

View File

@@ -2,10 +2,8 @@
#
from orgs.mixins import generics
from ..hands import IsAppUser
from .. import models
from ..serializers import RemoteAppConnectionInfoSerializer
from ..permissions import IsRemoteApp
__all__ = [
@@ -15,5 +13,4 @@ __all__ = [
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
model = models.Application
permission_classes = (IsAppUser, IsRemoteApp)
serializer_class = RemoteAppConnectionInfoSerializer

View File

@@ -1,7 +1,9 @@
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from django.apps import AppConfig
class ApplicationsConfig(AppConfig):
name = 'applications'
verbose_name = _('Applications')

View File

@@ -1,10 +1,10 @@
# coding: utf-8
#
from django.db.models import TextChoices
from django.db import models
from django.utils.translation import ugettext_lazy as _
class AppCategory(TextChoices):
class AppCategory(models.TextChoices):
db = 'db', _('Database')
remote_app = 'remote_app', _('Remote app')
cloud = 'cloud', 'Cloud'
@@ -13,14 +13,20 @@ class AppCategory(TextChoices):
def get_label(cls, category):
return dict(cls.choices).get(category, '')
@classmethod
def is_xpack(cls, category):
return category in ['remote_app']
class AppType(TextChoices):
class AppType(models.TextChoices):
# db category
mysql = 'mysql', 'MySQL'
mariadb = 'mariadb', 'MariaDB'
oracle = 'oracle', 'Oracle'
pgsql = 'postgresql', 'PostgreSQL'
mariadb = 'mariadb', 'MariaDB'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
# remote-app category
chrome = 'chrome', 'Chrome'
@@ -34,8 +40,14 @@ class AppType(TextChoices):
@classmethod
def category_types_mapper(cls):
return {
AppCategory.db: [cls.mysql, cls.oracle, cls.pgsql, cls.mariadb, cls.sqlserver],
AppCategory.remote_app: [cls.chrome, cls.mysql_workbench, cls.vmware_client, cls.custom],
AppCategory.db: [
cls.mysql, cls.mariadb, cls.oracle, cls.pgsql,
cls.sqlserver, cls.redis, cls.mongodb
],
AppCategory.remote_app: [
cls.chrome, cls.mysql_workbench,
cls.vmware_client, cls.custom
],
AppCategory.cloud: [cls.k8s]
}
@@ -63,6 +75,11 @@ class AppType(TextChoices):
def cloud_types(cls):
return [tp.value for tp in cls.category_types_mapper()[AppCategory.cloud]]
@classmethod
def is_xpack(cls, tp):
tp_category_mapper = cls.type_category_mapper()
category = tp_category_mapper[tp]
if AppCategory.is_xpack(category):
return True
return tp in ['oracle', 'postgresql', 'sqlserver']

View File

@@ -11,5 +11,5 @@
"""
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser, NeedMFAVerify
from common.permissions import NeedMFAVerify
from users.models import User, UserGroup

View File

@@ -1,6 +1,6 @@
# Generated by Django 2.1.7 on 2019-05-20 11:04
import common.fields.model
import common.db.fields
from django.db import migrations, models
import django.db.models.deletion
import uuid
@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('Browser', (('chrome', 'Chrome'),)), ('Database tools', (('mysql_workbench', 'MySQL Workbench'),)), ('Virtualization tools', (('vmware_client', 'vSphere Client'),)), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type')),
('path', models.CharField(max_length=128, verbose_name='App path')),
('params', common.fields.model.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
('params', common.db.fields.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),

View File

@@ -1,7 +1,7 @@
# Generated by Django 3.1.12 on 2021-08-26 09:07
import assets.models.base
import common.fields.model
import common.db.fields
from django.conf import settings
import django.core.validators
from django.db import migrations, models
@@ -26,9 +26,9 @@ class Migration(migrations.Migration):
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
@@ -56,9 +56,9 @@ class Migration(migrations.Migration):
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2022-01-12 12:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('applications', '0014_auto_20211105_1605'),
]
operations = [
migrations.AlterField(
model_name='application',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('redis', 'Redis'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 3.1.13 on 2022-01-18 06:55
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('applications', '0015_auto_20220112_2035'),
]
operations = [
migrations.AlterField(
model_name='account',
name='app',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.application', verbose_name='Application'),
),
migrations.AlterField(
model_name='historicalaccount',
name='app',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='applications.application', verbose_name='Application'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 3.1.13 on 2022-02-17 13:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('applications', '0016_auto_20220118_1455'),
]
operations = [
migrations.AlterModelOptions(
name='account',
options={'permissions': [('view_applicationaccountsecret', 'Can view application account secret'), ('change_appplicationaccountsecret', 'Can change application account secret')], 'verbose_name': 'Application account'},
),
migrations.AlterModelOptions(
name='applicationuser',
options={'verbose_name': 'Application user'},
),
migrations.AlterModelOptions(
name='historicalaccount',
options={'get_latest_by': 'history_date', 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical Application account'},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2022-02-23 07:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('applications', '0017_auto_20220217_2135'),
]
operations = [
migrations.AlterField(
model_name='application',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 3.1.14 on 2022-03-10 10:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('applications', '0018_auto_20220223_1539'),
]
operations = [
migrations.AlterModelOptions(
name='application',
options={'ordering': ('name',), 'permissions': [('match_application', 'Can match application')], 'verbose_name': 'Application'},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.14 on 2022-03-16 12:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('applications', '0019_auto_20220310_1853'),
]
operations = [
migrations.AlterField(
model_name='application',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type'),
),
]

View File

@@ -1,5 +1,6 @@
from django.db import models
from simple_history.models import HistoricalRecords
from django.db.models import F
from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty
@@ -7,16 +8,24 @@ from assets.models.base import BaseUser
class Account(BaseUser):
app = models.ForeignKey('applications.Application', on_delete=models.CASCADE, null=True, verbose_name=_('Database'))
systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user"))
app = models.ForeignKey(
'applications.Application', on_delete=models.CASCADE, null=True, verbose_name=_('Application')
)
systemuser = models.ForeignKey(
'assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user")
)
version = models.IntegerField(default=1, verbose_name=_('Version'))
history = HistoricalRecords()
auth_attrs = ['username', 'password', 'private_key', 'public_key']
class Meta:
verbose_name = _('Account')
verbose_name = _('Application account')
unique_together = [('username', 'app', 'systemuser')]
permissions = [
('view_applicationaccountsecret', _('Can view application account secret')),
('change_appplicationaccountsecret', _('Can change application account secret')),
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -60,6 +69,10 @@ class Account(BaseUser):
def type(self):
return self.app.type
@lazyproperty
def attrs(self):
return self.app.attrs
@lazyproperty
def app_display(self):
return self.systemuser.name
@@ -84,5 +97,14 @@ class Account(BaseUser):
app = '*'
return '{}@{}'.format(username, app)
@classmethod
def get_queryset(cls):
queryset = cls.objects.all() \
.annotate(type=F('app__type')) \
.annotate(app_display=F('app__name')) \
.annotate(systemuser_display=F('systemuser__name')) \
.annotate(category=F('app__category'))
return queryset
def __str__(self):
return self.smart_name

View File

@@ -1,12 +1,17 @@
from collections import defaultdict
from urllib.parse import urlencode, parse_qsl
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from orgs.mixins.models import OrgModelMixin
from common.mixins import CommonModelMixin
from common.tree import TreeNode
from common.utils import is_uuid
from assets.models import Asset, SystemUser
from ..utils import KubernetesTree
from .. import const
@@ -15,6 +20,14 @@ class ApplicationTreeNodeMixin:
name: str
type: str
category: str
attrs: dict
@staticmethod
def create_tree_id(pid, type, v):
i = dict(parse_qsl(pid))
i[type] = v
tree_id = urlencode(i)
return tree_id
@classmethod
def create_choice_node(cls, c, id_, pid, tp, opened=False, counts=None,
@@ -65,13 +78,15 @@ class ApplicationTreeNodeMixin:
return node
@classmethod
def create_category_tree_nodes(cls, root_node, counts=None, show_empty=True, show_count=True):
def create_category_tree_nodes(cls, pid, counts=None, show_empty=True, show_count=True):
nodes = []
categories = const.AppType.category_types_mapper().keys()
for category in categories:
i = root_node.id + '_' + category.value
if not settings.XPACK_ENABLED and const.AppCategory.is_xpack(category):
continue
i = cls.create_tree_id(pid, 'category', category.value)
node = cls.create_choice_node(
category, i, pid=root_node.id, tp='category',
category, i, pid=pid, tp='category',
counts=counts, opened=False, show_empty=show_empty,
show_count=show_count
)
@@ -81,17 +96,23 @@ class ApplicationTreeNodeMixin:
return nodes
@classmethod
def create_types_tree_nodes(cls, root_node, counts, show_empty=True, show_count=True):
def create_types_tree_nodes(cls, pid, counts, show_empty=True, show_count=True):
nodes = []
temp_pid = pid
type_category_mapper = const.AppType.type_category_mapper()
for tp in const.AppType.type_category_mapper().keys():
types = const.AppType.type_category_mapper().keys()
for tp in types:
if not settings.XPACK_ENABLED and const.AppType.is_xpack(tp):
continue
category = type_category_mapper.get(tp)
pid = root_node.id + '_' + category.value
i = root_node.id + '_' + tp.value
pid = cls.create_tree_id(pid, 'category', category.value)
i = cls.create_tree_id(pid, 'type', tp.value)
node = cls.create_choice_node(
tp, i, pid, tp='type', counts=counts, opened=False,
show_empty=show_empty, show_count=show_count
)
pid = temp_pid
if not node:
continue
nodes.append(node)
@@ -109,40 +130,69 @@ class ApplicationTreeNodeMixin:
return counts
@classmethod
def create_tree_nodes(cls, queryset, root_node=None, show_empty=True, show_count=True):
def create_category_type_tree_nodes(cls, queryset, pid, show_empty=True, show_count=True):
counts = cls.get_tree_node_counts(queryset)
tree_nodes = []
# 类别的节点
tree_nodes += cls.create_category_tree_nodes(
pid, counts, show_empty=show_empty,
show_count=show_count
)
# 类型的节点
tree_nodes += cls.create_types_tree_nodes(
pid, counts, show_empty=show_empty,
show_count=show_count
)
return tree_nodes
@classmethod
def create_tree_nodes(cls, queryset, root_node=None, show_empty=True, show_count=True):
tree_nodes = []
# 根节点有可能是组织名称
if root_node is None:
root_node = cls.create_root_tree_node(queryset, show_count=show_count)
tree_nodes.append(root_node)
# 类别的节点
tree_nodes += cls.create_category_tree_nodes(
root_node, counts, show_empty=show_empty,
show_count=show_count
)
# 类型的节点
tree_nodes += cls.create_types_tree_nodes(
root_node, counts, show_empty=show_empty,
show_count=show_count
tree_nodes += cls.create_category_type_tree_nodes(
queryset, root_node.id, show_empty=show_empty, show_count=show_count
)
# 应用的节点
for app in queryset:
pid = root_node.id + '_' + app.type
tree_nodes.append(app.as_tree_node(pid))
if not settings.XPACK_ENABLED and const.AppType.is_xpack(app.type):
continue
node = app.as_tree_node(root_node.id)
tree_nodes.append(node)
return tree_nodes
def as_tree_node(self, pid):
def create_app_tree_pid(self, root_id):
pid = self.create_tree_id(root_id, 'category', self.category)
pid = self.create_tree_id(pid, 'type', self.type)
return pid
def as_tree_node(self, pid, k8s_as_tree=False):
if self.type == const.AppType.k8s and k8s_as_tree:
node = KubernetesTree(pid).as_tree_node(self)
else:
node = self._as_tree_node(pid)
return node
def _attrs_to_tree(self):
if self.category == const.AppCategory.db:
return self.attrs
return {}
def _as_tree_node(self, pid):
icon_skin_category_mapper = {
'remote_app': 'chrome',
'db': 'database',
'cloud': 'cloud'
}
icon_skin = icon_skin_category_mapper.get(self.category, 'file')
pid = self.create_app_tree_pid(pid)
node = TreeNode(**{
'id': str(self.id),
'name': self.name,
@@ -156,6 +206,7 @@ class ApplicationTreeNodeMixin:
'data': {
'category': self.category,
'type': self.type,
'attrs': self._attrs_to_tree()
}
}
})
@@ -183,6 +234,9 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
verbose_name = _('Application')
unique_together = [('org_id', 'name')]
ordering = ('name',)
permissions = [
('match_application', _('Can match application')),
]
def __str__(self):
category_display = self.get_category_display()
@@ -193,6 +247,14 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
def category_remote_app(self):
return self.category == const.AppCategory.remote_app.value
@property
def category_cloud(self):
return self.category == const.AppCategory.cloud.value
@property
def category_db(self):
return self.category == const.AppCategory.db.value
def get_rdp_remote_app_setting(self):
from applications.serializers.attrs import get_serializer_class_by_application_type
if not self.category_remote_app:
@@ -218,14 +280,26 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
'parameters': parameters
}
def get_remote_app_asset(self):
def get_remote_app_asset(self, raise_exception=True):
asset_id = self.attrs.get('asset')
if not asset_id:
if is_uuid(asset_id):
return Asset.objects.filter(id=asset_id).first()
if raise_exception:
raise ValueError("Remote App not has asset attr")
asset = Asset.objects.filter(id=asset_id).first()
return asset
def get_target_ip(self):
target_ip = ''
if self.category_remote_app:
asset = self.get_remote_app_asset()
target_ip = asset.ip if asset else target_ip
elif self.category_cloud:
target_ip = self.attrs.get('cluster')
elif self.category_db:
target_ip = self.attrs.get('host')
return target_ip
class ApplicationUser(SystemUser):
class Meta:
proxy = True
verbose_name = _('Application user')

View File

@@ -1,15 +1,15 @@
# coding: utf-8
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.serializers.base import AuthSerializerMixin
from common.drf.serializers import MethodSerializer
from common.drf.serializers import MethodSerializer, SecretReadableMixin
from .attrs import (
category_serializer_classes_mapping,
type_serializer_classes_mapping
type_serializer_classes_mapping,
type_secret_serializer_classes_mapping
)
from .. import models
from .. import const
@@ -23,17 +23,28 @@ __all__ = [
class AppSerializerMixin(serializers.Serializer):
attrs = MethodSerializer()
@property
def app(self):
if isinstance(self.instance, models.Application):
instance = self.instance
else:
instance = None
return instance
def get_attrs_serializer(self):
default_serializer = serializers.Serializer(read_only=True)
if isinstance(self.instance, models.Application):
_type = self.instance.type
_category = self.instance.category
instance = self.app
if instance:
_type = instance.type
_category = instance.category
else:
_type = self.context['request'].query_params.get('type')
_category = self.context['request'].query_params.get('category')
if _type:
serializer_class = type_serializer_classes_mapping.get(_type)
if isinstance(self, AppAccountSecretSerializer):
serializer_class = type_secret_serializer_classes_mapping.get(_type)
else:
serializer_class = type_serializer_classes_mapping.get(_type)
elif _category:
serializer_class = category_serializer_classes_mapping.get(_category)
else:
@@ -84,11 +95,13 @@ class MiniAppSerializer(serializers.ModelSerializer):
fields = AppSerializer.Meta.fields_mini
class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class AppAccountSerializer(AppSerializerMixin, AuthSerializerMixin, BulkOrgResourceModelSerializer):
category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True)
category_display = serializers.SerializerMethodField(label=_('Category display'))
type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
type_display = serializers.SerializerMethodField(label=_('Type display'))
date_created = serializers.DateTimeField(label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True)
date_updated = serializers.DateTimeField(label=_('Date updated'), format="%Y/%m/%d %H:%M:%S", read_only=True)
category_mapper = dict(const.AppCategory.choices)
type_mapper = dict(const.AppType.choices)
@@ -96,22 +109,32 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class Meta:
model = models.Account
fields_mini = ['id', 'username', 'version']
fields_write_only = ['password', 'private_key']
fields_write_only = ['password', 'private_key', 'public_key', 'passphrase']
fields_other = ['date_created', 'date_updated']
fields_fk = ['systemuser', 'systemuser_display', 'app', 'app_display']
fields = fields_mini + fields_fk + fields_write_only + [
'type', 'type_display', 'category', 'category_display',
fields = fields_mini + fields_fk + fields_write_only + fields_other + [
'type', 'type_display', 'category', 'category_display', 'attrs'
]
extra_kwargs = {
'username': {'default': '', 'required': False},
'password': {'write_only': True},
'app_display': {'label': _('Application display')},
'systemuser_display': {'label': _('System User')}
'systemuser_display': {'label': _('System User')},
'account': {'label': _('account')}
}
use_model_bulk_create = True
model_bulk_create_kwargs = {
'ignore_conflicts': True
}
@property
def app(self):
if isinstance(self.instance, models.Account):
instance = self.instance.app
else:
instance = None
return instance
def get_category_display(self, obj):
return self.category_mapper.get(obj.category)
@@ -129,8 +152,13 @@ class AppAccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
return super().to_representation(instance)
class AppAccountSecretSerializer(AppAccountSerializer):
class AppAccountSecretSerializer(SecretReadableMixin, AppAccountSerializer):
class Meta(AppAccountSerializer.Meta):
fields_backup = [
'id', 'app_display', 'attrs', 'username', 'password', 'private_key',
'public_key', 'date_created', 'date_updated', 'version'
]
extra_kwargs = {
'password': {'write_only': False},
'private_key': {'write_only': False},

View File

@@ -1,7 +1,6 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
__all__ = ['CloudSerializer']

View File

@@ -10,7 +10,6 @@ from assets.models import Asset
logger = get_logger(__file__)
__all__ = ['RemoteAppSerializer']

View File

@@ -4,6 +4,8 @@ from .mariadb import *
from .oracle import *
from .pgsql import *
from .sqlserver import *
from .redis import *
from .mongodb import *
from .chrome import *
from .mysql_workbench import *

View File

@@ -1,10 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['ChromeSerializer']
__all__ = ['ChromeSerializer', 'ChromeSecretSerializer']
class ChromeSerializer(RemoteAppSerializer):
@@ -14,13 +14,21 @@ class ChromeSerializer(RemoteAppSerializer):
max_length=128, label=_('Application path'), default=CHROME_PATH, allow_null=True,
)
chrome_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target URL'), allow_null=True,
max_length=128, allow_blank=True, required=False,
label=_('Target URL'), allow_null=True,
)
chrome_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'), allow_null=True,
max_length=128, allow_blank=True, required=False,
label=_('Chrome username'), allow_null=True,
)
chrome_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True
chrome_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Chrome password'), allow_null=True
)
class ChromeSecretSerializer(ChromeSerializer):
chrome_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Chrome password'), allow_null=True, write_only=False
)

View File

@@ -1,11 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['CustomSerializer']
__all__ = ['CustomSerializer', 'CustomSecretSerializer']
class CustomSerializer(RemoteAppSerializer):
@@ -18,10 +17,17 @@ class CustomSerializer(RemoteAppSerializer):
allow_null=True,
)
custom_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'),
max_length=128, allow_blank=True, required=False, label=_('Custom Username'),
allow_null=True,
)
custom_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True,
custom_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Custom password'), allow_null=True,
)
class CustomSecretSerializer(RemoteAppSerializer):
custom_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Custom password'), allow_null=True,
)

View File

@@ -1,6 +1,5 @@
from ..application_category import CloudSerializer
__all__ = ['K8SSerializer']

View File

@@ -1,6 +1,5 @@
from .mysql import MySQLSerializer
__all__ = ['MariaDBSerializer']

View File

@@ -0,0 +1,11 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['MongoDBSerializer']
class MongoDBSerializer(DBSerializer):
port = serializers.IntegerField(default=27017, label=_('Port'), allow_null=True)

View File

@@ -3,13 +3,9 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['MySQLSerializer']
class MySQLSerializer(DBSerializer):
port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True)

View File

@@ -1,10 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['MySQLWorkbenchSerializer']
__all__ = ['MySQLWorkbenchSerializer', 'MySQLWorkbenchSecretSerializer']
class MySQLWorkbenchSerializer(RemoteAppSerializer):
@@ -27,10 +27,17 @@ class MySQLWorkbenchSerializer(RemoteAppSerializer):
allow_null=True,
)
mysql_workbench_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'),
max_length=128, allow_blank=True, required=False, label=_('Mysql workbench username'),
allow_null=True,
)
mysql_workbench_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True,
mysql_workbench_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Mysql workbench password'), allow_null=True,
)
class MySQLWorkbenchSecretSerializer(RemoteAppSerializer):
mysql_workbench_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Mysql workbench password'), allow_null=True,
)

View File

@@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['OracleSerializer']
class OracleSerializer(DBSerializer):
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)

View File

@@ -3,10 +3,8 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['PostgreSerializer']
class PostgreSerializer(DBSerializer):
port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True)

View File

@@ -0,0 +1,11 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['RedisSerializer']
class RedisSerializer(DBSerializer):
port = serializers.IntegerField(default=6379, label=_('Port'), allow_null=True)

View File

@@ -3,7 +3,6 @@ from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['SQLServerSerializer']

View File

@@ -1,10 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['VMwareClientSerializer']
__all__ = ['VMwareClientSerializer', 'VMwareClientSecretSerializer']
class VMwareClientSerializer(RemoteAppSerializer):
@@ -23,10 +23,17 @@ class VMwareClientSerializer(RemoteAppSerializer):
allow_null=True
)
vmware_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Username'),
max_length=128, allow_blank=True, required=False, label=_('Vmware username'),
allow_null=True
)
vmware_password = serializers.CharField(
max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password'),
allow_null=True
vmware_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Vmware password'), allow_null=True
)
class VMwareClientSecretSerializer(RemoteAppSerializer):
vmware_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Vmware password'), allow_null=True
)

View File

@@ -1,15 +1,15 @@
from rest_framework import serializers
import copy
from applications import const
from . import application_category, application_type
__all__ = [
'category_serializer_classes_mapping',
'type_serializer_classes_mapping',
'get_serializer_class_by_application_type',
'type_secret_serializer_classes_mapping'
]
# define `attrs` field `category serializers mapping`
# ---------------------------------------------------
@@ -29,15 +29,34 @@ type_serializer_classes_mapping = {
const.AppType.oracle.value: application_type.OracleSerializer,
const.AppType.pgsql.value: application_type.PostgreSerializer,
const.AppType.sqlserver.value: application_type.SQLServerSerializer,
# remote-app
const.AppType.chrome.value: application_type.ChromeSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSerializer,
const.AppType.custom.value: application_type.CustomSerializer,
const.AppType.redis.value: application_type.RedisSerializer,
const.AppType.mongodb.value: application_type.MongoDBSerializer,
# cloud
const.AppType.k8s.value: application_type.K8SSerializer
}
remote_app_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSerializer,
const.AppType.custom.value: application_type.CustomSerializer
}
type_serializer_classes_mapping.update(remote_app_serializer_classes_mapping)
remote_app_secret_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSecretSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSecretSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSecretSerializer,
const.AppType.custom.value: application_type.CustomSecretSerializer
}
type_secret_serializer_classes_mapping = copy.deepcopy(type_serializer_classes_mapping)
type_secret_serializer_classes_mapping.update(remote_app_secret_serializer_classes_mapping)
def get_serializer_class_by_application_type(_application_type):
return type_serializer_classes_mapping.get(_application_type)

View File

@@ -11,6 +11,7 @@ app_name = 'applications'
router = BulkRouter()
router.register(r'applications', api.ApplicationViewSet, 'application')
router.register(r'accounts', api.ApplicationAccountViewSet, 'application-account')
router.register(r'system-users-apps-relations', api.SystemUserAppRelationViewSet, 'system-users-apps-relation')
router.register(r'account-secrets', api.ApplicationAccountSecretViewSet, 'application-account-secret')

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
#
from .kubernetes_util import *

View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
from urllib3.exceptions import MaxRetryError
from urllib.parse import urlencode
from kubernetes.client import api_client
from kubernetes.client.api import core_v1_api
from kubernetes import client
from kubernetes.client.exceptions import ApiException
from rest_framework.generics import get_object_or_404
from common.utils import get_logger
from common.tree import TreeNode
from assets.models import SystemUser
from .. import const
logger = get_logger(__file__)
class KubernetesClient:
def __init__(self, url, token):
self.url = url
self.token = token
def get_api(self):
configuration = client.Configuration()
configuration.host = self.url
configuration.verify_ssl = False
configuration.api_key = {"authorization": "Bearer " + self.token}
c = api_client.ApiClient(configuration=configuration)
api = core_v1_api.CoreV1Api(c)
return api
def get_namespace_list(self):
api = self.get_api()
namespace_list = []
for ns in api.list_namespace().items:
namespace_list.append(ns.metadata.name)
return namespace_list
def get_services(self):
api = self.get_api()
ret = api.list_service_for_all_namespaces(watch=False)
for i in ret.items:
print("%s \t%s \t%s \t%s \t%s \n" % (
i.kind, i.metadata.namespace, i.metadata.name, i.spec.cluster_ip, i.spec.ports))
def get_pod_info(self, namespace, pod):
api = self.get_api()
resp = api.read_namespaced_pod(namespace=namespace, name=pod)
return resp
def get_pod_logs(self, namespace, pod):
api = self.get_api()
log_content = api.read_namespaced_pod_log(pod, namespace, pretty=True, tail_lines=200)
return log_content
def get_pods(self):
api = self.get_api()
try:
ret = api.list_pod_for_all_namespaces(watch=False, _request_timeout=(3, 3))
except MaxRetryError:
logger.warning('Kubernetes connection timed out')
return
except ApiException as e:
if e.status == 401:
logger.warning('Kubernetes User not authenticated')
else:
logger.warning(e)
return
data = {}
for i in ret.items:
namespace = i.metadata.namespace
pod_info = {
'pod_name': i.metadata.name,
'containers': [j.name for j in i.spec.containers]
}
if namespace in data:
data[namespace].append(pod_info)
else:
data[namespace] = [pod_info, ]
return data
@staticmethod
def get_kubernetes_data(app_id, system_user_id):
from ..models import Application
app = get_object_or_404(Application, id=app_id)
system_user = get_object_or_404(SystemUser, id=system_user_id)
k8s = KubernetesClient(app.attrs['cluster'], system_user.token)
return k8s.get_pods()
class KubernetesTree:
def __init__(self, tree_id):
self.tree_id = tree_id
def as_tree_node(self, app):
pid = app.create_app_tree_pid(self.tree_id)
app_id = str(app.id)
parent_info = {'app_id': app_id}
node = self.create_tree_node(
app_id, pid, app.name, 'k8s', parent_info
)
return node
def as_system_user_tree_node(self, system_user, parent_info):
from ..models import ApplicationTreeNodeMixin
system_user_id = str(system_user.id)
username = system_user.username
username = username if username else '*'
name = f'{system_user.name}({username})'
pid = urlencode({'app_id': self.tree_id})
i = ApplicationTreeNodeMixin.create_tree_id(pid, 'system_user_id', system_user_id)
parent_info.update({'system_user_id': system_user_id})
node = self.create_tree_node(
i, pid, name, 'system_user', parent_info, icon='user-tie'
)
return node
def as_namespace_pod_tree_node(self, name, meta, type, counts=0, is_container=False):
from ..models import ApplicationTreeNodeMixin
i = ApplicationTreeNodeMixin.create_tree_id(self.tree_id, type, name)
meta.update({type: name})
name = name if is_container else f'{name}({counts})'
node = self.create_tree_node(
i, self.tree_id, name, type, meta, icon='cloud', is_container=is_container
)
return node
@staticmethod
def create_tree_node(id_, pid, name, identity, parent_info, icon='', is_container=False):
node = TreeNode(**{
'id': id_,
'name': name,
'title': name,
'pId': pid,
'isParent': not is_container,
'open': False,
'iconSkin': icon,
'parentInfo': urlencode(parent_info),
'meta': {
'type': 'application',
'data': {
'category': const.AppCategory.cloud,
'type': const.AppType.k8s,
'identity': identity
}
}
})
return node
def async_tree_node(self, parent_info):
pod_name = parent_info.get('pod')
app_id = parent_info.get('app_id')
namespace = parent_info.get('namespace')
system_user_id = parent_info.get('system_user_id')
tree_nodes = []
data = KubernetesClient.get_kubernetes_data(app_id, system_user_id)
if not data:
return tree_nodes
if pod_name:
for container in next(
filter(
lambda x: x['pod_name'] == pod_name, data[namespace]
)
)['containers']:
container_node = self.as_namespace_pod_tree_node(
container, parent_info, 'container', is_container=True
)
tree_nodes.append(container_node)
elif namespace:
for pod in data[namespace]:
pod_nodes = self.as_namespace_pod_tree_node(
pod['pod_name'], parent_info, 'pod', len(pod['containers'])
)
tree_nodes.append(pod_nodes)
elif system_user_id:
for namespace, pods in data.items():
namespace_node = self.as_namespace_pod_tree_node(
namespace, parent_info, 'namespace', len(pods)
)
tree_nodes.append(namespace_node)
return tree_nodes

View File

@@ -10,3 +10,4 @@ from .domain import *
from .cmd_filter import *
from .gathered_user import *
from .favorite_asset import *
from .account_backup import *

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
#
from rest_framework import status, viewsets
from rest_framework.response import Response
from orgs.mixins.api import OrgBulkModelViewSet
from .. import serializers
from ..tasks import execute_account_backup_plan
from ..models import (
AccountBackupPlan, AccountBackupPlanExecution
)
__all__ = [
'AccountBackupPlanViewSet', 'AccountBackupPlanExecutionViewSet'
]
class AccountBackupPlanViewSet(OrgBulkModelViewSet):
model = AccountBackupPlan
filter_fields = ('name',)
search_fields = filter_fields
ordering_fields = ('name',)
ordering = ('name',)
serializer_class = serializers.AccountBackupPlanSerializer
class AccountBackupPlanExecutionViewSet(viewsets.ModelViewSet):
serializer_class = serializers.AccountBackupPlanExecutionSerializer
search_fields = ('trigger',)
filterset_fields = ('trigger', 'plan_id')
http_method_names = ['get', 'post', 'options']
def get_queryset(self):
queryset = AccountBackupPlanExecution.objects.all()
return queryset
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
pid = serializer.data.get('plan')
task = execute_account_backup_plan.delay(
pid=pid, trigger=AccountBackupPlanExecution.Trigger.manual
)
return Response({'task': task.id}, status=status.HTTP_201_CREATED)
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = queryset.order_by('-date_start')
return queryset

View File

@@ -1,13 +1,15 @@
from django.db.models import F, Q
from rest_framework.decorators import action
from django_filters import rest_framework as filters
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from django_filters import rest_framework as filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.generics import CreateAPIView
from orgs.mixins.api import OrgBulkModelViewSet
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, NeedMFAVerify
from rbac.permissions import RBACPermission
from common.drf.filters import BaseFilterSet
from common.mixins import RecordViewLogMixin
from common.permissions import NeedMFAVerify
from ..tasks.account_connectivity import test_accounts_connectivity_manual
from ..models import AuthBook, Node
from .. import serializers
@@ -26,6 +28,7 @@ class AccountFilterSet(BaseFilterSet):
qs = super().qs
qs = self.filter_username(qs)
qs = self.filter_node(qs)
qs = qs.distinct()
return qs
def filter_username(self, qs):
@@ -61,12 +64,13 @@ class AccountViewSet(OrgBulkModelViewSet):
'default': serializers.AccountSerializer,
'verify_account': serializers.AssetTaskSerializer
}
permission_classes = (IsOrgAdmin,)
rbac_perms = {
'verify_account': 'assets.test_authbook',
'partial_update': 'assets.change_assetaccountsecret',
}
def get_queryset(self):
queryset = super().get_queryset() \
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname'))
queryset = AuthBook.get_queryset()
return queryset
@action(methods=['post'], detail=True, url_path='verify')
@@ -76,24 +80,30 @@ class AccountViewSet(OrgBulkModelViewSet):
return Response(data={'task': task.id})
class AccountSecretsViewSet(AccountViewSet):
class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet):
"""
因为可能要导出所有账号,所以单独建立了一个 viewset
"""
serializer_classes = {
'default': serializers.AccountSecretSerializer
}
permission_classes = (IsOrgAdmin, NeedMFAVerify)
http_method_names = ['get']
permission_classes = [RBACPermission, NeedMFAVerify]
rbac_perms = {
'list': 'assets.view_assetaccountsecret',
'retrieve': 'assets.view_assetaccountsecret',
}
class AccountTaskCreateAPI(CreateAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.AccountTaskSerializer
filterset_fields = AccountViewSet.filterset_fields
search_fields = AccountViewSet.search_fields
filterset_class = AccountViewSet.filterset_class
def check_permissions(self, request):
return request.user.has_perm('assets.test_assetconnectivity')
def get_accounts(self):
queryset = AuthBook.objects.all()
queryset = self.filter_queryset(queryset)
@@ -110,5 +120,4 @@ class AccountTaskCreateAPI(CreateAPIView):
def get_exception_handler(self):
def handler(e, context):
return Response({"error": str(e)}, status=400)
return handler

View File

@@ -2,9 +2,9 @@ from django.db.models import Count
from orgs.mixins.api import OrgBulkModelViewSet
from common.utils import get_logger
from ..hands import IsOrgAdmin
from ..models import SystemUser
from .. import serializers
from rbac.permissions import RBACPermission
logger = get_logger(__file__)
@@ -20,7 +20,7 @@ class AdminUserViewSet(OrgBulkModelViewSet):
filterset_fields = ("name", "username")
search_fields = filterset_fields
serializer_class = serializers.AdminUserSerializer
permission_classes = (IsOrgAdmin,)
permission_classes = (RBACPermission,)
ordering_fields = ('name',)
ordering = ('name', )

View File

@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
#
from assets.api import FilterAssetByNodeMixin
from rest_framework.viewsets import ModelViewSet
from rest_framework.generics import RetrieveAPIView, ListAPIView
from django.shortcuts import get_object_or_404
from django.db.models import Q
from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsSuperUser
from common.mixins.api import SuggestionMixin
from users.models import User, UserGroup
from users.serializers import UserSerializer, UserGroupSerializer
@@ -17,7 +15,8 @@ from perms.serializers import AssetPermissionSerializer
from perms.filters import AssetPermissionFilter
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from ..models import Asset, Node, Platform
from assets.api import FilterAssetByNodeMixin
from ..models import Asset, Node, Platform, Gateway
from .. import serializers
from ..tasks import (
update_assets_hardware_info_manual, test_assets_connectivity_manual,
@@ -55,7 +54,9 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
'default': serializers.AssetSerializer,
'suggestion': serializers.MiniAssetSerializer
}
permission_classes = (IsOrgAdminOrAppUser,)
rbac_perms = {
'match': 'assets.match_asset'
}
extra_filter_backends = [FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend]
def set_assets_node(self, assets):
@@ -76,8 +77,10 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
class AssetPlatformRetrieveApi(RetrieveAPIView):
queryset = Platform.objects.all()
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.PlatformSerializer
rbac_perms = {
'retrieve': 'assets.view_gateway'
}
def get_object(self):
asset_pk = self.kwargs.get('pk')
@@ -87,16 +90,10 @@ class AssetPlatformRetrieveApi(RetrieveAPIView):
class AssetPlatformViewSet(ModelViewSet):
queryset = Platform.objects.all()
permission_classes = (IsSuperUser,)
serializer_class = serializers.PlatformSerializer
filterset_fields = ['name', 'base']
search_fields = ['name']
def get_permissions(self):
if self.request.method.lower() in ['get', 'options']:
self.permission_classes = (IsOrgAdmin,)
return super().get_permissions()
def check_object_permissions(self, request, obj):
if request.method.lower() in ['delete', 'put', 'patch'] and obj.internal:
self.permission_denied(
@@ -131,7 +128,6 @@ class AssetsTaskMixin:
class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
model = Asset
serializer_class = serializers.AssetTaskSerializer
permission_classes = (IsOrgAdmin,)
def create(self, request, *args, **kwargs):
pk = self.kwargs.get('pk')
@@ -139,11 +135,26 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
request.data['assets'] = [pk]
return super().create(request, *args, **kwargs)
def check_permissions(self, request):
action = request.data.get('action')
action_perm_require = {
'refresh': 'assets.refresh_assethardwareinfo',
'push_system_user': 'assets.push_assetsystemuser',
'test': 'assets.test_assetconnectivity',
'test_system_user': 'assets.test_assetconnectivity'
}
perm_required = action_perm_require.get(action)
has = self.request.user.has_perm(perm_required)
if not has:
self.permission_denied(request)
def perform_asset_task(self, serializer):
data = serializer.validated_data
action = data['action']
if action not in ['push_system_user', 'test_system_user']:
return
asset = data['asset']
system_users = data.get('system_users')
if not system_users:
@@ -166,24 +177,37 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
model = Asset
serializer_class = serializers.AssetsTaskSerializer
permission_classes = (IsOrgAdmin,)
def check_permissions(self, request):
action = request.data.get('action')
action_perm_require = {
'refresh': 'assets.refresh_assethardwareinfo',
}
perm_required = action_perm_require.get(action)
has = self.request.user.has_perm(perm_required)
if not has:
self.permission_denied(request)
class AssetGatewayListApi(generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.GatewayWithAuthSerializer
rbac_perms = {
'list': 'assets.view_gateway'
}
def get_queryset(self):
asset_id = self.kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id)
if not asset.domain:
return []
return Gateway.objects.none()
queryset = asset.domain.gateways.filter(protocol='ssh')
return queryset
class BaseAssetPermUserOrUserGroupListApi(ListAPIView):
permission_classes = (IsOrgAdmin,)
rbac_perms = {
'GET': 'perms.view_assetpermission'
}
def get_object(self):
asset_id = self.kwargs.get('pk')
@@ -201,6 +225,9 @@ class AssetPermUserListApi(BaseAssetPermUserOrUserGroupListApi):
filterset_class = UserFilter
search_fields = ('username', 'email', 'name', 'id', 'source', 'role')
serializer_class = UserSerializer
rbac_perms = {
'GET': 'perms.view_assetpermission'
}
def get_queryset(self):
perms = self.get_asset_related_perms()
@@ -220,11 +247,13 @@ class AssetPermUserGroupListApi(BaseAssetPermUserOrUserGroupListApi):
class BaseAssetPermUserOrUserGroupPermissionsListApiMixin(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
model = AssetPermission
serializer_class = AssetPermissionSerializer
filterset_class = AssetPermissionFilter
search_fields = ('name',)
rbac_perms = {
'list': 'perms.view_assetpermission'
}
def get_object(self):
asset_id = self.kwargs.get('pk')

View File

@@ -8,14 +8,11 @@ from django.shortcuts import get_object_or_404
from common.utils import reverse
from common.utils import lazyproperty
from orgs.mixins.api import OrgBulkModelViewSet
from tickets.api import GenericTicketStatusRetrieveCloseAPI
from ..hands import IsOrgAdmin, IsAppUser
from ..models import CommandFilter, CommandFilterRule
from .. import serializers
__all__ = [
'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI',
'CommandConfirmStatusAPI'
]
@@ -23,7 +20,6 @@ class CommandFilterViewSet(OrgBulkModelViewSet):
model = CommandFilter
filterset_fields = ("name",)
search_fields = filterset_fields
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.CommandFilterSerializer
@@ -31,7 +27,6 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
model = CommandFilterRule
filterset_fields = ('content',)
search_fields = filterset_fields
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.CommandFilterRuleSerializer
def get_queryset(self):
@@ -43,8 +38,10 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
class CommandConfirmAPI(CreateAPIView):
permission_classes = (IsAppUser,)
serializer_class = serializers.CommandConfirmSerializer
rbac_perms = {
'POST': 'tickets.add_superticket'
}
def create(self, request, *args, **kwargs):
ticket = self.create_command_confirm_ticket()
@@ -56,14 +53,14 @@ class CommandConfirmAPI(CreateAPIView):
run_command=self.serializer.data.get('run_command'),
session=self.serializer.session,
cmd_filter_rule=self.serializer.cmd_filter_rule,
org_id=self.serializer.org.id
org_id=self.serializer.org.id,
)
return ticket
@staticmethod
def get_response_data(ticket):
confirm_status_url = reverse(
view_name='api-assets:command-confirm-status',
view_name='api-tickets:super-ticket-status',
kwargs={'pk': str(ticket.id)}
)
ticket_detail_url = reverse(
@@ -86,6 +83,3 @@ class CommandConfirmAPI(CreateAPIView):
serializer.is_valid(raise_exception=True)
return serializer
class CommandConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
pass

View File

@@ -6,7 +6,6 @@ from rest_framework.views import APIView, Response
from rest_framework.serializers import ValidationError
from common.utils import get_logger
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from orgs.mixins.api import OrgBulkModelViewSet
from ..models import Domain, Gateway
from .. import serializers
@@ -20,7 +19,6 @@ class DomainViewSet(OrgBulkModelViewSet):
model = Domain
filterset_fields = ("name", )
search_fields = filterset_fields
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.DomainSerializer
ordering_fields = ('name',)
ordering = ('name', )
@@ -35,13 +33,15 @@ class GatewayViewSet(OrgBulkModelViewSet):
model = Gateway
filterset_fields = ("domain__name", "name", "username", "ip", "domain")
search_fields = ("domain__name", "name", "username", "ip")
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.GatewaySerializer
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
permission_classes = (IsOrgAdmin,)
queryset = Gateway.objects.all()
object = None
rbac_perms = {
'POST': 'assets.test_gateway'
}
def post(self, request, *args, **kwargs):
self.object = self.get_object(Gateway.objects.all())

View File

@@ -3,7 +3,6 @@
from orgs.mixins.api import OrgModelViewSet
from assets.models import GatheredUser
from common.permissions import IsOrgAdmin
from ..serializers import GatheredUserSerializer
from ..filters import AssetRelatedByNodeFilterBackend
@@ -15,7 +14,6 @@ __all__ = ['GatheredUserViewSet']
class GatheredUserViewSet(OrgModelViewSet):
model = GatheredUser
serializer_class = GatheredUserSerializer
permission_classes = [IsOrgAdmin]
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
filterset_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname', 'asset_id']

View File

@@ -17,7 +17,6 @@ from django.db.models import Count
from common.utils import get_logger
from orgs.mixins.api import OrgBulkModelViewSet
from ..hands import IsOrgAdmin
from ..models import Label
from .. import serializers
@@ -30,7 +29,6 @@ class LabelViewSet(OrgBulkModelViewSet):
model = Label
filterset_fields = ("name", "value")
search_fields = filterset_fields
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.LabelSerializer
def list(self, request, *args, **kwargs):

View File

@@ -17,10 +17,9 @@ from common.mixins.api import SuggestionMixin
from assets.models import Asset
from common.utils import get_logger, get_object_or_none
from common.tree import TreeNodeSerializer
from orgs.mixins.api import OrgModelViewSet
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from orgs.utils import current_org
from ..hands import IsOrgAdmin
from ..models import Node
from ..tasks import (
update_node_assets_hardware_info_manual,
@@ -31,7 +30,6 @@ from .. import serializers
from .mixin import SerializeToTreeNodeMixin
from assets.locks import NodeAddChildrenLock
logger = get_logger(__file__)
__all__ = [
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
@@ -42,18 +40,15 @@ __all__ = [
]
class NodeViewSet(SuggestionMixin, OrgModelViewSet):
class NodeViewSet(SuggestionMixin, OrgBulkModelViewSet):
model = Node
filterset_fields = ('value', 'key', 'id')
search_fields = ('value', )
permission_classes = (IsOrgAdmin,)
search_fields = ('value',)
serializer_class = serializers.NodeSerializer
# 仅支持根节点指直接创建子节点下的节点需要通过children接口创建
def perform_create(self, serializer):
child_key = Node.org_root().get_next_child_key()
serializer.validated_data["key"] = child_key
serializer.save()
rbac_perms = {
'match': 'assets.match_node',
'check_assets_amount_task': 'assets.change_node'
}
@action(methods=[POST], detail=False, url_path='check_assets_amount_task')
def check_assets_amount_task(self, request):
@@ -91,7 +86,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
]
"""
model = Node
permission_classes = (IsOrgAdmin,)
serializer_class = TreeNodeSerializer
@staticmethod
@@ -106,7 +100,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
class NodeChildrenApi(generics.ListCreateAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeSerializer
instance = None
is_initial = False
@@ -205,7 +198,6 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
class NodeAssetsApi(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSerializer
def get_queryset(self):
@@ -220,7 +212,6 @@ class NodeAssetsApi(generics.ListAPIView):
class NodeAddChildrenApi(generics.UpdateAPIView):
model = Node
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeAddChildrenSerializer
instance = None
@@ -237,7 +228,6 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
class NodeAddAssetsApi(generics.UpdateAPIView):
model = Node
serializer_class = serializers.NodeAssetsSerializer
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
@@ -249,7 +239,6 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
class NodeRemoveAssetsApi(generics.UpdateAPIView):
model = Node
serializer_class = serializers.NodeAssetsSerializer
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
@@ -268,7 +257,6 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
class MoveAssetsToNodeApi(generics.UpdateAPIView):
model = Node
serializer_class = serializers.NodeAssetsSerializer
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
@@ -309,9 +297,21 @@ class MoveAssetsToNodeApi(generics.UpdateAPIView):
class NodeTaskCreateApi(generics.CreateAPIView):
perm_model = Asset
model = Node
serializer_class = serializers.NodeTaskSerializer
permission_classes = (IsOrgAdmin,)
def check_permissions(self, request):
action = request.data.get('action')
action_perm_require = {
'refresh': 'assets.refresh_assethardwareinfo',
'test': 'assets.test_assetconnectivity'
}
perm_required = action_perm_require.get(action)
has = self.request.user.has_perm(perm_required)
if not has:
self.permission_denied(request)
def get_object(self):
node_id = self.kwargs.get('pk')
@@ -344,4 +344,3 @@ class NodeTaskCreateApi(generics.CreateAPIView):
else:
task = test_node_assets_connectivity_manual.delay(node)
self.set_serializer_data(serializer, task)

View File

@@ -1,18 +1,15 @@
# ~*~ coding: utf-8 ~*~
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from django.db.models import Q
from rest_framework.decorators import action
from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsValidUser
from common.permissions import IsValidUser
from common.mixins.api import SuggestionMixin
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from common.mixins.api import SuggestionMixin
from orgs.utils import tmp_to_root_org
from rest_framework.decorators import action
from users.models import User, UserGroup
from applications.models import Application
from ..models import SystemUser, Asset, CommandFilter, CommandFilterRule
from ..models import SystemUser, CommandFilterRule
from .. import serializers
from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
from ..tasks import (
@@ -47,7 +44,11 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
}
ordering_fields = ('name', 'protocol', 'login_mode')
ordering = ('name', )
permission_classes = (IsOrgAdminOrAppUser,)
rbac_perms = {
'su_from': 'assets.view_systemuser',
'su_to': 'assets.view_systemuser',
'match': 'assets.match_systemuser'
}
@action(methods=['get'], detail=False, url_path='su-from')
def su_from(self, request, *args, **kwargs):
@@ -81,8 +82,13 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
Get system user auth info
"""
model = SystemUser
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = SystemUserWithAuthInfoSerializer
rbac_perms = {
'retrieve': 'assets.view_systemusersecret',
'list': 'assets.view_systemusersecret',
'change': 'assets.change_systemuser',
'destroy': 'assets.change_systemuser',
}
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
@@ -98,14 +104,14 @@ class SystemUserTempAuthInfoApi(generics.CreateAPIView):
def create(self, request, *args, **kwargs):
serializer = super().get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
pk = kwargs.get('pk')
user = self.request.user
data = serializer.validated_data
instance_id = data.get('instance_id')
asset_or_app_id = data.get('instance_id')
with tmp_to_root_org():
instance = get_object_or_404(SystemUser, pk=pk)
instance.set_temp_auth(instance_id, user.id, data)
instance.set_temp_auth(asset_or_app_id, self.request.user.id, data)
return Response(serializer.data, status=201)
@@ -114,7 +120,6 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
Get system user with asset auth info
"""
model = SystemUser
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = SystemUserWithAuthInfoSerializer
def get_object(self):
@@ -131,8 +136,10 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
Get system user with asset auth info
"""
model = SystemUser
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = SystemUserWithAuthInfoSerializer
rbac_perms = {
'retrieve': 'assets.view_systemusersecret',
}
def get_object(self):
instance = super().get_object()
@@ -144,7 +151,6 @@ class SystemUserAppAuthInfoApi(generics.RetrieveAPIView):
class SystemUserTaskApi(generics.CreateAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.SystemUserTaskSerializer
def do_push(self, system_user, asset_ids=None):
@@ -166,6 +172,18 @@ class SystemUserTaskApi(generics.CreateAPIView):
pk = self.kwargs.get('pk')
return get_object_or_404(SystemUser, pk=pk)
def check_permissions(self, request):
action = request.data.get('action')
action_perm_require = {
'push': 'assets.push_assetsystemuser',
'test': 'assets.test_assetconnectivity'
}
perm_required = action_perm_require.get(action)
has = self.request.user.has_perm(perm_required)
if not has:
self.permission_denied(request)
def perform_create(self, serializer):
action = serializer.validated_data["action"]
asset = serializer.validated_data.get('asset')
@@ -189,56 +207,40 @@ class SystemUserTaskApi(generics.CreateAPIView):
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
rbac_perms = {
'list': 'assets.view_commandfilterule'
}
def get_serializer_class(self):
from ..serializers import CommandFilterRuleSerializer
return CommandFilterRuleSerializer
def get_queryset(self):
user_groups = []
user_id = self.request.query_params.get('user_id')
user = get_object_or_none(User, pk=user_id)
if user:
user_groups.extend(list(user.groups.all()))
user_group_id = self.request.query_params.get('user_group_id')
user_group = get_object_or_none(UserGroup, pk=user_group_id)
if user_group:
user_groups.append(user_group)
system_user_id = self.kwargs.get('pk', None)
system_user = get_object_or_none(SystemUser, pk=system_user_id)
if not system_user:
system_user_id = self.request.query_params.get('system_user_id')
system_user = get_object_or_none(SystemUser, pk=system_user_id)
asset_id = self.request.query_params.get('asset_id')
asset = get_object_or_none(Asset, pk=asset_id)
application_id = self.request.query_params.get('application_id')
application = get_object_or_none(Application, pk=application_id)
q = Q()
if user:
q |= Q(users=user)
if user_groups:
q |= Q(user_groups__in=set(user_groups))
if system_user:
q |= Q(system_users=system_user)
if asset:
q |= Q(assets=asset)
if application:
q |= Q(applications=application)
if q:
cmd_filters = CommandFilter.objects.filter(q).filter(is_active=True)
rule_ids = cmd_filters.values_list('rules', flat=True)
rules = CommandFilterRule.objects.filter(id__in=rule_ids)
else:
rules = CommandFilterRule.objects.none()
rules = CommandFilterRule.get_queryset(
user_id=user_id,
user_group_id=user_group_id,
system_user_id=system_user_id,
asset_id=asset_id,
application_id=application_id
)
return rules
class SystemUserAssetsListView(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSimpleSerializer
filterset_fields = ("hostname", "ip")
search_fields = filterset_fields
rbac_perms = {
'list': 'assets.view_asset'
}
def get_object(self):
pk = self.kwargs.get('pk')

View File

@@ -5,7 +5,6 @@ from django.db.models import F, Value, Model
from django.db.models.signals import m2m_changed
from django.db.models.functions import Concat
from common.permissions import IsOrgAdmin
from common.utils import get_logger
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import current_org
@@ -65,13 +64,13 @@ class RelationMixin:
class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet):
pass
perm_model = models.SystemUser
class SystemUserAssetRelationViewSet(BaseRelationViewSet):
perm_model = models.AuthBook
serializer_class = serializers.SystemUserAssetRelationSerializer
model = models.SystemUser.assets.through
permission_classes = (IsOrgAdmin,)
filterset_fields = [
'id', 'asset', 'systemuser',
]
@@ -97,7 +96,6 @@ class SystemUserAssetRelationViewSet(BaseRelationViewSet):
class SystemUserNodeRelationViewSet(BaseRelationViewSet):
serializer_class = serializers.SystemUserNodeRelationSerializer
model = models.SystemUser.nodes.through
permission_classes = (IsOrgAdmin,)
filterset_fields = [
'id', 'node', 'systemuser',
]
@@ -118,7 +116,6 @@ class SystemUserNodeRelationViewSet(BaseRelationViewSet):
class SystemUserUserRelationViewSet(BaseRelationViewSet):
serializer_class = serializers.SystemUserUserRelationSerializer
model = models.SystemUser.users.through
permission_classes = (IsOrgAdmin,)
filterset_fields = [
'id', 'user', 'systemuser',
]
@@ -140,4 +137,3 @@ class SystemUserUserRelationViewSet(BaseRelationViewSet):
)
)
return queryset

View File

@@ -1,11 +1,16 @@
from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from django.apps import AppConfig
class AssetsConfig(AppConfig):
name = 'assets'
verbose_name = _('App assets')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def ready(self):
super().ready()
from . import signals_handler
from . import signal_handlers

View File

@@ -11,5 +11,4 @@
"""
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
from users.models import User, UserGroup

View File

@@ -1,7 +1,7 @@
# Generated by Django 2.1.7 on 2019-06-24 13:08
import assets.models.utils
import common.fields.model
import common.db.fields
from django.db import migrations
@@ -15,61 +15,61 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='adminuser',
name='_password',
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
),
migrations.AlterField(
model_name='adminuser',
name='_private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='adminuser',
name='_public_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
),
migrations.AlterField(
model_name='authbook',
name='_password',
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
),
migrations.AlterField(
model_name='authbook',
name='_private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='authbook',
name='_public_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
),
migrations.AlterField(
model_name='gateway',
name='_password',
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
),
migrations.AlterField(
model_name='gateway',
name='_private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='gateway',
name='_public_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
),
migrations.AlterField(
model_name='systemuser',
name='_password',
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
),
migrations.AlterField(
model_name='systemuser',
name='_private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='systemuser',
name='_public_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
),
]

View File

@@ -1,6 +1,6 @@
# Generated by Django 2.1.7 on 2019-07-11 12:18
import common.fields.model
import common.db.fields
from django.db import migrations
@@ -14,21 +14,21 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='adminuser',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='authbook',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='gateway',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
migrations.AlterField(
model_name='systemuser',
name='private_key',
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
),
]

View File

@@ -1,6 +1,6 @@
# Generated by Django 2.2.7 on 2019-12-06 07:26
import common.fields.model
import common.db.fields
from django.db import migrations, models
@@ -36,7 +36,7 @@ class Migration(migrations.Migration):
('name', models.SlugField(allow_unicode=True, unique=True, verbose_name='Name')),
('base', models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=16, verbose_name='Base')),
('charset', models.CharField(choices=[('utf8', 'UTF-8'), ('gbk', 'GBK')], default='utf8', max_length=8, verbose_name='Charset')),
('meta', common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
('meta', common.db.fields.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
('internal', models.BooleanField(default=False, verbose_name='Internal')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
],

View File

@@ -1,6 +1,6 @@
# Generated by Django 3.1.6 on 2021-06-05 16:10
import common.fields.model
import common.db.fields
from django.conf import settings
import django.core.validators
from django.db import migrations, models
@@ -58,9 +58,9 @@ class Migration(migrations.Migration):
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),

View File

@@ -0,0 +1,62 @@
# Generated by Django 3.1.13 on 2022-01-12 11:59
import common.db.encoder
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('assets', '0083_auto_20211215_1436'),
]
operations = [
migrations.CreateModel(
name='AccountBackupPlan',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False)),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Cycle perform')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Regularly perform')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('types', models.IntegerField(choices=[(255, 'All'), (1, 'Asset'), (2, 'Application')], default=255, verbose_name='Type')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('recipients', models.ManyToManyField(blank=True, related_name='recipient_escape_route_plans', to=settings.AUTH_USER_MODEL, verbose_name='Recipient')),
],
options={
'verbose_name': 'Account backup plan',
'ordering': ['name'],
'unique_together': {('name', 'org_id')},
},
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('redis', 'Redis'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.CreateModel(
name='AccountBackupPlanExecution',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('date_start', models.DateTimeField(auto_now_add=True, verbose_name='Date start')),
('timedelta', models.FloatField(default=0.0, null=True, verbose_name='Time')),
('plan_snapshot', models.JSONField(blank=True, default=dict, encoder=common.db.encoder.ModelJSONFieldEncoder, null=True, verbose_name='Account backup snapshot')),
('trigger', models.CharField(choices=[('manual', 'Manual trigger'), ('timing', 'Timing trigger')], default='manual', max_length=128, verbose_name='Trigger mode')),
('reason', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Reason')),
('is_success', models.BooleanField(default=False, verbose_name='Is success')),
('plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='execution', to='assets.accountbackupplan', verbose_name='Account backup plan')),
],
options={
'verbose_name': 'Account backup execution',
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2022-02-08 02:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0084_auto_20220112_1959'),
]
operations = [
migrations.AddField(
model_name='commandfilterrule',
name='ignore_case',
field=models.BooleanField(default=True, verbose_name='Ignore case'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 3.1.13 on 2022-02-17 13:35
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0085_commandfilterrule_ignore_case'),
]
operations = [
migrations.AlterModelOptions(
name='asset',
options={'ordering': ['hostname'], 'permissions': [('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset')], 'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='authbook',
options={'permissions': [('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret')], 'verbose_name': 'AuthBook'},
),
migrations.AlterModelOptions(
name='label',
options={'verbose_name': 'Label'},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2022-02-23 07:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0086_auto_20220217_2135'),
]
operations = [
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 3.1.14 on 2022-03-03 08:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0087_auto_20220223_1539'),
]
operations = [
migrations.AlterModelOptions(
name='asset',
options={'ordering': ['hostname'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset'), ('match_asset', 'Can match asset')], 'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='node',
options={'ordering': ['parent_key', 'value'], 'permissions': [('match_node', 'Can match node')], 'verbose_name': 'Node'},
),
migrations.AlterModelOptions(
name='systemuser',
options={'ordering': ['name'], 'permissions': [('match_systemuser', 'Can match system user')], 'verbose_name': 'System user'},
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 3.1.14 on 2022-03-09 22:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0088_auto_20220303_1612'),
]
operations = [
migrations.AlterModelOptions(
name='authbook',
options={'permissions': [('test_authbook', 'Can test asset account connectivity'), ('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret')], 'verbose_name': 'AuthBook'},
),
migrations.AlterModelOptions(
name='systemuser',
options={'ordering': ['name'], 'permissions': [('match_systemuser', 'Can match system user')], 'verbose_name': 'System user'},
),
migrations.AlterModelOptions(
name='asset',
options={'ordering': ['hostname'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetsystemuser', 'Can push system user to asset'), ('match_asset', 'Can match asset'), ('add_assettonode', 'Add asset to node'), ('move_assettonode', 'Move asset to node')], 'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='gateway',
options={'permissions': [('test_gateway', 'Test gateway')], 'verbose_name': 'Gateway'},
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 3.1.14 on 2022-04-12 03:45
from django.db import migrations, models
def create_internal_platform(apps, schema_editor):
model = apps.get_model("assets", "Platform")
db_alias = schema_editor.connection.alias
type_platforms = (
('AIX', 'Unix', None),
)
for name, base, meta in type_platforms:
defaults = {'name': name, 'base': base, 'meta': meta, 'internal': True}
model.objects.using(db_alias).update_or_create(
name=name, defaults=defaults
)
class Migration(migrations.Migration):
dependencies = [
('assets', '0089_auto_20220310_0616'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='number',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Asset number'),
),
migrations.RunPython(create_internal_platform)
]

View File

@@ -12,3 +12,4 @@ from .utils import *
from .authbook import *
from .gathered_user import *
from .favorite_asset import *
from .backup import *

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#
import uuid
import logging
@@ -8,11 +8,10 @@ from functools import reduce
from collections import OrderedDict
from django.db import models
from common.db.models import TextChoices
from django.utils.translation import ugettext_lazy as _
from rest_framework.exceptions import ValidationError
from common.fields.model import JsonDictTextField
from common.db.fields import JsonDictTextField
from common.utils import lazyproperty
from orgs.mixins.models import OrgModelMixin, OrgManager
@@ -59,7 +58,7 @@ class AssetQuerySet(models.QuerySet):
class ProtocolsMixin:
protocols = ''
class Protocol(TextChoices):
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
@@ -224,7 +223,7 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
# Some information
public_ip = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Public IP'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
number = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Asset number'))
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
created_by = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Created by'))
@@ -236,6 +235,9 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
def __str__(self):
return '{0.hostname}({0.ip})'.format(self)
def get_target_ip(self):
return self.ip
def set_admin_user_relation(self):
from .authbook import AuthBook
if not self.admin_user:
@@ -281,16 +283,44 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
def is_support_ansible(self):
return self.has_protocol('ssh') and self.platform_base not in ("Other",)
def get_auth_info(self):
def get_auth_info(self, with_become=False):
if not self.admin_user:
return {}
self.admin_user.load_asset_special_auth(self)
if self.is_unixlike() and self.admin_user.su_enabled and self.admin_user.su_from:
auth_user = self.admin_user.su_from
become_user = self.admin_user
else:
auth_user = self.admin_user
become_user = None
auth_user.load_asset_special_auth(self)
info = {
'username': self.admin_user.username,
'password': self.admin_user.password,
'private_key': self.admin_user.private_key_file,
'username': auth_user.username,
'password': auth_user.password,
'private_key': auth_user.private_key_file
}
if not with_become or self.is_windows():
return info
if become_user:
become_user.load_asset_special_auth(self)
become_method = 'su'
become_username = become_user.username
become_pass = become_user.password
else:
become_method = 'sudo'
become_username = 'root'
become_pass = auth_user.password
become_info = {
'become': {
'method': become_method,
'username': become_username,
'pass': become_pass
}
}
info.update(become_info)
return info
def nodes_display(self):
@@ -355,3 +385,11 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
unique_together = [('org_id', 'hostname')]
verbose_name = _("Asset")
ordering = ["hostname", ]
permissions = [
('refresh_assethardwareinfo', _('Can refresh asset hardware info')),
('test_assetconnectivity', _('Can test asset connectivity')),
('push_assetsystemuser', _('Can push system user to asset')),
('match_asset', _('Can match asset')),
('add_assettonode', _('Add asset to node')),
('move_assettonode', _('Move asset to node')),
]

View File

@@ -2,6 +2,7 @@
#
from django.db import models
from django.db.models import F
from django.utils.translation import ugettext_lazy as _
from simple_history.models import HistoricalRecords
@@ -25,6 +26,11 @@ class AuthBook(BaseUser, AbsConnectivity):
class Meta:
verbose_name = _('AuthBook')
unique_together = [('username', 'asset', 'systemuser')]
permissions = [
('test_authbook', _('Can test asset account connectivity')),
('view_assetaccountsecret', _('Can view asset account secret')),
('change_assetaccountsecret', _('Can change asset account secret'))
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -116,6 +122,15 @@ class AuthBook(BaseUser, AbsConnectivity):
self.asset.save()
logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
@classmethod
def get_queryset(cls):
queryset = cls.objects.all() \
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname')) \
.annotate(platform=F('asset__platform__name')) \
.annotate(protocols=F('asset__protocols'))
return queryset
def __str__(self):
return self.smart_name

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import uuid
from celery import current_task
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.models import OrgModelMixin
from ops.mixin import PeriodTaskModelMixin
from common.utils import get_logger
from common.db.encoder import ModelJSONFieldEncoder
from common.db.models import BitOperationChoice
from common.mixins.models import CommonModelMixin
__all__ = ['AccountBackupPlan', 'AccountBackupPlanExecution', 'Type']
logger = get_logger(__file__)
class Type(BitOperationChoice):
NONE = 0
ALL = 0xff
Asset = 0b1
App = 0b1 << 1
DB_CHOICES = (
(ALL, _('All')),
(Asset, _('Asset')),
(App, _('Application'))
)
NAME_MAP = {
ALL: "all",
Asset: "asset",
App: "application"
}
NAME_MAP_REVERSE = {v: k for k, v in NAME_MAP.items()}
CHOICES = []
for i, j in DB_CHOICES:
CHOICES.append((NAME_MAP[i], j))
class AccountBackupPlan(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
types = models.IntegerField(choices=Type.DB_CHOICES, default=Type.ALL, verbose_name=_('Type'))
recipients = models.ManyToManyField(
'users.User', related_name='recipient_escape_route_plans', blank=True,
verbose_name=_("Recipient")
)
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __str__(self):
return f'{self.name}({self.org_id})'
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
verbose_name = _('Account backup plan')
def get_register_task(self):
from ..tasks import execute_account_backup_plan
name = "account_backup_plan_period_{}".format(str(self.id)[:8])
task = execute_account_backup_plan.name
args = (str(self.id), AccountBackupPlanExecution.Trigger.timing)
kwargs = {}
return name, task, args, kwargs
def to_attr_json(self):
return {
'name': self.name,
'is_periodic': self.is_periodic,
'interval': self.interval,
'crontab': self.crontab,
'org_id': self.org_id,
'created_by': self.created_by,
'types': Type.value_to_choices(self.types),
'recipients': {
str(recipient.id): (str(recipient), bool(recipient.secret_key))
for recipient in self.recipients.all()
}
}
def execute(self, trigger):
try:
hid = current_task.request.id
except AttributeError:
hid = str(uuid.uuid4())
execution = AccountBackupPlanExecution.objects.create(
id=hid, plan=self, plan_snapshot=self.to_attr_json(), trigger=trigger
)
return execution.start()
class AccountBackupPlanExecution(OrgModelMixin):
class Trigger(models.TextChoices):
manual = 'manual', _('Manual trigger')
timing = 'timing', _('Timing trigger')
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
date_start = models.DateTimeField(
auto_now_add=True, verbose_name=_('Date start')
)
timedelta = models.FloatField(
default=0.0, verbose_name=_('Time'), null=True
)
plan_snapshot = models.JSONField(
encoder=ModelJSONFieldEncoder, default=dict,
blank=True, null=True, verbose_name=_('Account backup snapshot')
)
trigger = models.CharField(
max_length=128, default=Trigger.manual, choices=Trigger.choices,
verbose_name=_('Trigger mode')
)
reason = models.CharField(
max_length=1024, blank=True, null=True, verbose_name=_('Reason')
)
is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
plan = models.ForeignKey(
'AccountBackupPlan', related_name='execution', on_delete=models.CASCADE,
verbose_name=_('Account backup plan')
)
class Meta:
verbose_name = _('Account backup execution')
@property
def types(self):
types = self.plan_snapshot.get('types')
return types
@property
def recipients(self):
recipients = self.plan_snapshot.get('recipients')
if not recipients:
return []
return recipients.values()
def start(self):
from ..task_handlers import ExecutionManager
manager = ExecutionManager(execution=self)
return manager.run()

View File

@@ -19,7 +19,7 @@ from common.utils import (
)
from common.utils.encode import ssh_pubkey_gen
from common.validators import alphanumeric
from common import fields
from common.db import fields
from orgs.mixins.models import OrgModelMixin

View File

@@ -4,15 +4,19 @@ import uuid
import re
from django.db import models
from django.db.models import Q
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty, get_logger
from users.models import User, UserGroup
from applications.models import Application
from ..models import SystemUser, Asset
from common.utils import lazyproperty, get_logger, get_object_or_none
from orgs.mixins.models import OrgModelMixin
logger = get_logger(__file__)
__all__ = [
'CommandFilter', 'CommandFilterRule'
]
@@ -72,11 +76,16 @@ class CommandFilterRule(OrgModelMixin):
confirm = 2, _('Reconfirm')
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules')
filter = models.ForeignKey(
'CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules'
)
type = models.CharField(max_length=16, default=TYPE_COMMAND, choices=TYPE_CHOICES, verbose_name=_("Type"))
priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)])
priority = models.IntegerField(
default=50, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)]
)
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command"))
ignore_case = models.BooleanField(default=True, verbose_name=_('Ignore case'))
action = models.IntegerField(default=ActionChoices.deny, choices=ActionChoices.choices, verbose_name=_("Action"))
# 动作: 附加字段
# - confirm: 命令复核人
@@ -121,13 +130,16 @@ class CommandFilterRule(OrgModelMixin):
regex.append(r'\b{0}\b'.format(cmd))
else:
regex.append(r'\b{0}'.format(cmd))
s = r'(?i){}'.format('|'.join(regex))
s = r'{}'.format('|'.join(regex))
return s
@staticmethod
def compile_regex(regex):
def compile_regex(regex, ignore_case):
try:
pattern = re.compile(regex)
if ignore_case:
pattern = re.compile(regex, re.IGNORECASE)
else:
pattern = re.compile(regex)
except Exception as e:
error = _('The generated regular expression is incorrect: {}').format(str(e))
logger.error(error)
@@ -135,7 +147,7 @@ class CommandFilterRule(OrgModelMixin):
return True, '', pattern
def match(self, data):
succeed, error, pattern = self.compile_regex(regex=self.pattern)
succeed, error, pattern = self.compile_regex(self.pattern, self.ignore_case)
if not succeed:
return self.ACTION_UNKNOWN, ''
@@ -169,6 +181,46 @@ class CommandFilterRule(OrgModelMixin):
'org_id': org_id,
}
ticket = Ticket.objects.create(**data)
ticket.create_process_map_and_node(self.reviewers.all())
ticket.open(applicant=session.user_obj)
applicant = session.user_obj
assignees = self.reviewers.all()
ticket.create_process_map_and_node(assignees, applicant)
ticket.open(applicant)
return ticket
@classmethod
def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None,
asset_id=None, application_id=None, org_id=None):
user_groups = []
user = get_object_or_none(User, pk=user_id)
if user:
user_groups.extend(list(user.groups.all()))
user_group = get_object_or_none(UserGroup, pk=user_group_id)
if user_group:
org_id = user_group.org_id
user_groups.append(user_group)
system_user = get_object_or_none(SystemUser, pk=system_user_id)
asset = get_object_or_none(Asset, pk=asset_id)
application = get_object_or_none(Application, pk=application_id)
q = Q()
if user:
q |= Q(users=user)
if user_groups:
q |= Q(user_groups__in=set(user_groups))
if system_user:
org_id = system_user.org_id
q |= Q(system_users=system_user)
if asset:
org_id = asset.org_id
q |= Q(assets=asset)
if application:
org_id = application.org_id
q |= Q(applications=application)
if q:
cmd_filters = CommandFilter.objects.filter(q).filter(is_active=True)
if org_id:
cmd_filters = cmd_filters.filter(org_id=org_id)
rule_ids = cmd_filters.values_list('rules', flat=True)
rules = cls.objects.filter(id__in=rule_ids)
else:
rules = cls.objects.none()
return rules

View File

@@ -7,7 +7,6 @@ import random
from django.core.cache import cache
import paramiko
from django.db import models
from django.db.models import TextChoices
from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger
@@ -55,7 +54,7 @@ class Gateway(BaseUser):
UNCONNECTIVE_SILENCE_PERIOD_KEY_TMPL = 'asset_unconnective_gateway_silence_period_{}'
UNCONNECTIVE_SILENCE_PERIOD_BEGIN_VALUE = 60 * 5
class Protocol(TextChoices):
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
@@ -71,6 +70,9 @@ class Gateway(BaseUser):
class Meta:
unique_together = [('name', 'org_id')]
verbose_name = _("Gateway")
permissions = [
('test_gateway', _('Test gateway'))
]
def set_unconnective(self):
unconnective_key = self.UNCONNECTIVE_KEY_TMPL.format(self.id)

View File

@@ -37,3 +37,4 @@ class Label(OrgModelMixin):
class Meta:
db_table = "assets_label"
unique_together = [('name', 'value', 'org_id')]
verbose_name = _('Label')

View File

@@ -558,6 +558,9 @@ class Node(OrgModelMixin, SomeNodesMixin, FamilyMixin, NodeAssetsMixin):
class Meta:
verbose_name = _("Node")
ordering = ['parent_key', 'value']
permissions = [
('match_node', _('Can match node')),
]
def __str__(self):
return self.full_value

View File

@@ -10,7 +10,6 @@ from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.cache import cache
from common.utils import signer, get_object_or_none
from common.db.models import TextChoices
from .base import BaseUser
from .asset import Asset
from .authbook import AuthBook
@@ -23,7 +22,7 @@ logger = logging.getLogger(__name__)
class ProtocolMixin:
protocol: str
class Protocol(TextChoices):
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
@@ -33,6 +32,8 @@ class ProtocolMixin:
mariadb = 'mariadb', 'MariaDB'
postgresql = 'postgresql', 'PostgreSQL'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
k8s = 'k8s', 'K8S'
SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp]
@@ -44,7 +45,9 @@ class ProtocolMixin:
Protocol.rdp
]
APPLICATION_CATEGORY_DB_PROTOCOLS = [
Protocol.mysql, Protocol.oracle, Protocol.mariadb, Protocol.postgresql, Protocol.sqlserver
Protocol.mysql, Protocol.mariadb, Protocol.oracle,
Protocol.postgresql, Protocol.sqlserver,
Protocol.redis, Protocol.mongodb
]
APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [
Protocol.k8s
@@ -130,11 +133,23 @@ class AuthMixin:
self.password = password
def load_app_more_auth(self, app_id=None, username=None, user_id=None):
# 清除认证信息
self._clean_auth_info_if_manual_login_mode()
# 加载临时认证信息
# 先加载临时认证信息
if self.login_mode == self.LOGIN_MANUAL:
self._load_tmp_auth_if_has(app_id, user_id)
return
# Remote app
from applications.models import Application
app = get_object_or_none(Application, pk=app_id)
if app and app.category_remote_app:
# Remote app
self._load_remoteapp_more_auth(app, username, user_id)
return
# Other app
# 更新用户名
from users.models import User
user = get_object_or_none(User, pk=user_id) if user_id else None
@@ -145,17 +160,44 @@ class AuthMixin:
_username = username
self.username = _username
def _load_remoteapp_more_auth(self, app, username, user_id):
asset = app.get_remote_app_asset(raise_exception=False)
if asset:
self.load_asset_more_auth(asset_id=asset.id, username=username, user_id=user_id)
def load_asset_special_auth(self, asset, username=''):
"""
AuthBook 的数据状态
| asset | systemuser | username |
1 | * | * | x |
2 | * | x | * |
当前 AuthBook 只有以上两种状态systemuser 与 username 不会并存。
正常的资产与系统用户关联产生的是第1种状态改密则产生第2种状态。改密之后
只有 username 而没有 systemuser 。
Freq: 关联同一资产的多个系统用户指定同一用户名时,修改用户密码会影响所有系统用户
这里有一个不对称的行为,同名系统用户密码覆盖
当有相同 username 的多个系统用户时,有改密动作之后,所有的同名系统用户都使用最后
一次改动,但如果没有发生过改密,同名系统用户使用的密码还是各自的。
"""
authbooks = list(AuthBook.objects.filter(asset=asset, systemuser=self))
if len(authbooks) == 0:
if username == '':
username = self.username
authbook = AuthBook.objects.filter(
asset=asset, username=username, systemuser__isnull=True
).order_by('-date_created').first()
if not authbook:
authbook = AuthBook.objects.filter(
asset=asset, systemuser=self
).order_by('-date_created').first()
if not authbook:
return None
elif len(authbooks) == 1:
authbook = authbooks[0]
else:
authbooks.sort(key=lambda x: 1 if x.username == username else 0, reverse=True)
authbook = authbooks[0]
authbook.load_auth()
self.password = authbook.password
self.private_key = authbook.private_key
@@ -192,7 +234,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
(LOGIN_MANUAL, _('Manually input'))
)
class Type(TextChoices):
class Type(models.TextChoices):
common = 'common', _('Common user')
admin = 'admin', _('Admin user')
@@ -298,9 +340,12 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
ordering = ['name']
unique_together = [('name', 'org_id')]
verbose_name = _("System user")
permissions = [
('match_systemuser', _('Can match system user')),
]
# Todo: 准备废弃
# Deprecated: 准备废弃
class AdminUser(BaseUser):
"""
A privileged user that ansible can use it to push system user and so on

View File

@@ -0,0 +1,25 @@
from django.utils.translation import ugettext_lazy as _
from users.models import User
from common.tasks import send_mail_attachment_async
class AccountBackupExecutionTaskMsg(object):
subject = _('Notification of account backup route task results')
def __init__(self, name: str, user: User):
self.name = name
self.user = user
@property
def message(self):
name = self.name
if self.user.secret_key:
return _('{} - The account backup passage task has been completed. See the attachment for details').format(name)
return _("{} - The account backup passage task has been completed: the encryption password has not been set - "
"please go to personal information -> file encryption password to set the encryption password").format(name)
def publish(self, attachment_list=None):
send_mail_attachment_async.delay(
self.subject, self.message, [self.user.email], attachment_list
)

View File

@@ -11,3 +11,4 @@ from .cmd_filter import *
from .gathered_user import *
from .favorite_asset import *
from .account import *
from .backup import *

View File

@@ -5,33 +5,64 @@ from assets.models import AuthBook
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import AuthSerializerMixin
from .utils import validate_password_contains_left_double_curly_bracket
from common.utils.encode import ssh_pubkey_gen
from common.drf.serializers import SecretReadableMixin
class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
ip = serializers.ReadOnlyField(label=_("IP"))
hostname = serializers.ReadOnlyField(label=_("Hostname"))
platform = serializers.ReadOnlyField(label=_("Platform"))
protocols = serializers.SerializerMethodField(label=_("Protocols"))
date_created = serializers.DateTimeField(
label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True
)
date_updated = serializers.DateTimeField(
label=_('Date updated'), format="%Y/%m/%d %H:%M:%S", read_only=True
)
class Meta:
model = AuthBook
fields_mini = ['id', 'username', 'ip', 'hostname', 'version']
fields_write_only = ['password', 'private_key', "public_key"]
fields_mini = ['id', 'username', 'ip', 'hostname', 'platform', 'protocols', 'version']
fields_write_only = ['password', 'private_key', "public_key", 'passphrase']
fields_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment']
fields_small = fields_mini + fields_write_only + fields_other
fields_fk = ['asset', 'systemuser', 'systemuser_display']
fields = fields_small + fields_fk
extra_kwargs = {
'username': {'required': True},
'password': {
'write_only': True,
"validators": [validate_password_contains_left_double_curly_bracket]
},
'private_key': {'write_only': True},
'public_key': {'write_only': True},
'systemuser_display': {'label': _('System user display')}
}
ref_name = 'AssetAccountSerializer'
def _validate_gen_key(self, attrs):
private_key = attrs.get('private_key')
if not private_key:
return attrs
password = attrs.get('passphrase')
username = attrs.get('username')
public_key = ssh_pubkey_gen(private_key, password=password, username=username)
attrs['public_key'] = public_key
return attrs
def validate(self, attrs):
attrs = self._validate_gen_key(attrs)
return attrs
def get_protocols(self, v):
""" protocols 是 queryset 中返回的Post 创建成功后返回序列化时没有这个字段 """
if hasattr(v, 'protocols'):
protocols = v.protocols
elif hasattr(v, 'asset') and v.asset:
protocols = v.asset.protocols
else:
protocols = ''
protocols = protocols.replace(' ', ', ')
return protocols
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
@@ -43,8 +74,12 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
return super().to_representation(instance)
class AccountSecretSerializer(AccountSerializer):
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class Meta(AccountSerializer.Meta):
fields_backup = [
'hostname', 'ip', 'platform', 'protocols', 'username', 'password',
'private_key', 'public_key', 'date_created', 'date_updated', 'version'
]
extra_kwargs = {
'password': {'write_only': False},
'private_key': {'write_only': False},

View File

@@ -15,6 +15,7 @@ class AdminUserSerializer(SuS):
SuS.Meta.fields_m2m + \
[
'type', 'protocol', "priority", 'sftp_root', 'ssh_key_fingerprint',
'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment', 'created_by',
]

View File

@@ -5,8 +5,6 @@ from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from users.models import User, UserGroup
from perms.models import AssetPermission
from ..models import Asset, Node, Platform, SystemUser
__all__ = [

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext as _
from rest_framework import serializers
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ops.mixin import PeriodTaskSerializerMixin
from common.utils import get_logger
from .base import TypesField
from ..models import AccountBackupPlan, AccountBackupPlanExecution
logger = get_logger(__file__)
__all__ = ['AccountBackupPlanSerializer', 'AccountBackupPlanExecutionSerializer']
class AccountBackupPlanSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSerializer):
types = TypesField(required=False, allow_null=True, label=_("Actions"))
class Meta:
model = AccountBackupPlan
fields = [
'id', 'name', 'is_periodic', 'interval', 'crontab', 'date_created',
'date_updated', 'created_by', 'periodic_display', 'comment',
'recipients', 'types'
]
extra_kwargs = {
'name': {'required': True},
'periodic_display': {'label': _('Periodic perform')},
'recipients': {'label': _('Recipient'), 'help_text': _(
'Currently only mail sending is supported'
)}
}
class AccountBackupPlanExecutionSerializer(serializers.ModelSerializer):
trigger_display = serializers.ReadOnlyField(
source='get_trigger_display', label=_('Trigger mode')
)
class Meta:
model = AccountBackupPlanExecution
fields = [
'id', 'date_start', 'timedelta', 'plan_snapshot', 'trigger', 'reason',
'is_success', 'plan', 'org_id', 'recipients', 'trigger_display'
]
read_only_fields = (
'id', 'date_start', 'timedelta', 'plan_snapshot', 'trigger', 'reason',
'is_success', 'org_id', 'recipients'
)

View File

@@ -1,15 +1,19 @@
# -*- coding: utf-8 -*-
#
from io import StringIO
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.utils import ssh_pubkey_gen, validate_ssh_private_key
from common.utils import ssh_pubkey_gen, ssh_private_key_gen, validate_ssh_private_key
from common.drf.fields import EncryptedField
from assets.models import Type
from .utils import validate_password_for_ansible
class AuthSerializer(serializers.ModelSerializer):
password = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=1024)
private_key = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=4096)
password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024, label=_('Password'))
private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=16384, label=_('Private key'))
def gen_keys(self, private_key=None, password=None):
if private_key is None:
@@ -28,17 +32,35 @@ class AuthSerializer(serializers.ModelSerializer):
return self.instance
class AuthSerializerMixin:
class AuthSerializerMixin(serializers.ModelSerializer):
password = EncryptedField(
label=_('Password'), required=False, allow_blank=True, allow_null=True, max_length=1024,
validators=[validate_password_for_ansible]
)
private_key = EncryptedField(
label=_('SSH private key'), required=False, allow_blank=True, allow_null=True, max_length=16384
)
passphrase = serializers.CharField(
allow_blank=True, allow_null=True, required=False, max_length=512,
write_only=True, label=_('Key password')
)
def validate_password(self, password):
return password
def validate_private_key(self, private_key):
if not private_key:
return
password = self.initial_data.get("password")
valid = validate_ssh_private_key(private_key, password)
passphrase = self.initial_data.get('passphrase')
passphrase = passphrase if passphrase else None
valid = validate_ssh_private_key(private_key, password=passphrase)
if not valid:
raise serializers.ValidationError(_("private key invalid"))
raise serializers.ValidationError(_("private key invalid or passphrase error"))
private_key = ssh_private_key_gen(private_key, password=passphrase)
string_io = StringIO()
private_key.write_private_key(string_io)
private_key = string_io.getvalue()
return private_key
def validate_public_key(self, public_key):
@@ -50,6 +72,7 @@ class AuthSerializerMixin:
value = validated_data.get(field)
if not value:
validated_data.pop(field, None)
validated_data.pop('passphrase', None)
def create(self, validated_data):
self.clean_auth_fields(validated_data)
@@ -58,3 +81,24 @@ class AuthSerializerMixin:
def update(self, instance, validated_data):
self.clean_auth_fields(validated_data)
return super().update(instance, validated_data)
class TypesField(serializers.MultipleChoiceField):
def __init__(self, *args, **kwargs):
kwargs['choices'] = Type.CHOICES
super().__init__(*args, **kwargs)
def to_representation(self, value):
return Type.value_to_choices(value)
def to_internal_value(self, data):
if data is None:
return data
return Type.choices_to_value(data)
class ActionsDisplayField(TypesField):
def to_representation(self, value):
values = super().to_representation(value)
choices = dict(Type.CHOICES)
return [choices.get(i) for i in values]

View File

@@ -12,13 +12,11 @@ from terminal.models import Session
class CommandFilterSerializer(BulkOrgResourceModelSerializer):
class Meta:
model = CommandFilter
fields_mini = ['id', 'name']
fields_small = fields_mini + [
'org_id', 'org_name',
'is_active',
'org_id', 'org_name', 'is_active',
'date_created', 'date_updated',
'comment', 'created_by',
]
@@ -26,25 +24,32 @@ class CommandFilterSerializer(BulkOrgResourceModelSerializer):
fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications']
fields = fields_small + fields_fk + fields_m2m
extra_kwargs = {
'rules': {'read_only': True}
'rules': {'read_only': True},
'date_created': {'label': _("Date created")},
'date_updated': {'label': _("Date updated")},
}
class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
type_display = serializers.ReadOnlyField(source='get_type_display')
action_display = serializers.ReadOnlyField(source='get_action_display')
type_display = serializers.ReadOnlyField(source='get_type_display', label=_("Type display"))
action_display = serializers.ReadOnlyField(source='get_action_display', label=_("Action display"))
class Meta:
model = CommandFilterRule
fields_mini = ['id']
fields_small = fields_mini + [
'type', 'type_display', 'content', 'pattern', 'priority',
'action', 'action_display', 'reviewers',
'date_created', 'date_updated',
'comment', 'created_by',
'type', 'type_display', 'content', 'ignore_case', 'pattern',
'priority', 'action', 'action_display', 'reviewers',
'date_created', 'date_updated', 'comment', 'created_by',
]
fields_fk = ['filter']
fields = fields_small + fields_fk
extra_kwargs = {
'date_created': {'label': _("Date created")},
'date_updated': {'label': _("Date updated")},
'action_display': {'label': _("Action display")},
'pattern': {'label': _("Pattern")}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -66,7 +71,8 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
regex = CommandFilterRule.construct_command_regex(content)
else:
regex = content
succeed, error, pattern = CommandFilterRule.compile_regex(regex)
ignore_case = self.initial_data.get('ignore_case')
succeed, error, pattern = CommandFilterRule.compile_regex(regex, ignore_case)
if not succeed:
raise serializers.ValidationError(error)
return content

View File

@@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from common.validators import alphanumeric
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.drf.serializers import SecretReadableMixin
from ..models import Domain, Gateway
from .base import AuthSerializerMixin
@@ -43,13 +44,13 @@ class DomainSerializer(BulkOrgResourceModelSerializer):
class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
is_connective = serializers.BooleanField(required=False)
is_connective = serializers.BooleanField(required=False, label=_('Connectivity'))
class Meta:
model = Gateway
fields_mini = ['id', 'name']
fields_write_only = [
'password', 'private_key', 'public_key',
'password', 'private_key', 'public_key', 'passphrase'
]
fields_small = fields_mini + fields_write_only + [
'username', 'ip', 'port', 'protocol',
@@ -67,7 +68,7 @@ class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
}
class GatewayWithAuthSerializer(GatewaySerializer):
class GatewayWithAuthSerializer(SecretReadableMixin, GatewaySerializer):
class Meta(GatewaySerializer.Meta):
extra_kwargs = {
'password': {'write_only': False},

Some files were not shown because too many files have changed in this diff Show More