Merge pull request #3 from jumpserver/dev

update from origin
pull/140/head
Astraeux 2016-03-21 20:08:34 +08:00
commit 4b90cd9b82
13 changed files with 75 additions and 65 deletions

View File

@ -92,11 +92,20 @@ class Tty(object):
self.remote_ip = '' self.remote_ip = ''
self.login_type = login_type self.login_type = login_type
self.vim_flag = False self.vim_flag = False
self.ps1_pattern = re.compile('\[.*@.*\][\$#]\s') self.ps1_pattern = re.compile('\[?.*@.*\]?[\$#]\s')
self.vim_pattern = re.compile(r'\Wvi[m]+\s.* | \Wfg\s.*', re.X) self.vim_pattern = re.compile(r'\W?vi[m]?\s.* | \W?fg\s.*', re.X)
self.vim_data = '' self.vim_data = ''
self.stream = pyte.ByteStream() self.stream = None
self.screen = None self.screen = None
self.__init_screen_stream()
def __init_screen_stream(self):
"""
初始化虚拟屏幕和字符流
"""
self.stream = pyte.ByteStream()
self.screen = pyte.Screen(80, 24)
self.stream.attach(self.screen)
@staticmethod @staticmethod
def is_output(strings): def is_output(strings):
@ -125,12 +134,15 @@ class Tty(object):
result = match[-1].strip() result = match[-1].strip()
return result return result
def deal_command(self): def deal_command(self, data):
""" """
处理截获的命令 处理截获的命令
:param data: 要处理的命令
:return:返回最后的处理结果 :return:返回最后的处理结果
""" """
command = '' command = ''
try:
self.stream.feed(data)
# 从虚拟屏幕中获取处理后的数据 # 从虚拟屏幕中获取处理后的数据
for line in reversed(self.screen.buffer): for line in reversed(self.screen.buffer):
line_data = "".join(map(operator.attrgetter("data"), line)).strip() line_data = "".join(map(operator.attrgetter("data"), line)).strip()
@ -149,6 +161,8 @@ class Tty(object):
self.vim_flag = True self.vim_flag = True
# 虚拟屏幕清空 # 虚拟屏幕清空
self.screen.reset() self.screen.reset()
except Exception:
pass
return command return command
def get_log(self): def get_log(self):
@ -348,16 +362,15 @@ class SshTty(Tty):
# 这个是用来处理用户的复制操作 # 这个是用来处理用户的复制操作
if input_str != x: if input_str != x:
data += input_str data += input_str
self.stream.feed(data)
if self.vim_flag: if self.vim_flag:
match = self.ps1_pattern.search(self.vim_data) match = self.ps1_pattern.search(self.vim_data)
if match: if match:
self.vim_flag = False self.vim_flag = False
data = self.deal_command()[0:200] data = self.deal_command(data)[0:200]
if len(data) > 0: if len(data) > 0:
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save() TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
else: else:
data = self.deal_command()[0:200] data = self.deal_command(data)[0:200]
if len(data) > 0: if len(data) > 0:
TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save() TtyLog(log=log, datetime=datetime.datetime.now(), cmd=data).save()
data = '' data = ''
@ -393,10 +406,8 @@ class SshTty(Tty):
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size # 获取连接的隧道并设置窗口大小 Make a channel and set windows size
global channel global channel
win_size = self.get_win_size() win_size = self.get_win_size()
#self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm') # self.channel = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1], term='xterm')
self.channel = channel = transport.open_session() self.channel = channel = transport.open_session()
self.screen = pyte.Screen(win_size[1], win_size[0])
self.stream.attach(self.screen)
channel.get_pty(term='xterm', height=win_size[0], width=win_size[1]) channel.get_pty(term='xterm', height=win_size[0], width=win_size[1])
channel.invoke_shell() channel.invoke_shell()
try: try:

View File

@ -26,7 +26,7 @@ class PermSudo(models.Model):
class PermRole(models.Model): class PermRole(models.Model):
name = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=100, unique=True)
comment = models.CharField(max_length=100, null=True, blank=True, default='') comment = models.CharField(max_length=100, null=True, blank=True, default='')
password = models.CharField(max_length=100) password = models.CharField(max_length=128)
key_path = models.CharField(max_length=100) key_path = models.CharField(max_length=100)
date_added = models.DateTimeField(auto_now=True) date_added = models.DateTimeField(auto_now=True)
sudo = models.ManyToManyField(PermSudo, related_name='perm_role') sudo = models.ManyToManyField(PermSudo, related_name='perm_role')

View File

@ -182,8 +182,9 @@ def gen_resource(ob, perm=None):
info = {'hostname': asset.hostname, info = {'hostname': asset.hostname,
'ip': asset.ip, 'ip': asset.ip,
'port': asset_info.get('port', 22), 'port': asset_info.get('port', 22),
'ansible_ssh_private_key_file': role_key,
'username': role.name, 'username': role.name,
'password': CRYPTOR.decrypt(role.password) # 'password': CRYPTOR.decrypt(role.password)
} }
if os.path.isfile(role_key): if os.path.isfile(role_key):

View File

@ -519,12 +519,12 @@ def perm_role_push(request):
ret["pass_push"] = task.add_user(role.name) ret["pass_push"] = task.add_user(role.name)
ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub')) ret["key_push"] = task.push_key(role.name, os.path.join(role.key_path, 'id_rsa.pub'))
# 2. 推送账号密码 # 2. 推送账号密码 <为了安全 系统用户统一使用秘钥进行通信, 不再提供密码方式的推送>
elif password_push: # elif password_push:
ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password)) # ret["pass_push"] = task.add_user(role.name, CRYPTOR.decrypt(role.password))
# 3. 推送sudo配置文件 # 3. 推送sudo配置文件
if password_push or key_push: if key_push:
sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3) sudo_list = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3)
if sudo_list: if sudo_list:
ret['sudo'] = task.push_sudo_file([role], sudo_list) ret['sudo'] = task.push_sudo_file([role], sudo_list)

View File

@ -1,6 +1,6 @@
[base] [base]
url = http://192.168.244.129 url = http://192.168.10.165
key = i6k2zeu8x6mncl76 key = 941enj9neshd1wes
ip = 0.0.0.0 ip = 0.0.0.0
port = 80 port = 80
log = debug log = debug
@ -14,9 +14,9 @@ database = jumpserver
[mail] [mail]
mail_enable = 1 mail_enable = 1
email_host = smtp.exmail.qq.com email_host =
email_port = 25 email_port = 587
email_host_user = noreply@jumpserver.org email_host_user =
email_host_password = xxxxxxxxxx email_host_password =
email_use_tls = True email_use_tls = True

View File

@ -10,7 +10,6 @@ import os.path
import threading import threading
import re import re
import functools import functools
from django.core.signals import request_started, request_finished from django.core.signals import request_started, request_finished
import tornado.ioloop import tornado.ioloop
@ -371,9 +370,10 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
vim_data = self.term.deal_command(self.term.vim_data)[0:200] vim_data = self.term.deal_command(self.term.vim_data)[0:200]
if len(data) > 0: if len(data) > 0:
TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save() TtyLog(log=self.log, datetime=datetime.datetime.now(), cmd=vim_data).save()
vim_data = self.term.deal_command(self.term.vim_data)[0:200]
if len(vim_data) > 0:
TtyLog(log=self.log, datetime=datetime.datetime.now(), TtyLog(log=self.log, datetime=datetime.datetime.now(),
cmd=self.term.deal_command(self.term.data)[0:200]).save() cmd=vim_data).save()
self.term.vim_data = '' self.term.vim_data = ''
self.term.data = '' self.term.data = ''
self.term.input_mode = False self.term.input_mode = False
@ -412,7 +412,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
if self.term.vim_flag: if self.term.vim_flag:
self.term.vim_data += recv self.term.vim_data += recv
try: try:
self.write_message(json.dumps({'data': data})) self.write_message(data.decode('utf-8', 'replace'))
now_timestamp = time.time() now_timestamp = time.time()
self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data))) self.log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data)))
self.log_file_f.write(data) self.log_file_f.write(data)
@ -460,7 +460,7 @@ def main():
} }
tornado_app = tornado.web.Application( tornado_app = tornado.web.Application(
[ [
(r'/monitor', MonitorHandler), (r'/ws/monitor', MonitorHandler),
(r'/ws/terminal', WebTerminalHandler), (r'/ws/terminal', WebTerminalHandler),
(r'/kill', WebTerminalKillHandler), (r'/kill', WebTerminalKillHandler),
(r'/ws/exec', ExecHandler), (r'/ws/exec', ExecHandler),

View File

@ -1,3 +1,4 @@
/** /**
* Created by liuzheng on 3/3/16. * Created by liuzheng on 3/3/16.
*/ */
@ -35,13 +36,12 @@ WSSHClient.prototype.connect = function (options) {
}; };
this._connection.onmessage = function (evt) { this._connection.onmessage = function (evt) {
try{
options.onData(evt.data);
} catch (e) {
var data = JSON.parse(evt.data.toString()); var data = JSON.parse(evt.data.toString());
if (data.error !== undefined) {
options.onError(data.error); options.onError(data.error);
} }
else {
options.onData(data.data);
}
}; };
this._connection.onclose = function (evt) { this._connection.onclose = function (evt) {

View File

@ -136,7 +136,7 @@
var protocol = 'ws://'; var protocol = 'ws://';
} }
var endpoint = protocol + document.URL.match(RegExp('//(.*?)/'))[1] + '/monitor'; var endpoint = protocol + document.URL.match(RegExp('//(.*?)/'))[1] + '/ws/monitor';
var file_path = obj.attr('file_path'); var file_path = obj.attr('file_path');
var socket = new WebSocket(endpoint + '?file_path=' + file_path); var socket = new WebSocket(endpoint + '?file_path=' + file_path);

File diff suppressed because one or more lines are too long

View File

@ -104,6 +104,10 @@ $('#roleForm').validator({
ok: "", ok: "",
msg: {required: "系统用户名称必填"} msg: {required: "系统用户名称必填"}
}, },
"role_password": {
rule: "length[0~64]",
tip: "系统密码"
},
"role_key": { "role_key": {
rule: "check_begin", rule: "check_begin",
ok: "", ok: "",

View File

@ -105,12 +105,16 @@ $('#roleForm').validator({
tip: "输入系统用户名称", tip: "输入系统用户名称",
ok: "", ok: "",
msg: {required: "系统用户名称必填"} msg: {required: "系统用户名称必填"}
},
"role_password": {
rule: "length[0~64]",
tip: "系统密码"
}, },
"role_key": { "role_key": {
rule: "check_begin", rule: "check_begin",
ok: "", ok: "",
empty: true empty: true
}, }
}, },
valid: function(form) { valid: function(form) {

View File

@ -74,16 +74,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label for="j_group" class="col-sm-2 control-label">使用密码</label>
<div class="col-sm-1">
<div class="radio i-checks">
<label>
<input type="checkbox" value="1" id="use_password" name="use_password">
</label>
</div>
</div>
</div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">