Commit 98f9e632 authored by ibuler's avatar ibuler

Merge pull request #170 from jumpserver/TermLogRecorder

Term log recorder, 需要数据库增加filename字段
parents 6e7e4d47 8cd6d23f
......@@ -33,6 +33,7 @@ from jumpserver.settings import LOG_DIR
from jperm.ansible_api import MyRunner
# from jlog.log_api import escapeString
from jlog.models import ExecLog, FileLog
from jlog.views import TermLogRecorder
login_user = get_object(User, username=getpass.getuser())
try:
......@@ -299,6 +300,8 @@ class SshTty(Tty):
使用paramiko模块的channel,连接后端,进入交互式
"""
log_file_f, log_time_f, log = self.get_log()
termlog = TermLogRecorder(User.objects.get(id=self.user.id))
termlog.setid(log.id)
old_tty = termios.tcgetattr(sys.stdin)
pre_timestamp = time.time()
data = ''
......@@ -335,6 +338,8 @@ class SshTty(Tty):
if msg.errno == errno.EAGAIN:
continue
now_timestamp = time.time()
termlog.write(x)
termlog.recoder = False
log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x)))
log_time_f.flush()
log_file_f.write(x)
......@@ -355,6 +360,7 @@ class SshTty(Tty):
x = os.read(sys.stdin.fileno(), 4096)
except OSError:
pass
termlog.recoder = True
input_mode = True
input_str += x
if str(x) in ['\r', '\n', '\r\n']:
......@@ -387,6 +393,8 @@ class SshTty(Tty):
log_file_f.write('End time is %s' % datetime.datetime.now())
log_file_f.close()
log_time_f.close()
termlog.save()
log.filename = termlog.filename
log.is_finished = True
log.end_time = datetime.datetime.now()
log.save()
......
from django.db import models
from juser.models import User
import time
class Log(models.Model):
......@@ -11,6 +13,20 @@ class Log(models.Model):
pid = models.IntegerField()
is_finished = models.BooleanField(default=False)
end_time = models.DateTimeField(null=True)
filename = models.CharField(max_length=40)
'''
add by liuzheng
'''
# userMM = models.ManyToManyField(User)
# logPath = models.TextField()
# filename = models.CharField(max_length=40)
# logPWD = models.TextField() # log zip file's
# nick = models.TextField(null=True) # log's nick name
# log = models.TextField(null=True)
# history = models.TextField(null=True)
# timestamp = models.IntegerField(default=int(time.time()))
# datetimestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.log_path
......@@ -47,3 +63,13 @@ class FileLog(models.Model):
datetime = models.DateTimeField(auto_now=True)
class TermLog(models.Model):
user = models.ManyToManyField(User)
logPath = models.TextField()
filename = models.CharField(max_length=40)
logPWD = models.TextField() # log zip file's
nick = models.TextField(null=True) # log's nick name
log = models.TextField(null=True)
history = models.TextField(null=True)
timestamp = models.IntegerField(default=int(time.time()))
datetimestamp = models.DateTimeField(auto_now_add=True)
This diff is collapsed.
[base]
url = http://192.168.10.165
url = http://127.0.0.1
key = 941enj9neshd1wes
ip = 0.0.0.0
port = 80
port = 8000
log = debug
[db]
......
......@@ -35,8 +35,10 @@ except ImportError:
os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings'
from jumpserver.settings import IP, PORT
define("port", default=PORT, help="run on the given port", type=int)
define("host", default=IP, help="run port on given host", type=str)
from jlog.views import TermLogRecorder
def django_request_support(func):
......@@ -46,6 +48,7 @@ def django_request_support(func):
response = func(*args, **kwargs)
request_finished.send_robust(func)
return response
return _deco
......@@ -63,6 +66,7 @@ def require_auth(role='user'):
logger.debug('Websocket: session: %s' % session)
if session and datetime.datetime.now() < session.expire_date:
user_id = session.get_decoded().get('_auth_user_id')
request.user_id = user_id
user = get_object(User, id=user_id)
if user:
logger.debug('Websocket: user [ %s ] request websocket' % user.username)
......@@ -82,6 +86,7 @@ def require_auth(role='user'):
logger.warning('Websocket: Request auth failed.')
return _deco2
return _deco
......@@ -101,7 +106,7 @@ class EventHandler(ProcessEvent):
self.client = client
def process_IN_MODIFY(self, event):
self.client.write_message(f.read())
self.client.write_message(f.read().decode('utf-8', 'replace'))
def file_monitor(path='.', client=None):
......@@ -311,6 +316,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
role_name = self.get_argument('role', 'sb')
asset_id = self.get_argument('id', 9999)
asset = get_object(Asset, id=asset_id)
self.termlog = TermLogRecorder(User.objects.get(id=self.user_id))
if asset:
roles = user_have_perm(self.user, asset)
logger.debug(roles)
......@@ -361,6 +367,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
jsondata.get('data').get('resize').get('rows', 24)
)
elif jsondata.get('data'):
self.termlog.recoder = True
self.term.input_mode = True
if str(jsondata['data']) in ['\r', '\n', '\r\n']:
if self.term.vim_flag:
......@@ -384,12 +391,15 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
def on_close(self):
logger.debug('Websocket: Close request')
print self.termlog.CMD
self.termlog.save()
if self in WebTerminalHandler.clients:
WebTerminalHandler.clients.remove(self)
try:
self.log_file_f.write('End time is %s' % datetime.datetime.now())
self.log.is_finished = True
self.log.end_time = datetime.datetime.now()
self.log.filename = self.termlog.filename
self.log.save()
self.log_time_f.close()
self.ssh.close()
......@@ -400,6 +410,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
def forward_outbound(self):
self.log_file_f, self.log_time_f, self.log = self.term.get_log()
self.id = self.log.id
self.termlog.setid(self.id)
try:
data = ''
pre_timestamp = time.time()
......@@ -414,8 +425,10 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
self.term.vim_data += recv
try:
self.write_message(data.decode('utf-8', 'replace'))
self.termlog.write(data)
self.termlog.recoder = False
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)
pre_timestamp = now_timestamp
self.log_file_f.flush()
......@@ -429,6 +442,24 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
pass
# class MonitorHandler(WebTerminalHandler):
# @django_request_support
# @require_auth('user')
# def open(self):
# try:
# self.returnlog = TermLogRecorder.loglist[self.get_argument('id')]
# self.returnlog.write_message = self.write_message
# except:
# self.write_message('Log is None')
# self.close()
#
# def on_message(self, message):
# pass
#
# def on_close(self):
# self.close()
class Application(tornado.web.Application):
def __init__(self):
handlers = [
......@@ -475,6 +506,7 @@ def main():
tornado.ioloop.IOLoop.instance().start()
if __name__ == '__main__':
# tornado.options.parse_command_line()
# app = Application()
......
/*
AngularJS v1.2.5
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(h,e,A){'use strict';function u(w,q,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,n){function y(){l&&(l.$destroy(),l=null);g&&(k.leave(g),g=null)}function v(){var b=w.current&&w.current.locals;if(b&&b.$template){var b=a.$new(),f=w.current;g=n(b,function(d){k.enter(d,null,g||c,function(){!e.isDefined(t)||t&&!a.$eval(t)||q()});y()});l=f.scope=b;l.$emit("$viewContentLoaded");l.$eval(h)}else y()}var l,g,t=b.autoscroll,h=b.onload||"";a.$on("$routeChangeSuccess",
v);v()}}}function z(e,h,k){return{restrict:"ECA",priority:-400,link:function(a,c){var b=k.current,f=b.locals;c.html(f.$template);var n=e(c.contents());b.controller&&(f.$scope=a,f=h(b.controller,f),b.controllerAs&&(a[b.controllerAs]=f),c.data("$ngControllerController",f),c.children().data("$ngControllerController",f));n(a)}}}h=e.module("ngRoute",["ng"]).provider("$route",function(){function h(a,c){return e.extend(new (e.extend(function(){},{prototype:a})),c)}function q(a,e){var b=e.caseInsensitiveMatch,
f={originalPath:a,regexp:a},h=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g,function(a,e,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",b?"i":"");return f}var k={};this.when=function(a,c){k[a]=e.extend({reloadOnSearch:!0},c,a&&q(a,c));if(a){var b="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[b]=e.extend({redirectTo:a},
q(b,c))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,c,b,f,n,q,v,l){function g(){var d=t(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!x)m.params=d.params,e.copy(m.params,b),a.$broadcast("$routeUpdate",m);else if(d||m)x=!1,a.$broadcast("$routeChangeStart",d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?
c.path(u(d.redirectTo,d.params)).search(d.params).replace():c.url(d.redirectTo(d.pathParams,c.path(),c.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),c,b;e.forEach(a,function(d,c){a[c]=e.isString(d)?n.get(d):n.invoke(d)});e.isDefined(c=d.template)?e.isFunction(c)&&(c=c(d.params)):e.isDefined(b=d.templateUrl)&&(e.isFunction(b)&&(b=b(d.params)),b=l.getTrustedResourceUrl(b),e.isDefined(b)&&(d.loadedTemplateUrl=b,c=q.get(b,{cache:v}).then(function(a){return a.data})));
e.isDefined(c)&&(a.$template=c);return f.all(a)}}).then(function(c){d==r.current&&(d&&(d.locals=c,e.copy(d.params,b)),a.$broadcast("$routeChangeSuccess",d,m))},function(c){d==r.current&&a.$broadcast("$routeChangeError",d,m,c)})}function t(){var a,b;e.forEach(k,function(f,k){var p;if(p=!b){var s=c.path();p=f.keys;var l={};if(f.regexp)if(s=f.regexp.exec(s)){for(var g=1,q=s.length;g<q;++g){var n=p[g-1],r="string"==typeof s[g]?decodeURIComponent(s[g]):s[g];n&&r&&(l[n.name]=r)}p=l}else p=null;else p=null;
p=a=p}p&&(b=h(f,{params:e.extend({},c.search(),a),pathParams:a}),b.$$route=f)});return b||k[null]&&h(k[null],{params:{},pathParams:{}})}function u(a,c){var b=[];e.forEach((a||"").split(":"),function(a,d){if(0===d)b.push(a);else{var e=a.match(/(\w+)(.*)/),f=e[1];b.push(c[f]);b.push(e[2]||"");delete c[f]}});return b.join("")}var x=!1,r={routes:k,reload:function(){x=!0;a.$evalAsync(g)}};a.$on("$locationChangeSuccess",g);return r}]});h.provider("$routeParams",function(){this.$get=function(){return{}}});
h.directive("ngView",u);h.directive("ngView",z);u.$inject=["$route","$anchorScroll","$animate"];z.$inject=["$compile","$controller","$route"]})(window,window.angular);
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* Created by liuzheng on 3/25/16.
*/
'use strict';
var NgApp = angular.module('NgApp', ['ngRoute']);
NgApp.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.transformRequest = function (obj) {
var str = [];
for (var p in obj) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
};
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
$httpProvider.defaults.headers.post = {
'Content-Type': 'application/x-www-form-urlencoded'
}
}]);
NgApp.controller('TerminalRecordCtrl', function ($scope, $http) {
$http.post(window.location.href).success(function (data) {
var toggle = true;
var totalTime = 0;
var TICK = 33;
var TIMESTEP = 33;
var time = 33;
var timer;
var pos = 0;
// Thanks http://stackoverflow.com/a/2998822
function zeroPad(num, size) {
var s = "0" + num;
return s.substr(s.length - size);
}
$scope.scrub = function () {
var setPercent = document.getElementById('scrubber').value;
time = (setPercent / 100) * totalTime;
$scope.restart(time);
};
function buildTimeString(millis) {
var hours = zeroPad(Math.floor(millis / (1000 * 60 * 60)), 2);
millis -= hours * (1000 * 60 * 60);
var minutes = zeroPad(Math.floor(millis / (1000 * 60)), 2);
millis -= minutes * (1000 * 60);
var seconds = zeroPad(Math.floor(millis / 1000), 2);
return hours + ':' + minutes + ':' + seconds;
}
function advance() {
document.getElementById('scrubber').value =
Math.ceil((time / totalTime) * 100);
document.getElementById("beforeScrubberText").innerHTML = buildTimeString(time);
for (; pos < timelist.length; pos++) {
if (timelist[pos] * 1000 <= time) {
term.write(data[timelist[pos]]);
} else {
break;
}
}
if (pos >= timelist.length) {
clearInterval(timer);
}
time += TIMESTEP;
}
$scope.pause = function (test) {
if (!toggle && test) {
return;
}
if (toggle) {
clearInterval(timer);
toggle = !toggle;
} else {
timer = setInterval(advance, TICK);
toggle = !toggle;
}
};
$scope.setSpeed = function () {
var speed = document.getElementById('speed').value;
if (speed == 0) {
TIMESTEP = TICK;
} else if (speed < 0) {
TIMESTEP = TICK / -speed;
} else {
TIMESTEP = TICK * speed;
}
};
$scope.restart = function (millis) {
clearInterval(timer);
term.reset();
time = millis;
pos = 0;
toggle = true;
timer = setInterval(advance, TICK);
};
var term = new Terminal({
rows: 24,
cols: 80,
useStyle: true,
screenKeys: true
});
var timelist = [];
for (var i in data) {
totalTime = Math.max(totalTime, i);
timelist.push(i);
}
timelist = timelist.sort(function(a, b){return a-b});
totalTime = totalTime * 1000;
document.getElementById("afterScrubberText").innerHTML = buildTimeString(totalTime);
term.open(document.getElementById('terminal'));
timer = setInterval(advance, TICK);
})
})
\ No newline at end of file
......@@ -104,13 +104,13 @@ $(document).ready(function () {
$('#ssh').show();
var term_client = openTerminal(options);
console.log(rowHeight);
window.onresize = function () {
var geom = resize();
console.log(geom);
term_client.term.resize(geom.cols, geom.rows);
term_client.client.send({'resize': {'rows': geom.rows, 'cols': geom.cols}});
$('#ssh').show();
}
// by liuzheng712 because it will bring record bug
//window.onresize = function () {
// var geom = resize();
// console.log(geom);
// term_client.term.resize(geom.cols, geom.rows);
// term_client.client.send({'resize': {'rows': geom.rows, 'cols': geom.cols}});
// $('#ssh').show();
//}
});
\ No newline at end of file
......@@ -8,6 +8,11 @@
{% include 'nav_cat_bar.html' %}
<style>
iframe {
overflow:hidden;
}
.bootstrap-dialog-body {
background-color: rgba(0, 0, 0, 0);
}
......@@ -157,7 +162,7 @@
title: title,
maxmin: true,
shade: false,
area: ['800px', '520px'],
area: ['620px', '450px'],
content: url
});
return false;
......
......@@ -111,7 +111,7 @@
<td id="remote_ip" class="text-center"> {{ post.remote_ip }} </td>
<td class="text-center"> {{ post.login_type }} </td>
<td class="text-center"><a href="{% url 'log_history' %}?id={{ post.id }}" class="log_command"> 统计 </a></td>
<td class="text-center"><a class="monitor" file_path="{{ post.log_path }}"> 监控 </a></td>
<td class="text-center"><a class="monitor" monitor-id="{{ post.id }}" file_path="{{ post.log_path }}"> 监控 </a></td>
<td class="text-center"><input type="button" id="cut" class="btn btn-danger btn-xs" name="cut" value="阻断" onclick='cut("{{ post.pid }}", "{{ post.login_type }}")' /></td>
<td class="text-center" id="start_time"> {{ post.start_time|date:"Y-m-d H:i:s" }} </td>
</tr>
......@@ -139,9 +139,10 @@
}
var endpoint = protocol + document.URL.match(RegExp('//(.*?)/'))[1] + '/ws/monitor';
var monitorid = obj.attr('monitor-id');
var file_path = obj.attr('file_path');
{# var socket = new WebSocket(endpoint + '?id=' + monitorid);#}
var socket = new WebSocket(endpoint + '?file_path=' + file_path);
var term = new Terminal({
cols: 98,
rows: 28,
......@@ -155,7 +156,7 @@
term.resize(98, 28);
socket.onopen = function(evt){
socket.send('hello');
{# socket.send('hello');#}
term.write('~.~ Connect WebSocket Success.~.~ \r\n');
};
......
<!DOCTYPE html>
<html ng-app="NgApp" style=" overflow:hidden;">
<head lang="en">
<title>Jumpserver 录像回放</title>
<script type="application/javascript" src='/static/js/jquery-2.1.1.js'></script>
<script type="application/javascript" src='/static/js/angular.min.js'></script>
<script type="application/javascript" src='/static/js/angular-route.min.js'></script>
<script type="application/javascript" src='/static/js/term.js'></script>
</head>
<body>
{% csrf_token %}
<div ng-controller="TerminalRecordCtrl">
<input type="button" value="Play/Pause" ng-click="pause(false);"/>
<input type="button" value="Restart" ng-click="restart(1);"/>
<span id="beforeScrubberText"></span>
<input id="scrubber" type="range" value="0" min=0 max=100
ng-mousedown="pause(true);" ng-mouseup="scrub();"/>
<span id="afterScrubberText"></span>
-5x <input id="speed" type="range" value="0" min=-5 max=5
ng-mouseup="setSpeed();"/> +5x
<div id="terminal"></div>
</div>
<script type="application/javascript" src='/static/js/record.js'></script>
</body>
</html>
\ No newline at end of file
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