Now core server not access database directly anymore.

TODO: tornado must use asynchronous handler otherwise core and web will lock each other.
pull/32/merge
Apex Liu 2017-03-02 06:12:37 +08:00
parent b72ef8f8f9
commit 411eab32f0
31 changed files with 1544 additions and 2111 deletions

View File

@ -1,6 +1,8 @@
# IDEA
# 一些想法
简化core服务使之不用直接访问数据库
## 服务解耦
将core服务和web服务解耦合让core服务不直接访问数据库。
优点:
@ -13,16 +15,61 @@
目前的core服务中直接访问数据库涉及到
目前的core服务中直接访问数据库涉及到
- 生成会话ID需要根据认证ID获取认证信息可改为web查询数据库然后将认证信息传递给core服务。
- 生成会话ID需要根据认证ID获取认证信息
- 更新当前连接状态(连接、结束,出错等);
可改为web查询数据库然后将数据传递给core服务。_但是这里有一个潜在的安全漏洞认证信息中包含的私密信息会在网络上传播了。_解决的办法是如果core和web在同一台主机上没有漏洞如果不在同一台主机上core需要配置一个密钥放在配置文件中web配置时需要输入此密钥才能与指定的core连接且传输隐秘数据时使用此密钥进行加密。
## 会话ID
关于会话ID的有效期现有版本是设置一个引用计数对于rdp可以使用两次而ssh只能使用一次原因是rdp连接时通常第一次连接用于协商子协议然后会断开并立即重连并使用确定好的子协议进行后续操作。
改进的方法是为一个会话ID设置一个标志及一个最后活动时间会话ID刚生成时标志为false调用take_session()时设置为true当连接断开时或者未能连接成功时重置为false并更新最后活动时间为当前时间。标志为true时不允许重复take_session()每次调用take_session()时检查标志为false的回话ID的最后活动时间如果超过5秒未活动则删除此会话ID。
这样做可以适配各种连接方式。
这样做可以适配各种连接方式。
## 用户体验
纯净安装基本上就是复制文件没有别的操作了。安装完成后提示teleport的运维管理WEB服务的URL让用户进行访问。
### 纯净安装
**尽可能使用WEB-UI进行配置**
完全新安装的服务端仅启动了web服务但是web服务的监听IP和端口如何设定监听IP可以设置为在0.0.0.0上监听但是端口如何设定。因此端口必须用配置文件的方式进行设定。其次对于log级别、log输出路径等设定涉及到系统的调试可能web服务根本就启动不了所以无法通过web界面进行设置因此也需要通过配置文件的方式设定。
core与web之间通过JSON-RPC方式进行通讯core并不直接操作数据库而是转由web提供RPC接口进行操作。web服务并不直接读取core的配置而是通过core提供的RPC接口获取或修改其配置。
core服务提供RPC接口能够获取其内部状态例如当前连接数、网络IO负载等等。
可以通过RPC接口停止或重启core和web服务。
web服务启动后访问登录页面但是如果缺少配置数据表就跳转到配置页面。
- 设定所用数据库可选本地sqlite或者MySQL如果是MySQL需要给出root权限或者指定有权限访问的数据库和用户名及密码以及teleport相关表的前缀默认为“tp_”注意这些信息必须存放在配置文件中因此配置文件需要支持写入操作
- 创建配置数据库(包括用户表、配置表等);
- 设定管理员密码管理员用户名固定为admin
- 设置core服务的地址和端口默认是本机127.0.0.1端口包括RPC端口、SSH/RDP/Telnet端口等
- 设定core服务存放日志回放记录文件的目录地址
- core和web的配置信息均存放在.ini配置文件中方便特定情况下用户手工编辑
安装时需要用户选择安装core还是web还是二者皆安装。
安装完成后core服务和web服务均会启动在第一次启动时需要创建config数据库里面是一堆空表只有用户表里面有一个管理员账号admin安装部署人员可以使用此账号登陆系统。
需要增强服务的自检功能例如端口已经被占用等需要在日志中给出明确的错误信息。另外考虑到将来core服务可能部署到多个主机上以提升承载能力所以web需要访问core的数据时包括取日志回放文件数据等均使用RPC接口进行。
第一次登陆系统,会自动跳转到配置向导界面,让用户一步一步进行服务器配置。
-

File diff suppressed because it is too large Load Diff

View File

@ -79,13 +79,13 @@ public:
//session 结束
bool session_end(int id,int ret_code);
//获取所有的认证ID
bool get_auth_id_list_by_all(AuthInfo2Vec& auth_info_list);
// bool get_auth_id_list_by_all(AuthInfo2Vec& auth_info_list);
//通过IP获取认证ID
bool get_auth_id_list_by_ip(ex_astr host_ip, AuthInfo2Vec& auth_info_list);
// bool get_auth_id_list_by_ip(ex_astr host_ip, AuthInfo2Vec& auth_info_list);
//获取所有的认证的信息
bool get_auth_info_list_by_all(AuthInfo3Vec& auth_info_list);
// bool get_auth_info_list_by_all(AuthInfo3Vec& auth_info_list);
//通过IP获取认证信息
bool get_auth_info_list_by_ip(ex_astr host_ip, AuthInfo3Vec& auth_info_list);
// bool get_auth_info_list_by_ip(ex_astr host_ip, AuthInfo3Vec& auth_info_list);
// //获取server 的配置信息
// bool get_server_config(TS_DB_SERVER_CONFIG* server_config);
//

View File

@ -1,50 +1,108 @@
#include "ts_http_client.h"
#include <mongoose.h>
#include<map>
using namespace std;
#include <ex/ex_str.h>
map<unsigned int, unsigned int> session_map;
// #include<map>
// using namespace std;
//map<unsigned int, unsigned int> session_map;
int s_exit_flag = 0;
void ts_url_encode(const char *src, ex_astr& out)
{
static const char *dont_escape = "._-$,;~()/";
static const char *hex = "0123456789abcdef";
size_t s_len = strlen(src);
size_t dst_len = s_len * 3 + 1;
char* dst = new char[dst_len];
memset(dst, 0, dst_len);
size_t i = 0, j = 0;
for (i = j = 0; dst_len > 0 && i < s_len && j + 2 < dst_len - 1; i++, j++) {
if (isalnum(*(const unsigned char *)(src + i)) ||
strchr(dont_escape, *(const unsigned char *)(src + i)) != NULL) {
dst[j] = src[i];
}
else if (j + 3 < dst_len) {
dst[j] = '%';
dst[j + 1] = hex[(*(const unsigned char *)(src + i)) >> 4];
dst[j + 2] = hex[(*(const unsigned char *)(src + i)) & 0xf];
j += 2;
}
}
dst[j] = '\0';
out = dst;
delete []dst;
}
typedef struct HTTP_DATA {
bool exit_flag;
bool have_error;
ex_astr body;
}HTTP_DATA;
//int s_exit_flag = 0;
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
{
HTTP_DATA* hdata = (HTTP_DATA*)nc->user_data;
struct http_message *hm = (struct http_message *) ev_data;
switch (ev) {
case MG_EV_CONNECT:
if (*(int *)ev_data != 0) {
s_exit_flag = 1;
hdata->exit_flag = true;
hdata->have_error = true;
}
break;
case MG_EV_HTTP_REPLY:
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
s_exit_flag = 1;
hdata->exit_flag = true;
hdata->body.assign(hm->body.p, hm->body.len);
break;
case MG_EV_CLOSE:
// if (s_exit_flag == 0) {
// printf("Server closed connection\n");
// s_exit_flag = 1;
// }
hdata->exit_flag = true;
break;
default:
break;
}
}
bool ts_http_get(ex_astr url)
bool ts_http_get(const ex_astr& url, ex_astr& body)
{
struct mg_mgr mgr;
mg_mgr_init(&mgr, NULL);
mg_connection* con = mg_connect_http(&mgr, ev_handler, url.c_str(), NULL, NULL);
if (NULL == con)
mg_connection* nc = mg_connect_http(&mgr, ev_handler, url.c_str(), NULL, NULL);
if (NULL == nc)
return false;
int count = 0;
while (s_exit_flag == 0)
HTTP_DATA* hdata = new HTTP_DATA;
hdata->exit_flag = false;
hdata->have_error = false;
nc->user_data = hdata;
// int count = 0;
while (!hdata->exit_flag)
{
mg_mgr_poll(&mgr, 1000);
count++;
if (count > 2)
break;
mg_mgr_poll(&mgr, 100);
// count++;
// if (count > 2)
// break;
}
bool ret = !hdata->have_error;
if (ret)
body = hdata->body;
delete hdata;
mg_mgr_free(&mgr);
return true;
return ret;
}

View File

@ -3,6 +3,7 @@
#include <ex.h>
bool ts_http_get(ex_astr url);
void ts_url_encode(const char *src, ex_astr& out);
bool ts_http_get(const ex_astr& url, ex_astr& body);
#endif // __TS_HTTP_CLIENT_H__

View File

@ -73,7 +73,6 @@ void TsHttpRpc::_set_stop_flag(void)
m_stop_flag = true;
}
bool TsHttpRpc::init(void)
{
struct mg_connection* nc = NULL;
@ -118,36 +117,43 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
{
case MG_EV_HTTP_REQUEST:
{
#ifdef EX_DEBUG
const char* dbg_method = NULL;
if (hm->method.len == 3 && 0 == memcmp(hm->method.p, "GET", hm->method.len))
dbg_method = "GET";
else if (hm->method.len == 4 && 0 == memcmp(hm->method.p, "POST", hm->method.len))
dbg_method = "POST";
else
dbg_method = "UNSUPPORTED-HTTP-METHOD";
ex_chars dbg_uri;
dbg_uri.resize(hm->uri.len + 1);
memset(&dbg_uri[0], 0, hm->uri.len + 1);
memcpy(&dbg_uri[0], hm->uri.p, hm->uri.len);
EXLOGV("[core-rpc] got %s request: %s\n", dbg_method, &dbg_uri[0]);
#endif
ex_astr ret_buf;
ex_astr method;
ex_astr json_param;
ex_rv rv = _this->_parse_request(hm, method, json_param);
if (TSR_OK != rv)
//const char* dbg_method = NULL;
// ex_chars _uri;
// _uri.resize(hm->uri.len + 1);
// memset(&_uri[0], 0, hm->uri.len + 1);
// memcpy(&_uri[0], hm->uri.p, hm->uri.len);
// ex_astr uri = &_uri[0];
ex_astr uri;
uri.assign(hm->uri.p, hm->uri.len);
EXLOGV("got request: %s\n", uri.c_str());
if (uri == "/rpc")
{
EXLOGE("[core-rpc] got invalid request.\n");
_this->_create_json_ret(ret_buf, rv);
ex_astr method;
Json::Value json_param;
ex_rv rv = _this->_parse_request(hm, method, json_param);
if (TSR_OK != rv)
{
EXLOGE("[core-rpc] got invalid request.\n");
_this->_create_json_ret(ret_buf, rv);
}
else
{
_this->_process_request(method, json_param, ret_buf);
}
}
else
{
_this->_process_js_request(method, json_param, ret_buf);
EXLOGE("[core-rpc] got invalid request: not `rpc` uri.\n");
_this->_create_json_ret(ret_buf, TSR_INVALID_REQUEST, "not a `rpc` request.");
}
mg_printf(nc, "HTTP/1.0 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: %d\r\nContent-Type: application/json\r\n\r\n%s", (int)ret_buf.size() - 1, &ret_buf[0]);
nc->flags |= MG_F_SEND_AND_CLOSE;
}
@ -157,7 +163,7 @@ void TsHttpRpc::_mg_event_handler(struct mg_connection *nc, int ev, void *ev_dat
}
}
ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_astr& func_args)
ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param)
{
if (NULL == req)
return TSR_INVALID_REQUEST;
@ -170,94 +176,59 @@ ex_rv TsHttpRpc::_parse_request(struct http_message* req, ex_astr& func_cmd, ex_
else
return TSR_INVALID_REQUEST;
std::vector<ex_astr> strs;
ex_astr json_str;
if (is_get)
json_str.assign(req->query_string.p, req->query_string.len);
else
json_str.assign(req->body.p, req->body.len);
size_t pos_start = 1; // 跳过第一个字节,一定是 '/'
size_t i = 0;
for (i = pos_start; i < req->uri.len; ++i)
{
if (req->uri.p[i] == '/')
{
if (i - pos_start > 0)
{
ex_astr tmp_uri;
tmp_uri.assign(req->uri.p + pos_start, i - pos_start);
strs.push_back(tmp_uri);
}
pos_start = i + 1; // 跳过当前找到的分隔符
}
}
if (pos_start < req->uri.len)
{
ex_astr tmp_uri;
tmp_uri.assign(req->uri.p + pos_start, req->uri.len - pos_start);
strs.push_back(tmp_uri);
}
if(0 == strs.size())
if (0 == json_str.length())
return TSR_INVALID_REQUEST;
if (is_get)
{
if (1 == strs.size())
{
func_cmd = strs[0];
}
else if (2 == strs.size())
{
func_cmd = strs[0];
func_args = strs[1];
}
else
{
return TSR_INVALID_REQUEST;
}
}
else
{
if (1 == strs.size())
{
func_cmd = strs[0];
}
else
{
return TSR_INVALID_REQUEST;
}
// 将参数进行 url-decode 解码
int len = json_str.length() * 2;
ex_chars sztmp;
sztmp.resize(len);
memset(&sztmp[0], 0, len);
if (-1 == ts_url_decode(json_str.c_str(), json_str.length(), &sztmp[0], len, 0))
return TSR_INVALID_URL_ENCODE;
if (req->body.len > 0)
{
func_args.assign(req->body.p, req->body.len);
}
}
json_str = &sztmp[0];
if (func_args.length()>0)
{
// 将参数进行 url-decode 解码
int len = func_args.length() * 2;
ex_chars sztmp;
sztmp.resize(len);
memset(&sztmp[0], 0, len);
if (-1 == ts_url_decode(func_args.c_str(), func_args.length(), &sztmp[0], len, 0))
return TSR_INVALID_URL_ENCODE;
func_args = &sztmp[0];
}
//EXLOGV("[rpc] method=%s, json_param=%s\n", func_cmd.c_str(), func_args.c_str());
Json::Reader jreader;
if (!jreader.parse(json_str.c_str(), json_param))
return TSR_INVALID_JSON_FORMAT;
if (json_param.isArray())
return TSR_INVALID_JSON_PARAM;
if (json_param["method"].isNull() || !json_param["method"].isString())
return TSR_INVALID_JSON_PARAM;
func_cmd = json_param["method"].asCString();
json_param = json_param["param"];
return TSR_OK;
}
void TsHttpRpc::_create_json_ret(ex_astr& buf, Json::Value& jr_root)
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_data)
{
// 返回: {"code":errcode, "data":{jr_data}}
Json::FastWriter jr_writer;
Json::Value jr_root;
jr_root["code"] = errcode;
jr_root["data"] = jr_data;
buf = jr_writer.write(jr_root);
}
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode)
{
// 返回: {"code":123}
// 返回: {"code":errcode}
Json::FastWriter jr_writer;
Json::Value jr_root;
@ -266,27 +237,31 @@ void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode)
buf = jr_writer.write(jr_root);
}
void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func_args, ex_astr& buf)
void TsHttpRpc::_create_json_ret(ex_astr& buf, int errcode, const char* message)
{
// 返回: {"code":errcode, "message":message}
Json::FastWriter jr_writer;
Json::Value jr_root;
jr_root["code"] = errcode;
jr_root["message"] = message;
buf = jr_writer.write(jr_root);
}
void TsHttpRpc::_process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf)
{
if (func_cmd == "request_session")
{
_rpc_func_request_session(func_args, buf);
_rpc_func_request_session(json_param, buf);
}
else if (func_cmd == "enc")
{
_rpc_func_enc(func_args, buf);
_rpc_func_enc(json_param, buf);
}
// else if (func_cmd == "get_auth_id")
// {
// _rpc_func_get_auth_id(func_args, buf);
// }
// else if (func_cmd == "get_auth_info")
// {
// _rpc_func_get_auth_info(func_args, buf);
// }
else if (func_cmd == "exit")
{
_rpc_func_exit(func_args, buf);
_rpc_func_exit(json_param, buf);
}
else
{
@ -296,183 +271,211 @@ void TsHttpRpc::_process_js_request(const ex_astr& func_cmd, const ex_astr& func
}
extern bool g_exit_flag; // 要求整个TS退出的标志用于停止各个工作线程
void TsHttpRpc::_rpc_func_exit(const ex_astr& func_args, ex_astr& buf)
void TsHttpRpc::_rpc_func_exit(const Json::Value& json_param, ex_astr& buf)
{
// 设置一个全局退出标志
g_exit_flag = true;
_create_json_ret(buf, TSR_OK);
return;
}
void TsHttpRpc::_rpc_func_request_session(const ex_astr& func_args, ex_astr& buf)
void TsHttpRpc::_rpc_func_request_session(const Json::Value& json_param, ex_astr& buf)
{
// 申请一个会话ID
// 入参: 两种模式
// MODE A: 已知目标服务器信息及认证信息
// 示例: {"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2,"enc":0}
// ip: 目标服务器IP地址
// port: 目标服务器端口
// uname: 目标服务器认证所用的用户名
// uauth: 目标服务器认证所用的密码或私钥
// authmode: 1=password, 2=private-key
// protocol: 1=rdp, 2=ssh
// enc: 1=uauth中的内容是加密的0=uauth中的内容是明文仅用于开发测试阶段
// MODE B: 认证ID需要根据这个ID到数据库中取得目标服务器信息及认证信息
// 示例: {"authid":123456}
// 返回:
// SSH返回 {"code":0, "data":{"sid":"0123abcde"}}
// RDP返回 {"code":0, "data":{"sid":"0123abcde0A"}}
// 错误返回: {"code":1234}
// https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#request_session
Json::Reader jreader;
Json::Value jsRoot;
int authid = 0;
if (!jreader.parse(func_args.c_str(), jsRoot))
{
_create_json_ret(buf, TSR_INVALID_JSON_FORMAT);
return;
}
if (jsRoot.isArray())
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
}
ex_astr host_ip;
int host_port = 0;
int sys_type = 0;
ex_astr user_name;
ex_astr user_auth;
ex_astr user_param;
ex_astr account_name;
int auth_mode = 0;
int protocol = 0;
int is_enc = 1;
int auth_id = 0;
// 入参模式
if (!jsRoot["auth_id"].isNull())
if (!json_param["authid"].isNull())
{
// 使用认证ID的方式申请SID
if (!jsRoot["auth_id"].isNumeric())
if (!json_param["authid"].isNumeric())
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
}
auth_id = jsRoot["auth_id"].asUInt();
authid = json_param["authid"].asUInt();
TS_DB_AUTH_INFO ts_auth_info;
if (!g_db.get_auth_info(auth_id, ts_auth_info))
if (!g_db.get_auth_info(authid, ts_auth_info))
{
_create_json_ret(buf, TSR_GETAUTH_INFO_ERROR);
return;
}
if (ts_auth_info.host_lock !=0 )
{
_create_json_ret(buf, TSR_HOST_LOCK_ERROR);
return;
}
if (ts_auth_info.account_lock != 0)
{
_create_json_ret(buf, TSR_ACCOUNT_LOCK_ERROR);
return;
}
host_ip = ts_auth_info.host_ip;
host_port = ts_auth_info.host_port;
sys_type = ts_auth_info.sys_type;
user_name = ts_auth_info.user_name;
user_auth = ts_auth_info.user_auth;
user_param = ts_auth_info.user_param;
auth_mode = ts_auth_info.auth_mode;
protocol = ts_auth_info.protocol;
is_enc = ts_auth_info.is_encrypt;
account_name = ts_auth_info.account_name;
}
else
{
// 判断参数是否正确
if (jsRoot["ip"].isNull() || !jsRoot["ip"].isString()
|| jsRoot["port"].isNull() || !jsRoot["port"].isNumeric()
|| jsRoot["systype"].isNull() || !jsRoot["systype"].isNumeric()
|| jsRoot["account"].isNull() || !jsRoot["account"].isString()
|| jsRoot["uname"].isNull() || !jsRoot["uname"].isString()
|| jsRoot["uauth"].isNull() || !jsRoot["uauth"].isString()
|| jsRoot["authmode"].isNull() || !jsRoot["authmode"].isNumeric()
|| jsRoot["protocol"].isNull() || !jsRoot["protocol"].isNumeric()
|| jsRoot["enc"].isNull() || !jsRoot["enc"].isNumeric()
)
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
}
host_ip = jsRoot["ip"].asCString();
host_port = jsRoot["port"].asUInt();
sys_type = jsRoot["systype"].asUInt();
account_name = jsRoot["account"].asCString();
user_name = jsRoot["uname"].asCString();
user_auth = jsRoot["uauth"].asCString();
if (jsRoot["uparam"].isNull())
{
user_param = "";
}
else
{
user_param = jsRoot["uparam"].asCString();
}
auth_mode = jsRoot["authmode"].asUInt();
protocol = jsRoot["protocol"].asUInt();
is_enc = jsRoot["enc"].asUInt();
}
// 进一步判断参数是否合法
if (host_ip.length() == 0 || host_port >= 65535 || account_name.length() == 0
|| !(auth_mode == TS_AUTH_MODE_NONE || auth_mode == TS_AUTH_MODE_PASSWORD || auth_mode == TS_AUTH_MODE_PRIVATE_KEY)
|| !(protocol == TS_PROXY_PROTOCOL_RDP || protocol == TS_PROXY_PROTOCOL_SSH || protocol == TS_PROXY_PROTOCOL_TELNET)
|| !(is_enc == 0 || is_enc == 1)
)
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
}
if(is_enc)
{
if (user_auth.length() > 0)
{
ex_astr _auth;
if (!ts_db_field_decrypt(user_auth, _auth))
{
_create_json_ret(buf, TSR_FAILED);
return;
}
user_auth = _auth;
}
}
// 生成一个session-id内部会避免重复
ex_astr sid;
ex_rv rv = g_session_mgr.request_session(sid, account_name, auth_id,
host_ip, host_port, sys_type, protocol,
user_name, user_auth, user_param, auth_mode);
if (rv != TSR_OK)
{
_create_json_ret(buf, rv);
return;
}
EXLOGD("[core-rpc] new session-id: %s\n", sid.c_str());
Json::Value jr_root;
jr_root["code"] = TSR_OK;
jr_root["data"]["sid"] = sid;
_create_json_ret(buf, jr_root);
_create_json_ret(buf, TSR_FAILED);
}
void TsHttpRpc::_rpc_func_enc(const ex_astr& func_args, ex_astr& buf)
// void TsHttpRpc::_rpc_func_request_session(const ex_astr& func_args, ex_astr& buf)
// {
// // 申请一个会话ID
// // 入参: 两种模式
// // MODE A: 已知目标服务器信息及认证信息
// // 示例: {"ip":"192.168.5.11","port":22,"uname":"root","uauth":"abcdefg","authmode":1,"protocol":2,"enc":0}
// // ip: 目标服务器IP地址
// // port: 目标服务器端口
// // uname: 目标服务器认证所用的用户名
// // uauth: 目标服务器认证所用的密码或私钥
// // authmode: 1=password, 2=private-key
// // protocol: 1=rdp, 2=ssh
// // enc: 1=uauth中的内容是加密的0=uauth中的内容是明文仅用于开发测试阶段
// // MODE B: 认证ID需要根据这个ID到数据库中取得目标服务器信息及认证信息
// // 示例: {"authid":123456}
// // 返回:
// // SSH返回 {"code":0, "data":{"sid":"0123abcde"}}
// // RDP返回 {"code":0, "data":{"sid":"0123abcde0A"}}
// // 错误返回: {"code":1234}
//
// Json::Reader jreader;
// Json::Value jsRoot;
//
// if (!jreader.parse(func_args.c_str(), jsRoot))
// {
// _create_json_ret(buf, TSR_INVALID_JSON_FORMAT);
// return;
// }
// if (jsRoot.isArray())
// {
// _create_json_ret(buf, TSR_INVALID_JSON_PARAM);
// return;
// }
//
// ex_astr host_ip;
// int host_port = 0;
// int sys_type = 0;
// ex_astr user_name;
// ex_astr user_auth;
// ex_astr user_param;
//
// ex_astr account_name;
// int auth_mode = 0;
// int protocol = 0;
// int is_enc = 1;
// int auth_id = 0;
// // 入参模式
// if (!jsRoot["auth_id"].isNull())
// {
// // 使用认证ID的方式申请SID
// if (!jsRoot["auth_id"].isNumeric())
// {
// _create_json_ret(buf, TSR_INVALID_JSON_PARAM);
// return;
// }
// auth_id = jsRoot["auth_id"].asUInt();
// TS_DB_AUTH_INFO ts_auth_info;
// if (!g_db.get_auth_info(auth_id, ts_auth_info))
// {
// _create_json_ret(buf, TSR_GETAUTH_INFO_ERROR);
// return;
// }
// if (ts_auth_info.host_lock !=0 )
// {
// _create_json_ret(buf, TSR_HOST_LOCK_ERROR);
// return;
// }
// if (ts_auth_info.account_lock != 0)
// {
// _create_json_ret(buf, TSR_ACCOUNT_LOCK_ERROR);
// return;
// }
// host_ip = ts_auth_info.host_ip;
// host_port = ts_auth_info.host_port;
// sys_type = ts_auth_info.sys_type;
// user_name = ts_auth_info.user_name;
// user_auth = ts_auth_info.user_auth;
// user_param = ts_auth_info.user_param;
// auth_mode = ts_auth_info.auth_mode;
// protocol = ts_auth_info.protocol;
// is_enc = ts_auth_info.is_encrypt;
// account_name = ts_auth_info.account_name;
// }
// else
// {
// // 判断参数是否正确
// if (jsRoot["ip"].isNull() || !jsRoot["ip"].isString()
// || jsRoot["port"].isNull() || !jsRoot["port"].isNumeric()
// || jsRoot["systype"].isNull() || !jsRoot["systype"].isNumeric()
// || jsRoot["account"].isNull() || !jsRoot["account"].isString()
// || jsRoot["uname"].isNull() || !jsRoot["uname"].isString()
// || jsRoot["uauth"].isNull() || !jsRoot["uauth"].isString()
// || jsRoot["authmode"].isNull() || !jsRoot["authmode"].isNumeric()
// || jsRoot["protocol"].isNull() || !jsRoot["protocol"].isNumeric()
// || jsRoot["enc"].isNull() || !jsRoot["enc"].isNumeric()
// )
// {
// _create_json_ret(buf, TSR_INVALID_JSON_PARAM);
// return;
// }
//
// host_ip = jsRoot["ip"].asCString();
// host_port = jsRoot["port"].asUInt();
// sys_type = jsRoot["systype"].asUInt();
// account_name = jsRoot["account"].asCString();
// user_name = jsRoot["uname"].asCString();
// user_auth = jsRoot["uauth"].asCString();
// if (jsRoot["uparam"].isNull())
// {
// user_param = "";
// }
// else
// {
// user_param = jsRoot["uparam"].asCString();
// }
//
// auth_mode = jsRoot["authmode"].asUInt();
// protocol = jsRoot["protocol"].asUInt();
// is_enc = jsRoot["enc"].asUInt();
// }
//
// // 进一步判断参数是否合法
// if (host_ip.length() == 0 || host_port >= 65535 || account_name.length() == 0
// || !(auth_mode == TS_AUTH_MODE_NONE || auth_mode == TS_AUTH_MODE_PASSWORD || auth_mode == TS_AUTH_MODE_PRIVATE_KEY)
// || !(protocol == TS_PROXY_PROTOCOL_RDP || protocol == TS_PROXY_PROTOCOL_SSH || protocol == TS_PROXY_PROTOCOL_TELNET)
// || !(is_enc == 0 || is_enc == 1)
// )
// {
// _create_json_ret(buf, TSR_INVALID_JSON_PARAM);
// return;
// }
//
// if(is_enc)
// {
// if (user_auth.length() > 0)
// {
// ex_astr _auth;
// if (!ts_db_field_decrypt(user_auth, _auth))
// {
// _create_json_ret(buf, TSR_FAILED);
// return;
// }
//
// user_auth = _auth;
// }
// }
//
// // 生成一个session-id内部会避免重复
// ex_astr sid;
// ex_rv rv = g_session_mgr.request_session(sid, account_name, auth_id,
// host_ip, host_port, sys_type, protocol,
// user_name, user_auth, user_param, auth_mode);
// if (rv != TSR_OK)
// {
// _create_json_ret(buf, rv);
// return;
// }
//
// EXLOGD("[core-rpc] new session-id: %s\n", sid.c_str());
//
// Json::Value jr_root;
// jr_root["code"] = TSR_OK;
// jr_root["data"]["sid"] = sid;
//
// _create_json_ret(buf, jr_root);
// }
void TsHttpRpc::_rpc_func_enc(const Json::Value& json_param, ex_astr& buf)
{
// https://github.com/eomsoft/teleport/wiki/TELEPORT-CORE-JSON-RPC#enc
// 加密一个字符串 [ p=plain-text, c=cipher-text ]
// 入参: {"p":"need be encrypt"}
// 示例: {"p":"this-is-a-password"}
@ -482,15 +485,7 @@ void TsHttpRpc::_rpc_func_enc(const ex_astr& func_args, ex_astr& buf)
// 示例: {"code":0, "data":{"c":"Mxs340a9r3fs+3sdf=="}}
// 错误返回: {"code":1234}
Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot))
{
_create_json_ret(buf, TSR_INVALID_JSON_FORMAT);
return;
}
if (jsRoot.isArray())
if (json_param.isArray())
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
@ -498,13 +493,13 @@ void TsHttpRpc::_rpc_func_enc(const ex_astr& func_args, ex_astr& buf)
ex_astr plain_text;
if (jsRoot["p"].isNull() || !jsRoot["p"].isString())
if (json_param["p"].isNull() || !json_param["p"].isString())
{
_create_json_ret(buf, TSR_INVALID_JSON_PARAM);
return;
}
plain_text = jsRoot["p"].asCString();
plain_text = json_param["p"].asCString();
if (plain_text.length() == 0)
{
_create_json_ret(buf, TSR_DATA_LEN_ZERO);
@ -518,11 +513,9 @@ void TsHttpRpc::_rpc_func_enc(const ex_astr& func_args, ex_astr& buf)
return;
}
Json::Value jr_root;
jr_root["code"] = TSR_OK;
jr_root["data"]["c"] = cipher_text;
_create_json_ret(buf, jr_root);
Json::Value jr_data;
jr_data["c"] = cipher_text;
_create_json_ret(buf, TSR_OK, jr_data);
}
#if 0

View File

@ -47,22 +47,20 @@ protected:
void _set_stop_flag(void);
private:
ex_rv _parse_request(struct http_message* req, ex_astr& func_cmd, ex_astr& func_args);
void _process_js_request(const ex_astr& func_cmd, const ex_astr& func_args, ex_astr& buf);
ex_rv _parse_request(struct http_message* req, ex_astr& func_cmd, Json::Value& json_param);
void _process_request(const ex_astr& func_cmd, const Json::Value& json_param, ex_astr& buf);
void _create_json_ret(ex_astr& buf, Json::Value& jr_root);
//void _create_json_ret(ex_astr& buf, Json::Value& jr_root);
void _create_json_ret(ex_astr& buf, int errcode, const Json::Value& jr_root);
void _create_json_ret(ex_astr& buf, int errcode);
void _create_json_ret(ex_astr& buf, int errcode, const char* message);
// 请求一个会话ID
void _rpc_func_request_session(const ex_astr& func_args, ex_astr& buf);
void _rpc_func_request_session(const Json::Value& json_param, ex_astr& buf);
// 加密一个字符串返回的是密文的BASE64编码
void _rpc_func_enc(const ex_astr& func_args, ex_astr& buf);
// void _rpc_func_get_auth_id(const ex_astr& func_args, ex_astr& buf);
// void _rpc_func_get_auth_info(const ex_astr& func_args, ex_astr& buf);
// ÒªÇóÕû¸öTSÍ˳ö
void _rpc_func_exit(const ex_astr& func_args, ex_astr& buf);
void _rpc_func_enc(const Json::Value& json_param, ex_astr& buf);
// ÒªÇóÕû¸öºËÐÄ·þÎñÍ˳ö
void _rpc_func_exit(const Json::Value& json_param, ex_astr& buf);
static void _mg_event_handler(struct mg_connection *nc, int ev, void *ev_data);

View File

@ -1,18 +1,14 @@
# -*- coding: utf-8 -*-
from .core import SwxCore
from .core import WebServerCore
from eom_common.eomcore.logger import *
__all__ = ['run']
def run(options):
log.initialize()
# log.set_attribute(min_level=LOG_VERBOSE, log_datetime=False, trace_error=TRACE_ERROR_NONE)
# log.set_attribute(min_level=LOG_VERBOSE, trace_error=TRACE_ERROR_NONE)
# log.set_attribute(min_level=LOG_DEBUG, trace_error=TRACE_ERROR_FULL)
_app = SwxCore()
_app = WebServerCore()
if not _app.init(options):
return 1

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import os
# import sys
import configparser
from eom_common.eomcore.logger import *
@ -28,9 +27,6 @@ class AttrDict(dict):
class ConfigFile(AttrDict):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self.__file_name = None
# self.__save_indent = 0
# self.__loaded = False
def load_web(self, cfg_file):
if not os.path.exists(cfg_file):
@ -67,7 +63,7 @@ class ConfigFile(AttrDict):
else:
self['log_level'] = LOG_VERBOSE
log.set_attribute(min_level=self['log_level'])
# log.set_attribute(min_level=self['log_level'])
return True
@ -112,31 +108,6 @@ class ConfigFile(AttrDict):
self['core']['telnet']['enabled'] = _cfg['protocol-telnet'].getboolean('enabled', False)
self['core']['telnet']['port'] = _cfg['protocol-telnet'].getint('bind-port', 52389)
# if 'common' not in _cfg:
# log.e('invalid configuration file: [{}]\n'.format(cfg_file))
# return False
#
# _comm = _cfg['common']
# self['server_port'] = _comm.getint('port', 7190)
# self['log_file'] = _comm.get('log-file', None)
# if self['log_file'] is not None:
# self['log_path'] = os.path.dirname(self['log_file'])
#
# _level = _comm.getint('log-level', 2)
# if _level == 0:
# self['log_level'] = LOG_DEBUG
# elif _level == 1:
# self['log_level'] = LOG_VERBOSE
# elif _level == 2:
# self['log_level'] = LOG_INFO
# elif _level == 3:
# self['log_level'] = LOG_WARN
# elif _level == 4:
# self['log_level'] = LOG_ERROR
# else:
# self['log_level'] = LOG_VERBOSE
return True

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
APP_MODE_UNKNOWN = 0
APP_MODE_NORMAL = 1
APP_MODE_INSTALL = 2
APP_MODE_UPGRADE = 3
APP_MODE_MAINTENANCE = 4

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import os
import sys
@ -12,150 +13,63 @@ import tornado.web
from eom_common.eomcore.eom_sqlite import get_sqlite_pool
import eom_common.eomcore.utils as utils
from eom_common.eomcore.logger import log
from .const import *
from .configs import app_cfg
from .session import swx_session
from .session import web_session
cfg = app_cfg()
class SwxCore:
class WebServerCore:
def __init__(self):
# self._cfg = ConfigFile()
pass
def init(self, options):
cfg.debug = False
log.initialize()
cfg.dev_mode = options['dev_mode']
cfg.app_path = os.path.abspath(options['app_path'])
cfg.static_path = os.path.abspath(options['static_path'])
cfg.data_path = os.path.abspath(options['data_path'])
cfg.template_path = os.path.abspath(options['template_path'])
cfg.res_path = os.path.abspath(options['res_path'])
cfg.cfg_path = os.path.abspath(options['cfg_path'])
if not self._load_config(options):
cfg.app_mode = APP_MODE_NORMAL
# cfg.app_mode = APP_MODE_UNKNOWN
# if os.path.exists(os.path.join(cfg.app_path, 'maintenance-mode')):
# cfg.app_mode = APP_MODE_UPGRADE
# else:
# cfg.app_mode = APP_MODE_NORMAL
_cfg_file = os.path.join(cfg.cfg_path, 'web.ini')
if not cfg.load_web(_cfg_file):
return False
if cfg.log_file is None:
if 'log_path' not in options:
return False
else:
cfg.log_path = options['log_path']
# TODO: 不要直接读取core.ini而是通过core的json-rpc获取其配置数据
_cfg_file = os.path.join(cfg.cfg_path, 'core.ini')
if not cfg.load_core(_cfg_file):
return False
cfg.log_file = os.path.join(cfg.log_path, 'tpweb.log')
cfg.log_path = os.path.abspath(options['log_path'])
cfg.log_file = os.path.join(cfg.log_path, 'tpweb.log')
if not os.path.exists(cfg.log_path):
utils.make_dir(cfg.log_path)
if not os.path.exists(cfg.log_path):
log.e('Can not create log path.\n')
log.e('Can not create log path:{}\n'.format(cfg.log_path))
return False
log.set_attribute(filename=cfg.log_file)
log.set_attribute(min_level=cfg.log_level, filename=cfg.log_file)
# log.set_attribute(min_level=self['log_level'])
if 'app_path' not in options:
return False
else:
cfg.app_path = options['app_path']
if 'static_path' in options:
cfg.static_path = options['static_path']
else:
cfg.static_path = os.path.join(options['app_path'], 'static')
if 'data_path' in options:
cfg.data_path = options['data_path']
else:
cfg.data_path = os.path.join(options['app_path'], 'data')
if 'template_path' in options:
cfg.template_path = options['template_path']
else:
cfg.template_path = os.path.join(options['app_path'], 'view')
if 'res_path' in options:
cfg.res_path = options['res_path']
else:
cfg.res_path = os.path.join(options['app_path'], 'res')
if not swx_session().init():
if not web_session().init():
return False
# TODO: 这里不要初始化数据库接口,需要根据配置文件来决定使用什么数据库(初始安装时还没有配置数据库信息)
# get_mysql_pool().init(cfg.mysql_ip, cfg.mysql_port, cfg.mysql_user, cfg.mysql_pass)
# db_path = os.path.join(cfg.data_path, 'ts_db.db')
get_sqlite_pool().init(cfg.data_path)
# var_js = os.path.join(cfg.static_path, 'js', 'var.js')
# try:
# # if not os.path.exists(var_js):
# f = open(var_js, 'w')
# f.write("\"use strict\";\nvar teleport_ip = \"{}\";\n".format(get_sqlite_pool().get_config_server_ip()))
# f.close()
# except Exception:
# log.e('can not load config: server_ip.\n')
# return False
return True
def _load_config(self, options):
if 'cfg_path' in options:
_cfg_path = options['cfg_path']
else:
_cfg_path = os.path.join(options['app_path'], 'conf')
_cfg_file = os.path.join(_cfg_path, 'web.ini')
if not cfg.load_web(_cfg_file):
return False
_cfg_file = os.path.join(_cfg_path, 'core.ini')
if not cfg.load_core(_cfg_file):
return False
cfg.cfg_path = _cfg_path
return True
@staticmethod
def _daemon():
# fork for daemon.
if sys.platform == 'win32':
# log.v('os.fork() not support Windows, operation ignored.\n')
return True
try:
pid = os.fork()
if pid > 0:
# log.w('parent #1 exit.\n')
# return False
os._exit(0)
except OSError:
log.e('fork #1 failed.\n')
os._exit(1)
# Detach from parent env.
os.chdir('/')
os.umask(0)
os.setsid()
# Second fork.
try:
pid = os.fork()
if pid > 0:
# log.w('parent #2 exit.\n')
# return False
os._exit(0)
except OSError:
log.e('fork #2 failed.\n')
# return False
os._exit(1)
# OK I'm daemon now.
for f in sys.stdout, sys.stderr:
f.flush()
si = open('/dev/null', 'r')
so = open('/dev/null', 'a+')
se = open('/dev/null', 'a+')
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# test print() not raise exception.
# print('good.')
return True
def run(self):
@ -185,6 +99,7 @@ class SwxCore:
'static_hash_cache': False,
}
# TODO: 配置文件中应该增加 debug 选项
# if cfg.debug:
# settings['compiled_template_cache'] = False
# settings['static_hash_cache'] = False
@ -194,50 +109,6 @@ class SwxCore:
from eom_app.controller import controllers
web_app = tornado.web.Application(controllers, **settings)
# if sys.platform == 'win32':
# web_app.listen(cfg.server_port)
# log.v('Web Server start on http://127.0.0.1:{}\n'.format(cfg.server_port))
# tornado.ioloop.IOLoop.instance().start()
# else:
# if not cfg.debug:
# if not self._daemon():
# return False
# # 进入daemon模式了不再允许输出信息到控制台了
# log.set_attribute(console=False, filename='/var/log/eom/ts-backend.log')
# log.v('\n=====================================\n')
#
# def _run(port):
# log.v('Web Server start on http://127.0.0.1:{}\n'.format(port))
# web_app.listen(port)
# tornado.ioloop.IOLoop.instance().start()
# log.w('a tornado io-loop exit.\n')
#
# jobs = list()
# port = cfg.server_port
# for x in range(cfg.server_worker):
# p = multiprocessing.Process(target=_run, args=(port,))
# jobs.append(p)
# p.start()
# port = port + 1
#
# else:
# # sockets = tornado.netutil.bind_sockets(cfg.server_port)
# # tornado.process.fork_processes(2)
# # server = tornado.httpserver.HTTPServer(web_app)
# # server.add_sockets(sockets)
# web_app.listen(cfg.server_port)
# log.v('Web Server start on http://127.0.0.1:{}\n'.format(cfg.server_port))
# tornado.ioloop.IOLoop.instance().start()
# server = tornado.httpserver.HTTPServer(web_app, ssl_options={
# 'certfile': os.path.join(cfg.cfg_path, 'ssl', 'server.pem'),
# 'keyfile': os.path.join(cfg.cfg_path, 'ssl', 'server.key')
# })
# if sys.platform == 'win32':
# log.set_attribute(console=False, filename='/var/log/eom_ts/ts-backend.log')
# else:
# log.set_attribute(console=False, filename='/var/log/eom_ts/ts-backend.log')
server = tornado.httpserver.HTTPServer(web_app)
try:
server.listen(cfg.server_port)
@ -246,9 +117,5 @@ class SwxCore:
log.e('Can not listen on port {}, maybe it been used by another application.\n'.format(cfg.server_port))
return 0
# if not cfg.dev_mode:
# log_file = os.path.join(cfg.log_path, 'tpweb.log')
# log.set_attribute(console=False, filename=log_file)
tornado.ioloop.IOLoop.instance().start()
return 0

View File

@ -9,19 +9,19 @@ cfg = app_cfg()
SESSION_EXPIRE = 3600 # 60*60
# TODO: session接口需要支持超时超时的session应该移除避免内存占用越来越大
# SESSION_EXPIRE = 1800 # 30*60
# SESSION_EXPIRE = 30
class SwxSession:
class WebSession:
"""
:type _mem_client: pymemcache.client.base.Client
"""
def __init__(self):
import builtins
if '__swx_session__' in builtins.__dict__:
raise RuntimeError('SwxSession object exists, you can not create more than one instance.')
if '__web_session__' in builtins.__dict__:
raise RuntimeError('WebSession object exists, you can not create more than one instance.')
self._session_dict = dict()
def init(self):
@ -41,14 +41,14 @@ class SwxSession:
return v
def swx_session():
def web_session():
"""
取得 SwxSession 的唯一实例
:rtype : SwxSession
:rtype : WebSession
"""
import builtins
if '__swx_session__' not in builtins.__dict__:
builtins.__dict__['__swx_session__'] = SwxSession()
return builtins.__dict__['__swx_session__']
if '__web_session__' not in builtins.__dict__:
builtins.__dict__['__web_session__'] = WebSession()
return builtins.__dict__['__web_session__']

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import os
from . import rpc
from . import auth
from . import host
from . import cert
@ -10,6 +11,7 @@ from . import set
from . import group
from . import index
from . import record
from . import maintenance
import tornado.web
from eom_app.app.configs import app_cfg
@ -21,6 +23,13 @@ __all__ = ['controllers']
controllers = [
(r'/', index.IndexHandler),
# (r'/install/', maintenance.InstallHandler),
# (r'/install', maintenance.InstallHandler),
(r'/maintenance/', maintenance.IndexHandler),
(r'/maintenance', maintenance.IndexHandler),
(r'/rpc', rpc.RpcHandler),
(r'/auth/login', auth.LoginHandler),
(r'/auth/verify-user', auth.VerifyUser),
(r'/auth/logout', auth.LogoutHandler),

View File

@ -6,11 +6,11 @@ from random import Random
from eom_app.module import user
from eom_common.eomcore.logger import *
from .base import SwxBaseHandler, SwxJsonpHandler, SwxAuthJsonHandler
from .base import SwxAppHandler, SwxJsonpHandler, SwxAuthJsonHandler
from .helper.captcha import gen_captcha
class LoginHandler(SwxBaseHandler):
class LoginHandler(SwxAppHandler):
def get(self):
ref = self.get_argument('ref', '/')
@ -71,7 +71,7 @@ class VerifyUser(SwxJsonpHandler):
self.write_jsonp(-1)
class LogoutHandler(SwxBaseHandler):
class LogoutHandler(SwxAppHandler):
def get(self):
user = self.get_current_user()
user['is_login'] = False
@ -81,7 +81,7 @@ class LogoutHandler(SwxBaseHandler):
self.redirect('/auth/login')
class GetCaptchaHandler(SwxBaseHandler):
class GetCaptchaHandler(SwxAppHandler):
def get(self):
code, img_data = gen_captcha()
self.set_session('captcha', code)
@ -266,11 +266,11 @@ class ModifyPwd(SwxAuthJsonHandler):
# self.write_json(-1)
def random_str(randomlength=8):
_str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
_random = Random()
for i in range(randomlength):
_str += chars[_random.randint(0, length)]
return _str
# def random_str(randomlength=8):
# _str = ''
# chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
# length = len(chars) - 1
# _random = Random()
# for i in range(randomlength):
# _str += chars[_random.randint(0, length)]
# return _str

View File

@ -10,7 +10,11 @@ import mako.template
import tornado.web
from tornado.escape import json_encode
from eom_app.app.session import swx_session
from eom_app.app.session import web_session
from eom_app.app.configs import app_cfg
from eom_app.app.const import *
cfg = app_cfg()
class SwxBaseHandler(tornado.web.RequestHandler):
@ -19,6 +23,7 @@ class SwxBaseHandler(tornado.web.RequestHandler):
self._s_id = None
self._s_val = dict()
# self.lookup = None
def initialize(self):
template_path = self.get_template_path()
@ -34,25 +39,26 @@ class SwxBaseHandler(tornado.web.RequestHandler):
self.finish(self.render_string(template_path, **kwargs))
def prepare(self):
super().prepare()
if self.application.settings.get("xsrf_cookies"):
x = self.xsrf_token
# if self.application.settings.get("xsrf_cookies"):
# x = self.xsrf_token
self._s_id = self.get_cookie('_sid')
if self._s_id is None:
self._s_id = 'ywl_{}_{}'.format(int(time.time()), binascii.b2a_hex(os.urandom(8)).decode())
self.set_cookie('_sid', self._s_id)
swx_session().add(self._s_id, self._s_val)
web_session().add(self._s_id, self._s_val)
else:
# print('sid:', self._s_id)
self._s_val = swx_session().get(self._s_id)
self._s_val = web_session().get(self._s_id)
if self._s_val is None:
self._s_val = dict()
swx_session().add(self._s_id, self._s_val)
web_session().add(self._s_id, self._s_val)
def set_session(self, name, value):
self._s_val[name] = value
swx_session().set(self._s_id, self._s_val)
web_session().set(self._s_id, self._s_val)
def get_session(self, name, default=None):
if name in self._s_val:
@ -81,7 +87,23 @@ class SwxBaseHandler(tornado.web.RequestHandler):
return user
class SwxJsonpHandler(SwxBaseHandler):
class SwxAppHandler(SwxBaseHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
def prepare(self):
super().prepare()
if self._finished:
return
if cfg.app_mode == APP_MODE_NORMAL:
return
# self.redirect('/maintenance')
self.render('maintenance/index.mako')
class SwxJsonpHandler(SwxAppHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
@ -89,6 +111,8 @@ class SwxJsonpHandler(SwxBaseHandler):
def prepare(self):
super().prepare()
if self._finished:
return
self._js_callback = self.get_argument('callback', None)
if self._js_callback is None:
@ -112,13 +136,14 @@ class SwxJsonpHandler(SwxBaseHandler):
self.write('})')
class SwxJsonHandler(SwxBaseHandler):
class SwxJsonHandler(SwxAppHandler):
"""
所有返回JSON数据的控制器均从本类集成返回的数据格式一律包含三个字段code/msg/data
code: 0=成功其他=失败
msg: 字符串一般用于code为非零指出错误原因
msg: 字符串一般用于code为非零指出错误原因
data: 一般用于成功操作的返回的业务数据
"""
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
@ -131,7 +156,7 @@ class SwxJsonHandler(SwxBaseHandler):
if data is None:
data = list()
_ret = {'code':code, 'message':message, 'data':data}
_ret = {'code': code, 'message': message, 'data': data}
self.set_header("Content-Type", "application/json")
self.write(json_encode(_ret))
@ -145,12 +170,14 @@ class SwxJsonHandler(SwxBaseHandler):
self.write(json_encode(data))
class SwxAuthHandler(SwxBaseHandler):
class SwxAuthHandler(SwxAppHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
def prepare(self):
super().prepare()
if self._finished:
return
reference = self.request.uri
@ -163,12 +190,14 @@ class SwxAuthHandler(SwxBaseHandler):
self.redirect('/auth/login')
class SwxAdminHandler(SwxBaseHandler):
class SwxAdminHandler(SwxAppHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
def prepare(self):
super().prepare()
if self._finished:
return
reference = self.request.uri
@ -181,9 +210,9 @@ class SwxAdminHandler(SwxBaseHandler):
self.redirect('/auth/login')
class SwxAuthJsonpHandler(SwxBaseHandler):
def __init__(self, application, request, **kwargs):
super().__init__(application, request, **kwargs)
# class SwxAuthJsonpHandler(SwxAppHandler):
# def __init__(self, application, request, **kwargs):
# super().__init__(application, request, **kwargs)
class SwxAuthJsonHandler(SwxJsonHandler):
@ -192,16 +221,17 @@ class SwxAuthJsonHandler(SwxJsonHandler):
def prepare(self):
super().prepare()
if self._finished:
return
reference = self.request.uri
user = self.get_current_user()
if not user['is_login']:
if reference != '/auth/login':
x = quote(reference)
self.redirect('/auth/login?ref={}'.format(x))
self.write_json(-99)
else:
self.redirect('/auth/login')
self.write_json(-99)
class SwxAdminJsonHandler(SwxJsonHandler):
@ -210,6 +240,8 @@ class SwxAdminJsonHandler(SwxJsonHandler):
def prepare(self):
super().prepare()
if self._finished:
return
reference = self.request.uri

View File

@ -83,7 +83,7 @@ class LoadFile(SwxAuthJsonHandler):
"""
ret = dict()
ret['code'] = 0
ret['msg'] = list() # 记录跳过的行(格式不正确,或者数据重复等)
ret['msg'] = list() # 记录跳过的行(格式不正确,或者数据重复等)
csv_filename = ''
try:
@ -136,7 +136,7 @@ class LoadFile(SwxAuthJsonHandler):
# 格式错误则记录在案,然后继续
if len(csv_recorder) != 13:
ret['msg'].append({'reason':'格式错误', 'line':', '.join(csv_recorder)})
ret['msg'].append({'reason': '格式错误', 'line': ', '.join(csv_recorder)})
continue
# pro_type = int(line[6])
@ -201,7 +201,7 @@ class LoadFile(SwxAuthJsonHandler):
ret['msg'].append({'reason': '添加登录账号失败,账号已存在', 'line': ', '.join(csv_recorder)})
else:
ret['msg'].append({'reason': '添加登录账号失败,操作数据库失败', 'line': ', '.join(csv_recorder)})
# log.e('sys_user_add() failed.\n')
# log.e('sys_user_add() failed.\n')
ret = json.dumps(ret).encode('utf8')
self.write(ret)
@ -215,7 +215,7 @@ class LoadFile(SwxAuthJsonHandler):
if os.path.exists(csv_filename):
os.remove(csv_filename)
# self.write_json(0)
# self.write_json(0)
class GetListHandler(SwxAuthJsonHandler):
@ -409,7 +409,7 @@ class ExportHost(SwxAuthJsonHandler):
limit = dict()
limit['page_index'] = 0
limit['per_page'] = 999999
_total, _hosts = host.get_all_host_info_list(dict(), order, limit,True)
_total, _hosts = host.get_all_host_info_list(dict(), order, limit, True)
export_file = os.path.join(cfg.static_path, 'download', 'export_csv_data.csv')
if os.path.exists(export_file):
os.remove(export_file)
@ -809,7 +809,6 @@ class GetSessionId(SwxAuthJsonHandler):
# ret = {'code':-1}
self.write_json(-1)
return
values = dict()
if 'auth_id' not in args:
self.write_json(-1)
return
@ -826,9 +825,9 @@ class GetSessionId(SwxAuthJsonHandler):
ts_server_rpc_ip = cfg.core.rpc.ip
ts_server_rpc_port = cfg.core.rpc.port
url = 'http://{}:{}/request_session'.format(ts_server_rpc_ip, ts_server_rpc_port)
values['auth_id'] = auth_id
return_data = post_http(url, values)
url = 'http://{}:{}/rpc'.format(ts_server_rpc_ip, ts_server_rpc_port)
req = {'method': 'request_session', 'param': {'authid': auth_id}}
return_data = post_http(url, req)
if return_data is None:
return self.write_json(-1)
return_data = json.loads(return_data)
@ -1113,7 +1112,6 @@ class SysUserDelete(SwxAuthJsonHandler):
return
if host.sys_user_delete(host_auth_id):
return self.write_json(0)
return self.write_json(0)
return self.write_json(-1)

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# import sys
# import tornado.ioloop
from .base import SwxBaseHandler
class IndexHandler(SwxBaseHandler):
def get(self):
self.render('maintenance/index.mako')

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
import tornado.web
import tornado.gen
import json
import urllib.parse
from eom_app.module import host
from .base import SwxJsonHandler
class RpcHandler(SwxJsonHandler):
@tornado.web.asynchronous
@tornado.gen.coroutine
def get(self):
_uri = self.request.uri.split('?', 1)
print(_uri)
if len(_uri) != 2:
self.write_json(-1, message='need request param.')
return
self._dispatch(urllib.parse.unquote(_uri[1]))
def post(self):
# curl -X POST --data '{"method":"get_auth_info","param":{"authid":0}}' http://127.0.0.1:7190/rpc
req = self.request.body.decode('utf-8')
if req == '':
self.write_json(-1, message='need request param.')
return
self._dispatch(req)
def _dispatch(self, req):
print('rpc-req:', req)
try:
_req = json.loads(req)
if 'method' not in _req or 'param' not in _req:
self.write_json(-1, message='invalid request format.')
return
except:
self.write_json(-1, message='invalid json format.')
return
if 'get_auth_info' == _req['method']:
return self._get_auth_info(_req['param'])
elif 'session_begin' == _req['method']:
return self._session_begin(_req['param'])
elif 'session_end' == _req['method']:
return self._session_end(_req['param'])
elif 'session_fix' == _req['method']:
return self._session_fix()
elif 'exit' == _req['method']:
return self._exit()
self.write_json(-1, message='invalid method.')
def _get_auth_info(self, param):
# todo: 如果是页面上进行连接测试增加或修改主机和用户时信息并不写入数据库而是在内存中存在传递给core服务的
# 应该是随机字符串做authid名称为 tauthid。本接口应该支持区分这两种认证ID。
if 'authid' not in param:
self.write_json(-1, message='invalid request.')
return
# 根据authid从数据库中查询对应的数据然后返回给调用者
x = host.get_auth_info(param['authid'])
print('get_auth_info():', x)
self.write_json(0, data=x)
def _session_begin(self, param):
if 'sid' not in param:
self.write_json(-1, message='invalid request.')
return
self.write_json(0, data={'rid': 12})
def _session_end(self, param):
if 'rid' not in param or 'code' not in param:
self.write_json(-1, message='invalid request.')
return
self.write_json(0)
def _session_fix(self):
# do db operation.
self.write_json(0)
def _exit(self):
# set exit flag.
self.write_json(0)

View File

@ -1,15 +1,10 @@
# -*- coding: utf-8 -*-
# from eom_common.eomcore.utils import *
from .common import *
import time
# import eom_common.eomcore.eom_mysql as mysql
# import sqlite3
# 获取主机列表包括主机的基本信息CPU型号、内存大小、磁盘大小未激活的主机编号也会返回
# 获取主机列表,包括主机的基本信息
def get_all_host_info_list(filter, order, limit, with_pwd=False):
sql_exec = get_db_con()
@ -43,7 +38,7 @@ def get_all_host_info_list(filter, order, limit, with_pwd=False):
_where += ')'
# http://www.jb51.net/article/46015.htm
field_a = ['host_id', 'host_lock', 'host_ip','host_port', 'protocol', 'host_desc', 'group_id', 'host_sys_type']
field_a = ['host_id', 'host_lock', 'host_ip', 'host_port', 'protocol', 'host_desc', 'group_id', 'host_sys_type']
field_b = ['group_name']
# field_c = ['id', 'auth_mode', 'user_name']
@ -173,12 +168,12 @@ def get_host_info_list_by_user(filter, order, limit):
# http://www.jb51.net/article/46015.htm
field_a = ['auth_id', 'host_id', 'account_name', 'host_auth_id']
field_b = ['host_id', 'host_lock', 'host_ip', 'protocol', 'host_port', 'host_desc', 'group_id', 'host_sys_type']
field_b = ['host_id', 'host_lock', 'host_ip', 'protocol', 'host_port', 'host_desc', 'group_id', 'host_sys_type']
field_c = ['group_name']
field_d = ['auth_mode', 'user_name']
str_sql = 'SELECT COUNT(DISTINCT a.host_id) ' \
'FROM ts_auth AS a ' \
'LEFT JOIN ts_host_info AS b ON a.host_id = b.host_id '\
'LEFT JOIN ts_host_info AS b ON a.host_id = b.host_id ' \
'{};'.format(_where)
db_ret = sql_exec.ExecProcQuery(str_sql)
@ -232,7 +227,7 @@ def get_host_info_list_by_user(filter, order, limit):
host_ip = x.b_host_ip
protocol = x.b_protocol
key = '{}-{}'.format(host_ip, protocol)
temp_auth = None
temp_auth = None
extend_auth_list = sys_user_list(x.b_host_id, False, x.a_host_auth_id)
if extend_auth_list is not None and len(extend_auth_list) > 0:
auth = extend_auth_list[0]
@ -373,7 +368,7 @@ def add_host(args, must_not_exists=True):
host_port = args['host_port']
host_ip = args['host_ip']
str_sql = 'SELECT host_id FROM ts_host_info WHERE (host_ip=\'{}\' and protocol={} and host_port={});'\
str_sql = 'SELECT host_id FROM ts_host_info WHERE (host_ip=\'{}\' and protocol={} and host_port={});' \
.format(host_ip, protocol, host_port)
db_ret = sql_exec.ExecProcQuery(str_sql)
if db_ret is not None and len(db_ret) > 0:
@ -521,7 +516,7 @@ def get_host_auth_info(host_auth_id):
sql_exec = get_db_con()
field_a = ['id', 'auth_mode', 'user_name', 'user_pswd', 'user_param', 'cert_id', 'encrypt']
field_b = ['host_id', 'host_lock', 'host_ip', 'host_port', 'host_desc', 'group_id', 'host_sys_type', 'protocol']
field_b = ['host_id', 'host_lock', 'host_ip', 'host_port', 'host_desc', 'group_id', 'host_sys_type', 'protocol']
str_sql = 'SELECT {},{} ' \
'FROM ts_auth_info AS a ' \
@ -564,7 +559,7 @@ def get_host_auth_info(host_auth_id):
if x.a_cert_id is None:
cert_id = 0
else:
cert_id = int(x.a_cert_id) #int(user_auth)
cert_id = int(x.a_cert_id) # int(user_auth)
str_sql = 'SELECT cert_pri FROM ts_cert WHERE cert_id = {}'.format(cert_id)
db_ret = sql_exec.ExecProcQuery(str_sql)
if db_ret is not None and len(db_ret) == 1:
@ -677,7 +672,7 @@ def sys_user_list(host_id, with_pwd=True, host_auth_id=0):
def GetNowTime():
return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
def sys_user_add(args):
@ -697,7 +692,7 @@ def sys_user_add(args):
sql_exec = get_db_con()
# 判断此登录账号是否已经存在,如果存在则报错
str_sql = 'SELECT id FROM ts_auth_info WHERE (host_id={} and auth_mode={} and user_name=\'{}\');'\
str_sql = 'SELECT id FROM ts_auth_info WHERE (host_id={} and auth_mode={} and user_name=\'{}\');' \
.format(host_id, auth_mode, user_name)
db_ret = sql_exec.ExecProcQuery(str_sql)
if db_ret is not None and len(db_ret) > 0:
@ -708,7 +703,7 @@ def sys_user_add(args):
if auth_mode == 1:
str_sql = 'INSERT INTO ts_auth_info (host_id, auth_mode, user_name, user_pswd, user_param,' \
'encrypt, cert_id, log_time) ' \
'VALUES ({},{},\'{}\',\'{}\',\'{}\',{}, {},\'{}\')'.format(host_id, auth_mode, user_name, user_pswd, user_param,encrypt, 0, log_time)
'VALUES ({},{},\'{}\',\'{}\',\'{}\',{}, {},\'{}\')'.format(host_id, auth_mode, user_name, user_pswd, user_param, encrypt, 0, log_time)
elif auth_mode == 2:
str_sql = 'INSERT INTO ts_auth_info (host_id, auth_mode, user_name,user_param, ' \
'user_pswd,cert_id, encrypt, log_time) ' \
@ -724,7 +719,6 @@ def sys_user_add(args):
if not ret:
return -101
str_sql = 'select last_insert_rowid()'
db_ret = sql_exec.ExecProcQuery(str_sql)
if db_ret is None:
@ -752,15 +746,91 @@ def sys_user_update(_id, kv):
return db_ret
def sys_user_delete(id):
def sys_user_delete(_id):
sql_exec = get_db_con()
try:
str_sql = 'DELETE FROM ts_auth_info WHERE id = {} '.format(id)
str_sql = 'DELETE FROM ts_auth_info WHERE id = {} '.format(_id)
ret = sql_exec.ExecProcNonQuery(str_sql)
str_sql = 'DELETE FROM ts_auth WHERE host_auth_id = {} '.format(id)
str_sql = 'DELETE FROM ts_auth WHERE host_auth_id = {} '.format(_id)
ret = sql_exec.ExecProcNonQuery(str_sql)
except Exception as e:
return False
return True
return True
def get_auth_info(auth_id):
"""
根据指定的auth_id查询相关的认证信息远程主机IP端口登录用户名登录密码或私钥等等
@param auth_id: integer
@return:
"""
sql_exec = get_db_con()
field_a = ['auth_id', 'account_name', 'host_auth_id', 'host_id']
field_b = ['host_sys_type', 'host_ip', 'host_port', 'protocol']
field_c = ['user_pswd', 'cert_id', 'user_name', 'encrypt', 'auth_mode', 'user_param']
field_d = ['account_lock']
str_sql = 'SELECT {},{},{},{} ' \
'FROM ts_auth AS a ' \
'LEFT JOIN ts_host_info AS b ON a.host_id = b.host_id ' \
'LEFT JOIN ts_auth_info AS c ON a.host_auth_id = c.id ' \
'LEFT JOIN ts_account AS d ON a.account_name = d.account_name ' \
'WHERE a.auth_id={};'.format(
','.join(['a.{}'.format(i) for i in field_a]),
','.join(['b.{}'.format(i) for i in field_b]),
','.join(['c.{}'.format(i) for i in field_c]),
','.join(['d.{}'.format(i) for i in field_d]),
auth_id)
print(str_sql)
"""
"SELECT a.auth_id as auth_id, a.account_name as account_name, \
// a.host_auth_id as host_auth_id, a.host_id as host_id,host_lock, \
// b.host_sys_type as host_sys_type, host_ip, host_port, protocol, \
// c.user_pswd as user_pswd, c.cert_id as cert_id, c.user_name as user_name, \
// c.encrypt as encrypt, c.auth_mode as auth_mode,c.user_param as user_param, \
// d.account_lock as account_lock FROM ts_auth as a \
// LEFT JOIN ts_host_info as b ON a.host_id = b.host_id \
// LEFT JOIN ts_auth_info as c ON a.host_auth_id = c.id \
// LEFT JOIN ts_account as d ON a.account_name = d.account_name \
// WHERE a.auth_id=%d", auth_id
"""
db_ret = sql_exec.ExecProcQuery(str_sql)
if db_ret is None:
return None
ret = list()
for item in db_ret:
x = DbItem()
x.load(item,
['a_{}'.format(i) for i in field_a] +
['b_{}'.format(i) for i in field_b] +
['c_{}'.format(i) for i in field_c] +
['d_{}'.format(i) for i in field_d]
)
h = dict()
h['host_ip'] = x.b_host_ip
h['sys_type'] = x.b_host_sys_type
h['account_name'] = x.a_account_name
h['account_lock'] = x.d_account_lock
# h['host_lock'] = x.a_host_lock
h['host_port'] = x.b_host_port
h['protocol'] = x.b_protocol
h['encrypt'] = x.c_encrypt
h['auth_mode'] = x.c_auth_mode
h['user_name'] = x.c_user_name
h['user_param'] = x.c_user_param
h['user_pswd'] = x.c_user_pswd
h['cert_id'] = x.c_cert_id
ret.append(h)
print(ret)
return ret

View File

@ -1,6 +0,0 @@
# -*- coding: utf-8 -*-
"""EOM Algorithm Package."""
__version__ = '1.0.1'

View File

@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
"""DES"""
import os
from ctypes import *
use_alg = True
try:
# Try to locate the .so file in the same directory as this file
_file = 'alg.dll'
_path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
_mod = cdll.LoadLibrary(_path)
_des3_cbc_encrypt = _mod.des3_cbc_encrypt
_des3_cbc_encrypt.argtypes = (c_void_p, c_int, c_void_p, c_int, c_void_p)
_des3_cbc_encrypt.restype = c_int
_des3_cbc_decrypt = _mod.des3_cbc_decrypt
_des3_cbc_decrypt.argtypes = (c_void_p, c_int, c_void_p, c_int, c_void_p)
_des3_cbc_decrypt.restype = c_int
_free_buffer = _mod.free_buffer
_free_buffer.argtypes = (c_void_p,)
except OSError as e:
use_alg = False
from eom_common.eomcore.algorithm import pyDes
# raise RuntimeError('kx')
# print('xxxxxxxxxx')
# pass
def print_bin(data):
for i in range(len(data)):
print('%02X ' % data[i], end='')
if (i + 1) % 16 == 0:
print('')
print('')
def des3_cbc_encrypt(key, plain_data):
if use_alg:
out = POINTER(c_ubyte)()
out_len = _des3_cbc_encrypt(key, len(key), plain_data, len(plain_data), byref(out))
if out_len < 0:
return None
ret = bytes(cast(out, POINTER(c_ubyte * out_len)).contents)
_free_buffer(out)
return ret
else:
try:
return pyDes.triple_des(key, pyDes.CBC, b'\x00'*8).encrypt(plain_data, None, pyDes.PAD_PKCS5)
except Exception:
return None
def des3_cbc_decrypt(key, enc_data):
if use_alg:
out = POINTER(c_ubyte)()
out_len = _des3_cbc_decrypt(key, len(key), enc_data, len(enc_data), byref(out))
if out_len < 0:
return None
ret = bytes(cast(out, POINTER(c_ubyte * out_len)).contents)
_free_buffer(out)
return ret
else:
try:
return pyDes.triple_des(key, pyDes.CBC, b'\x00'*8).decrypt(enc_data, None, pyDes.PAD_PKCS5)
except Exception:
return None
if __name__ == '__main__':
key = b'\x00' * 24
plain_data = os.urandom(110)
print_bin(plain_data)
try:
enc = des3_cbc_encrypt(key, plain_data)
print('==========================')
print_bin(enc)
print('==========================')
dec = des3_cbc_decrypt(key, enc)
print('==========================')
print_bin(dec)
print('==========================')
except Exception:
print('error')
raise

View File

@ -1 +0,0 @@
__author__ = 'apex'

View File

@ -1,853 +0,0 @@
#############################################################################
# Documentation #
#############################################################################
# Author: Todd Whiteman
# Date: 16th March, 2009
# Verion: 2.0.0
# License: Public Domain - free to do as you wish
# Homepage: http://twhiteman.netfirms.com/des.html
#
# This is a pure python implementation of the DES encryption algorithm.
# It's pure python to avoid portability issues, since most DES
# implementations are programmed in C (for performance reasons).
#
# Triple DES class is also implemented, utilising the DES base. Triple DES
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
#
# See the README.txt that should come with this python module for the
# implementation methods used.
#
# Thanks to:
# * David Broadwell for ideas, comments and suggestions.
# * Mario Wolff for pointing out and debugging some triple des CBC errors.
# * Santiago Palladino for providing the PKCS5 padding technique.
# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
#
"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
Class initialization
--------------------
pyDes.des(key, [mode], [IV], [pad], [padmode])
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
for Triple DES
mode -> Optional argument for encryption type, can be either
pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Length must be 8 bytes.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
to use during all encrypt/decrpt operations done with this instance.
I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
padding issues, as the padding can be removed unambiguously upon decrypting
data that was encrypted using PAD_PKCS5 padmode.
Common methods
--------------
encrypt(data, [pad], [padmode])
decrypt(data, [pad], [padmode])
data -> Bytes to be encrypted/decrypted
pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
encryption, adds this characters to the end of the data block when
data is not a multiple of 8 bytes. For decryption, will remove the
trailing characters that match this pad character from the last 8
bytes of the unencrypted data block.
padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
or PAD_PKCS5). Defaults to PAD_NORMAL.
Example
-------
from pyDes import *
data = "Please encrypt my data"
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
# For Python3, you'll need to use bytes, i.e.:
# data = b"Please encrypt my data"
# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = k.encrypt(data)
print "Encrypted: %r" % d
print "Decrypted: %r" % k.decrypt(d)
assert k.decrypt(d, padmode=PAD_PKCS5) == data
See the module source (pyDes.py) for more examples of use.
You can also run the pyDes.py file without and arguments to see a simple test.
Note: This code was not written for high-end systems needing a fast
implementation, but rather a handy portable solution with small usage.
"""
import sys
# _pythonMajorVersion is used to handle Python2 and Python3 differences.
_pythonMajorVersion = sys.version_info[0]
# Modes of crypting / cyphering
ECB = 0
CBC = 1
# Modes of padding
PAD_NORMAL = 1
PAD_PKCS5 = 2
# PAD_PKCS5: is a method that will unambiguously remove all padding
# characters after decryption, when originally encrypted with
# this padding mode.
# For a good description of the PKCS5 padding technique, see:
# http://www.faqs.org/rfcs/rfc1423.html
# The base class shared by des and triple des.
class _baseDes(object):
def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
if IV:
IV = self._guardAgainstUnicode(IV)
if pad:
pad = self._guardAgainstUnicode(pad)
self.block_size = 8
# Sanity checking of arguments.
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if IV and len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
# Set the passed in variables
self._mode = mode
self._iv = IV
self._padding = pad
self._padmode = padmode
def getKey(self):
"""getKey() -> bytes"""
return self.__key
def setKey(self, key):
"""Will set the crypting key for this object."""
key = self._guardAgainstUnicode(key)
self.__key = key
def getMode(self):
"""getMode() -> pyDes.ECB or pyDes.CBC"""
return self._mode
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
self._mode = mode
def getPadding(self):
"""getPadding() -> bytes of length 1. Padding character."""
return self._padding
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
if pad is not None:
pad = self._guardAgainstUnicode(pad)
self._padding = pad
def getPadMode(self):
"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
return self._padmode
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
self._padmode = mode
def getIV(self):
"""getIV() -> bytes"""
return self._iv
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
if not IV or len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
IV = self._guardAgainstUnicode(IV)
self._iv = IV
def _padData(self, data, pad, padmode):
# Pad data depending on the mode
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode == PAD_NORMAL:
if len(data) % self.block_size == 0:
# No padding required.
return data
if not pad:
# Get the default padding.
pad = self.getPadding()
if not pad:
raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
data += (self.block_size - (len(data) % self.block_size)) * pad
elif padmode == PAD_PKCS5:
pad_len = 8 - (len(data) % self.block_size)
if _pythonMajorVersion < 3:
data += pad_len * chr(pad_len)
else:
data += bytes([pad_len] * pad_len)
return data
def _unpadData(self, data, pad, padmode):
# Unpad data depending on the mode.
if not data:
return data
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode is None:
# Get the default padding mode.
padmode = self.getPadMode()
if padmode == PAD_NORMAL:
if not pad:
# Get the default padding.
pad = self.getPadding()
if pad:
data = data[:-self.block_size] + \
data[-self.block_size:].rstrip(pad)
elif padmode == PAD_PKCS5:
if _pythonMajorVersion < 3:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
data = data[:-pad_len]
return data
def _guardAgainstUnicode(self, data):
# Only accept byte strings or ascii unicode values, otherwise
# there is no way to correctly decode the data into bytes.
if _pythonMajorVersion < 3:
if isinstance(data, unicode):
raise ValueError("pyDes can only work with bytes, not Unicode strings.")
else:
if isinstance(data, str):
# Only accept ascii unicode values.
try:
return data.encode('ascii')
except UnicodeEncodeError:
pass
raise ValueError("pyDes can only work with encoded strings, not Unicode.")
return data
#############################################################################
# DES #
#############################################################################
class des(_baseDes):
"""DES encryption/decrytpion class
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key,[mode], [IV])
key -> Bytes containing the encryption key, must be exactly 8 bytes
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
# Permutation and translation tables for DES
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
# number left rotations of pc1
__left_rotations = [
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
]
# permuted choice key (table 2)
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
# initial permutation IP
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
# Expansion table for turning 32 bit blocks into 48 bits
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
]
# The (in)famous S-boxes
__sbox = [
# S1
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
# S2
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
# S3
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
# S4
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
# S5
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
# S6
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
# S7
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
# S8
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
# 32-bit permutation function P used on the output of the S-boxes
__p = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23, 13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]
# final permutation IP^-1
__fp = [
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25,
32, 0, 40, 8, 48, 16, 56, 24
]
# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x01
# Initialisation
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
# Sanity checking of arguments.
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
_baseDes.__init__(self, mode, IV, pad, padmode)
self.key_size = 8
self.L = []
self.R = []
self.Kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16)
self.final = []
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Must be 8 bytes."""
_baseDes.setKey(self, key)
self.__create_sub_keys()
def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
# Turn the strings into integers. Python 3 uses a bytes
# class, which already has this behaviour.
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
i = 7
while i >= 0:
if ch & (1 << i) != 0:
result[pos] = 1
else:
result[pos] = 0
pos += 1
i -= 1
return result
def __BitList_to_String(self, data):
"""Turn the list of bits -> data, into a string"""
result = []
pos = 0
c = 0
while pos < len(data):
c += data[pos] << (7 - (pos % 8))
if (pos % 8) == 7:
result.append(c)
c = 0
pos += 1
if _pythonMajorVersion < 3:
return ''.join([chr(c) for c in result])
else:
return bytes(result)
def __permutate(self, table, block):
"""Permutate this block with the specified table"""
return list(map(lambda x: block[x], table))
# Transform the secret key, so that it is ready for data processing
# Create the 16 subkeys, K[1] - K[16]
def __create_sub_keys(self):
"""Create the 16 subkeys K[1] to K[16] from the given key"""
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
i = 0
# Split into Left and Right sections
self.L = key[:28]
self.R = key[28:]
while i < 16:
j = 0
# Perform circular left shifts
while j < des.__left_rotations[i]:
self.L.append(self.L[0])
del self.L[0]
self.R.append(self.R[0])
del self.R[0]
j += 1
# Create one of the 16 subkeys through pc2 permutation
self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
i += 1
# Main part of the encryption algorithm, the number cruncher :)
def __des_crypt(self, block, crypt_type):
"""Crypt the block of data through DES bit-manipulation"""
block = self.__permutate(des.__ip, block)
self.L = block[:32]
self.R = block[32:]
# Encryption starts from Kn[1] through to Kn[16]
if crypt_type == des.ENCRYPT:
iteration = 0
iteration_adjustment = 1
# Decryption starts from Kn[16] down to Kn[1]
else:
iteration = 15
iteration_adjustment = -1
i = 0
while i < 16:
# Make a copy of R[i-1], this will later become L[i]
tempR = self.R[:]
# Permutate R[i - 1] to start creating R[i]
self.R = self.__permutate(des.__expansion_table, self.R)
# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
# Optimization: Replaced below commented code with above
# j = 0
# B = []
# while j < len(self.R):
# self.R[j] = self.R[j] ^ self.Kn[iteration][j]
# j += 1
# if j % 6 == 0:
# B.append(self.R[j-6:j])
# Permutate B[1] to B[8] using the S-Boxes
j = 0
Bn = [0] * 32
pos = 0
while j < 8:
# Work out the offsets
m = (B[j][0] << 1) + B[j][5]
n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
# Find the permutation value
v = des.__sbox[j][(m << 4) + n]
# Turn value into bits, add it to result: Bn
Bn[pos] = (v & 8) >> 3
Bn[pos + 1] = (v & 4) >> 2
Bn[pos + 2] = (v & 2) >> 1
Bn[pos + 3] = v & 1
pos += 4
j += 1
# Permutate the concatination of B[1] to B[8] (Bn)
self.R = self.__permutate(des.__p, Bn)
# Xor with L[i - 1]
self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
# Optimization: This now replaces the below commented code
# j = 0
# while j < len(self.R):
# self.R[j] = self.R[j] ^ self.L[j]
# j += 1
# L[i] becomes R[i - 1]
self.L = tempR
i += 1
iteration += iteration_adjustment
# Final permutation of R[16]L[16]
self.final = self.__permutate(des.__fp, self.R + self.L)
return self.final
# Data to be encrypted/decrypted
def crypt(self, data, crypt_type):
"""Crypt the data in blocks, running it through des_crypt()"""
# Error check the data
if not data:
return ''
if len(data) % self.block_size != 0:
if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
if not self.getPadding():
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
else:
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
# print "Len of data: %f" % (len(data) / self.block_size)
if self.getMode() == CBC:
if self.getIV():
iv = self.__String_to_BitList(self.getIV())
else:
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
# Split the data into blocks, crypting each one seperately
i = 0
dict = {}
result = []
# cached = 0
# lines = 0
while i < len(data):
# Test code for caching encryption results
# lines += 1
# if dict.has_key(data[i:i+8]):
# print "Cached result for: %s" % data[i:i+8]
# cached += 1
# result.append(dict[data[i:i+8]])
# i += 8
# continue
block = self.__String_to_BitList(data[i:i + 8])
# Xor with IV if using CBC mode
if self.getMode() == CBC:
if crypt_type == des.ENCRYPT:
block = list(map(lambda x, y: x ^ y, block, iv))
# j = 0
# while j < len(block):
# block[j] = block[j] ^ iv[j]
# j += 1
processed_block = self.__des_crypt(block, crypt_type)
if crypt_type == des.DECRYPT:
processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
# j = 0
# while j < len(processed_block):
# processed_block[j] = processed_block[j] ^ iv[j]
# j += 1
iv = block
else:
iv = processed_block
else:
processed_block = self.__des_crypt(block, crypt_type)
# Add the resulting crypted block to our list
# d = self.__BitList_to_String(processed_block)
# result.append(d)
result.append(self.__BitList_to_String(processed_block))
# dict[data[i:i+8]] = d
i += 8
# print "Lines: %d, cached: %d" % (lines, cached)
# Return the full crypted string
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self._padData(data, pad, padmode)
return self.crypt(data, des.ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : Bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after decrypting.
"""
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self.crypt(data, des.DECRYPT)
return self._unpadData(data, pad, padmode)
#############################################################################
# Triple DES #
#############################################################################
class triple_des(_baseDes):
"""Triple DES encryption/decrytpion class
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
pyDes.des(key, [mode], [IV])
key -> Bytes containing the encryption key, must be either 16 or
bytes long
mode -> Optional argument for encryption type, can be either pyDes.ECB
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
Must be 8 bytes in length.
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
during all encrypt/decrpt operations done with this instance.
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
PAD_PKCS5) to use during all encrypt/decrpt operations done
with this instance.
"""
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
_baseDes.__init__(self, mode, IV, pad, padmode)
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
self.key_size = 24 # Use DES-EDE3 mode
if len(key) != self.key_size:
if len(key) == 16: # Use DES-EDE2 mode
self.key_size = 16
else:
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
if self.getMode() == CBC:
if not self.getIV():
# Use the first 8 bytes of the key
self._iv = key[:self.block_size]
if len(self.getIV()) != self.block_size:
raise ValueError("Invalid IV, must be 8 bytes in length")
self.__key1 = des(key[:8], self._mode, self._iv,
self._padding, self._padmode)
self.__key2 = des(key[8:16], self._mode, self._iv,
self._padding, self._padmode)
if self.key_size == 16:
self.__key3 = self.__key1
else:
self.__key3 = des(key[16:], self._mode, self._iv,
self._padding, self._padmode)
_baseDes.setKey(self, key)
# Override setter methods to work on all 3 keys.
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
_baseDes.setMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setMode(mode)
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
_baseDes.setPadding(self, pad)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadding(pad)
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
_baseDes.setPadMode(self, mode)
for key in (self.__key1, self.__key2, self.__key3):
key.setPadMode(mode)
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
_baseDes.setIV(self, IV)
for key in (self.__key1, self.__key2, self.__key3):
key.setIV(IV)
def encrypt(self, data, pad=None, padmode=None):
"""encrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for encryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key. Data does not have to be a
multiple of 8 bytes if the padding character is supplied, or
the padmode is set to PAD_PKCS5, as bytes will then added to
ensure the be padded data is a multiple of 8 bytes.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
# Pad the data accordingly.
data = self._padData(data, pad, padmode)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
block = self.__key1.crypt(data[i:i + 8], ENCRYPT)
block = self.__key2.crypt(block, DECRYPT)
block = self.__key3.crypt(block, ENCRYPT)
self.__key1.setIV(block)
self.__key2.setIV(block)
self.__key3.setIV(block)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
else:
data = self.__key1.crypt(data, ENCRYPT)
data = self.__key2.crypt(data, DECRYPT)
return self.__key3.crypt(data, ENCRYPT)
def decrypt(self, data, pad=None, padmode=None):
"""decrypt(data, [pad], [padmode]) -> bytes
data : bytes to be encrypted
pad : Optional argument for decryption padding. Must only be one byte
padmode : Optional argument for overriding the padding mode.
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key. In PAD_NORMAL mode, if the
optional padding character is supplied, then the un-encrypted
data will have the padding characters removed from the end of
the bytes. This pad removal only occurs on the last 8 bytes of
the data (last data block). In PAD_PKCS5 mode, the special
padding end markers will be removed from the data after
decrypting, no pad character is required for PAD_PKCS5.
"""
ENCRYPT = des.ENCRYPT
DECRYPT = des.DECRYPT
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
if self.getMode() == CBC:
self.__key1.setIV(self.getIV())
self.__key2.setIV(self.getIV())
self.__key3.setIV(self.getIV())
i = 0
result = []
while i < len(data):
iv = data[i:i + 8]
block = self.__key3.crypt(iv, DECRYPT)
block = self.__key2.crypt(block, ENCRYPT)
block = self.__key1.crypt(block, DECRYPT)
self.__key1.setIV(iv)
self.__key2.setIV(iv)
self.__key3.setIV(iv)
result.append(block)
i += 8
if _pythonMajorVersion < 3:
data = ''.join(result)
else:
data = bytes.fromhex('').join(result)
else:
data = self.__key3.crypt(data, DECRYPT)
data = self.__key2.crypt(data, ENCRYPT)
data = self.__key1.crypt(data, DECRYPT)
return self._unpadData(data, pad, padmode)

View File

@ -1,19 +1,26 @@
# -*- coding: utf-8 -*-
# 检测运行环境和相对定位文件路径
"""
检测运行环境和相对定位文件路径
目录结构说明
PATH_APP_ROOT
|- app
|- res
|- static
\- view
"""
import os
import platform
import sys
__all__ = ['PATH_APP_ROOT', 'PATH_LOG', 'PATH_CONF', 'PATH_DATA', 'DEV_MODE']
__all__ = ['PATH_APP_ROOT', 'PATH_LOG', 'PATH_CONF', 'PATH_DATA']
PATH_LOG = ''
PATH_CONF = ''
PATH_DATA = ''
DEV_MODE = False
# 将Python安装的扩展库移除避免开发调试与正式发布所依赖的库文件不一致导致发布出去的版本无法运行
# 将Python安装的扩展库移除避免开发调试与正式发布所依赖的库文件不一致导致发布的版本无法运行
x = []
for p in sys.path:
if p.find('site-packages') != -1 or p.find('dist-packages') != -1:
@ -21,51 +28,64 @@ for p in sys.path:
for p in x:
sys.path.remove(p)
is_dev_mode = False
path_of_this_file = os.path.abspath(os.path.dirname(__file__))
PATH_APP_ROOT = os.path.abspath(os.path.join(path_of_this_file, '..'))
# 根据源代码目录形式,检查是否是开发版本
if os.path.exists(os.path.join(PATH_APP_ROOT, '..', '..', 'share', 'etc')):
is_dev_mode = True
# 检查操作系统目前仅支持Win和Linux
PLATFORM = platform.system().lower()
if PLATFORM not in ['windows', 'linux']:
print('Teleport WEB Server support Windows and Linux only.')
sys.exit(1)
BITS = 'x64'
if '32bit' == platform.architecture()[0]:
BITS = 'x86'
path_of_this_file = os.path.abspath(os.path.dirname(__file__))
PATH_APP_ROOT = os.path.abspath(os.path.join(path_of_this_file, '..'))
# 引入必要的扩展库
_ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-common'))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
# 如果没有打包,可能是开发版本,也可能是发布源代码版本,需要进一步判断
if os.path.exists(os.path.join(PATH_APP_ROOT, '..', '..', 'share', 'etc')):
DEV_MODE = True
elif os.path.exists(os.path.join(PATH_APP_ROOT, '..', '..', 'etc')):
DEV_MODE = False
else:
print('invalid installation.\n')
sys.exit(1)
_ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-{}'.format(PLATFORM), BITS))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
if DEV_MODE:
# 确定一些路径
if is_dev_mode:
# 开发调试模式
_ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-common'))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
_ext_path = os.path.abspath(
os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-{}'.format(PLATFORM), BITS))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
# _ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-common'))
# if _ext_path not in sys.path:
# sys.path.append(_ext_path)
#
# _ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-{}'.format(PLATFORM), BITS))
# if _ext_path not in sys.path:
# sys.path.append(_ext_path)
#
PATH_LOG = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'share', 'log'))
PATH_CONF = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'share', 'etc'))
PATH_DATA = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'share', 'data'))
else:
_ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-common'))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
_ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-{}'.format(PLATFORM), BITS))
if _ext_path not in sys.path:
sys.path.append(_ext_path)
# _ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-common'))
# if _ext_path not in sys.path:
# sys.path.append(_ext_path)
#
# _ext_path = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', 'packages', 'packages-{}'.format(PLATFORM), BITS))
# if _ext_path not in sys.path:
# sys.path.append(_ext_path)
#
PATH_LOG = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'log'))
PATH_CONF = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'etc'))
PATH_DATA = os.path.abspath(os.path.join(PATH_APP_ROOT, '..', '..', 'data'))
if PLATFORM == 'linux':
# 根据Linux目录规范建议设置各个必要的路径
PATH_LOG = '/var/log/teleport'
PATH_CONF = '/etc/teleport'
PATH_DATA = '/var/lib/teleport'

View File

@ -4,35 +4,30 @@ import os
import sys
from eom_env import *
import eom_app.app as app
# from eom_common.eomcore.logger import *
# log.set_attribute(min_level=LOG_INFO, trace_error=TRACE_ERROR_NONE)
def main():
options = {
# app_path 网站程序代码路径用于内部合成controller和model的路径必须指定
# app_path 网站程序根路径(应该是本文件所在目录的上一级目录)
'app_path': PATH_APP_ROOT,
# cfg_path 网站配置文件路径,如未指定,默认为 $_root_path$/conf
# cfg_path 网站配置文件路径
'cfg_path': PATH_CONF,
# log_path 网站运行时日志文件路径,如未指定,默认为 $_root_path$/log
# log_path 网站运行时日志文件路径
'log_path': PATH_LOG,
# static_path 网站静态文件路径,如未指定,默认为 $_root_path$/static
# static_path 网站静态文件路径
'static_path': os.path.join(PATH_APP_ROOT, 'static'),
# data_path 网站数据文件路径,如未指定,默认为 $_root_path$/data
# data_path 网站数据文件路径
'data_path': PATH_DATA,
# template_path 网站模板文件路径,如未指定,默认为 $_root_path$/template
# template_path 网站模板文件路径
'template_path': os.path.join(PATH_APP_ROOT, 'view'),
# res_path 网站资源文件路径,例如字体文件等,默认为 $_root_path$/res
'res_path': os.path.join(PATH_APP_ROOT, 'res'),
'dev_mode': DEV_MODE,
# res_path 网站资源文件路径
'res_path': os.path.join(PATH_APP_ROOT, 'res')
}
return app.run(options)

View File

@ -575,7 +575,7 @@ ywl.on_host_table_created = function (tbl) {
// ret.push('<a href="javascript:;" class="btn btn-sm btn-primary" ywl-btn-remote="' + fields.id + '"><i class="fa fa-desktop fa-fw"></i> 远程</a>');
ret.push('</div>');
return ret.join('');
}
};
render.make_user_btn = function (row_id, fields) {
var ret = [];
ret.push('<div class="btn-group btn-group-sm" role="group">');
@ -866,7 +866,6 @@ ywl.create_host_edit_dlg = function (tbl) {
return dlg_edit_host;
};
ywl.create_host_user_edit_dlg = function (tbl) {
var dlg_user_edit_host = {};
dlg_user_edit_host.dom_id = "#dialog-host-user-edit";
@ -1025,7 +1024,6 @@ ywl.create_host_user_edit_dlg = function (tbl) {
return dlg_user_edit_host;
};
ywl.create_sys_user = function (tbl) {
var dlg_sys_user = {};
@ -1433,6 +1431,7 @@ ywl.create_sys_user = function (tbl) {
return dlg_sys_user;
};
ywl.create_batch_join_group_dlg = function (tbl) {
var batch_join_dlg = {};

View File

@ -113,11 +113,11 @@ var to_teleport = function (url, args, func_success, func_error) {
}
});
} else {
func_error(TP_ERR_CORE_SRV, 'Web:远程连接请求失败可能teleport核心服务尚未启动');
func_error(TP_ERR_CORE_SRV, '远程连接请求失败可能teleport核心服务尚未启动');
}
},
error: function () {
func_error(TP_ERR_NETWORK, 'Web:远程网络通讯失败!');
func_error(TP_ERR_NETWORK, '远程网络通讯失败!');
}
});
};
@ -162,11 +162,11 @@ var to_admin_teleport = function (url, args, func_success, func_error) {
}
});
} else {
func_error(TP_ERR_CORE_SRV, 'Web:远程连接请求失败可能teleport核心服务尚未启动');
func_error(TP_ERR_CORE_SRV, '远程连接请求失败可能teleport核心服务尚未启动');
}
},
error: function () {
func_error(TP_ERR_NETWORK, 'Web:远程网络通讯失败!');
func_error(TP_ERR_NETWORK, '远程网络通讯失败!');
}
});
};
@ -214,11 +214,11 @@ var to_admin_fast_teleport = function (url, args, func_success, func_error) {
}
});
} else {
func_error(TP_ERR_CORE_SRV, 'Web:远程连接请求失败可能teleport核心服务尚未启动');
func_error(TP_ERR_CORE_SRV, '远程连接请求失败可能teleport核心服务尚未启动');
}
},
error: function () {
func_error(TP_ERR_NETWORK, 'Web:远程网络通讯失败!');
func_error(TP_ERR_NETWORK, '远程网络通讯失败!');
}
});
};
@ -241,7 +241,7 @@ var start_rdp_replay = function (args, func_success, func_error) {
console.log('ret', ret);
},
error: function () {
func_error(TP_ERR_NETWORK, 'Web:远程网络通讯失败!');
func_error(TP_ERR_NETWORK, '远程网络通讯失败!');
}
});
};

View File

@ -0,0 +1,69 @@
<%!
page_title_ = '系统维护'
## page_menu_ = ['user']
## page_id_ = 'user'
%>
<%inherit file="../page_maintenance_base.mako"/>
<%block name="breadcrumb">
<ol class="breadcrumb">
<li><i class="fa fa-cog fa-fw"></i> ${self.attr.page_title_}</li>
</ol>
</%block>
<%block name="embed_css">
<style type="text/css">
.content_box {
margin-top:48px;
}
.content_box .error_sidebar {
float: left;
width: 160px;
margin-left: 120px;
font-size: 260px;
color: #e3693b;
}
.content_box .error_content {
min-height: 400px;
width: 800px;
padding: 30px;
margin-left: 300px;
background: #ffffff;
border-radius: 5px;
background: rgba(255, 255, 255, 0.8);
background: #fff \9;
z-index: 9;
position: relative;
}
h1 .fa-spin {
color:#aaa;
}
</style>
</%block>
## Begin Main Body.
<div class="page-content">
<div class="content_box">
<div class="container">
<div class="error_sidebar">
<i class="fa fa-exclamation-triangle"></i>
</div>
<div class="error_content">
<br/>
<h1><i class="fa fa-cog fa-spin"></i> 系统维护中...</h1>
<hr/>
<p>系统管理员正在紧张地维护系统,请稍后刷新页面重试!</p>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,104 @@
<!DOCTYPE html>
<%!
page_title_ = ''
%>
<!--[if IE 8]><html lang="en" class="ie8"> <![endif]-->
<!--[if !IE]><!-->
<html lang="zh_CN">
<!--<![endif]-->
<head>
<meta charset="utf-8"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
<title>${self.attr.page_title_}::TELEPORT</title>
<link rel="shortcut icon" href="${ static_url('favicon.png') }">
<link href="${ static_url('plugins/google-cache/open-sans.css') }" rel="stylesheet">
<link href="${ static_url('plugins/bootstrap/css/bootstrap.min.css') }" rel="stylesheet" type="text/css"/>
<link href="${ static_url('plugins/font-awesome/css/font-awesome.min.css') }" rel="stylesheet">
<link href="${ static_url('plugins/gritter/css/jquery.gritter.css') }" rel="stylesheet">
<link href="${ static_url('css/sub.css') }" rel="stylesheet" type="text/css"/>
<%block name="extend_css"/>
<%block name="embed_css"/>
</head>
<body>
<!-- begin #page-container -->
<div id="page-container" class="page-header-fixed">
<div id="header" class="header header-fixed-top">
<div class="container-fluid top-navbar">
<div class="brand"><div class="site-logo"></div></div>
<div class="breadcrumb-container">
<%block name="breadcrumb" />
</div>
## <div class="status-container">
## more status or global tool buttons goes here.
## </div>
</div>
</div>
<div id="footer" class="footer footer-fixed-bottom">
<div class="container">
<p>触维软件旗下产品 | TELEPORT | &copy;2015 - 2017 <a href="http://www.eomsoft.net/" target="_blank">触维软件</a>,保留所有权利。</p>
</div>
</div>
<div class="container-fluid">
${self.body()}
</div>
</div>
<%block name="extend_content" />
<script type="text/javascript" src="${ static_url('plugins/underscore/underscore.js') }"></script>
<script type="text/javascript" src="${ static_url('plugins/jquery/jquery.min.js') }"></script>
<script type="text/javascript" src="${ static_url('plugins/bootstrap/js/bootstrap.min.js') }"></script>
<!--[if lt IE 9]>
<script src="${ static_url('plugins/html5shiv/html5shiv.min.js') }"></script>
<![endif]-->
## <script type="text/javascript" src="${ static_url('js/json2.js') }"></script>
<script type="text/javascript" src="${ static_url('plugins/gritter/js/jquery.gritter.js') }"></script>
<script type="text/javascript" src="${ static_url('plugins/jstree/jstree.js') }"></script>
<script type="text/javascript" src="${ static_url('plugins/keypress/keypress.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ywl_const.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ywl_common.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ywl.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ywl_assist.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ui/common.js') }"></script>
## <script type="text/javascript" src="${ static_url('js/ui/controls.js') }"></script>
<%block name="extend_js"/>
<script type="text/javascript">
$(document).ready(function () {
// once page ready, init ywl object.
## ywl.init();
});
</script>
<%block name="embed_js" />
</body>
</html>

View File

@ -48,7 +48,7 @@
<div id="footer" class="footer footer-fixed-bottom">
<div class="container">
<p>触维软件旗下产品 | TELEPORT | &copy;2015 - 2016 <a href="http://www.eomsoft.net/" target="_blank">触维软件</a>,保留所有权利。</p>
<p>触维软件旗下产品 | TELEPORT | &copy;2015 - 2017 <a href="http://teleport.eomsoft.net/" target="_blank">触维软件</a>,保留所有权利。</p>
</div>
</div>