mirror of https://github.com/tp4a/teleport
支持记录SFTP操作日志了,能记录文件打开、删除,目录创建、删除,改名以及创建符号链接等操作。
parent
428dc323f3
commit
ef23397be8
File diff suppressed because it is too large
Load Diff
|
@ -1,133 +1,134 @@
|
|||
#ifndef __SSH_SESSION_H__
|
||||
#define __SSH_SESSION_H__
|
||||
|
||||
#include "ssh_recorder.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/sftp.h>
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#define TS_SSH_CHANNEL_TYPE_UNKNOWN 0
|
||||
#define TS_SSH_CHANNEL_TYPE_SHELL 1
|
||||
#define TS_SSH_CHANNEL_TYPE_SFTP 2
|
||||
|
||||
#define TS_SSH_DATA_FROM_CLIENT 1
|
||||
#define TS_SSH_DATA_FROM_SERVER 2
|
||||
|
||||
typedef struct TS_SSH_CHANNEL_INFO
|
||||
{
|
||||
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
|
||||
ssh_channel channel;
|
||||
|
||||
TS_SSH_CHANNEL_INFO()
|
||||
{
|
||||
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
|
||||
channel = NULL;
|
||||
}
|
||||
}TS_SSH_CHANNEL_INFO;
|
||||
|
||||
typedef std::map<ssh_channel, TS_SSH_CHANNEL_INFO*> ts_ssh_channel_map;
|
||||
|
||||
class SshProxy;
|
||||
|
||||
class SshSession : public ExThreadBase
|
||||
{
|
||||
public:
|
||||
SshSession(SshProxy* proxy, ssh_session sess_client);
|
||||
virtual ~SshSession();
|
||||
|
||||
SshProxy* get_proxy(void) { return m_proxy; }
|
||||
|
||||
|
||||
TS_SSH_CHANNEL_INFO* _get_cli_channel(ssh_channel srv_channel);
|
||||
TS_SSH_CHANNEL_INFO* _get_srv_channel(ssh_channel cli_channel);
|
||||
|
||||
void client_ip(const char* ip) { m_client_ip = ip; }
|
||||
const char* client_ip(void) const { return m_client_ip.c_str(); }
|
||||
void client_port(ex_u16 port) { m_client_port = port; }
|
||||
ex_u16 client_port(void) const { return m_client_port; }
|
||||
|
||||
protected:
|
||||
// 继承自 TppSessionBase
|
||||
bool _on_session_begin(const TPP_SESSION_INFO* info);
|
||||
bool _on_session_end(void);
|
||||
|
||||
|
||||
void _thread_loop(void);
|
||||
void _set_stop_flag(void);
|
||||
|
||||
void _process_command(int from, const ex_u8* data, int len);
|
||||
|
||||
private:
|
||||
void _run(void);
|
||||
|
||||
void _close_channels(void);
|
||||
|
||||
void _enter_sftp_mode(void);
|
||||
|
||||
static int _on_auth_password_request(ssh_session session, const char *user, const char *password, void *userdata);
|
||||
static ssh_channel _on_new_channel_request(ssh_session session, void *userdata);
|
||||
static int _on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata);
|
||||
static int _on_client_shell_request(ssh_session session, ssh_channel channel, void *userdata);
|
||||
static void _on_client_channel_close(ssh_session session, ssh_channel channel, void* userdata);
|
||||
static int _on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
|
||||
static int _on_client_pty_win_change(ssh_session session, ssh_channel channel, int width, int height, int pxwidth, int pwheight, void *userdata);
|
||||
|
||||
static int _on_client_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata);
|
||||
static int _on_client_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata);
|
||||
|
||||
static int _on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
|
||||
static void _on_server_channel_close(ssh_session session, ssh_channel channel, void* userdata);
|
||||
|
||||
private:
|
||||
int m_retcode;
|
||||
int m_db_id;
|
||||
|
||||
TppSshRec m_rec;
|
||||
|
||||
SshProxy* m_proxy;
|
||||
ssh_session m_cli_session;
|
||||
ssh_session m_srv_session;
|
||||
|
||||
ExThreadLock m_lock;
|
||||
|
||||
ex_astr m_client_ip;
|
||||
ex_u16 m_client_port;
|
||||
|
||||
ex_astr m_sid;
|
||||
ex_astr m_server_ip;
|
||||
ex_u16 m_server_port;
|
||||
ex_astr m_user_name;
|
||||
ex_astr m_user_auth;
|
||||
int m_auth_mode;
|
||||
|
||||
bool m_is_first_server_data;
|
||||
bool m_is_sftp;
|
||||
|
||||
bool m_is_logon;
|
||||
// 一个ssh_session中可以打开多个ssh_channel
|
||||
ts_ssh_channel_map m_channel_cli_srv; // 通过客户端通道查找服务端通道
|
||||
ts_ssh_channel_map m_channel_srv_cli; // 通过服务端通道查找客户端通道
|
||||
|
||||
bool m_have_error;
|
||||
|
||||
bool m_recving_from_srv; // 是否正在从服务器接收数据?
|
||||
bool m_recving_from_cli; // 是否正在从客户端接收数据?
|
||||
|
||||
struct ssh_server_callbacks_struct m_srv_cb;
|
||||
struct ssh_channel_callbacks_struct m_cli_channel_cb;
|
||||
struct ssh_channel_callbacks_struct m_srv_channel_cb;
|
||||
|
||||
int m_command_flag;
|
||||
|
||||
std::list<char> m_cmd_char_list;
|
||||
std::list<char>::iterator m_cmd_char_pos;
|
||||
};
|
||||
|
||||
#endif // __SSH_SESSION_H__
|
||||
#ifndef __SSH_SESSION_H__
|
||||
#define __SSH_SESSION_H__
|
||||
|
||||
#include "ssh_recorder.h"
|
||||
|
||||
#include <ex.h>
|
||||
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/server.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <libssh/sftp.h>
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#define TS_SSH_CHANNEL_TYPE_UNKNOWN 0
|
||||
#define TS_SSH_CHANNEL_TYPE_SHELL 1
|
||||
#define TS_SSH_CHANNEL_TYPE_SFTP 2
|
||||
|
||||
#define TS_SSH_DATA_FROM_CLIENT 1
|
||||
#define TS_SSH_DATA_FROM_SERVER 2
|
||||
|
||||
typedef struct TS_SSH_CHANNEL_INFO
|
||||
{
|
||||
int type; // TS_SSH_CHANNEL_TYPE_SHELL or TS_SSH_CHANNEL_TYPE_SFTP
|
||||
ssh_channel channel;
|
||||
|
||||
TS_SSH_CHANNEL_INFO()
|
||||
{
|
||||
type = TS_SSH_CHANNEL_TYPE_UNKNOWN;
|
||||
channel = NULL;
|
||||
}
|
||||
}TS_SSH_CHANNEL_INFO;
|
||||
|
||||
typedef std::map<ssh_channel, TS_SSH_CHANNEL_INFO*> ts_ssh_channel_map;
|
||||
|
||||
class SshProxy;
|
||||
|
||||
class SshSession : public ExThreadBase
|
||||
{
|
||||
public:
|
||||
SshSession(SshProxy* proxy, ssh_session sess_client);
|
||||
virtual ~SshSession();
|
||||
|
||||
SshProxy* get_proxy(void) { return m_proxy; }
|
||||
|
||||
|
||||
TS_SSH_CHANNEL_INFO* _get_cli_channel(ssh_channel srv_channel);
|
||||
TS_SSH_CHANNEL_INFO* _get_srv_channel(ssh_channel cli_channel);
|
||||
|
||||
void client_ip(const char* ip) { m_client_ip = ip; }
|
||||
const char* client_ip(void) const { return m_client_ip.c_str(); }
|
||||
void client_port(ex_u16 port) { m_client_port = port; }
|
||||
ex_u16 client_port(void) const { return m_client_port; }
|
||||
|
||||
protected:
|
||||
// 继承自 TppSessionBase
|
||||
bool _on_session_begin(const TPP_SESSION_INFO* info);
|
||||
bool _on_session_end(void);
|
||||
|
||||
|
||||
void _thread_loop(void);
|
||||
void _set_stop_flag(void);
|
||||
|
||||
void _process_ssh_command(int from, const ex_u8* data, int len);
|
||||
void _process_sftp_command(const ex_u8* data, int len);
|
||||
|
||||
private:
|
||||
void _run(void);
|
||||
|
||||
void _close_channels(void);
|
||||
|
||||
void _enter_sftp_mode(void);
|
||||
|
||||
static int _on_auth_password_request(ssh_session session, const char *user, const char *password, void *userdata);
|
||||
static ssh_channel _on_new_channel_request(ssh_session session, void *userdata);
|
||||
static int _on_client_pty_request(ssh_session session, ssh_channel channel, const char *term, int x, int y, int px, int py, void *userdata);
|
||||
static int _on_client_shell_request(ssh_session session, ssh_channel channel, void *userdata);
|
||||
static void _on_client_channel_close(ssh_session session, ssh_channel channel, void* userdata);
|
||||
static int _on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
|
||||
static int _on_client_pty_win_change(ssh_session session, ssh_channel channel, int width, int height, int pxwidth, int pwheight, void *userdata);
|
||||
|
||||
static int _on_client_channel_subsystem_request(ssh_session session, ssh_channel channel, const char *subsystem, void *userdata);
|
||||
static int _on_client_channel_exec_request(ssh_session session, ssh_channel channel, const char *command, void *userdata);
|
||||
|
||||
static int _on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata);
|
||||
static void _on_server_channel_close(ssh_session session, ssh_channel channel, void* userdata);
|
||||
|
||||
private:
|
||||
int m_retcode;
|
||||
int m_db_id;
|
||||
|
||||
TppSshRec m_rec;
|
||||
|
||||
SshProxy* m_proxy;
|
||||
ssh_session m_cli_session;
|
||||
ssh_session m_srv_session;
|
||||
|
||||
ExThreadLock m_lock;
|
||||
|
||||
ex_astr m_client_ip;
|
||||
ex_u16 m_client_port;
|
||||
|
||||
ex_astr m_sid;
|
||||
ex_astr m_server_ip;
|
||||
ex_u16 m_server_port;
|
||||
ex_astr m_user_name;
|
||||
ex_astr m_user_auth;
|
||||
int m_auth_mode;
|
||||
|
||||
bool m_is_first_server_data;
|
||||
bool m_is_sftp;
|
||||
|
||||
bool m_is_logon;
|
||||
// 一个ssh_session中可以打开多个ssh_channel
|
||||
ts_ssh_channel_map m_channel_cli_srv; // 通过客户端通道查找服务端通道
|
||||
ts_ssh_channel_map m_channel_srv_cli; // 通过服务端通道查找客户端通道
|
||||
|
||||
bool m_have_error;
|
||||
|
||||
bool m_recving_from_srv; // 是否正在从服务器接收数据?
|
||||
bool m_recving_from_cli; // 是否正在从客户端接收数据?
|
||||
|
||||
struct ssh_server_callbacks_struct m_srv_cb;
|
||||
struct ssh_channel_callbacks_struct m_cli_channel_cb;
|
||||
struct ssh_channel_callbacks_struct m_srv_channel_cb;
|
||||
|
||||
int m_command_flag;
|
||||
|
||||
std::list<char> m_cmd_char_list;
|
||||
std::list<char>::iterator m_cmd_char_pos;
|
||||
};
|
||||
|
||||
#endif // __SSH_SESSION_H__
|
||||
|
|
|
@ -80,10 +80,20 @@ class ReplayStaticFileHandler(tornado.web.StaticFileHandler):
|
|||
class ComandLogHandler(TPBaseAdminAuthHandler):
|
||||
def get(self, protocol, record_id):
|
||||
|
||||
header = record.read_record_head(record_id)
|
||||
if header is None:
|
||||
return self.write_json(-3, '操作失败')
|
||||
|
||||
# ret = dict()
|
||||
# ret['header'] = header
|
||||
# return self.write_json(0, data=ret)
|
||||
|
||||
param = dict()
|
||||
param['header'] = header
|
||||
param['count'] = 0
|
||||
param['op'] = list()
|
||||
|
||||
cmd_type = 0 # 0 = ssh, 1 = sftp
|
||||
protocol = int(protocol)
|
||||
if protocol == 1:
|
||||
pass
|
||||
|
@ -94,12 +104,26 @@ class ComandLogHandler(TPBaseAdminAuthHandler):
|
|||
file = open(file_info, 'r')
|
||||
data = file.readlines()
|
||||
for i in range(len(data)):
|
||||
param['op'].append({'t': data[i][1:20], 'c': data[i][22:-1]})
|
||||
if 0 == i:
|
||||
cmd = data[i][22:-1]
|
||||
if 'SFTP INITIALIZE' == cmd:
|
||||
cmd_type = 1
|
||||
continue
|
||||
if cmd_type == 0:
|
||||
param['op'].append({'t': data[i][1:20], 'c': data[i][22:-1]})
|
||||
else:
|
||||
cmd_info = data[i][22:-1].split(':')
|
||||
if len(cmd_info) != 4:
|
||||
continue
|
||||
param['op'].append({'t': data[i][1:20], 'c': cmd_info[0], 'p1': cmd_info[2], 'p2': cmd_info[3]})
|
||||
except:
|
||||
pass
|
||||
param['count'] = len(param['op'])
|
||||
|
||||
self.render('log/record-ssh-cmd.mako', page_param=json.dumps(param))
|
||||
if cmd_type == 0:
|
||||
self.render('log/record-ssh-cmd.mako', page_param=json.dumps(param))
|
||||
else:
|
||||
self.render('log/record-sftp-cmd.mako', page_param=json.dumps(param))
|
||||
|
||||
|
||||
class RecordGetHeader(TPBaseAdminAuthJsonHandler):
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
<%!
|
||||
page_title_ = 'SFTP操作记录'
|
||||
%>
|
||||
|
||||
<%inherit file="../page_no_sidebar_base.mako"/>
|
||||
<%block name="extend_js">
|
||||
</%block>
|
||||
|
||||
<%block name="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li><i class="fa fa-file-text-o"></i> ${self.attr.page_title_}</li>
|
||||
<li><span id="recorder-info"></span></li>
|
||||
</ol>
|
||||
</%block>
|
||||
|
||||
<%block name="extend_css">
|
||||
<style type="text/css">
|
||||
#no-op-msg {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 50px;
|
||||
background-color: #fffed5;
|
||||
border-radius: 5px;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
#op-list {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 20px 10px 20px 10px;
|
||||
background-color: #ffffff;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.op-item {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.time, .cmd, .path {
|
||||
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
|
||||
font-size:13px;
|
||||
line-height: 15px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.time {
|
||||
margin-right: 15px;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
|
||||
.path {
|
||||
margin:0 5px 0 5px;
|
||||
}
|
||||
|
||||
.cmd-danger {
|
||||
background-color: #ffbba6;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cmd-info {
|
||||
background-color: #b4fdb1;
|
||||
}
|
||||
</style>
|
||||
</%block>
|
||||
|
||||
<div class="page-content">
|
||||
<div id="no-op-msg">
|
||||
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
||||
</div>
|
||||
<div id="op-list"></div>
|
||||
</div>
|
||||
|
||||
<%block name="embed_js">
|
||||
<script type="text/javascript">
|
||||
|
||||
ywl.add_page_options(${page_param});
|
||||
|
||||
ywl.on_init = function (cb_stack, cb_args) {
|
||||
|
||||
if (ywl.page_options.count === 0) {
|
||||
$('#no-op-msg').show();
|
||||
} else {
|
||||
var header = ywl.page_options.header;
|
||||
$('#recorder-info').html(header.account + ' 于 ' + format_datetime(header.start) + ' 访问 ' + header.user_name + '@' + header.ip + ':' + header.port);
|
||||
|
||||
var dom_op_list = $('#op-list');
|
||||
var html = [];
|
||||
for (var i = 0; i < ywl.page_options.count; i++) {
|
||||
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> ');
|
||||
|
||||
if (ywl.page_options.op[i].c === '3') {
|
||||
html.push('<span class="cmd">打开文件</span>');
|
||||
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
} else if (ywl.page_options.op[i].c === '13') {
|
||||
html.push('<span class="cmd cmd-danger">删除文件</span>');
|
||||
html.push('<span class="path cmd-danger">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
} else if (ywl.page_options.op[i].c === '14') {
|
||||
html.push('<span class="cmd">创建目录</span>');
|
||||
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
} else if (ywl.page_options.op[i].c === '15') {
|
||||
html.push('<span class="cmd cmd-danger">删除目录</span>');
|
||||
html.push('<span class="path cmd-danger">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
} else if (ywl.page_options.op[i].c === '18') {
|
||||
html.push('<span class="cmd cmd-info">更改名称</span>');
|
||||
html.push('<span class="path cmd-info">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
html.push('<i class="fa fa-arrow-circle-right"></i>');
|
||||
html.push('<span class="path cmd-info">' + ywl.page_options.op[i].p2 + '</span>');
|
||||
} else if (ywl.page_options.op[i].c === '21') {
|
||||
html.push('<span class="cmd">创建链接</span>');
|
||||
html.push('<span class="path">' + ywl.page_options.op[i].p2 + '</span>');
|
||||
html.push('<i class="fa fa-arrow-right"></i>');
|
||||
html.push('<span class="path">' + ywl.page_options.op[i].p1 + '</span>');
|
||||
}
|
||||
|
||||
html.push('</div>');
|
||||
}
|
||||
dom_op_list.append(html.join(''));
|
||||
dom_op_list.show();
|
||||
}
|
||||
cb_stack.exec();
|
||||
};
|
||||
</script>
|
||||
</%block>
|
|
@ -1,101 +1,105 @@
|
|||
<%!
|
||||
page_title_ = '操作记录'
|
||||
%>
|
||||
|
||||
<%inherit file="../page_no_sidebar_base.mako"/>
|
||||
<%block name="extend_js">
|
||||
</%block>
|
||||
|
||||
<%block name="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li><i class="fa fa-server"></i> ${self.attr.page_title_}</li>
|
||||
</ol>
|
||||
</%block>
|
||||
|
||||
<%block name="extend_css">
|
||||
<style type="text/css">
|
||||
#no-op-msg {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 50px;
|
||||
background-color: #fffed5;
|
||||
border-radius: 5px;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
#op-list {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 20px 10px 20px 10px;
|
||||
background-color: #ffffff;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.op-item {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.time, .cmd {
|
||||
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
|
||||
line-height: 15px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.time {
|
||||
margin-right: 15px;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
|
||||
.cmd-danger {
|
||||
background-color: #ffbba6;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cmd-info {
|
||||
background-color: #b4fdb1;
|
||||
}
|
||||
</style>
|
||||
</%block>
|
||||
|
||||
<div class="page-content">
|
||||
<div id="no-op-msg">
|
||||
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
||||
</div>
|
||||
<div id="op-list"></div>
|
||||
</div>
|
||||
|
||||
<%block name="embed_js">
|
||||
<script type="text/javascript">
|
||||
|
||||
ywl.add_page_options(${page_param});
|
||||
|
||||
var danger_cmd = ['chmod', 'chown', 'kill', 'rm', 'su', 'sudo'];
|
||||
var info_cmd = ['exit'];
|
||||
|
||||
ywl.on_init = function (cb_stack, cb_args) {
|
||||
if (ywl.page_options.count == 0) {
|
||||
$('#no-op-msg').show();
|
||||
} else {
|
||||
var dom_op_list = $('#op-list');
|
||||
var html = [];
|
||||
for (var i = 0; i < ywl.page_options.count; i++) {
|
||||
var cmd_list = ywl.page_options.op[i].c.split(' ');
|
||||
|
||||
var cmd_class = '';
|
||||
if (_.intersection(cmd_list, danger_cmd).length > 0) {
|
||||
cmd_class = ' cmd-danger';
|
||||
} else if (_.intersection(cmd_list, info_cmd).length > 0) {
|
||||
cmd_class = ' cmd-info';
|
||||
}
|
||||
|
||||
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> <span class="cmd' + cmd_class + '">' + ywl.page_options.op[i].c + '</span></li></div>');
|
||||
}
|
||||
dom_op_list.append(html.join(''));
|
||||
dom_op_list.show();
|
||||
}
|
||||
cb_stack.exec();
|
||||
};
|
||||
</script>
|
||||
<%!
|
||||
page_title_ = 'SSH操作记录'
|
||||
%>
|
||||
|
||||
<%inherit file="../page_no_sidebar_base.mako"/>
|
||||
<%block name="extend_js">
|
||||
</%block>
|
||||
|
||||
<%block name="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li><i class="fa fa-file-text-o"></i> ${self.attr.page_title_}</li>
|
||||
<li><span id="recorder-info"></span></li>
|
||||
</ol>
|
||||
</%block>
|
||||
|
||||
<%block name="extend_css">
|
||||
<style type="text/css">
|
||||
#no-op-msg {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 50px;
|
||||
background-color: #fffed5;
|
||||
border-radius: 5px;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
#op-list {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
margin: 20px 10px 20px 10px;
|
||||
background-color: #ffffff;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.op-item {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.time, .cmd {
|
||||
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
|
||||
line-height: 15px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.time {
|
||||
margin-right: 15px;
|
||||
background-color: #d8d8d8;
|
||||
}
|
||||
|
||||
.cmd-danger {
|
||||
background-color: #ffbba6;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cmd-info {
|
||||
background-color: #b4fdb1;
|
||||
}
|
||||
</style>
|
||||
</%block>
|
||||
|
||||
<div class="page-content">
|
||||
<div id="no-op-msg">
|
||||
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
||||
</div>
|
||||
<div id="op-list"></div>
|
||||
</div>
|
||||
|
||||
<%block name="embed_js">
|
||||
<script type="text/javascript">
|
||||
|
||||
ywl.add_page_options(${page_param});
|
||||
|
||||
var danger_cmd = ['chmod', 'chown', 'kill', 'rm', 'su', 'sudo'];
|
||||
var info_cmd = ['exit'];
|
||||
|
||||
ywl.on_init = function (cb_stack, cb_args) {
|
||||
if (ywl.page_options.count === 0) {
|
||||
$('#no-op-msg').show();
|
||||
} else {
|
||||
var header = ywl.page_options.header;
|
||||
$('#recorder-info').html(header.account + ' 于 ' + format_datetime(header.start) + ' 访问 ' + header.user_name + '@' + header.ip + ':' + header.port);
|
||||
|
||||
var dom_op_list = $('#op-list');
|
||||
var html = [];
|
||||
for (var i = 0; i < ywl.page_options.count; i++) {
|
||||
var cmd_list = ywl.page_options.op[i].c.split(' ');
|
||||
|
||||
var cmd_class = '';
|
||||
if (_.intersection(cmd_list, danger_cmd).length > 0) {
|
||||
cmd_class = ' cmd-danger';
|
||||
} else if (_.intersection(cmd_list, info_cmd).length > 0) {
|
||||
cmd_class = ' cmd-info';
|
||||
}
|
||||
|
||||
html.push('<div class="op-item"><span class="time">' + ywl.page_options.op[i].t + '</span> <span class="cmd' + cmd_class + '">' + ywl.page_options.op[i].c + '</span></div>');
|
||||
}
|
||||
dom_op_list.append(html.join(''));
|
||||
dom_op_list.show();
|
||||
}
|
||||
cb_stack.exec();
|
||||
};
|
||||
</script>
|
||||
</%block>
|
Loading…
Reference in New Issue