Compare commits

...

1007 Commits
1.0.0 ... 1.5.0

Author SHA1 Message Date
BaiJiangJie
e5185ebd57 Merge pull request #2770 from jumpserver/dev
Dev
2019-06-03 16:35:58 +08:00
ibuler
1ba9351957 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-06-03 16:28:56 +08:00
ibuler
505b801423 [Update] 当授权规则发生改变时刷新所有缓存 2019-06-03 16:28:34 +08:00
BaiJiangJie
35a0b42bea Merge pull request #2769 from jumpserver/dev
[Update] 修改录像回放判断逻辑 (#2768)
2019-06-03 15:06:16 +08:00
BaiJiangJie
4f289963d1 [Update] 修改录像回放判断逻辑 (#2768) 2019-06-03 14:47:33 +08:00
BaiJiangJie
1631c32868 Merge pull request #2767 from jumpserver/dev
[Update] 解决csv导入导出的问题
2019-05-31 18:25:34 +08:00
ibuler
9721b805f3 [Update] 解决csv导入导出的问题 2019-05-31 18:20:24 +08:00
BaiJiangJie
d2b1b19404 Merge pull request #2765 from jumpserver/dev
Dev
2019-05-31 17:49:50 +08:00
ibuler
49632241b6 [Update] 导入解析csv coding 2019-05-31 17:40:57 +08:00
ibuler
b4f23f9731 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-31 17:04:23 +08:00
ibuler
1cdcc66dba [Update] 修改excel打开乱码 2019-05-31 17:04:00 +08:00
zbhlove100
0a1a260a22 Update README.md 2019-05-31 11:45:01 +08:00
BaiJiangJie
0e377cd0e9 Merge pull request #2762 from jumpserver/dev
[Bugfix] 修复RemoteApp用户列表显示Bug (#2761)
2019-05-31 10:40:51 +08:00
BaiJiangJie
3f3cc6359a [Bugfix] 修复RemoteApp用户列表显示Bug (#2761) 2019-05-31 10:39:33 +08:00
BaiJiangJie
eb0a6f00b5 Merge pull request #2758 from jumpserver/dev
Dev
2019-05-30 16:42:34 +08:00
BaiJiangJie
a87610a8d8 Merge pull request #2757 from jumpserver/dev_bai
[Update] 修改依赖版本号
2019-05-30 16:41:40 +08:00
BaiJiangJie
e3630a9961 [Update] 修改依赖版本号 2019-05-30 16:38:46 +08:00
BaiJiangJie
6ba78e2cf3 Merge pull request #2756 from jumpserver/dev
Dev
2019-05-30 16:08:44 +08:00
BaiJiangJie
63a06b5dd8 Merge pull request #2755 from jumpserver/dev_bai
[Update] 修改获取资产信息时返回org_id字段值
2019-05-30 16:07:42 +08:00
BaiJiangJie
ec60697912 [Update] 修改细节 2019-05-30 16:07:19 +08:00
BaiJiangJie
d6a8c04d45 [Update] 修改获取资产信息时返回org_id字段值 2019-05-30 16:02:03 +08:00
BaiJiangJie
d46f5858f8 Merge pull request #2737 from jumpserver/dev
Dev
2019-05-29 11:34:43 +08:00
BaiJiangJie
a8491eafea Merge pull request #2747 from jumpserver/dev_bai
[Update] 修改版本号
2019-05-29 10:44:23 +08:00
BaiJiangJie
0d046d8356 [Update] 修改版本号 2019-05-29 10:22:13 +08:00
八千流
9c55450a9e [Bugfix] 修复资源表可以选择多页的资源数据 (#2744)
* [Bugfix] 修复资源表可以选择多页的资源数据

* [Bugfix]修改小问题
2019-05-29 10:13:19 +08:00
BaiJiangJie
00e986a64e [Update] 优化LDAP用户导入功能,可导入跨页选取的所有用户 (#2745) 2019-05-29 10:11:58 +08:00
BaiJiangJie
dc4bf669b0 [Update] 修改RemoteApp迁移文件 (#2742) 2019-05-28 17:05:31 +08:00
BaiJiangJie
059a8de44a [Bufix] 修复用户更新时password_strategy字段不能为空的bug (#2741) 2019-05-28 12:11:55 +08:00
ibuler
aa25b7745c Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-28 10:28:49 +08:00
ibuler
1097b11115 [Update] 修改ldap支持ssl 2019-05-28 10:28:03 +08:00
BaiJiangJie
9f67daeb1e [Update] 优化AssetPermissionUtil动态设置系统用户actions属性逻辑 (#2739)
* [Update] 优化AssetPermissionUtil动态设置系统用户actions属性逻辑

* [Bugfix] 修复can't pickle dict_keys objects的bug
2019-05-27 20:04:41 +08:00
ibuler
2065692199 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-27 16:39:02 +08:00
ibuler
33e342f03f [Update] 修改菜单 2019-05-27 16:38:43 +08:00
八千流
7a6027f35a [Update] csv翻译 (#2736) 2019-05-27 16:12:19 +08:00
BaiJiangJie
e46a6f1d12 [Update] 添加应用下载器地址;添加系统设置中字段的最大、最小值限制 (#2735)
* [Update] 添加应用下载器地址

* [Update] 系统设置中的Form, 添加最大、最小值限制

* [Update] 更新翻译
2019-05-27 15:52:25 +08:00
ibuler
0f3996369b Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-27 15:50:54 +08:00
ibuler
0847539e02 [Update] 修改导出的csv名称 2019-05-27 15:50:44 +08:00
八千流
f576f2eda2 [Bugfix]修复解析器顺序bug,csv解析器需在file解析器前 (#2734) 2019-05-27 15:21:33 +08:00
BaiJiangJie
21ac3eaf8b [Update] 用户创建添加密码设置策略 (#2731)
* [Update] 优化创建用户的密码策略功能

* [Update] 优化用户初始密码设置以及清除初始密码

* [Update] 优化创建用户的密码策略功能

* [Update]统一变量名前缀

* [Update] 用户密码策略去掉自定义策略

* [Update] 修改小问题

* [Update] 优化创建用户密码策略

* [Update] 翻译

* [Update] 优化mfa按钮排布和间距

* [Update] 优化mfa按钮样式由前端控制

* [Update] 优化前端密码策略按钮的显示与隐藏

* [Update] 用户创建设置密码添加密码校验弹窗
2019-05-24 19:41:07 +08:00
ibuler
b237cbb20f Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-24 18:34:35 +08:00
ibuler
9b5b48dd1a [Update] 修改settings conf配置 2019-05-24 18:34:21 +08:00
八千流
b7eac837f7 [Update] 创建用户自定义邮件内容 (#2703)
* [Update] 增加创建用户自定义邮件内容功能

* [Update] 优化自定义创建用户邮件功能

* [Update] 修改自定义邮件内容的小图标icon

* [Update] 发送邮件引入样式,避免不同浏览器不显示邮件格式问题

* [Update] 优化创建用户自定义邮件内容

* [Update] 修改翻译

* [Update] 优化邮件内容的变量名

* [Update]优化自定义邮件内容

* [Update] 修改小问题
2019-05-24 18:12:58 +08:00
BaiJiangJie
217bb81722 [Update] 优化LDAPUtil逻辑 (#2728) 2019-05-24 11:11:50 +08:00
xiaomao
b791073802 [Update] 解决ldap映射is_active等字段为bool值的问题 (#2716) (#2721) 2019-05-23 18:55:28 +08:00
BaiJiangJie
45cb39e971 [Update] RemoteApp设置type vSphere Client的默认路径 (#2725) 2019-05-23 18:52:39 +08:00
BaiJiangJie
2df1dd2bb1 [Bugfix] LDAP同步用户列表中如果API获取到的用户名中有空格,在设置为id的时候默认会取第一个空格前的字符串作为用户名,导致不会导入用户 (#2726) 2019-05-23 18:52:19 +08:00
BaiJiangJie
4cd3dd3670 [Update] 更新RemoteApp (#2720)
* [Update] RemoteAppForm添加RemoteApp各类型参数保存逻辑

* [Update] RemoteApp添加默认应用路径
2019-05-22 18:56:34 +08:00
BaiJiangJie
b18ca8c94f [Update] 校验用户对RemoteApp权限API添加权限控制 (#2715) 2019-05-22 12:35:14 +08:00
ibuler
75fb37d247 Merge branch 'password' into dev 2019-05-22 10:26:26 +08:00
ibuler
f863ed0f4f [Update] 修改获取common store 2019-05-22 10:25:53 +08:00
BaiJiangJie
3eaf4cd142 [Update] 修改翻译 (#2714) 2019-05-22 10:01:41 +08:00
ibuler
7859499c97 [Update] 更新dockerfile 2019-05-21 17:46:39 +08:00
ibuler
e8ceb58292 [Update] 允许资产ip填写为host地址 2019-05-21 17:07:47 +08:00
BaiJiangJie
84610f2a2c [Bugfix] 修改PermsModel抽象后的objects-bug (#2713)
* [Bugfix] 修改PermsModel抽象后的objects-bug

* [Bugfix] 删除无用代码
2019-05-21 17:01:47 +08:00
BaiJiangJie
d906df5b00 [Update] 抽象BasePermission (#2710)
* [Update] AssetPermission/RemoteAppPermission抽象BasePermission

* [Update] Perms模块添加迁移文件

* [Update] Perms删除多余迁移文件

* [Update] Perms重新生成RemoteAppPermission迁移文件
2019-05-21 16:27:01 +08:00
八千流
22f362aab3 Dev csv (#2640)
* [Update] 封装JMSCSVRender和JMSCSVParser

* [Update] 更改JMSCSVRender,根据请求参数控制导出csv的字段和下载csv模板的字段

* [Update] 导入空数据,提示错误消息

* [Update] 修改用户导入和导出功能代码

* [Update] 修改导入路由为动态反向解析

* [Update] 修改JMSCSVRender和JMSCSVParser以及用户导入导出代码

* [Update] 优化parsers逻辑

* [Update] 优化parsers csv代码结构

* [Update] 优化renders csv代码逻辑

* [Update] 删除parsers csv多余代码

* [Update] 删除parsers csv多余变量

* [Update] 优化renders csv代码结构

* [Update] 优化renders csv代码结构2

* [Update] 优化renders csv获取header逻辑

* [Update] 优化Cache Resources ID View逻辑

* [Update] 优化ViewSet IDCacheFilterMixin逻辑

* [Update] csv: parser render 添加异常捕获逻辑

* [Update] 删除多余代码

* [Update] 优化前端代码

* [Update] 修改小问题

* [Update] 修改前端导出用户的问题

* [Update] 前端 - 优化数据导出逻辑 APIExportData

* [Update] 修复批量创建用户时发送created信号的bug

* [Update] 优化导入时错误信息展示

* [Update] 优化parser、render时,对于多对多字段的处理

* [Update] 修改前端上传空文件问题

* [Update] 添加IDExportFilter,控制下载模版时的queryset

* [Update] 修改判断导出模版时参数变量名 action => template

* [Update] 修复导入用户数据时,用户组不生效的bug

* [Update] 修改前端导入信息展示

* [Update] 抽象资源导入模版

* [Update] 优化资源导入模版

* [Update] 修改js设置url的params逻辑

* [Update] 修改users序列类控制read_only字段方式

* [Update] 资产列表采用新的导入/导出csv文件逻辑

* [Update] 修改导入资产时设置资产所在节点逻辑

* [Update] 添加用户组导入/导出功能

* [Update] 修改前端变量名

* [Update] 修改下载导入模版,不包含org字段

* [Update] 增加管理用户导入/导出功能

* [Update] 导入模版提供id字段(为了资源备份后导入直接使用); 修复资源导入时联合唯一字段不校验导致创建时报错的bug

* [Update] 增加系统用户导入/导出功能

* [Update] 排序资源导入/导出字段

* [Update] 翻译导入/导出的字段和模版

* [Update] 更改csv导出和导出模版数据的控制在render实现

* [Update] 资产添加 更新导入 功能

* [Update] 用户/用户组/管理用户/系统用户/ 添加导入更新

* [Update] 翻译

* [Update] 优化资源序列化中的label

* [Update] 去掉资源IDInFilterMixin过滤

* [Update] 翻译
2019-05-21 16:24:01 +08:00
BaiJiangJie
4942900886 [Update] RemoteApp修改左侧菜单描述 (#2709) 2019-05-21 16:10:48 +08:00
BaiJiangJie
1e505d3d0f [Update] OpenID Middleware去掉输出日志 (#2711) 2019-05-21 16:07:40 +08:00
BaiJiangJie
1eca517978 [Feature] 添加功能 RemoteApp (#2706)
* [Feature] RemoteApp添加Model

* [Feature] RemoteApp添加ViewSet API

* [Feature] RemoteApp添加获取connection-info API

* [Feature] Perms模块修改目录结构

* [Feature] RemoteAppPermission添加Model

* [Feature] RemoteAppPermission添加ViewSet API

* [Feature] RemoteAppPermission添加用户/用户组获取被授权的RemoteApp API

* [Feature] RemoteAppPermission添加校验用户对RemoteApp的权限 API

* [Feature] RemoteAppPermission添加获取用户授权的RemoteApp树 API

* [Feature] RemoteAppPermission添加<添加/移除>所授权的<用户/RemoteApp> API

* [Feature] RemoteApp添加创建、更新、详情、删除、用户RemoteApp等页面

* [Feature] RemoteAppPermission添加创建、更新、详情、删除、授权用户、授权RemoteApp等页面

* [Feature] RemoteApp从assets模块迁移到新添加的applications模块

* [Feature] RemoteApp/RemoteAppPermission添加迁移文件

* [Feature] RemoteApp/RemoteAppPermission修改小细节

* [Feature] RemoteApp/RemoteAppPermission修改小细节2

* [Feature] RemoteApp/RemoteAppPermission修改小细节3

* [Feature] RemoteApp更新迁移文件

* [Feature] RemoteApp/RemoteAppPermission添加翻译信息

* [Feature] RemoteApp/RemoteAppPermission删除迁移文件

* [Feature] RemoteApp/RemoteAppPermission添加迁移文件

* [Feature] RemoteApp/RemoteAppPermission修改代码风格
2019-05-20 19:39:53 +08:00
ibuler
38acce7460 [Update] 修改错误提示颜色 2019-05-20 12:36:35 +08:00
老广
3855fecc69 Password message (#2702)
* [Update] 密码信封

* [Update]  查看密码

* [Update] 支持查看密码

* [Update] 修改语言翻译

* [Update] 迁移ansible到2.8版本

* [Update] 修改auth book的可连接性

* [Update] 删除不使用的方法
2019-05-20 12:30:55 +08:00
ibuler
466b922ea0 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-16 14:41:40 +08:00
ibuler
9e52579ca6 [Update] 修改api meta data 2019-05-16 14:41:11 +08:00
老广
1ca1e519b6 Merge pull request #2695 from jumpserver/dev_bugfix
[Bugfix] 修复AssetUserManager.get获取username为""的AuthBook对象时,返回多个结果的bug
2019-05-16 10:38:47 +08:00
BaiJiangJie
54a9070c58 [Update] 资产节点API添加search功能 2019-05-16 10:36:47 +08:00
BaiJiangJie
e108aae3c0 [Bugfix] 修复AssetUserManager.get获取username为""的AuthBook对象时,返回多个结果的bug 2019-05-14 21:39:34 +08:00
老广
74ae7d138e Merge pull request #2683 from jumpserver/dev
[Update] 修改排序节点排序规则
2019-05-10 11:35:46 +08:00
ibuler
20ce5d11a6 [Update] 修改排序节点排序规则 2019-05-10 11:33:26 +08:00
老广
cbe919c4b3 Merge pull request #2682 from jumpserver/dev
Dev
2019-05-10 11:31:28 +08:00
ibuler
497cba6b13 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-05-10 11:30:01 +08:00
ibuler
e2849be72c [Update] 修改节点树排序 2019-05-10 11:29:38 +08:00
老广
a025930957 Merge pull request #2642 from jumpserver/dev
Dev
2019-04-30 10:46:28 +08:00
老广
990c78e7cc Merge branch 'master' into dev 2019-04-30 10:46:10 +08:00
BaiJiangJie
0ef12906d3 [Update] 修改翻译信息 (#2643)
* [Update] 更新翻译信息

* [Update] 更新翻译信息
2019-04-30 10:30:58 +08:00
ibuler
61a37731ec Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-04-30 10:20:57 +08:00
ibuler
d3217b6a67 [Update] 修改版本号 2019-04-30 10:20:48 +08:00
BaiJiangJie
04266cc20b [Update] 修改Copyright (#2649) 2019-04-30 10:07:57 +08:00
ibuler
4f36cf7dd1 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-04-29 10:39:51 +08:00
ibuler
490041587b [Update] 修改copyright 2019-04-29 10:39:31 +08:00
BaiJiangJie
3a3da94468 [Update] 修改预留/auth/login/页面方式(admin=1);解决luna页面刷新不跳转openid认证的问题 (#2634) 2019-04-25 18:55:48 +08:00
BaiJiangJie
b7ad6cfe62 [Update] 防止 XSS (#2633)
* [Bugfix] 修改管理用户列表显示bug

* [Bugfix] 修复刷新批量命令页面的bug

* [Update] 防止 XSS
2019-04-25 18:16:41 +08:00
ibuler
4463e7545d [Update] 去掉多余的 2019-04-25 14:34:47 +08:00
ibuler
d0eafc8b8e [Update] Merge 2019-04-25 14:32:53 +08:00
ibuler
8b98c20d68 [Update] xss 2019-04-25 14:31:34 +08:00
BaiJiangJie
caa5060ecd [Update] 控制组织管理员不允许更新、删除超级用户;修复ViewSet API批量更新的bug (#2629)
* [Update] 控制组织管理员不允许编辑(更新、删除)超级用户 - 待续(控制批量更新API)

* [Update] 修改方法名称

* [Update] 控制组织管理员不允许批量更新包含超级用户的用户列表

* [Bugfix] 修复所有ViewSet API进行批量更新时rest_framework_bulk库内部的bug

* [Update] 修改 OpenID Middleware 日志输出模式 info => debug
2019-04-25 10:11:50 +08:00
BaiJiangJie
aabcf7f31c [Update] 授权列表下拉信息添加action字段 (#2618) 2019-04-22 12:31:40 +08:00
ibuler
40d48cdfe4 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-04-22 12:07:59 +08:00
ibuler
8196537878 [Update] 去掉logger.info openid msg 2019-04-22 12:07:48 +08:00
BaiJiangJie
33a00f043b [Bugfix] 修复创建azure类型的录像存储时前端的bug (#2617) 2019-04-22 11:55:14 +08:00
BaiJiangJie
f235e20153 [Feature] 授权规则添加 actions 选项,控制用户对资产的操作行为 (#2610)
* [Feature] 1. perms actions - 添加 Action Model

* [Feature] 2. perms actions - 添加 Action API

* [Feature] 3. perms actions - 授权规则: 添加actions字段

* [Feature] 4. perms actions - 授权规则创建页面: 设置 actions 默认 all

* [Feature] 5. perms actions - 资产授权工具: 动态给system_user设置actions属性; 修改授权相关的API-serializer类: 添加actions字段值

* [Feature] 6. perms actions - 更新API(用户使用系统用户连接资产时权限校验): 添加actions校验

* [Feature] 7. perms actions - 迁移文件中为已经存在的perms添加默认的action

* [Feature] 8. perms actions - 创建授权规则时设置默认action(如果actions字段值为空)

* [Feature] 9. check actions - 修改校验用户资产权限API逻辑(添加actions校验)

* [Feature] 10. check actions - 修改注释

* [Feature] 11. check actions - 添加API: 获取用户指定资产和系统用户被授权的actions

* [Feature] 12. check actions - 添加翻译信息
2019-04-22 11:42:20 +08:00
BaiJiangJie
cf2455c084 [Bugfix] 修复创建azure类型的录像存储时前端的bug (#2608) 2019-04-18 11:50:30 +08:00
BaiJiangJie
fc1068a9dc [Update] 更新翻译 (#2599) 2019-04-15 16:34:56 +08:00
zhnxin
35a0ca1875 Update node.py (#2527)
bugfix: node get_all_children逻辑
2019-04-15 14:43:49 +08:00
八千流
56519354b6 Email reset password (#2547)
* [Update]更改英文登录界面标题,可两行

* [Update] 更改用户通过邮箱修改密码后,该链接就失效

* [Update]更改页面左上角logo_text图片

* [Update] 优化发送邮箱改密连接失效代码

* [Update] 优化发送邮箱改密连接失效代码(2)

* [Update] 优化发送邮箱改密连接失效代码(3)

* [Update] 更新interface一键恢复默认的翻译

* [Update] 更改登录失败 用户名密码错误信息的翻译

* [Update] 优化生成token并设置缓存的代码
2019-04-15 14:37:45 +08:00
jokimina
78e4e13fb9 [Bugfix] IsSuperUserOrAppUser deny app user (#2558) 2019-04-15 14:33:40 +08:00
八千流
699b8d9980 [Update] 修改批量更新资产url过长导致的错误 (#2571) 2019-04-15 14:33:16 +08:00
zbhlove100
ba9581801c Update README.md 2019-04-09 16:57:01 +08:00
zbhlove100
0a5fdf4ea1 功能转为表格 2019-04-09 16:53:54 +08:00
zhangbohan
3849fa2b15 update readme 2019-04-09 15:27:37 +08:00
zhangbohan
0952cbc7c6 英文readme 2019-04-09 12:21:13 +08:00
zhangbohan
bb06c39dd4 更新readme 2019-04-09 12:09:11 +08:00
zhangbohan
d60dc31443 更新readme 2019-04-08 18:30:39 +08:00
老广
76b3cd8edd Merge pull request #2583 from jumpserver/dev
Dev
2019-04-04 12:33:16 +08:00
老广
638ba31694 Merge pull request #2582 from jumpserver/dev_bugfix_assets
[Bugfix] 修复资产对象获取所有节点时的bug
2019-04-04 12:21:42 +08:00
BaiJiangJie
c31b169cae [Bugfix] 修复资产对象获取所有节点时的bug 2019-04-04 12:17:53 +08:00
老广
fc167526ae Merge pull request #2575 from jumpserver/dev
[Bugfix] 修复OpenID判断退出登录的中间件逻辑的bug (#2574)
2019-04-03 12:19:23 +08:00
BaiJiangJie
55eff5eab9 [Bugfix] 修复OpenID判断退出登录的中间件逻辑的bug (#2574) 2019-04-03 12:18:58 +08:00
老广
f5a7f4e086 Merge pull request #2570 from jumpserver/dev
Bugfix (#2569)
2019-04-02 16:26:28 +08:00
老广
f7b0932cdd Bugfix (#2569)
* [Update] 修改Logo 的位置

* [Update] 修改镜像build
2019-04-02 16:25:39 +08:00
老广
ba89ce8fb9 Merge pull request #2560 from jumpserver/dev
[Update] 修复小问题 (#2557)
2019-03-29 19:34:28 +08:00
BaiJiangJie
9d62deeabe [Update] 修复小问题 (#2557)
* [Update] 修复小问题

* [Bugfix] 修复操作日志动作、资源类型过滤不成功的bug
2019-03-29 19:14:18 +08:00
老广
459b41f327 Merge pull request #2559 from jumpserver/dev
[Update] 修复重定向的问题
2019-03-29 15:54:20 +08:00
ibuler
3062e3f64a [Update] 修复重定向的问题 2019-03-29 15:53:31 +08:00
老广
c1362ca4e2 Merge pull request #2552 from jumpserver/dev
Dev
2019-03-28 13:13:36 +08:00
ibuler
9d24912ad9 [Update] Merge 2019-03-28 13:12:19 +08:00
老广
db290609a8 Merge pull request #2551 from jumpserver/dev
Dev
2019-03-28 13:07:34 +08:00
ibuler
4bc5eced6c Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-03-28 13:07:04 +08:00
BaiJiangJie
b82a66c83d [Update] 优化LDAP用户导入功能 (#2550) 2019-03-28 12:50:04 +08:00
ibuler
bf7079df9e [Update] 修改logo 2019-03-28 12:35:16 +08:00
ibuler
f137c5740e [Update] 添加openldap client 2019-03-28 11:53:52 +08:00
ibuler
ee47905966 [Update] 修改超时时间 2019-03-27 20:39:31 +08:00
fit2cloudrd
f6cd193f9e Update README.md (#2548)
* Dev (#2544)

* [Update] 更新翻译 (#2541)

* [Update] 更新缓存机制

* [Update] 增加task最大允许事件,并设置命令最大运行时间为60s

* Update README.md
2019-03-27 18:47:38 +08:00
ibuler
a31775dd23 [Update] 整理 copyright 和 版本 2019-03-27 17:46:35 +08:00
老广
30ba1e5886 Merge pull request #2545 from jumpserver/dev
Dev
2019-03-27 13:04:58 +08:00
ibuler
f97bfa7bf1 [Update] 修复s3创建时端点不含协议的提示 2019-03-27 12:49:46 +08:00
ibuler
ace028fa7f [Update] 统一可更改的interface 2019-03-27 12:20:43 +08:00
老广
69f6401e87 Dev (#2544)
* [Update] 更新翻译 (#2541)

* [Update] 更新缓存机制

* [Update] 增加task最大允许事件,并设置命令最大运行时间为60s
2019-03-27 11:21:21 +08:00
ibuler
bd4d974df1 [Update] merge 2019-03-27 11:13:35 +08:00
ibuler
6e7446f530 [Update] 增加task最大允许事件,并设置命令最大运行时间为60s 2019-03-27 11:12:34 +08:00
ibuler
afe9471aa2 [Update] 更新缓存机制 2019-03-26 19:46:04 +08:00
BaiJiangJie
4d56b84861 [Update] 更新翻译 (#2541) 2019-03-26 15:47:24 +08:00
老广
8fede58c64 Merge pull request #2456 from jumpserver/dev
Dev
2019-03-26 11:39:43 +08:00
ibuler
370904212f [Update] 修改翻译 2019-03-25 20:36:20 +08:00
ibuler
ae03a5aeb7 [Update] 翻译问题 2019-03-25 19:57:18 +08:00
ibuler
24a38841dd [Update] 修改Dockerfile 2019-03-22 18:26:25 +08:00
ibuler
bbc6156bd7 [Update] 数据库支持CA 2019-03-22 18:06:46 +08:00
ibuler
f387df41d7 [Update] 修改版本 2019-03-22 17:23:02 +08:00
ibuler
ceb8b2f5b3 [Update] 修改翻译 2019-03-22 17:16:02 +08:00
ibuler
877781a6ca [Update] 删掉一个migrations 2019-03-22 16:56:46 +08:00
BaiJiangJie
612d5efd1b [Update] 抽象Inventory,更新翻译 (#2530)
* [Update] 抽象Inventory, 支持自定义用户名密码构建Inventory

* [Update] 更新翻译
2019-03-22 16:52:26 +08:00
ibuler
7ea03801d0 [Update] 修改翻译 2019-03-22 15:57:37 +08:00
八千流
ce8f4b4a48 Ldap synchronization (#2512)
* [Add]初步实现ldap一键导入用户到jumpserver用户表里

* [update]增加定时延迟一秒刷新页面

* [Update]更改前端以表格形式显示用户信息,优化代码结构

* [Update]增加用户显示表格

* [Update]settings配置文件取消注释

* [Update]优化ldap同步用功能代码

* [Update]删除ldap同步用户旧html模版

* [Update]修改登录页面图片拉伸问题

* [Update]增加 是否已经导入,在前端提示用户

* [Update]优化ldap同步用户的代码,以及翻译

* [Update] 更新翻译(改密计划) (#2525)

* [Update] 更新翻译(添加改密计划)

* [Update] 更新翻译(改密计划)

* [Update] 更新翻译

* [Update] 更新翻译

* Export login log (#2511)

* [Add]增加登录日志导出功能

* [Update]优化导出登录日志代码

* [Update]优化导出登录日志代码

* [Update]更改导出登录日志按钮
2019-03-22 15:55:46 +08:00
ibuler
15179d2450 [Update] 修改批量命令的翻译 2019-03-22 15:55:20 +08:00
ibuler
9aae106970 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-03-22 15:19:24 +08:00
ibuler
c82044f6bc [Update] 修改一些bug 2019-03-22 15:18:47 +08:00
八千流
e4e6f59589 Export login log (#2511)
* [Add]增加登录日志导出功能

* [Update]优化导出登录日志代码

* [Update]优化导出登录日志代码

* [Update]更改导出登录日志按钮
2019-03-22 15:17:22 +08:00
BaiJiangJie
c8aa9d006f [Update] 更新翻译(改密计划) (#2525)
* [Update] 更新翻译(添加改密计划)

* [Update] 更新翻译(改密计划)

* [Update] 更新翻译

* [Update] 更新翻译
2019-03-22 09:42:39 +08:00
老广
311538dcf8 Bugfix (#2513)
* [Update] 用户页面添加跳转

* [Update] 网关测试支持nat, 修复创建node等id不能指定的问题, 修复settings频繁redis, 没有has_replay录像不可以播放
2019-03-19 19:09:09 +08:00
老广
324cf2469f Bugfix (#2506)
* [Update] 增加清理celery日志

* [Update] 修复两周前会话命令数量系那是问题

* [Update] 修复两周前会话命令数量系那是问题

* [Update] 修改结构

* [Update] 添加datatable失败的日志

* [Update] 转换配置文件格式

* [Update] 添加traceback
2019-03-18 11:34:13 +08:00
ibuler
01745ead1f [Update] 添加migrations 2019-03-18 10:26:40 +08:00
BaiJiangJie
4e705a52eb [Feature] 添加资产用户管理器 (#2489)
* [Feature] 1. 资产用户管理器

* [Feature] 2. 资产用户管理器: 更新AuthBook

* [Feature] 3. 资产用户管理器: 添加 AssetUser API

* [Feature] 4. AssetUser Model: 添加方法 load_related_asset_auth

* [Feature] 5. AdminUser: 更新管理用户获取认证信息时,先加载相关资产的认证

* [Feature] 6. SystemUser: 更新系统用户获取认证信息时,先加载相关资产的认证

* [Feature] 前端页面: 添加资产用户列表页面

* [Feature] 前端页面: 管理用户的资产管理页面添加按钮: 修改资产用户认证信息

* [Feature] 前端页面: 系统用户的资产管理页面添加按钮: 修改资产用户认证信息

* [Feature] 优化: 从管理用户和系统用户的backend中获取相关资产用户的逻辑

* [Update] Fix 1

* [Feature] 优化: SystemUserBackend之filter功能

* [Feature] 优化: AdminUserBackend之filter功能

* [Feature] 优化: AdminUserBackend和SystemUserBackend功能

* [Feature] 更新翻译: 资产用户管理器

* [Update] 更新资产用户列表页名称为: asset_asset_user_list.html

* [Bugfix] 修改bug: SystemUserBackend 根据用户名过滤系统用户

* [Feature] 添加: 资产用户列表中可测试资产用户的连接性

* [Update] 修改: AdHoc model的run_as字段从SystemUser外键修改为username字符串

* [Feature] 添加: 获取系统用户认证信息(对应某个资产)API

* [Update] 更新: API获取asset user时进行排序

* [Bugfix] 修改: 资产用户可连接性CACHE_KEY

* [Update] 更新翻译信息

* [Update] 修改获取资产用户认证信息API的返回响应(200/400)

* [Update] 修改BaseUser获取特定资产的方法名

* [Update] 修改logger输出,AuthBook set_version_and_latest

* [Update] 修改日志输出添加exc_info参数

* [Update] 移除AuthBook迁移文件0026

* [Bugfix] 修复AdminUserBackend获取instances为空的bug
2019-03-18 10:15:33 +08:00
ibuler
9bb58afee1 [Update] bugfix 2019-03-11 10:27:40 +08:00
老广
b45b33380c Monitor (#2485)
* [Update]

* [Update] 修改fields
2019-03-11 10:06:45 +08:00
BaiJiangJie
c86a036ac6 [Update] OpenID认证流程添加详细日志 (#2462)
* [Update] OpenID认证流程添加详细日志

* [Update] 优化日志格式
2019-03-07 18:41:42 +08:00
ibuler
8694511d86 [Update] 完成 2019-03-06 14:58:25 +08:00
ibuler
58c4a46f6e [Update] 用户权限增加cache 2019-03-05 19:47:14 +08:00
ibuler
dfd26d88d4 [Update] 用户资产添加缓存 2019-03-04 20:45:57 +08:00
ibuler
dcf6959cff [Update] 每次启动失效设置的cache 2019-03-04 18:24:02 +08:00
ibuler
924affd978 [Update] 更新移动的model 2019-03-04 17:07:51 +08:00
ibuler
ad6d233c11 [Update] 修改表名称 2019-03-04 15:51:17 +08:00
ibuler
d84ab1d215 [Update] 修改迁移 2019-03-04 15:38:59 +08:00
ibuler
a5fc04e0ce [Update] merge 2019-03-04 11:36:10 +08:00
ibuler
7f71513085 [Update] 去掉fixtures 2019-03-04 11:32:28 +08:00
ibuler
6004ef3f0d [Update] 修改settings 2019-03-04 10:53:49 +08:00
ibuler
e76392a169 Merge branch 'stable' into dev 2019-03-04 10:47:31 +08:00
ibuler
53f0b2e9b0 [Update] 增加清理登陆日志的任务 2019-03-04 10:39:44 +08:00
ibuler
de79e36251 [Update] 增加配置 2019-03-04 10:17:35 +08:00
ibuler
c84e984eae [Update] 任务列表去掉日期 2019-03-01 18:26:13 +08:00
ibuler
6d65c967b1 [Update] config example增加radius 2019-03-01 18:21:14 +08:00
ibuler
8199ea84f4 [Bugfix] 修复有时导致的token解析失败 2019-03-01 13:50:57 +08:00
ibuler
ce1b0da09d [Update] Merge 2019-02-28 18:24:52 +08:00
ibuler
cd6bb848e9 [Update] Squash table 2019-02-28 18:23:39 +08:00
ibuler
34040fcd59 [Update] 移动model 2019-02-28 17:58:53 +08:00
ibuler
1969fb79fe [Update] 修改authentication backends 2019-02-28 15:12:45 +08:00
BaiJiangJie
904f64604b [Bugfix] 修复authentication模块之间引用的bug 2019-02-28 14:41:33 +08:00
ibuler
9b3509208d [Update] 修改authentication目录结构 2019-02-28 11:58:48 +08:00
BaiJiangJie
6700dc969f [Update] users认证逻辑迁移到authentication中 2019-02-27 20:55:28 +08:00
ibuler
21714cc411 [Update] Stash 2019-02-27 08:45:00 +08:00
ibuler
1a247d60e7 [Update] sudo命令添加help text 2019-02-26 16:49:55 +08:00
jokimina
adf8b1f7aa [Bugfix] operation on closed file. (#2438) 2019-02-26 12:42:34 +08:00
老广
69f640daa4 Api (#2439)
* [Update] 迁移settings到独立app

* [Update] 修改settings migrations

* [Update] 修改docs说明
2019-02-26 12:38:20 +08:00
老广
a43314f5be Merge pull request #2432 from jokimina/patch-5
[Bugfix] celery beat crontab task timezone issue
2019-02-25 10:43:52 +08:00
jokimina
599e8a7e37 [Bugfix] crontab task timezone issue 2019-02-24 21:34:45 +08:00
老广
0e00451e1f Merge pull request #2425 from jumpserver/dev
[Update] 修改ldap test密码为空
2019-02-22 19:21:21 +08:00
ibuler
866e5d2011 [Update] 修改ldap test密码为空 2019-02-22 19:14:07 +08:00
老广
232674b1c1 Merge pull request #2423 from jumpserver/dev
Dev
2019-02-22 17:10:05 +08:00
ibuler
ddf60d2512 [Update] 添加jms日志和修改entrypoint 2019-02-22 16:33:27 +08:00
ibuler
3e6e0153cf [Update] 修改entrypoint并增加debug日志 2019-02-22 16:14:50 +08:00
ibuler
6b984aac53 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-02-22 12:49:03 +08:00
ibuler
6d3ee8116e [Update] 修改版本号 2019-02-22 12:48:43 +08:00
老广
904a0f67dd Merge pull request #2422 from jumpserver/dev
Dev
2019-02-22 10:16:08 +08:00
BaiJiangJie
81e1ce2688 [Update] Interface - 1. settings 添加TERMINAL_HEADER_TITLE (#2421) 2019-02-21 19:36:15 +08:00
ibuler
7c422d2ed6 [Update] Merge 2019-02-21 19:24:52 +08:00
ibuler
2a5129c481 [Update] 修改支持记录cmd filter日志 2019-02-21 19:22:23 +08:00
BaiJiangJie
28cdfca14b [Update] 优化 License (#2420)
* [Update] License - 1.添加标签过滤器

* [Update] License - 2.添加翻译信息
2019-02-21 18:49:40 +08:00
ibuler
202aba048b [Update] 修改用户页面授权资产 2019-02-21 17:35:28 +08:00
ibuler
3c2a4703bc [Update] ldap允许空密码 2019-02-21 16:37:36 +08:00
ibuler
4904aac2df [Update] 更新依赖版本 2019-02-21 16:16:08 +08:00
ibuler
303f88d6ee [Update] 更新replay storage 配置 2019-02-21 16:13:08 +08:00
老广
cccc74279d Dev license (#2409)
* [Update] 更新用户组

* [Update] license - 更新翻译

* [Feature] interface - 自定义配置logo,修改前端(待续)

* [Update] interface更新前端页面,两个登陆首页的模版设

* [Update]修改新登陆模版全局变量,国际化翻译,登陆逻辑页面切换

* [Update] 优化Interface前端逻辑

* [Update] Interface 修改前端一些小问题

* [Update] License/Interface 生成翻译信息
2019-02-20 19:20:13 +08:00
老广
16db2abca5 Merge pull request #2419 from jumpserver/dev
Dev
2019-02-20 19:02:40 +08:00
ibuler
859f2d9795 [Update] 支持telnet自定义正则 2019-02-20 19:00:01 +08:00
ibuler
4fd9957bec [Update] 修改celery日志显示 2019-02-20 17:51:53 +08:00
老广
0ac6e6ba2c Merge pull request #2418 from jumpserver/dev
[Update] 修改gunicorn日志
2019-02-20 16:16:44 +08:00
ibuler
785cc04126 [Update] 修改gunicorn日志 2019-02-20 16:11:58 +08:00
老广
f269eae774 Merge pull request #2415 from jumpserver/dev
[Update] 修改导入
2019-02-20 10:41:59 +08:00
ibuler
6f19fcb702 [Update] 修改tasks 2019-02-20 10:40:43 +08:00
老广
b7b6218306 Merge pull request #2414 from jumpserver/dev
[Update] gunicorn的日志
2019-02-19 20:06:37 +08:00
ibuler
5cd809b48a [Update] 修改导入 2019-02-19 20:03:02 +08:00
ibuler
5a1b894138 [Update] gunicorn的日志 2019-02-19 16:59:00 +08:00
老广
4d402617b6 Merge pull request #2410 from jumpserver/dev
Dev
2019-02-19 13:04:00 +08:00
ibuler
666ef366e7 [Bugfix] 修复session不显示在线的问题 2019-02-19 12:50:33 +08:00
ibuler
28d029a553 [Update] 修改settings 2019-02-19 12:18:39 +08:00
老广
00763e986a Merge pull request #2408 from jumpserver/dev
[Update] 修改token有效期
2019-02-19 12:00:04 +08:00
ibuler
f9a7cca478 [Update] 修改token有效期 2019-02-19 10:29:25 +08:00
老广
d09b34e232 Merge pull request #2404 from jumpserver/dev
[Update] 修改配置文件判断
2019-02-18 15:10:36 +08:00
ibuler
2737675c36 [Update] 修改配置文件判断 2019-02-18 15:09:02 +08:00
老广
7591f40b2c Merge pull request #2402 from jumpserver/dev
[Update] 创建子节点支持id
2019-02-18 13:02:15 +08:00
ibuler
c4af6fa72d [Update] 创建子节点支持id 2019-02-18 12:59:36 +08:00
老广
19be7ac580 Merge pull request #2394 from jumpserver/dev
Dev
2019-02-15 11:16:54 +08:00
ibuler
49404f763d [Update] 修改terminal字体 2019-02-15 11:15:46 +08:00
ibuler
87f2a67789 [Update] 修改录像获取 2019-02-15 11:14:37 +08:00
老广
041edb6177 Merge pull request #2392 from jumpserver/dev
Dev
2019-02-15 10:44:32 +08:00
ibuler
df2fad76c7 [Update] 修改Dockerfile 优先使用阿里云镜像 2019-02-14 16:23:56 +08:00
ibuler
94020a8fbb [Update] 修改日志存储 2019-02-14 16:15:28 +08:00
ibuler
cb1e19d28f [Update] 修改录像路径使用utc 2019-02-14 15:47:55 +08:00
ibuler
0980dffb47 [Update] 修复一下新建两个节点的bug 2019-02-13 19:38:51 +08:00
ibuler
4051225ecb [Bugfix] 修复命令执行bug,修复修改日志级别后无法查看日志 2019-02-13 15:55:11 +08:00
老广
507518da04 Merge pull request #2369 from jumpserver/dev
[Update] 修改版本号
2019-01-29 19:33:06 +08:00
ibuler
d3bdbc0b81 [Update] 修改版本号 2019-01-29 19:24:28 +08:00
老广
3b56027edc Merge pull request #2364 from jumpserver/dev
Dev
2019-01-29 17:45:31 +08:00
ibuler
8285610097 [Update] 创建Host Key 2019-01-29 14:07:46 +08:00
ibuler
6acac9cb3d Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-01-29 12:28:14 +08:00
ibuler
9d78f26807 [Update] 创建Host Key 2019-01-29 12:27:51 +08:00
BaiJiangJie
9d53ba22e1 [Update] 取消ceph录像存储,添加命令执行翻译 (#2365) 2019-01-28 14:59:06 +08:00
ibuler
0d0cf04543 [Update] 修改日志查看 2019-01-28 12:18:53 +08:00
ibuler
56a47b6ba3 [Update] 存储添加说明 2019-01-25 14:55:56 +08:00
ibuler
1dbcf4e3ab [Update] 数据库make migration移动到gunicorn 2019-01-24 11:25:13 +08:00
ibuler
95fcd60f64 [Update] 兼容之前的node api 2019-01-22 12:44:01 +08:00
ibuler
178055eb57 [Update] 修改任务小写 2019-01-21 17:43:08 +08:00
老广
01a101a710 [Update] 修改一些terminal storage (#2357) 2019-01-21 17:05:31 +08:00
ibuler
eee6dd1436 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-01-21 12:27:28 +08:00
ibuler
27693c6288 [Update] 修改配置文件 2019-01-21 12:27:20 +08:00
ibuler
55b55f6162 [Update] 修改测试连接节点时,资产为下面的所有资产 2019-01-21 11:58:51 +08:00
ibuler
384cdfbc19 [Update] 启动脚本 2019-01-19 19:29:38 +08:00
ibuler
c8d007f9d7 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-01-19 16:52:59 +08:00
ibuler
d8069f47f2 [Update] 修改配置文件加载 2019-01-17 20:38:56 +08:00
ibuler
d664018bd7 [Update] 修改config.py 2019-01-17 20:32:10 +08:00
ibuler
0f32e78891 [Update] 修改dockerfile 2019-01-17 20:26:47 +08:00
ibuler
6e061d2da5 [Bugfix] 修改小bug 2019-01-17 18:39:38 +08:00
ibuler
b2717133ee [Update] 修改日志目录 2019-01-17 18:06:47 +08:00
老广
2a0e68c58f Bugfix (#2350)
* [Update] 权限页面增加过滤规则

* [Update] 修改terminal注册,更新以后使用api完成

* [Update] 修改terminal注册,更新以后使用api完成

* [Update] 修改更新注册逻辑
2019-01-16 18:13:16 +08:00
老广
176052e8e9 [Update] 权限页面增加过滤规则 (#2349) 2019-01-15 19:01:33 +08:00
老广
d026b31c9f Bugfix (#2346)
* [Update] 修改command Post导致的output错误和定时任务创建问题

* [Update] 修改celery 日志

* [Update] 修改task日志方式

* [Update] 修改Docker file
2019-01-15 10:23:30 +08:00
老广
50c1b3ed4a [Update] 统一coco host key (#2336) 2019-01-10 11:50:08 +08:00
mago960806
131e588d82 is_running()里的打开pid文件的操作已经在get_pid()中执行过了,不需要再次执行 (#2335) 2019-01-10 10:21:05 +08:00
xiaomao
4bf0dfcf7b [bugfix] 解决日志中文报错以及各种utf8问题 (#2328) 2019-01-08 15:27:15 +08:00
老广
49a166552e Bugfix (#2327)
* [Bugfix] 修复两个配置文件冲突问题

* [Update] Docker中不再提供配置文件
2019-01-08 11:15:09 +08:00
老广
0e1d3f93ff [Update] 支持radius认证 (#2323)
* [Update] 支持radius认证

* [Update] 支持radius

* [Update] 增加requirements

* [Update] 修改copyright

* [Update] 修改migrations
2019-01-07 19:20:39 +08:00
老广
35403086ab Config (#2322)
* [Update] 修改配置文件

* [Update] 修改配置文件形式
2019-01-07 18:55:02 +08:00
ibuler
2fde6cfe24 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-01-03 12:41:21 +08:00
老广
ed1dc7a984 Merge pull request #2300 from jumpserver/dev
Dev
2019-01-03 11:52:13 +08:00
老广
9ab3f0441f Merge pull request #2298 from jumpserver/dev_bugfix
[Bugfix] 修复-用户页面资产详情显示bug
2019-01-03 10:41:48 +08:00
BaiJiangJie
d6567f0e57 [Bugfix] 修复-用户页面资产详情显示bug 2019-01-03 10:39:09 +08:00
老广
84bd465b30 Merge pull request #2296 from jumpserver/dev
Dev
2019-01-02 15:39:05 +08:00
ibuler
d0af8eba32 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2019-01-02 15:35:06 +08:00
ibuler
b2a8415f77 [Update] 修改创建子节点api 2019-01-02 15:33:35 +08:00
老广
545d4fa691 Merge pull request #2287 from jumpserver/dev
[Update] 禁用其他认证方式修改密码 (#2286)
2018-12-27 18:58:52 +08:00
老广
560df5027a [Update] 禁用其他认证方式修改密码 (#2286)
* [Update] 禁用其他认证方式修改密码

* [Update] 禁用其他认证方式修改密码

* [Update] 禁用其他认证方式修改密码
2018-12-27 16:47:40 +08:00
老广
e3db7462f7 Merge pull request #2284 from jumpserver/dev
Dev
2018-12-27 11:45:38 +08:00
老广
b55d137e7f Merge pull request #2283 from jumpserver/dev_bai
[Update] 修改在线会话终端按钮vnc协议disable
2018-12-27 11:44:58 +08:00
BaiJiangJie
7c10f8743f [Update] 修改在线会话终端按钮vnc协议disable 2018-12-27 11:39:11 +08:00
ibuler
c5d1ed126e [Update] 修改Jms脚本 2018-12-26 19:20:32 +08:00
老广
6b02cdfc37 Merge pull request #2279 from jumpserver/dev
Dev
2018-12-26 14:56:25 +08:00
BaiJiangJie
340c615efe [Bugfix] session model 添加 vnc 协议 (#2278)
* [Bugfix] session model 添加 vnc 协议

* [Update] 修改表结构
2018-12-26 14:43:43 +08:00
BaiJiangJie
8e51f97dc7 [Update] 添加资产树右击菜单: 刷新所有节点资产数量 (#2274)
* [Update] 添加资产树右击菜单: 刷新所有节点资产数量

* [Update] 修改右击菜单样式
2018-12-26 13:54:42 +08:00
BaiJiangJie
76a08c9039 [Bugfix] 修复创建/更新 Perms 时,名称重复不提示错误信息的问题 (#2272) 2018-12-26 12:59:09 +08:00
BaiJiangJie
b9b8c35a81 [Update] 修改创建资产选择vnc协议,默认端口5901 (#2276)
* [Update] 修改创建资产选择vnc协议,默认端口5901

* [update] 修改创建命令/录像存储时账户无效的提示信息
2018-12-26 12:57:59 +08:00
老广
e8fba2ec44 Merge pull request #2270 from jumpserver/dev
Dev
2018-12-25 16:56:02 +08:00
老广
61df6f55b9 Merge pull request #2269 from jumpserver/bugfix
[Update] 修改bug
2018-12-25 16:55:13 +08:00
ibuler
ef02b1f83a [Update] 修改bug 2018-12-25 16:53:52 +08:00
老广
0798e3c466 Merge pull request #2266 from jumpserver/dev
Dev
2018-12-25 14:32:47 +08:00
老广
a990098744 [Update] 优化资产选择 (#2267)
* [Update] 优化资产选择

* [Update] 优化资产任务
2018-12-25 13:33:37 +08:00
BaiJiangJie
dab692c0eb [Update] 限制终端设置中心跳间隔和会话保留时长的form最小值 (#2262)
* [Update] 限制终端设置中心跳间隔和会话保留时长的form最小值

* [Update] 删除terminal forms表单的初始化值

* [Update] 取消安全设置中forms的初始化值,并采用默认值;添加密码过期时间的最大值限制
2018-12-25 09:58:01 +08:00
BaiJiangJie
8e93bfecb0 [Bugfix] 修复DEFAULT组织下,批量删除(某组织下)一部分用户失败的bug (#2261) 2018-12-24 15:50:58 +08:00
ibuler
e5953e1932 Merge branch 'dev' of bitbucket.org:jumpserver/core into dev 2018-12-23 22:59:30 +08:00
老广
67b21f0489 Merge pull request #2254 from jumpserver/dev
Dev
2018-12-21 15:58:56 +08:00
ibuler
e15c9e6588 [Update] 修改小bug 2018-12-21 15:58:19 +08:00
ibuler
94d6525548 [Bugfix] 修复小bug 2018-12-21 15:57:23 +08:00
老广
a14d6b298d Merge pull request #2253 from jumpserver/dev
Bugfix (#2252)
2018-12-21 15:48:29 +08:00
老广
e7725e6910 Bugfix (#2252)
* [Update] 修改心跳偶人时间

* [Update] 修改Node比较

* [Bugfix] 修复bug
2018-12-21 15:47:52 +08:00
老广
23bf2b0f8e Merge pull request #2250 from jumpserver/dev
Dev
2018-12-21 15:10:37 +08:00
BaiJiangJie
ac9178cb93 [Update] 更新deb_requirements依赖 (#2239) 2018-12-21 14:59:00 +08:00
老广
7ff39259af Required opt (#2246)
* [Update] 修改心跳偶人时间

* [Update] 去掉required的label

* [Update] 修改默认心跳时间

* [Update] 去掉默认的placeholder

* [Update] 修改utils
2018-12-21 14:57:42 +08:00
ibuler
b2aef87fdd [Update] 修改心跳偶人时间 2018-12-21 10:35:17 +08:00
老广
e1f1bed9c9 Merge pull request #2231 from jumpserver/dev
Dev
2018-12-20 11:44:34 +08:00
老广
08945f0a19 [Bugfix] 修复adhoc 日志查看的bug, 修改config example (#2230) 2018-12-20 11:42:00 +08:00
ibuler
a1b80f5f0b [Update] 清理task adhoc和history 2018-12-20 11:05:36 +08:00
ibuler
7773c30240 [Update] 增加索引 2018-12-20 10:40:52 +08:00
jokimina
231c907c64 [Update] ops adhoc date_created index (#2223) 2018-12-20 10:34:42 +08:00
BaiJiangJie
db8882a2b9 [Bugfix] 修复创建网域时,选择资产的input和弹出table显示不一致的bug (#2224) 2018-12-20 10:32:41 +08:00
ibuler
e5285f312b [Bugfix] 修复vnc录像的bug 2018-12-19 19:55:41 +08:00
老广
332be54b46 Vnc (#2226)
* [Update] 优化授权协议, 支持vnc

* [Update] 添加协议vnc

* [Update] 修改系统用户添加

* [Update] 修改vnc信息
2018-12-19 19:40:58 +08:00
老广
fe7c3c29ad [Update] 优化授权协议, 支持vnc (#2220)
* [Update] 优化授权协议, 支持vnc
2018-12-19 17:03:10 +08:00
老广
0e9ebed19d Merge pull request #2214 from jumpserver/dev
Dev
2018-12-19 13:57:15 +08:00
老广
4a3327bc4b Merge pull request #2213 from jumpserver/bugfix
[Bugfix] 修复录像bug
2018-12-19 13:56:30 +08:00
ibuler
5d47bebb6b [Bugfix] 修复录像bug 2018-12-19 13:55:49 +08:00
老广
2ece3545ed Merge pull request #2210 from jumpserver/dev
Dev
2018-12-19 12:33:34 +08:00
ibuler
a9a1bae805 [Update] 修改列表页,显示可连接状态 2018-12-19 12:32:53 +08:00
ibuler
f35c02b346 [Update] 修改列表页,显示可连接状态 2018-12-19 12:30:51 +08:00
老广
32df515f4b Merge pull request #2209 from jumpserver/dev
Dev
2018-12-19 12:02:16 +08:00
ibuler
71750970b2 [Update] 暂时去掉刷新资产树 2018-12-19 12:01:18 +08:00
ibuler
a85099ee60 [Update] 修改版本号 2018-12-19 11:43:13 +08:00
老广
a5b9b4e1d2 Merge pull request #2206 from jumpserver/dev
Dev
2018-12-19 10:53:00 +08:00
老广
b3079a4a9b [Update] Add index (#2208) 2018-12-19 10:49:30 +08:00
ibuler
9a22874305 [Update] 去掉多余的任务 2018-12-18 20:14:44 +08:00
ibuler
179018bf67 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-18 20:07:16 +08:00
ibuler
583214e91a [Update] 优化获取授权资产api 2018-12-18 20:01:59 +08:00
BaiJiangJie
fb44ef0986 [Bugfix] 修复 资产授权/标签 选择资产时,input框内的资产和弹出资产tabl显示不一致的bug (#2205)
* [Bugfix] 修复授权规则,选择资产时,输入框资产和弹出表格中资产显示不一致

* [Bugfix] 修复创建/更新资产标签,选择资产时,输入框资产和弹出表格中资产显示不一致的bug

* [Update] 修复标签/授权选择资产时, 2次初始化table的bug;资产input和弹出table统一的代码逻辑移到js中;
2018-12-18 18:50:02 +08:00
ibuler
90b77fdb08 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-18 17:30:59 +08:00
老广
a609f17078 [Update] Stash it (#2197)
* [Update] Stash it

* [Bugfix] 修复错误

* [Update] 修改jms
2018-12-18 17:28:45 +08:00
ibuler
068a280350 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-18 11:38:42 +08:00
老广
1293d72189 Session task (#2196)
* [Bugfix] 修复错误

* [Update] 增加会话定期清理
2018-12-18 11:29:21 +08:00
ibuler
164c5ebabd Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-18 09:52:25 +08:00
老广
b56d73ba9e Node asset amount (#2192)
* [Bugfix] 修复错误

* [Update] 优化用户api
2018-12-17 20:09:25 +08:00
ibuler
dbdcdb722d Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-17 18:21:51 +08:00
老广
517a27ea33 Node asset amount (#2191)
* [Bugfix] 修复错误

* [Update] 修改树结构,统一api
2018-12-17 18:20:44 +08:00
vkill
ab6c88823d Support for TOTP valid_window configuration (#2187) 2018-12-17 14:26:00 +08:00
ibuler
1ff9f0eaa6 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-17 11:50:18 +08:00
老广
b95f8a7d6b [Update] 修复获取节点数量比较慢的问题 (#2184) 2018-12-17 11:49:57 +08:00
ibuler
29ff0efdc1 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-17 11:45:37 +08:00
老广
985bd6fc82 Bugfix2 (#2183)
* [Bugfix] 修复错误

* [Bugfix] 修复一些bug
2018-12-17 11:44:43 +08:00
BaiJiangJie
374039d287 [Update] 终端设置,添加coco端资产列表页面大小配置项 (#2182)
* [Update] 终端设置,添加coco端资产列表页面大小配置项

* [Update] 添加页面大小选项
2018-12-17 11:18:55 +08:00
ibuler
c7ac93fcc1 [Bugfix] 修复错误 2018-12-17 10:21:16 +08:00
老广
ac7e3e7f97 Merge pull request #2161 from jumpserver/dev
[Update] 更改readme
2018-12-13 10:16:08 +08:00
ibuler
4e0b25ae0f [Update] 更改readme 2018-12-13 10:15:31 +08:00
老广
559f4d2f5c Merge pull request #2160 from jumpserver/dev
[Bugfix] 修复access key的错误
2018-12-13 10:08:16 +08:00
ibuler
f40f6bc61e [Bugfix] 修复accekt的错误 2018-12-13 10:06:51 +08:00
老广
0f61b36bff Merge pull request #2155 from jumpserver/dev
Dev
2018-12-12 18:04:27 +08:00
ibuler
55ff82545a [Bugfix] 修复任务执行args为空的bug 2018-12-12 18:03:28 +08:00
ibuler
a99d5609fa [Bugfix] 修复运行测试硬件等报错的bug 2018-12-12 18:00:21 +08:00
老广
dbc2779b34 Merge pull request #2151 from jumpserver/dev
Dev
2018-12-12 15:58:57 +08:00
ibuler
33b1de0d85 [Update] 优化一下 2018-12-12 15:56:36 +08:00
ibuler
bcfe82f162 [Update] 优化错误颜色 2018-12-12 12:40:45 +08:00
ibuler
82af5f8f16 [Update] 资产详情页面增加网域 2018-12-12 12:28:13 +08:00
ibuler
84f52eb337 [Update] 修改api 2018-12-12 12:11:12 +08:00
老广
f00a650366 Merge pull request #2145 from jumpserver/dev
Dev
2018-12-12 10:54:41 +08:00
ibuler
2fedeb9834 [Update] 修改命令错误输出 2018-12-12 10:41:28 +08:00
ibuler
daadcedc21 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-12 09:57:26 +08:00
ibuler
951e8261ad [Update] 修改版本 2018-12-12 09:57:02 +08:00
老广
9c0f00f625 [Update] 修改命令执行 (#2148)
* [Update] 修改命令执行

* Update forms.py
2018-12-11 20:32:55 +08:00
老广
6a23983331 Command (#2142)
* [Update] 修改节点

* [Update] 优化命令运行失败日志
2018-12-11 12:51:22 +08:00
BaiJiangJie
18e590effd [Bugfix] 修复创建授权规则,选择资产导致其他输入框清空的bug - select2 (#2141) 2018-12-11 12:04:19 +08:00
vkill
9d1f5d3184 Add alpine_requirements.txt (#2139)
* Add alpine_requirements.txt

* Update alpine_requirements.txt
2018-12-11 11:21:07 +08:00
ibuler
b54d389c7c [Update] 修改翻译 2018-12-11 11:12:47 +08:00
老广
31356e825f [Update] 修改运行命令名称 (#2140) 2018-12-11 11:09:46 +08:00
ibuler
76aadad6fe [Bugfix] 修复菜单选中bug 2018-12-10 19:54:50 +08:00
ibuler
c7510bcf19 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-12-10 19:51:57 +08:00
ibuler
52e5487e7d [Update] 修改执行命令 2018-12-10 19:51:35 +08:00
BaiJiangJie
4a0d6842de [Update] 修改select2 css (#2125)
* [Update] 修改select2 css

* [Update] 创建资产选择节点时,关闭自动收起下拉列表
2018-12-10 15:19:08 +08:00
老广
d6b22e9ff8 [Update] 修改gateway test connection (#2135) 2018-12-10 13:03:02 +08:00
Titan
2833f343b2 otp issuer配置项 (#2133)
多机房环境中otp issuer配置
2018-12-10 12:03:42 +08:00
老广
3d13f3a17d Command (#2134)
* [Update] 任务区分org

* [Update] 修改翻译

* [Update] 使用id而不是hostname

* [Update] 执行命令

* [Update] 修改一些东西

* [Update] 暂存

* [Update] 用户执行命令

* [Update] 添加资产授权模块-tree

* [Update] 暂时这样

* [Update] 批量命令执行

* [Update] 修改表结构

* [Update] 更新翻译

* [Update] 删除cloud模块无效中文翻译
2018-12-10 10:11:54 +08:00
BaiJiangJie
d91599ffab [Update] 更新依赖 (#2117)
* [Update] 更新依赖

* [Update] 修改翻译小细节
2018-12-04 10:01:40 +08:00
老广
e22e832d49 [Bugfix] 修复批量更新时 节点和标签是增加而不是覆盖 (#2115) 2018-11-30 15:39:13 +08:00
BaiJiangJie
8f479e364b [Update] 修复小细节 (#2111)
* [Update] 命令/录像存储,禁用default删除按钮

* [Update] 修复 _asset_list_modal 取消资产全选时不能映射到资产选择框的bug
2018-11-29 16:01:03 +08:00
老广
0b0fdbfc82 [Update] 日志审计中改密日志权限 (#2108)
* [Update] 日志审计中改密日志权限

* [Update] 日志审计中改密日志权限
2018-11-28 18:15:02 +08:00
老广
24fe3ade9c [Update] 默认中可以看到所有用户 (#2106) 2018-11-28 10:02:58 +08:00
老广
9499a16a8b [Update] 默认中可以看到所有用户 (#2102) 2018-11-27 11:00:39 +08:00
BaiJiangJie
f380d82b55 [Update] 修改小问题 (#2100)
* [Update] 不修改admin用户的用户来源

* [Bugfix] 修复Default组织下用户列表会显示所有用户,并查看详情会报错

* [Update] 关闭telnet资产测试可连接性

* [Update] 只有ssh协议资产可测试连接性
2018-11-27 10:21:47 +08:00
老广
060248d1ca Req (#2098)
* [Update] 修改requirements

* [Update] 修改requirements

* [Update] 编译语言
2018-11-26 18:50:30 +08:00
ibuler
da8fec77bb [Update] 更新配置文件 2018-11-26 10:38:56 +08:00
ibuler
67f52888f6 [Update] 浏览器会话火气时间 2018-11-26 10:30:40 +08:00
ibuler
c4d6f32528 [Update] 增加启动超时时间 2018-11-26 10:22:17 +08:00
ibuler
2661bbb70a [Update] 修改config example 2018-11-23 18:30:41 +08:00
ibuler
3e3ab556d3 [Update] 修改设置 2018-11-23 18:28:02 +08:00
BaiJiangJie
646a29108c [Update] 修改安全设置模块排版,翻译 (#2083) 2018-11-23 17:00:35 +08:00
ibuler
c7f86cdde9 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-11-23 16:36:49 +08:00
BaiJiangJie
79208a95c1 [Update] authentication 添加自定义 LDAPBackends (#2081) 2018-11-23 16:36:28 +08:00
ibuler
091cf39e4e [Bugfix] ldap可以修改密码问题,platform大小写问题 2018-11-23 16:24:30 +08:00
ibuler
0df7c6909e [Update] 修改config_example 2018-11-23 11:30:31 +08:00
ibuler
f6def0b43f [Update] 修改表结构 2018-11-23 11:15:11 +08:00
老广
9cfcadc2f6 服务账号注册机制更改 (#2079)
* [Update] 服务账号注册

* [Update] 修改settings配置

* [Update] 修改settings

* [Update] 整理terminal api

* [Update] 修改terminal api

* [Update] 修改terminal注册机制
2018-11-23 10:25:35 +08:00
BaiJiangJie
363985ee7a [Feature] 添加功能,密码过期间隔时间配置,检测用户密码过期 (#2043)
* [Update] user model 添加date_password_last_updated字段, 并在用户详情/个人信息页进行展示、重置密码/修改密码时进行更新;安全设置添加密码过期时间配置项;

* [Update] 修改依赖,deb_requirements 删除 gcc automake

* [Update] 添加定时任务: 检测用户密码是否过期

* [Update] 登录页面添加检测用户密码是否过期,并给出过期提示,拒绝登录

* [update] 用户密码过期时间5天以内,每天发送重置密码邮件

* [Update] api 登录认证,添加密码过期检测

* [Update] 添加提示用户密码即将过期信息

* [Update] 修改小细节

* [Update] User model 添加密码即将过期/已过期 property属性

* [Update] 修改用户api auth检测用户密码过期逻辑

* [Update] 添加翻译,用户密码过期

* [Update] 用户密码即将过期,发送密码过期提醒邮件

* [Update] 修改检测用户密码过期任务,修改interval为crontab

* [Update] 修改翻译小细节

* [Update] 修改翻译小细节

* [Bugfix] 修复在用户更新页面修改密码时, 不更新最后密码修改时间的bug

* [Update] 修复小细节

* [Update] 修改系统设置成功提示翻译信息
2018-11-22 18:02:12 +08:00
老广
16cc4a0f4e [Update] 修改settings配置 (#2067)
* [Update] 修改settings配置

* [Update] 修改settings

* [Update] 修改密码校验规则前后端逻辑

* [Update] 修改用户config机制

* [Update] 修改配置

* [Update] 修改config example增加翻译
2018-11-22 12:27:27 +08:00
ibuler
5931c5a032 [Update] terminal配置修改 2018-11-19 12:45:33 +08:00
ibuler
742200e462 Merge remote-tracking branch 'github/dev' into dev 2018-11-19 12:27:57 +08:00
ibuler
9d7b82085e [Update] 修改common settings 2018-11-19 12:27:29 +08:00
ibuler
dda367a956 [Update] 修改common settings配置 2018-11-19 12:18:52 +08:00
老广
c0d51e22d7 Merge pull request #2035 from jumpserver/origin_dev
Origin dev
2018-11-14 12:49:47 +08:00
老广
2348c8c335 Merge pull request #2034 from jumpserver/origin_dev
[Bugfix] 修复luna不显示多组织资产的问题
2018-11-14 12:49:21 +08:00
ibuler
87abe63a20 [Bugfix] 修复luna不显示多组织资产的问题 2018-11-14 12:48:59 +08:00
ibuler
4d26fd8b56 [Bugfix] 修复luna不显示多组织资产的问题 2018-11-14 12:47:43 +08:00
ibuler
c9aab608a9 [Bugfix] 修复luna不显示多组织资产的问题 2018-11-14 12:42:26 +08:00
ibuler
641567be10 [Update] 修改docker file依赖 2018-11-13 16:18:25 +08:00
ibuler
5f68f6cb69 [Update] 增加脚本导入migration sql 2018-11-13 16:09:26 +08:00
ibuler
d50ad66b78 [Update] 维护统一的migrations 2018-11-13 15:57:44 +08:00
老广
69ab3e3542 Merge pull request #2028 from jumpserver/dev
Dev
2018-11-13 15:17:47 +08:00
ibuler
ff1b902b2e Merge remote-tracking branch 'github/dev' into dev 2018-11-13 15:01:07 +08:00
BaiJiangJie
52647da79b [Update] 修改jumpserver功能图片 (#2020) 2018-11-13 14:59:50 +08:00
BaiJiangJie
28b6144189 [Bugfix] 修复创建用户common_settings.SITE_URL 为None的bug (#2027) 2018-11-13 14:59:29 +08:00
ibuler
f7daf26a03 [Update] 修改celery运行,最大20个进程 2018-11-12 17:47:41 +08:00
ibuler
e1673334af Merge remote-tracking branch 'github/dev' into dev 2018-11-12 16:46:21 +08:00
BaiJiangJie
fd6e561d4b [Bugfix] 资产/用户等升序排序问题,显示创建oss录像存储bucket输入框 (#2017)
* [Bugfix] 修复资产列表,用户列表排序只有降序的问题

* [update] 系统设置创建oss录像存储, 添加bucket输入框
2018-11-12 16:45:57 +08:00
ibuler
4cdddaa493 [Bugfix] 修复网域网关列表的bug 2018-11-12 16:43:35 +08:00
老广
9491827e01 Merge pull request #2014 from jumpserver/dev
Dev
2018-11-11 12:04:00 +08:00
老广
5459d1114f Merge branch 'master' into dev 2018-11-11 11:39:02 +08:00
ibuler
6acda27d67 [Update] 更新版本 2018-11-11 10:38:15 +08:00
BaiJiangJie
0f9326bd8f [Update] 更新OpenID依赖 (#2013) 2018-11-09 15:00:37 +08:00
BaiJiangJie
e09f3ca4fd [Update] 支持 OpenID 认证 (#2008)
* [Update] core支持openid登录,coco还不支持

* [Update] coco支持openid登录

* [Update] 修改注释

* [Update] 修改 OpenID Auth Code Backend 用户认证失败返回None, 不是Anonymoususer

* [Update] 修改OpenID Code用户认证异常捕获

* [Update] 修改OpenID Auth Middleware, check用户是否单点退出的异常捕获

* [Update] 修改config_example Auth OpenID 配置

* [Update] 登录页面添加 更多登录方式

* [Update] 重构OpenID认证架构

* [Update] 修改小细节

* [Update] OpenID用户认证成功后,更新用户来源

* [update] 添加OpenID用户登录成功日志
2018-11-09 14:54:38 +08:00
ibuler
1fcb272ddc [Bugfix] 修复coco tree失败的问题,不应该过滤没有asset的node 2018-11-09 12:33:45 +08:00
ibuler
b577c626f7 [Bugfix] 修复luna取到协议不同的系统用户 2018-11-09 11:23:41 +08:00
ibuler
2e4e5503cc [Bugfix] 修复获取common settings时数据库问题 2018-11-07 11:26:39 +08:00
liuzheng712
4212cb3600 feat: add tencentcloud-sdk-python==3.0.32 2018-11-05 12:48:32 +08:00
ibuler
b8874e1855 [Update] 修改版本号 2018-11-05 11:52:44 +08:00
ibuler
9bb498f7b3 [Update] 修改sdk版本 2018-11-05 11:25:22 +08:00
ibuler
e38d089056 Merge remote-tracking branch 'github/dev' into dev 2018-11-05 11:11:33 +08:00
BaiJiangJie
f9e9bf0b2d [Update] 更新用户/授权规则过期时间精确到min (#1993) 2018-11-05 11:11:20 +08:00
BaiJiangJie
1e5387ef47 [Update] 更新组织管理api (#1986)
* [Update] 更新组织管理api

* [Update] 重写-组织管理员/用户API,采用through类

* [Update] 修改OrgMembershipSerializerMixin目录

* [Update] 修改组织管理API,限制http method

* [Update] 修改rpm依赖
2018-11-02 14:38:44 +08:00
BaiJiangJie
f87e08efff [Update] 添加deb依赖,完善用户登录失败日志,修复资产标签bug (#1983)
* [Update] 修改deb依赖

* [Update] 修改记录用户登录失败日志,用户不存在的情况,但是显示登录日志列表还不全..

* [Update] 用户登录日志,记录用户名不存在的情况(Default组织显示所有用户的登录日志)

* [Bugfix] 修复标签名为search, limit时资产列表不显示的bug
2018-11-01 16:28:19 +08:00
BaiJiangJie
82d866db7d [Update] 添加用户授权资产列表页的分页,搜索,排序 (#1963)
* [Update] 分页获取用户授权资产

* [Update] 修改前端-用户授权资产分页

* [Update] 用户授权资产支持搜索

* [Update] 用户授权资产支持排序

* [Update] 用户授权的节点with资产Api,对资产进行排序

* [Update] 获取用户授权的节点下的资产的api,进行分页、排序、查询

* [Update] 抽象用户授权资产列表的查询,排序

* [Update] 优化小细节

* [Update] 删除无用导入

* [Update] 修改AssetFilterMixins目录从common到perms

* [Update] 资产授权规则列表: 添加分页、搜索

* [Update] 添加管理用户,系统用户列表分页、搜索

* [Update] 用户组列表添加分页,搜索

* [Update] 资产标签列表添加分页、搜索

* [Update] 网域网关列表添加分页、搜索

* [Update] 命令过滤列表添加分页、搜索,修改翻译小细节

* [Update] 删除前端注释initDataTable

* [Update] 修改文案,资产组-节点

* [Update] 普通用户资产列表添加分页、搜索
2018-10-31 15:31:09 +08:00
老广
ba0d822734 Merge pull request #1971 from waiwaiku/master
修复子用户资产列表点击无法收起
2018-10-31 15:30:14 +08:00
fangjian
c8568eb244 修复子用户资产列表点击无法收起 2018-10-31 11:26:24 +08:00
ibuler
6e19b9d5bc [Update] Luna支持异步加载 2018-10-30 12:06:39 +08:00
老广
354b728f75 Merge pull request #1945 from jumpserver/dev_storage
[Update] 更新命令/录像存储设置
2018-10-29 10:07:10 +08:00
老广
ce553710ba Merge pull request #1958 from jumpserver/dev_bugfix_ops
[Bugfix] 修复组织管理员刷新硬件信息,获取资产为None(使用fullname)和查看celery log 403问题
2018-10-29 10:04:57 +08:00
BaiJiangJie
4f806f11f2 [Update] 修改命令/录像存储,创建/删除API的权限为SuperUser 2018-10-28 02:17:46 +08:00
ibuler
e9247dd578 [Update] default组织显示所有用户 2018-10-27 11:34:10 +08:00
老广
0a94a346a0 Merge pull request #1946 from jumpserver/dev_bugfix_perms
[Bugfix] 修复授权规则下Default节点下的资产不显示
2018-10-27 11:14:23 +08:00
老广
d8afe72d4c Merge pull request #1947 from jumpserver/dev_user_paging
[Update] 用户列表页,添加分页,搜索
2018-10-27 11:14:03 +08:00
BaiJiangJie
e2072a1e02 [Update] 修复组织管理员刷新硬件信息,获取资产为None(使用fullname)和查看celery log 403问题 2018-10-26 21:03:09 +08:00
BaiJiangJie
cc387bf511 [Update] 用户列表页,添加分页,搜索 2018-10-24 14:55:04 +08:00
BaiJiangJie
5c002e91ee [Update] 修改获取资产distinct 2018-10-24 13:05:32 +08:00
BaiJiangJie
41a8831034 [Bugfix] 修复授权规则下Default节点下的资产不显示 2018-10-24 12:13:47 +08:00
BaiJiangJie
ebd92c79c7 [Update] 修改翻译小细节 2018-10-24 11:05:39 +08:00
BaiJiangJie
6278900201 [Update] 创建es命令存储,添加提示信息 2018-10-24 10:57:55 +08:00
ibuler
4f580e0df8 [Update] 修改启动脚本 2018-10-24 10:53:00 +08:00
BaiJiangJie
1f502e02c7 [Update] 修改settings为common_settings 2018-10-24 10:48:03 +08:00
BaiJiangJie
cdf8398169 [Update] 修复创建es命令存储校验有效性异常 2018-10-24 10:29:40 +08:00
BaiJiangJie
1bfef829f3 [Update] 修改terminal表单获取storage, 删除无用代码 2018-10-24 10:11:38 +08:00
BaiJiangJie
cc0cf8ed1c Merge branch 'dev' into dev_replay 2018-10-24 09:52:43 +08:00
BaiJiangJie
2791213844 [Update] 更新storage翻译 2018-10-23 20:41:01 +08:00
BaiJiangJie
284e8be45c [Update] 修改系统设置-命令/录像存储页面(添加,删除) 2018-10-23 19:22:18 +08:00
老广
76109f1808 Merge pull request #1938 from jumpserver/dev
Dev
2018-10-23 10:10:53 +08:00
ibuler
54b6e06d1f [Update] 暂时不支持上传目录 2018-10-19 16:30:52 +08:00
ibuler
5c30c76ea3 [Update] 修改jms-storage版本 2018-10-19 10:49:13 +08:00
老广
94b5eb8685 Merge pull request #1929 from jumpserver/dev
Dev
2018-10-19 09:56:52 +08:00
ibuler
c9f4b104c7 Merge remote-tracking branch 'github/dev' into dev 2018-10-19 09:38:18 +08:00
ibuler
3bf1c036c5 [Bugfix] 修改sshpubkey库版本 2018-10-19 09:27:33 +08:00
老广
09fbd3a5ab Merge pull request #1922 from jumpserver/dev
Dev
2018-10-16 17:18:31 +08:00
ibuler
ebecd00581 [Update] 修改优先级逻辑 2018-10-16 16:47:47 +08:00
ibuler
143fa060d1 [Update] 修改节点信息 2018-10-16 13:44:26 +08:00
ibuler
2c18a27e3a [Update] 修改节点创建规则 2018-10-16 12:37:42 +08:00
ibuler
910dd4e593 [Update] 修改节点生成规则 2018-10-16 12:03:11 +08:00
ibuler
11aefa479b [Bugfix] 修复common设置问题 2018-10-16 10:56:25 +08:00
ibuler
abc56016f2 Merge remote-tracking branch 'github/dev' into dev 2018-10-16 10:19:00 +08:00
老广
f44db2a25b Merge pull request #1916 from jumpserver/dev_tag_filter
[Update] 添加(减法)标签过滤器
2018-10-16 10:16:25 +08:00
BaiJiangJie
3fa6807837 [Update] 添加(减法)标签过滤器 2018-10-16 09:53:30 +08:00
ibuler
2c4195d619 Merge remote-tracking branch 'github/dev' into dev 2018-10-15 17:47:32 +08:00
老广
265ef0c8ac Merge pull request #1913 from jumpserver/dev_requirements
[Update] 更新requirements
2018-10-15 17:47:10 +08:00
BaiJiangJie
0b0b06a5c2 [Update] 更新requirements-aliyun 2018-10-15 17:06:01 +08:00
ibuler
d77ba1d5ea Merge remote-tracking branch 'github/dev' into dev 2018-10-15 16:30:08 +08:00
老广
a3bd7cee80 Merge pull request #1912 from jumpserver/dev_cloud_manager
[Update] 修改文案
2018-10-15 16:29:50 +08:00
BaiJiangJie
8d73cd43e1 Merge branch 'dev' into dev_cloud_manager 2018-10-15 16:09:55 +08:00
BaiJiangJie
7e3fd73ae5 [Update] 修改文案 2018-10-15 16:05:53 +08:00
ibuler
70960d2ae4 Merge remote-tracking branch 'github/dev' into dev 2018-10-15 11:34:20 +08:00
老广
d2c574fe9d Merge pull request #1910 from jumpserver/dev_cloud_manager
[Feature] 添加xpack-云管中心模块
2018-10-15 11:30:38 +08:00
BaiJiangJie
a70fcf057b [Update] 修改js,添加3级菜单active属性 2018-10-15 10:48:24 +08:00
ibuler
f37582ec53 [Update] 用户导航加上文件管理 2018-10-15 10:14:20 +08:00
BaiJiangJie
29b87c40fe [Update] 修改云管理模块-文案 2018-10-14 00:03:04 +08:00
BaiJiangJie
1ec77c5bb9 [Update] 添加3级导航菜单-云管理模块 2018-10-12 20:29:04 +08:00
BaiJiangJie
21c71aba93 [Update] Merge branch dev_cloud_manager (baijiangjie_local) to dev 2018-10-12 18:41:01 +08:00
BaiJiangJie
f8db9f480e [Update] 添加翻译-云管理模块 2018-10-12 18:26:56 +08:00
老广
0665644fd0 Merge pull request #1905 from jumpserver/dev
支持命令过滤
2018-10-12 15:33:52 +08:00
ibuler
7bafa546b5 [Update] 修改版本 2018-10-12 15:05:04 +08:00
ibuler
666815b324 [Update] 修改文案 2018-10-12 15:04:01 +08:00
ibuler
532abb86b5 [Update] 添加文案说明和翻译 2018-10-11 11:10:47 +08:00
ibuler
76d4e4ad55 [Update] 修改翻译 2018-10-10 19:31:28 +08:00
ibuler
70fa43adaa [Update] 修改一些逻辑 2018-10-10 19:29:53 +08:00
ibuler
44bf01d4ed [Update] 支持命令过滤 2018-10-10 15:37:20 +08:00
老广
1341983fd3 Merge pull request #1878 from jumpserver/dev
[Update] 更改版本号
2018-10-08 19:13:12 +08:00
ibuler
78936bf9f2 [Update] 更改版本号 2018-10-08 19:03:02 +08:00
老广
9a5d3cb475 Merge pull request #1877 from jumpserver/dev
支持web sftp
2018-10-08 18:57:40 +08:00
ibuler
9bddc29da4 [Update] 暂时不存status数据 2018-09-29 10:44:52 +08:00
ibuler
d68a4d9cae [Update] 修改背景色 2018-09-29 10:28:12 +08:00
ibuler
5457118fb6 [Update] 修改css 2018-09-21 22:49:38 +08:00
ibuler
7ee68f7eeb [Update] 修改static 2018-09-21 20:35:58 +08:00
ibuler
2063f2f257 [Update] Add file to static 2018-09-21 16:37:33 +08:00
ibuler
2637c608a6 [Update] 修改gunicorn配置 2018-09-14 11:29:26 +08:00
老广
32519ea326 Merge pull request #1819 from jumpserver/dev
Dev
2018-09-14 11:08:03 +08:00
ibuler
3ce9d01b6d [Bugfix] 修复coco无法查看资产的bug 2018-09-13 12:16:49 +08:00
ibuler
310bc6ad0b [Update] 修复获取token的bug 2018-09-13 11:41:44 +08:00
ibuler
b54afbe7bb [Bugfix] 修复组织管理员无法查看用户授权的bug 2018-09-13 11:17:55 +08:00
ibuler
ab848afdb9 [Bugfix] 修复创建节点bug 2018-09-12 15:19:40 +08:00
ibuler
5bb867d10d [Update] 添加禁用用户mfa脚本 2018-09-12 11:36:27 +08:00
ibuler
0eda8865e6 [Bugfix] 修复首页显示问题 2018-09-12 11:24:07 +08:00
老广
2a37107abc Merge pull request #1805 from jumpserver/dev
Dev
2018-09-11 16:04:18 +08:00
ibuler
c78107f62f [Bugfix] Remote datepicker bug 2018-09-11 16:01:49 +08:00
ibuler
b022bf36ba [Update] 使用gthread替代eventlet 2018-09-11 12:42:49 +08:00
ibuler
6dc2272a26 [Update] 修复无法取消授权资产的前端bug 2018-09-11 12:17:17 +08:00
老广
1d462aea1b Merge pull request #1796 from jumpserver/dev
Dev
2018-09-07 12:42:55 +08:00
ibuler
88a29c0a93 [Update] 增加手动结束非正常关闭的连接 2018-09-07 12:40:26 +08:00
ibuler
9ffae722f3 [Update] 升级gunicorn版本 2018-09-05 12:26:46 +08:00
老广
9ab2f4bc56 Merge pull request #1780 from jumpserver/dev
[Update] 修改图标
2018-09-05 10:55:26 +08:00
ibuler
41e7f45c20 [Update] 修改图标 2018-09-05 10:53:12 +08:00
老广
9945ac172b Merge pull request #1775 from jumpserver/dev
[Update] 修改web terminal访问bug
2018-09-04 17:10:13 +08:00
ibuler
67ddd42b3d [Update] 修改web terminal访问bug 2018-09-04 17:09:37 +08:00
老广
03adddefa3 Merge pull request #1771 from jumpserver/dev
Dev
2018-09-04 16:05:19 +08:00
ibuler
60b7ccddc0 [Update] 更新翻译 2018-09-04 16:03:42 +08:00
老广
1194932bc0 Merge pull request #1770 from jumpserver/dev
settings加密存储
单独推送系统用户到某个资产
支持了 改密日志 操作日志
翻译更加完善,支持切换语言
2018-09-04 15:29:11 +08:00
ibuler
5c8fd91cf9 [Update] 修复上传录像文件,无法读取的问题 2018-09-04 12:19:20 +08:00
ibuler
bb13003a10 [Bugfix] 修复资产权限 2018-09-03 19:41:44 +08:00
ibuler
9a18817dbb [Bugfix] 修复rdp系统用户密码无法成功的问题 2018-09-03 19:40:06 +08:00
ibuler
2c4966c678 [Update] 修改form 2018-09-03 15:33:35 +08:00
ibuler
d1390a1cd7 [Update] 修改版本 2018-09-03 11:28:56 +08:00
老广
fe45d839fb Dev2 (#1766)
* [Update] 初始化操作日志

* [Feature] 完成操作日志记录

* [Update] 修改mfa失败提示

* [Update] 修改增加created by内容

* [Update] 增加改密日志

* [Update] 登录日志迁移到日志审计中

* [Update] change block user logic, if login success, clean block limit

*  [Update] 更新中/英文翻译(ALL) (#1662)

* Revert "授权页面分页问题"

* 增加命令导出 (#1566)

* [Update] gunicorn不使用eventlet

* [Update] 添加eventlet

* 替换淘宝IP查询接口

* [Feature] 添加命令记录下载功能 (#1559)

* [Feature] 添加命令记录下载功能

* [Update] 文案修改,导出记录、提交,取消全部命令导出

* [Update] 命令导出,修复时间问题

* [Update] paramiko => 2.4.1

* [Update] 修改settings

* [Update] 修改权限判断

* Dev (#1646)

* [Update] 添加org

* [Update] 修改url

* [Update] 完成基本框架

* [Update] 修改一些逻辑

* [Update] 修改用户view

* [Update] 修改资产

* [Update] 修改asset api

* [Update] 修改协议小问题

* [Update] stash it

* [Update] 修改约束

* [Update] 修改外键为org_id

* [Update] 删掉Premiddleware

* [Update] 修改Node

* [Update] 修改get_current_org 为 proxy对象 current_org

* [Bugfix] 解决Node.root() 死循环,移动AdminRequired到permission中 (#1571)

* [Update] 修改permission (#1574)

* Tmp org (#1579)

* [Update] 添加org api, 升级到django 2.0

* [Update] fix some bug

* [Update] 修改一些bug

* [Update] 添加授权规则org (#1580)

* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;

* Tmp org (#1583)

* [Update] 修改一些内容

* [Update] 修改datatable 支持process

* [Bugfix] 修复asset queryset 没有valid方法的bug

* [Update] 在线/历史/命令model添加org;修复命令记录保存org失败bug (#1584)

* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;

* [Update] 在线/历史/命令model添加org

* [Bugfix] 修复命令记录,保存org不成功bug

* [Update] Org功能修改

* [Bugfix] 修复merge带来的问题

* [Update] org admin显示资产详情右侧选项卡;修复资产授权添加用户,会显示其他org用户的bug (#1594)

* [Bugfix] 修复资产授权添加用户,显示其他org的用户bug

* [Update] org admin 显示资产详情右侧选项卡

* Tmp org (#1596)

* [Update] 修改index view

* [Update] 修改nav

* [Update] 修改profile

* [Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug

* [Update] 修改get_all_assets

* [Bugfix] 修复节点前面有个空目录

* [Bugfix] 修复merge引起的bug

* [Update] Add init

* [Update] Node get_all_assets 过滤游离资产,条件nodes_key=None -> nodes=None

* [Update] 恢复原来的api地址

* [Update] 修改api

* [Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug

* [Bugfix] Fix perm name unique

* [Bugfix] 修复校验失败api

* [Update] Merge with org

* [Merge] 修改一下bug

* [Update] 暂时修改一些url

* [Update] 修改url 为django 2.0 path

* [Update] 优化datatable 和显示组织优化

* [Update] 升级url

* [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode(… (#1613)

* [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode() method的bug

* [Bugfix] (task任务系统)修复资产连接性测试、硬件刷新和系统用户连接性测试失败等bug

* [Bugfix] 修复一些bug

* [Bugfix] 修复一些bug

*  [Update] 更新org下普通用户的资产详情 (#1619)

* [Update] 更新org下普通用户查看资产详情,只显示数据

* [Update] 优化org下普通用户查看资产详情前端代码

* [Update] 创建/更新用户的role选项;密码强度提示信息中英文; (#1623)

* [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题

* [Update] 用户密码强度提示信息支持中英文

* [Update] 修改token返回

* [Update] Asset返回org name

* [Update] 修改支持xpack

* [Update] 修改url

* [Bugfix] 修复不登录就能查看资产的bug

* [Update] 用户修改

* [Bugfix] ...

* [Bugfix] 修复跳转错误的问题

*  [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; (#1644)

* [Update] 更新xpack下orgs的翻译信息

* [Update] 更新model Label,继承OrgModelMixin;

* [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug;

* [Bugfix] 修复小bug

* [Update] 优化一些api

* [Update] 优化用户资产页面

* [Update] 更新 xpack/orgs 删除功能:限制在当前org下删除当前org (#1645)

* [Update] 修改版本号

* [Update] 添加功能: 语言切换(中/英);修改 header_bar <商业支持、文档>显示方式

* [Update] 中/英切换文案修改;修改django_language key 从 settings 中获取

* [Update] 修改Dashboard页面文案,支持英文

* [Update] 更新中/英文翻译(ALL)

* [Update] 解决翻译文件冲突

* [Update] 系统用户支持单独隋松

* [Update] 重置用户MFA

* [Update] 设置session空闲时间

* [Update] 加密setting配置

* [Update] 修改单独推送和测试资产可连接性

*  [Update] 添加功能:用户个人详情页添加 更改MFA操作 (#1748)

* [Update] 添加功能:用户个人详情页添加 更改MFA操作

* [Update] 删除print

* [Bugfix] 添加部分views的权限控制;从组织移除用户,同时从授权规则和用户组中移除此用户。 (#1746)

* [Bugfix] 修复上传command log 为空

* [Update] 修复执行任务的bug

* [Bugfix] 修复将用户从组内移除,其依然具有之前的组权限的bug, perms and user_groups

* [Bugfix] 修复组管理员可以访问部分url-views的bug(如: /settings/)添加views权限控制

* [Update] 修改日志滚动

* [Bugfix] 修复组织权限控制的bug (#1763)

* [Bugfix] 修复将用户从组内移除,其依然具有之前的组权限的bug, perms and user_groups

* [Bugfix] 修复组管理员可以访问部分url-views的bug(如: /settings/)添加views权限控制
2018-09-03 11:24:25 +08:00
老广
9f96f1c537 Merge pull request #1737 from jumpserver/dev
Dev
2018-08-24 12:12:38 +08:00
老广
dc918c031c Merge pull request #1736 from jumpserver/bugfix_run_adhoc
[Update] 修复执行任务的bug
2018-08-24 12:11:27 +08:00
ibuler
6b047ca702 Merge remote-tracking branch 'github/dev' into bugfix_run_adhoc 2018-08-24 12:09:29 +08:00
ibuler
47d31005b5 [Update] 修复执行任务的bug 2018-08-24 12:08:42 +08:00
老广
57e1ca93f0 Merge pull request #1715 from jumpserver/dev
[Bugfix] 修复上传command log 为空
2018-08-20 08:41:15 -05:00
ibuler
483a7617ce [Bugfix] 修复上传command log 为空 2018-08-20 21:39:15 +08:00
老广
5470ab752e Merge pull request #1702 from jumpserver/dev
Dev
2018-08-16 06:19:27 -05:00
ibuler
2dbd6b6f6e [Bugfix] 修复获取资产失败 2018-08-16 18:29:54 +08:00
老广
504d9242c6 Merge pull request #1700 from jumpserver/dev
Dev
2018-08-16 04:47:13 -05:00
ibuler
14b1e3fa13 [Update] 添加backup脚本 2018-08-16 17:08:45 +08:00
ibuler
7eeca511f1 [Bugfix] 修复打印日志的bug 2018-08-16 16:40:00 +08:00
ibuler
670c8a6d0b [Bugfix] 修复唯一认证 2018-08-16 16:32:49 +08:00
老广
a2aa923abe Merge pull request #1698 from jumpserver/dev
Dev
2018-08-15 23:53:49 -05:00
ibuler
2ac5786ba1 [Bugfix] 修复Hostname可能不唯一引起的任务执行失败 2018-08-16 12:44:39 +08:00
ibuler
5b93a1a0a5 [Bugfix] 修复单页面bug 2018-08-15 17:44:06 +08:00
ibuler
00928dd46d [Update] 不选择节点默认导出所有 2018-08-15 15:01:27 +08:00
ibuler
7ddf7f2a79 [Update] 修改cpu的数量问题 2018-08-15 12:00:47 +08:00
ibuler
3533bf588b [Bugfix] 修复修改资产报错无提示的问题 2018-08-14 18:08:07 +08:00
老广
dea007f27b Merge pull request #1688 from jumpserver/dev
[Update] 支持full value
2018-08-14 03:01:12 -05:00
ibuler
cd2b88caee [Update] 修改节点full value 2018-08-14 15:59:53 +08:00
ibuler
1877511acf [Update] 更改组织同事更改节点名称 2018-08-14 10:16:38 +08:00
ibuler
1c5ce61ed0 [Update] 修改Org刷新cache 2018-08-13 18:26:13 +08:00
老广
b1132bfc37 Merge pull request #1683 from jumpserver/dev
Dev
2018-08-13 02:19:27 -05:00
ibuler
75e67410cf [Bugfix] 修复批量更新资产和用户的bug 2018-08-13 15:01:56 +08:00
ibuler
c9d137bc20 [Update] 修改Mixin,允许org_id不填写 2018-08-13 11:44:44 +08:00
ibuler
d97e606503 [Update] 修改django版本 2018-08-13 11:02:00 +08:00
ibuler
e59b95e97a Merge remote-tracking branch 'github/dev' into dev 2018-08-13 10:46:02 +08:00
ibuler
bb6394150d [Update] 修改批量更新资产的bug 2018-08-12 15:52:16 +08:00
ibuler
2354f0c970 [Bugfix] 修复无法批量禁用启用的bug 2018-08-10 17:11:17 +08:00
老广
ae564ed0d4 Merge pull request #1670 from jumpserver/dev
Dev
2018-08-10 03:16:35 -05:00
ibuler
05ecd7497a [Update] 不可以更改root节点名称 2018-08-10 16:11:40 +08:00
ibuler
6b86b8b485 [Bugfix] 修复资产数量不对的bug 2018-08-10 14:46:04 +08:00
老广
fa0bd85fd4 Merge pull request #1667 from jumpserver/dev
[Bugfix] 修复点击资产报错
2018-08-09 21:07:40 -05:00
ibuler
7da46354ca [Update] 去掉debug 2018-08-10 09:29:26 +08:00
ibuler
e41aad1576 [Bugfix] 修复点击资产报错 2018-08-10 09:12:24 +08:00
ibuler
3f049440b7 [Bugfix] 修复可能引起的创建外键失败 2018-08-08 13:10:58 +08:00
老广
4f532f588b Merge pull request #1651 from jumpserver/dev
Dev
2018-08-07 22:48:26 -05:00
老广
a792781b98 Merge branch 'master' into dev 2018-08-07 22:48:12 -05:00
ibuler
3a4c7846bf [Bugfix] 修改创建第一个节点的Bug 2018-08-08 11:44:06 +08:00
ibuler
ccc292d9a9 [Update] 去掉debug信息 2018-08-07 18:38:46 +08:00
pufer
337338ebf3 增加多OU支持 (#1262)
* 增加多OU支持

* Update models.py

need to import LDAPSearchUnion
2018-08-07 05:11:46 -05:00
ibuler
aa3bc7b53a [Update] 权限修改 2018-08-07 16:20:22 +08:00
老广
d5451a482a Dev (#1646)
* [Update] 添加org

* [Update] 修改url

* [Update] 完成基本框架

* [Update] 修改一些逻辑

* [Update] 修改用户view

* [Update] 修改资产

* [Update] 修改asset api

* [Update] 修改协议小问题

* [Update] stash it

* [Update] 修改约束

* [Update] 修改外键为org_id

* [Update] 删掉Premiddleware

* [Update] 修改Node

* [Update] 修改get_current_org 为 proxy对象 current_org

* [Bugfix] 解决Node.root() 死循环,移动AdminRequired到permission中 (#1571)

* [Update] 修改permission (#1574)

* Tmp org (#1579)

* [Update] 添加org api, 升级到django 2.0

* [Update] fix some bug

* [Update] 修改一些bug

* [Update] 添加授权规则org (#1580)

* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;

* Tmp org (#1583)

* [Update] 修改一些内容

* [Update] 修改datatable 支持process

* [Bugfix] 修复asset queryset 没有valid方法的bug

* [Update] 在线/历史/命令model添加org;修复命令记录保存org失败bug (#1584)

* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;

* [Update] 在线/历史/命令model添加org

* [Bugfix] 修复命令记录,保存org不成功bug

* [Update] Org功能修改

* [Bugfix] 修复merge带来的问题

* [Update] org admin显示资产详情右侧选项卡;修复资产授权添加用户,会显示其他org用户的bug (#1594)

* [Bugfix] 修复资产授权添加用户,显示其他org的用户bug

* [Update] org admin 显示资产详情右侧选项卡

* Tmp org (#1596)

* [Update] 修改index view

* [Update] 修改nav

* [Update] 修改profile

* [Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug

* [Update] 修改get_all_assets

* [Bugfix] 修复节点前面有个空目录

* [Bugfix] 修复merge引起的bug

* [Update] Add init

* [Update] Node get_all_assets 过滤游离资产,条件nodes_key=None -> nodes=None

* [Update] 恢复原来的api地址

* [Update] 修改api

* [Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug

* [Bugfix] Fix perm name unique

* [Bugfix] 修复校验失败api

* [Update] Merge with org

* [Merge] 修改一下bug

* [Update] 暂时修改一些url

* [Update] 修改url 为django 2.0 path

* [Update] 优化datatable 和显示组织优化

* [Update] 升级url

* [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode(… (#1613)

* [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode() method的bug

* [Bugfix] (task任务系统)修复资产连接性测试、硬件刷新和系统用户连接性测试失败等bug

* [Bugfix] 修复一些bug

* [Bugfix] 修复一些bug

*  [Update] 更新org下普通用户的资产详情 (#1619)

* [Update] 更新org下普通用户查看资产详情,只显示数据

* [Update] 优化org下普通用户查看资产详情前端代码

* [Update] 创建/更新用户的role选项;密码强度提示信息中英文; (#1623)

* [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题

* [Update] 用户密码强度提示信息支持中英文

* [Update] 修改token返回

* [Update] Asset返回org name

* [Update] 修改支持xpack

* [Update] 修改url

* [Bugfix] 修复不登录就能查看资产的bug

* [Update] 用户修改

* [Bugfix] ...

* [Bugfix] 修复跳转错误的问题

*  [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; (#1644)

* [Update] 更新xpack下orgs的翻译信息

* [Update] 更新model Label,继承OrgModelMixin;

* [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug;

* [Bugfix] 修复小bug

* [Update] 优化一些api

* [Update] 优化用户资产页面

* [Update] 更新 xpack/orgs 删除功能:限制在当前org下删除当前org (#1645)

* [Update] 修改版本号
2018-08-06 23:34:35 -05:00
ibuler
534734881c [Update] 修改版本号 2018-08-07 11:32:11 +08:00
ibuler
8236c7baa0 Merge remote-tracking branch 'github/dev' into dev 2018-08-06 18:17:38 +08:00
BaiJiangJie
96ec5fac99 [Update] 更新 xpack/orgs 删除功能:限制在当前org下删除当前org (#1645) 2018-08-06 05:17:17 -05:00
ibuler
b7fcf80fc5 [Update] 优化用户资产页面 2018-08-06 18:16:25 +08:00
ibuler
de3695bf97 Merge remote-tracking branch 'github/dev' into dev 2018-08-06 17:17:49 +08:00
ibuler
7c814080b2 [Update] 优化一些api 2018-08-06 17:16:52 +08:00
BaiJiangJie
f5531b6065 [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; (#1644)
* [Update] 更新xpack下orgs的翻译信息

* [Update] 更新model Label,继承OrgModelMixin;

* [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug;

* [Bugfix] 修复小bug
2018-08-06 03:08:05 -05:00
ibuler
b0aa9f197a [Bugfix] 修复跳转错误的问题 2018-08-06 12:20:27 +08:00
ibuler
fc156e23f3 [Bugfix] ... 2018-08-06 10:39:22 +08:00
ibuler
1a05dab572 [Update] 修改user api权限 2018-08-06 10:28:30 +08:00
ibuler
b8ecb6f81d [Update] 用户修改 2018-08-06 10:24:51 +08:00
ibuler
c01936facc [Bugfix] 修复不登录就能查看资产的bug 2018-08-03 10:32:02 +08:00
ibuler
90c629c837 [Update] 修改url 2018-08-03 10:25:41 +08:00
ibuler
c9d192eefc Merge remote-tracking branch 'github/org' into org 2018-08-02 12:37:17 +08:00
ibuler
9c4ebf9c75 [Update] 修改支持xpack 2018-08-02 12:36:31 +08:00
ibuler
37d89b4ea2 Merge branch 'tmp_org' into org 2018-08-01 13:07:12 +08:00
ibuler
87e0e1f2c4 [Update] Asset返回org name 2018-08-01 13:06:50 +08:00
ibuler
183ff09530 [Update] 修改token返回 2018-08-01 12:35:48 +08:00
BaiJiangJie
485a178c0a [Update] 创建/更新用户的role选项;密码强度提示信息中英文; (#1623)
* [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题

* [Update] 用户密码强度提示信息支持中英文
2018-07-31 21:44:43 -05:00
BaiJiangJie
227cc4e965 [Update] 更新org下普通用户的资产详情 (#1619)
* [Update] 更新org下普通用户查看资产详情,只显示数据

* [Update] 优化org下普通用户查看资产详情前端代码
2018-07-30 22:18:38 -05:00
ibuler
01bef95e6e Merge remote-tracking branch 'github/org' into org 2018-07-31 11:01:31 +08:00
ibuler
672dd66023 [Bugfix] 修复一些bug 2018-07-31 11:00:48 +08:00
ibuler
c032294b14 [Bugfix] 修复一些bug 2018-07-31 11:00:21 +08:00
BaiJiangJie
6ce813faf8 [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode(… (#1613)
* [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode() method的bug

* [Bugfix] (task任务系统)修复资产连接性测试、硬件刷新和系统用户连接性测试失败等bug
2018-07-30 21:27:55 -05:00
ibuler
aefc18d73b Merge branch 'org' into tmp_org 2018-07-27 19:02:26 +08:00
ibuler
23815f87c5 [Update] 升级url 2018-07-27 18:56:40 +08:00
ibuler
206e037cf2 [Update] 优化datatable 和显示组织优化 2018-07-27 18:55:07 +08:00
ibuler
492fd98882 [Update] 修改url 为django 2.0 path 2018-07-27 17:43:18 +08:00
ibuler
d92d462dab [Update] 暂时修改一些url 2018-07-27 16:54:40 +08:00
ibuler
8afd5ef90a [Merge] 修改一下bug 2018-07-27 16:21:55 +08:00
ibuler
d3dca5d077 [Update] Merge with org 2018-07-27 16:19:19 +08:00
ibuler
9166a26f80 Merge branch 'org' into tmp_org 2018-07-27 16:16:29 +08:00
ibuler
3039284666 [Bugfix] 修复校验失败api 2018-07-27 16:15:04 +08:00
老广
2f395794ef Merge pull request #1602 from jumpserver/bai_org
[Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug
2018-07-27 03:13:59 -05:00
ibuler
c6d50802db [Bugfix] Fix perm name unique 2018-07-27 15:35:12 +08:00
BaiJiangJie
a10e47f72c Merge branch 'org' into michael_org 2018-07-27 15:24:52 +08:00
BaiJiangJie
3dc214d1fa [Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug 2018-07-27 15:24:09 +08:00
ibuler
f7fb36a176 [Update] 修改api 2018-07-27 15:21:14 +08:00
ibuler
0d7295b60e [Update] 恢复原来的api地址 2018-07-27 15:10:48 +08:00
BaiJiangJie
8f654c37a9 [Update] Node get_all_assets 过滤游离资产,条件nodes_key=None -> nodes=None 2018-07-27 13:01:41 +08:00
ibuler
b29a541aa6 [Update] Add init 2018-07-27 12:55:59 +08:00
ibuler
9fd52f6665 [Bugfix] 修复merge引起的bug 2018-07-27 12:14:13 +08:00
ibuler
f4c86718dc [Update] Merge dev 2018-07-27 12:12:32 +08:00
老广
4ff7a1f066 Merge pull request #1600 from jumpserver/bai_org
[Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug
2018-07-26 23:08:55 -05:00
ibuler
eca245fdd5 [Bugfix] 修复节点前面有个空目录 2018-07-27 11:59:39 +08:00
ibuler
7e3cf908a1 [Update] 修改get_all_assets 2018-07-27 11:09:37 +08:00
老广
dded4e10fb Merge pull request #1599 from jumpserver/dev
Dev
2018-07-26 21:45:32 -05:00
BaiJiangJie
45a354f848 [Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug 2018-07-27 10:42:32 +08:00
ibuler
8386f107c6 Merge remote-tracking branch 'github/dev' into dev 2018-07-26 19:32:23 +08:00
ibuler
5ce3dd4079 [Update] 添加unblock user脚本 2018-07-26 19:30:37 +08:00
老广
a48fb9de8d Tmp org (#1596)
* [Update] 修改index view

* [Update] 修改nav

* [Update] 修改profile
2018-07-26 05:27:09 -05:00
BaiJiangJie
04e7f54c69 [Update] org admin显示资产详情右侧选项卡;修复资产授权添加用户,会显示其他org用户的bug (#1594)
* [Bugfix] 修复资产授权添加用户,显示其他org的用户bug

* [Update] org admin 显示资产详情右侧选项卡
2018-07-26 05:25:44 -05:00
BaiJiangJie
d649aacfd6 [Update] asset platform 取消*required (#1595) 2018-07-26 05:25:14 -05:00
ibuler
7e65e44a3c [Update] 兼容guacamole手动模式上传system user是uuid 2018-07-26 18:12:25 +08:00
ibuler
74c3f12275 [Update] 添加脚本,将windows协议改为rdp 2018-07-26 14:37:10 +08:00
ibuler
8c12c382a5 [Bugfix] 修复merge带来的问题 2018-07-25 18:21:37 +08:00
ibuler
2ecfecb06f [Update] Merge with dev 2018-07-25 18:18:18 +08:00
BaiJiangJie
ac238aa36e [Update] 修改用户登录失败限制次数,3->7 (#1586)
* [Update] 修改用户登录失败限制次数,3->7

* [Update] 修改用户登录失败限制次数,3->7 - 续
2018-07-25 04:51:09 -05:00
ibuler
2abb9efe96 [Update] Org功能修改 2018-07-25 17:21:13 +08:00
BaiJiangJie
f17727deb9 [Update] 在线/历史/命令model添加org;修复命令记录保存org失败bug (#1584)
* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;

* [Update] 在线/历史/命令model添加org

* [Bugfix] 修复命令记录,保存org不成功bug
2018-07-25 02:13:53 -05:00
老广
36f1165d1b Tmp org (#1583)
* [Update] 修改一些内容

* [Update] 修改datatable 支持process

* [Bugfix] 修复asset queryset 没有valid方法的bug
2018-07-25 02:05:28 -05:00
BaiJiangJie
e7c530d8e6 [Update] 添加授权规则org (#1580)
* [Update] 修复创建授权规则,显示org_name不是有效UUID的bug

* [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug;
2018-07-24 22:28:20 -05:00
老广
b156f4ad16 Tmp org (#1579)
* [Update] 添加org api, 升级到django 2.0

* [Update] fix some bug

* [Update] 修改一些bug
2018-07-24 22:21:12 -05:00
老广
16b23a37fe 增加命令导出 (#1566)
* [Update] gunicorn不使用eventlet

* [Update] 添加eventlet

* 替换淘宝IP查询接口

* [Feature] 添加命令记录下载功能 (#1559)

* [Feature] 添加命令记录下载功能

* [Update] 文案修改,导出记录、提交,取消全部命令导出

* [Update] 命令导出,修复时间问题

* [Update] paramiko => 2.4.1

* [Update] 修改settings

* [Update] 修改权限判断
2018-07-24 22:09:58 -05:00
ibuler
e41add6126 Merge remote-tracking branch 'github/dev' into dev 2018-07-25 10:23:18 +08:00
ibuler
f4c31d8e86 [Update] 修改权限判断 2018-07-25 10:22:32 +08:00
ibuler
28e8f204ec Merge remote-tracking branch 'github/dev' into dev 2018-07-24 16:41:17 +08:00
ibuler
80f147cf13 [Update] 修改settings 2018-07-24 16:40:41 +08:00
老广
c816875f28 [Update] 修改permission (#1574) 2018-07-22 23:55:13 -05:00
ibuler
1c56ba5a11 [Update] paramiko => 2.4.1 2018-07-20 18:48:57 +08:00
老广
2208d6d51e [Bugfix] 解决Node.root() 死循环,移动AdminRequired到permission中 (#1571) 2018-07-20 05:42:01 -05:00
ibuler
e3aa18ff2d [Update] 修改get_current_org 为 proxy对象 current_org 2018-07-20 17:49:47 +08:00
ibuler
b5f6f80ae6 [Update] 修改Node 2018-07-20 13:25:50 +08:00
ibuler
bbe4080008 [Update] 删掉Premiddleware 2018-07-20 12:15:45 +08:00
ibuler
cd797b18fb Merge remote-tracking branch 'github/dev' into dev 2018-07-20 11:10:51 +08:00
ibuler
b6523da603 [Update] 修改外键为org_id 2018-07-20 10:54:16 +08:00
ibuler
c24f1a0517 [Update] 修改约束 2018-07-19 19:24:29 +08:00
BaiJiangJie
83f220d7de [Feature] 添加命令记录下载功能 (#1559)
* [Feature] 添加命令记录下载功能

* [Update] 文案修改,导出记录、提交,取消全部命令导出

* [Update] 命令导出,修复时间问题
2018-07-19 05:36:52 -05:00
老广
8e42a65736 Merge pull request #1560 from wojiushixiaobai/dev
[Update]替换淘宝IP查询接口
2018-07-19 05:03:59 -05:00
wojiushixiaobai
e1fff18ce3 替换淘宝IP查询接口 2018-07-19 14:14:58 +08:00
ibuler
3052744203 [Update] 添加eventlet 2018-07-18 19:17:20 +08:00
ibuler
7924b094f8 [Update] gunicorn不使用eventlet 2018-07-18 16:15:35 +08:00
老广
3d34b06203 Merge pull request #1551 from jumpserver/dev
[Bugfix] 修复转义引起的中文乱码问题
2018-07-18 02:33:33 -05:00
ibuler
c94d018d7e [Bugfix] 修复转义引起的中文乱码问题 2018-07-18 15:31:14 +08:00
老广
53086a8977 Merge pull request #1550 from jumpserver/dev
Dev
2018-07-18 02:28:13 -05:00
ibuler
ce1fc0f3e2 [Bugfix] 修复授权列表引起的分页bug 2018-07-18 15:26:58 +08:00
ibuler
c215278978 [Bugfix] 修复授权列表引起的分页bug 2018-07-18 15:24:58 +08:00
老广
061963a316 Merge pull request #1549 from jumpserver/revert-1515-master
Revert "授权页面分页问题"
2018-07-18 01:56:18 -05:00
老广
97b240cfdd Revert "授权页面分页问题" 2018-07-18 14:55:16 +08:00
ibuler
fd5f562cbf [Update] stash it 2018-07-18 12:57:08 +08:00
老广
722bf786f1 Merge pull request #1542 from jumpserver/dev
Dev to master
2018-07-17 04:26:37 -05:00
老广
2cb5876d1a Merge branch 'master' into dev 2018-07-17 04:25:37 -05:00
ibuler
8883e0090f [Update] 更改版本号 2018-07-17 17:12:28 +08:00
ibuler
790652ff4d [Update] 修改协议小问题 2018-07-17 17:09:36 +08:00
ibuler
ff3f74abe6 [Update] 修改api 2018-07-17 12:06:47 +08:00
老广
1a49cf4d9c Merge pull request #1532 from wojiushixiaobai/dev
[Update]修改url地址
2018-07-16 22:24:09 -05:00
ibuler
4d1da56872 [Update] 修改asset api 2018-07-16 14:47:06 +08:00
老广
ff9e109a2c Merge pull request #1533 from jumpserver/update_unblockuser
[Update] 取消系统用户-清除认证信息,取消-网关rdp协议认证信息,添加解除用户登录限制功能
2018-07-16 01:42:30 -05:00
BaiJiangJie
09e636495e [Update] 模版删除unblock的id属性 2018-07-16 12:22:32 +08:00
BaiJiangJie
14076d8fe1 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-07-16 12:13:59 +08:00
BaiJiangJie
812078331e [Update] 添加解除用户登录限制功能 2018-07-16 12:13:13 +08:00
wojiushixiaobai
696589a3cf 修改url 2018-07-16 12:13:06 +08:00
wojiushixiaobai
7eba46b303 Merge branch 'dev' of https://github.com/jumpserver/jumpserver into dev 2018-07-16 11:01:49 +08:00
wojiushixiaobai
5d63d2369f 合并冲突解决 2018-07-16 11:01:06 +08:00
ibuler
3e17e94245 [Update] 修改资产 2018-07-15 18:39:11 +08:00
ibuler
5648dcd7e7 [Update] 修改用户view 2018-07-15 00:55:05 +08:00
ibuler
28e47f33c1 [Update] 修改一些逻辑 2018-07-14 00:47:21 +08:00
老广
91b3b7ce69 Merge pull request #1515 from wstart/master
授权页面分页问题
2018-07-13 08:22:34 -05:00
老广
8c587a1376 Merge pull request #1523 from wojiushixiaobai/dev
[Update]修改url地址
2018-07-13 08:19:40 -05:00
BaiJiangJie
1182313c1a [Update] 取消系统用户-清除认证信息,取消-网关rdp协议认证信息,添加用户unblock功能 2018-07-13 19:30:48 +08:00
ibuler
7412bdcba7 [Update] 完成基本框架 2018-07-13 15:05:46 +08:00
ibuler
d6ec92d82d [Update] 修改url 2018-07-13 00:31:50 +08:00
ibuler
ad3214641d [Update] 添加org 2018-07-13 00:00:35 +08:00
wojiushixiaobai
9004351ad1 修正字符串 2018-07-12 16:47:35 +08:00
wojiushixiaobai
d3e22a2a90 修改url地址 2018-07-12 10:53:39 +08:00
wstart
fd3df81a64 Update api.py 2018-07-10 17:03:31 +08:00
ycfuck
72517a2c72 授权页面分页
授权页面分页
2018-07-10 16:33:28 +08:00
ibuler
01185a2d07 [Update] 添加gateway api 2018-07-10 16:03:05 +08:00
ibuler
8bfd2be21f [Update] 清除系统用户认证信息时提示 2018-07-10 15:50:15 +08:00
老广
b03ac46df9 Merge pull request #1456 from wojiushixiaobai/dev
[Update]修改提示文字
2018-07-10 02:35:26 -05:00
老广
76be054fcb Merge pull request #1502 from Nevss/patch-1
update docker-guacamole version  to latest
2018-07-10 02:32:06 -05:00
老广
df95c93bb1 Merge pull request #1503 from jumpserver/update_loginlog
[Update] 记录用户登录失败日志,限制用户登录失败次数
2018-07-10 02:31:39 -05:00
ycfuck
eaefb5c669 Revert "资产授权页面增加分页"
This reverts commit 0ddb9476ba.
2018-07-09 16:00:02 +08:00
林峰任1049851
0ddb9476ba 资产授权页面增加分页
资产授权页面增加分页
2018-07-09 15:55:07 +08:00
ibuler
75a9deebdd Merge remote-tracking branch 'github/dev' into dev 2018-07-08 13:03:43 +08:00
ibuler
b6cd4a20c5 [Bugfix] Data table xss attack 2018-07-08 13:02:43 +08:00
BaiJiangJie
435acafccd [Update] 在线会话,历史会话显示登录来源 Web/SSH Terminal 2018-07-06 13:22:20 +08:00
BaiJiangJie
86e4bc5e9a Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-07-06 10:57:42 +08:00
ibuler
315609bc45 Merge remote-tracking branch 'github/dev' into dev 2018-07-06 10:35:31 +08:00
ibuler
814e6a7df8 [Update] 修改sshpass 引号引入密码,特殊字符处理 2018-07-06 10:34:16 +08:00
Nevss
7a7c6d40df update docker-guacamole version to latest
update docker-guacamole version  to latest
2018-07-05 19:08:35 +08:00
BaiJiangJie
5d800fa629 [Update] 修改coco端登录限制次数逻辑 2018-07-05 17:07:03 +08:00
BaiJiangJie
bd14266abd [Update] 修改登录日志模型类的MFA字段默认状态为- 2018-07-05 16:45:05 +08:00
BaiJiangJie
88f36c6f02 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-07-05 16:23:48 +08:00
BaiJiangJie
512fc8f8f0 [Update] 优化登录失败次数限制的逻辑,并添加系统安全设置选项 2018-07-05 16:23:33 +08:00
ibuler
37bb344166 [Bugfix] 修复网关端口不对的问题 2018-07-05 10:58:52 +08:00
ibuler
f8c2a445f7 [Bugfix] 修改gateway port 2018-07-05 10:26:16 +08:00
BaiJiangJie
ff9b1a887f [Update] 添加用户登录失败次数限制 2018-07-04 16:55:11 +08:00
BaiJiangJie
43370c547a [Update] 登录日志逻辑修改变量名 2018-07-04 10:57:37 +08:00
BaiJiangJie
2eda58eadd Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-07-03 17:48:22 +08:00
BaiJiangJie
e1be867913 [Update] 更新用户登录日志,记录登录失败日志并添加MFA启用状态信息 2018-07-03 17:47:12 +08:00
ibuler
01afcf701c Merge remote-tracking branch 'origin/dev' into dev 2018-07-03 13:24:07 +08:00
ibuler
4002289974 [Bugfix] 修复详情中删除时的bug 2018-07-03 13:23:21 +08:00
ibuler
ef9e03c7ed Merge remote-tracking branch 'github/dev' into dev 2018-07-02 12:11:45 +08:00
老广
442d4e727a Merge pull request #1492 from jumpserver/feature_telnet
[Feature] 添加功能,支持 telnet server.
2018-07-01 22:23:20 -05:00
BaiJiangJie
3993797527 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-06-29 15:00:15 +08:00
BaiJiangJie
f1f06491d6 [Feature] 添加功能, 支持 telnet server. 2018-06-29 14:59:43 +08:00
ibuler
eb95a0a912 [Bugfix] 修改下载录像没有目录的bug 2018-06-27 10:34:16 +08:00
ibuler
401a7f88a8 [Update] 更新依赖storage的版本 2018-06-26 22:27:16 +08:00
ibuler
0a2ff83ca1 Merge remote-tracking branch 'origin/dev' into dev 2018-06-26 22:14:21 +08:00
ibuler
7276bd0b2a [Update] 更新获取session log的方法,以后统一到media/replay中 2018-06-26 22:13:39 +08:00
wojiushixiaobai
2950613b69 修改提示文字 2018-06-20 15:58:01 +08:00
老广
6fa0562d7a Merge pull request #1441 from chnliyong/master
python-gssapi libkrb5-dev for gssapi/gssapi.h
2018-06-18 21:25:15 -05:00
老广
ef22d33afa Merge pull request #1161 from Nidhoggur1993/dev
更新make_migrations.sh的migration逻辑
2018-06-13 21:53:23 -05:00
老广
86404db6c7 Merge pull request #1414 from leostudio/dev
[fixed #1404]新增Redis DB配置
2018-06-13 21:51:53 -05:00
老广
fdf2807d9b Merge pull request #1419 from jumpserver/feature_login_mode
[Update] 添加功能,系统用户选择登录模式(自动/手动登录)
2018-06-13 21:25:00 -05:00
BaiJiangJie
2e6d238c76 [Bugfix] 修复系统用户选择手动登录,ssh命令行连接获取不到login_mode字段值 2018-06-11 19:09:24 +08:00
BaiJiangJie
f5a4370b80 [Update] 系统用户选择手动登录,取消自动推送 2018-06-11 17:56:20 +08:00
BaiJiangJie
db2273ef27 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-06-11 16:28:10 +08:00
BaiJiangJie
dd07fa678f [Update] 添加功能,系统用户选择登录模式(自动/手动登录) 2018-06-11 16:27:40 +08:00
老广
e7a731fae9 Merge pull request #1417 from jumpserver/dev
Dev
2018-06-10 22:33:57 -05:00
ibuler
73f9f54620 [Update] 修改版本号 2018-06-11 11:23:32 +08:00
梁志艳
a2f23e9681 新增Redis DB配置 2018-06-09 00:19:57 +08:00
ibuler
dbc471c195 [Bugfix] 修复小错误 2018-06-08 18:40:31 +08:00
ibuler
4c8eb4a94b Merge remote-tracking branch 'github/dev' into dev 2018-06-08 18:34:03 +08:00
老广
9946c4612f Merge pull request #1409 from jokimina/dev_for_pr2
[Update]删除示例配置中未使用的BROKER_URL配置
2018-06-08 05:32:38 -05:00
老广
8a5e1b8223 Merge pull request #1407 from jokimina/dev_for_pr
[Bugfix] 更新用户信息时不填密码也会触发密码规则验证,导致无法更新
2018-06-08 05:31:05 -05:00
老广
06ce098e00 Merge pull request #1408 from jumpserver/bugfix_user_date_expired
[Bugfix] 修复用户失效日期截止后还可登录问题
2018-06-08 05:29:12 -05:00
ibuler
5579d3f0de [Update] 修复测试网关连接known_host问题 2018-06-08 18:28:15 +08:00
xiaodong
1ef582e9ac [Update]删除示例配置中未使用的BROKER_URL配置 2018-06-08 16:39:16 +08:00
BaiJiangJie
221fae5875 [Bugfix] 修复用户失效日期截止后还可登录问题 2018-06-08 16:34:15 +08:00
xiaodong
f4084c800a [Bugfix] 更新用户信息时不填密码也会触发密码规则验证,导致无法更新 2018-06-08 16:20:28 +08:00
老广
5464ac8167 Merge pull request #1397 from jumpserver/feature_security_setting
[Feature] 增加功能,安全设置(全局MFA设置,密码强度校验)
2018-06-06 23:57:48 -05:00
BaiJiangJie
c6a8967376 [Update] 删除多余代码 2018-06-07 12:52:40 +08:00
BaiJiangJie
5f2c31e42c [Update] 重置公钥,添加成功信息(user-pubkey-update)/自动跳转下一步(first-login) 2018-06-07 12:29:55 +08:00
BaiJiangJie
64db02c3f8 [Update] 添加用户来源 db/ldap 2018-06-06 16:59:32 +08:00
BaiJiangJie
842841128f Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-06-06 15:35:40 +08:00
BaiJiangJie
b026e86741 [Update] 修改判断MFA是否全局启用的逻辑,放到User.otp_force_enabled中 2018-06-06 15:35:26 +08:00
老广
283b1c1d64 Merge pull request #1403 from jumpserver/dev
[Update] 修改清理用户组脚本
2018-06-06 02:25:10 -05:00
ibuler
c8c0479ce5 [Update] 修改清理用户组脚本 2018-06-06 15:18:23 +08:00
老广
2a30204c4e Merge pull request #1401 from jumpserver/dev
Dev
2018-06-06 01:57:41 -05:00
ibuler
30afcecf59 Merge remote-tracking branch 'github/dev' into dev 2018-06-06 14:43:11 +08:00
ibuler
80d11bbaab [Update] 删除user group继承的抽象类 2018-06-06 14:42:06 +08:00
ibuler
58e36b5f63 [Bugfix] 暂时还不能删除Tree这个类 2018-06-06 11:13:13 +08:00
ibuler
2c413e8d51 [Bugfix] 修复权限查看引起的bug 2018-06-06 10:49:16 +08:00
老广
17de014ee9 Merge pull request #1400 from jokimina/dev_for_pr
remove useless image
2018-06-05 21:19:08 -05:00
xiaodong
6b2a38c78d remove useless image 2018-06-06 08:17:45 +08:00
BaiJiangJie
fcd17460d7 [Update] 增加密码校验规则中密码最小长度限制6位 2018-06-05 17:56:58 +08:00
BaiJiangJie
4c4430661b [Merge] Merge branch dev of github.com:jumpserver/jumpserver into github_dev 2018-06-05 17:31:55 +08:00
BaiJiangJie
ee35ca3643 [Feature] 增加功能,安全设置(全局MFA设置,密码强度校验) 2018-06-05 17:26:31 +08:00
老广
99c4875dd7 Merge pull request #1395 from jumpserver/dev
Dev
2018-06-04 22:06:58 -05:00
ibuler
482d1bb27f [Update] 修改permisstion util 2018-06-01 16:22:52 +08:00
ibuler
e7c7c3a7a8 Merge branch 'dev' into perm_tree 2018-06-01 15:41:50 +08:00
ibuler
54efc88799 Merge remote-tracking branch 'github/dev' into dev 2018-06-01 15:39:43 +08:00
ibuler
a9d1538135 [Update] 修改一些方法 2018-06-01 15:34:08 +08:00
ibuler
f8ff223f90 [Update] 更新api和model方法 2018-05-31 19:47:57 +08:00
老广
a3f1622a50 Merge pull request #1371 from jumpserver/fix_asset_count
[Update] 修改资产树节点(数量)总显示此节点以及子孙节点下的所有资产总数
2018-05-31 12:31:44 +08:00
老广
0021f2e5e1 Merge pull request #1377 from wojiushixiaobai/dev
[Update]更新自动升级脚本
2018-05-31 12:29:21 +08:00
wojiushixiaobai
dbcad47214 更新升级文档 2018-05-30 11:23:54 +08:00
wojiushixiaobai
6c384b49fe 修改脚本 2018-05-30 11:19:37 +08:00
BaiJiangJie
526943a041 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-05-29 19:26:19 +08:00
BaiJiangJie
e9d0104a69 Merge branch 'dev' of github.com:jumpserver/jumpserver into fix_asset_count_dev 2018-05-29 19:20:03 +08:00
BaiJiangJie
7c694c6885 [Update] 合并Node的parent.setter方法,修改node_fake的parent逻辑 2018-05-29 19:19:35 +08:00
ibuler
69e5ab438a [Bugfix] 解决发送邮件重启的问题 2018-05-29 18:58:56 +08:00
ibuler
757a31a52f [Update] 优化asset api, 获取时增加prefetch 2018-05-29 13:19:58 +08:00
BaiJiangJie
5b53cfb4dd [Update] 修改资产树节点(数量)总显示此节点下的资产总数 2018-05-29 11:50:09 +08:00
ibuler
b0710c42b0 Merge remote-tracking branch 'github/dev' into dev 2018-05-29 11:27:56 +08:00
老广
d9d82cea5e Merge pull request #1370 from jumpserver/fix_asset_count
[Update] 修改资产树和资产列表数量显示(游离资产)
2018-05-29 11:26:23 +08:00
老广
4f521e5a94 Merge pull request #1369 from gengkeye/master
录像回放API BUG: SessionReplayViewSet
2018-05-29 11:20:23 +08:00
BaiJiangJie
3a2973023c [Update] 修改资产树和资产列表数量显示(游离资产) 2018-05-29 11:17:09 +08:00
oldseven
4f28f85410 #1368 2018-05-29 09:51:55 +08:00
oldseven
a1905ecfdb Merge branch 'master' of https://github.com/jumpserver/jumpserver
Conflicts:
	apps/terminal/api.py
2018-05-29 09:39:30 +08:00
ibuler
47397d2308 [Bugfix] 修复网关测试连接需要ssh信任key的问题 2018-05-28 16:44:00 +08:00
ibuler
7b57d24dc9 [Update] 优化代码 2018-05-28 15:00:06 +08:00
老广
f2216274c5 Merge pull request #1366 from jumpserver/dev
Dev
2018-05-28 13:26:04 +08:00
ibuler
ffabef0040 Merge branch 'large' into dev 2018-05-28 13:20:58 +08:00
ibuler
0b4df78393 [Update] 修改permistion utils优化返回的属性 2018-05-28 13:20:26 +08:00
BaiJiangJie
2fab69ca61 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-05-28 11:15:48 +08:00
老广
7987056b12 Merge pull request #1365 from jumpserver/dev
Dev修复
2018-05-28 11:05:11 +08:00
BaiJiangJie
f9ab0abc37 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-05-28 10:58:47 +08:00
ibuler
0bc86543b5 [Update] 修改用户组详情显示授权资产的bug 2018-05-26 21:53:50 +08:00
ibuler
fc2a44621b [Bugfix] 修复右击位置不对的bug 2018-05-25 19:17:57 +08:00
ibuler
9b4b9e6900 [Update] 发送邮件增加用户名显示 2018-05-25 18:13:48 +08:00
老广
62c114d9c4 Merge pull request #1362 from jumpserver/dev
修改树形结构
2018-05-25 17:53:40 +08:00
ibuler
22a84d57ca [Update] 修改树形结构 2018-05-25 17:32:10 +08:00
ibuler
3f4b5ad465 [Update] Merge 2018-05-25 17:28:53 +08:00
ibuler
a96bda8ca9 [Update] 修改树形结构 2018-05-25 17:26:57 +08:00
ibuler
bff3868b8f [Update] 统一requirements版本 2018-05-25 15:23:52 +08:00
老广
8470dce805 Merge pull request #1357 from jumpserver/dev
Dev
2018-05-24 18:58:47 +08:00
ibuler
b9d0d89f66 [Update] 更改版本好 2018-05-24 13:32:20 +08:00
ibuler
5a7192e035 [Update] 修改心跳bug 2018-05-24 13:30:15 +08:00
BaiJiangJie
de2416b173 Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-05-24 12:42:06 +08:00
BaiJiangJie
486793ddcd [Bugfix] 修复取消LDAP认证不成功bug 2018-05-24 12:41:48 +08:00
老广
c40c5ac543 Merge pull request #1355 from jumpserver/dev
调整Luna树形结构修复一些bug
2018-05-24 11:39:01 +08:00
ibuler
c529061ee0 [Bugfix] 修复授权uitls的错误 2018-05-24 11:18:20 +08:00
ibuler
fe52c57a11 [Bugfix] 修复授权uitls的错误 2018-05-24 11:13:22 +08:00
ibuler
f8384973a1 [Update] 调整Luna的树形结构 2018-05-23 15:15:27 +08:00
ibuler
dc1d228e07 [Update] 更新requirements 2018-05-22 19:44:13 +08:00
ibuler
092b33d4d1 [Update] 更新requirements 2018-05-22 19:30:15 +08:00
ibuler
d615eb80b5 [Update] 优化使用storage sdk 2018-05-22 18:22:06 +08:00
ibuler
46520287d9 [Update] 添加清理重复用户组脚本 2018-05-17 15:26:22 +08:00
ibuler
4b7af1457d [Update] 系统用户新增清除认证的操作 2018-05-17 11:39:04 +08:00
BaiJiangJie
c1db33713f [Bugfix] 修复资产授权树root节点bug 2018-05-16 16:12:27 +08:00
老广
c3101dba29 Merge pull request #1334 from jumpserver/dev
Dev
2018-05-16 12:43:54 +08:00
老广
e66cfc2e13 Merge pull request #1333 from jumpserver/update_rdp
[Update] 去掉windows相关无用配置,修复资产树节点移动等问题
2018-05-16 12:42:01 +08:00
BaiJiangJie
ac67c231fc [Update] 修改前端rdp相关配置的选择逻辑 2018-05-16 12:31:13 +08:00
BaiJiangJie
599431f402 [Update] 去掉添加windows相关配置时的一些无用功能,网域网关/会话列表 2018-05-15 17:32:20 +08:00
ibuler
ed18cb317f [Bugfix] 修复创建node api的bug 2018-05-15 15:05:49 +08:00
ibuler
cb4afabc91 [Bugfix] 修复创建node节点的问题,修复windows session的问题 2018-05-15 14:44:02 +08:00
BaiJiangJie
718715cc6d [Update] 创建系统用户选择rdp协议,去掉相关配置的一些无用功能 2018-05-15 12:52:53 +08:00
BaiJiangjie
38f8c5bb72 [Bugfix] 修复资产树节点移动,资产授权树节点下的资产显示问题 2018-05-14 14:21:32 +08:00
ibuler
ebc1b4975a [Update] 自动生成秘钥去掉密码 2018-05-11 18:38:26 +08:00
ibuler
2da87151ed Merge remote-tracking branch 'github/dev' into dev 2018-05-11 12:01:54 +08:00
老广
ab9d457ce0 Merge pull request #1312 from jumpserver/bugfix_asset_tree_node
修复:资产树节点,取消model value unique,并添加唯一性校验,serializers/api中;修改新增节点计数器规则;
2018-05-11 12:01:13 +08:00
BaiJiangjie
cbc4a0a97b [Bugfix] 资产树节点Node Value值校验修改异常抛出为400 2018-05-11 11:27:48 +08:00
ibuler
0031d025aa [Bugfix] 修复上次提交引起的bug 2018-05-11 09:28:20 +08:00
BaiJiangjie
4c53eebdbe Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev 2018-05-10 21:15:49 +08:00
BaiJiangjie
2583c0b26c [Bugfix] 资产树节点,取消model value unique,并添加唯一性校验,修改新增节点计数器规则 2018-05-10 20:48:16 +08:00
ibuler
ebef4f254a Merge remote-tracking branch 'github/dev' into dev 2018-05-10 12:29:09 +08:00
老广
84d3fa6db0 Merge pull request #1303 from jumpserver/update_asset_tree
[Update] 添加功能:资产树显示全部/只显示当前节点下的资产;修复资产树节点主机数与列表显示不一致
2018-05-10 12:28:34 +08:00
ibuler
e2fa492987 Merge remote-tracking branch 'github/dev' into dev 2018-05-10 12:25:10 +08:00
ibuler
116a04da68 Merge branch 'term_js' into dev 2018-05-10 12:24:09 +08:00
ibuler
7de6af89ad [Update] 使用xterm.js替换原来的term.js 2018-05-10 12:19:37 +08:00
ibuler
8e74a04282 [Update] 修改一些bug 2018-05-09 17:35:46 +08:00
BaiJiangjie
3af01d6a31 [Update] 添加功能:资产树显示全部/只显示当前节点下的资产;修复资产树节点主机数与列表显示不一致 2018-05-08 18:02:19 +08:00
老广
5bfc34a9ea Merge pull request #1301 from jumpserver/dev
Dev
2018-05-08 11:00:26 +08:00
老广
869a84964d Merge branch 'master' into dev 2018-05-08 10:59:59 +08:00
老广
2a8358b1aa Bugfix ldap option (#1300)
* [Update] 修改replay api

* [Bugfix] 修复ldap参数改错引起的bug
2018-05-08 10:48:52 +08:00
ibuler
0a9af98729 [Bugfix] 修复ldap参数改错引起的bug 2018-05-08 10:13:33 +08:00
老广
dfd98f8aea Dev (#1299)
* [Update] 修改版本号

* [Update] 默认关闭定时任务

* [Update] 修改授权列表创建api,返回id

* [Update] 修改用户名校验规则

* Perms return (#1293)

* [Update] 修改版本号

* [Update] 默认关闭定时任务

* [Update] 修改授权列表创建api,返回id

* [Update] 修改用户名校验规则

* [Update] 添加ldap auth timeout时间

* [Update] 修改replay api (#1298)
2018-05-08 09:06:33 +08:00
老广
e9b86ca668 [Update] 修改replay api (#1298) 2018-05-08 09:05:10 +08:00
ibuler
d2b0aba620 [Update] 修改replay api 2018-05-07 19:28:01 +08:00
ibuler
7f670ab709 Merge remote-tracking branch 'github/dev' into dev 2018-05-07 13:13:08 +08:00
ibuler
941e55bdec [Update] 添加ldap auth timeout时间 2018-05-07 13:11:02 +08:00
老广
e630321e55 Perms return (#1293) (#1294)
* [Update] 修改版本号

* [Update] 默认关闭定时任务

* [Update] 修改授权列表创建api,返回id

* [Update] 修改用户名校验规则
2018-05-04 15:04:39 +08:00
老广
01d136cf1e Perms return (#1293)
* [Update] 修改版本号

* [Update] 默认关闭定时任务

* [Update] 修改授权列表创建api,返回id

* [Update] 修改用户名校验规则
2018-05-04 15:01:49 +08:00
ibuler
4ed9e11090 [Update] 修改用户名校验规则 2018-05-04 14:58:52 +08:00
ibuler
cde2e7adb0 [Update] 修改授权列表创建api,返回id 2018-05-04 14:54:58 +08:00
zhangbohan
7a27021d3d 图片调小一些 2018-05-03 23:02:12 +08:00
zhangbohan
966123e4c6 README功能说明修改 2018-05-03 22:48:24 +08:00
ibuler
e291ca9057 [Update] 默认关闭定时任务 2018-05-02 18:15:48 +08:00
老广
84003b777c Merge pull request #1283 from jumpserver/dev
更改版本号
2018-05-02 10:54:38 +08:00
老广
1edfb1cec4 [Update] 修改版本号 (#1282) 2018-05-02 10:52:57 +08:00
ibuler
0b89ff17fd [Update] 修改版本号 2018-05-02 10:48:39 +08:00
老广
9fa6b3e387 [Update] 暂时隐藏rdp的session,等待windows录像播放 (#1276) (#1278) 2018-04-27 21:23:18 +08:00
老广
c0bbda9769 [Update] 暂时隐藏rdp的session,等待windows录像播放 (#1276) 2018-04-27 15:38:13 +08:00
oldseven
0d8a600277 #1258 bug fix: 阿里云OSS录像无法回放问题 2018-04-27 15:17:32 +08:00
老广
d7a32120ba Merge pull request #1274 from jumpserver/dev
Merge with dev
2018-04-27 12:44:00 +08:00
老广
55096f9ad5 Bugfix perm asset not active (#1273)
* [Bugfix] 修复资产禁用了还可以登录的bug
2018-04-27 11:41:47 +08:00
老广
494cd760d7 Trans change (#1272)
* [Update] 更新授权规则生效日期文案
2018-04-27 11:33:28 +08:00
老广
6f494ef09c 校验用户名长度和特殊字符 (#1271)
* [Bugfix] 修复系统用户用户名校验问题

* [Update] 增加长度限制
2018-04-27 11:27:16 +08:00
wojiushixiaobai
e476cab2a1 更新自动升级脚本 (#1250) 2018-04-27 11:01:52 +08:00
老广
cc67fcb53b Merge pull request #1269 from jumpserver/dev
Dev
2018-04-26 21:02:00 +08:00
老广
b074bd8fbd Merge pull request #1267 from jumpserver/bugfix_user
[Bugfix] 修复首次登录条款问题及引导页面MFA配置问题
2018-04-26 21:00:20 +08:00
老广
627582233b Merge pull request #1268 from jumpserver/bufix_for_perm_tree
[Bugfix] 解决上次引入的bug
2018-04-26 20:59:05 +08:00
ibuler
5103dab72e [Bugfix] 解决上次引入的bug 2018-04-26 20:48:32 +08:00
老广
43c13355f2 Merge pull request #1266 from jumpserver/dev
Dev
2018-04-26 19:54:42 +08:00
老广
59eb1f8e3e Merge pull request #1265 from jumpserver/bufix_for_perm_tree
[Bugfix] 修复授权树列表和资产树列表不同的bug
2018-04-26 19:53:39 +08:00
ibuler
16aa42a861 [Bugfix] 修复授权树列表和资产树列表不同的bug 2018-04-26 19:51:32 +08:00
BaiJiangjie
0962a16b22 [Bugfix] 修复首次登录条款问题及引导页面MFA配置问题 2018-04-26 19:49:09 +08:00
wojiushixiaobai
7c35e75586 更新自动升级脚本 2018-04-25 19:49:22 +08:00
老广
787be3ff7a Merge pull request #1248 from jumpserver/dev
Bugfix 修复搜索命令的bug
2018-04-25 17:55:39 +08:00
ibuler
d5debc375e [Bugfix] 修复命令搜索异常bug 2018-04-25 17:48:44 +08:00
ibuler
40a0c4597b Merge remote-tracking branch 'github/dev' into dev 2018-04-25 15:15:36 +08:00
老广
5c17b1a7f7 Merge pull request #1240 from jumpserver/dev
首次登陆免验证码,首次登录信息变更
2018-04-25 10:00:56 +08:00
BaiJiangjie
ea2863a51b [Update] 创建用户、资产成功的提示信息可关闭 2018-04-24 22:10:52 +08:00
BaiJiangjie
102e1ca97c [Bugfix] 创建资产,资产系统置上,RDP:3389 2018-04-24 18:29:14 +08:00
BaiJiangjie
2823d02763 [Bugfix] 修复资产列表导入,资产id问题 2018-04-24 16:15:04 +08:00
BaiJiangjie
c37414045b [Bugfix] 修改用户列表导出,默认导出全部用户 2018-04-24 15:16:05 +08:00
BaiJiangjie
784bec42ff [Update] 修改用户登录,首次登录不需要验证码,登录失败时需要验证码校验 2018-04-24 13:00:36 +08:00
ibuler
9a3d0732bc [Update] 更新授权代码 2018-04-24 11:07:09 +08:00
ibuler
20a7247b16 Merge remote-tracking branch 'github/dev' into dev 2018-04-24 10:52:31 +08:00
ibuler
df60981eb4 Merge branch 'dev' of bitbucket.org:jumpserver/core into dev 2018-04-24 10:51:30 +08:00
老广
7aa2bb06e8 Merge pull request #1228 from wojiushixiaobai/dev
添加升级脚本
2018-04-24 10:41:23 +08:00
liuzheng712
536be1175a feat: replay api update
make a v2 session replay api, get the json response
2018-04-24 08:53:30 +08:00
BaiJiangjie
b5fc76d6a5 [Update] 修改用户首次登录页 2018-04-23 21:04:46 +08:00
ibuler
941dd627e3 [Update] 更新session列表 2018-04-23 11:44:09 +08:00
ibuler
8389c85054 [Update] 更改sesion表结构 2018-04-23 11:40:30 +08:00
ibuler
02ca8c3139 [Update] 更新session api 2018-04-23 11:32:46 +08:00
wojiushixiaobai
abd20f31b8 更新升级脚本 2018-04-22 22:37:51 +08:00
wojiushixiaobai
5c7acae018 更改版本号 2018-04-22 20:18:10 +08:00
wojiushixiaobai
1c623f71e0 添加升级脚本 2018-04-22 19:22:55 +08:00
BaiJiangjie
5b52b907c0 [Update] 创建用户,更新用户,添加MFA设置选项 2018-04-20 16:15:45 +08:00
BaiJiangjie
8447c6f487 [Update] 修改coco文案OTP改为MFA 2018-04-20 11:51:32 +08:00
老广
e865484a56 Merge pull request #1224 from jumpserver/dev
Dev
2018-04-20 11:36:16 +08:00
BaiJiangjie
ad6e22cd42 Merge branch 'dev' of github.com:jumpserver/jumpserver into dev 2018-04-20 11:24:21 +08:00
BaiJiangjie
7a219e1710 [Update] OTP文案修改为MFA 2018-04-20 11:23:31 +08:00
老广
a0a8419c5e Merge pull request #1221 from jumpserver/dev
[Bugfix] 修复用户错误storage的bug
2018-04-19 18:27:49 +08:00
ibuler
0c24310510 [Bugfix] 修复用户错误storage的bug 2018-04-19 18:26:59 +08:00
老广
967491fba5 Merge pull request #1220 from jumpserver/dev
[Update] 修改用户详情,自己更改自己otp,提示小写
2018-04-19 17:56:41 +08:00
ibuler
9ac7f26c74 [Update] 修改用户详情,自己更改自己otp,提示小写 2018-04-19 17:55:40 +08:00
老广
910f3cdddc Merge pull request #1219 from jumpserver/dev
[Bugfix] 修复用户登录缓存设置问题
2018-04-19 17:22:04 +08:00
BaiJiangjie
f73fe1f315 [Bugfix] 修复用户登录缓存设置问题 2018-04-19 17:20:53 +08:00
老广
28acc6cc63 Merge pull request #1217 from jumpserver/dev
[Bugfix] 修复解密None的bug
2018-04-19 16:39:27 +08:00
ibuler
763cf0d981 [Bugfix] 修复解密None的bug 2018-04-19 16:35:38 +08:00
老广
611289a5ec Merge pull request #1214 from jumpserver/dev
支持二次认证
2018-04-19 12:16:25 +08:00
ibuler
95a8bf0988 Merge remote-tracking branch 'github/dev' into dev 2018-04-19 11:46:07 +08:00
ibuler
947f7d206a [Bugfix] 修复生成system user的问题 2018-04-19 11:16:52 +08:00
BaiJiangjie
12c8cf6b76 [Update] 添加OTP认证功能 2018-04-19 11:13:11 +08:00
BaiJiangjie
33bc73aba7 [Update] 添加OTP认证功能 2018-04-19 10:49:47 +08:00
老广
53c532a6ad Merge pull request #1213 from jumpserver/dev
更新资产选择
2018-04-19 10:49:01 +08:00
ibuler
035dd16b36 [Bugfix] 修复授权详情中选择用户或资产的bug 2018-04-19 10:34:17 +08:00
BaiJiangjie
f450accbf8 [Merge] with dev 2018-04-18 12:49:17 +08:00
BaiJiangjie
0bbfc7433d [Feature] 支持otp 2018-04-18 12:48:07 +08:00
ibuler
48e8785725 [Update] 修改users otp secret key 2018-04-18 12:46:25 +08:00
ibuler
b90d3306c5 [Bugfix] 去掉gateway name连接 2018-04-17 12:01:00 +08:00
ibuler
7f7d634c38 Merge remote-tracking branch 'github/master' into dev 2018-04-16 17:08:01 +08:00
老广
45b13abed3 Merge pull request #1204 from jumpserver/bugfix_user_assets
[Bugfix] 修复用户看不到我的资产的bug
2018-04-16 17:04:29 +08:00
ibuler
72cd7a3be2 [Bugfix] 修复用户看不到我的资产的bug 2018-04-16 17:01:25 +08:00
ibuler
3ccd54680e [Bugfix] 修改授权详情快速Node更改失效的bug 2018-04-14 12:18:15 +08:00
ibuler
071d14c639 [Update] 修改资产获取select 2018-04-13 21:26:10 +08:00
ibuler
823e879432 [Update] 更新节点移动交互 2018-04-13 15:48:10 +08:00
ibuler
739932b005 [Update] 更新资产导入 2018-04-12 18:50:43 +08:00
ibuler
24f144fdc3 [Bugfix] 修复导入资产时url地址问题 2018-04-12 18:17:42 +08:00
ibuler
967800391e [Update] 更新api 2018-04-12 17:31:37 +08:00
老广
3ccb6637d7 Merge pull request #1197 from jumpserver/dev
[Update] 更新迁移脚本
2018-04-12 17:03:39 +08:00
ibuler
8dfdefd428 [Update] 更新迁移脚本 2018-04-12 17:02:04 +08:00
老广
ab2c58b626 Merge pull request #1194 from jumpserver/dev
[Bugfix] 修复资产重复的bug
2018-04-12 11:08:14 +08:00
ibuler
ee4f5a8194 [Bugfix] 修复资产重复的bug 2018-04-12 11:07:40 +08:00
老广
084a76b215 Merge pull request #1193 from jumpserver/dev
更改表结构
2018-04-12 10:17:53 +08:00
ibuler
2398e9acbd 更改表截稿 2018-04-12 10:16:38 +08:00
老广
5ad8b3cc70 Merge pull request #1191 from jumpserver/dev
[Update] 更改版本号
2018-04-12 09:51:54 +08:00
ibuler
7d14e1f248 [Update] 更改版本号 2018-04-12 09:49:44 +08:00
老广
819f8f469d Merge pull request #1187 from jumpserver/dev
[Bugfix] 修复一个脚本的bug
2018-04-11 17:02:02 +08:00
ibuler
a31b7a8800 [Update] 2018-04-11 16:59:07 +08:00
老广
24bdaecab4 Merge pull request #1185 from jumpserver/dev
授权规则优化,支持细颗粒授权
2018-04-11 15:25:02 +08:00
ibuler
8b3b517bab [Update] 修改授权规则详情列表页面 2018-04-11 15:24:12 +08:00
ibuler
7fc2ef00ee [Update] 修改资产api获取的bug 2018-04-11 12:45:04 +08:00
ibuler
cbd6c3ee69 [Update] 添加迁移脚本 2018-04-11 12:23:35 +08:00
ibuler
3835adafb8 [Update] 修改bug 2018-04-11 12:13:49 +08:00
ibuler
bbaa35c773 [Update] 修改Perms 2018-04-11 11:34:15 +08:00
ibuler
0fa8287811 Merge branch 'dev' into perms 2018-04-11 10:25:18 +08:00
ibuler
78f4e5a89a [Update] 修改用户Opt 2018-04-10 21:04:56 +08:00
ibuler
3193c5549d Merge remote-tracking branch 'github/dev' into dev 2018-04-10 21:02:36 +08:00
ibuler
ed71e7d2d9 [Update] 修改用户Opt 2018-04-10 21:02:07 +08:00
ibuler
33c299566a [Update] 修复bug 2018-04-10 20:45:01 +08:00
ibuler
84634eb8c0 [Update] 修改Permr认证 2018-04-10 20:29:06 +08:00
ibuler
a4ff2181c5 [Update] 修改用户view的api 2018-04-10 09:41:06 +08:00
ibuler
fffa0def9e [Update] 修改api和view 2018-04-08 20:02:40 +08:00
ibuler
d0ede246e7 [Update] 修改授权 2018-04-08 00:16:37 +08:00
BaiJiangJie
b8b78ffeb2 Merge pull request #1162 from BaiJiangJie/dev
创建用户发送邮件
2018-04-06 15:50:50 +08:00
老广
d2d10b59ac Merge pull request #1175 from jumpserver/dev
支持sftp
2018-04-06 13:30:05 +08:00
ibuler
cb8e59edf2 [Update] 修改文案 2018-04-06 13:23:15 +08:00
ibuler
1c0d783eec [update] 添加Migrations 2018-04-06 11:36:47 +08:00
ibuler
4fa72400be Merge remote-tracking branch 'github/dev' into dev 2018-04-06 11:35:33 +08:00
ibuler
37c0062fae [Update] 添加审计模块 2018-04-06 11:27:52 +08:00
BaiJiangjie
8504c3d2fd 修复资产列表导出问题 2018-04-04 19:38:32 +08:00
老广
2d10e13057 Merge pull request #1171 from jumpserver/dev
Dev
2018-04-04 14:05:16 +08:00
ibuler
1d7ba3e204 [Bugfix] 修复小bug 2018-04-04 13:03:36 +08:00
ibuler
d966e22cf9 [Update] 修改小bug 2018-04-04 13:01:57 +08:00
ibuler
6a88fd2d60 [Update] 修改api权限 2018-04-04 08:47:02 +08:00
BaiJiangjie
b63999f385 创建用户发送邮件 2018-04-03 16:28:58 +08:00
Nidhoggur1993
4fd83bd5be Update make_migrations.sh
增加自动合并而不是在弹出warning结束
2018-04-03 16:02:51 +08:00
老广
82d06351e7 Merge pull request #1158 from jumpserver/dev
bugfix for celery log path
2018-04-03 14:50:49 +08:00
ibuler
aa8bece724 Merge branch 'ansible' into dev 2018-04-03 14:49:58 +08:00
老广
241bdff7c8 Merge pull request #1156 from jumpserver/dev
Dev
2018-04-03 12:18:41 +08:00
ibuler
168335a381 [Update] 更新内容 2018-04-03 12:14:58 +08:00
ibuler
121726b731 Merge remote-tracking branch 'github/dev' into dev 2018-04-03 10:44:35 +08:00
ibuler
0b812a03c6 [Bugfix] 修复 ApiUpdateAttr的bug 2018-04-03 10:36:11 +08:00
ibuler
79ae6efafb [Bugfix] 修复没有Log path引起的bug 2018-04-02 19:14:42 +08:00
老广
d2f108eeec Merge pull request #1154 from BaiJiangJie/master
终端管理用户显示,资产导入IP字段空格,资产导入domain字段保存
2018-04-02 19:03:51 +08:00
BaiJiangJie
c48beb10af Merge pull request #1 from BaiJiangJie/dev
终端管理用户,资产列表IP字段,domain保存对象
2018-04-02 18:52:52 +08:00
老广
ea9264ec49 Merge pull request #1153 from BaiJiangJie/dev
修复导入资产列表IP字段前后有空格问题,导入资产列表时domain保存为对象
2018-04-02 18:43:33 +08:00
BaiJiangjie
5b65ed8a19 修复导入资产列表IP字段前后有空格问题,导入资产列表时doamin保存为对象 2018-04-02 18:32:24 +08:00
ibuler
951ac252fe [Merge] 更新ansible功能 2018-04-02 17:45:17 +08:00
ibuler
24c4e1df50 [Update] 资产,系统用户,管理用户等支持查看日志 2018-04-02 16:55:39 +08:00
ibuler
d247e49b70 [Update] 修改celery位置 2018-04-02 15:54:49 +08:00
ibuler
a4c843ff13 [Update] 迁移celery到ops 2018-04-02 13:19:31 +08:00
BaiJiangjie
ec6103448e 使终端管理的应用用户不显示在用户组里 2018-04-02 12:02:06 +08:00
老广
0ca14463cd Merge pull request #1147 from jumpserver/dev
支持网域功能
2018-04-02 10:59:55 +08:00
ibuler
df80e8047a [Update] 修改日志 2018-04-01 23:45:37 +08:00
ibuler
09fc2776df [Update] Support history view 2018-03-30 22:03:43 +08:00
ibuler
e4c2affb5f Merge branch 'rev' into dev 2018-03-29 15:07:49 +08:00
ibuler
6fae4d5dee [Bugfix] for some commit 2018-03-29 15:06:35 +08:00
ibuler
0c80e3e815 [rev] for api commit 2018-03-29 15:02:09 +08:00
ibuler
d32f070b5c [Update] 修改Inverntoy,增加更多属性 2018-03-28 19:37:29 +08:00
ibuler
b959f1f68b Merge branch 'bugfix_ip_lookup' into dev 2018-03-28 16:47:44 +08:00
ibuler
e1cab35db0 [Bugfix] 修改获取城市 2018-03-28 16:47:36 +08:00
ibuler
8014cc48b6 [Update] 修改settings和缩进 2018-03-28 15:47:20 +08:00
ibuler
829f57e2d7 [Bugfix] 修复小bug 2018-03-28 14:35:27 +08:00
ibuler
5f0b4a4b63 [Update] 修改一些翻译 2018-03-28 11:28:40 +08:00
ibuler
c5af4d47eb Merge branch 'some_auth_api' into dev 2018-03-27 18:37:04 +08:00
ibuler
c37bfb682a [Update] 添加设置认证api和创建用户时可以不选择组 2018-03-27 18:34:41 +08:00
ibuler
3aaea6cc31 Merge branch 'bugfix_useradd' into dev 2018-03-27 17:48:35 +08:00
ibuler
f85e5b6f75 [Bugfix] 修改添加用户的bug 2018-03-27 17:47:53 +08:00
ibuler
d598571dc1 [Update] 添加一个log 2018-03-27 16:51:27 +08:00
ibuler
e873be95d5 [Update] 导入到当前node 2018-03-26 16:16:18 +08:00
ibuler
dbaa4ab502 [Update] 修改资产导入的事务问题 2018-03-26 15:55:31 +08:00
ibuler
ac1e319cd9 [Merge] merge with vpc 2018-03-26 09:56:25 +08:00
ibuler
a39424ac09 [Update] 更新jms脚本 2018-03-25 21:48:41 +08:00
ibuler
75319b99ae [Update] 更新ap请求 2018-03-25 21:47:29 +08:00
ibuler
7f4f67aa8d [Update] 支持网域 2018-03-23 19:46:46 +08:00
ibuler
fe1862120f [Update] 删掉集群等等 2018-03-21 18:13:16 +08:00
ibuler
759760e7d9 [Update] 服务器可以生成用户密钥 2018-03-21 15:22:10 +08:00
老广
15b74da57c Merge pull request #1100 from jumpserver/dev
Dev
2018-03-21 11:59:05 +08:00
ibuler
6f29cf5ddd [Update] 修改quickstart 2018-03-21 09:26:14 +08:00
ibuler
0bba840e4d Merge branch 'pubkey' into dev 2018-03-19 16:24:59 +08:00
ibuler
2156e0f51a [Update] 修改jms 2018-03-19 16:24:50 +08:00
ibuler
2fc9c04228 [Bugfix] 修复更新系统用户后关联节点丢失的问题 2018-03-19 15:32:02 +08:00
ibuler
d8e614c54d [Update] 管理脚本 2018-03-19 11:26:51 +08:00
ibuler
2ab26e25cc [Update] 改为supervisor启动 2018-03-16 10:43:21 +08:00
ibuler
f195b309d4 [Bugfix] 全选后编辑checkbox问题 2018-03-16 10:42:53 +08:00
ibuler
5e41c5cadc [Update] 删掉没用的脚本 2018-03-15 18:14:43 +08:00
ibuler
d92d09bd80 [Update] 更新说明 2018-03-15 09:34:19 +08:00
liuzheng
227804b7ab Update step_by_step.rst 2018-03-15 07:33:56 +08:00
liuzheng
eeae989c06 Update step_by_step.rst 2018-03-15 07:30:32 +08:00
liuzheng
c67a9eb845 Update quickstart.rst 2018-03-15 07:27:30 +08:00
chnliyong
6035d1f130 python-gssapi libkrb5-dev for gssapi/gssapi.h 2018-03-10 10:46:28 +08:00
779 changed files with 119284 additions and 11100 deletions

View File

@@ -1,7 +1,8 @@
[简述你的问题]
##### 使用版本
[请提供你使用的Jumpserver版本 0.3.2 或 0.5.0]
[请提供你使用的Jumpserver版本 1.x.x 注: 0.3.x不再提供支持]
##### 问题复现步骤
1. [步骤1]

4
.gitignore vendored
View File

@@ -17,7 +17,7 @@ dump.rdb
.idea/
db.sqlite3
config.py
migrations/
config.yml
*.log
host_rsa_key
*.bat
@@ -32,3 +32,5 @@ django.db
celerybeat-schedule.db
data/static
docs/_build/
xpack
logs/*

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
FROM registry.fit2cloud.com/public/python:v3
MAINTAINER Jumpserver Team <ibuler@qq.com>
WORKDIR /opt/jumpserver
RUN useradd jumpserver
COPY ./requirements /tmp/requirements
RUN yum -y install epel-release && \
echo -e "[mysql]\nname=mysql\nbaseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo
RUN cd /tmp/requirements && yum -y install $(cat rpm_requirements.txt)
RUN cd /tmp/requirements && pip install --upgrade pip setuptools && \
pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt || pip install -r requirements.txt
RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config
COPY . /opt/jumpserver
RUN echo > config.yml
VOLUME /opt/jumpserver/data
VOLUME /opt/jumpserver/logs
ENV LANG=zh_CN.UTF-8
ENV LC_ALL=zh_CN.UTF-8
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]

211
README.md
View File

@@ -1,58 +1,213 @@
## Jumpserver
## Jumpserver 多云环境下更好用的堡垒机
[![Python3](https://img.shields.io/badge/python-3.6-green.svg?style=plastic)](https://www.python.org/)
[![Django](https://img.shields.io/badge/django-1.11-brightgreen.svg?style=plastic)](https://www.djangoproject.com/)
[![Ansible](https://img.shields.io/badge/ansible-2.2.2.0-blue.svg?style=plastic)](https://www.ansible.com/)
[![Paramiko](https://img.shields.io/badge/paramiko-2.1.2-green.svg?style=plastic)](http://www.paramiko.org/)
[![Django](https://img.shields.io/badge/django-2.1-brightgreen.svg?style=plastic)](https://www.djangoproject.com/)
[![Ansible](https://img.shields.io/badge/ansible-2.4.2.0-blue.svg?style=plastic)](https://www.ansible.com/)
[![Paramiko](https://img.shields.io/badge/paramiko-2.4.1-green.svg?style=plastic)](http://www.paramiko.org/)
----
Jumpserver是全球首款完全开源的堡垒机使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
Jumpserver 是全球首款完全开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 的专业运维审计系统。
Jumpserver使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
Jumpserver 使用 Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
Jumpserver采纳分布式架构支持多机房跨区域部署中心节点提供 API各机房部署登录节点可横向扩展、无并发限制。
Jumpserver 采纳分布式架构,支持多机房跨区域部署,中心节点提供 API各机房部署登录节点可横向扩展、无并发限制。
改变世界,从一点点开始。
----
- [English Version](https://github.com/jumpserver/jumpserver/blob/master/README_EN.md)
### 功能
- 统一认证
- 资产管理
- 统一授权
- 审计
- 支持LDAP认证
- Web terminal
- SSH Server
- 支持Windows RDP
----
<table class="subscription-level-table">
<tr class="subscription-level-tr-border">
<th style="background-color: #1ab394;color: #ffffff;" colspan="3">Jumpserver提供的堡垒机必备功能</th>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-first-td-background-style" rowspan="4">身份验证 Authentication</td>
<td class="features-second-td-background-style" rowspan="3" >登录认证
</td>
<td class="features-third-td-background-style">资源统一登录和认证
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">LDAP认证
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">支持OpenID实现单点登录
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">多因子认证
</td>
<td class="features-third-td-background-style">MFAGoogle Authenticator
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-first-td-background-style" rowspan="9">账号管理 Account</td>
<td class="features-second-td-background-style" rowspan="2">集中账号管理
</td>
<td class="features-third-td-background-style">管理用户管理
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">系统用户管理
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style" rowspan="4">统一密码管理
</td>
<td class="features-third-td-background-style">资产密码托管
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">自动生成密码
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">密码自动推送
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">密码过期设置
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-outline-td-background-style" rowspan="2">批量密码变更(X-PACK)
</td>
<td class="features-outline-td-background-style">定期批量修改密码
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-outline-td-background-style">生成随机密码
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-outline-td-background-style">多云环境的资产纳管(X-PACK)
</td>
<td class="features-outline-td-background-style">对私有云、公有云资产统一纳管
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-first-td-background-style" rowspan="9">授权控制 Authorization</td>
<td class="features-second-td-background-style" rowspan="3">资产授权管理
</td>
<td class="features-third-td-background-style">资产树
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">资产或资产组灵活授权
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">节点内资产自动继承授权
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-outline-td-background-style">RemoteApp(X-PACK)
</td>
<td class="features-outline-td-background-style">实现更细粒度的应用级授权
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-outline-td-background-style">组织管理(X-PACK)
</td>
<td class="features-outline-td-background-style">实现多租户管理,权限隔离
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">多维度授权
</td>
<td class="features-third-td-background-style">可对用户、用户组或系统角色授权
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">指令限制
</td>
<td class="features-third-td-background-style">限制特权指令使用,支持黑白名单
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">统一文件传输
</td>
<td class="features-third-td-background-style">SFTP 文件上传/下载
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">文件管理
</td>
<td class="features-third-td-background-style">Web SFTP 文件管理
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-first-td-background-style" rowspan="6">安全审计 Audit</td>
<td class="features-second-td-background-style" rowspan="2">会话管理
</td>
<td class="features-third-td-background-style">在线会话管理
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">历史会话管理
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style" rowspan="2">录像管理
</td>
<td class="features-third-td-background-style">Linux 录像支持
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-third-td-background-style">Windows 录像支持
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">指令审计
</td>
<td class="features-third-td-background-style">指令记录
</td>
</tr>
<tr class="subscription-level-tr-border">
<td class="features-second-td-background-style">文件传输审计
</td>
<td class="features-third-td-background-style">上传/下载记录审计
</td>
</tr>
</table>
### 开始使用
----
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/latest/quickstart.html)
- 快速开始文档 [Docker 安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/latest/step_by_step.html)
- Step by Step 安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org)
- 也可以查看我们完整文档 [文档](http://docs.jumpserver.org)
### Demo 和 截图
### Demo、视频 和 截图
----
我们提供了DEMO和截图可以让你快速了解Jumpserver
我们提供了 Demo 、演示视频和截图可以让你快速了解 Jumpserver
[DEMO](http://demo.jumpserver.org)
[截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
- [Demo](https://demo.jumpserver.org/auth/login/?next=/)
- [视频](https://fit2cloud2-offline-installer.oss-cn-beijing.aliyuncs.com/tools/Jumpserver%20%E4%BB%8B%E7%BB%8Dv1.4.mp4)
- [截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
### SDK
### SDK
----
我们还编写了一些SDK供你其它系统快速和Jumpserver APi交互
我们还编写了一些SDK供你其它系统快速和 Jumpserver API 交互
- [python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
- [java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
- [Python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
- [Java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
### License & Copyright
Copyright (c) 2014-2018 Beijing Duizhan Tech, Inc., All rights reserved.
Copyright (c) 2014-2019 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

58
README_EN.md Normal file
View File

@@ -0,0 +1,58 @@
## Jumpserver
[![Python3](https://img.shields.io/badge/python-3.6-green.svg?style=plastic)](https://www.python.org/)
[![Django](https://img.shields.io/badge/django-2.1-brightgreen.svg?style=plastic)](https://www.djangoproject.com/)
[![Ansible](https://img.shields.io/badge/ansible-2.4.2.0-blue.svg?style=plastic)](https://www.ansible.com/)
[![Paramiko](https://img.shields.io/badge/paramiko-2.4.1-green.svg?style=plastic)](http://www.paramiko.org/)
----
- [中文版](https://github.com/jumpserver/jumpserver/blob/master/README_EN.md)
Jumpserver is the first fully open source bastion in the world, based on the GNU GPL v2.0 open source protocol. Jumpserver is a professional operation and maintenance audit system conforms to 4A specifications.
Jumpserver is developed using Python / Django, conforms to the Web 2.0 specification, and is equipped with the industry-leading Web Terminal solution which have beautiful interface and great user experience.
Jumpserver adopts a distributed architecture to support multi-branch deployment across multiple areas. The central node provides APIs, and login nodes are deployed in each branch. It can be scaled horizontally without concurrency restrictions.
Change the world, starting from little things.
----
### Features
![Jumpserver 功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver148.jpeg "Jumpserver 功能")
### Start
Quick start [Docker Install](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
Step by Step deployment. [Docs](http://docs.jumpserver.org/zh/docs/step_by_step.html)
Full documentation [Docs](http://docs.jumpserver.org)
### Demo、Video 和 Snapshot
We provide online demo, demo video and screenshots to get you started quickly.
[Demo](https://demo.jumpserver.org/auth/login/?next=/)
[Video](https://fit2cloud2-offline-installer.oss-cn-beijing.aliyuncs.com/tools/Jumpserver%20%E4%BB%8B%E7%BB%8Dv1.4.mp4)
[Snapshot](http://docs.jumpserver.org/zh/docs/snapshot.html)
### SDK
We provide the SDK for your other systems to quickly interact with the Jumpserver API.
- [Python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver other components use this SDK to complete the interaction.
- [Java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK thanks to 恺珺 for provide Java SDK
### License & Copyright
Copyright (c) 2014-2019 Beijing Duizhan Tech, Inc., All rights reserved.
Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-2.0.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
__version__ = "0.5.0"
#

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1 @@
from .remote_app import *

View File

@@ -0,0 +1,31 @@
# coding: utf-8
#
from rest_framework import generics
from rest_framework.pagination import LimitOffsetPagination
from rest_framework_bulk import BulkModelViewSet
from ..hands import IsOrgAdmin, IsAppUser
from ..models import RemoteApp
from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer
__all__ = [
'RemoteAppViewSet', 'RemoteAppConnectionInfoApi',
]
class RemoteAppViewSet(BulkModelViewSet):
filter_fields = ('name',)
search_fields = filter_fields
permission_classes = (IsOrgAdmin,)
queryset = RemoteApp.objects.all()
serializer_class = RemoteAppSerializer
pagination_class = LimitOffsetPagination
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
queryset = RemoteApp.objects.all()
permission_classes = (IsAppUser, )
serializer_class = RemoteAppConnectionInfoSerializer

View File

@@ -0,0 +1,7 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class ApplicationsConfig(AppConfig):
name = 'applications'

View File

@@ -0,0 +1,68 @@
# coding: utf-8
#
from django.utils.translation import ugettext_lazy as _
# RemoteApp
REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor'
REMOTE_APP_TYPE_CHROME = 'chrome'
REMOTE_APP_TYPE_MYSQL_WORKBENCH = 'mysql_workbench'
REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client'
REMOTE_APP_TYPE_CUSTOM = 'custom'
REMOTE_APP_TYPE_CHOICES = (
(
_('Browser'),
(
(REMOTE_APP_TYPE_CHROME, 'Chrome'),
)
),
(
_('Database tools'),
(
(REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'),
)
),
(
_('Virtualization tools'),
(
(REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'),
)
),
(REMOTE_APP_TYPE_CUSTOM, _('Custom')),
)
# Fields attribute write_only default => False
REMOTE_APP_TYPE_CHROME_FIELDS = [
{'name': 'chrome_target'},
{'name': 'chrome_username'},
{'name': 'chrome_password', 'write_only': True}
]
REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS = [
{'name': 'mysql_workbench_ip'},
{'name': 'mysql_workbench_name'},
{'name': 'mysql_workbench_username'},
{'name': 'mysql_workbench_password', 'write_only': True}
]
REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS = [
{'name': 'vmware_target'},
{'name': 'vmware_username'},
{'name': 'vmware_password', 'write_only': True}
]
REMOTE_APP_TYPE_CUSTOM_FIELDS = [
{'name': 'custom_cmdline'},
{'name': 'custom_target'},
{'name': 'custom_username'},
{'name': 'custom_password', 'write_only': True}
]
REMOTE_APP_TYPE_MAP_FIELDS = {
REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS,
REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS,
REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS,
REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS
}

View File

@@ -0,0 +1 @@
from .remote_app import *

View File

@@ -0,0 +1,132 @@
# coding: utf-8
#
from django.utils.translation import ugettext as _
from django import forms
from orgs.mixins import OrgModelForm
from assets.models import Asset, SystemUser
from ..models import RemoteApp
from .. import const
__all__ = [
'RemoteAppCreateUpdateForm',
]
class RemoteAppTypeChromeForm(forms.ModelForm):
chrome_target = forms.CharField(
max_length=128, label=_('Target URL'), required=False
)
chrome_username = forms.CharField(
max_length=128, label=_('Login username'), required=False
)
chrome_password = forms.CharField(
widget=forms.PasswordInput, strip=True,
max_length=128, label=_('Login password'), required=False
)
class RemoteAppTypeMySQLWorkbenchForm(forms.ModelForm):
mysql_workbench_ip = forms.CharField(
max_length=128, label=_('Database IP'), required=False
)
mysql_workbench_name = forms.CharField(
max_length=128, label=_('Database name'), required=False
)
mysql_workbench_username = forms.CharField(
max_length=128, label=_('Database username'), required=False
)
mysql_workbench_password = forms.CharField(
widget=forms.PasswordInput, strip=True,
max_length=128, label=_('Database password'), required=False
)
class RemoteAppTypeVMwareForm(forms.ModelForm):
vmware_target = forms.CharField(
max_length=128, label=_('Target address'), required=False
)
vmware_username = forms.CharField(
max_length=128, label=_('Login username'), required=False
)
vmware_password = forms.CharField(
widget=forms.PasswordInput, strip=True,
max_length=128, label=_('Login password'), required=False
)
class RemoteAppTypeCustomForm(forms.ModelForm):
custom_cmdline = forms.CharField(
max_length=128, label=_('Operating parameter'), required=False
)
custom_target = forms.CharField(
max_length=128, label=_('Target address'), required=False
)
custom_username = forms.CharField(
max_length=128, label=_('Login username'), required=False
)
custom_password = forms.CharField(
widget=forms.PasswordInput, strip=True,
max_length=128, label=_('Login password'), required=False
)
class RemoteAppTypeForms(
RemoteAppTypeChromeForm,
RemoteAppTypeMySQLWorkbenchForm,
RemoteAppTypeVMwareForm,
RemoteAppTypeCustomForm
):
pass
class RemoteAppCreateUpdateForm(RemoteAppTypeForms, OrgModelForm):
def __init__(self, *args, **kwargs):
# 过滤RDP资产和系统用户
super().__init__(*args, **kwargs)
field_asset = self.fields['asset']
field_asset.queryset = field_asset.queryset.filter(
protocol=Asset.PROTOCOL_RDP
)
field_system_user = self.fields['system_user']
field_system_user.queryset = field_system_user.queryset.filter(
protocol=SystemUser.PROTOCOL_RDP
)
class Meta:
model = RemoteApp
fields = [
'name', 'asset', 'system_user', 'type', 'path', 'comment'
]
widgets = {
'asset': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Asset')
}),
'system_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('System user')
})
}
def _clean_params(self):
app_type = self.data.get('type')
fields = const.REMOTE_APP_TYPE_MAP_FIELDS.get(app_type, [])
params = {}
for field in fields:
name = field['name']
value = self.cleaned_data[name]
params.update({name: value})
return params
def _save_params(self, instance):
params = self._clean_params()
instance.params = params
instance.save()
return instance
def save(self, commit=True):
instance = super().save(commit=commit)
instance = self._save_params(instance)
return instance

View File

@@ -0,0 +1,16 @@
"""
jumpserver.__app__.hands.py
~~~~~~~~~~~~~~~~~
This app depends other apps api, function .. should be import or write mack here.
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2018 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""
from common.permissions import AdminUserRequiredMixin
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
from users.models import User, UserGroup

View File

@@ -0,0 +1,42 @@
# Generated by Django 2.1.7 on 2019-05-20 11:04
import common.fields.model
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('assets', '0026_auto_20190325_2035'),
]
operations = [
migrations.CreateModel(
name='RemoteApp',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('Browser', (('chrome', 'Chrome'),)), ('Database tools', (('mysql_workbench', 'MySQL Workbench'),)), ('Virtualization tools', (('vmware_client', 'vSphere Client'),)), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type')),
('path', models.CharField(max_length=128, verbose_name='App path')),
('params', common.fields.model.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
('system_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.SystemUser', verbose_name='System user')),
],
options={
'verbose_name': 'RemoteApp',
'ordering': ('name',),
},
),
migrations.AlterUniqueTogether(
name='remoteapp',
unique_together={('org_id', 'name')},
),
]

View File

View File

@@ -0,0 +1 @@
from .remote_app import *

View File

@@ -0,0 +1,89 @@
# coding: utf-8
#
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelMixin
from common.fields.model import EncryptJsonDictTextField
from .. import const
__all__ = [
'RemoteApp',
]
class RemoteApp(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
asset = models.ForeignKey(
'assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')
)
system_user = models.ForeignKey(
'assets.SystemUser', on_delete=models.CASCADE,
verbose_name=_('System user')
)
type = models.CharField(
default=const.REMOTE_APP_TYPE_CHROME,
choices=const.REMOTE_APP_TYPE_CHOICES,
max_length=128, verbose_name=_('App type')
)
path = models.CharField(
max_length=128, blank=False, null=False,
verbose_name=_('App path')
)
params = EncryptJsonDictTextField(
max_length=4096, default={}, blank=True, null=True,
verbose_name=_('Parameters')
)
created_by = models.CharField(
max_length=32, null=True, blank=True, verbose_name=_('Created by')
)
date_created = models.DateTimeField(
auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')
)
comment = models.TextField(
max_length=128, default='', blank=True, verbose_name=_('Comment')
)
class Meta:
verbose_name = _("RemoteApp")
unique_together = [('org_id', 'name')]
ordering = ('name', )
def __str__(self):
return self.name
@property
def parameters(self):
"""
返回Guacamole需要的RemoteApp配置参数信息中的parameters参数
"""
_parameters = list()
_parameters.append(self.type)
path = '\"%s\"' % self.path
_parameters.append(path)
for field in const.REMOTE_APP_TYPE_MAP_FIELDS[self.type]:
value = self.params.get(field['name'])
if value is None:
continue
_parameters.append(value)
_parameters = ' '.join(_parameters)
return _parameters
@property
def asset_info(self):
return {
'id': self.asset.id,
'hostname': self.asset.hostname
}
@property
def system_user_info(self):
return {
'id': self.system_user.id,
'name': self.system_user.name
}

View File

@@ -0,0 +1 @@
from .remote_app import *

View File

@@ -0,0 +1,103 @@
# coding: utf-8
#
from rest_framework import serializers
from common.mixins import BulkSerializerMixin
from common.serializers import AdaptedBulkListSerializer
from .. import const
from ..models import RemoteApp
__all__ = [
'RemoteAppSerializer', 'RemoteAppConnectionInfoSerializer',
]
class RemoteAppParamsDictField(serializers.DictField):
"""
RemoteApp field => params
"""
@staticmethod
def filter_attribute(attribute, instance):
"""
过滤掉params字段值中write_only特性的key-value值
For example, the chrome_password field is not returned when serializing
{
'chrome_target': 'http://www.jumpserver.org/',
'chrome_username': 'admin',
'chrome_password': 'admin',
}
"""
for field in const.REMOTE_APP_TYPE_MAP_FIELDS[instance.type]:
if field.get('write_only', False):
attribute.pop(field['name'], None)
return attribute
def get_attribute(self, instance):
"""
序列化时调用
"""
attribute = super().get_attribute(instance)
attribute = self.filter_attribute(attribute, instance)
return attribute
@staticmethod
def filter_value(dictionary, value):
"""
过滤掉不属于当前app_type所包含的key-value值
"""
app_type = dictionary.get('type', const.REMOTE_APP_TYPE_CHROME)
fields = const.REMOTE_APP_TYPE_MAP_FIELDS[app_type]
fields_names = [field['name'] for field in fields]
no_need_keys = [k for k in value.keys() if k not in fields_names]
for k in no_need_keys:
value.pop(k)
return value
def get_value(self, dictionary):
"""
反序列化时调用
"""
value = super().get_value(dictionary)
value = self.filter_value(dictionary, value)
return value
class RemoteAppSerializer(BulkSerializerMixin, serializers.ModelSerializer):
params = RemoteAppParamsDictField()
class Meta:
model = RemoteApp
list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'name', 'asset', 'system_user', 'type', 'path', 'params',
'comment', 'created_by', 'date_created', 'asset_info',
'system_user_info', 'get_type_display',
]
read_only_fields = [
'created_by', 'date_created', 'asset_info',
'system_user_info', 'get_type_display'
]
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
parameter_remote_app = serializers.SerializerMethodField()
class Meta:
model = RemoteApp
fields = [
'id', 'name', 'asset', 'system_user', 'parameter_remote_app',
]
read_only_fields = ['parameter_remote_app']
@staticmethod
def get_parameter_remote_app(obj):
parameter = {
'program': const.REMOTE_APP_BOOT_PROGRAM_NAME,
'working_directory': '',
'parameters': obj.parameters,
}
return parameter

View File

@@ -0,0 +1,123 @@
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="appForm" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.asset layout="horizontal" %}
{% bootstrap_field form.system_user layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %}
{% bootstrap_field form.path layout="horizontal" %}
<div class="hr-line-dashed"></div>
{# chrome #}
<div class="chrome-fields">
{% bootstrap_field form.chrome_target layout="horizontal" %}
{% bootstrap_field form.chrome_username layout="horizontal" %}
{% bootstrap_field form.chrome_password layout="horizontal" %}
</div>
{# mysql workbench #}
<div class="mysql_workbench-fields">
{% bootstrap_field form.mysql_workbench_ip layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_name layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_username layout="horizontal" %}
{% bootstrap_field form.mysql_workbench_password layout="horizontal" %}
</div>
{# vmware #}
<div class="vmware_client-fields">
{% bootstrap_field form.vmware_target layout="horizontal" %}
{% bootstrap_field form.vmware_username layout="horizontal" %}
{% bootstrap_field form.vmware_password layout="horizontal" %}
</div>
{# custom #}
<div class="custom-fields">
{% bootstrap_field form.custom_cmdline layout="horizontal" %}
{% bootstrap_field form.custom_target layout="horizontal" %}
{% bootstrap_field form.custom_username layout="horizontal" %}
{% bootstrap_field form.custom_password layout="horizontal" %}
</div>
{% bootstrap_field form.comment layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
var app_type_id = '#' + '{{ form.type.id_for_label }}';
var app_path_id = '#' + '{{ form.path.id_for_label }}';
var all_type_fields = [
'.chrome-fields',
'.mysql_workbench-fields',
'.vmware_client-fields',
'.custom-fields'
];
var app_type_map_default_fields_value = {
'chrome': {
'app_path': 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
},
'mysql_workbench': {
'app_path': 'C:\\Program Files\\MySQL\\MySQL Workbench 8.0 CE\\MySQLWorkbench.exe'
},
'vmware_client': {
'app_path': 'C:\\Program Files (x86)\\VMware\\Infrastructure\\Virtual Infrastructure Client\\Launcher\\VpxClient.exe'
},
'custom': {'app_path': ''}
};
function getAppType(){
return $(app_type_id+ " option:selected").val();
}
function initialDefaultValue(){
var app_type = getAppType();
var app_path = $(app_path_id).val();
if(app_path){
app_type_map_default_fields_value[app_type]['app_path'] = app_path
}
}
function setDefaultValue(){
// 设置类型相关字段的默认值
var app_type = getAppType();
var app_path = app_type_map_default_fields_value[app_type]['app_path'];
$(app_path_id).val(app_path)
}
function hiddenFields(){
var app_type = getAppType();
$.each(all_type_fields, function(index, value){
$(value).addClass('hidden')
});
$('.' + app_type + '-fields').removeClass('hidden');
}
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: true
});
initialDefaultValue();
hiddenFields();
setDefaultValue();
})
.on('change', app_type_id, function(){
hiddenFields();
setDefaultValue();
});
</script>
{% endblock %}

View File

@@ -0,0 +1,109 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'applications:remote-app-detail' pk=remote_app.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'applications:remote-app-update' pk=remote_app.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-application">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-8" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ remote_app.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td>{% trans 'Name' %}:</td>
<td><b>{{ remote_app.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Asset' %}:</td>
<td><b><a href="{% url 'assets:asset-detail' pk=remote_app.asset.id %}">{{ remote_app.asset.hostname }}</a></b></td>
</tr>
<tr>
<td>{% trans 'System user' %}:</td>
<td><b><a href="{% url 'assets:system-user-detail' pk=remote_app.system_user.id %}">{{ remote_app.system_user.name }}</a></b></td>
</tr>
<tr>
<td>{% trans 'App type' %}:</td>
<td><b>{{ remote_app.get_type_display }}</b></td>
</tr>
<tr>
<td>{% trans 'App path' %}:</td>
<td><b>{{ remote_app.path }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ remote_app.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Created by' %}:</td>
<td><b>{{ remote_app.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ remote_app.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.nodes_selected = {};
$(document).ready(function () {
})
.on('click', '.btn-delete-application', function () {
var $this = $(this);
var name = "{{ remote_app.name }}";
var rid = "{{ remote_app.id }}";
var the_url = '{% url "api-applications:remote-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
var redirect_url = "{% url 'applications:remote-app-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}

View File

@@ -0,0 +1,90 @@
{% extends '_base_list.html' %}
{% load i18n static %}
{% block help_message %}
<div class="alert alert-info help-message">
{% trans 'Before using this feature, make sure that the application loader has been uploaded to the application server and successfully published as a RemoteApp application' %}
<b><a href="https://github.com/jumpserver/Jmservisor/releases" target="view_window" >{% trans 'Download application loader' %}</a></b>
</div>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'applications:remote-app-create' %}" class="btn btn-sm btn-primary"> {% trans "Create RemoteApp" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'App type' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#remote_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
cellData = htmlEscape(cellData);
{% url 'applications:remote-app-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var hostname = htmlEscape(cellData.hostname);
var detail_btn = '<a href="{% url 'assets:asset-detail' pk=DEFAULT_PK %}">' + hostname + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', cellData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData.name);
var detail_btn = '<a href="{% url 'assets:system-user-detail' pk=DEFAULT_PK %}">' + name + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', cellData.id));
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "applications:remote-app-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-rid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-applications:remote-app-list" %}',
columns: [
{data: "id"},
{data: "name" },
{data: "get_type_display", orderable: false},
{data: "asset_info", orderable: false},
{data: "system_user_info", orderable: false},
{data: "comment"},
{data: "id", orderable: false}
],
op_html: $('#actions').html()
};
jumpserver.initServerSideDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn-delete', function () {
var $this = $(this);
var $data_table = $('#remote_app_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var rid = $this.data('rid');
var the_url = '{% url "api-applications:remote-app-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', rid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}

View File

@@ -0,0 +1,79 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="mail-box-header">
<table class="table table-striped table-bordered table-hover " id="remote_app_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'App type' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var inited = false;
var remote_app_table, url;
function initTable() {
if (inited){
return
} else {
inited = true;
}
url = '{% url "api-perms:my-remote-apps" %}';
var options = {
ele: $('#remote_app_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData);
$(td).html(name)
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var hostname = htmlEscape(cellData.hostname);
$(td).html(hostname);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(cellData.name);
$(td).html(name);
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(conn_btn)
}}
],
ajax_url: url,
columns: [
{data: "id"},
{data: "name"},
{data: "get_type_display", orderable: false},
{data: "asset_info", orderable: false},
{data: "system_user_info", orderable: false},
{data: "comment", orderable: false},
{data: "id", orderable: false}
]
};
remote_app_table = jumpserver.initServerSideDataTable(options);
return remote_app_table
}
$(document).ready(function(){
initTable();
})
</script>
{% endblock %}

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,7 @@
# coding: utf-8
#
__all__ = [
]

View File

@@ -0,0 +1,20 @@
# coding:utf-8
#
from django.urls import path
from rest_framework_bulk.routes import BulkRouter
from .. import api
app_name = 'applications'
router = BulkRouter()
router.register(r'remote-app', api.RemoteAppViewSet, 'remote-app')
urlpatterns = [
path('remote-apps/<uuid:pk>/connection-info/',
api.RemoteAppConnectionInfoApi.as_view(),
name='remote-app-connection-info')
]
urlpatterns += router.urls

View File

@@ -0,0 +1,16 @@
# coding:utf-8
from django.urls import path
from .. import views
app_name = 'applications'
urlpatterns = [
# RemoteApp
path('remote-app/', views.RemoteAppListView.as_view(), name='remote-app-list'),
path('remote-app/create/', views.RemoteAppCreateView.as_view(), name='remote-app-create'),
path('remote-app/<uuid:pk>/update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'),
path('remote-app/<uuid:pk>/', views.RemoteAppDetailView.as_view(), name='remote-app-detail'),
# User RemoteApp view
path('user-remote-app/', views.UserRemoteAppListView.as_view(), name='user-remote-app-list')
]

View File

@@ -0,0 +1 @@
from .remote_app import *

View File

@@ -0,0 +1,99 @@
# coding: utf-8
#
from django.utils.translation import ugettext as _
from django.views.generic import TemplateView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from common.permissions import AdminUserRequiredMixin
from common.const import create_success_msg, update_success_msg
from ..models import RemoteApp
from .. import forms
__all__ = [
'RemoteAppListView', 'RemoteAppCreateView', 'RemoteAppUpdateView',
'RemoteAppDetailView', 'UserRemoteAppListView',
]
class RemoteAppListView(AdminUserRequiredMixin, TemplateView):
template_name = 'applications/remote_app_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('RemoteApp list'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class RemoteAppCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
template_name = 'applications/remote_app_create_update.html'
model = RemoteApp
form_class = forms.RemoteAppCreateUpdateForm
success_url = reverse_lazy('applications:remote-app-list')
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create RemoteApp'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return create_success_msg % ({'name': cleaned_data['name']})
class RemoteAppUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
template_name = 'applications/remote_app_create_update.html'
model = RemoteApp
form_class = forms.RemoteAppCreateUpdateForm
success_url = reverse_lazy('applications:remote-app-list')
def get_initial(self):
return {k: v for k, v in self.object.params.items()}
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Update RemoteApp'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return update_success_msg % ({'name': cleaned_data['name']})
class RemoteAppDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'applications/remote_app_detail.html'
model = RemoteApp
context_object_name = 'remote_app'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('RemoteApp detail'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class UserRemoteAppListView(LoginRequiredMixin, TemplateView):
template_name = 'applications/user_remote_app_list.html'
def get_context_data(self, **kwargs):
context = {
'action': _('My RemoteApp'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)

View File

@@ -3,3 +3,6 @@ from .asset import *
from .label import *
from .system_user import *
from .node import *
from .domain import *
from .cmd_filter import *
from .asset_user import *

View File

@@ -14,37 +14,55 @@
# limitations under the License.
from django.db import transaction
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination
from common.mixins import IDInFilterMixin
from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger
from ..hands import IsSuperUser
from ..hands import IsOrgAdmin
from ..models import AdminUser, Asset
from .. import serializers
from ..tasks import test_admin_user_connectability_manual
from ..tasks import test_admin_user_connectivity_manual
logger = get_logger(__file__)
__all__ = [
'AdminUserViewSet', 'ReplaceNodesAdminUserApi', 'AdminUserTestConnectiveApi'
'AdminUserViewSet', 'ReplaceNodesAdminUserApi',
'AdminUserTestConnectiveApi', 'AdminUserAuthApi',
'AdminUserAssetsListView',
]
class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
class AdminUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
"""
Admin user api set, for add,delete,update,list,retrieve resource
"""
filter_fields = ("name", "username")
search_fields = filter_fields
queryset = AdminUser.objects.all()
serializer_class = serializers.AdminUserSerializer
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
pagination_class = LimitOffsetPagination
def get_queryset(self):
queryset = super().get_queryset().all()
return queryset
class AdminUserAuthApi(generics.UpdateAPIView):
queryset = AdminUser.objects.all()
serializer_class = serializers.AdminUserAuthSerializer
permission_classes = (IsOrgAdmin,)
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
queryset = AdminUser.objects.all()
serializer_class = serializers.ReplaceNodeAdminUserSerializer
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
def update(self, request, *args, **kwargs):
admin_user = self.get_object()
@@ -65,12 +83,30 @@ class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
"""
Test asset admin user connectivity
Test asset admin user assets_connectivity
"""
queryset = AdminUser.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.TaskIDSerializer
def retrieve(self, request, *args, **kwargs):
admin_user = self.get_object()
test_admin_user_connectability_manual.delay(admin_user)
return Response({"msg": "Task created"})
task = test_admin_user_connectivity_manual.delay(admin_user)
return Response({"task": task.id})
class AdminUserAssetsListView(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSimpleSerializer
pagination_class = LimitOffsetPagination
filter_fields = ("hostname", "ip")
http_method_names = ['get']
search_fields = filter_fields
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(AdminUser, pk=pk)
def get_queryset(self):
admin_user = self.get_object()
return admin_user.get_related_assets()

View File

@@ -1,33 +1,42 @@
# -*- coding: utf-8 -*-
#
import uuid
import random
from rest_framework import generics
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from rest_framework.pagination import LimitOffsetPagination
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.core.cache import cache
from django.db.models import Q
from common.mixins import IDInFilterMixin
from common.utils import get_logger
from ..hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
NodePermissionUtil
from ..models import Asset, SystemUser, AdminUser, Node
from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
from ..models import Asset, AdminUser, Node
from .. import serializers
from ..tasks import update_asset_hardware_info_manual, \
test_asset_connectability_manual
test_asset_connectivity_manual
from ..utils import LabelFilter
logger = get_logger(__file__)
__all__ = [
'AssetViewSet', 'UserAssetListView', 'AssetListUpdateApi',
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi'
'AssetViewSet', 'AssetListUpdateApi',
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi',
'AssetGatewayApi', 'AssetBulkUpdateSelectAPI'
]
class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet):
"""
API endpoint that allows Asset to be viewed or edited.
"""
@@ -37,43 +46,85 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
pagination_class = LimitOffsetPagination
permission_classes = (IsSuperUserOrAppUser,)
permission_classes = (IsOrgAdminOrAppUser,)
def get_queryset(self):
queryset = super().get_queryset()
admin_user_id = self.request.query_params.get('admin_user_id')
node_id = self.request.query_params.get("node_id")
if admin_user_id:
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
queryset = queryset.filter(admin_user=admin_user)
def set_assets_node(self, assets):
if not isinstance(assets, list):
assets = [assets]
node = Node.root()
node_id = self.request.query_params.get('node_id')
if node_id:
node = get_object_or_404(Node, id=node_id)
if not node.is_root():
queryset = queryset.filter(nodes__key__startswith=node.key).distinct()
node = get_object_or_none(Node, pk=node_id)
node.assets.add(*assets)
def perform_create(self, serializer):
assets = serializer.save()
self.set_assets_node(assets)
def filter_node(self, queryset):
node_id = self.request.query_params.get("node_id")
if not node_id:
return queryset
node = get_object_or_404(Node, id=node_id)
show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true')
if node.is_root() and show_current_asset:
queryset = queryset.filter(
Q(nodes=node_id) | Q(nodes__isnull=True)
)
elif node.is_root() and not show_current_asset:
pass
elif not node.is_root() and show_current_asset:
queryset = queryset.filter(nodes=node)
else:
queryset = queryset.filter(
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
)
return queryset
def filter_admin_user_id(self, queryset):
admin_user_id = self.request.query_params.get('admin_user_id')
if not admin_user_id:
return queryset
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
queryset = queryset.filter(admin_user=admin_user)
return queryset
class UserAssetListView(generics.ListAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,)
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = self.filter_node(queryset)
queryset = self.filter_admin_user_id(queryset)
return queryset
def get_queryset(self):
assets_granted = NodePermissionUtil.get_user_assets(self.request.user).keys()
queryset = self.queryset.filter(
id__in=[asset.id for asset in assets_granted]
)
queryset = super().get_queryset().distinct()
queryset = self.get_serializer_class().setup_eager_loading(queryset)
return queryset
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
class AssetListUpdateApi(IDInCacheFilterMixin, ListBulkCreateUpdateDestroyAPIView):
"""
Asset bulk update api
"""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
class AssetBulkUpdateSelectAPI(APIView):
permission_classes = (IsOrgAdmin,)
def post(self, request, *args, **kwargs):
assets_id = request.data.get('assets_id', '')
if assets_id:
spm = uuid.uuid4().hex
key = CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX.format(spm)
cache.set(key, assets_id, 300)
url = reverse_lazy('assets:asset-bulk-update') + '?spm=%s' % spm
return Response({'url': url})
error = _('Please select assets that need to be updated')
return Response({'error': error}, status=400)
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
@@ -82,31 +133,43 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView):
"""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
def retrieve(self, request, *args, **kwargs):
asset_id = kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id)
summary = update_asset_hardware_info_manual(asset)[1]
logger.debug("Refresh summary: {}".format(summary))
if summary.get('dark'):
return Response(summary['dark'].values(), status=501)
else:
return Response({"msg": "ok"})
task = update_asset_hardware_info_manual.delay(asset)
return Response({"task": task.id})
class AssetAdminUserTestApi(generics.RetrieveAPIView):
"""
Test asset admin user connectivity
Test asset admin user assets_connectivity
"""
queryset = Asset.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.TaskIDSerializer
def retrieve(self, request, *args, **kwargs):
asset_id = kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id)
ok, msg = test_asset_connectability_manual(asset)
if ok:
return Response({"msg": "pong"})
task = test_asset_connectivity_manual.delay(asset)
return Response({"task": task.id})
class AssetGatewayApi(generics.RetrieveAPIView):
queryset = Asset.objects.all()
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.GatewayWithAuthSerializer
def retrieve(self, request, *args, **kwargs):
asset_id = kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id)
if asset.domain and \
asset.domain.gateways.filter(protocol=asset.protocol).exists():
gateway = random.choice(asset.domain.gateways.filter(protocol=asset.protocol))
serializer = serializers.GatewayWithAuthSerializer(instance=gateway)
return Response(serializer.data)
else:
return Response({"error": msg}, status=502)
return Response({"msg": "Not have gateway"}, status=404)

View File

@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
#
from rest_framework.response import Response
from rest_framework import viewsets, status, generics
from rest_framework.pagination import LimitOffsetPagination
from common.permissions import IsOrgAdminOrAppUser
from common.utils import get_object_or_none, get_logger
from ..backends.multi import AssetUserManager
from ..models import Asset
from .. import serializers
from ..tasks import test_asset_users_connectivity_manual
__all__ = [
'AssetUserViewSet', 'AssetUserAuthInfoApi', 'AssetUserTestConnectiveApi',
]
logger = get_logger(__name__)
class AssetUserViewSet(viewsets.GenericViewSet):
pagination_class = LimitOffsetPagination
serializer_class = serializers.AssetUserSerializer
permission_classes = (IsOrgAdminOrAppUser, )
http_method_names = ['get', 'post']
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
def get_queryset(self):
username = self.request.GET.get('username')
asset_id = self.request.GET.get('asset_id')
asset = get_object_or_none(Asset, pk=asset_id)
queryset = AssetUserManager.filter(username=username, asset=asset)
return queryset
def filter_queryset(self, queryset):
queryset = sorted(
queryset,
key=lambda q: (q.asset.hostname, q.connectivity, q.username)
)
return queryset
class AssetUserAuthInfoApi(generics.RetrieveAPIView):
serializer_class = serializers.AssetUserAuthInfoSerializer
permission_classes = (IsOrgAdminOrAppUser,)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
status_code = status.HTTP_200_OK
if not instance:
status_code = status.HTTP_400_BAD_REQUEST
return Response(serializer.data, status=status_code)
def get_object(self):
username = self.request.GET.get('username')
asset_id = self.request.GET.get('asset_id')
asset = get_object_or_none(Asset, pk=asset_id)
try:
instance = AssetUserManager.get(username, asset)
except Exception as e:
logger.error(e, exc_info=True)
return None
else:
return instance
class AssetUserTestConnectiveApi(generics.RetrieveAPIView):
"""
Test asset users connective
"""
def get_asset_users(self):
username = self.request.GET.get('username')
asset_id = self.request.GET.get('asset_id')
asset = get_object_or_none(Asset, pk=asset_id)
asset_users = AssetUserManager.filter(username=username, asset=asset)
return asset_users
def retrieve(self, request, *args, **kwargs):
asset_users = self.get_asset_users()
task = test_asset_users_connectivity_manual.delay(asset_users)
return Response({"task": task.id})

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#
from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination
from django.shortcuts import get_object_or_404
from ..hands import IsOrgAdmin
from ..models import CommandFilter, CommandFilterRule
from .. import serializers
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
class CommandFilterViewSet(BulkModelViewSet):
filter_fields = ("name",)
search_fields = filter_fields
permission_classes = (IsOrgAdmin,)
queryset = CommandFilter.objects.all()
serializer_class = serializers.CommandFilterSerializer
pagination_class = LimitOffsetPagination
class CommandFilterRuleViewSet(BulkModelViewSet):
filter_fields = ("content",)
search_fields = filter_fields
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.CommandFilterRuleSerializer
pagination_class = LimitOffsetPagination
def get_queryset(self):
fpk = self.kwargs.get('filter_pk')
if not fpk:
return CommandFilterRule.objects.none()
cmd_filter = get_object_or_404(CommandFilter, pk=fpk)
return cmd_filter.rules.all()

61
apps/assets/api/domain.py Normal file
View File

@@ -0,0 +1,61 @@
# ~*~ coding: utf-8 ~*~
from rest_framework_bulk import BulkModelViewSet
from rest_framework.views import APIView, Response
from rest_framework.pagination import LimitOffsetPagination
from django.views.generic.detail import SingleObjectMixin
from common.utils import get_logger
from common.permissions import IsOrgAdmin, IsAppUser, IsOrgAdminOrAppUser
from ..models import Domain, Gateway
from .. import serializers
logger = get_logger(__file__)
__all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
class DomainViewSet(BulkModelViewSet):
queryset = Domain.objects.all()
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.DomainSerializer
pagination_class = LimitOffsetPagination
def get_queryset(self):
queryset = super().get_queryset().all()
return queryset
def get_serializer_class(self):
if self.request.query_params.get('gateway'):
return serializers.DomainWithGatewaySerializer
return super().get_serializer_class()
def get_permissions(self):
if self.request.query_params.get('gateway'):
self.permission_classes = (IsOrgAdminOrAppUser,)
return super().get_permissions()
class GatewayViewSet(BulkModelViewSet):
filter_fields = ("domain__name", "name", "username", "ip", "domain")
search_fields = filter_fields
queryset = Gateway.objects.all()
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.GatewaySerializer
pagination_class = LimitOffsetPagination
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
permission_classes = (IsOrgAdmin,)
model = Gateway
object = None
def post(self, request, *args, **kwargs):
self.object = self.get_object(Gateway.objects.all())
local_port = self.request.data.get('port') or self.object.port
ok, e = self.object.test_connective(local_port=local_port)
if ok:
return Response("ok")
else:
return Response({"failed": e}, status=404)

View File

@@ -14,10 +14,11 @@
# limitations under the License.
from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination
from django.db.models import Count
from common.utils import get_logger
from ..hands import IsSuperUser
from ..hands import IsOrgAdmin
from ..models import Label
from .. import serializers
@@ -27,12 +28,18 @@ __all__ = ['LabelViewSet']
class LabelViewSet(BulkModelViewSet):
queryset = Label.objects.annotate(asset_count=Count("assets"))
permission_classes = (IsSuperUser,)
filter_fields = ("name", "value")
search_fields = filter_fields
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.LabelSerializer
pagination_class = LimitOffsetPagination
def list(self, request, *args, **kwargs):
if request.query_params.get("distinct"):
self.serializer_class = serializers.LabelDistinctSerializer
self.queryset = self.queryset.values("name").distinct()
return super().list(request, *args, **kwargs)
def get_queryset(self):
self.queryset = Label.objects.annotate(asset_count=Count("assets"))
return self.queryset

View File

@@ -13,32 +13,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from rest_framework import generics, mixins
from rest_framework import generics, mixins, viewsets
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404
from common.utils import get_logger, get_object_or_none
from ..hands import IsSuperUser
from common.tree import TreeNodeSerializer
from ..hands import IsOrgAdmin
from ..models import Node
from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util
from ..tasks import update_assets_hardware_info_util, test_asset_connectivity_util
from .. import serializers
logger = get_logger(__file__)
__all__ = [
'NodeViewSet', 'NodeChildrenApi',
'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeReplaceAssetsApi',
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
'TestNodeConnectiveApi'
'TestNodeConnectiveApi', 'NodeListAsTreeApi',
'NodeChildrenAsTreeApi', 'RefreshAssetsAmount',
]
class NodeViewSet(BulkModelViewSet):
class NodeViewSet(viewsets.ModelViewSet):
filter_fields = ('value', 'key', )
search_fields = filter_fields
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeSerializer
def perform_create(self, serializer):
@@ -46,42 +50,174 @@ class NodeViewSet(BulkModelViewSet):
serializer.validated_data["key"] = child_key
serializer.save()
def update(self, request, *args, **kwargs):
node = self.get_object()
if node.is_root():
node_value = node.value
post_value = request.data.get('value')
if node_value != post_value:
return Response(
{"msg": _("You can't update the root node name")},
status=400
)
return super().update(request, *args, **kwargs)
class NodeListAsTreeApi(generics.ListAPIView):
"""
获取节点列表树
[
{
"id": "",
"name": "",
"pId": "",
"meta": ""
}
]
"""
permission_classes = (IsOrgAdmin,)
serializer_class = TreeNodeSerializer
def get_queryset(self):
queryset = [node.as_tree_node() for node in Node.objects.all()]
return queryset
def filter_queryset(self, queryset):
if self.request.query_params.get('refresh', '0') == '1':
queryset = self.refresh_nodes(queryset)
return queryset
@staticmethod
def refresh_nodes(queryset):
Node.expire_nodes_assets_amount()
Node.expire_nodes_full_value()
return queryset
class NodeChildrenAsTreeApi(generics.ListAPIView):
"""
节点子节点作为树返回,
[
{
"id": "",
"name": "",
"pId": "",
"meta": ""
}
]
"""
permission_classes = (IsOrgAdmin,)
serializer_class = TreeNodeSerializer
node = None
is_root = False
def get_queryset(self):
node_key = self.request.query_params.get('key')
if node_key:
self.node = Node.objects.get(key=node_key)
queryset = self.node.get_children(with_self=False)
else:
self.is_root = True
self.node = Node.root()
queryset = list(self.node.get_children(with_self=True))
nodes_invalid = Node.objects.exclude(key__startswith=self.node.key)
queryset.extend(list(nodes_invalid))
queryset = [node.as_tree_node() for node in queryset]
queryset = sorted(queryset)
return queryset
def filter_assets(self, queryset):
include_assets = self.request.query_params.get('assets', '0') == '1'
if not include_assets:
return queryset
assets = self.node.get_assets()
for asset in assets:
queryset.append(asset.as_tree_node(self.node))
return queryset
def filter_queryset(self, queryset):
queryset = self.filter_assets(queryset)
queryset = self.filter_refresh_nodes(queryset)
return queryset
def filter_refresh_nodes(self, queryset):
if self.request.query_params.get('refresh', '0') == '1':
Node.expire_nodes_assets_amount()
Node.expire_nodes_full_value()
return queryset
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeSerializer
instance = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
instance = self.get_object()
if not request.data.get("value"):
request.data["value"] = _("New node {}").format(
Node.root().get_next_child_key().split(":")[-1]
)
request.data["value"] = instance.get_next_child_preset_name()
return super().post(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
instance = self.get_object()
value = request.data.get("value")
node = instance.create_child(value=value)
return Response(
{"id": node.id, "key": node.key, "value": node.value},
status=201,
)
_id = request.data.get('id') or None
values = [child.value for child in instance.get_children()]
if value in values:
raise ValidationError(
'The same level node name cannot be the same'
)
node = instance.create_child(value=value, _id=_id)
return Response(self.serializer_class(instance=node).data, status=201)
def get(self, request, *args, **kwargs):
instance = self.get_object()
if self.request.query_params.get("all"):
children = instance.get_all_children()
def get_object(self):
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
if not pk:
node = Node.root()
else:
children = instance.get_children()
response = [{"id": node.id, "key": node.key, "value": node.value} for node in children]
return Response(response, status=200)
node = get_object_or_404(Node, pk=pk)
return node
def get_queryset(self):
queryset = []
query_all = self.request.query_params.get("all")
node = self.get_object()
if node is None:
node = Node.root()
node.assets__count = node.get_all_assets().count()
queryset.append(node)
if query_all:
children = node.get_all_children()
else:
children = node.get_children()
queryset.extend(list(children))
return queryset
class NodeAssetsApi(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSerializer
def get_queryset(self):
node_id = self.kwargs.get('pk')
query_all = self.request.query_params.get('all')
instance = get_object_or_404(Node, pk=node_id)
if query_all:
return instance.get_all_assets()
else:
return instance.get_assets()
class NodeAddChildrenApi(generics.UpdateAPIView):
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeAddChildrenSerializer
instance = None
@@ -93,14 +229,13 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
if not node:
continue
node.parent = instance
node.save()
return Response("OK")
class NodeAddAssetsApi(generics.UpdateAPIView):
serializer_class = serializers.NodeAssetsSerializer
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
@@ -112,7 +247,7 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
class NodeRemoveAssetsApi(generics.UpdateAPIView):
serializer_class = serializers.NodeAssetsSerializer
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
@@ -120,31 +255,56 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
instance = self.get_object()
if instance != Node.root():
instance.assets.remove(*tuple(assets))
else:
assets = [asset for asset in assets if asset.nodes.count() > 1]
instance.assets.remove(*tuple(assets))
class NodeReplaceAssetsApi(generics.UpdateAPIView):
serializer_class = serializers.NodeAssetsSerializer
queryset = Node.objects.all()
permission_classes = (IsOrgAdmin,)
instance = None
def perform_update(self, serializer):
assets = serializer.validated_data.get('assets')
instance = self.get_object()
for asset in assets:
asset.nodes.set([instance])
class RefreshNodeHardwareInfoApi(APIView):
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
model = Node
def get(self, request, *args, **kwargs):
node_id = kwargs.get('pk')
node = get_object_or_404(self.model, id=node_id)
assets = node.assets.all()
# task_name = _("Refresh node assets hardware info: {}".format(node.name))
task_name = _("更新节点资产硬件信息: {}".format(node.name))
update_assets_hardware_info_util.delay(assets, task_name=task_name)
return Response({"msg": "Task created"})
assets = node.get_all_assets()
# task_name = _("更新节点资产硬件信息: {}".format(node.name))
task_name = _("Update node asset hardware information: {}").format(node.name)
task = update_assets_hardware_info_util.delay(assets, task_name=task_name)
return Response({"task": task.id})
class TestNodeConnectiveApi(APIView):
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
model = Node
def get(self, request, *args, **kwargs):
node_id = kwargs.get('pk')
node = get_object_or_404(self.model, id=node_id)
assets = node.assets.all()
task_name = _("测试节点下资产是否可连接: {}".format(node.name))
test_asset_connectability_util.delay(assets, task_name=task_name)
return Response({"msg": "Task created"})
assets = node.get_all_assets()
# task_name = _("测试节点下资产是否可连接: {}".format(node.name))
task_name = _("Test if the assets under the node are connectable: {}".format(node.name))
task = test_asset_connectivity_util.delay(assets, task_name=task_name)
return Response({"task": task.id})
class RefreshAssetsAmount(APIView):
permission_classes = (IsOrgAdmin,)
model = Node
def get(self, request, *args, **kwargs):
self.model.expire_nodes_assets_amount()
return Response("Ok")

View File

@@ -13,49 +13,76 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
from rest_framework.pagination import LimitOffsetPagination
from common.utils import get_logger
from ..hands import IsSuperUser, IsSuperUserOrAppUser
from ..models import SystemUser
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from common.mixins import IDInCacheFilterMixin
from ..models import SystemUser, Asset
from .. import serializers
from ..tasks import push_system_user_to_assets_manual, \
test_system_user_connectability_manual
test_system_user_connectivity_manual, push_system_user_a_asset_manual, \
test_system_user_connectivity_a_asset
logger = get_logger(__file__)
__all__ = [
'SystemUserViewSet', 'SystemUserAuthInfoApi',
'SystemUserPushApi', 'SystemUserTestConnectiveApi'
'SystemUserViewSet', 'SystemUserAuthInfoApi', 'SystemUserAssetAuthInfoApi',
'SystemUserPushApi', 'SystemUserTestConnectiveApi',
'SystemUserAssetsListView', 'SystemUserPushToAssetApi',
'SystemUserTestAssetConnectivityApi', 'SystemUserCommandFilterRuleListApi',
]
class SystemUserViewSet(BulkModelViewSet):
class SystemUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
"""
System user api set, for add,delete,update,list,retrieve resource
"""
filter_fields = ("name", "username")
search_fields = filter_fields
queryset = SystemUser.objects.all()
serializer_class = serializers.SystemUserSerializer
permission_classes = (IsSuperUserOrAppUser,)
permission_classes = (IsOrgAdminOrAppUser,)
pagination_class = LimitOffsetPagination
def get_queryset(self):
queryset = super().get_queryset().all()
return queryset
class SystemUserAuthInfoApi(generics.RetrieveUpdateAPIView):
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
"""
Get system user auth info
"""
queryset = SystemUser.objects.all()
permission_classes = (IsSuperUserOrAppUser,)
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.SystemUserAuthSerializer
def update(self, request, *args, **kwargs):
password = request.data.pop("password", None)
private_key = request.data.pop("private_key", None)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.clear_auth()
return Response(status=204)
if password or private_key:
instance.set_auth(password=password, private_key=private_key)
return super().update(request, *args, **kwargs)
class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
"""
Get system user with asset auth info
"""
queryset = SystemUser.objects.all()
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.SystemUserAuthSerializer
def get_object(self):
instance = super().get_object()
aid = self.kwargs.get('aid')
asset = get_object_or_404(Asset, pk=aid)
instance.load_specific_asset_auth(asset)
return instance
class SystemUserPushApi(generics.RetrieveAPIView):
@@ -63,12 +90,15 @@ class SystemUserPushApi(generics.RetrieveAPIView):
Push system user to cluster assets api
"""
queryset = SystemUser.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
push_system_user_to_assets_manual.delay(system_user)
return Response({"msg": "Task created"})
nodes = system_user.nodes.all()
for node in nodes:
system_user.assets.add(*tuple(node.get_all_assets()))
task = push_system_user_to_assets_manual.delay(system_user)
return Response({"task": task.id})
class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
@@ -76,9 +106,65 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
Push system user to cluster assets api
"""
queryset = SystemUser.objects.all()
permission_classes = (IsSuperUser,)
permission_classes = (IsOrgAdmin,)
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
test_system_user_connectability_manual.delay(system_user)
return Response({"msg": "Task created"})
task = test_system_user_connectivity_manual.delay(system_user)
return Response({"task": task.id})
class SystemUserAssetsListView(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSimpleSerializer
pagination_class = LimitOffsetPagination
filter_fields = ("hostname", "ip")
http_method_names = ['get']
search_fields = filter_fields
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(SystemUser, pk=pk)
def get_queryset(self):
system_user = self.get_object()
return system_user.assets.all()
class SystemUserPushToAssetApi(generics.RetrieveAPIView):
queryset = SystemUser.objects.all()
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.TaskIDSerializer
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
asset_id = self.kwargs.get('aid')
asset = get_object_or_404(Asset, id=asset_id)
task = push_system_user_a_asset_manual.delay(system_user, asset)
return Response({"task": task.id})
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
queryset = SystemUser.objects.all()
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.TaskIDSerializer
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
asset_id = self.kwargs.get('aid')
asset = get_object_or_404(Asset, id=asset_id)
task = test_system_user_connectivity_a_asset.delay(system_user, asset)
return Response({"task": task.id})
class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
def get_serializer_class(self):
from ..serializers import CommandFilterRuleSerializer
return CommandFilterRuleSerializer
def get_queryset(self):
pk = self.kwargs.get('pk', None)
system_user = get_object_or_404(SystemUser, pk=pk)
return system_user.cmd_filter_rules

View File

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
#
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from abc import abstractmethod
class NotSupportError(Exception):
pass
class BaseBackend:
ObjectDoesNotExist = ObjectDoesNotExist
MultipleObjectsReturned = MultipleObjectsReturned
NotSupportError = NotSupportError
MSG_NOT_EXIST = '{} Object matching query does not exist'
MSG_MULTIPLE = '{} get() returned more than one object ' \
'-- it returned {}!'
@classmethod
def get(cls, username, asset):
instances = cls.filter(username, asset)
if len(instances) == 1:
return instances[0]
elif len(instances) == 0:
cls.raise_does_not_exist(cls.__name__)
else:
cls.raise_multiple_return(cls.__name__, len(instances))
@classmethod
@abstractmethod
def filter(cls, username=None, asset=None, latest=True):
"""
:param username: 用户名
:param asset: <Asset>对象
:param latest: 是否是最新记录
:return: 元素为<AuthBook>的可迭代对象(<list> or <QuerySet>)
"""
pass
@classmethod
@abstractmethod
def create(cls, **kwargs):
"""
:param kwargs:
{
name, username, asset, comment, password, public_key, private_key,
(org_id)
}
:return: <AuthBook>对象
"""
pass
@classmethod
def raise_does_not_exist(cls, name):
raise cls.ObjectDoesNotExist(cls.MSG_NOT_EXIST.format(name))
@classmethod
def raise_multiple_return(cls, name, length):
raise cls.MultipleObjectsReturned(cls.MSG_MULTIPLE.format(name, length))

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
#

31
apps/assets/backends/external/db.py vendored Normal file
View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
#
from assets.models import AuthBook
from ..base import BaseBackend
class AuthBookBackend(BaseBackend):
@classmethod
def filter(cls, username=None, asset=None, latest=True):
queryset = AuthBook.objects.all()
if username is not None:
queryset = queryset.filter(username=username)
if asset:
queryset = queryset.filter(asset=asset)
if latest:
queryset = queryset.latest_version()
return queryset
@classmethod
def create(cls, **kwargs):
auth_info = {
'password': kwargs.pop('password', ''),
'public_key': kwargs.pop('public_key', ''),
'private_key': kwargs.pop('private_key', '')
}
obj = AuthBook.objects.create(**kwargs)
obj.set_auth(**auth_info)
return obj

16
apps/assets/backends/external/utils.py vendored Normal file
View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
#
# from django.conf import settings
from .db import AuthBookBackend
# from .vault import VaultBackend
def get_backend():
default_backend = AuthBookBackend
# if settings.BACKEND_ASSET_USER_AUTH_VAULT:
# return VaultBackend
return default_backend

19
apps/assets/backends/external/vault.py vendored Normal file
View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
#
from ..base import BaseBackend
class VaultBackend(BaseBackend):
@classmethod
def get(cls, username, asset):
pass
@classmethod
def filter(cls, username=None, asset=None, latest=True):
pass
@classmethod
def create(cls, **kwargs):
pass

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
#

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
#
from assets.models import Asset
from ..base import BaseBackend
from .utils import construct_authbook_object
class AdminUserBackend(BaseBackend):
@classmethod
def filter(cls, username=None, asset=None, **kwargs):
instances = cls.construct_authbook_objects(username, asset)
return instances
@classmethod
def _get_assets(cls, asset):
if not asset:
assets = Asset.objects.all().prefetch_related('admin_user')
else:
assets = [asset]
return assets
@classmethod
def construct_authbook_objects(cls, username, asset):
instances = []
assets = cls._get_assets(asset)
for asset in assets:
if username is not None and asset.admin_user.username != username:
continue
instance = construct_authbook_object(asset.admin_user, asset)
instances.append(instance)
return instances
@classmethod
def create(cls, **kwargs):
raise cls.NotSupportError("Not support create")

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
#
from ..base import BaseBackend
from .admin_user import AdminUserBackend
from .system_user import SystemUserBackend
class AssetUserBackend(BaseBackend):
@classmethod
def filter(cls, username=None, asset=None, **kwargs):
admin_user_instances = AdminUserBackend.filter(username, asset)
system_user_instances = SystemUserBackend.filter(username, asset)
instances = cls._merge_instances(admin_user_instances, system_user_instances)
return instances
@classmethod
def _merge_instances(cls, admin_user_instances, system_user_instances):
admin_user_instances_keyword_list = [
{'username': instance.username, 'asset': instance.asset}
for instance in admin_user_instances
]
instances = [
instance for instance in system_user_instances
if instance.keyword not in admin_user_instances_keyword_list
]
admin_user_instances.extend(instances)
return admin_user_instances
@classmethod
def create(cls, **kwargs):
raise cls.NotSupportError("Not support create")

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#
import itertools
from assets.models import Asset
from ..base import BaseBackend
from .utils import construct_authbook_object
class SystemUserBackend(BaseBackend):
@classmethod
def filter(cls, username=None, asset=None, **kwargs):
instances = cls.construct_authbook_objects(username, asset)
return instances
@classmethod
def _distinct_system_users_by_username(cls, system_users):
system_users = sorted(
system_users,
key=lambda su: (su.username, su.priority, su.date_updated),
reverse=True,
)
results = itertools.groupby(system_users, key=lambda su: su.username)
system_users = [next(result[1]) for result in results]
return system_users
@classmethod
def _filter_system_users_by_username(cls, system_users, username):
_system_users = cls._distinct_system_users_by_username(system_users)
if username is not None:
_system_users = [su for su in _system_users if username == su.username]
return _system_users
@classmethod
def _construct_authbook_objects(cls, system_users, asset):
instances = []
for system_user in system_users:
instance = construct_authbook_object(system_user, asset)
instances.append(instance)
return instances
@classmethod
def _get_assets_with_system_users(cls, asset=None):
"""
{ 'asset': set(<SystemUser>, <SystemUser>, ...) }
"""
if not asset:
_assets = Asset.objects.all().prefetch_related('systemuser_set')
else:
_assets = [asset]
assets = {asset: set(asset.systemuser_set.all()) for asset in _assets}
return assets
@classmethod
def construct_authbook_objects(cls, username, asset):
"""
:return: [<AuthBook>, <AuthBook>, ...]
"""
instances = []
assets = cls._get_assets_with_system_users(asset)
for _asset, _system_users in assets.items():
_system_users = cls._filter_system_users_by_username(_system_users, username)
_instances = cls._construct_authbook_objects(_system_users, _asset)
instances.extend(_instances)
return instances
@classmethod
def create(cls, **kwargs):
raise Exception("Not support create")

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
#
from assets.models import AuthBook
def construct_authbook_object(asset_user, asset):
"""
作用: 将<AssetUser>对象构造成为<AuthBook>对象并返回
:param asset_user: <AdminUser>或<SystemUser>对象
:param asset: <Asset>对象
:return: <AuthBook>对象
"""
fields = [
'id', 'name', 'username', 'comment', 'org_id',
'_password', '_private_key', '_public_key',
'date_created', 'date_updated', 'created_by'
]
obj = AuthBook(asset=asset, version=0, is_latest=True)
for field in fields:
value = getattr(asset_user, field)
setattr(obj, field, value)
return obj

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#
from .base import BaseBackend
from .external.utils import get_backend
from .internal.asset_user import AssetUserBackend
class AssetUserManager(BaseBackend):
"""
资产用户管理器
"""
external_backend = get_backend()
internal_backend = AssetUserBackend
@classmethod
def filter(cls, username=None, asset=None, **kwargs):
external_instance = list(cls.external_backend.filter(username, asset))
internal_instance = list(cls.internal_backend.filter(username, asset))
instances = cls._merge_instances(external_instance, internal_instance)
return instances
@classmethod
def create(cls, **kwargs):
instance = cls.external_backend.create(**kwargs)
return instance
@classmethod
def _merge_instances(cls, external_instances, internal_instances):
external_instances_keyword_list = [
{'username': instance.username, 'asset': instance.asset}
for instance in external_instances
]
instances = [
instance for instance in internal_instances
if instance.keyword not in external_instances_keyword_list
]
external_instances.extend(instances)
return external_instances

View File

@@ -1,6 +1,9 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
UPDATE_ASSETS_HARDWARE_TASKS = [
{
'name': "setup",
@@ -32,7 +35,23 @@ TEST_SYSTEM_USER_CONN_TASKS = [
}
]
ASSET_USER_CONN_CACHE_KEY = 'ASSET_USER_CONN_{}_{}'
TEST_ASSET_USER_CONN_TASKS = [
{
"name": "ping",
"action": {
"module": "ping",
}
}
]
TASK_OPTIONS = {
'timeout': 10,
'forks': 10,
}
CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX = '_KEY_ASSET_BULK_UPDATE_ID_{}'

View File

@@ -3,3 +3,5 @@
from .asset import *
from .label import *
from .user import *
from .domain import *
from .cmd_filter import *

View File

@@ -3,19 +3,23 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import Asset, AdminUser
from common.utils import get_logger
from orgs.mixins import OrgModelForm
from ..models import Asset, AdminUser
logger = get_logger(__file__)
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm']
class AssetCreateForm(forms.ModelForm):
class AssetCreateForm(OrgModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment',
'nodes', 'is_active', 'admin_user', 'labels', 'platform',
'domain', 'protocol',
]
widgets = {
@@ -26,57 +30,65 @@ class AssetCreateForm(forms.ModelForm):
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
'class': 'select2', 'data-placeholder': _('Label')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
labels = {
'nodes': _("Node"),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'admin_user': _(
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
),
'platform': _("* required Must set exact system platform, Windows, Linux ...")
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
'domain': _("If your have some network not connect with each other, you can set domain")
}
class AssetUpdateForm(forms.ModelForm):
class AssetUpdateForm(OrgModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform',
'public_ip', 'number', 'comment', 'admin_user', 'labels',
'domain', 'protocol',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Nodes')
'class': 'select2', 'data-placeholder': _('Node')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
'class': 'select2', 'data-placeholder': _('Label')
}),
'port': forms.TextInput(),
'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain')
}),
}
labels = {
'nodes': _("Node"),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _(
'root or other NOPASSWD sudo privilege user existed in asset,'
'If asset is windows or other set any one, more see admin user left menu'
),
'platform': _("* required Must set exact system platform, Windows, Linux ...")
'platform': _("Windows 2016 RDP protocol is different, If is window 2016, set it"),
'domain': _("If your have some network not connect with each other, you can set domain")
}
class AssetBulkUpdateForm(forms.ModelForm):
class AssetBulkUpdateForm(OrgModelForm):
assets = forms.ModelMultipleChoiceField(
required=True, help_text='* required',
required=True,
label=_('Select assets'), queryset=Asset.objects.all(),
widget=forms.SelectMultiple(
attrs={
@@ -85,34 +97,29 @@ class AssetBulkUpdateForm(forms.ModelForm):
}
)
)
port = forms.IntegerField(
label=_('Port'), required=False, min_value=1, max_value=65535,
)
admin_user = forms.ModelChoiceField(
required=False, queryset=AdminUser.objects.all(),
label=_("Admin user"),
widget=forms.Select(
attrs={
'class': 'select2',
'data-placeholder': _('Admin user')
}
)
)
class Meta:
model = Asset
fields = [
'assets', 'port', 'admin_user', 'labels', 'nodes', 'platform'
'assets', 'port', 'admin_user', 'labels', 'platform',
'protocol', 'domain',
]
widgets = {
'labels': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select labels')}
attrs={'class': 'select2', 'data-placeholder': _('Label')}
),
'nodes': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select nodes')}
attrs={'class': 'select2', 'data-placeholder': _('Node')}
),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 重写其他字段为不再required
for name, field in self.fields.items():
if name != 'assets':
field.required = False
def save(self, commit=True):
changed_fields = []
for field in self._meta.fields:
@@ -123,14 +130,14 @@ class AssetBulkUpdateForm(forms.ModelForm):
if k in changed_fields}
assets = cleaned_data.pop('assets')
labels = cleaned_data.pop('labels', [])
nodes = cleaned_data.pop('nodes')
nodes = cleaned_data.pop('nodes', None)
assets = Asset.objects.filter(id__in=[asset.id for asset in assets])
assets.update(**cleaned_data)
if labels:
for label in labels:
label.assets.add(*tuple(assets))
for asset in assets:
asset.labels.set(labels)
if nodes:
for node in nodes:
node.assets.add(*tuple(assets))
for asset in assets:
asset.nodes.set(nodes)
return assets

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
from django import forms
from orgs.mixins import OrgModelForm
from ..models import CommandFilter, CommandFilterRule
__all__ = ['CommandFilterForm', 'CommandFilterRuleForm']
class CommandFilterForm(OrgModelForm):
class Meta:
model = CommandFilter
fields = ['name', 'comment']
class CommandFilterRuleForm(OrgModelForm):
class Meta:
model = CommandFilterRule
fields = [
'filter', 'type', 'content', 'priority', 'action', 'comment'
]
widgets = {
'content': forms.Textarea(attrs={
'placeholder': 'eg:\r\nreboot\r\nrm -rf'
}),
}

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from orgs.mixins import OrgModelForm
from ..models import Domain, Asset, Gateway
from .user import PasswordAndKeyAuthForm
__all__ = ['DomainForm', 'GatewayForm']
class DomainForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Domain
fields = ['name', 'comment', 'assets']
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
# 前端渲染优化, 防止过多资产
assets_field = self.fields.get('assets')
if not self.data:
instance = kwargs.get('instance')
if instance:
assets_field.queryset = instance.assets.all()
else:
assets_field.queryset = Asset.objects.none()
def save(self, commit=True):
instance = super().save(commit=commit)
assets = self.cleaned_data['assets']
instance.assets.set(assets)
return instance
class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
password_field = self.fields.get('password')
password_field.help_text = _('Password should not contain special characters')
protocol_field = self.fields.get('protocol')
protocol_field.choices = [Gateway.PROTOCOL_CHOICES[0]]
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
instance = super().save()
password = self.cleaned_data.get('password')
private_key, public_key = super().gen_keys()
instance.set_auth(password=password, private_key=private_key)
return instance
class Meta:
model = Gateway
fields = [
'name', 'ip', 'port', 'username', 'protocol', 'domain', 'password',
'private_key_file', 'is_active', 'comment',
]
help_texts = {
'protocol': _("SSH gateway support proxy SSH,RDP,VNC")
}
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}

View File

@@ -26,6 +26,15 @@ class LabelForm(forms.ModelForm):
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
# 前端渲染优化, 防止过多资产
assets_field = self.fields.get('assets')
if not self.data:
instance = kwargs.get('instance')
if instance:
assets_field.queryset = instance.assets.all()
else:
assets_field.queryset = Asset.objects.none()
def save(self, commit=True):
label = super().save(commit=commit)
assets = self.cleaned_data['assets']

View File

@@ -3,12 +3,13 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import AdminUser, SystemUser
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
from orgs.mixins import OrgModelForm
from ..models import AdminUser, SystemUser
logger = get_logger(__file__)
__all__ = [
'FileForm', 'SystemUserForm', 'AdminUserForm',
'FileForm', 'SystemUserForm', 'AdminUserForm', 'PasswordAndKeyAuthForm',
]
@@ -34,8 +35,12 @@ class PasswordAndKeyAuthForm(forms.ModelForm):
if private_key_file:
key_string = private_key_file.read()
private_key_file.seek(0)
key_string = key_string.decode()
if not validate_ssh_private_key(key_string, password):
raise forms.ValidationError(_('Invalid private key'))
msg = _('Invalid private key, Only support '
'RSA/DSA format key')
raise forms.ValidationError(msg)
return private_key_file
def validate_password_key(self):
@@ -79,13 +84,9 @@ class AdminUserForm(PasswordAndKeyAuthForm):
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
}
help_texts = {
'name': '* required',
'username': '* required',
}
class SystemUserForm(PasswordAndKeyAuthForm):
class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False)
@@ -93,14 +94,26 @@ class SystemUserForm(PasswordAndKeyAuthForm):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super().save()
password = self.cleaned_data.get('password', '') or None
login_mode = self.cleaned_data.get('login_mode', '') or None
protocol = self.cleaned_data.get('protocol') or None
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
private_key, public_key = super().gen_keys()
if login_mode == SystemUser.LOGIN_MANUAL or \
protocol in [SystemUser.PROTOCOL_RDP,
SystemUser.PROTOCOL_TELNET,
SystemUser.PROTOCOL_VNC]:
system_user.auto_push = 0
auto_generate_key = False
system_user.save()
if auto_generate_key:
logger.info('Auto generate key and set system user auth')
system_user.auto_gen_auth()
else:
system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
system_user.set_auth(password=password, private_key=private_key,
public_key=public_key)
return system_user
def clean(self):
@@ -109,27 +122,38 @@ class SystemUserForm(PasswordAndKeyAuthForm):
if not self.instance and not auto_generate:
super().validate_password_key()
def clean_username(self):
username = self.data.get('username')
login_mode = self.data.get('login_mode')
protocol = self.data.get('protocol')
if username:
return username
if login_mode == SystemUser.LOGIN_AUTO and \
protocol != SystemUser.PROTOCOL_VNC:
msg = _('* Automatic login mode must fill in the username.')
raise forms.ValidationError(msg)
return username
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key',
'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'nodes', 'priority',
'comment', 'shell', 'priority', 'login_mode', 'cmd_filters',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'nodes': forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _('Nodes')
}
),
'cmd_filters': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Command filter')
}),
}
help_texts = {
'name': '* required',
'username': '* required',
'nodes': _('If auto push checked, system user will be create at node assets'),
'auto_push': _('Auto push system user to asset'),
'priority': _('High level will be using login asset as default, if user was granted more than 2 system user'),
}
'priority': _('1-100, High level will be using login asset as default, '
'if user was granted more than 2 system user'),
'login_mode': _('If you choose manual login mode, you do not '
'need to fill in the username and password.'),
'sudo': _("Use comma split multi command, ex: /bin/whoami,/bin/ifconfig")
}

View File

@@ -11,7 +11,6 @@
"""
from common.mixins import AdminUserRequiredMixin
from common.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser
from common.permissions import AdminUserRequiredMixin
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
from users.models import User, UserGroup
from perms.utils import NodePermissionUtil

View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-05 10:07
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='adminuser',
options={'ordering': ['name'], 'verbose_name': 'Admin user'},
),
migrations.AlterModelOptions(
name='asset',
options={'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='assetgroup',
options={'ordering': ['name'], 'verbose_name': 'Asset group'},
),
migrations.AlterModelOptions(
name='cluster',
options={'ordering': ['name'], 'verbose_name': 'Cluster'},
),
migrations.AlterModelOptions(
name='systemuser',
options={'ordering': ['name'], 'verbose_name': 'System user'},
),
]

View File

@@ -0,0 +1,158 @@
# Generated by Django 2.1.7 on 2019-02-28 10:16
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
replaces = [('assets', '0002_auto_20180105_1807'), ('assets', '0003_auto_20180109_2331'), ('assets', '0004_auto_20180125_1218'), ('assets', '0005_auto_20180126_1637'), ('assets', '0006_auto_20180130_1502'), ('assets', '0007_auto_20180225_1815'), ('assets', '0008_auto_20180306_1804'), ('assets', '0009_auto_20180307_1212')]
dependencies = [
('assets', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='adminuser',
options={'ordering': ['name'], 'verbose_name': 'Admin user'},
),
migrations.AlterModelOptions(
name='asset',
options={'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='assetgroup',
options={'ordering': ['name'], 'verbose_name': 'Asset group'},
),
migrations.AlterModelOptions(
name='cluster',
options={'ordering': ['name'], 'verbose_name': 'Cluster'},
),
migrations.AlterModelOptions(
name='systemuser',
options={'ordering': ['name'], 'verbose_name': 'System user'},
),
migrations.RemoveField(
model_name='asset',
name='cluster',
),
migrations.AlterField(
model_name='assetgroup',
name='created_by',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by'),
),
migrations.CreateModel(
name='Label',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('value', models.CharField(max_length=128, verbose_name='Value')),
('category', models.CharField(choices=[('S', 'System'), ('U', 'User')], default='U', max_length=128, verbose_name='Category')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
options={
'db_table': 'assets_label',
},
),
migrations.AlterUniqueTogether(
name='label',
unique_together={('name', 'value')},
),
migrations.AddField(
model_name='asset',
name='labels',
field=models.ManyToManyField(blank=True, related_name='assets', to='assets.Label', verbose_name='Labels'),
),
migrations.RemoveField(
model_name='asset',
name='cabinet_no',
),
migrations.RemoveField(
model_name='asset',
name='cabinet_pos',
),
migrations.RemoveField(
model_name='asset',
name='env',
),
migrations.RemoveField(
model_name='asset',
name='remote_card_ip',
),
migrations.RemoveField(
model_name='asset',
name='status',
),
migrations.RemoveField(
model_name='asset',
name='type',
),
migrations.CreateModel(
name='Node',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('key', models.CharField(max_length=64, unique=True, verbose_name='Key')),
('value', models.CharField(max_length=128, verbose_name='Value')),
('child_mark', models.IntegerField(default=0)),
('date_create', models.DateTimeField(auto_now_add=True)),
],
),
migrations.RemoveField(
model_name='asset',
name='groups',
),
migrations.RemoveField(
model_name='systemuser',
name='cluster',
),
migrations.AlterField(
model_name='asset',
name='admin_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='assets.AdminUser', verbose_name='Admin user'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AddField(
model_name='asset',
name='nodes',
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
),
migrations.AddField(
model_name='systemuser',
name='nodes',
field=models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes'),
),
migrations.AlterField(
model_name='adminuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='systemuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
]

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-09 15:31
from __future__ import unicode_literals
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0002_auto_20180105_1807'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='cluster',
field=models.ForeignKey(default=assets.models.asset.default_cluster, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='assets', to='assets.Cluster', verbose_name='Cluster'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-25 04:18
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0003_auto_20180109_2331'),
]
operations = [
migrations.AlterField(
model_name='assetgroup',
name='created_by',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by'),
),
]

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-26 08:37
from __future__ import unicode_literals
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0004_auto_20180125_1218'),
]
operations = [
migrations.CreateModel(
name='Label',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('value', models.CharField(max_length=128, verbose_name='Value')),
('category', models.CharField(choices=[('S', 'System'), ('U', 'User')], default='U', max_length=128, verbose_name='Category')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
options={
'db_table': 'assets_label',
},
),
migrations.AlterUniqueTogether(
name='label',
unique_together=set([('name', 'value')]),
),
migrations.AddField(
model_name='asset',
name='labels',
field=models.ManyToManyField(blank=True, related_name='assets', to='assets.Label', verbose_name='Labels'),
),
]

View File

@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-30 07:02
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0005_auto_20180126_1637'),
]
operations = [
migrations.RemoveField(
model_name='asset',
name='cabinet_no',
),
migrations.RemoveField(
model_name='asset',
name='cabinet_pos',
),
migrations.RemoveField(
model_name='asset',
name='env',
),
migrations.RemoveField(
model_name='asset',
name='remote_card_ip',
),
migrations.RemoveField(
model_name='asset',
name='status',
),
migrations.RemoveField(
model_name='asset',
name='type',
),
]

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-25 10:15
from __future__ import unicode_literals
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0006_auto_20180130_1502'),
]
operations = [
migrations.CreateModel(
name='Node',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('key', models.CharField(max_length=64, unique=True, verbose_name='Key')),
('value', models.CharField(max_length=128, unique=True, verbose_name='Value')),
('child_mark', models.IntegerField(default=0)),
('date_create', models.DateTimeField(auto_now_add=True)),
],
),
migrations.RemoveField(
model_name='asset',
name='cluster',
),
migrations.RemoveField(
model_name='asset',
name='groups',
),
migrations.RemoveField(
model_name='systemuser',
name='cluster',
),
migrations.AlterField(
model_name='asset',
name='admin_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='assets.AdminUser', verbose_name='Admin user'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AddField(
model_name='asset',
name='nodes',
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
),
migrations.AddField(
model_name='systemuser',
name='nodes',
field=models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes'),
),
]

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-06 10:04
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0007_auto_20180225_1815'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='systemuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-07 04:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_auto_20180306_1804'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, verbose_name='Value'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-07 09:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0009_auto_20180307_1212'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, unique=True, verbose_name='Value'),
),
]

View File

@@ -0,0 +1,220 @@
# Generated by Django 2.1.7 on 2019-02-28 10:16
import assets.models.utils
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
# Functions from the following migrations need manual copying.
# Move them and any dependencies into this file, then update the
# RunPython operations to refer to the local versions:
# assets.migrations.0017_auto_20180702_1415
def migrate_win_to_ssh_protocol(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
db_alias = schema_editor.connection.alias
asset_model.objects.using(db_alias).filter(platform__startswith='Win').update(protocol='rdp')
class Migration(migrations.Migration):
replaces = [('assets', '0010_auto_20180307_1749'), ('assets', '0011_auto_20180326_0957'), ('assets', '0012_auto_20180404_1302'), ('assets', '0013_auto_20180411_1135'), ('assets', '0014_auto_20180427_1245'), ('assets', '0015_auto_20180510_1235'), ('assets', '0016_auto_20180511_1203'), ('assets', '0017_auto_20180702_1415'), ('assets', '0018_auto_20180807_1116'), ('assets', '0019_auto_20180816_1320')]
dependencies = [
('assets', '0009_auto_20180307_1212'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, unique=True, verbose_name='Value'),
),
migrations.CreateModel(
name='Domain',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
),
migrations.CreateModel(
name='Gateway',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('username', models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('ip', models.GenericIPAddressField(db_index=True, verbose_name='IP')),
('port', models.IntegerField(default=22, verbose_name='Port')),
('protocol', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol')),
('comment', models.CharField(blank=True, max_length=128, null=True, verbose_name='Comment')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Domain', verbose_name='Domain')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='asset',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.Domain', verbose_name='Domain'),
),
migrations.AddField(
model_name='systemuser',
name='assets',
field=models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets'),
),
migrations.AlterField(
model_name='systemuser',
name='sudo',
field=models.TextField(default='/bin/whoami', verbose_name='Sudo'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, verbose_name='Value'),
),
migrations.AddField(
model_name='asset',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=128, verbose_name='Protocol'),
),
migrations.AddField(
model_name='systemuser',
name='login_mode',
field=models.CharField(choices=[('auto', 'Automatic login'), ('manual', 'Manually login')], default='auto', max_length=10, verbose_name='Login mode'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Windows2016', 'Windows(2016)'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.RunPython(
code=migrate_win_to_ssh_protocol,
),
migrations.AddField(
model_name='adminuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='asset',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='domain',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='gateway',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='label',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='node',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='systemuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AlterField(
model_name='adminuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='asset',
name='hostname',
field=models.CharField(max_length=128, verbose_name='Hostname'),
),
migrations.AlterField(
model_name='gateway',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='systemuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterUniqueTogether(
name='adminuser',
unique_together={('name', 'org_id')},
),
migrations.AddField(
model_name='asset',
name='cpu_vcpus',
field=models.IntegerField(null=True, verbose_name='CPU vcpus'),
),
migrations.AlterUniqueTogether(
name='asset',
unique_together={('org_id', 'hostname')},
),
migrations.AlterUniqueTogether(
name='gateway',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='systemuser',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='label',
unique_together={('name', 'value', 'org_id')},
),
]

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-26 01:57
from __future__ import unicode_literals
import assets.models.utils
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0010_auto_20180307_1749'),
]
operations = [
migrations.CreateModel(
name='Domain',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
),
migrations.CreateModel(
name='Gateway',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('username', models.CharField(max_length=128, verbose_name='Username')),
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('ip', models.GenericIPAddressField(db_index=True, verbose_name='IP')),
('port', models.IntegerField(default=22, verbose_name='Port')),
('protocol', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol')),
('comment', models.CharField(blank=True, max_length=128, null=True, verbose_name='Comment')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Domain', verbose_name='Domain')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='asset',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='assets.Domain', verbose_name='Domain'),
),
]

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-04 05:02
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0011_auto_20180326_0957'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.Domain', verbose_name='Domain'),
),
]

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-11 03:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0012_auto_20180404_1302'),
]
operations = [
migrations.AddField(
model_name='systemuser',
name='assets',
field=models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets'),
),
migrations.AlterField(
model_name='systemuser',
name='sudo',
field=models.TextField(default='/bin/whoami', verbose_name='Sudo'),
),
]

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-27 04:45
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0013_auto_20180411_1135'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
]

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-05-10 04:35
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0014_auto_20180427_1245'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
]

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-05-11 04:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0015_auto_20180510_1235'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, verbose_name='Value'),
),
]

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-07-02 06:15
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
def migrate_win_to_ssh_protocol(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
db_alias = schema_editor.connection.alias
asset_model.objects.using(db_alias).filter(platform__startswith='Win').update(protocol='rdp')
class Migration(migrations.Migration):
dependencies = [
('assets', '0016_auto_20180511_1203'),
]
operations = [
migrations.AddField(
model_name='asset',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=128, verbose_name='Protocol'),
),
migrations.AddField(
model_name='systemuser',
name='login_mode',
field=models.CharField(choices=[('auto', 'Automatic login'), ('manual', 'Manually login')], default='auto', max_length=10, verbose_name='Login mode'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Windows2016', 'Windows(2016)'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.RunPython(migrate_win_to_ssh_protocol),
]

View File

@@ -0,0 +1,84 @@
# Generated by Django 2.0.7 on 2018-08-07 03:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0017_auto_20180702_1415'),
]
operations = [
migrations.AddField(
model_name='adminuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='asset',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='domain',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='gateway',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='label',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='node',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='systemuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AlterField(
model_name='adminuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='asset',
name='hostname',
field=models.CharField(max_length=128, verbose_name='Hostname'),
),
migrations.AlterField(
model_name='gateway',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='systemuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterUniqueTogether(
name='adminuser',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='asset',
unique_together={('org_id', 'hostname')},
),
migrations.AlterUniqueTogether(
name='gateway',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='systemuser',
unique_together={('name', 'org_id')},
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 2.0.7 on 2018-08-16 05:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0018_auto_20180807_1116'),
]
operations = [
migrations.AddField(
model_name='asset',
name='cpu_vcpus',
field=models.IntegerField(null=True, verbose_name='CPU vcpus'),
),
migrations.AlterUniqueTogether(
name='label',
unique_together={('name', 'value', 'org_id')},
),
]

View File

@@ -0,0 +1,48 @@
# Generated by Django 2.0.7 on 2018-08-16 08:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0019_auto_20180816_1320'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='asset',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='domain',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='gateway',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='label',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='node',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
migrations.AlterField(
model_name='systemuser',
name='org_id',
field=models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization'),
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 2.1 on 2018-09-03 03:32
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0020_auto_20180816_1652'),
]
operations = [
migrations.AlterModelOptions(
name='domain',
options={'verbose_name': 'Domain'},
),
migrations.AlterModelOptions(
name='gateway',
options={'verbose_name': 'Gateway'},
),
migrations.AlterModelOptions(
name='node',
options={'verbose_name': 'Node'},
),
]

View File

@@ -0,0 +1,56 @@
# Generated by Django 2.1.1 on 2018-10-12 09:17
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0021_auto_20180903_1132'),
]
operations = [
migrations.CreateModel(
name='CommandFilter',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=64, verbose_name='Name')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(blank=True, default='', max_length=128, verbose_name='Created by')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='CommandFilterRule',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('type', models.CharField(choices=[('regex', 'Regex'), ('command', 'Command')], default='command', max_length=16, verbose_name='Type')),
('priority', models.IntegerField(default=50, help_text='1-100, the lower will be match first', validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Priority')),
('content', models.TextField(help_text='One line one command', max_length=1024, verbose_name='Content')),
('action', models.IntegerField(choices=[(0, 'Deny'), (1, 'Allow')], default=0, verbose_name='Action')),
('comment', models.CharField(blank=True, default='', max_length=64, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(blank=True, default='', max_length=128, verbose_name='Created by')),
('filter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='assets.CommandFilter', verbose_name='Filter')),
],
options={
'ordering': ('priority', 'action'),
},
),
migrations.AddField(
model_name='systemuser',
name='cmd_filters',
field=models.ManyToManyField(blank=True, related_name='system_users', to='assets.CommandFilter', verbose_name='Command filter'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 2.1.1 on 2018-10-16 08:50
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0022_auto_20181012_1717'),
]
operations = [
migrations.AlterModelOptions(
name='commandfilterrule',
options={'ordering': ('-priority', 'action')},
),
migrations.AlterField(
model_name='commandfilterrule',
name='priority',
field=models.IntegerField(default=50, help_text='1-100, the higher will be match first', validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Priority'),
),
migrations.AlterField(
model_name='systemuser',
name='priority',
field=models.IntegerField(default=20, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Priority'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 2.1.4 on 2018-12-19 08:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0023_auto_20181016_1650'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)'), ('vnc', 'vnc')], default='ssh', max_length=128, verbose_name='Protocol'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)'), ('vnc', 'vnc')], default='ssh', max_length=16, verbose_name='Protocol'),
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 2.1.7 on 2019-02-21 11:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0024_auto_20181219_1614'),
]
operations = [
migrations.AlterModelOptions(
name='commandfilter',
options={'verbose_name': 'Command filter'},
),
migrations.AlterModelOptions(
name='commandfilterrule',
options={'ordering': ('-priority', 'action'), 'verbose_name': 'Command filter rule'},
),
]

View File

@@ -0,0 +1,43 @@
# Generated by Django 2.1.7 on 2019-03-25 12:35
import assets.models.utils
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0025_auto_20190221_1902'),
]
operations = [
migrations.CreateModel(
name='AuthBook',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('is_latest', models.BooleanField(default=False, verbose_name='Latest version')),
('version', models.IntegerField(default=1, verbose_name='Version')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
],
options={
'verbose_name': 'AuthBook',
},
),
migrations.AlterModelOptions(
name='node',
options={'ordering': ['key'], 'verbose_name': 'Node'},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-05-21 09:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0026_auto_20190325_2035'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='ip',
field=models.CharField(db_index=True, max_length=128, verbose_name='IP'),
),
]

View File

@@ -1,10 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from .user import AdminUser, SystemUser
from .user import *
from .label import Label
from .cluster import *
from .group import *
from .domain import *
from .node import *
from .asset import *
from .cmd_filter import *
from .utils import *
from .authbook import *
from applications.models.remote_app import *

View File

@@ -4,15 +4,17 @@
import uuid
import logging
import random
from functools import reduce
from collections import defaultdict
from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
from .cluster import Cluster
from .group import AssetGroup
from .user import AdminUser, SystemUser
from orgs.mixins import OrgModelMixin, OrgManager
__all__ = ['Asset']
logger = logging.getLogger(__name__)
@@ -31,12 +33,21 @@ def default_cluster():
def default_node():
try:
from .node import Node
return Node.root()
root = Node.root()
return root
except:
return None
class Asset(models.Model):
class AssetQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def valid(self):
return self.active()
class Asset(OrgModelMixin):
# Important
PLATFORM_CHOICES = (
('Linux', 'Linux'),
@@ -44,12 +55,28 @@ class Asset(models.Model):
('MacOS', 'MacOS'),
('BSD', 'BSD'),
('Windows', 'Windows'),
('Windows2016', 'Windows(2016)'),
('Other', 'Other'),
)
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet'
PROTOCOL_VNC = 'vnc'
PROTOCOL_CHOICES = (
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet (beta)'),
(PROTOCOL_VNC, 'vnc'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
protocol = models.CharField(max_length=128, default=PROTOCOL_SSH, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
@@ -68,11 +95,11 @@ class Asset(models.Model):
cpu_model = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU model'))
cpu_count = models.IntegerField(null=True, verbose_name=_('CPU count'))
cpu_cores = models.IntegerField(null=True, verbose_name=_('CPU cores'))
cpu_vcpus = models.IntegerField(null=True, verbose_name=_('CPU vcpus'))
memory = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Memory'))
disk_total = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk total'))
disk_info = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk info'))
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
os_version = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('OS version'))
os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS arch'))
@@ -83,8 +110,17 @@ class Asset(models.Model):
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
objects = OrgManager.from_queryset(AssetQuerySet)()
CONNECTIVITY_CACHE_KEY = '_JMS_ASSET_CONNECTIVITY_{}'
UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3)
CONNECTIVITY_CHOICES = (
(UNREACHABLE, _("Unreachable")),
(REACHABLE, _('Reachable')),
(UNKNOWN, _("Unknown")),
)
def __str__(self):
return self.hostname
return '{0.hostname}({0.ip})'.format(self)
@property
def is_valid(self):
@@ -95,49 +131,109 @@ class Asset(models.Model):
return True, ''
return False, warning
def support_ansible(self):
if self.platform in ("Windows", "Windows2016", "Other"):
return False
if self.protocol != 'ssh':
return False
return True
def is_unixlike(self):
if self.platform not in ("Windows", "Other"):
if self.platform not in ("Windows", "Windows2016"):
return True
else:
return False
def get_nodes(self):
from .node import Node
nodes = self.nodes.all() or [Node.root()]
return nodes
def get_all_nodes(self, flat=False):
nodes = []
for node in self.get_nodes():
_nodes = node.get_ancestor(with_self=True)
nodes.append(_nodes)
if flat:
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
return nodes
@classmethod
def get_queryset_by_fullname_list(cls, fullname_list):
org_fullname_map = defaultdict(list)
for fullname in fullname_list:
hostname, org = cls.split_fullname(fullname)
org_fullname_map[org].append(hostname)
filter_arg = Q()
for org, hosts in org_fullname_map.items():
if org.is_real():
filter_arg |= Q(hostname__in=hosts, org_id=org.id)
else:
filter_arg |= Q(Q(org_id__isnull=True) | Q(org_id=''), hostname__in=hosts)
return Asset.objects.filter(filter_arg)
@property
def hardware_info(self):
if self.cpu_count:
return '{} Core {} {}'.format(
self.cpu_count * self.cpu_cores,
self.cpu_vcpus or self.cpu_count * self.cpu_cores,
self.memory, self.disk_total
)
else:
return ''
@property
def is_connective(self):
def connectivity(self):
if not self.is_unixlike():
return True
val = cache.get(ASSET_ADMIN_CONN_CACHE_KEY.format(self.hostname))
if val == 1:
return True
else:
return False
return self.REACHABLE
key = self.CONNECTIVITY_CACHE_KEY.format(str(self.id))
cached = cache.get(key, None)
return cached if cached is not None else self.UNKNOWN
@connectivity.setter
def connectivity(self, value):
key = self.CONNECTIVITY_CACHE_KEY.format(str(self.id))
cache.set(key, value, 3600*2)
def get_auth_info(self):
if self.admin_user:
self.admin_user.load_specific_asset_auth(self)
return {
'username': self.admin_user.username,
'password': self.admin_user.password,
'private_key': self.admin_user.private_key_file,
'become': self.admin_user.become_info,
}
def as_node(self):
from .node import Node
fake_node = Node()
fake_node.id = self.id
fake_node.key = self.id
fake_node.value = self.hostname
fake_node.asset = self
fake_node.is_node = False
return fake_node
def to_json(self):
return {
info = {
'id': self.id,
'hostname': self.hostname,
'ip': self.ip,
'port': self.port,
}
if self.domain and self.domain.gateway_set.all():
info["gateways"] = [d.id for d in self.domain.gateway_set.all()]
return info
def _to_secret_json(self):
"""
Ansible use it create inventory, First using asset user,
otherwise using cluster admin user
Ansible use it create inventory
Todo: May be move to ops implements it
"""
data = self.to_json()
if self.admin_user:
self.admin_user.load_specific_asset_auth(self)
admin_user = self.admin_user
data.update({
'username': admin_user.username,
@@ -148,8 +244,38 @@ class Asset(models.Model):
})
return data
def as_tree_node(self, parent_node):
from common.tree import TreeNode
icon_skin = 'file'
if self.platform.lower() == 'windows':
icon_skin = 'windows'
elif self.platform.lower() == 'linux':
icon_skin = 'linux'
data = {
'id': str(self.id),
'name': self.hostname,
'title': self.ip,
'pId': parent_node.key,
'isParent': False,
'open': False,
'iconSkin': icon_skin,
'meta': {
'type': 'asset',
'asset': {
'id': self.id,
'hostname': self.hostname,
'ip': self.ip,
'port': self.port,
'platform': self.platform,
'protocol': self.protocol,
}
}
}
tree_node = TreeNode(**data)
return tree_node
class Meta:
unique_together = ('ip', 'port')
unique_together = [('org_id', 'hostname')]
verbose_name = _("Asset")
@classmethod
@@ -157,20 +283,25 @@ class Asset(models.Model):
from random import seed, choice
import forgery_py
from django.db import IntegrityError
from .node import Node
nodes = list(Node.objects.all())
seed()
for i in range(count):
asset = cls(ip='%s.%s.%s.%s' % (i, i, i, i),
ip = [str(i) for i in random.sample(range(255), 4)]
asset = cls(ip='.'.join(ip),
hostname=forgery_py.internet.user_name(True),
admin_user=choice(AdminUser.objects.all()),
port=22,
created_by='Fake')
try:
asset.save()
if nodes and len(nodes) > 3:
_nodes = random.sample(nodes, 3)
else:
_nodes = [Node.default_node()]
asset.nodes.set(_nodes)
asset.system_users = [choice(SystemUser.objects.all()) for i in range(3)]
asset.groups = [choice(AssetGroup.objects.all()) for i in range(3)]
logger.debug('Generate fake asset : %s' % asset.ip)
except IntegrityError:
print('Error continue')
continue

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
#
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from orgs.mixins import OrgManager
from .base import AssetUser
from ..const import ASSET_USER_CONN_CACHE_KEY
__all__ = ['AuthBook']
class AuthBookQuerySet(models.QuerySet):
def latest_version(self):
return self.filter(is_latest=True)
class AuthBookManager(OrgManager):
pass
class AuthBook(AssetUser):
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
is_latest = models.BooleanField(default=False, verbose_name=_('Latest version'))
version = models.IntegerField(default=1, verbose_name=_('Version'))
objects = AuthBookManager.from_queryset(AuthBookQuerySet)()
class Meta:
verbose_name = _('AuthBook')
def _set_latest(self):
self._remove_pre_obj_latest()
self.is_latest = True
self.save()
def _get_pre_obj(self):
pre_obj = self.__class__.objects.filter(
username=self.username, asset=self.asset).latest_version().first()
return pre_obj
def _remove_pre_obj_latest(self):
pre_obj = self._get_pre_obj()
if pre_obj:
pre_obj.is_latest = False
pre_obj.save()
def _set_version(self):
pre_obj = self._get_pre_obj()
if pre_obj:
self.version = pre_obj.version + 1
else:
self.version = 1
self.save()
def set_version_and_latest(self):
self._set_version()
self._set_latest()
@property
def _conn_cache_key(self):
return ASSET_USER_CONN_CACHE_KEY.format(self.id, self.asset.id)
@property
def connectivity(self):
value = cache.get(self._conn_cache_key, self.UNKNOWN)
return value
@connectivity.setter
def connectivity(self, value):
_connectivity = self.UNKNOWN
for host in value.get('dark', {}).keys():
if host == self.asset.hostname:
_connectivity = self.UNREACHABLE
for host in value.get('contacted', []):
if host == self.asset.hostname:
_connectivity = self.REACHABLE
cache.set(self._conn_cache_key, _connectivity, 3600)
@property
def keyword(self):
return {'username': self.username, 'asset': self.asset}
def __str__(self):
return '{}@{}'.format(self.username, self.asset)

172
apps/assets/models/base.py Normal file
View File

@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
#
import os
import uuid
from hashlib import md5
import sshpubkeys
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.utils import (
get_signer, ssh_key_string_to_obj, ssh_key_gen, get_logger
)
from common.validators import alphanumeric
from orgs.mixins import OrgModelMixin
from .utils import private_key_validator
signer = get_signer()
logger = get_logger(__file__)
class AssetUser(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3)
CONNECTIVITY_CHOICES = (
(UNREACHABLE, _("Unreachable")),
(REACHABLE, _('Reachable')),
(UNKNOWN, _("Unknown")),
)
@property
def password(self):
if self._password:
return signer.unsign(self._password)
else:
return None
@password.setter
def password(self, password_raw):
# raise AttributeError("Using set_auth do that")
self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
# raise AttributeError("Using set_auth do that")
self._private_key = signer.sign(private_key_raw)
@property
def private_key_obj(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@property
def private_key_file(self):
if not self.private_key_obj:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_str = signer.unsign(self._private_key)
key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path):
self.private_key_obj.write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
@property
def public_key(self):
key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@public_key.setter
def public_key(self, public_key_raw):
# raise AttributeError("Using set_auth do that")
self._public_key = signer.sign(public_key_raw)
@property
def public_key_obj(self):
if self.public_key:
try:
return sshpubkeys.SSHKey(self.public_key)
except TabError:
pass
return None
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
self._password = signer.sign(password)
update_fields.append('_password')
if private_key:
self._private_key = signer.sign(private_key)
update_fields.append('_private_key')
if public_key:
self._public_key = signer.sign(public_key)
update_fields.append('_public_key')
if update_fields:
self.save(update_fields=update_fields)
def get_auth(self, asset=None):
pass
def load_specific_asset_auth(self, asset):
from ..backends.multi import AssetUserManager
try:
other = AssetUserManager.get(username=self.username, asset=asset)
except Exception as e:
logger.error(e, exc_info=True)
else:
self._merge_auth(other)
def _merge_auth(self, other):
if not other:
return
if other.password:
self.password = other.password
if other.public_key:
self.public_key = other.public_key
if other.private_key:
self.private_key = other.private_key
def clear_auth(self):
self._password = ''
self._private_key = ''
self._public_key = ''
self.save()
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.username
)
self.set_auth(password=password,
private_key=private_key,
public_key=public_key)
def _to_secret_json(self):
"""Push system user use it"""
return {
'name': self.name,
'username': self.username,
'password': self.password,
'public_key': self.public_key,
'private_key': self.private_key_file,
}
class Meta:
abstract = True

View File

@@ -52,7 +52,8 @@ class Cluster(models.Model):
contact=forgery_py.name.full_name(),
phone=forgery_py.address.phone(),
address=forgery_py.address.city() + forgery_py.address.street_address(),
operator=choice(['北京联通', '北京电信', 'BGP全网通']),
# operator=choice(['北京联通', '北京电信', 'BGP全网通']),
operator=choice([_('Beijing unicom'), _('Beijing telecom'), _('BGP full netcom')]),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
#
import uuid
import re
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelMixin
__all__ = [
'CommandFilter', 'CommandFilterRule'
]
class CommandFilter(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=64, verbose_name=_("Name"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
comment = models.TextField(blank=True, default='', verbose_name=_("Comment"))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
def __str__(self):
return self.name
class Meta:
verbose_name = _("Command filter")
class CommandFilterRule(OrgModelMixin):
TYPE_REGEX = 'regex'
TYPE_COMMAND = 'command'
TYPE_CHOICES = (
(TYPE_REGEX, _('Regex')),
(TYPE_COMMAND, _('Command')),
)
ACTION_DENY, ACTION_ALLOW, ACTION_UNKNOWN = range(3)
ACTION_CHOICES = (
(ACTION_DENY, _('Deny')),
(ACTION_ALLOW, _('Allow')),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules')
type = models.CharField(max_length=16, default=TYPE_COMMAND, choices=TYPE_CHOICES, verbose_name=_("Type"))
priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the higher will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)])
content = models.TextField(max_length=1024, verbose_name=_("Content"), help_text=_("One line one command"))
action = models.IntegerField(default=ACTION_DENY, choices=ACTION_CHOICES, verbose_name=_("Action"))
comment = models.CharField(max_length=64, blank=True, default='', verbose_name=_("Comment"))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
__pattern = None
class Meta:
ordering = ('-priority', 'action')
verbose_name = _("Command filter rule")
@property
def _pattern(self):
if self.__pattern:
return self.__pattern
if self.type == 'command':
regex = []
for cmd in self.content.split('\r\n'):
cmd = cmd.replace(' ', '\s+')
regex.append(r'\b{0}\b'.format(cmd))
self.__pattern = re.compile(r'{}'.format('|'.join(regex)))
else:
self.__pattern = re.compile(r'{0}'.format(self.content))
return self.__pattern
def match(self, data):
found = self._pattern.search(data)
if not found:
return self.ACTION_UNKNOWN, ''
if self.action == self.ACTION_ALLOW:
return self.ACTION_ALLOW, found.group()
else:
return self.ACTION_DENY, found.group()
def __str__(self):
return '{} % {}'.format(self.type, self.content)

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#
import uuid
import random
import paramiko
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelMixin
from .base import AssetUser
__all__ = ['Domain', 'Gateway']
class Domain(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True,
verbose_name=_('Date created'))
class Meta:
verbose_name = _("Domain")
def __str__(self):
return self.name
def has_gateway(self):
return self.gateway_set.filter(is_active=True).exists()
@property
def gateways(self):
return self.gateway_set.filter(is_active=True)
def random_gateway(self):
return random.choice(self.gateways)
class Gateway(AssetUser):
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_CHOICES = (
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
)
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
port = models.IntegerField(default=22, verbose_name=_('Port'))
protocol = models.CharField(choices=PROTOCOL_CHOICES, max_length=16, default=PROTOCOL_SSH, verbose_name=_("Protocol"))
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("Domain"))
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
def __str__(self):
return self.name
class Meta:
unique_together = [('name', 'org_id')]
verbose_name = _("Gateway")
def test_connective(self, local_port=None):
if local_port is None:
local_port = self.port
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.SSHClient()
proxy.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
proxy.connect(self.ip, port=self.port,
username=self.username,
password=self.password,
pkey=self.private_key_obj)
except(paramiko.AuthenticationException,
paramiko.BadAuthenticationType,
paramiko.SSHException) as e:
return False, str(e)
try:
sock = proxy.get_transport().open_channel(
'direct-tcpip', ('127.0.0.1', local_port), ('127.0.0.1', 0)
)
client.connect("127.0.0.1", port=local_port,
username=self.username,
password=self.password,
key_filename=self.private_key_file,
sock=sock,
timeout=5)
except (paramiko.SSHException, paramiko.ssh_exception.SSHException,
paramiko.AuthenticationException, TimeoutError) as e:
return False, str(e)
finally:
client.close()
return True, None

View File

@@ -4,9 +4,10 @@
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelMixin
class Label(models.Model):
class Label(OrgModelMixin):
SYSTEM_CATEGORY = "S"
USER_CATEGORY = "U"
CATEGORY_CHOICES = (
@@ -16,7 +17,8 @@ class Label(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_("Name"))
value = models.CharField(max_length=128, verbose_name=_("Value"))
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, default=USER_CATEGORY, verbose_name=_("Category"))
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES,
default=USER_CATEGORY, verbose_name=_("Category"))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
comment = models.TextField(blank=True, null=True, verbose_name=_("Comment"))
date_created = models.DateTimeField(
@@ -34,4 +36,4 @@ class Label(models.Model):
class Meta:
db_table = "assets_label"
unique_together = ('name', 'value')
unique_together = [('name', 'value', 'org_id')]

View File

@@ -2,33 +2,122 @@
#
import uuid
from django.db import models
from django.db import models, transaction
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from django.core.cache import cache
from orgs.mixins import OrgModelMixin
from orgs.utils import set_current_org, get_current_org
from orgs.models import Organization
__all__ = ['Node']
class Node(models.Model):
class Node(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
value = models.CharField(max_length=128, unique=True, verbose_name=_("Value"))
value = models.CharField(max_length=128, verbose_name=_("Value"))
child_mark = models.IntegerField(default=0)
date_create = models.DateTimeField(auto_now_add=True)
is_node = True
_assets_amount = None
_full_value_cache_key = '_NODE_VALUE_{}'
_assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}'
class Meta:
verbose_name = _("Node")
ordering = ['key']
def __str__(self):
return self.value
return self.full_value
def __eq__(self, other):
if not other:
return False
return self.key == other.key
def __gt__(self, other):
if self.is_root():
return True
self_key = [int(k) for k in self.key.split(':')]
other_key = [int(k) for k in other.key.split(':')]
self_parent_key = self_key[:-1]
other_parent_key = other_key[:-1]
if self_parent_key == other_parent_key:
return self.name > other.name
return self_key > other_key
def __lt__(self, other):
return not self.__gt__(other)
@property
def name(self):
return self.value
@property
def assets_amount(self):
"""
获取节点下所有资产数量速度太慢所以需要重写使用cache等方案
:return:
"""
if self._assets_amount is not None:
return self._assets_amount
cache_key = self._assets_amount_cache_key.format(self.key)
cached = cache.get(cache_key)
if cached is not None:
return cached
assets_amount = self.get_all_assets().count()
cache.set(cache_key, assets_amount, 3600)
return assets_amount
@assets_amount.setter
def assets_amount(self, value):
self._assets_amount = value
def expire_assets_amount(self):
ancestor_keys = self.get_ancestor_keys(with_self=True)
cache_keys = [self._assets_amount_cache_key.format(k) for k in ancestor_keys]
cache.delete_many(cache_keys)
@classmethod
def expire_nodes_assets_amount(cls, nodes=None):
if nodes:
for node in nodes:
node.expire_assets_amount()
return
key = cls._assets_amount_cache_key.format('*')
cache.delete_pattern(key)
@property
def full_value(self):
if self == self.__class__.root():
key = self._full_value_cache_key.format(self.key)
cached = cache.get(key)
if cached:
return cached
if self.is_root():
return self.value
else:
return '{}/{}'.format(self.value, self.parent.full_value)
parent_full_value = self.parent.full_value
value = parent_full_value + ' / ' + self.value
key = self._full_value_cache_key.format(self.key)
cache.set(key, value, 3600)
return value
def expire_full_value(self):
key = self._full_value_cache_key.format(self.key)
cache.delete_pattern(key+'*')
@classmethod
def expire_nodes_full_value(cls, nodes=None):
if nodes:
for node in nodes:
node.expire_full_value()
return
key = cls._full_value_cache_key.format('*')
cache.delete_pattern(key+'*')
@property
def level(self):
@@ -40,80 +129,184 @@ class Node(models.Model):
self.save()
return "{}:{}".format(self.key, mark)
def create_child(self, value):
child_key = self.get_next_child_key()
child = self.__class__.objects.create(key=child_key, value=value)
return child
def get_next_child_preset_name(self):
name = ugettext("New node")
values = [
child.value[child.value.rfind(' '):]
for child in self.get_children()
if child.value.startswith(name)
]
values = [int(value) for value in values if value.strip().isdigit()]
count = max(values) + 1 if values else 1
return '{} {}'.format(name, count)
def get_children(self):
return self.__class__.objects.filter(key__regex=r'{}:[0-9]+$'.format(self.key))
def create_child(self, value, _id=None):
with transaction.atomic():
child_key = self.get_next_child_key()
child = self.__class__.objects.create(id=_id, key=child_key, value=value)
return child
def get_all_children(self):
return self.__class__.objects.filter(key__startswith='{}:'.format(self.key))
def get_children(self, with_self=False):
pattern = r'^{0}$|^{0}:[0-9]+$' if with_self else r'^{0}:[0-9]+$'
return self.__class__.objects.filter(
key__regex=pattern.format(self.key)
)
def get_all_children(self, with_self=False):
pattern = r'^{0}$|^{0}:' if with_self else r'^{0}:'
return self.__class__.objects.filter(
key__regex=pattern.format(self.key)
)
def get_sibling(self, with_self=False):
key = ':'.join(self.key.split(':')[:-1])
pattern = r'^{}:[0-9]+$'.format(key)
sibling = self.__class__.objects.filter(
key__regex=pattern.format(self.key)
)
if not with_self:
sibling = sibling.exclude(key=self.key)
return sibling
def get_family(self):
children = list(self.get_all_children())
children.append(self)
return children
ancestor = self.get_ancestor()
children = self.get_all_children()
return [*tuple(ancestor), self, *tuple(children)]
def get_assets(self):
from .asset import Asset
assets = Asset.objects.filter(nodes__id=self.id)
return assets
if self.is_default_node():
assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
else:
assets = Asset.objects.filter(nodes__id=self.id)
return assets.distinct()
def get_active_assets(self):
return self.get_assets().filter(is_active=True)
def get_valid_assets(self):
return self.get_assets().valid()
def get_all_assets(self):
from .asset import Asset
pattern = r'^{0}$|^{0}:'.format(self.key)
args = []
kwargs = {}
if self.is_root():
assets = Asset.objects.all()
args.append(Q(nodes__key__regex=pattern) | Q(nodes=None))
else:
nodes = self.get_family()
assets = Asset.objects.filter(nodes__in=nodes)
kwargs['nodes__key__regex'] = pattern
assets = Asset.objects.filter(*args, **kwargs).distinct()
return assets
def get_all_active_assets(self):
return self.get_all_assets().filter(is_active=True)
def get_all_valid_assets(self):
return self.get_all_assets().valid()
def is_default_node(self):
return self.is_root() and self.key == '0'
def is_root(self):
return self.key == '0'
if self.key.isdigit():
return True
else:
return False
@property
def parent_key(self):
parent_key = ":".join(self.key.split(":")[:-1])
return parent_key
@property
def parent(self):
if self.key == "0":
return self.__class__.root()
elif not self.key.startswith("0"):
return self.__class__.root()
parent_key = ":".join(self.key.split(":")[:-1])
if self.is_root():
return self
try:
parent = self.__class__.objects.get(key=parent_key)
parent = self.__class__.objects.get(key=self.parent_key)
return parent
except Node.DoesNotExist:
return self.__class__.root()
else:
return parent
@parent.setter
def parent(self, parent):
self.key = parent.get_next_child_key()
if not self.is_node:
self.key = parent.key + ':fake'
return
children = self.get_all_children()
old_key = self.key
with transaction.atomic():
self.key = parent.get_next_child_key()
for child in children:
child.key = child.key.replace(old_key, self.key, 1)
child.save()
self.save()
@property
def ancestor(self):
if self.parent == self.__class__.root():
return [self.__class__.root()]
else:
return [self.parent, *tuple(self.parent.ancestor)]
def get_ancestor_keys(self, with_self=False):
parent_keys = []
key_list = self.key.split(":")
if not with_self:
key_list.pop()
for i in range(len(key_list)):
parent_keys.append(":".join(key_list))
key_list.pop()
return parent_keys
@property
def ancestor_with_node(self):
ancestor = self.ancestor
ancestor.insert(0, self)
def get_ancestor(self, with_self=False):
ancestor_keys = self.get_ancestor_keys(with_self=with_self)
ancestor = self.__class__.objects.filter(
key__in=ancestor_keys
).order_by('key')
return ancestor
@classmethod
def create_root_node(cls):
# 如果使用current_org 在set_current_org时会死循环
_current_org = get_current_org()
with transaction.atomic():
if not _current_org.is_real():
return cls.default_node()
set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or ['1']
key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(_current_org)
root = cls.objects.create(key=key, value=_current_org.name)
return root
@classmethod
def root(cls):
obj, created = cls.objects.get_or_create(
key='0', defaults={"key": '0', 'value': "ROOT"}
)
root = cls.objects.filter(key__regex=r'^[0-9]+$')
if root:
return root[0]
else:
return cls.create_root_node()
@classmethod
def default_node(cls):
defaults = {'value': 'Default'}
obj, created = cls.objects.get_or_create(defaults=defaults, key='1')
return obj
def as_tree_node(self):
from common.tree import TreeNode
from ..serializers import NodeSerializer
name = '{} ({})'.format(self.value, self.assets_amount)
node_serializer = NodeSerializer(instance=self)
data = {
'id': self.key,
'name': name,
'title': name,
'pId': self.parent_key,
'isParent': True,
'open': self.is_root(),
'meta': {
'node': node_serializer.data,
'type': 'node'
}
}
tree_node = TreeNode(**data)
return tree_node
@classmethod
def generate_fake(cls, count=100):
import random
for i in range(count):
node = random.choice(cls.objects.all())
node.create_child('Node {}'.format(i))

View File

@@ -2,138 +2,23 @@
# -*- coding: utf-8 -*-
#
import os
import logging
import uuid
from hashlib import md5
import sshpubkeys
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.core.validators import MinValueValidator, MaxValueValidator
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
from .utils import private_key_validator
from common.utils import get_signer
from ..const import SYSTEM_USER_CONN_CACHE_KEY
from .base import AssetUser
__all__ = ['AdminUser', 'SystemUser',]
__all__ = ['AdminUser', 'SystemUser']
logger = logging.getLogger(__name__)
signer = get_signer()
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
@property
def password(self):
if self._password:
return signer.unsign(self._password)
else:
return None
@password.setter
def password(self, password_raw):
raise AttributeError("Using set_auth do that")
# self._password = signer.sign(password_raw)
@property
def private_key(self):
if self._private_key:
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
raise AttributeError("Using set_auth do that")
# self._private_key = signer.sign(private_key_raw)
@property
def private_key_obj(self):
if self._private_key:
key_str = signer.unsign(self._private_key)
return ssh_key_string_to_obj(key_str, password=self.password)
else:
return None
@property
def private_key_file(self):
if not self.private_key_obj:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_str = signer.unsign(self._private_key)
key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
if not os.path.exists(key_path):
self.private_key_obj.write_private_key_file(key_path)
os.chmod(key_path, 0o400)
return key_path
@property
def public_key(self):
key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@property
def public_key_obj(self):
if self.public_key:
try:
return sshpubkeys.SSHKey(self.public_key)
except TabError:
pass
return None
def set_auth(self, password=None, private_key=None, public_key=None):
update_fields = []
if password:
self._password = signer.sign(password)
update_fields.append('_password')
if private_key:
self._private_key = signer.sign(private_key)
update_fields.append('_private_key')
if public_key:
self._public_key = signer.sign(public_key)
update_fields.append('_public_key')
if update_fields:
self.save(update_fields=update_fields)
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.name, password=password
)
self.set_auth(password=password,
private_key=private_key,
public_key=public_key)
def _to_secret_json(self):
"""Push system user use it"""
return {
'name': self.name,
'username': self.username,
'password': self.password,
'public_key': self.public_key,
'private_key': self.private_key_file,
}
class Meta:
abstract = True
class AdminUser(AssetUser):
"""
A privileged user that ansible can use it to push system user and so on
@@ -146,6 +31,7 @@ class AdminUser(AssetUser):
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
become_user = models.CharField(default='root', max_length=64)
_become_pass = models.CharField(default='', max_length=128)
CONNECTIVE_CACHE_KEY = '_JMS_ADMIN_USER_CONNECTIVE_{}'
def __str__(self):
return self.name
@@ -182,8 +68,26 @@ class AdminUser(AssetUser):
def assets_amount(self):
return self.get_related_assets().count()
@property
def connectivity(self):
from .asset import Asset
assets = self.get_related_assets().values_list('id', 'hostname', flat=True)
data = {
'unreachable': [],
'reachable': [],
}
for asset_id, hostname in assets:
key = Asset.CONNECTIVITY_CACHE_KEY.format(str(self.id))
value = cache.get(key, Asset.UNKNOWN)
if value == Asset.REACHABLE:
data['reachable'].append(hostname)
elif value == Asset.UNREACHABLE:
data['unreachable'].append(hostname)
return data
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
verbose_name = _("Admin user")
@classmethod
@@ -208,22 +112,39 @@ class AdminUser(AssetUser):
class SystemUser(AssetUser):
SSH_PROTOCOL = 'ssh'
RDP_PROTOCOL = 'rdp'
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet'
PROTOCOL_VNC = 'vnc'
PROTOCOL_CHOICES = (
(SSH_PROTOCOL, 'ssh'),
(RDP_PROTOCOL, 'rdp'),
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet (beta)'),
(PROTOCOL_VNC, 'vnc'),
)
LOGIN_AUTO = 'auto'
LOGIN_MANUAL = 'manual'
LOGIN_MODE_CHOICES = (
(LOGIN_AUTO, _('Automatic login')),
(LOGIN_MANUAL, _('Manually login'))
)
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
priority = models.IntegerField(default=10, verbose_name=_("Priority"))
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
priority = models.IntegerField(default=20, verbose_name=_("Priority"), validators=[MinValueValidator(1), MaxValueValidator(100)])
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True)
SYSTEM_USER_CACHE_KEY = "__SYSTEM_USER_CACHED_{}"
CONNECTIVE_CACHE_KEY = '_JMS_SYSTEM_USER_CONNECTIVE_{}'
def __str__(self):
return self.name
return '{0.name}({0.username})'.format(self)
def to_json(self):
return {
@@ -235,34 +156,94 @@ class SystemUser(AssetUser):
'auto_push': self.auto_push,
}
@property
def assets(self):
assets = set()
for node in self.nodes.all():
assets.update(set(node.get_all_assets()))
def get_related_assets(self):
assets = set(self.assets.all())
return assets
@property
def assets_connective(self):
_result = cache.get(SYSTEM_USER_CONN_CACHE_KEY.format(self.name), {})
return _result
def connectivity(self):
cache_key = self.CONNECTIVE_CACHE_KEY.format(str(self.id))
value = cache.get(cache_key, None)
if not value or 'unreachable' not in value:
return {'unreachable': [], 'reachable': []}
else:
return value
@connectivity.setter
def connectivity(self, value):
data = self.connectivity
unreachable = data['unreachable']
reachable = data['reachable']
for host in value.get('dark', {}).keys():
if host not in unreachable:
unreachable.append(host)
if host in reachable:
reachable.remove(host)
for host in value.get('contacted'):
if host not in reachable:
reachable.append(host)
if host in unreachable:
unreachable.remove(host)
cache_key = self.CONNECTIVE_CACHE_KEY.format(str(self.id))
cache.set(cache_key, data, 3600)
@property
def unreachable_assets(self):
return list(self.assets_connective.get('dark', {}).keys())
def assets_unreachable(self):
return self.connectivity.get('unreachable')
@property
def reachable_assets(self):
return self.assets_connective.get('contacted', [])
def assets_reachable(self):
return self.connectivity.get('reachable')
@property
def login_mode_display(self):
return self.get_login_mode_display()
def is_need_push(self):
if self.auto_push and self.protocol == self.__class__.SSH_PROTOCOL:
if self.auto_push and self.protocol == self.PROTOCOL_SSH:
return True
else:
return False
def set_cache(self):
cache.set(self.SYSTEM_USER_CACHE_KEY.format(self.id), self, 3600)
def expire_cache(self):
cache.delete(self.SYSTEM_USER_CACHE_KEY.format(self.id))
@property
def cmd_filter_rules(self):
from .cmd_filter import CommandFilterRule
rules = CommandFilterRule.objects.filter(
filter__in=self.cmd_filters.all()
).distinct()
return rules
def is_command_can_run(self, command):
for rule in self.cmd_filter_rules:
action, matched_cmd = rule.match(command)
if action == rule.ACTION_ALLOW:
return True, None
elif action == rule.ACTION_DENY:
return False, matched_cmd
return True, None
@classmethod
def get_system_user_by_id_or_cached(cls, sid):
cached = cache.get(cls.SYSTEM_USER_CACHE_KEY.format(sid))
if cached:
return cached
try:
system_user = cls.objects.get(id=sid)
system_user.set_cache()
return system_user
except cls.DoesNotExist:
return None
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
verbose_name = _("System user")
@classmethod
@@ -284,6 +265,3 @@ class SystemUser(AssetUser):
except IntegrityError:
print('Error continue')
continue

View File

@@ -10,15 +10,15 @@ __all__ = ['init_model', 'generate_fake']
def init_model():
from . import Cluster, SystemUser, AdminUser, AssetGroup, Asset
for cls in [Cluster, SystemUser, AdminUser, AssetGroup, Asset]:
from . import SystemUser, AdminUser, Asset
for cls in [SystemUser, AdminUser, Asset]:
if hasattr(cls, 'initial'):
cls.initial()
def generate_fake():
from . import Cluster, SystemUser, AdminUser, AssetGroup, Asset
for cls in [Cluster, SystemUser, AdminUser, AssetGroup, Asset]:
from . import SystemUser, AdminUser, Asset
for cls in [SystemUser, AdminUser, Asset]:
if hasattr(cls, 'generate_fake'):
cls.generate_fake()

View File

@@ -6,3 +6,6 @@ from .admin_user import *
from .label import *
from .system_user import *
from .node import *
from .domain import *
from .cmd_filter import *
from .asset_user import *

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