mirror of https://github.com/tp4a/teleport
改进sftp操作的历史记录和展示。
parent
49708edd3c
commit
13b9a1ef17
|
@ -15,6 +15,7 @@ TP_SSH_CHANNEL_PAIR::TP_SSH_CHANNEL_PAIR() {
|
||||||
db_id = 0;
|
db_id = 0;
|
||||||
channel_id = 0;
|
channel_id = 0;
|
||||||
|
|
||||||
|
win_width = 0;
|
||||||
is_first_server_data = true;
|
is_first_server_data = true;
|
||||||
|
|
||||||
server_ready = false;
|
server_ready = false;
|
||||||
|
@ -630,7 +631,7 @@ void SshSession::_on_client_channel_close(ssh_session session, ssh_channel chann
|
||||||
|
|
||||||
int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata)
|
int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata)
|
||||||
{
|
{
|
||||||
EXLOG_BIN((ex_u8*)data, len, "on_client_channel_data [is_stderr=%d]:", is_stderr);
|
//EXLOG_BIN((ex_u8*)data, len, "on_client_channel_data [is_stderr=%d]:", is_stderr);
|
||||||
|
|
||||||
SshSession *_this = (SshSession *)userdata;
|
SshSession *_this = (SshSession *)userdata;
|
||||||
|
|
||||||
|
@ -666,10 +667,10 @@ int SshSession::_on_client_channel_data(ssh_session session, ssh_channel channel
|
||||||
|
|
||||||
_this->_process_ssh_command(cp, TP_SSH_CLIENT_SIDE, (ex_u8*)data, _len);
|
_this->_process_ssh_command(cp, TP_SSH_CLIENT_SIDE, (ex_u8*)data, _len);
|
||||||
|
|
||||||
ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
// ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
||||||
ex_replace_all(str, "\r", "");
|
// ex_replace_all(str, "\r", "");
|
||||||
ex_replace_all(str, "\n", "");
|
// ex_replace_all(str, "\n", "");
|
||||||
EXLOGD("[ssh] -- [%s]\n", str.c_str());
|
// EXLOGD("[ssh] -- [%s]\n", str.c_str());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_this->_process_sftp_command(cp, (ex_u8*)data, _len);
|
_this->_process_sftp_command(cp, (ex_u8*)data, _len);
|
||||||
|
@ -742,7 +743,7 @@ int SshSession::_on_client_channel_exec_request(ssh_session session, ssh_channel
|
||||||
}
|
}
|
||||||
|
|
||||||
int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) {
|
int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel, void *data, unsigned int len, int is_stderr, void *userdata) {
|
||||||
EXLOG_BIN((ex_u8*)data, len, "on_server_channel_data [is_stderr=%d]:", is_stderr);
|
//EXLOG_BIN((ex_u8*)data, len, "on_server_channel_data [is_stderr=%d]:", is_stderr);
|
||||||
|
|
||||||
SshSession *_this = (SshSession *)userdata;
|
SshSession *_this = (SshSession *)userdata;
|
||||||
|
|
||||||
|
@ -781,10 +782,10 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
||||||
}
|
}
|
||||||
|
|
||||||
_this->_process_ssh_command(cp, TP_SSH_SERVER_SIDE, (ex_u8*)data, len);
|
_this->_process_ssh_command(cp, TP_SSH_SERVER_SIDE, (ex_u8*)data, len);
|
||||||
ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
// ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
||||||
ex_replace_all(str, "\r", "");
|
// ex_replace_all(str, "\r", "");
|
||||||
ex_replace_all(str, "\n", "");
|
// ex_replace_all(str, "\n", "");
|
||||||
EXLOGD("[ssh] -- [%s]\n", str.c_str());
|
// EXLOGD("[ssh] -- [%s]\n", str.c_str());
|
||||||
|
|
||||||
cp->rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len);
|
cp->rec.record(TS_RECORD_TYPE_SSH_DATA, (unsigned char *)data, len);
|
||||||
}
|
}
|
||||||
|
@ -811,29 +812,15 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
||||||
|
|
||||||
int w = min(cp->win_width, 128);
|
int w = min(cp->win_width, 128);
|
||||||
ex_astr line(w, '=');
|
ex_astr line(w, '=');
|
||||||
// char line[129] = { 0 };
|
|
||||||
// for (int i = 0; i < w; ++i)
|
|
||||||
// line[i] = '=';
|
|
||||||
|
|
||||||
// snprintf(buf, sizeof(buf),
|
|
||||||
// "\r\n\r\n"\
|
|
||||||
// "=============================================\r\n"\
|
|
||||||
// "Welcome to Teleport-SSH-Server...\r\n"\
|
|
||||||
// " - teleport to %s:%d\r\n"\
|
|
||||||
// " - authroized by %s\r\n"\
|
|
||||||
// "=============================================\r\n"\
|
|
||||||
// "\r\n",
|
|
||||||
// _this->m_conn_ip.c_str(),
|
|
||||||
// _this->m_conn_port, auth_mode
|
|
||||||
// );
|
|
||||||
snprintf(buf, sizeof(buf),
|
snprintf(buf, sizeof(buf),
|
||||||
"\r\n\r\n"\
|
"\r\n"\
|
||||||
"%s\r\n"\
|
"%s\r\n"\
|
||||||
"Welcome to Teleport-SSH-Server...\r\n"\
|
"Teleport SSH Bastion Server...\r\n"\
|
||||||
" - teleport to %s:%d\r\n"\
|
" - teleport to %s:%d\r\n"\
|
||||||
" - authroized by %s\r\n"\
|
" - authroized by %s\r\n"\
|
||||||
"%s\r\n"\
|
"%s\r\n"\
|
||||||
"\r\n",
|
"\r\n\r\n",
|
||||||
line.c_str(),
|
line.c_str(),
|
||||||
_this->m_conn_ip.c_str(),
|
_this->m_conn_ip.c_str(),
|
||||||
_this->m_conn_port, auth_mode,
|
_this->m_conn_port, auth_mode,
|
||||||
|
@ -854,15 +841,94 @@ int SshSession::_on_server_channel_data(ssh_session session, ssh_channel channel
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
// 直接转发数据到客户端
|
||||||
if (is_stderr)
|
if (is_stderr)
|
||||||
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
||||||
else
|
else
|
||||||
ret = ssh_channel_write(cp->cli_channel, data, len);
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
#else
|
||||||
|
// 分析收到的服务端数据包,如果包含类似 \033]0;AABB\007 这样的数据,客户端会根据此改变窗口标题
|
||||||
|
// 我们需要替换这部分数据,使之显示类似 \033]0;TP#ssh://remote-ip\007 这样的标题。
|
||||||
|
// 但是这样会降低一些性能,因此目前不启用,保留此部分代码备用。
|
||||||
|
if (is_stderr) {
|
||||||
|
ret = ssh_channel_write_stderr(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
else if (cp->type != TS_SSH_CHANNEL_TYPE_SHELL) {
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (len > 5 && len < 256) {
|
||||||
|
const ex_u8* _begin = ex_memmem((const ex_u8*)data, len, (const ex_u8*)"\033]0;", 4);
|
||||||
|
if (NULL != _begin) {
|
||||||
|
size_t len_before = _begin - (const ex_u8*)data;
|
||||||
|
const ex_u8* _end = ex_memmem(_begin + 4, len - len_before, (const ex_u8*)"\007", 1);
|
||||||
|
if (NULL != _end)
|
||||||
|
{
|
||||||
|
_end++;
|
||||||
|
|
||||||
|
// 这个包中含有改变标题的数据,将标题换为我们想要的
|
||||||
|
EXLOGD("-- found title\n");
|
||||||
|
size_t len_end = len - (_end - (const ex_u8*)data);
|
||||||
|
MemBuffer mbuf;
|
||||||
|
|
||||||
|
if (len_before > 0)
|
||||||
|
mbuf.append((ex_u8*)data, len_before);
|
||||||
|
|
||||||
|
mbuf.append((ex_u8*)"\033]0;TP#ssh://", 13);
|
||||||
|
mbuf.append((ex_u8*)_this->m_conn_ip.c_str(), _this->m_conn_ip.length());
|
||||||
|
mbuf.append((ex_u8*)"\007", 1);
|
||||||
|
|
||||||
|
if (len_end > 0)
|
||||||
|
mbuf.append((ex_u8*)_end, len_end);
|
||||||
|
|
||||||
|
if (mbuf.size() > 0)
|
||||||
|
{
|
||||||
|
for(;;){
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, mbuf.data(), mbuf.size());
|
||||||
|
if (ret == SSH_ERROR)
|
||||||
|
break;
|
||||||
|
if (ret == mbuf.size()) {
|
||||||
|
ret = len; // 表示我们已经处理了所有的数据了。
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mbuf.pop(ret);
|
||||||
|
ex_sleep_ms(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (ret <= 0)
|
||||||
|
// EXLOGE("[ssh] send to client failed (1).\n");
|
||||||
|
// else
|
||||||
|
// ret = len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = ssh_channel_write(cp->cli_channel, data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ret == SSH_ERROR) {
|
if (ret == SSH_ERROR) {
|
||||||
EXLOGE("[ssh] send data(%dB) to client failed. [%d][cli:%s][srv:%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_srv_session));
|
EXLOGE("[ssh] send data(%dB) to client failed. [%d][cli:%s][srv:%s]\n", len, ret, ssh_get_error(_this->m_cli_session), ssh_get_error(_this->m_srv_session));
|
||||||
ssh_channel_close(channel);
|
ssh_channel_close(channel);
|
||||||
}
|
}
|
||||||
|
else if (ret != len) {
|
||||||
|
EXLOGW("[ssh] received server data, got %dB, processed %dB.\n", len, ret);
|
||||||
|
}
|
||||||
|
|
||||||
_this->m_recving_from_srv = false;
|
_this->m_recving_from_srv = false;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -909,8 +975,8 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR* cp, int from, const e
|
||||||
|
|
||||||
// 客户端输入回车时,可能时执行了一条命令,需要根据服务端返回的数据进行进一步判断
|
// 客户端输入回车时,可能时执行了一条命令,需要根据服务端返回的数据进行进一步判断
|
||||||
cp->maybe_cmd = (data[len - 1] == 0x0d);
|
cp->maybe_cmd = (data[len - 1] == 0x0d);
|
||||||
if (cp->maybe_cmd)
|
// if (cp->maybe_cmd)
|
||||||
EXLOGD("[ssh] maybe cmd.\n");
|
// EXLOGD("[ssh] maybe cmd.\n");
|
||||||
|
|
||||||
// 有时在执行类似top命令的情况下,输入一个字母'q'就退出程序,没有输入回车,可能会导致后续记录命令时将返回的命令行提示符作为命令
|
// 有时在执行类似top命令的情况下,输入一个字母'q'就退出程序,没有输入回车,可能会导致后续记录命令时将返回的命令行提示符作为命令
|
||||||
// 记录下来了,要避免这种情况,排除的方式是:客户端单个字母,后续服务端如果收到的是控制序列 1b 5b xx xx,就不计做命令。
|
// 记录下来了,要避免这种情况,排除的方式是:客户端单个字母,后续服务端如果收到的是控制序列 1b 5b xx xx,就不计做命令。
|
||||||
|
@ -1065,13 +1131,13 @@ void SshSession::_process_ssh_command(TP_SSH_CHANNEL_PAIR* cp, int from, const e
|
||||||
case 0x0d:
|
case 0x0d:
|
||||||
{
|
{
|
||||||
if (offset + 1 < len && data[offset + 1] == 0x0a) {
|
if (offset + 1 < len && data[offset + 1] == 0x0a) {
|
||||||
if (cp->maybe_cmd)
|
// if (cp->maybe_cmd)
|
||||||
EXLOGD("[ssh] maybe cmd.\n");
|
// EXLOGD("[ssh] maybe cmd.\n");
|
||||||
if (cp->maybe_cmd) {
|
if (cp->maybe_cmd) {
|
||||||
if (cp->cmd_char_list.size() > 0)
|
if (cp->cmd_char_list.size() > 0)
|
||||||
{
|
{
|
||||||
ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
ex_astr str(cp->cmd_char_list.begin(), cp->cmd_char_list.end());
|
||||||
EXLOGD("[ssh] --==--==-- save cmd: [%s]\n", str.c_str());
|
// EXLOGD("[ssh] --==--==-- save cmd: [%s]\n", str.c_str());
|
||||||
cp->rec.record_command(0, str);
|
cp->rec.record_command(0, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1113,6 +1179,12 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat
|
||||||
// SFTP protocol: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
// SFTP protocol: https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13
|
||||||
//EXLOG_BIN(data, len, "[sftp] client channel data");
|
//EXLOG_BIN(data, len, "[sftp] client channel data");
|
||||||
|
|
||||||
|
// TODO: 根据客户端的请求和服务端的返回,可以进一步判断用户是如何操作文件的,比如读、写等等,以及操作的结果是成功还是失败。
|
||||||
|
// 记录格式: time-offset,flag,action,result,file-path,[file-path]
|
||||||
|
// 其中,flag目前总是为0,可以忽略(为保证与ssh-cmd格式一致),time-offset/action/result 都是数字
|
||||||
|
// file-path是被操作的对象,规格为 长度:实际内容,例如, 13:/root/abc.txt
|
||||||
|
|
||||||
|
|
||||||
if (len < 9)
|
if (len < 9)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1142,11 +1214,9 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat
|
||||||
int str2_len = 0;// (int)((data[9] << 24) | (data[10] << 16) | (data[11] << 8) | data[12]);
|
int str2_len = 0;// (int)((data[9] << 24) | (data[10] << 16) | (data[11] << 8) | data[12]);
|
||||||
|
|
||||||
|
|
||||||
const char* act = NULL;
|
|
||||||
switch (sftp_cmd) {
|
switch (sftp_cmd) {
|
||||||
case 0x03:
|
case 0x03:
|
||||||
// 0x03 = 3 = SSH_FXP_OPEN
|
// 0x03 = 3 = SSH_FXP_OPEN
|
||||||
act = "open file";
|
|
||||||
break;
|
break;
|
||||||
// case 0x0b:
|
// case 0x0b:
|
||||||
// // 0x0b = 11 = SSH_FXP_OPENDIR
|
// // 0x0b = 11 = SSH_FXP_OPENDIR
|
||||||
|
@ -1154,27 +1224,22 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat
|
||||||
// break;
|
// break;
|
||||||
case 0x0d:
|
case 0x0d:
|
||||||
// 0x0d = 13 = SSH_FXP_REMOVE
|
// 0x0d = 13 = SSH_FXP_REMOVE
|
||||||
act = "remove file";
|
|
||||||
break;
|
break;
|
||||||
case 0x0e:
|
case 0x0e:
|
||||||
// 0x0e = 14 = SSH_FXP_MKDIR
|
// 0x0e = 14 = SSH_FXP_MKDIR
|
||||||
act = "create dir";
|
|
||||||
break;
|
break;
|
||||||
case 0x0f:
|
case 0x0f:
|
||||||
// 0x0f = 15 = SSH_FXP_RMDIR
|
// 0x0f = 15 = SSH_FXP_RMDIR
|
||||||
act = "remove dir";
|
|
||||||
break;
|
break;
|
||||||
case 0x12:
|
case 0x12:
|
||||||
// 0x12 = 18 = SSH_FXP_RENAME
|
// 0x12 = 18 = SSH_FXP_RENAME
|
||||||
// rename操作数据中包含两个字符串
|
// rename操作数据中包含两个字符串
|
||||||
act = "rename";
|
|
||||||
str2_ptr = str1_ptr + str1_len + 4;
|
str2_ptr = str1_ptr + str1_len + 4;
|
||||||
str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
||||||
break;
|
break;
|
||||||
case 0x15:
|
case 0x15:
|
||||||
// 0x15 = 21 = SSH_FXP_LINK
|
// 0x15 = 21 = SSH_FXP_LINK
|
||||||
// link操作数据中包含两个字符串,前者是新的链接文件名,后者是现有被链接的文件名
|
// link操作数据中包含两个字符串,前者是新的链接文件名,后者是现有被链接的文件名
|
||||||
act = "create link";
|
|
||||||
str2_ptr = str1_ptr + str1_len + 4;
|
str2_ptr = str1_ptr + str1_len + 4;
|
||||||
str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
str2_len = (int)((str2_ptr[0] << 24) | (str2_ptr[1] << 16) | (str2_ptr[2] << 8) | str2_ptr[3]);
|
||||||
break;
|
break;
|
||||||
|
@ -1191,12 +1256,12 @@ void SshSession::_process_sftp_command(TP_SSH_CHANNEL_PAIR* cp, const ex_u8* dat
|
||||||
char msg[2048] = { 0 };
|
char msg[2048] = { 0 };
|
||||||
if (str2_len == 0) {
|
if (str2_len == 0) {
|
||||||
ex_astr str1((char*)((ex_u8*)data + 13), str1_len);
|
ex_astr str1((char*)((ex_u8*)data + 13), str1_len);
|
||||||
ex_strformat(msg, 2048, "%d:%s:%s:\r\n", sftp_cmd, act, str1.c_str());
|
ex_strformat(msg, 2048, "%d,%d,%s", sftp_cmd, 0, str1.c_str());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ex_astr str1((char*)(str1_ptr + 4), str1_len);
|
ex_astr str1((char*)(str1_ptr + 4), str1_len);
|
||||||
ex_astr str2((char*)(str2_ptr + 4), str2_len);
|
ex_astr str2((char*)(str2_ptr + 4), str2_len);
|
||||||
ex_strformat(msg, 2048, "%d:%s:%s:%s\r\n", sftp_cmd, act, str1.c_str(), str2.c_str());
|
ex_strformat(msg, 2048, "%d,%d,%s:%s", sftp_cmd, 0, str1.c_str(), str2.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
cp->rec.record_command(0, msg);
|
cp->rec.record_command(0, msg);
|
||||||
|
|
|
@ -8,9 +8,13 @@
|
||||||
</%block>
|
</%block>
|
||||||
|
|
||||||
<%block name="extend_css">
|
<%block name="extend_css">
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
#no-op-msg {
|
#op-box, #no-op-box {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#no-op-box {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 50px;
|
margin: 50px;
|
||||||
background-color: #fffed5;
|
background-color: #fffed5;
|
||||||
|
@ -18,41 +22,68 @@
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#op-list {
|
#op-list, .op-msg {
|
||||||
display: none;
|
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 20px 10px 20px 10px;
|
margin: 10px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
box-shadow: 1px 1px 2px rgba(0, 0, 0, .2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-item {
|
.op-item {
|
||||||
margin-bottom: 3px;
|
margin: 2px 3px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.op-item.bold {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-left: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time, .cmd, .path {
|
.time, .cmd, .path {
|
||||||
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
|
font-family: Consolas, Lucida Console, Monaco, Courier, 'Courier New', monospace;
|
||||||
font-size:13px;
|
|
||||||
line-height: 15px;
|
line-height: 15px;
|
||||||
padding: 0 5px;
|
padding: 2px 5px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
background-color: #d8d8d8;
|
background-color: #ececec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time.multi, .cmd-multi {
|
||||||
|
background-color: #fff2cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.path {
|
.path {
|
||||||
margin:0 5px 0 5px;
|
margin: 0 5px 0 5px;
|
||||||
|
background-color: #dbffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmd {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cmd-danger {
|
.cmd-danger {
|
||||||
background-color: #ffbba6;
|
background-color: #ffd2c2;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cmd-danger:before {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 16px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 3px;
|
||||||
|
margin-top: 0px;
|
||||||
|
color: #ff533e;
|
||||||
|
font-size: 16px;
|
||||||
|
content: "\f06a";
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
}
|
||||||
|
|
||||||
.cmd-info {
|
.cmd-info {
|
||||||
background-color: #b4fdb1;
|
background-color: #b4fdb1;
|
||||||
}
|
}
|
||||||
|
@ -71,59 +102,89 @@
|
||||||
</%block>
|
</%block>
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<div id="no-op-msg">
|
<div id="no-op-box">
|
||||||
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
||||||
</div>
|
</div>
|
||||||
|
<div id="op-box">
|
||||||
<div id="op-list"></div>
|
<div id="op-list"></div>
|
||||||
|
<div class="op-msg">
|
||||||
|
<div class="op-item bold">图例说明:</div>
|
||||||
|
## <div class="op-item"><span class="time multi">YYYY-mm-dd HH:MM:SS</span> <span class="cmd cmd-multi">此记录可能是被复制粘贴到SSH客户端的,有可能是批量执行命令,也可能是在做文本编辑,详情见录像回放。</span></div>
|
||||||
|
|
||||||
|
<div class="op-item"><span class="time">YYYY-mm-dd HH:MM:SS</span> <span class="cmd cmd-danger">此命令可能是危险操作。</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%block name="embed_js">
|
<%block name="embed_js">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var SSH_FXP_OPEN = 3;
|
||||||
|
var SSH_FXP_REMOVE = 13;
|
||||||
|
var SSH_FXP_MKDIR = 14;
|
||||||
|
var SSH_FXP_RMDIR = 15;
|
||||||
|
var SSH_FXP_RENAME = 18;
|
||||||
|
var SSH_FXP_LINK = 21;
|
||||||
|
|
||||||
$app.add_options(${page_param});
|
$app.add_options(${page_param});
|
||||||
|
|
||||||
$app.on_init = function (cb_stack, cb_args) {
|
$app.on_init = function (cb_stack, cb_args) {
|
||||||
|
$app.dom = {
|
||||||
|
rec_info: $('#recorder-info')
|
||||||
|
, no_op_box: $('#no-op-box')
|
||||||
|
, op_box: $('#op-box')
|
||||||
|
, op_list: $('#op-list')
|
||||||
|
};
|
||||||
|
|
||||||
console.log($app.options);
|
console.log($app.options);
|
||||||
|
|
||||||
var header = $app.options.header;
|
var header = $app.options.header;
|
||||||
$('#recorder-info').html(tp_format_datetime(header.start) + ': ' + header.user_name + '@' + header.client_ip + ' 访问 ' + header.account + '@' + header.conn_ip + ':' + header.conn_port);
|
$app.dom.rec_info.html(tp_format_datetime(header.start) + ': ' + header.user_name + '@' + header.client_ip + ' 访问 ' + header.account + '@' + header.conn_ip + ':' + header.conn_port);
|
||||||
|
|
||||||
|
var op = $app.options.op;
|
||||||
|
if (op.length === 0) {
|
||||||
|
$app.dom.no_op_box.show();
|
||||||
|
cb_stack.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($app.options.count === 0) {
|
|
||||||
$('#no-op-msg').show();
|
|
||||||
} else {
|
|
||||||
var dom_op_list = $('#op-list');
|
|
||||||
var html = [];
|
var html = [];
|
||||||
for (var i = 0; i < $app.options.count; i++) {
|
html.push('<div class="op-item bold">操作历史记录:</div>');
|
||||||
html.push('<div class="op-item"><span class="time">' + $app.options.op[i].t + '</span> ');
|
|
||||||
|
|
||||||
if ($app.options.op[i].c === '3') {
|
for (var i = 0; i < op.length; i++) {
|
||||||
|
var t = tp_format_datetime(header.start + parseInt(op[i].t / 1000));
|
||||||
|
html.push('<div class="op-item"><span class="time">' + t + '</span> ');
|
||||||
|
|
||||||
|
if (op[i].c === 3) {
|
||||||
html.push('<span class="cmd">打开文件</span>');
|
html.push('<span class="cmd">打开文件</span>');
|
||||||
html.push('<span class="path">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
} else if ($app.options.op[i].c === '13') {
|
} else if (op[i].c === 13) {
|
||||||
html.push('<span class="cmd cmd-danger">删除文件</span>');
|
html.push('<span class="cmd cmd-danger">删除文件</span>');
|
||||||
html.push('<span class="path cmd-danger">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
} else if ($app.options.op[i].c === '14') {
|
} else if (op[i].c === 14) {
|
||||||
html.push('<span class="cmd">创建目录</span>');
|
html.push('<span class="cmd">创建目录</span>');
|
||||||
html.push('<span class="path">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
} else if ($app.options.op[i].c === '15') {
|
} else if (op[i].c === 15) {
|
||||||
html.push('<span class="cmd cmd-danger">删除目录</span>');
|
html.push('<span class="cmd cmd-danger">删除目录</span>');
|
||||||
html.push('<span class="path cmd-danger">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
} else if ($app.options.op[i].c === '18') {
|
} else if (op[i].c === 18) {
|
||||||
html.push('<span class="cmd cmd-info">更改名称</span>');
|
html.push('<span class="cmd cmd-info">更改名称</span>');
|
||||||
html.push('<span class="path cmd-info">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
html.push('<i class="fa fa-arrow-circle-right"></i>');
|
html.push(' <i class="fa fa-long-arrow-right fa-fw"></i> ');
|
||||||
html.push('<span class="path cmd-info">' + $app.options.op[i].p2 + '</span>');
|
html.push('<span class="path">' + op[i].p2 + '</span>');
|
||||||
} else if ($app.options.op[i].c === '21') {
|
} else if (op[i].c === 21) {
|
||||||
html.push('<span class="cmd">创建链接</span>');
|
html.push('<span class="cmd">创建链接</span>');
|
||||||
html.push('<span class="path">' + $app.options.op[i].p2 + '</span>');
|
html.push('<span class="path">' + op[i].p2 + '</span>');
|
||||||
html.push('<i class="fa fa-arrow-right"></i>');
|
html.push(' <i class="fa fa-arrow-right fa-fw"></i> ');
|
||||||
html.push('<span class="path">' + $app.options.op[i].p1 + '</span>');
|
html.push('<span class="path">' + op[i].p1 + '</span>');
|
||||||
}
|
}
|
||||||
|
|
||||||
html.push('</div>');
|
html.push('</div>');
|
||||||
}
|
}
|
||||||
dom_op_list.append(html.join(''));
|
$app.dom.op_list.append(html.join(''));
|
||||||
dom_op_list.show();
|
$app.dom.op_box.show();
|
||||||
}
|
|
||||||
cb_stack.exec();
|
cb_stack.exec();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
<%block name="extend_css">
|
<%block name="extend_css">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
#err-box, #op-box, #no-msg-box {
|
#err-box, #op-box, #no-op-box {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#no-msg-box {
|
#no-op-box {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin: 50px;
|
margin: 50px;
|
||||||
background-color: #fffed5;
|
background-color: #fffed5;
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
错误原因:<span id="err-more-info" class="bold"></span>
|
错误原因:<span id="err-more-info" class="bold"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="no-msg-box">
|
<div id="no-op-box">
|
||||||
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
他悄悄地来,又悄悄地走,挥一挥衣袖,没有留下任何操作~~~~
|
||||||
</div>
|
</div>
|
||||||
<div id="op-box">
|
<div id="op-box">
|
||||||
|
@ -117,6 +117,7 @@
|
||||||
|
|
||||||
<%block name="embed_js">
|
<%block name="embed_js">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
"use strict";
|
||||||
|
|
||||||
$app.add_options(${page_param});
|
$app.add_options(${page_param});
|
||||||
|
|
||||||
|
@ -130,7 +131,7 @@
|
||||||
, rec_info: $('#recorder-info')
|
, rec_info: $('#recorder-info')
|
||||||
, err_box: $('#err-box')
|
, err_box: $('#err-box')
|
||||||
, err_more: $('#err-more-info')
|
, err_more: $('#err-more-info')
|
||||||
, no_msg_box: $('#no-op-msg')
|
, no_op_box: $('#no-op-box')
|
||||||
, op_box: $('#op-box')
|
, op_box: $('#op-box')
|
||||||
, op_list: $('#op-list')
|
, op_list: $('#op-list')
|
||||||
};
|
};
|
||||||
|
@ -151,7 +152,7 @@
|
||||||
|
|
||||||
var op = $app.options.op;
|
var op = $app.options.op;
|
||||||
if (op.length === 0) {
|
if (op.length === 0) {
|
||||||
$app.dom.no_msg_box.show();
|
$app.dom.no_op_box.show();
|
||||||
cb_stack.exec();
|
cb_stack.exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,11 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
|
<li>微信小程序
|
||||||
|
<ul class="list">
|
||||||
|
<li><a href="javascript:;" data-switch="wechat"><i class="fa fa-wechat fa-fw"></i> 二次验证码</a> <span class="label label-success">推荐</span></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li>谷歌身份验证器
|
<li>谷歌身份验证器
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
<li><a href="javascript:;" data-switch="g-ios-appstore"><i class="fa fa-apple fa-fw"></i> iOS(Apple Store)</a></li>
|
<li><a href="javascript:;" data-switch="g-ios-appstore"><i class="fa fa-apple fa-fw"></i> iOS(Apple Store)</a></li>
|
||||||
|
@ -149,11 +154,6 @@
|
||||||
<li><a href="javascript:;" data-switch="mi-android-mi"><i class="fa fa-android fa-fw"></i> Android(小米应用商店)</a></li>
|
<li><a href="javascript:;" data-switch="mi-android-mi"><i class="fa fa-android fa-fw"></i> Android(小米应用商店)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>微信小程序
|
|
||||||
<ul class="list">
|
|
||||||
<li><a href="javascript:;" data-switch="wechat"><i class="fa fa-wechat fa-fw"></i> 二次验证码</a> <span class="label label-success">推荐</span></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
|
|
|
@ -201,7 +201,7 @@ class ComandLogHandler(TPBaseHandler):
|
||||||
file = open(file_info, 'r')
|
file = open(file_info, 'r')
|
||||||
data = file.readlines()
|
data = file.readlines()
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
cmd = data[i].split(',', 2)
|
cmd = data[i].rstrip('\r\n').split(',', 2)
|
||||||
if len(cmd) != 3:
|
if len(cmd) != 3:
|
||||||
continue
|
continue
|
||||||
if 0 == i:
|
if 0 == i:
|
||||||
|
@ -215,13 +215,20 @@ class ComandLogHandler(TPBaseHandler):
|
||||||
if cmd_type == 0:
|
if cmd_type == 0:
|
||||||
param['op'].append({'t': t, 'f': f, 'c': cmd[2]})
|
param['op'].append({'t': t, 'f': f, 'c': cmd[2]})
|
||||||
else:
|
else:
|
||||||
cmd_info = cmd[2].split(':')
|
cmd_info = cmd[2].split(',', 2)
|
||||||
if len(cmd_info) != 4:
|
if len(cmd_info) != 3:
|
||||||
continue
|
continue
|
||||||
param['op'].append({'t': t, 'c': cmd[2], 'p1': cmd_info[2], 'p2': cmd_info[3]})
|
c = int(cmd_info[0])
|
||||||
|
r = int(cmd_info[1])
|
||||||
|
p = cmd_info[2].split(':')
|
||||||
|
p1 = p[0]
|
||||||
|
p2 = ''
|
||||||
|
if len(p) > 1:
|
||||||
|
p2 = p[1]
|
||||||
|
param['op'].append({'t': t, 'c': c, 'r': r, 'p1': p1, 'p2': p2})
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
param['count'] = len(param['op'])
|
# param['count'] = len(param['op'])
|
||||||
|
|
||||||
if cmd_type == 0:
|
if cmd_type == 0:
|
||||||
self.render('audit/record-ssh-cmd.mako', page_param=json.dumps(param))
|
self.render('audit/record-ssh-cmd.mako', page_param=json.dumps(param))
|
||||||
|
@ -277,19 +284,3 @@ class DoGetRecordDataHandler(TPBaseJsonHandler):
|
||||||
|
|
||||||
data_list, data_size, err = record.read_record_data(record_id, offset)
|
data_list, data_size, err = record.read_record_data(record_id, offset)
|
||||||
self.write_json(err, data={'data_list': data_list, 'data_size': data_size})
|
self.write_json(err, data={'data_list': data_list, 'data_size': data_size})
|
||||||
|
|
||||||
# class DeleteLog(TPBaseAdminAuthJsonHandler):
|
|
||||||
# # TODO: 用户可能会批量删除大量录像文件,因此io操作可能会比较耗时,这里应该改为异步方式。
|
|
||||||
# def post(self):
|
|
||||||
# args = self.get_argument('args', None)
|
|
||||||
# if args is not None:
|
|
||||||
# args = json.loads(args)
|
|
||||||
# else:
|
|
||||||
# return self.write_json(-1, '参数错误')
|
|
||||||
#
|
|
||||||
# log_list = args['log_list']
|
|
||||||
#
|
|
||||||
# if not record.delete_log(log_list):
|
|
||||||
# return self.write_json(-3, '操作失败')
|
|
||||||
#
|
|
||||||
# return self.write_json(0)
|
|
||||||
|
|
Loading…
Reference in New Issue