Unverified Commit e1c9e93c authored by liuzheng712's avatar liuzheng712

Merge branch 'dev' of bitbucket.org:jumpserver/coco into dev

parents afba37f4 1c8dfae8
......@@ -205,6 +205,7 @@ class Coco:
for client in self.clients:
self.remove_client(client)
time.sleep(1)
self.heartbeat()
self.stop_evt.set()
self.sshd.shutdown()
self.httpd.shutdown()
......@@ -234,4 +235,4 @@ class Coco:
with self.lock:
logger.info("Remove session: {}".format(session))
self.sessions.remove(session)
self.service.finish_session(session.id)
self.service.finish_session(session.to_json())
......@@ -4,7 +4,9 @@
BACKSPACE_CHAR = {b'\x08': b'\x08\x1b[K', b'\x7f': b'\x08\x1b[K'}
ENTER_CHAR = [b'\r', b'\n', b'\r\n']
ENTER_CHAR_ORDER = [ord(b'\r'), ord(b'\n')]
UNSUPPORTED_CHAR = {b'\x15': 'Ctrl-U', b'\x0c': 'Ctrl-L', b'\x05': 'Ctrl-E'}
CLEAR_CHAR = b'\x1b[H\x1b[2J'
BELL_CHAR = b'\x07'
NEW_LINE = b'\r\n'
RZ_PROTOCOL_CHAR = b'**\x18B0900000000a87c\r\x8a\x11'
......@@ -80,6 +80,7 @@ class InteractiveServer:
input_data = []
parser = TtyIOParser()
self.client.send(wr(prompt, before=1, after=0))
while True:
data = self.client.recv(10)
logger.debug(data)
......@@ -113,7 +114,7 @@ class InteractiveServer:
# handle shell expect
multi_char_with_enter = False
if len(data) > 1 and data[-1] in char.ENTER_CHAR:
if len(data) > 1 and data[-1] in char.ENTER_CHAR_ORDER:
self.client.send(data)
input_data.append(data[:-1])
multi_char_with_enter = True
......@@ -182,7 +183,7 @@ class InteractiveServer:
self.get_user_asset_groups()
if len(self.asset_groups) == 0:
self.client.send(warning(_("Nothing")))
self.client.send(warning(_("")))
return
fake_group = AssetGroup(name=_("Name"), assets_amount=_("Assets"), comment=_("Comment"))
......@@ -196,11 +197,11 @@ class InteractiveServer:
self.client.send(title(header.format(fake_group, "ID")))
for index, group in enumerate(self.asset_groups, 1):
self.client.send(wr(line.format(group, index)))
self.client.send(wr(_("Total: {}").format(len(self.asset_groups)), before=1))
self.client.send(wr(_("总共: {}").format(len(self.asset_groups)), before=1))
def display_group_assets(self, _id):
if _id > len(self.asset_groups) or _id <= 0:
self.client.send(wr(warning("No matched group, select again")))
self.client.send(wr(warning("没有匹配分组,请重新输入")))
self.display_asset_groups()
return
......@@ -222,7 +223,7 @@ class InteractiveServer:
self.client.send(wr(title(header.format(fake_asset, "ID"))))
for index, asset in enumerate(self.search_result, 1):
self.client.send(wr(line.format(asset, index)))
self.client.send(wr(_("Total: {} Matched: {}").format(
self.client.send(wr(_("总共: {} 匹配: {}").format(
len(self.assets), len(self.search_result)), before=1)
)
......@@ -264,7 +265,7 @@ class InteractiveServer:
return None
while True:
self.client.send(wr(_("Choose one to login: "), after=1))
self.client.send(wr(_("选择一个登陆: "), after=1))
self.display_system_users(system_users)
opt = self.get_option("ID> ")
if opt.isdigit() and len(system_users) > int(opt):
......@@ -283,14 +284,18 @@ class InteractiveServer:
def search_and_proxy(self, opt):
self.search_assets(opt)
if self.search_result and len(self.search_result) == 1:
self.proxy(self.search_result[0])
asset = self.search_result[0]
if asset.platform == "Windows":
self.client.send(warning(_("终端不支持登录windows, 请使用web terminal访问")))
return
self.proxy(asset)
else:
self.display_search_result()
def proxy(self, asset):
system_user = self.choose_system_user(asset.system_users_granted)
if system_user is None:
self.client.send(_("No user"))
self.client.send(_("没有系统用户"))
return
forwarder = ProxyServer(self.app, self.client)
forwarder.proxy(asset, system_user)
......
......@@ -22,6 +22,18 @@ class Request:
self.date_start = datetime.datetime.now()
class SizedList(list):
def __init__(self, maxsize=0):
self.maxsize = maxsize
self.size = 0
super().__init__()
def append(self, b):
if self.maxsize == 0 or self.size < self.maxsize:
super().append(b)
self.size += len(b)
class Client:
"""
Client is the request client. Nothing more to say
......@@ -78,8 +90,8 @@ class Server:
self.recv_bytes = 0
self.stop_evt = threading.Event()
self.input_data = []
self.output_data = []
self.input_data = SizedList(maxsize=1024)
self.output_data = SizedList(maxsize=1024)
self._in_input_state = True
self._input_initial = False
self._in_vim_state = False
......@@ -101,7 +113,7 @@ class Server:
else:
return None
def send(self, b):
def parse(self, b):
if isinstance(b, str):
b = b.encode("utf-8")
if not self._input_initial:
......@@ -118,10 +130,14 @@ class Server:
self._input, self._output,
"#" * 30 + " End " + "#" * 30,
))
if self._input:
self.session.put_command(self._input, self._output)
del self.input_data[:]
del self.output_data[:]
self._in_input_state = True
def send(self, b):
self.parse(b)
return self.chan.send(b)
def recv(self, size):
......@@ -136,7 +152,7 @@ class Server:
def close(self):
logger.info("Closed server {}".format(self))
self.send(b'')
self.parse(b'')
self.chan.close()
self.stop_evt.set()
self.chan.close()
......@@ -149,10 +165,14 @@ class Server:
return False
def _parse_output(self):
if not self.output_data:
return ''
parser = utils.TtyIOParser()
return parser.parse_output(self.output_data)
def _parse_input(self):
if not self.input_data or self.input_data[0] == char.RZ_PROTOCOL_CHAR:
return
parser = utils.TtyIOParser()
return parser.parse_input(self.input_data)
......@@ -231,3 +251,7 @@ class WSProxy:
self.child.close()
self.ws.logout(self.connection)
logger.debug("Proxy {} closed".format(self))
......@@ -92,7 +92,7 @@ class ProxyServer:
timeout=TIMEOUT, compress=True, auth_timeout=10,
look_for_keys=False
)
except (paramiko.AuthenticationException, paramiko.BadAuthenticationType):
except (paramiko.AuthenticationException, paramiko.BadAuthenticationType, SSHException):
admins = self.app.config['ADMINS'] or 'administrator'
self.client.send(warning(wr(
"Authenticate with server failed, contact {}".format(admins),
......
......@@ -28,6 +28,7 @@ class Session:
self._command_recorder = command_recorder
self._replay_recorder = replay_recorder
self.server.set_session(self)
self.date_last_active = datetime.datetime.utcnow()
def add_watcher(self, watcher, silent=False):
"""
......@@ -129,6 +130,8 @@ class Session:
logger.info(msg)
self.close()
break
self.date_last_active = datetime.datetime.utcnow()
for watcher in [self.client] + self._watchers + self._sharers:
watcher.send(data)
elif sock == self.client:
......@@ -171,6 +174,7 @@ class Session:
"login_from": "ST",
"remote_addr": self.client.addr[0],
"is_finished": True if self.stop_evt.is_set() else False,
"date_last_active": self.date_last_active.strftime("%Y-%m-%d %H:%M:%S") + " +0000",
"date_start": self.date_created.strftime("%Y-%m-%d %H:%M:%S") + " +0000",
"date_end": self.date_end.strftime("%Y-%m-%d %H:%M:%S") + " +0000" if self.date_end else None
}
......
......@@ -289,4 +289,17 @@ def get_logger(file_name):
return logging.getLogger('coco.'+file_name)
zh_pattern = re.compile(u'[\u4e00-\u9fa5]+')
def len_display(s):
length = 0
for i in s:
if zh_pattern.match(i):
length += 2
else:
length += 1
return length
ugettext = _gettext()
......@@ -30,5 +30,5 @@ tornado==4.5.2
urllib3==1.22
wcwidth==0.1.7
werkzeug==0.12.2
jumpserver-python-sdk==0.0.30
jumpserver-python-sdk==0.0.31
jms-es-sdk
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