Compare commits

...

204 Commits

Author SHA1 Message Date
fit2bot
a6a9eb3e70 feat: Update v3.2.0 2023-04-21 18:36:18 +08:00
ibuler
802d6136d6 perf: 账号模版 protocols 过滤 2023-04-21 17:11:46 +08:00
ibuler
7e8bb9752c perf: 优化自定义类型的冲突 2023-04-21 15:20:50 +08:00
feng
3fb0197e99 perf: 创建资产 nodes 可为空 默认 default 2023-04-21 14:57:50 +08:00
ibuler
7127b2da93 perf: 去掉 debug msg 2023-04-21 11:32:21 +08:00
ibuler
48b3699591 perf: 优化支持 自定义 applet
perf: 优化平台
2023-04-21 11:31:49 +08:00
Jiangjie.Bai
f2e7845d4b Merge pull request #10286 from jumpserver/dev
v3.2.0
2023-04-20 18:33:59 +08:00
fit2bot
d75b7c014e perf: 更新模版暂不同步修改账号 (#10285)
Co-authored-by: feng <1304903146@qq.com>
2023-04-20 18:29:31 +08:00
Jiangjie.Bai
b44e6c258f Merge pull request #10284 from jumpserver/dev
v3.2.0
2023-04-20 18:23:12 +08:00
Bai
2ae951e6e6 fix: 修改翻译 2023-04-20 17:53:42 +08:00
fit2bot
10b033ee97 perf: 批量更新资产消息 (#10280)
Co-authored-by: feng <1304903146@qq.com>
2023-04-20 16:22:02 +08:00
ibuler
177d634d85 fix: 修复登录 acl 显示不对 2023-04-20 15:48:56 +08:00
feng
ee122690ff perf: asset date_updated 2023-04-20 15:48:34 +08:00
ibuler
dac708f952 perf: 优化 api doc 报错 2023-04-20 15:47:50 +08:00
老广
75724cbddb Merge pull request #10272 from jumpserver/pr@dev@perf_asset_task_i18n
perf: 优化资产任务的 i18n
2023-04-20 14:45:24 +08:00
老广
4b5d9d3a76 Merge pull request #10273 from jumpserver/pr@dev@account_auto_i18n
perf: account auto i18n
2023-04-20 14:44:54 +08:00
fit2bot
0de6c41406 perf: update templat account (#10274)
Co-authored-by: feng <1304903146@qq.com>
2023-04-20 14:43:40 +08:00
ibuler
b52f18aea6 perf: 修改 i18n 2023-04-20 14:39:31 +08:00
ibuler
be58539df8 perf: 修改支持 i18n 2023-04-20 14:19:13 +08:00
feng
f030638ba4 perf: account auto i18n 2023-04-20 14:10:39 +08:00
ibuler
f496f7d635 perf: 优化资产任务的 i18n 2023-04-20 13:51:41 +08:00
老广
7887548174 Merge pull request #10269 from jumpserver/pr@dev@fix_loong64_build
fix: 修复 loong64 构建失败
2023-04-20 13:22:00 +08:00
老广
8e61dc8e02 Merge pull request #10267 from jumpserver/pr@dev@perf_yaml_support_i18n
perf: yaml 文件支持 i18n
2023-04-20 13:21:22 +08:00
吴小白
651c53a92c fix: 修复 loong64 构建失败 2023-04-20 11:38:50 +08:00
fit2bot
c9ee46c0fb perf: windows 账号可连接性 ansible 刷新 (#10268)
Co-authored-by: feng <1304903146@qq.com>
2023-04-20 11:32:33 +08:00
ibuler
f2d34de161 perf: 修改格式 2023-04-20 11:27:10 +08:00
fit2bot
dc5f7a5c05 fix: 资产 克隆有切换至的账号400 (#10266)
Co-authored-by: feng <1304903146@qq.com>
2023-04-20 11:14:23 +08:00
ibuler
6b3665e8d0 perf: yaml 文件支持 i18n 2023-04-20 11:13:28 +08:00
fit2bot
11ad6ab273 fix: 修复作业中心未开启的acl生效的问题 (#10265)
* fix: 修复作业中心未开启的acl生效的问题

* perf: 优化代码风格

---------

Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2023-04-20 10:33:53 +08:00
jiangweidong
2ba32f6971 perf: 优化自定义改密时需要在命令中包含ssh登录用户的密码问题 2023-04-20 09:52:13 +08:00
Bai
96eb87f935 feat: 服务启动时校验 migrations 文件是否有冲突(DEBUG_DEV) 2023-04-19 14:14:37 +08:00
fit2bot
3afab38c5f perf: 资产平台排序 (#10258)
Co-authored-by: feng <1304903146@qq.com>
2023-04-19 13:21:57 +08:00
fit2bot
9dedce6264 perf: 翻译 (#10257)
Co-authored-by: feng <1304903146@qq.com>
2023-04-19 13:02:24 +08:00
ibuler
4849b2627a perf: 优化一下迁移 2023-04-19 11:13:29 +08:00
ibuler
12adf66f41 perf: 优化账号历史过滤 2023-04-19 11:13:29 +08:00
fit2bot
fc4a77df1a fix: 账号导入500 (#10255)
Co-authored-by: feng <1304903146@qq.com>
2023-04-19 10:57:38 +08:00
fit2bot
3bc8eda66a perf: 更新模版关联更新账号 (#10250)
Co-authored-by: feng <1304903146@qq.com>
2023-04-19 10:18:13 +08:00
Bai
d402780d00 feat: 服务启动时校验 migrations 文件是否有冲突(DEBUG_DEV) 2023-04-18 20:23:56 +08:00
fit2bot
28f08251b3 perf: 修复创建资产时,account 的校验 (#10247)
* perf: 修复创建资产时,account 的校验

* perf: 优化一下提示

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-04-18 20:10:51 +08:00
Aaron3S
ca898ed7b5 perf: 优化代码 2023-04-18 19:54:14 +08:00
Aaron3S
50421a1f89 fix: 修复作业中心选择收藏资产取不到资产的问题 2023-04-18 19:54:14 +08:00
ibuler
a83d1c7c46 perf: 优化 applet host platform 2023-04-18 19:13:04 +08:00
ibuler
389f94d672 perf: 修改 assets serializer 支持 accounts template 2023-04-18 19:12:26 +08:00
fit2bot
f47d0b1a40 perf: 工单发消息失败 (#10242)
Co-authored-by: feng <1304903146@qq.com>
2023-04-18 18:56:17 +08:00
fit2bot
a28239f313 fix: 登录复核刷新后404 (#10240)
Co-authored-by: feng <1304903146@qq.com>
2023-04-18 18:07:54 +08:00
老广
996690fc02 Merge pull request #10235 from jumpserver/pr@dev@perf_asset_set_default_nodes
perf: custom fields 仅custom 类型资产支持
2023-04-18 17:18:53 +08:00
ibuler
8b98bbddaa perf: custom fields 仅custom 类型资产支持 2023-04-18 17:07:01 +08:00
fit2bot
cf197f7efc perf: 创建资产通过账号模版创建账号 (#10234)
Co-authored-by: feng <1304903146@qq.com>
2023-04-18 17:03:49 +08:00
ibuler
5921b2ee8f perf: 修改默认节点 2023-04-18 16:29:41 +08:00
老广
52891bfca3 Merge pull request #10232 from jumpserver/pr@dev@perf_platform_internal_create
perf: 修改 platforms
2023-04-18 16:06:36 +08:00
fit2bot
0856b0cbbe fix: k8s 无密码账号登录 (#10233)
Co-authored-by: feng <1304903146@qq.com>
2023-04-18 15:31:47 +08:00
ibuler
b30e9aedce perf: 优化内置平台创建 2023-04-18 15:06:22 +08:00
ibuler
bacda8248b perf: 修改内置 platform 创建 2023-04-18 14:25:35 +08:00
ibuler
ce38b2263c perf: 修改 platforms 2023-04-18 14:02:24 +08:00
fit2bot
810aff9597 perf: k8s 支持网关 (#10229)
Co-authored-by: feng <1304903146@qq.com>
2023-04-18 11:32:59 +08:00
jiangweidong
cad88560bb perf: 修改OAuth2的access_token前缀格式 2023-04-18 10:29:29 +08:00
ibuler
faff0cd20a perf: 优化创建内置 platform 2023-04-17 18:54:55 +08:00
fit2bot
5a34372ca5 perf: 批量创建账号 没解密 (#10226)
Co-authored-by: feng <1304903146@qq.com>
2023-04-17 17:46:10 +08:00
fit2bot
cff4309b03 fix: export assets (#10224)
Co-authored-by: feng <1304903146@qq.com>
2023-04-17 15:42:46 +08:00
ibuler
024d344f7e perf: 去掉 _autmoation_id 2023-04-17 15:41:35 +08:00
ibuler
20e7efcd70 perf: 优化 platform automation 结构 2023-04-17 15:41:35 +08:00
fit2bot
2b00e6e3a1 fix: 修复资产列表 mini 时报错 (#10220)
Co-authored-by: ibuler <ibuler@qq.com>
2023-04-17 15:10:34 +08:00
fit2bot
b3b7575b0c fix: 批量更新资产账号 错误 secret (#10221)
Co-authored-by: feng <1304903146@qq.com>
2023-04-17 15:07:34 +08:00
Jiangjie.Bai
9109a5e6a2 Merge pull request #10213 from jumpserver/dev
v3.2.0 rc2
2023-04-14 18:33:00 +08:00
jiangweidong
690e01cb78 feat: 支持部分资源的自定义自动化任务(Ping/VerifyAccount/ChangeSecret) (#9947)
* feat: 支持部分资源的自定义自动化任务(Ping/VerifyAccount/ChangeSecret)

* perf: 去掉无用的属性

* perf: 优化自定义改密逻辑

* feat: 支持ssh_key认证

* perf: 去掉无用注释

* perf: 优化

* perf: 优化逻辑

* perf: 优化标题

* perf: 去掉一些无用的函数

* perf: 优化helptext
2023-04-14 18:31:09 +08:00
fit2bot
f07e4e53ec perf: 推送成功后 设置账号可连接性 (#10211)
Co-authored-by: feng <1304903146@qq.com>
2023-04-14 17:53:21 +08:00
fit2bot
b1374c6aba fix: 更新平台 ansible_enabled 未设置 (#10210)
Co-authored-by: feng <1304903146@qq.com>
2023-04-14 17:10:33 +08:00
fit2bot
e0f077b054 fix: k8s api 500 (#10209)
Co-authored-by: feng <1304903146@qq.com>
2023-04-14 16:57:08 +08:00
feng
31653cab11 perf: 单独推送账号 2023-04-14 16:22:54 +08:00
feng
976daaa726 fix: 修复AllTypes to_tree_nodes 方法 2023-04-14 15:39:37 +08:00
feng
b359b1059c fix: 修复所有ansible任务执行失败问题 2023-04-14 14:25:01 +08:00
ibuler
490611c560 perf: 修改 applet 2023-04-14 11:30:04 +08:00
Jiangjie.Bai
8a3a9c87a8 Merge pull request #10201 from jumpserver/dev
v3.2.0 rc1
2023-04-13 21:29:05 +08:00
feng
00fd546776 fix: 修复迁移文件冲突bug 2023-04-13 21:28:12 +08:00
Jiangjie.Bai
68351b1c39 Merge pull request #10196 from jumpserver/dev
v3.2.0 rc1
2023-04-13 19:21:05 +08:00
Bai
21da805e78 fix: fix conflicts 2023-04-13 19:20:18 +08:00
fit2bot
928513edd0 fix: fix conflicts (#10197)
* perf: domain gateway 也添加

* fix: 不支持es8 提示

* perf: 授权过期通知

* fix: 过滤系统用户密码过滤ansible不支持的字符

* perf: 优化 apt (#8398)

* pref: 修改 oracle lib path

* perf: 优化 apt

Co-authored-by: ibuler <ibuler@qq.com>

* fix: 修复授权过期通知bug (#8404)

Co-authored-by: feng626 <1304903146@qq.com>

* fix: 修改推送系统用户提示文案

* feat: add client linux arm64 version

* perf: 优化签名认证

* pref: 优化没有获取到节点的问题

* fix: 修复openid用户登录时默认邮件后缀使用配置项

* fix: 修复华为短信配置错误,前端提示不对的问题

* fix: 修复账号备份失败问题 (#8852)

Co-authored-by: feng626 <1304903146@qq.com>

* perf: 优化加密,没有rsa则不加密

* feat: 支持对开启SSL/TLS的MongoDb数据库改密

* perf: 工单新增相关过滤

* fix: 修复配置mfa失效日期 失效问题 (#8856)

Co-authored-by: feng626 <1304903146@qq.com>

* fix: 修复日志记录到syslog时中文编码问题

* workflow: 修改 Gitee 同步的目的仓库

* fix: 修复导出账号历史翻译信息

---------

Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com>
Co-authored-by: 老广 <ibuler@qq.com>
Co-authored-by: feng626 <1304903146@qq.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com>
Co-authored-by: Bai <baijiangjie@gmail.com>
Co-authored-by: BugKing <wangzhen@fit2cloud.com>
2023-04-13 19:16:46 +08:00
fit2bot
1eb8e40d3e feat: 账号推送附加参数 (#10080)
* feat: 账号推送附加参数

* perf: 通过节点 资产 过滤平台api

* perf: push automation params

* perf: 修改playbook

* perf: params serializer

* perf: 账号推送playbook 调整

* perf: Automation serializer add params field

* perf: params 非必填

* perf: 添加is_params 给前端判断

* perf: is_params bool

* perf: 修改push account ansible逻辑

* perf: 修改获取push_kwargs方法

* perf: platform migrate

* perf: 修改api

* perf: 单个推送

* perf: push account

* perf: 修改asset auto_config

---------

Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2023-04-13 19:02:04 +08:00
jiangweidong
8e81aee1fd perf: luna连接时不显示 WinRM 协议选项 2023-04-13 18:29:21 +08:00
jiangweidong
e12b832992 perf: 关闭SFTP后,luna界面不显示相应选项 (#10186)
* perf: 关闭SFTP后,luna界面不显示相应选项

* perf: 修改默认值

* perf: 增加资产协议冗余字段,减少关联查询

* perf: 修改

* perf: 优化

* perf: 精简

* perf: 删掉空格

* perf: 修改继承类
2023-04-13 17:26:24 +08:00
fit2bot
1aadb760f4 perf: 优化命令长度限制到8m (#10193)
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2023-04-13 15:47:16 +08:00
fit2bot
4654756966 fix: 修复账号克隆 500 (#10192)
Co-authored-by: feng <1304903146@qq.com>
2023-04-13 14:20:07 +08:00
Bai
4c7c8f482d fix: 修复执行批量命令时报错的问题 TypeError:set obiect is not subscriptable 2023-04-12 18:30:34 +08:00
fit2bot
30b89e5cc9 perf: 账号模版更新 (#10184)
Co-authored-by: feng <1304903146@qq.com>
2023-04-12 17:59:13 +08:00
老广
b0365838fb Merge pull request #10183 from O-Jiangweidong/pr@dev@perf_connect_gateway_no_gateway
perf: 当连接资产为网关时,connection_token不返回网域网关信息
2023-04-12 13:07:29 +08:00
jiangweidong
a59f1895a3 perf: 当连接资产为网关时,connection_token不返回网域网关信息 2023-04-12 11:43:06 +08:00
ibuler
59b27822be perf: 添加迁移文件 2023-04-11 19:32:29 +08:00
ibuler
36813f64db perf: 修改 device platform 支持 su 2023-04-11 19:32:29 +08:00
jiangweidong
111296ecd2 fix: 手机号码校验逻辑问题 2023-04-10 18:00:39 +08:00
jiangweidong
b7badc146a fix 2023-04-10 17:36:26 +08:00
jiangweidong
8ff1bae7e6 fix: 手机号可以为空及验证逻辑修改 2023-04-10 17:36:26 +08:00
Eric
b58488a7e9 perf: connection token api 兼容处理 2023-04-10 16:43:31 +08:00
老广
1f63a9675f Merge pull request #10169 from jumpserver/pr@dev@fix_migrate_error
perf: 优化 custom info
2023-04-10 15:19:38 +08:00
ibuler
907fcd7555 perf: 优化 custom info 2023-04-10 15:18:27 +08:00
Bai
616e636837 fix: 修复手机号字段问题 2023-04-10 14:36:22 +08:00
ibuler
34e846927b perf: 优化 connect token asset info 2023-04-10 13:47:38 +08:00
fit2bot
1248458451 perf: 优化支持 choices (#10151)
* perf: 支持自定义类型资产

* perf: 改名前

* perf: 优化支持 choices

* perf: 优化自定义资产

* perf: 优化资产的详情

* perf: 修改完成自定义平台和资产

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
2023-04-10 10:57:44 +08:00
wulabing
cec176cc33 fix notifications.py
fix ops.tasks.check_server_performance_period AttributeError: type object 'Status' has no attribute 'get_terminal_latest_stat'
2023-04-10 10:35:16 +08:00
jiangweidong
7833433d5f feat: 手机号支持选择区号 2023-04-10 10:33:31 +08:00
halo
ec2c8538d9 fix: 首次登录强制开启MFA页面bug 2023-04-07 10:19:49 +08:00
fit2bot
e34fbce082 perf: patch account 400 (#10153)
Co-authored-by: feng <1304903146@qq.com>
2023-04-06 19:53:01 +08:00
fit2bot
fb1978a40b fix: terminal status (#10142)
Co-authored-by: feng <1304903146@qq.com>
2023-04-06 10:31:41 +08:00
fit2bot
47d0882090 perf: 用户添加密码 设置是否已存在 (#10138)
Co-authored-by: feng <1304903146@qq.com>
2023-04-04 18:02:58 +08:00
fit2bot
7c1e92c787 fix: 更新账号 跳过name检查 (#10136)
Co-authored-by: feng <1304903146@qq.com>
2023-04-04 16:04:44 +08:00
老广
9af2974bad Merge pull request #10104 from O-Jiangweidong/pr@dev@feat_windows_winrm
feat: Windows类型资产增加winrm协议
2023-04-04 14:08:34 +08:00
Eric_Lee
ba5ca3532b Merge pull request #10133 from jumpserver/pr@dev@fix_deploy_applet
fix: 修正 applet 部署失败
2023-04-04 12:43:49 +08:00
吴小白
211963a098 fix: 修正 applet 部署失败 2023-04-04 12:32:54 +08:00
jiangweidong
187c1e3804 perf: 优化winrm协议网域连接支持ssh_key 2023-04-04 11:55:07 +08:00
Bai
55774dae02 fix: 修复Luna页面用户授权树搜索问题(同步加载方式) 2023-04-04 11:53:01 +08:00
Bai
00ec9b6d5a fix: 修复Luna页面用户授权树默认展开所有节点的问题(同步加载方式) 2023-04-04 11:31:38 +08:00
老广
98a2d9ffdb Merge pull request #10127 from jumpserver/pr@dev@fix_systemuser_without_username_migrate_error
fix: 优化系统用户迁移
2023-04-04 11:16:55 +08:00
ibuler
2b8d0a64fb fix: 优化系统用户迁移 2023-04-04 10:31:57 +08:00
fit2bot
3c07667689 perf: 修改 account migrate (#10125)
Co-authored-by: feng <1304903146@qq.com>
2023-04-03 18:53:37 +08:00
Eric
9686c66874 perf: 会话分享记录字段翻译 2023-04-03 18:37:01 +08:00
fit2bot
c5340b5adc perf: 修改 account (#10088)
* perf: 优化账号创建策略

* perf: 修改账号

* perf: 修改 account

* perf: 修改 account

* perf: 修改批量创建

* perf: 修改账号批量创建

* perf: 继续优化账号批量添加

* perf: 优化创建 accounts 的结果

* perf: 优化账号批量返回的格式

* perf: 优化账号

---------

Co-authored-by: ibuler <ibuler@qq.com>
2023-04-03 18:18:31 +08:00
fit2bot
4601bb9e58 perf: 优化mac客户端名字 (#10122)
Co-authored-by: feng <1304903146@qq.com>
2023-04-03 17:50:52 +08:00
老广
7d68148f7a Merge pull request #10110 from jumpserver/dependabot/pip/requirements/redis-4.5.4
build(deps): bump redis from 4.5.3 to 4.5.4 in /requirements
2023-04-03 17:13:46 +08:00
老广
e386e7f33a Merge pull request #10119 from jumpserver/pr@dev@fix_ldapuserimport
fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题
2023-04-03 17:10:39 +08:00
Bai
34c9044d03 fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题 2023-04-03 08:47:21 +00:00
fit2bot
90cbf653ac perf: 优化luna tree title (#10118)
Co-authored-by: feng <1304903146@qq.com>
2023-04-03 16:47:14 +08:00
Eric_Lee
1c93d7f0a3 Merge pull request #10107 from jumpserver/pr@dev@perf_dockerfile
perf: applet 使用 powershell 部署
2023-04-03 15:58:47 +08:00
Eric
d9ad5aee4a perf: 修改默认值和变量名 2023-04-03 15:39:15 +08:00
Eric
1fbaa85178 perf: 修改接口 2023-04-03 15:39:15 +08:00
Eric
789eb0cf36 feat: 协作分享增加读写控制 2023-04-03 15:39:15 +08:00
jiangweidong
cbe384161a perf: 优化一个函数名 2023-04-03 10:17:00 +08:00
jiangweidong
6aaa20ba17 Perf: 优化 2023-04-03 09:57:40 +08:00
dependabot[bot]
8b6a64d8ed build(deps): bump redis from 4.5.3 to 4.5.4 in /requirements
Bumps [redis](https://github.com/redis/redis-py) from 4.5.3 to 4.5.4.
- [Release notes](https://github.com/redis/redis-py/releases)
- [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/redis/redis-py/compare/v4.5.3...v4.5.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-31 14:31:35 +00:00
jiangweidong
4c5e47cb99 perf: 普通用户工单申请时,选择指定账号,提示无权限 2023-03-30 16:26:12 +08:00
jiangweidong
cfe0206179 feat: winrm协议支持网域自动化 2023-03-30 14:58:20 +08:00
吴小白
caef6a5052 perf: applet 使用 powershell 部署 2023-03-30 12:09:03 +08:00
老广
0cd6667ede Merge pull request #10079 from jumpserver/pr@dev@perf_super_switch
perf: 支持 super 方式的切换用户
2023-03-30 10:24:43 +08:00
老广
d3cc8e5efb Merge pull request #10082 from jumpserver/dependabot/pip/requirements/redis-4.5.3
build(deps): bump redis from 4.3.3 to 4.5.3 in /requirements
2023-03-30 10:23:35 +08:00
jiangweidong
bc186df8d5 Merge branch 'dev' of https://github.com/jumpserver/jumpserver into pr@dev@feat_windows_winrm 2023-03-29 17:11:07 +08:00
jiangweidong
cab72c6991 feat: Windows类型资产增加winrm协议 2023-03-29 17:10:58 +08:00
Bai
8acfcda956 perf: issues 模版 2023-03-29 15:19:17 +08:00
Eric
344451ba55 perf: navicat 移到企业版 2023-03-29 14:56:20 +08:00
feng
678df5bf3e perf: 补回get_terminal_latest_stat 方法 2023-03-29 14:03:35 +08:00
老广
f214b47306 Merge pull request #10094 from jumpserver/pr@dev@perf_dockerfile
perf: 使用 docker.io 仓库
2023-03-29 10:06:42 +08:00
吴小白
5b017daba1 perf: 使用 docker.io 仓库 2023-03-29 10:03:14 +08:00
fit2bot
8d3319717e perf: 开启安全模式后过滤root administrator (#10089)
Co-authored-by: feng <1304903146@qq.com>
2023-03-28 16:26:40 +08:00
fit2bot
23b13db9e2 perf: category order (#10087)
Co-authored-by: feng <1304903146@qq.com>
2023-03-28 15:04:24 +08:00
Bai
3fa1b46312 fix: 修复授权规则Util类 2023-03-28 11:39:19 +08:00
Eric_Lee
1cad4a7add Merge pull request #10084 from jumpserver/pr@dev@fix_ssh_config
fix: 修正错误的 ssh 参数配置
2023-03-28 11:12:41 +08:00
吴小白
d04a0ff5d7 fix: 修正错误的 ssh 参数配置 2023-03-28 11:11:15 +08:00
dependabot[bot]
616e1ded20 build(deps): bump redis from 4.3.3 to 4.5.3 in /requirements
Bumps [redis](https://github.com/redis/redis-py) from 4.3.3 to 4.5.3.
- [Release notes](https://github.com/redis/redis-py/releases)
- [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/redis/redis-py/compare/v4.3.3...v4.5.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 22:33:27 +00:00
jiangweidong
5b87470b5c perf: 优化账号活动日志界面的提示及操作日志的字段内容 2023-03-27 18:23:55 +08:00
Eric
118d33fa02 perf: 支持 super 方式的切换用户 2023-03-27 18:06:30 +08:00
Aaron3S
017682b383 perf: 增加我的资产通过 node_id 过滤我的资产 2023-03-27 14:53:39 +08:00
feng
1ac2fec13f feat: 收集账号 可选同步表 2023-03-27 14:13:44 +08:00
Bai
66d368f882 fix: 修改nodes_display required = False 2023-03-24 17:11:03 +08:00
Bai
30ab6836ab fix: 修改 login_button 2023-03-24 17:09:07 +08:00
fit2bot
55e1ef116b perf: clear secret (#10053)
Co-authored-by: feng <1304903146@qq.com>
2023-03-23 16:04:09 +08:00
Bai
5d022c7056 fix: 修复资产导入支持填写节点路径 2023-03-23 16:01:19 +08:00
fit2bot
f6c5c35a2c feat: 账号收集批量同步账号 (#10051)
Co-authored-by: feng <1304903146@qq.com>
2023-03-23 15:24:19 +08:00
老广
d3170e4815 Merge pull request #10050 from jumpserver/pr@dev@chore_readme
perf: 修改 readme
2023-03-23 14:34:42 +08:00
ibuler
3959f4615a perf: 修改 readme 2023-03-23 14:33:33 +08:00
ibuler
772ad7aff7 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2023-03-23 14:18:21 +08:00
fit2bot
166d074adb perf: 修改 port (#10049)
Co-authored-by: ibuler <ibuler@qq.com>
2023-03-23 14:13:08 +08:00
ibuler
f12e6af86e perf: 修改 port 2023-03-23 14:11:26 +08:00
fit2bot
3b45ad0c61 feat: account remove secret (#10045)
Co-authored-by: feng <1304903146@qq.com>
2023-03-23 11:20:48 +08:00
老广
72b731629e Merge pull request #10043 from jumpserver/pr@dev@perf_ssh_negotiate
perf: 支持旧版本 SSH 服务端认证
2023-03-23 10:03:08 +08:00
吴小白
f9b83b11fb perf: 支持旧版本 SSH 服务端认证 2023-03-23 08:58:31 +08:00
老广
4b8fd64c5d Merge pull request #10041 from jumpserver/pr@dev@feat_k8s_support_gateway
feat: k8s 支持 gateway
2023-03-22 19:43:54 +08:00
ibuler
e3bd698baf feat: k8s 支持 gateway 2023-03-22 19:42:51 +08:00
fit2bot
0be3cb3c27 fix: account update (#10039)
Co-authored-by: feng <1304903146@qq.com>
2023-03-22 19:14:43 +08:00
fit2bot
f7ae23f7d9 perf: 导入资产账号模版api优化 (#10038)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2023-03-22 18:35:23 +08:00
老广
402c68edd0 Merge pull request #10036 from jumpserver/pr@dev@perf_device_add_sudo
perf: 优化一下
2023-03-22 17:21:27 +08:00
ibuler
4f703e2b31 merge: with dev 2023-03-22 17:20:56 +08:00
ibuler
1e0a6b5072 perf: 优化一下 2023-03-22 17:17:49 +08:00
ibuler
47c207ce13 perf: 硬件设备支持账号切换 2023-03-22 16:49:29 +08:00
ibuler
c6071740b1 perf: 硬件设备支持账号切换 2023-03-22 16:43:00 +08:00
老广
463d54a4d8 Merge pull request #10023 from jumpserver/pr@dev@asset_accounts_secret_type_default
perf: 设置资产账号的默认值,方便导入
2023-03-22 15:38:26 +08:00
老广
8289e4c2c8 Merge pull request #10032 from jumpserver/pr@dev@platform_set_protocols
perf: 修改 platform protocols
2023-03-22 15:38:01 +08:00
ibuler
aca0d62feb perf: 优化 protocols 2023-03-22 15:28:05 +08:00
ibuler
59d9572d07 perf: 优化 protocol 选择 2023-03-22 15:26:23 +08:00
ibuler
ba076f6612 perf: 优化提示 2023-03-22 14:56:20 +08:00
fit2bot
43d805d0ca perf: 配置CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED 对改密的特权账号过滤 (#10033)
Co-authored-by: feng <1304903146@qq.com>
2023-03-22 14:48:05 +08:00
ibuler
180ded1773 perf: 修改 platform protocols 2023-03-22 14:15:25 +08:00
fit2bot
81b04c449a fix: tanslate (#10031)
Co-authored-by: feng <1304903146@qq.com>
2023-03-22 13:47:12 +08:00
fit2bot
ed4a4ceca1 perf: 批量创建账号 定义创建账号策略 忽略或抛出错误 (#10028)
Co-authored-by: feng <1304903146@qq.com>
2023-03-22 11:09:48 +08:00
老广
8e61b53460 Merge pull request #10016 from jumpserver/pr@dev@use_ghcr_registry
perf: 使用 ghcr.io 托管镜像
2023-03-21 14:17:31 +08:00
ibuler
cac59db1ec perf: 设置资产账号的默认值,方便导入 2023-03-21 12:43:54 +08:00
fit2bot
9413fd4cd1 perf: 支持 iframe 标签选择 (#9908)
* perf: 支持 iframe 标签选择

* perf: 完善 iframe 的选择语法

---------

Co-authored-by: Eric <xplzv@126.com>
2023-03-20 17:26:47 +08:00
jiangweidong
bac296f82e Merge pull request #10015 from O-Jiangweidong/pr@dev@perf_dbeaver_no_upgrade_no_download
perf: DBeaver连接时不检查更新、不提示创建实例数据库、不弹出下载驱动框
2023-03-20 16:01:37 +08:00
fit2bot
69cd7bce17 perf: 批量创建账号时,跳过unique检查 不去创建 (#9966)
Co-authored-by: feng <1304903146@qq.com>
2023-03-20 15:45:21 +08:00
吴小白
664ab0797a perf: 使用 ghcr.io 托管镜像 2023-03-20 14:22:33 +08:00
老广
4a55c55022 Merge pull request #10003 from jumpserver/pr@dev@perf_import_export
perf: 优化导入导出
2023-03-20 10:07:48 +08:00
ibuler
44b6fd8771 fix: 去掉 warning 2023-03-20 10:05:29 +08:00
jiangweidong
b6ccc53a71 perf: DBeaver连接时不检查更新、不提示创建实例数据库、不弹出下载驱动框 2023-03-20 10:02:50 +08:00
ibuler
209f0d72b4 perf: 去掉 warning 2023-03-20 10:01:42 +08:00
ibuler
eac4b41783 perf: 优化 warning 2023-03-20 09:59:34 +08:00
老广
7a35309e88 Merge pull request #10009 from WeiZhixiong/dev
fix: SyntaxWarning, apps/common/drf/parsers/base.py:114, "is" should be "=="
2023-03-20 09:51:40 +08:00
fit2bot
39e618c127 perf: 资产批量更新平台 (#10013)
Co-authored-by: feng <1304903146@qq.com>
2023-03-19 23:55:16 +08:00
WeiZhixiong
8e33c6f422 fix: SyntaxWarning, apps/common/drf/parsers/base.py:114, "is" should be "==" 2023-03-18 23:19:51 +08:00
ibuler
f5523aaf7b perf: 优化导入导出 2023-03-17 10:45:45 +00:00
ibuler
12db64ea18 perf: 优化导入导出 2023-03-17 18:44:21 +08:00
fit2bot
1acfdf0398 perf: 批量推送账号 分批处理 (#10000)
Co-authored-by: feng <1304903146@qq.com>
2023-03-17 17:10:10 +08:00
老广
074c9c85b1 Merge pull request #9999 from jumpserver/pr@dev@perf_ansible_config
perf: 优化 ansible 写法
2023-03-17 17:03:46 +08:00
ibuler
c094bce71e perf: 优化 ansible 写法 2023-03-17 16:57:40 +08:00
ibuler
cad6fffd74 perf: 优化 Ansible 账号选择 2023-03-16 19:09:29 +08:00
Bai
93a7cee4de fix: 修复导出账号历史翻译信息 2023-03-16 11:18:41 +08:00
老广
1cfdfacdf7 Merge pull request #9982 from wan92hen/patch-1
workflow: 修改 Gitee 同步的目的仓库
2023-03-16 10:45:21 +08:00
BugKing
8b6c2f4cc6 workflow: 修改 Gitee 同步的目的仓库 2023-03-16 09:56:25 +08:00
Bai
41edeb9027 fix: 修复日志记录到syslog时中文编码问题 2023-03-15 19:46:01 +08:00
215 changed files with 6200 additions and 2465 deletions

View File

@@ -8,4 +8,4 @@ celerybeat.pid
### Vagrant ###
.vagrant/
apps/xpack/.git
.history/

View File

@@ -3,7 +3,10 @@ name: 需求建议
about: 提出针对本项目的想法和建议
title: "[Feature] "
labels: 类型:需求
assignees: ibuler
assignees:
- ibuler
- baijiangjie
---

View File

@@ -3,7 +3,9 @@ name: Bug 提交
about: 提交产品缺陷帮助我们更好的改进
title: "[Bug] "
labels: 类型:bug
assignees: wojiushixiaobai
assignees:
- wojiushixiaobai
- baijiangjie
---

View File

@@ -3,7 +3,9 @@ name: 问题咨询
about: 提出针对本项目安装部署、使用及其他方面的相关问题
title: "[Question] "
labels: 类型:提问
assignees: wojiushixiaobai
assignees:
- wojiushixiaobai
- baijiangjie
---

1
.gitignore vendored
View File

@@ -43,3 +43,4 @@ releashe
/apps/script.py
data/*
test.py
.history/

View File

@@ -55,7 +55,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "set mouse-=a" > ~/.vimrc \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \

View File

@@ -53,7 +53,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "set mouse-=a" > ~/.vimrc \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
@@ -76,8 +76,8 @@ RUN --mount=type=cache,target=/root/.cache/pip \
&& pip install --upgrade setuptools wheel \
&& pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-38.0.4-cp39-cp39-linux_loongarch64.whl \
&& pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp39-cp39-linux_loongarch64.whl \
&& pip install $(grep 'PyNaCl' requirements/requirements.txt) \
&& GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=true pip install grpcio \
&& pip install https://download.jumpserver.org/pypi/simple/PyNaCl/PyNaCl-1.5.0-cp39-cp39-linux_loongarch64.whl \
&& pip install https://download.jumpserver.org/pypi/simple/grpcio/grpcio-1.54.0-cp39-cp39-linux_loongarch64.whl \
&& pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \
&& pip install -r requirements/requirements.txt

1
GITSHA Normal file
View File

@@ -0,0 +1 @@
802d6136d6859657053faa431d8a3f589a42f6e8

View File

@@ -54,6 +54,7 @@ JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运
- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/)
- [产品文档](https://docs.jumpserver.org)
- [在线学习](https://edu.fit2cloud.com/page/2635362)
- [知识库](https://kb.fit2cloud.com/categories/jumpserver)
## 案例研究

View File

@@ -1,20 +1,22 @@
from django.shortcuts import get_object_or_404
from rest_framework.decorators import action
from rest_framework.generics import ListAPIView
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK
from accounts import serializers
from accounts.filters import AccountFilterSet
from accounts.models import Account
from assets.models import Asset, Node
from common.permissions import UserConfirmation, ConfirmType
from common.api import ExtraFilterFieldsMixin
from common.permissions import UserConfirmation, ConfirmType, IsValidUser
from common.views.mixins import RecordViewLogMixin
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
__all__ = [
'AccountViewSet', 'AccountSecretsViewSet',
'AccountHistoriesSecretAPI'
'AccountHistoriesSecretAPI', 'AssetAccountBulkCreateApi',
]
@@ -28,7 +30,7 @@ class AccountViewSet(OrgBulkModelViewSet):
rbac_perms = {
'partial_update': ['accounts.change_account'],
'su_from_accounts': 'accounts.view_account',
'username_suggestions': 'accounts.view_account',
'clear_secret': 'accounts.change_account',
}
@action(methods=['get'], detail=False, url_path='su-from-accounts')
@@ -48,7 +50,10 @@ class AccountViewSet(OrgBulkModelViewSet):
serializer = serializers.AccountSerializer(accounts, many=True)
return Response(data=serializer.data)
@action(methods=['get'], detail=False, url_path='username-suggestions')
@action(
methods=['get'], detail=False, url_path='username-suggestions',
permission_classes=[IsValidUser]
)
def username_suggestions(self, request, *args, **kwargs):
asset_ids = request.query_params.get('assets')
node_keys = request.query_params.get('keys')
@@ -71,6 +76,12 @@ class AccountViewSet(OrgBulkModelViewSet):
usernames = common + others
return Response(data=usernames)
@action(methods=['patch'], detail=False, url_path='clear-secret')
def clear_secret(self, request, *args, **kwargs):
account_ids = request.data.get('account_ids', [])
self.model.objects.filter(id__in=account_ids).update(secret=None)
return Response(status=HTTP_200_OK)
class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet):
"""
@@ -87,7 +98,21 @@ class AccountSecretsViewSet(RecordViewLogMixin, AccountViewSet):
}
class AccountHistoriesSecretAPI(RecordViewLogMixin, ListAPIView):
class AssetAccountBulkCreateApi(CreateAPIView):
serializer_class = serializers.AssetAccountBulkSerializer
rbac_perms = {
'POST': 'accounts.add_account',
}
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.create(serializer.validated_data)
serializer = serializers.AssetAccountBulkSerializerResultSerializer(data, many=True)
return Response(data=serializer.data, status=HTTP_200_OK)
class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, RecordViewLogMixin, ListAPIView):
model = Account.history.model
serializer_class = serializers.AccountHistorySerializer
http_method_names = ['get', 'options']
@@ -99,6 +124,10 @@ class AccountHistoriesSecretAPI(RecordViewLogMixin, ListAPIView):
def get_object(self):
return get_object_or_404(Account, pk=self.kwargs.get('pk'))
@staticmethod
def filter_spm_queryset(resource_ids, queryset):
return queryset.filter(history_id__in=resource_ids)
def get_queryset(self):
account = self.get_object()
histories = account.history.all()

View File

@@ -24,15 +24,16 @@ class AccountsTaskCreateAPI(CreateAPIView):
def perform_create(self, serializer):
data = serializer.validated_data
accounts = data.get('accounts', [])
params = data.get('params')
account_ids = [str(a.id) for a in accounts]
if data['action'] == 'push':
task = push_accounts_to_assets_task.delay(account_ids)
task = push_accounts_to_assets_task.delay(account_ids, params)
else:
account = accounts[0]
asset = account.asset
if not asset.auto_info['ansible_enabled'] or \
not asset.auto_info['ping_enabled']:
if not asset.auto_config['ansible_enabled'] or \
not asset.auto_config['ping_enabled']:
raise NotSupportedTemporarilyError()
task = verify_accounts_connectivity_task.delay(account_ids)

View File

@@ -1,13 +1,13 @@
from django_filters import rest_framework as drf_filters
from assets.const import Protocol
from accounts import serializers
from accounts.models import AccountTemplate
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
from assets.const import Protocol
from common.drf.filters import BaseFilterSet
from common.permissions import UserConfirmation, ConfirmType
from common.views.mixins import RecordViewLogMixin
from common.drf.filters import BaseFilterSet
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
class AccountTemplateFilterSet(BaseFilterSet):
@@ -27,6 +27,8 @@ class AccountTemplateFilterSet(BaseFilterSet):
continue
_st = protocol_secret_type_map[p].get('secret_types', [])
secret_types.update(_st)
if not secret_types:
secret_types = ['password']
queryset = queryset.filter(secret_type__in=secret_types)
return queryset

View File

@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.const import Source
from accounts.filters import GatheredAccountFilterSet
from accounts.models import GatherAccountsAutomation
from accounts.models import GatheredAccount
@@ -50,22 +48,12 @@ class GatheredAccountViewSet(OrgBulkModelViewSet):
'default': serializers.GatheredAccountSerializer,
}
rbac_perms = {
'sync_account': 'assets.add_gatheredaccount',
'sync_accounts': 'assets.add_gatheredaccount',
}
@action(methods=['post'], detail=True, url_path='sync')
def sync_account(self, request, *args, **kwargs):
gathered_account = super().get_object()
asset = gathered_account.asset
username = gathered_account.username
accounts = asset.accounts.filter(username=username)
if accounts.exists():
accounts.update(source=Source.COLLECTED)
else:
asset.accounts.model.objects.create(
asset=asset, username=username,
name=f'{username}-{_("Collected")}',
source=Source.COLLECTED
)
@action(methods=['post'], detail=False, url_path='sync-accounts')
def sync_accounts(self, request, *args, **kwargs):
gathered_account_ids = request.data.get('gathered_account_ids')
gathered_accounts = self.model.objects.filter(id__in=gathered_account_ids)
self.model.sync_accounts(gathered_accounts)
return Response(status=status.HTTP_201_CREATED)

View File

@@ -0,0 +1,40 @@
- hosts: custom
gather_facts: no
vars:
ansible_connection: local
tasks:
- name: Test privileged account
ssh_ping:
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"
register: ping_info
- name: Change asset password
custom_command:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
commands: "{{ params.commands }}"
first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}"
when: ping_info is succeeded
register: change_info
- name: Verify password
ssh_ping:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
when:
- ping_info is succeeded
- change_info is succeeded

View File

@@ -0,0 +1,19 @@
id: change_secret_by_ssh
name: "{{ 'SSH account change secret' | trans }}"
category:
- device
- host
type:
- all
method: change_secret
params:
- name: commands
type: list
label: '自定义命令'
default: [ '' ]
help_text: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,<br />请使用 &#123;username&#125;、&#123;password&#125;、&#123;login_password&#125;格式,执行任务时会进行替换 。<br />比如针对 Cisco 主机进行改密,一般需要配置五条命令:<br />1. enable<br />2. &#123;login_password&#125;<br />3. configure terminal<br />4. username &#123;username&#125; privilege 0 password &#123;password&#125; <br />5. end'
i18n:
SSH account change secret:
zh: SSH 账号改密
ja: SSH アカウントのパスワード変更

View File

@@ -1,6 +1,11 @@
id: change_secret_mongodb
name: Change secret for MongoDB
name: "{{ 'MongoDB account change secret' | trans }}"
category: database
type:
- mongodb
method: change_secret
i18n:
MongoDB account change secret:
zh: MongoDB 账号改密
ja: MongoDB アカウントのパスワード変更

View File

@@ -1,7 +1,12 @@
id: change_secret_mysql
name: Change secret for MySQL
name: "{{ 'MySQL account change secret' | trans }}"
category: database
type:
- mysql
- mariadb
method: change_secret
i18n:
MySQL account change secret:
zh: MySQL 账号改密
ja: MySQL アカウントのパスワード変更

View File

@@ -1,6 +1,11 @@
id: change_secret_oracle
name: Change secret for Oracle
name: "{{ 'Oracle account change secret' | trans }}"
category: database
type:
- oracle
method: change_secret
i18n:
Oracle account change secret:
zh: Oracle 账号改密
ja: Oracle アカウントのパスワード変更

View File

@@ -1,6 +1,11 @@
id: change_secret_postgresql
name: Change secret for PostgreSQL
name: "{{ 'PostgreSQL account change secret' | trans }}"
category: database
type:
- postgresql
method: change_secret
i18n:
PostgreSQL account change secret:
zh: PostgreSQL 账号改密
ja: PostgreSQL アカウントのパスワード変更

View File

@@ -1,6 +1,11 @@
id: change_secret_sqlserver
name: Change secret for SQLServer
name: "{{ 'SQLServer account change secret' | trans }}"
category: database
type:
- sqlserver
method: change_secret
i18n:
SQLServer account change secret:
zh: SQLServer 账号改密
ja: SQLServer アカウントのパスワード変更

View File

@@ -18,18 +18,18 @@
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ kwargs.dest }}"
regexp: "{{ kwargs.regexp }}"
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- kwargs.strategy == "set_jms"
- ssh_params.strategy == "set_jms"
- name: Change SSH key
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ kwargs.exclusive }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
- name: Refresh connection

View File

@@ -1,6 +1,11 @@
id: change_secret_aix
name: Change secret for aix
name: "{{ 'AIX account change secret' | trans }}"
category: host
type:
- AIX
method: change_secret
i18n:
AIX account change secret:
zh: AIX 账号改密
ja: AIX アカウントのパスワード変更

View File

@@ -18,18 +18,18 @@
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ kwargs.dest }}"
regexp: "{{ kwargs.regexp }}"
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- kwargs.strategy == "set_jms"
- ssh_params.strategy == "set_jms"
- name: Change SSH key
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ kwargs.exclusive }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
- name: Refresh connection

View File

@@ -1,7 +1,12 @@
id: change_secret_posix
name: Change secret for posix
name: "{{ 'Posix account change secret' | trans }}"
category: host
type:
- unix
- linux
method: change_secret
i18n:
Posix account change secret:
zh: Posix 账号改密
ja: Posix アカウントのパスワード変更

View File

@@ -1,7 +1,12 @@
id: change_secret_local_windows
name: Change secret local account for Windows
name: "{{ 'Windows account change secret' | trans }}"
version: 1
method: change_secret
category: host
type:
- windows
i18n:
Windows account change secret:
zh: Windows 账号改密
ja: Windows アカウントのパスワード変更

View File

@@ -42,7 +42,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
def method_type(cls):
return AutomationTypes.change_secret
def get_kwargs(self, account, secret, secret_type):
def get_ssh_params(self, account, secret, secret_type):
kwargs = {}
if secret_type != SecretType.SSH_KEY:
return kwargs
@@ -76,6 +76,11 @@ class ChangeSecretManager(AccountBasePlaybookManager):
accounts = accounts.filter(id__in=self.account_ids)
if self.secret_type:
accounts = accounts.filter(secret_type=self.secret_type)
if settings.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED:
accounts = accounts.filter(privileged=False).exclude(
username__in=['root', 'administrator']
)
return accounts
def host_callback(
@@ -106,6 +111,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
print(f'Windows {asset} does not support ssh key push')
return inventory_hosts
host['ssh_params'] = {}
for account in accounts:
h = deepcopy(host)
secret_type = account.secret_type
@@ -124,7 +130,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
h['kwargs'] = self.get_kwargs(account, new_secret, secret_type)
h['ssh_params'].update(self.get_ssh_params(account, new_secret, secret_type))
h['account'] = {
'name': account.name,
'username': account.username,

View File

@@ -1,6 +1,11 @@
id: gather_accounts_mongodb
name: Gather account from MongoDB
name: "{{ 'MongoDB account gather' | trans }}"
category: database
type:
- mongodb
method: gather_accounts
i18n:
MongoDB account gather:
zh: MongoDB 账号收集
ja: MongoDB アカウントの収集

View File

@@ -1,7 +1,12 @@
id: gather_accounts_mysql
name: Gather account from MySQL
name: "{{ 'MySQL account gather' | trans }}"
category: database
type:
- mysql
- mariadb
method: gather_accounts
i18n:
MySQL account gather:
zh: MySQL 账号收集
ja: MySQL アカウントの収集

View File

@@ -1,6 +1,11 @@
id: gather_accounts_oracle
name: Gather account from Oracle
name: "{{ 'Oracle account gather' | trans }}"
category: database
type:
- oracle
method: gather_accounts
i18n:
Oracle account gather:
zh: Oracle 账号收集
ja: Oracle アカウントの収集

View File

@@ -1,6 +1,11 @@
id: gather_accounts_postgresql
name: Gather account for PostgreSQL
name: "{{ 'PostgreSQL account gather' | trans }}"
category: database
type:
- postgresql
method: gather_accounts
i18n:
PostgreSQL account gather:
zh: PostgreSQL 账号收集
ja: PostgreSQL アカウントの収集

View File

@@ -1,7 +1,12 @@
id: gather_accounts_posix
name: Gather posix account
name: "{{ 'Posix account gather' | trans }}"
category: host
type:
- linux
- unix
method: gather_accounts
i18n:
Posix account gather:
zh: Posix 账号收集
ja: Posix アカウントの収集

View File

@@ -1,7 +1,12 @@
id: gather_accounts_windows
name: Gather account windows
name: "{{ 'Windows account gather' | trans }}"
version: 1
method: gather_accounts
category: host
type:
- windows
i18n:
Windows account gather:
zh: Windows 账号收集
ja: Windows アカウントの収集

View File

@@ -12,6 +12,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.host_asset_mapper = {}
self.is_sync_account = self.execution.snapshot.get('is_sync_account')
@classmethod
def method_type(cls):
@@ -25,26 +26,38 @@ class GatherAccountsManager(AccountBasePlaybookManager):
def filter_success_result(self, tp, result):
result = GatherAccountsFilter(tp).run(self.method_id_meta_mapper, result)
return result
@staticmethod
def update_or_create_gathered_accounts(asset, result):
def generate_data(asset, result):
data = []
for username, info in result.items():
d = {'asset': asset, 'username': username, 'present': True}
if info.get('date'):
d['date_last_login'] = info['date']
if info.get('address'):
d['address_last_login'] = info['address'][:32]
data.append(d)
return data
def update_or_create_accounts(self, asset, result):
data = self.generate_data(asset, result)
with tmp_to_org(asset.org_id):
gathered_accounts = []
GatheredAccount.objects.filter(asset=asset, present=True).update(present=False)
for username, data in result.items():
d = {'asset': asset, 'username': username, 'present': True}
if data.get('date'):
d['date_last_login'] = data['date']
if data.get('address'):
d['address_last_login'] = data['address'][:32]
GatheredAccount.objects.update_or_create(
for d in data:
username = d['username']
gathered_account, __ = GatheredAccount.objects.update_or_create(
defaults=d, asset=asset, username=username,
)
gathered_accounts.append(gathered_account)
if not self.is_sync_account:
return
GatheredAccount.sync_accounts(gathered_accounts)
def on_host_success(self, host, result):
info = result.get('debug', {}).get('res', {}).get('info', {})
asset = self.host_asset_mapper.get(host)
if asset and info:
result = self.filter_success_result(asset.type, info)
self.update_or_create_gathered_accounts(asset, result)
self.update_or_create_accounts(asset, result)
else:
logger.error("Not found info".format(host))

View File

@@ -1,30 +1,6 @@
import os
import copy
from accounts.const import AutomationTypes
from assets.automations.methods import get_platform_automation_methods
def copy_change_secret_to_push_account(methods):
push_account = AutomationTypes.push_account
change_secret = AutomationTypes.change_secret
copy_methods = copy.deepcopy(methods)
for method in copy_methods:
if not method['id'].startswith(change_secret):
continue
copy_method = copy.deepcopy(method)
copy_method['method'] = push_account.value
copy_method['id'] = copy_method['id'].replace(
change_secret, push_account
)
copy_method['name'] = copy_method['name'].replace(
'Change secret', 'Push account'
)
methods.append(copy_method)
return methods
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
automation_methods = get_platform_automation_methods(BASE_DIR)
platform_automation_methods = copy_change_secret_to_push_account(automation_methods)
platform_automation_methods = get_platform_automation_methods(BASE_DIR)

View File

@@ -0,0 +1,58 @@
- hosts: mongodb
gather_facts: no
vars:
ansible_python_interpreter: /usr/local/bin/python
tasks:
- name: Test MongoDB connection
mongodb_ping:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
ssl: "{{ jms_asset.spec_info.use_ssl }}"
ssl_ca_certs: "{{ jms_asset.secret_info.ca_cert }}"
ssl_certfile: "{{ jms_asset.secret_info.client_key }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
register: db_info
- name: Display MongoDB version
debug:
var: db_info.server_version
when: db_info is succeeded
- name: Change MongoDB password
mongodb_user:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
ssl: "{{ jms_asset.spec_info.use_ssl }}"
ssl_ca_certs: "{{ jms_asset.secret_info.ca_cert }}"
ssl_certfile: "{{ jms_asset.secret_info.client_key }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
db: "{{ jms_asset.spec_info.db_name }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
when: db_info is succeeded
register: change_info
- name: Verify password
mongodb_ping:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
ssl: "{{ jms_asset.spec_info.use_ssl }}"
ssl_ca_certs: "{{ jms_asset.secret_info.ca_cert }}"
ssl_certfile: "{{ jms_asset.secret_info.client_key }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
when:
- db_info is succeeded
- change_info is succeeded

View File

@@ -0,0 +1,11 @@
id: push_account_mongodb
name: "{{ 'MongoDB account push' | trans }}"
category: database
type:
- mongodb
method: push_account
i18n:
MongoDB account push:
zh: MongoDB 账号推送
ja: MongoDB アカウントのプッシュ

View File

@@ -0,0 +1,43 @@
- hosts: mysql
gather_facts: no
vars:
ansible_python_interpreter: /usr/local/bin/python
db_name: "{{ jms_asset.spec_info.db_name }}"
tasks:
- name: Test MySQL connection
community.mysql.mysql_info:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
filter: version
register: db_info
- name: MySQL version
debug:
var: db_info.version.full
- name: Change MySQL password
community.mysql.mysql_user:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
host: "%"
priv: "{{ account.username + '.*:USAGE' if db_name == '' else db_name + '.*:ALL' }}"
when: db_info is succeeded
register: change_info
- name: Verify password
community.mysql.mysql_info:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
filter: version
when:
- db_info is succeeded
- change_info is succeeded

View File

@@ -0,0 +1,12 @@
id: push_account_mysql
name: "{{ 'MySQL account push' | trans }}"
category: database
type:
- mysql
- mariadb
method: push_account
i18n:
MySQL account push:
zh: MySQL 账号推送
ja: MySQL アカウントのプッシュ

View File

@@ -0,0 +1,44 @@
- hosts: oracle
gather_facts: no
vars:
ansible_python_interpreter: /usr/local/bin/python
tasks:
- name: Test Oracle connection
oracle_ping:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ jms_account.mode }}"
register: db_info
- name: Display Oracle version
debug:
var: db_info.server_version
when: db_info is succeeded
- name: Change Oracle password
oracle_user:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ jms_account.mode }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
when: db_info is succeeded
register: change_info
- name: Verify password
oracle_ping:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
when:
- db_info is succeeded
- change_info is succeeded

View File

@@ -0,0 +1,11 @@
id: push_account_oracle
name: "{{ 'Oracle account push' | trans }}"
category: database
type:
- oracle
method: push_account
i18n:
Oracle account push:
zh: Oracle 账号推送
ja: Oracle アカウントのプッシュ

View File

@@ -0,0 +1,46 @@
- hosts: postgre
gather_facts: no
vars:
ansible_python_interpreter: /usr/local/bin/python
tasks:
- name: Test PostgreSQL connection
community.postgresql.postgresql_ping:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_db: "{{ jms_asset.spec_info.db_name }}"
register: result
failed_when: not result.is_available
- name: Display PostgreSQL version
debug:
var: result.server_version.full
when: result is succeeded
- name: Change PostgreSQL password
community.postgresql.postgresql_user:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
role_attr_flags: LOGIN
when: result is succeeded
register: change_info
- name: Verify password
community.postgresql.postgresql_ping:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
when:
- result is succeeded
- change_info is succeeded
register: result
failed_when: not result.is_available

View File

@@ -0,0 +1,11 @@
id: push_account_postgresql
name: "{{ 'PostgreSQL account push' | trans }}"
category: database
type:
- postgresql
method: push_account
i18n:
PostgreSQL account push:
zh: PostgreSQL 账号推送
ja: PostgreSQL アカウントのプッシュ

View File

@@ -0,0 +1,69 @@
- hosts: sqlserver
gather_facts: no
vars:
ansible_python_interpreter: /usr/local/bin/python
tasks:
- name: Test SQLServer connection
community.general.mssql_script:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: |
SELECT @@version
register: db_info
- name: SQLServer version
set_fact:
info:
version: "{{ db_info.query_results[0][0][0][0].splitlines()[0] }}"
- debug:
var: info
- name: Check whether SQLServer User exist
community.general.mssql_script:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: "SELECT 1 from sys.sql_logins WHERE name='{{ account.username }}';"
when: db_info is succeeded
register: user_exist
- name: Change SQLServer password
community.general.mssql_script:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: "ALTER LOGIN {{ account.username }} WITH PASSWORD = '{{ account.secret }}'; select @@version"
when: user_exist.query_results[0] | length != 0
register: change_info
- name: Add SQLServer user
community.general.mssql_script:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: "CREATE LOGIN {{ account.username }} WITH PASSWORD = '{{ account.secret }}'; select @@version"
when: user_exist.query_results[0] | length == 0
register: change_info
- name: Verify password
community.general.mssql_script:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: |
SELECT @@version
when:
- db_info is succeeded
- change_info is succeeded

View File

@@ -0,0 +1,11 @@
id: push_account_sqlserver
name: "{{ 'SQLServer account push' | trans }}"
category: database
type:
- sqlserver
method: push_account
i18n:
SQLServer account push:
zh: SQLServer 账号推送
ja: SQLServer アカウントのプッシュ

View File

@@ -0,0 +1,93 @@
- hosts: demo
gather_facts: no
tasks:
- name: Test privileged account
ansible.builtin.ping:
- name: Push user
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ '/home/' + account.username }}"
groups: "{{ params.groups }}"
expires: -1
state: present
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
- name: Check home dir exists
ansible.builtin.stat:
path: "{{ '/home/' + account.username }}"
register: home_existed
- name: Set home dir permission
ansible.builtin.file:
path: "{{ '/home/' + account.username }}"
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: "0700"
when:
- home_existed.stat.exists == true
- name: Add user groups
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when: params.groups
- name: Push user password
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: Push SSH key
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
- name: Set sudo setting
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
- name: Verify password
ansible.builtin.ping:
become: no
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
ansible_become: no
when: account.secret_type == "password"
- name: Verify SSH key
ansible.builtin.ping:
become: no
vars:
ansible_user: "{{ account.username }}"
ansible_ssh_private_key_file: "{{ account.private_key_path }}"
ansible_become: no
when: account.secret_type == "ssh_key"

View File

@@ -0,0 +1,29 @@
id: push_account_aix
name: "{{ 'Aix account push' | trans }}"
category: host
type:
- AIX
method: push_account
params:
- name: sudo
type: str
label: 'Sudo'
default: '/bin/whoami'
help_text: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
- name: shell
type: str
label: 'Shell'
default: '/bin/bash'
- name: groups
type: str
label: '用户组'
default: ''
help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
i18n:
Aix account push:
zh: Aix 账号推送
ja: Aix アカウントのプッシュ

View File

@@ -0,0 +1,93 @@
- hosts: demo
gather_facts: no
tasks:
- name: Test privileged account
ansible.builtin.ping:
- name: Push user
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ '/home/' + account.username }}"
groups: "{{ params.groups }}"
expires: -1
state: present
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
- name: Check home dir exists
ansible.builtin.stat:
path: "{{ '/home/' + account.username }}"
register: home_existed
- name: Set home dir permission
ansible.builtin.file:
path: "{{ '/home/' + account.username }}"
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: "0700"
when:
- home_existed.stat.exists == true
- name: Add user groups
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when: params.groups
- name: Push user password
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: Push SSH key
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
- name: Set sudo setting
ansible.builtin.lineinfile:
dest: /etc/sudoers
state: present
regexp: "^{{ account.username }} ALL="
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- params.sudo
- name: Refresh connection
ansible.builtin.meta: reset_connection
- name: Verify password
ansible.builtin.ping:
become: no
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
ansible_become: no
when: account.secret_type == "password"
- name: Verify SSH key
ansible.builtin.ping:
become: no
vars:
ansible_user: "{{ account.username }}"
ansible_ssh_private_key_file: "{{ account.private_key_path }}"
ansible_become: no
when: account.secret_type == "ssh_key"

View File

@@ -0,0 +1,30 @@
id: push_account_posix
name: "{{ 'Posix account push' | trans }}"
category: host
type:
- unix
- linux
method: push_account
params:
- name: sudo
type: str
label: 'Sudo'
default: '/bin/whoami'
help_text: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
- name: shell
type: str
label: 'Shell'
default: '/bin/bash'
help_text: ''
- name: groups
type: str
label: '用户组'
default: ''
help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
i18n:
Posix account push:
zh: Posix 账号推送
ja: Posix アカウントのプッシュ

View File

@@ -0,0 +1,30 @@
- hosts: demo
gather_facts: no
tasks:
- name: Test privileged account
ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Push user password
ansible.windows.win_user:
fullname: "{{ account.username}}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
password_never_expires: yes
groups: "{{ params.groups }}"
groups_action: add
update_password: always
when: account.secret_type == "password"
- name: Refresh connection
ansible.builtin.meta: reset_connection
- name: Verify password
ansible.windows.win_ping:
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password"

View File

@@ -0,0 +1,18 @@
id: push_account_local_windows
name: "{{ 'Windows account push' | trans }}"
version: 1
method: push_account
category: host
type:
- windows
params:
- name: groups
type: str
label: '用户组'
default: 'Users,Remote Desktop Users'
help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
i18n:
Windows account push:
zh: Windows 账号推送
ja: Windows アカウントのプッシュ

View File

@@ -1,6 +1,6 @@
from copy import deepcopy
from accounts.const import AutomationTypes, SecretType
from accounts.const import AutomationTypes, SecretType, Connectivity
from assets.const import HostTypes
from common.utils import get_logger
from ..base.manager import AccountBasePlaybookManager
@@ -31,6 +31,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
print(msg)
return inventory_hosts
host['ssh_params'] = {}
for account in accounts:
h = deepcopy(host)
secret_type = account.secret_type
@@ -49,7 +50,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
h['kwargs'] = self.get_kwargs(account, new_secret, secret_type)
h['ssh_params'].update(self.get_ssh_params(account, new_secret, secret_type))
h['account'] = {
'name': account.name,
'username': account.username,
@@ -73,6 +74,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
return
account.secret = new_secret
account.save(update_fields=['secret'])
account.set_connectivity(Connectivity.OK)
def on_host_error(self, host, error, result):
pass

View File

@@ -0,0 +1,14 @@
- hosts: custom
gather_facts: no
vars:
ansible_connection: local
tasks:
- name: Verify account
ssh_ping:
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"

View File

@@ -0,0 +1,13 @@
id: verify_account_by_ssh
name: "{{ 'SSH account verify' | trans }}"
category:
- device
- host
type:
- all
method: verify_account
i18n:
SSH account verify:
zh: SSH 账号验证
ja: SSH アカウントの検証

View File

@@ -1,6 +1,11 @@
id: verify_account_mongodb
name: Verify account from MongoDB
name: "{{ 'MongoDB account verify' | trans }}"
category: database
type:
- mongodb
method: verify_account
i18n:
MongoDB account verify:
zh: MongoDB 账号验证
ja: MongoDB アカウントの検証

View File

@@ -1,7 +1,12 @@
id: verify_account_mysql
name: Verify account from MySQL
name: "{{ 'MySQL account verify' | trans }}"
category: database
type:
- mysql
- mariadb
method: verify_account
i18n:
MySQL account verify:
zh: MySQL 账号验证
ja: MySQL アカウントの検証

View File

@@ -1,6 +1,11 @@
id: verify_account_oracle
name: Verify account from Oracle
name: "{{ 'Oracle account verify' | trans }}"
category: database
type:
- oracle
method: verify_account
i18n:
Oracle account verify:
zh: Oracle 账号验证
ja: Oracle アカウントの検証

View File

@@ -1,6 +1,11 @@
id: verify_account_postgresql
name: Verify account for PostgreSQL
name: "{{ 'PostgreSQL account verify' | trans }}"
category: database
type:
- postgresql
method: verify_account
i18n:
PostgreSQL account verify:
zh: PostgreSQL 账号验证
ja: PostgreSQL アカウントの検証

View File

@@ -1,6 +1,11 @@
id: verify_account_sqlserver
name: Verify account from SQLServer
name: "{{ 'SQLServer account verify' | trans }}"
category: database
type:
- sqlserver
method: verify_account
i18n:
SQLServer account verify:
zh: SQLServer 账号验证
ja: SQLServer アカウントの検証

View File

@@ -1,7 +1,12 @@
id: verify_account_posix
name: Verify posix account
name: "{{ 'Posix account verify' | trans }}"
category: host
type:
- linux
- unix
method: verify_account
i18n:
Posix account verify:
zh: Posix 账号验证
ja: Posix アカウントの検証

View File

@@ -1,6 +1,9 @@
- hosts: windows
gather_facts: no
tasks:
- name: Refresh connection
ansible.builtin.meta: reset_connection
- name: Verify account
ansible.windows.win_ping:
vars:

View File

@@ -1,7 +1,12 @@
id: verify_account_windows
name: Verify account windows
name: "{{ 'Windows account verify' | trans }}"
version: 1
method: verify_account
category: host
type:
- windows
i18n:
Windows account verify:
zh: Windows 账号验证
ja: Windows アカウントの検証

View File

@@ -18,3 +18,10 @@ class AliasAccount(TextChoices):
class Source(TextChoices):
LOCAL = 'local', _('Local')
COLLECTED = 'collected', _('Collected')
TEMPLATE = 'template', _('Template')
class AccountInvalidPolicy(TextChoices):
SKIP = 'skip', _('Skip')
UPDATE = 'update', _('Update')
ERROR = 'error', _('Failed')

View File

@@ -0,0 +1,22 @@
# Generated by Django 3.2.16 on 2023-03-23 08:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0009_account_usernames_to_ids'),
]
operations = [
migrations.AddField(
model_name='gatheraccountsautomation',
name='is_sync_account',
field=models.BooleanField(blank=True, default=False, verbose_name='Is sync account'),
),
migrations.AddField(
model_name='account',
name='source_id',
field=models.CharField(max_length=128, null=True, blank=True, verbose_name='Source ID'),
),
]

View File

@@ -53,6 +53,7 @@ class Account(AbsConnectivity, BaseAccount):
version = models.IntegerField(default=0, verbose_name=_('Version'))
history = AccountHistoricalRecords(included_fields=['id', 'secret', 'secret_type', 'version'])
source = models.CharField(max_length=30, default=Source.LOCAL, verbose_name=_('Source'))
source_id = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Source ID'))
class Meta:
verbose_name = _('Account')

View File

@@ -28,7 +28,6 @@ class ChangeSecretMixin(models.Model):
default=SSHKeyStrategy.add, verbose_name=_('SSH key change strategy')
)
accounts: list[str] # account usernames
get_all_assets: callable # get all assets
class Meta:

View File

@@ -1,7 +1,9 @@
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from accounts.const import AutomationTypes
from accounts.const import AutomationTypes, Source
from accounts.models import Account
from orgs.mixins.models import JMSOrgBaseModel
from .base import AccountBaseAutomation
@@ -19,6 +21,25 @@ class GatheredAccount(JMSOrgBaseModel):
def address(self):
return self.asset.address
@staticmethod
def sync_accounts(gathered_accounts):
account_objs = []
for gathered_account in gathered_accounts:
asset_id = gathered_account.asset_id
username = gathered_account.username
accounts = Account.objects.filter(
Q(asset_id=asset_id, username=username) |
Q(asset_id=asset_id, name=username)
)
if accounts.exists():
continue
account = Account(
asset_id=asset_id, username=username,
name=username, source=Source.COLLECTED
)
account_objs.append(account)
Account.objects.bulk_create(account_objs)
class Meta:
verbose_name = _('Gather account automation')
unique_together = [
@@ -31,6 +52,17 @@ class GatheredAccount(JMSOrgBaseModel):
class GatherAccountsAutomation(AccountBaseAutomation):
is_sync_account = models.BooleanField(
default=False, blank=True, verbose_name=_("Is sync account")
)
def to_attr_json(self):
attr_json = super().to_attr_json()
attr_json.update({
'is_sync_account': self.is_sync_account,
})
return attr_json
def save(self, *args, **kwargs):
self.type = AutomationTypes.gather_accounts
super().save(*args, **kwargs)

View File

@@ -51,7 +51,8 @@ class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
def to_attr_json(self):
attr_json = super().to_attr_json()
attr_json.update({
'username': self.username
'username': self.username,
'params': self.params,
})
return attr_json

View File

@@ -1,75 +1,179 @@
import uuid
from django.db import IntegrityError
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from accounts.const import SecretType, Source
from accounts.const import SecretType, Source, AccountInvalidPolicy
from accounts.models import Account, AccountTemplate
from accounts.tasks import push_accounts_to_assets_task
from assets.const import Category, AllTypes
from assets.models import Asset
from common.serializers import SecretReadableMixin, BulkModelSerializer
from common.serializers import SecretReadableMixin
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from .base import BaseAccountSerializer
from common.utils import get_logger
from .base import BaseAccountSerializer, AuthValidateMixin
logger = get_logger(__name__)
class AccountSerializerCreateValidateMixin:
from_id: str
template: bool
push_now: bool
replace_attrs: callable
class AccountCreateUpdateSerializerMixin(serializers.Serializer):
template = serializers.PrimaryKeyRelatedField(
queryset=AccountTemplate.objects,
required=False, label=_("Template"), write_only=True
)
push_now = serializers.BooleanField(
default=False, label=_("Push now"), write_only=True
)
params = serializers.JSONField(
decoder=None, encoder=None, required=False, style={'base_template': 'textarea.html'}
)
on_invalid = LabeledChoiceField(
choices=AccountInvalidPolicy.choices, default=AccountInvalidPolicy.ERROR,
write_only=True, label=_('Exist policy')
)
_template = None
class Meta:
fields = ['template', 'push_now', 'params', 'on_invalid']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_initial_value()
def set_initial_value(self):
if not getattr(self, 'initial_data', None):
return
if isinstance(self.initial_data, dict):
initial_data = [self.initial_data]
else:
initial_data = self.initial_data
for data in initial_data:
if not data.get('asset') and not self.instance:
raise serializers.ValidationError({'asset': UniqueTogetherValidator.missing_message})
asset = data.get('asset') or self.instance.asset
self.from_template_if_need(data)
self.set_uniq_name_if_need(data, asset)
def to_internal_value(self, data):
from_id = data.pop('id', None)
ret = super().to_internal_value(data)
self.from_id = from_id
return ret
self.from_template_if_need(data)
return super().to_internal_value(data)
def set_secret(self, attrs):
_id = self.from_id
template = attrs.pop('template', None)
def set_uniq_name_if_need(self, initial_data, asset):
name = initial_data.get('name')
if name is not None:
return
if not name:
name = initial_data.get('username')
if self.instance and self.instance.name == name:
return
if Account.objects.filter(name=name, asset=asset).exists():
name = name + '_' + uuid.uuid4().hex[:4]
initial_data['name'] = name
if _id and template:
account_template = AccountTemplate.objects.get(id=_id)
attrs['secret'] = account_template.secret
elif _id and not template:
account = Account.objects.get(id=_id)
attrs['secret'] = account.secret
return attrs
def from_template_if_need(self, initial_data):
if isinstance(initial_data, str):
return
def validate(self, attrs):
attrs = super().validate(attrs)
return self.set_secret(attrs)
template_id = initial_data.pop('template', None)
if not template_id:
return
if isinstance(template_id, (str, uuid.UUID)):
template = AccountTemplate.objects.filter(id=template_id).first()
else:
template = template_id
if not template:
raise serializers.ValidationError({'template': 'Template not found'})
self._template = template
# Set initial data from template
ignore_fields = ['id', 'date_created', 'date_updated', 'org_id']
field_names = [
field.name for field in template._meta.fields
if field.name not in ignore_fields
]
attrs = {}
for name in field_names:
value = getattr(template, name, None)
if value is None:
continue
attrs[name] = value
initial_data.update(attrs)
@staticmethod
def push_account(instance, push_now):
if not push_now:
def push_account_if_need(instance, push_now, params, stat):
if not push_now or stat not in ['created', 'updated']:
return
push_accounts_to_assets_task.delay([str(instance.id)])
push_accounts_to_assets_task.delay([str(instance.id)], params)
def get_validators(self):
_validators = super().get_validators()
if getattr(self, 'initial_data', None) is None:
return _validators
on_invalid = self.initial_data.get('on_invalid')
if on_invalid == AccountInvalidPolicy.ERROR and not self.parent:
return _validators
_validators = [v for v in _validators if not isinstance(v, UniqueTogetherValidator)]
return _validators
@staticmethod
def do_create(vd):
on_invalid = vd.pop('on_invalid', None)
q = Q()
if vd.get('name'):
q |= Q(name=vd['name'])
if vd.get('username'):
q |= Q(username=vd['username'], secret_type=vd.get('secret_type'))
instance = Account.objects.filter(asset=vd['asset']).filter(q).first()
# 不存在这个资产,不用关系策略
if not instance:
instance = Account.objects.create(**vd)
return instance, 'created'
if on_invalid == AccountInvalidPolicy.SKIP:
return instance, 'skipped'
elif on_invalid == AccountInvalidPolicy.UPDATE:
for k, v in vd.items():
setattr(instance, k, v)
instance.save()
return instance, 'updated'
else:
raise serializers.ValidationError('Account already exists')
def generate_source_data(self, validated_data):
template = self._template
if template is None:
return
validated_data['source'] = Source.TEMPLATE
validated_data['source_id'] = str(template.id)
def create(self, validated_data):
push_now = validated_data.pop('push_now', None)
instance = super().create(validated_data)
self.push_account(instance, push_now)
params = validated_data.pop('params', None)
self.generate_source_data(validated_data)
instance, stat = self.do_create(validated_data)
self.push_account_if_need(instance, push_now, params, stat)
return instance
def update(self, instance, validated_data):
# account cannot be modified
validated_data.pop('username', None)
validated_data.pop('on_invalid', None)
push_now = validated_data.pop('push_now', None)
params = validated_data.pop('params', None)
validated_data['source_id'] = None
instance = super().update(instance, validated_data)
self.push_account(instance, push_now)
self.push_account_if_need(instance, push_now, params, 'updated')
return instance
class AccountSerializerCreateMixin(AccountSerializerCreateValidateMixin, BulkModelSerializer):
template = serializers.BooleanField(
default=False, label=_("Template"), write_only=True
)
push_now = serializers.BooleanField(
default=False, label=_("Push now"), write_only=True
)
has_secret = serializers.BooleanField(label=_("Has secret"), read_only=True)
class AccountAssetSerializer(serializers.ModelSerializer):
platform = ObjectRelatedField(read_only=True)
category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category'))
@@ -77,11 +181,11 @@ class AccountAssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ['id', 'name', 'address', 'type', 'category', 'platform', 'auto_info']
fields = ['id', 'name', 'address', 'type', 'category', 'platform', 'auto_config']
def to_internal_value(self, data):
if isinstance(data, dict):
i = data.get('id')
i = data.get('id') or data.get('pk')
else:
i = data
@@ -91,9 +195,10 @@ class AccountAssetSerializer(serializers.ModelSerializer):
raise serializers.ValidationError(_('Asset not found'))
class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerializer):
asset = AccountAssetSerializer(label=_('Asset'))
source = LabeledChoiceField(choices=Source.choices, label=_("Source"), read_only=True)
has_secret = serializers.BooleanField(label=_("Has secret"), read_only=True)
su_from = ObjectRelatedField(
required=False, queryset=Account.objects, allow_null=True, allow_empty=True,
label=_('Su from'), attrs=('id', 'name', 'username')
@@ -102,27 +207,182 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
class Meta(BaseAccountSerializer.Meta):
model = Account
fields = BaseAccountSerializer.Meta.fields + [
'su_from', 'asset', 'template', 'version',
'push_now', 'source', 'connectivity',
'su_from', 'asset', 'version',
'source', 'source_id', 'connectivity',
] + AccountCreateUpdateSerializerMixin.Meta.fields
read_only_fields = BaseAccountSerializer.Meta.read_only_fields + [
'source', 'source_id', 'connectivity'
]
extra_kwargs = {
**BaseAccountSerializer.Meta.extra_kwargs,
'name': {'required': False, 'allow_null': True},
'name': {'required': False},
}
def validate_name(self, value):
if not value:
value = self.initial_data.get('username')
return value
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset \
.prefetch_related('asset', 'asset__platform', 'asset__platform__automation')
queryset = queryset.prefetch_related(
'asset', 'asset__platform',
'asset__platform__automation'
)
return queryset
class AssetAccountBulkSerializerResultSerializer(serializers.Serializer):
asset = serializers.CharField(read_only=True, label=_('Asset'))
state = serializers.CharField(read_only=True, label=_('State'))
error = serializers.CharField(read_only=True, label=_('Error'))
changed = serializers.BooleanField(read_only=True, label=_('Changed'))
class AssetAccountBulkSerializer(
AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer
):
assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets'))
class Meta:
model = Account
fields = [
'name', 'username', 'secret', 'secret_type',
'privileged', 'is_active', 'comment', 'template',
'on_invalid', 'push_now', 'assets',
]
extra_kwargs = {
'name': {'required': False},
'secret_type': {'required': False},
}
def set_initial_value(self):
if not getattr(self, 'initial_data', None):
return
initial_data = self.initial_data
self.from_template_if_need(initial_data)
@staticmethod
def get_filter_lookup(vd):
return {
'username': vd['username'],
'secret_type': vd['secret_type'],
'asset': vd['asset'],
}
@staticmethod
def get_uniq_name(vd):
return vd['name'] + '-' + uuid.uuid4().hex[:4]
@staticmethod
def _handle_update_create(vd, lookup):
ori = Account.objects.filter(**lookup).first()
if ori and ori.secret == vd.get('secret'):
return ori, False, 'skipped'
instance, value = Account.objects.update_or_create(defaults=vd, **lookup)
state = 'created' if value else 'updated'
return instance, True, state
@staticmethod
def _handle_skip_create(vd, lookup):
instance, value = Account.objects.get_or_create(defaults=vd, **lookup)
state = 'created' if value else 'skipped'
return instance, value, state
@staticmethod
def _handle_err_create(vd, lookup):
instance, value = Account.objects.get_or_create(defaults=vd, **lookup)
if not value:
raise serializers.ValidationError(_('Account already exists'))
return instance, True, 'created'
def perform_create(self, vd, handler):
lookup = self.get_filter_lookup(vd)
try:
instance, changed, state = handler(vd, lookup)
except IntegrityError:
vd['name'] = self.get_uniq_name(vd)
instance, changed, state = handler(vd, lookup)
return instance, changed, state
def get_create_handler(self, on_invalid):
if on_invalid == 'update':
handler = self._handle_update_create
elif on_invalid == 'skip':
handler = self._handle_skip_create
else:
handler = self._handle_err_create
return handler
def perform_bulk_create(self, vd):
assets = vd.pop('assets')
on_invalid = vd.pop('on_invalid', 'skip')
secret_type = vd.get('secret_type', 'password')
if not vd.get('name'):
vd['name'] = vd.get('username')
create_handler = self.get_create_handler(on_invalid)
asset_ids = [asset.id for asset in assets]
secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type)
_results = {}
for asset in assets:
if asset not in secret_type_supports:
_results[asset] = {
'error': _('Asset does not support this secret type: %s') % secret_type,
'state': 'error',
}
continue
vd = vd.copy()
vd['asset'] = asset
try:
instance, changed, state = self.perform_create(vd, create_handler)
_results[asset] = {
'changed': changed, 'instance': instance.id, 'state': state
}
except serializers.ValidationError as e:
_results[asset] = {'error': e.detail[0], 'state': 'error'}
except Exception as e:
logger.exception(e)
_results[asset] = {'error': str(e), 'state': 'error'}
results = [{'asset': asset, **result} for asset, result in _results.items()]
state_score = {'created': 3, 'updated': 2, 'skipped': 1, 'error': 0}
results = sorted(results, key=lambda x: state_score.get(x['state'], 4))
if on_invalid != 'error':
return results
errors = []
errors.extend([result for result in results if result['state'] == 'error'])
for result in results:
if result['state'] != 'skipped':
continue
errors.append({
'error': _('Account has exist'),
'state': 'error',
'asset': str(result['asset'])
})
if errors:
raise serializers.ValidationError(errors)
return results
@staticmethod
def push_accounts_if_need(results, push_now):
if not push_now:
return
accounts = [str(v['instance']) for v in results if v.get('instance')]
push_accounts_to_assets_task.delay(accounts)
def create(self, validated_data):
push_now = validated_data.pop('push_now', False)
self.generate_source_data(validated_data)
results = self.perform_bulk_create(validated_data)
self.push_accounts_if_need(results, push_now)
for res in results:
res['asset'] = str(res['asset'])
return results
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class Meta(AccountSerializer.Meta):
extra_kwargs = {
@@ -132,6 +392,7 @@ class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class AccountHistorySerializer(serializers.ModelSerializer):
secret_type = LabeledChoiceField(choices=SecretType.choices, label=_('Secret type'))
id = serializers.IntegerField(label=_('ID'), source='history_id', read_only=True)
class Meta:
model = Account.history.model
@@ -157,3 +418,7 @@ class AccountTaskSerializer(serializers.Serializer):
queryset=Account.objects, required=False, allow_empty=True, many=True
)
task = serializers.CharField(read_only=True)
params = serializers.JSONField(
decoder=None, encoder=None, required=False,
style={'base_template': 'textarea.html'}
)

View File

@@ -13,10 +13,10 @@ __all__ = ['AuthValidateMixin', 'BaseAccountSerializer']
class AuthValidateMixin(serializers.Serializer):
secret_type = LabeledChoiceField(
choices=SecretType.choices, required=True, label=_('Secret type')
choices=SecretType.choices, label=_('Secret type'), default='password'
)
secret = EncryptedField(
label=_('Secret/Password'), required=False, max_length=40960, allow_blank=True,
label=_('Secret'), required=False, max_length=40960, allow_blank=True,
allow_null=True, write_only=True,
)
passphrase = serializers.CharField(
@@ -77,6 +77,5 @@ class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer):
'date_verified', 'created_by', 'date_created',
]
extra_kwargs = {
'name': {'required': True},
'spec_info': {'label': _('Spec info')},
}

View File

@@ -1,4 +1,8 @@
from accounts.models import AccountTemplate
from django.db.transaction import atomic
from django.db.utils import IntegrityError
from accounts.models import AccountTemplate, Account
from assets.models import Asset
from common.serializers import SecretReadableMixin
from .base import BaseAccountSerializer
@@ -7,17 +11,77 @@ class AccountTemplateSerializer(BaseAccountSerializer):
class Meta(BaseAccountSerializer.Meta):
model = AccountTemplate
# @classmethod
# def validate_required(cls, attrs):
# # TODO 选择模版后检查一些必填项
# required_field_dict = {}
# error = _('This field is required.')
# for k, v in cls().fields.items():
# if v.required and k not in attrs:
# required_field_dict[k] = error
# if not required_field_dict:
# return
# raise serializers.ValidationError(required_field_dict)
@staticmethod
def account_save(data, account):
for field, value in data.items():
setattr(account, field, value)
try:
account.save(update_fields=list(data.keys()))
except IntegrityError:
pass
# TODO 数据库访问的太多了 后期优化
@atomic()
def bulk_update_accounts(self, instance, diff):
accounts = Account.objects.filter(source_id=instance.id)
if not accounts:
return
diff.pop('secret', None)
name = diff.pop('name', None)
username = diff.pop('username', None)
secret_type = diff.pop('secret_type', None)
update_accounts = []
for account in accounts:
for field, value in diff.items():
setattr(account, field, value)
update_accounts.append(account)
if update_accounts:
Account.objects.bulk_update(update_accounts, diff.keys())
if name:
for account in accounts:
data = {'name': name}
self.account_save(data, account)
if secret_type and username:
asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type)
for account in accounts:
asset_id = account.asset_id
if asset_id not in asset_ids_supports:
data = {'username': username}
self.account_save(data, account)
continue
data = {'username': username, 'secret_type': secret_type, 'secret': instance.secret}
self.account_save(data, account)
elif secret_type:
asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type)
for account in accounts:
asset_id = account.asset_id
if asset_id not in asset_ids_supports:
continue
data = {'secret_type': secret_type, 'secret': instance.secret}
self.account_save(data, account)
elif username:
for account in accounts:
data = {'username': username}
self.account_save(data, account)
@staticmethod
def get_asset_ids_supports(accounts, secret_type):
asset_ids = accounts.values_list('asset_id', flat=True)
secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type)
return [asset.id for asset in secret_type_supports]
def update(self, instance, validated_data):
# diff = {
# k: v for k, v in validated_data.items()
# if getattr(instance, k) != v
# }
instance = super().update(instance, validated_data)
# self.bulk_update_accounts(instance, diff)
return instance
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer):

View File

@@ -58,6 +58,7 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
"Currently only mail sending is supported"
)},
}}
@property
def model_type(self):
return AutomationTypes.change_secret

View File

@@ -17,7 +17,8 @@ class GatherAccountAutomationSerializer(BaseAutomationSerializer):
class Meta:
model = GatherAccountsAutomation
read_only_fields = BaseAutomationSerializer.Meta.read_only_fields
fields = BaseAutomationSerializer.Meta.fields + read_only_fields
fields = BaseAutomationSerializer.Meta.fields \
+ ['is_sync_account'] + read_only_fields
extra_kwargs = BaseAutomationSerializer.Meta.extra_kwargs

View File

@@ -7,9 +7,10 @@ from .change_secret import (
class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer):
class Meta(ChangeSecretAutomationSerializer.Meta):
model = PushAccountAutomation
fields = [
fields = ['params'] + [
n for n in ChangeSecretAutomationSerializer.Meta.fields
if n not in ['recipients']
]

View File

@@ -8,8 +8,8 @@ logger = get_logger(__name__)
@receiver(pre_save, sender=Account)
def on_account_pre_save(sender, instance, created=False, **kwargs):
if created:
def on_account_pre_save(sender, instance, **kwargs):
if instance.version == 0:
instance.version = 1
else:
instance.version = instance.history.count()

View File

@@ -15,7 +15,7 @@ __all__ = [
queue="ansible", verbose_name=_('Push accounts to assets'),
activity_callback=lambda self, account_ids, *args, **kwargs: (account_ids, None)
)
def push_accounts_to_assets_task(account_ids):
def push_accounts_to_assets_task(account_ids, params=None):
from accounts.models import PushAccountAutomation
from accounts.models import Account
@@ -26,6 +26,7 @@ def push_accounts_to_assets_task(account_ids):
task_snapshot = {
'accounts': [str(account.id) for account in accounts],
'assets': [str(account.asset_id) for account in accounts],
'params': params or {},
}
tp = AutomationTypes.push_account

View File

@@ -25,6 +25,7 @@ router.register(r'push-account-executions', api.PushAccountExecutionViewSet, 'pu
router.register(r'push-account-records', api.PushAccountRecordViewSet, 'push-account-record')
urlpatterns = [
path('accounts/bulk/', api.AssetAccountBulkCreateApi.as_view(), name='account-bulk-create'),
path('accounts/tasks/', api.AccountsTaskCreateAPI.as_view(), name='account-task-create'),
path('account-secrets/<uuid:pk>/histories/', api.AccountHistoriesSecretAPI.as_view(),
name='account-secret-history'),

View File

@@ -1,7 +1,7 @@
from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from common.db.models import JMSBaseModel
from common.utils import contains_ip

View File

@@ -7,7 +7,7 @@ from orgs.models import Organization
from users.models import User
common_help_text = _(
"Format for comma-delimited string, with * indicating a match all. "
"With * indicating a match all. "
)
@@ -22,7 +22,7 @@ class ACLUsersSerializer(serializers.Serializer):
class ACLAssestsSerializer(serializers.Serializer):
address_group_help_text = _(
"Format for comma-delimited string, with * indicating a match all. "
"With * indicating a match all. "
"Such as: "
"192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64"
" (Domain name support)"

View File

@@ -1,19 +1,20 @@
from django.utils.translation import ugettext as _
from rest_framework import serializers
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from common.serializers import BulkModelSerializer, MethodSerializer
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from jumpserver.utils import has_valid_xpack_license
from users.models import User
from .rules import RuleSerializer
from ..models import LoginACL
from ..models.base import ActionChoices
__all__ = [
"LoginACLSerializer",
]
common_help_text = _(
"Format for comma-delimited string, with * indicating a match all. "
"With * indicating a match all. "
)
@@ -22,7 +23,7 @@ class LoginACLSerializer(BulkModelSerializer):
reviewers = ObjectRelatedField(
queryset=User.objects, label=_("Reviewers"), many=True, required=False
)
action = LabeledChoiceField(choices=LoginACL.ActionChoices.choices, label=_('Action'))
action = LabeledChoiceField(choices=ActionChoices.choices, label=_('Action'))
reviewers_amount = serializers.IntegerField(
read_only=True, source="reviewers.count", label=_("Reviewers amount")
)
@@ -55,7 +56,7 @@ class LoginACLSerializer(BulkModelSerializer):
choices = action.choices
if not has_valid_xpack_license():
choices.pop(LoginACL.ActionChoices.review, None)
action.choices = choices
action._choices = choices
def get_rules_serializer(self):
return RuleSerializer()

View File

@@ -22,7 +22,7 @@ def ip_group_child_validator(ip_group_child):
ip_group_help_text = _(
'Format for comma-delimited string, with * indicating a match all. '
'With * indicating a match all. '
'Such as: '
'192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 '
)

View File

@@ -1,7 +1,8 @@
from .asset import *
from .host import *
from .database import *
from .web import *
from .cloud import *
from .custom import *
from .database import *
from .device import *
from .host import *
from .permission import *
from .web import *

View File

@@ -2,15 +2,17 @@
#
import django_filters
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK
from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task
from assets import serializers
from assets.exceptions import NotSupportedTemporarilyError
from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend
from assets.models import Asset, Gateway
from assets.models import Asset, Gateway, Platform
from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual
from common.api import SuggestionMixin
from common.drf.filters import BaseFilterSet
@@ -18,6 +20,7 @@ from common.utils import get_logger, is_uuid
from orgs.mixins import generics
from orgs.mixins.api import OrgBulkModelViewSet
from ..mixin import NodeFilterMixin
from ...notifications import BulkUpdatePlatformSkipAssetUserMsg
logger = get_logger(__file__)
__all__ = [
@@ -93,20 +96,19 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
model = Asset
filterset_class = AssetFilterSet
search_fields = ("name", "address")
ordering = ("name", "connectivity")
ordering_fields = ('name', 'connectivity', 'platform', 'date_updated')
serializer_classes = (
("default", serializers.AssetSerializer),
("platform", serializers.PlatformSerializer),
("suggestion", serializers.MiniAssetSerializer),
("gateways", serializers.GatewaySerializer),
("spec_info", serializers.SpecSerializer),
)
rbac_perms = (
("match", "assets.match_asset"),
("platform", "assets.view_platform"),
("gateways", "assets.view_gateway"),
("spec_info", "assets.view_asset"),
("info", "assets.view_asset"),
("gathered_info", "assets.view_asset"),
)
extra_filter_backends = [LabelFilterBackend, IpInFilterBackend, NodeFilterBackend]
@@ -124,11 +126,6 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
serializer = super().get_serializer(instance=asset.platform)
return Response(serializer.data)
@action(methods=["GET"], detail=True, url_path="spec-info")
def spec_info(self, *args, **kwargs):
asset = super().get_object()
return Response(asset.spec_info)
@action(methods=["GET"], detail=True, url_path="gateways")
def gateways(self, *args, **kwargs):
asset = self.get_object()
@@ -144,6 +141,32 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
return Response({'error': error}, status=400)
return super().create(request, *args, **kwargs)
def filter_bulk_update_data(self):
bulk_data = []
skip_assets = []
for data in self.request.data:
pk = data.get('id')
platform = data.get('platform')
if not platform:
bulk_data.append(data)
continue
asset = get_object_or_404(Asset, pk=pk)
platform = get_object_or_404(Platform, **platform)
if platform.type == asset.type:
bulk_data.append(data)
continue
skip_assets.append(asset)
return bulk_data, skip_assets
def bulk_update(self, request, *args, **kwargs):
bulk_data, skip_assets = self.filter_bulk_update_data()
request._full_data = bulk_data
response = super().bulk_update(request, *args, **kwargs)
if response.status_code == HTTP_200_OK and skip_assets:
user = request.user
BulkUpdatePlatformSkipAssetUserMsg(user, skip_assets).publish()
return response
class AssetsTaskMixin:
def perform_assets_task(self, serializer):
@@ -154,8 +177,8 @@ class AssetsTaskMixin:
task = update_assets_hardware_info_manual(assets)
else:
asset = assets[0]
if not asset.auto_info['ansible_enabled'] or \
not asset.auto_info['ping_enabled']:
if not asset.auto_config['ansible_enabled'] or \
not asset.auto_config['ping_enabled']:
raise NotSupportedTemporarilyError()
task = test_assets_connectivity_manual(assets)
return task

View File

@@ -0,0 +1,16 @@
from assets.models import Custom, Asset
from assets.serializers import CustomSerializer
from .asset import AssetViewSet
__all__ = ['CustomViewSet']
class CustomViewSet(AssetViewSet):
model = Custom
perm_model = Asset
def get_serializer_classes(self):
serializer_classes = super().get_serializer_classes()
serializer_classes['default'] = CustomSerializer
return serializer_classes

View File

@@ -1,8 +1,5 @@
from rest_framework.decorators import action
from rest_framework.response import Response
from assets.models import Host, Asset
from assets.serializers import HostSerializer, HostInfoSerializer
from assets.serializers import HostSerializer
from .asset import AssetViewSet
__all__ = ['HostViewSet']
@@ -15,16 +12,4 @@ class HostViewSet(AssetViewSet):
def get_serializer_classes(self):
serializer_classes = super().get_serializer_classes()
serializer_classes['default'] = HostSerializer
serializer_classes['info'] = HostInfoSerializer
return serializer_classes
@action(methods=["GET"], detail=True, url_path="info")
def info(self, *args, **kwargs):
asset = super().get_object()
serializer = self.get_serializer(asset.info)
data = serializer.data
data['asset'] = {
'id': asset.id, 'name': asset.name,
'address': asset.address
}
return Response(data)

View File

@@ -3,6 +3,7 @@ from rest_framework.decorators import action
from rest_framework.response import Response
from common.api import JMSGenericViewSet
from common.permissions import IsValidUser
from assets.serializers import CategorySerializer, TypeSerializer
from assets.const import AllTypes
@@ -14,7 +15,7 @@ class CategoryViewSet(ListModelMixin, JMSGenericViewSet):
'default': CategorySerializer,
'types': TypeSerializer
}
permission_classes = ()
permission_classes = (IsValidUser,)
def get_queryset(self):
return AllTypes.categories()

View File

@@ -8,6 +8,15 @@ from common.utils import lazyproperty, timeit
class SerializeToTreeNodeMixin:
request: Request
@lazyproperty
def is_sync(self):
sync_paths = ['/api/v1/perms/users/self/nodes/all-with-assets/tree/']
for p in sync_paths:
if p == self.request.path:
return True
return False
@timeit
def serialize_nodes(self, nodes: List[Node], with_asset_amount=False):
@@ -17,6 +26,16 @@ class SerializeToTreeNodeMixin:
else:
def _name(node: Node):
return node.value
def _open(node):
if not self.is_sync:
# 异步加载资产树时,默认展开节点
return True
if not node.parent_key:
return True
else:
return False
data = [
{
'id': node.key,
@@ -24,7 +43,7 @@ class SerializeToTreeNodeMixin:
'title': _name(node),
'pId': node.parent_key,
'isParent': True,
'open': True,
'open': _open(node),
'meta': {
'data': {
"id": node.id,
@@ -38,11 +57,23 @@ class SerializeToTreeNodeMixin:
]
return data
@lazyproperty
def support_types(self):
from assets.const import AllTypes
return AllTypes.get_types_values(exclude_custom=True)
def get_icon(self, asset):
if asset.type in self.support_types:
return asset.type
else:
return 'file'
@timeit
def serialize_assets(self, assets, node_key=None):
sftp_enabled_platform = PlatformProtocol.objects \
.filter(name='ssh', setting__sftp_enabled=True) \
.values_list('platform', flat=True).distinct()
.values_list('platform', flat=True) \
.distinct()
if node_key is None:
get_pid = lambda asset: getattr(asset, 'parent_key', '')
else:
@@ -52,11 +83,11 @@ class SerializeToTreeNodeMixin:
{
'id': str(asset.id),
'name': asset.name,
'title': asset.address,
'title': f'{asset.address}\n{asset.comment}',
'pId': get_pid(asset),
'isParent': False,
'open': False,
'iconSkin': asset.type,
'iconSkin': self.get_icon(asset),
'chkDisabled': not asset.is_active,
'meta': {
'type': 'asset',
@@ -64,6 +95,8 @@ class SerializeToTreeNodeMixin:
'platform_type': asset.platform.type,
'org_name': asset.org_name,
'sftp': asset.platform_id in sftp_enabled_platform,
'name': asset.name,
'address': asset.address
},
}
}

View File

@@ -1,10 +1,16 @@
from rest_framework import generics
from rest_framework import serializers
from rest_framework.decorators import action
from rest_framework.response import Response
from assets.const import AllTypes
from assets.models import Platform
from assets.models import Platform, Node, Asset
from assets.serializers import PlatformSerializer
from common.api import JMSModelViewSet
from common.permissions import IsValidUser
from common.serializers import GroupedChoiceSerializer
__all__ = ['AssetPlatformViewSet']
__all__ = ['AssetPlatformViewSet', 'PlatformAutomationMethodsApi']
class AssetPlatformViewSet(JMSModelViewSet):
@@ -18,12 +24,13 @@ class AssetPlatformViewSet(JMSModelViewSet):
rbac_perms = {
'categories': 'assets.view_platform',
'type_constraints': 'assets.view_platform',
'ops_methods': 'assets.view_platform'
'ops_methods': 'assets.view_platform',
'filter_nodes_assets': 'assets.view_platform'
}
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(type__in=AllTypes.get_types())
queryset = queryset.filter(type__in=AllTypes.get_types_values())
return queryset
def get_object(self):
@@ -38,3 +45,44 @@ class AssetPlatformViewSet(JMSModelViewSet):
request, message={"detail": "Internal platform"}
)
return super().check_object_permissions(request, obj)
@action(methods=['post'], detail=False, url_path='filter-nodes-assets')
def filter_nodes_assets(self, request, *args, **kwargs):
node_ids = request.data.get('node_ids', [])
asset_ids = request.data.get('asset_ids', [])
nodes = Node.objects.filter(id__in=node_ids)
node_asset_ids = Node.get_nodes_all_assets(*nodes).values_list('id', flat=True)
direct_asset_ids = Asset.objects.filter(id__in=asset_ids).values_list('id', flat=True)
platform_ids = Asset.objects.filter(
id__in=set(list(direct_asset_ids) + list(node_asset_ids))
).values_list('platform_id', flat=True)
platforms = Platform.objects.filter(id__in=platform_ids)
serializer = self.get_serializer(platforms, many=True)
return Response(serializer.data)
class PlatformAutomationMethodsApi(generics.ListAPIView):
permission_classes = (IsValidUser,)
@staticmethod
def automation_methods():
return AllTypes.get_automation_methods()
def generate_serializer_fields(self):
data = self.automation_methods()
fields = {
i['id']: i['params_serializer'](label=i['name'])
if i['params_serializer'] else None
for i in data
}
return fields
def get_serializer_class(self):
fields = self.generate_serializer_fields()
serializer_name = 'AutomationMethodsSerializer'
return type(serializer_name, (serializers.Serializer,), fields)
def list(self, request, *args, **kwargs):
data = self.generate_serializer_fields()
serializer = self.get_serializer(data)
return Response(serializer.data)

View File

@@ -41,6 +41,28 @@ class BasePlaybookManager:
self.method_hosts_mapper = defaultdict(list)
self.playbooks = []
self.gateway_servers = dict()
params = self.execution.snapshot.get('params')
self.params = params or {}
def get_params(self, automation, method_type):
method_attr = '{}_method'.format(method_type)
method_params = '{}_params'.format(method_type)
method_id = getattr(automation, method_attr)
automation_params = getattr(automation, method_params)
serializer = self.method_id_meta_mapper[method_id]['params_serializer']
if serializer is None:
return {}
data = self.params.get(method_id)
if not data:
data = automation_params.get(method_id, {})
params = serializer(data).data
return {
field_name: automation_params.get(field_name, '')
if not params[field_name] else params[field_name]
for field_name in params
}
@property
def platform_automation_methods(self):
@@ -101,8 +123,9 @@ class BasePlaybookManager:
return host
def host_callback(self, host, automation=None, **kwargs):
enabled_attr = '{}_enabled'.format(self.__class__.method_type())
method_attr = '{}_method'.format(self.__class__.method_type())
method_type = self.__class__.method_type()
enabled_attr = '{}_enabled'.format(method_type)
method_attr = '{}_method'.format(method_type)
method_enabled = automation and \
getattr(automation, enabled_attr) and \
@@ -114,6 +137,7 @@ class BasePlaybookManager:
return host
host = self.convert_cert_to_file(host, kwargs.get('path_dir'))
host['params'] = self.get_params(automation, method_type)
return host
@staticmethod
@@ -239,10 +263,12 @@ class BasePlaybookManager:
jms_asset, jms_gateway = host['jms_asset'], host.get('gateway')
if not jms_gateway:
continue
server = SSHTunnelForwarder(
(jms_gateway['address'], jms_gateway['port']),
ssh_username=jms_gateway['username'],
ssh_password=jms_gateway['secret'],
ssh_pkey=jms_gateway['private_key_path'],
remote_bind_address=(jms_asset['address'], jms_asset['port'])
)
try:
@@ -252,8 +278,8 @@ class BasePlaybookManager:
print('\033[31m %s \033[0m\n' % err_msg)
not_valid.append(k)
else:
jms_asset['address'] = '127.0.0.1'
jms_asset['port'] = server.local_bind_port
host['ansible_host'] = jms_asset['address'] = '127.0.0.1'
host['ansible_port'] = jms_asset['port'] = server.local_bind_port
servers.append(server)
# 网域不可连接的,就不继续执行此资源的后续任务了

View File

@@ -1,6 +1,11 @@
id: gather_facts_mongodb
name: Gather facts from MongoDB
name: "{{ 'Gather facts from MongoDB' | trans }}"
category: database
type:
- mongodb
method: gather_facts
i18n:
Gather facts from MongoDB:
zh: 从 MongoDB 获取信息
en: Gather facts from MongoDB
ja: MongoDBから事実を取得する

View File

@@ -1,7 +1,12 @@
id: gather_facts_mysql
name: Gather facts from MySQL
name: "{{ 'Gather facts from MySQL' | trans }}"
category: database
type:
- mysql
- mariadb
method: gather_facts
i18n:
Gather facts from MySQL:
zh: 从 MySQL 获取信息
en: Gather facts from MySQL
ja: MySQLから事実を取得する

View File

@@ -1,6 +1,11 @@
id: gather_facts_oracle
name: Gather facts from Oracle
name: "{{ 'Gather facts from Oracle' | trans }}"
category: database
type:
- oracle
method: gather_facts
i18n:
Gather facts from Oracle:
zh: 从 Oracle 获取信息
en: Gather facts from Oracle
ja: Oracleから事実を取得する

View File

@@ -1,6 +1,11 @@
id: gather_facts_postgresql
name: Gather facts for PostgreSQL
name: "{{ 'Gather facts for PostgreSQL' | trans }}"
category: database
type:
- postgresql
method: gather_facts
i18n:
Gather facts for PostgreSQL:
zh: 从 PostgreSQL 获取信息
en: Gather facts for PostgreSQL
ja: PostgreSQLから事実を取得する

View File

@@ -1,7 +1,12 @@
id: gather_facts_posix
name: Gather posix facts
name: "{{ 'Gather posix facts' | trans }}"
category: host
type:
- linux
- unix
method: gather_facts
i18n:
Gather posix facts:
zh: 从 Posix 主机获取信息
en: Gather posix facts
ja: Posixから事実を取得する

View File

@@ -1,7 +1,12 @@
id: gather_facts_windows
name: Gather facts windows
name: "{{ 'Gather facts windows' | trans }}"
version: 1
method: gather_facts
category: host
type:
- windows
i18n:
Gather facts windows:
zh: 从 Windows 获取信息
en: Gather facts windows
ja: Windowsから事実を取得する

View File

@@ -29,7 +29,7 @@ class GatherFactsManager(BasePlaybookManager):
asset = self.host_asset_mapper.get(host)
if asset and info:
info = self.format_asset_info(asset.type, info)
asset.info = info
asset.save(update_fields=['info'])
asset.gathered_info = info
asset.save(update_fields=['gathered_info'])
else:
logger.error("Not found info: {}".format(host))

View File

@@ -1,8 +1,9 @@
import os
import yaml
import json
import os
from functools import partial
from common.utils.yml import yaml_load_with_i18n
def check_platform_method(manifest, manifest_path):
required_keys = ['category', 'method', 'name', 'id', 'type']
@@ -21,6 +22,15 @@ def check_platform_methods(methods):
raise ValueError("Duplicate id: {}".format(_id))
def generate_serializer(data):
from common.serializers import create_serializer_class
params = data.pop('params', None)
if not params:
return None
serializer_name = data['id'].title().replace('_', '') + 'Serializer'
return create_serializer_class(serializer_name, params)
def get_platform_automation_methods(path):
methods = []
for root, dirs, files in os.walk(path, topdown=False):
@@ -30,9 +40,10 @@ def get_platform_automation_methods(path):
continue
with open(path, 'r') as f:
manifest = yaml.safe_load(f)
manifest = yaml_load_with_i18n(f)
check_platform_method(manifest, path)
manifest['dir'] = os.path.dirname(path)
manifest['params_serializer'] = generate_serializer(manifest)
methods.append(manifest)
check_platform_methods(methods)
@@ -46,12 +57,12 @@ def filter_key(manifest, attr, value):
return value in manifest_value or 'all' in manifest_value
def filter_platform_methods(category, tp, method=None, methods=None):
def filter_platform_methods(category, tp_name, method=None, methods=None):
methods = platform_automation_methods if methods is None else methods
if category:
methods = filter(partial(filter_key, attr='category', value=category), methods)
if tp:
methods = filter(partial(filter_key, attr='type', value=tp), methods)
if tp_name:
methods = filter(partial(filter_key, attr='type', value=tp_name), methods)
if method:
methods = filter(lambda x: x['method'] == method, methods)
return methods

View File

@@ -0,0 +1,14 @@
- hosts: custom
gather_facts: no
vars:
ansible_connection: local
tasks:
- name: Test asset connection
ssh_ping:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"

View File

@@ -0,0 +1,8 @@
id: ping_by_ssh
name: Ping by SSH
category:
- device
- host
type:
- all
method: ping

View File

@@ -1,6 +1,11 @@
id: mongodb_ping
name: Ping MongoDB
name: "{{ 'Ping MongoDB' | trans }}"
category: database
type:
- mongodb
method: ping
i18n:
Ping MongoDB:
zh: 测试 MongoDB 可连接性
en: Ping MongoDB
ja: MongoDBにPingする

View File

@@ -1,7 +1,12 @@
id: mysql_ping
name: Ping MySQL
name: "{{ 'Ping MySQL' | trans }}"
category: database
type:
- mysql
- mariadb
method: ping
i18n:
Ping MySQL:
zh: 测试 MySQL 可连接性
en: Ping MySQL
ja: MySQLにPingする

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