Commit 026713de authored by BaiJiangJie's avatar BaiJiangJie Committed by 老广

[Update] 使用分页展示用户授权资产 (#118)

* [Update] 分页显示用户被授权的资产

* [Update] 修改分页展示资产列表为通用的分页展示结果列表

* [Update] 修改在显示资产时,输入id直接登录

* [Update] 修改分页显示page_size随窗口动态变化

* [Update] 删除之前异步获取用户资产的代码

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

* [Update] 用户授权资产分页展示-删除注释,添加翻译内容
parent 4f53020d
......@@ -5,6 +5,8 @@
import socket
import threading
import os
import math
import time
from . import char
from .config import config
......@@ -17,19 +19,34 @@ from .proxy import ProxyServer
logger = get_logger(__file__)
PAGE_DOWN = 'down'
PAGE_UP = 'up'
BACK = 'back'
PROXY = 'proxy'
class InteractiveServer:
_sentinel = object()
def __init__(self, client):
self.client = client
self.assets = None
self.closed = False
self._search_result = None
self.nodes = None
self.get_user_assets_async()
self.offset = 0
self.limit = 100
self.assets_list = []
self.finish = False
self.page = 1
self.total_assets = 0
self.total_count = 0 # 分页展示中用来存放数目总条数
self.get_user_assets_paging_async()
self.get_user_nodes_async()
@property
def page_size(self):
return self.client.request.meta['height'] - 8
@property
def search_result(self):
if self._search_result:
......@@ -101,38 +118,36 @@ class InteractiveServer:
self.search_and_proxy(opt)
def search_assets(self, q):
if self.assets is None:
self.get_user_assets()
if not self.finish:
assets = app_service.get_search_user_granted_assets(self.client.user, q)
return assets
assets = self.assets_list
result = []
# 所有的
if q in ('', None):
result = self.assets
# 用户输入的是数字,可能想使用id唯一键搜索
elif q.isdigit() and self.search_result and \
len(self.search_result) >= int(q):
result = [self.search_result[int(q) - 1]]
result = assets
# 全匹配到则直接返回全匹配的
if len(result) == 0:
_result = [asset for asset in self.assets
_result = [asset for asset in assets
if is_obj_attr_eq(asset, q)]
if len(_result) == 1:
result = _result
# 最后模糊匹配
if len(result) == 0:
result = [asset for asset in self.assets
result = [asset for asset in assets
if is_obj_attr_has(asset, q)]
self.search_result = result
return result
def display_assets(self):
"""
Display user all assets
:return:
"""
self.search_and_display('')
self.display_result_paging(self.assets_list)
def display_nodes(self):
if self.nodes is None:
......@@ -163,8 +178,8 @@ class InteractiveServer:
self.display_nodes()
return
self.search_result = self.nodes[_id - 1].assets_granted
self.display_search_result()
assets = self.nodes[_id - 1].assets_granted
self.display_result_paging(assets)
def display_search_result(self):
sort_by = config["ASSET_LIST_SORT_BY"]
......@@ -191,13 +206,15 @@ class InteractiveServer:
asset.system_users_name_list, asset.comment
]
self.client.send(wr(format_with_zh(size_list, *data)))
self.client.send(wr(_("Total: {} Match: {}").format(
len(self.assets), len(self.search_result)), before=1)
total_page = math.ceil(self.total_count/self.page_size)
self.client.send(wr(title(_("Page: {}, Count: {}, Total Page: {}, Total Count: {}").format(
self.page, len(self.search_result), total_page, self.total_count)), before=1)
)
def search_and_display(self, q):
self.search_assets(q)
self.display_search_result()
assets = self.search_assets(q)
self.display_result_paging(assets)
def get_user_nodes(self):
self.nodes = app_service.get_user_asset_groups(self.client.user)
......@@ -217,14 +234,24 @@ class InteractiveServer:
asset.system_users_granted = system_users_cleaned
return assets
def get_user_assets(self):
self.assets = app_service.get_user_assets(self.client.user)
logger.debug("Get user {} assets total: {}".format(
self.client.user, len(self.assets))
)
def get_user_assets_async(self):
thread = threading.Thread(target=self.get_user_assets)
def get_user_assets_paging(self):
while not self.closed:
assets, total = app_service.get_user_assets_paging(
self.client.user, offset=self.offset, limit=self.limit
)
logger.info('Get user assets paging async: {}'.format(len(assets)))
if not assets:
logger.info('Get user assets paging async finished.')
self.finish = True
return
if not self.total_assets:
self.total_assets = total
self.total_count = total
self.assets_list.extend(assets)
self.offset += self.limit
def get_user_assets_paging_async(self):
thread = threading.Thread(target=self.get_user_assets_paging)
thread.start()
def choose_system_user(self, system_users):
......@@ -251,9 +278,9 @@ class InteractiveServer:
self.client.send(wr("{} {}".format(index, system_user.name)))
def search_and_proxy(self, opt):
self.search_assets(opt)
if self.search_result and len(self.search_result) == 1:
asset = self.search_result[0]
assets = self.search_assets(opt)
if assets and len(assets) == 1:
asset = assets[0]
self.search_result = None
if asset.platform == "Windows":
self.client.send(warning(
......@@ -263,7 +290,102 @@ class InteractiveServer:
return
self.proxy(asset)
else:
self.display_search_result()
self.display_result_paging(assets)
def display_result_paging(self, result_list):
if result_list is self.assets_list:
self.total_count = self.total_assets
else:
if len(result_list) == 0:
return
self.total_count = len(result_list)
action = PAGE_DOWN
gen_result = self.get_result_page_down_or_up(result_list)
while True:
try:
page, result = gen_result.send(action)
except TypeError as e:
page, result = next(gen_result)
logger.info(e)
except StopIteration:
logger.info('StopIteration')
self.display_banner()
return None
self.display_result_of_page(page, result)
action = self.get_user_action()
def get_result_page_down_or_up(self, result_list):
left = 0
page = 1
page_up_size = 0 # 记录上一页大小
while True:
right = left + self.page_size
result = result_list[left:right]
if not result and (result_list is self.assets_list) and self.finish:
# 上一页已经是最后一页, 还是展示最后一页(展示上一页)
left -= page_up_size
page -= 1
continue
elif not result and (result_list is not self.assets_list):
# 上一页已经是最后一页, 还是展示最后一页(展示上一页)
left -= page_up_size
page -= 1
continue
elif not result and (result_list is self.assets_list) and not self.finish:
# 还有下一页(暂时没有加载完),需要等待
time.sleep(1)
continue
else:
# 其他4中情况,返回assets
action = yield (page, result)
if action == BACK:
return None, None
elif action == PAGE_UP:
if page <= 1:
# 已经是第一页了
page = 1
left = 0
else:
page -= 1
left -= self.page_size
else:
# PAGE_DOWN
page += 1
left += len(result)
page_up_size = len(result)
def display_result_of_page(self, page, result):
self.client.send(char.CLEAR_CHAR)
self.page = page
self.search_result = result
self.display_search_result()
self.display_prompt_of_page()
def display_prompt_of_page(self):
self.client.send(wr(_('Tips: Enter the asset ID and log directly into the asset.'), before=1))
prompt_page_up = _("Page up: P/p")
prompt_page_down = _("Page down: Enter|N/n")
prompt_exit = _("BACK: B/b")
prompts = [prompt_page_up, prompt_page_down, prompt_exit]
prompt = '\t'.join(prompts)
self.client.send(wr(prompt, before=1))
def get_user_action(self):
opt = net_input(self.client, prompt=':')
if opt in ('p', 'P'):
return PAGE_UP
elif opt in ('B', 'b'):
return BACK
elif opt.isdigit() and self.search_result and 0 < int(opt) <= len(self.search_result):
self.proxy(self.search_result[int(opt)-1])
return BACK
else:
# PAGE_DOWN
return PAGE_DOWN
def proxy(self, asset):
system_user = self.choose_system_user(asset.system_users_granted)
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-10 15:22+0800\n"
"POT-Creation-Date: 2018-10-30 10:37+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/en/LC\n"
......@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:141
#: coco/app.py:135
msgid "Connect idle more than {} minutes, disconnect"
msgstr ""
#: coco/interactive.py:61
#: coco/interactive.py:78
#, python-brace-format
msgid ""
"\n"
......@@ -28,115 +28,135 @@ msgid ""
"{end}{R}{R}"
msgstr ""
#: coco/interactive.py:63
#: coco/interactive.py:80
#, python-brace-format
msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
"Comment{end} to search login(if unique).{R}"
msgstr ""
#: coco/interactive.py:64
#: coco/interactive.py:81
#, python-brace-format
msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
"search, such as: /ip.{R}"
msgstr ""
#: coco/interactive.py:65
#: coco/interactive.py:82
#, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr ""
#: coco/interactive.py:66
#: coco/interactive.py:83
#, python-brace-format
msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr ""
#: coco/interactive.py:67
#: coco/interactive.py:84
#, python-brace-format
msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under "
"the node, such as g1.{R}"
msgstr ""
#: coco/interactive.py:68
#: coco/interactive.py:85
#, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr ""
#: coco/interactive.py:69
#: coco/interactive.py:86
#, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr ""
#: coco/interactive.py:70
#: coco/interactive.py:87
#, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr ""
#: coco/interactive.py:142
#: coco/interactive.py:157
msgid "No"
msgstr ""
#: coco/interactive.py:149
#: coco/interactive.py:164
msgid "Name"
msgstr ""
#: coco/interactive.py:149
#: coco/interactive.py:164
msgid "Assets"
msgstr ""
#: coco/interactive.py:155
#: coco/interactive.py:170
msgid "Total: {}"
msgstr ""
#: coco/interactive.py:161
#: coco/interactive.py:176
msgid "There is no matched node, please re-enter"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "ID"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "Hostname"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "IP"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "LoginAs"
msgstr ""
#: coco/interactive.py:186
#: coco/interactive.py:201
msgid "Comment"
msgstr ""
#: coco/interactive.py:194
msgid "Total: {} Match: {}"
#: coco/interactive.py:211
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr ""
#: coco/interactive.py:237
#: coco/interactive.py:264
msgid "Select a login:: "
msgstr ""
#: coco/interactive.py:260
#: coco/interactive.py:287
msgid ""
"Terminal does not support login Windows, please use web terminal to access"
msgstr ""
#: coco/interactive.py:271
#: coco/interactive.py:369
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr ""
#: coco/interactive.py:370
msgid "Page up: P/p"
msgstr ""
#: coco/interactive.py:371
msgid "Page down: Enter|N/n"
msgstr ""
#: coco/interactive.py:372
msgid "BACK: B/b"
msgstr ""
#: coco/interactive.py:393
msgid "No system user"
msgstr ""
#: coco/proxy.py:88
#: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission"
msgstr ""
#: coco/proxy.py:130
#: coco/proxy.py:131
msgid "Connecting to {}@{} {:.1f}"
msgstr ""
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-10 15:22+0800\n"
"POT-Creation-Date: 2018-10-30 10:37+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/zh\n"
......@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:141
#: coco/app.py:135
msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:61
#: coco/interactive.py:78
#, python-brace-format
msgid ""
"\n"
......@@ -30,7 +30,7 @@ msgstr ""
"\n"
"{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}"
#: coco/interactive.py:63
#: coco/interactive.py:80
#, python-brace-format
msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
......@@ -39,7 +39,7 @@ msgstr ""
"{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进"
"行搜索登录(如果唯一).{R}"
#: coco/interactive.py:64
#: coco/interactive.py:81
#, python-brace-format
msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
......@@ -48,104 +48,127 @@ msgstr ""
"{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. "
"如: /ip{R}"
#: coco/interactive.py:65
#: coco/interactive.py:82
#, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}"
#: coco/interactive.py:66
#: coco/interactive.py:83
#, python-brace-format
msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}"
#: coco/interactive.py:67
#: coco/interactive.py:84
#, python-brace-format
msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under "
"the node, such as g1.{R}"
msgstr "{T}5) 输入 {green}g{end} + {green}组ID{end} 显示节点下主机. 如: g1{R}"
#: coco/interactive.py:68
#: coco/interactive.py:85
#, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}"
#: coco/interactive.py:69
#: coco/interactive.py:86
#, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "{T}7) 输入 {green}h{end} 帮助.{R}"
#: coco/interactive.py:70
#: coco/interactive.py:87
#, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
#: coco/interactive.py:142
#: coco/interactive.py:157
msgid "No"
msgstr "无"
#: coco/interactive.py:149
#: coco/interactive.py:164
msgid "Name"
msgstr "名称"
#: coco/interactive.py:149
#: coco/interactive.py:164
msgid "Assets"
msgstr "资产"
#: coco/interactive.py:155
#: coco/interactive.py:170
msgid "Total: {}"
msgstr "总共: {}"
#: coco/interactive.py:161
#: coco/interactive.py:176
msgid "There is no matched node, please re-enter"
msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "ID"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "Hostname"
msgstr "主机名"
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "IP"
msgstr ""
#: coco/interactive.py:172
#: coco/interactive.py:187
msgid "LoginAs"
msgstr "登录用户"
#: coco/interactive.py:186
#: coco/interactive.py:201
msgid "Comment"
msgstr "备注"
#: coco/interactive.py:194
msgid "Total: {} Match: {}"
msgstr "总共: {} 匹配: {}"
#: coco/interactive.py:211
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
#: coco/interactive.py:237
#: coco/interactive.py:264
msgid "Select a login:: "
msgstr "选择一个登录:"
#: coco/interactive.py:260
#: coco/interactive.py:287
msgid ""
"Terminal does not support login Windows, please use web terminal to access"
msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:271
#: coco/interactive.py:369
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "提示: 输入资产ID,直接登录资产."
#: coco/interactive.py:370
msgid "Page up: P/p"
msgstr "上一页: P/p"
#: coco/interactive.py:371
msgid "Page down: Enter|N/n"
msgstr "下一页: Enter|N/n"
#: coco/interactive.py:372
msgid "BACK: B/b"
msgstr "返回: B/b"
#: coco/interactive.py:393
msgid "No system user"
msgstr "没有系统用户"
#: coco/proxy.py:88
#: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission"
msgstr "没有权限"
#: coco/proxy.py:130
#: coco/proxy.py:131
msgid "Connecting to {}@{} {:.1f}"
msgstr "开始连接到 {}@{} {:.1f}"
#: coco/session.py:143
msgid "Terminated by administrator"
msgstr "被管理员中断"
#~ msgid "Total: {} Match: {}"
#~ msgstr "总共: {} 匹配: {}"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment