..LDAP用户导入功能完成,准备实现导入用户的登录功能。

pull/130/head
Apex Liu 2018-11-06 01:31:54 +08:00
parent 4c14a58877
commit 493e991787
6 changed files with 567 additions and 142 deletions

View File

@ -28,7 +28,7 @@ $app.on_init = function (cb_stack) {
};
cb_stack
.add($app.test)
// .add($app.test)
.add($app.create_controls)
.add($app.load_role_list);
@ -109,7 +109,7 @@ $app.create_controls = function (cb_stack) {
align: 'center',
width: 70,
render: 'make_action_btn',
fields: {id: 'id', state: 'state'}
fields: {id: 'id', state: 'state', user_type: 'type'}
}
],
@ -144,6 +144,7 @@ $app.create_controls = function (cb_stack) {
});
$tp.create_table_pagination($app.table_users, 'table-user-list-pagination');
//-------------------------------
// 对话框
//-------------------------------
@ -190,7 +191,7 @@ $app.create_controls = function (cb_stack) {
$app.dlg_ldap_import = $app.create_dlg_ldap_import();
cb_stack.add($app.dlg_ldap_import.init);
$app.dom.btn_ldap_import.click(function () {
if(0 === $app.options.sys_cfg.ldap.server.length) {
if (0 === $app.options.sys_cfg.ldap.server.length) {
$tp.notify_error('LDAP服务尚未设置请先设置');
return;
}
@ -239,12 +240,19 @@ $app.on_table_users_cell_created = function (tbl, row_id, col_key, cell_obj) {
if (action === 'edit') {
$app.dlg_edit_user.show_edit(row_id);
} else if (action === 'reset-password') {
console.log(user);
if(user.type === TP_USER_TYPE_LDAP)
return;
$app.dlg_reset_password.show_edit(row_id);
} else if (action === 'reset-oath-bind') {
$app._reset_oath_bind(user.id);
} else if (action === 'lock') {
if (user.state !== TP_STATE_NORMAL)
return;
$app._lock_users([user.id]);
} else if (action === 'unlock') {
if (user.state === TP_STATE_NORMAL)
return;
$app._unlock_users([user.id]);
} else if (action === 'remove') {
$app._remove_users([user.id]);
@ -411,7 +419,15 @@ $app.on_table_users_render_created = function (render) {
h.push('<li' + class_unlock + '><a href="javascript:;" data-action="unlock"><i class="fa fa-unlock fa-fw"></i> 解禁</a></li>');
h.push('<li role="separator" class="divider"></li>');
h.push('<li><a href="javascript:;" data-action="reset-password"><i class="fa fa-street-view fa-fw"></i> 重置密码</a></li>');
var class_user_type = '';
if (fields.user_type === TP_USER_TYPE_LDAP) {
class_user_type = ' class="disabled"';
} else {
class_user_type = '';
}
h.push('<li' + class_user_type +'><a href="javascript:;" data-action="reset-password"><i class="fa fa-street-view fa-fw"></i> 重置密码</a></li>');
h.push('<li><a href="javascript:;" data-action="reset-oath-bind"><i class="fa fa-street-view fa-fw"></i> 重置身份验证器</a></li>');
h.push('<li role="separator" class="divider"></li>');
h.push('<li><a href="javascript:;" data-action="remove"><i class="fa fa-times-circle fa-fw"></i> 删除</a></li>');
@ -1288,7 +1304,7 @@ $app.create_dlg_ldap_config = function () {
};
dlg.init_fields = function () {
if(0 === $app.options.sys_cfg.ldap.server.length) {
if (0 === $app.options.sys_cfg.ldap.server.length) {
dlg.mode = 'set';
} else {
dlg.ldap_config = $app.options.sys_cfg.ldap;
@ -1348,7 +1364,7 @@ $app.create_dlg_ldap_config = function () {
return false;
}
if(dlg.mode === 'set') {
if (dlg.mode === 'set') {
if (tp_is_empty_str(dlg.ldap_config.password)) {
dlg.dom.password.focus();
$tp.notify_error('请填写LDAP的管理员密码');
@ -1436,6 +1452,7 @@ $app.create_dlg_ldap_config = function () {
dlg.dom.btn_save.removeAttr('disabled');
if (ret.code === TPE_OK) {
$app.options.sys_cfg.ldap = dlg.ldap_config;
dlg.dom.dialog.modal('hide');
$tp.notify_success('保存LDAP设置成功');
} else {
$tp.notify_error('保存LDAP设置失败' + tp_error_msg(ret.code, ret.message));
@ -1546,32 +1563,172 @@ $app.create_dlg_ldap_import = function () {
dialog: $('#' + dlg.dom_id),
table: $('#table-ldap-import'),
chkbox_user_list_select_all: $('#table-ldap-user-select-all'),
btn_refresh: $('#btn-ldap-import-refresh'),
btn_import: $('#btn-ldap-import-import')
};
dlg.init = function (cb_stack) {
dlg.dom.btn_refresh.click(dlg.do_refresh);
//-------------------------------
// LDAP用户列表表格
//-------------------------------
var table_ldap_users_options = {
dom_id: 'table-ldap-user-list',
data_source: {
type: 'none'
},
column_default: {sort: false, align: 'left'},
columns: [
{
// title: '<input type="checkbox" id="user-list-select-all" value="">',
title: '',
key: 'chkbox',
sort: false,
width: 36,
align: 'center',
render: 'make_check_box',
fields: {id: 'id'}
},
{
title: "用户名",
key: "username",
sort: false
},
{
title: "姓名",
key: "surname",
// width: 120,
sort: false
},
{
title: "邮箱",
key: "email",
// width: 120,
sort: false
},
{
title: "状态",
key: "bind",
sort: false,
width: 80,
align: 'center',
render: 'ldap_user_state',
fields: {bind: 'bind'}
}
],
// 重载回调函数
on_render_created: dlg.on_table_users_render_created,
on_cell_created: dlg.on_table_users_cell_created
};
$app.table_ldap_users = $tp.create_table(table_ldap_users_options);
cb_stack
.add($app.table_ldap_users.init);
dlg.dom.btn_refresh.click(function () {
dlg.load_users();
});
dlg.dom.chkbox_user_list_select_all.click(function () {
var _objects = $('#' + $app.table_ldap_users.dom_id + ' tbody').find('[data-check-box]');
if ($(this).is(':checked')) {
$.each(_objects, function (i, _obj) {
$(_obj).prop('checked', true);
});
} else {
$.each(_objects, function (i, _obj) {
$(_obj).prop('checked', false);
});
}
});
// dlg.dom.btn_refresh.click(dlg.load_users);
dlg.dom.btn_import.click(dlg.do_import);
cb_stack.exec();
};
dlg.show = function () {
dlg.dom.dialog.modal({backdrop: 'static'});
dlg.do_refresh();
dlg.get_selected_user = function (tbl) {
var items = [];
var _objs = $('#' + tbl.dom_id + ' tbody tr td input[data-check-box]');
$.each(_objs, function (i, _obj) {
if ($(_obj).is(':checked')) {
var _row_data = tbl.get_row(_obj);
items.push(_row_data.id);
}
});
return items;
};
dlg.do_refresh = function () {
dlg.on_table_users_cell_created = function (tbl, row_id, col_key, cell_obj) {
if (col_key === 'chkbox') {
cell_obj.find('[data-check-box]').click(function () {
dlg.check_user_list_all_selected();
});
}
};
dlg.on_table_users_render_created = function (render) {
render.make_check_box = function (row_id, fields) {
return '<span><input type="checkbox" data-check-box="' + fields.id + '" data-row-id="' + row_id + '"></span>';
};
render.user_state = function (row_id, fields) {
if (fields.bind) {
return '已导入';
}
};
};
dlg.check_user_list_all_selected = function (cb_stack) {
var _all_checked = true;
var _objs = $('#' + $app.table_ldap_users.dom_id + ' tbody').find('[data-check-box]');
if (_objs.length === 0) {
_all_checked = false;
} else {
$.each(_objs, function (i, _obj) {
if (!$(_obj).is(':checked')) {
_all_checked = false;
return false;
}
});
}
if (_all_checked) {
dlg.dom.chkbox_user_list_select_all.prop('checked', true);
} else {
dlg.dom.chkbox_user_list_select_all.prop('checked', false);
}
if (cb_stack)
cb_stack.exec();
};
dlg.show = function () {
dlg.load_users();
dlg.dom.dialog.modal({backdrop: 'static'});
};
dlg.load_users = function (cb_stack) {
cb_stack = cb_stack || CALLBACK_STACK.create();
dlg.dom.btn_refresh.attr('disabled', 'disabled');
$tp.ajax_post_json('/system/do-ldap-get-users', {
},
$tp.ajax_post_json('/system/do-ldap-get-users', {},
function (ret) {
dlg.dom.btn_refresh.removeAttr('disabled');
if (ret.code === TPE_OK) {
// $tp.notify_success('列举LDAP用户成功');
console.log(ret.data);
//$app.dlg_ldap_test_result.show(ret.data);
dlg._show_users(ret.data);
var _d = [];
for (var i = 0; i < ret.data.length; ++i) {
_d.push(ret.data[i]);
}
cb_stack.add(dlg.check_user_list_all_selected);
$app.table_ldap_users.set_data(cb_stack, {}, {total: ret.data.length, page_index: 1, data: _d});
} else {
$tp.notify_error('获取LDAP用户列表失败' + tp_error_msg(ret.code, ret.message));
}
@ -1584,60 +1741,34 @@ $app.create_dlg_ldap_import = function () {
);
};
dlg._show_users = function(data) {
dlg.dom.table.empty();
var attr_names = ['username', 'surname', 'email'];
var h = [];
var dn, x;
var th_created = false;
for (dn in data) {
if (!th_created) {
h.push('<thead>');
for (x in attr_names) {
h.push('<th style="text-align:left;" class="mono">');
h.push(attr_names[x]);
h.push('</th>');
}
h.push('</thead>');
th_created = true;
}
h.push('<tr>');
for (x in attr_names) {
h.push('<td style="text-align:left;" class="mono">');
if (!_.isEmpty(data[dn][attr_names[x]]))
h.push(data[dn][attr_names[x]]);
else
h.push('');
h.push('</td>');
}
h.push('</tr>');
dlg.do_import = function () {
var items = dlg.get_selected_user($app.table_ldap_users);
if (items.length === 0) {
$tp.notify_error('请选择要导入的账号!');
return;
}
dlg.dom.table.append($(h.join('')));
dlg.dom.dialog.modal();
};
console.log(items);
dlg.do_import = function () {
if (!dlg.check_fields())
return;
dlg.dom.btn_save.attr('disabled', 'disabled');
$tp.ajax_post_json('/system/do-ldap-import', {
},
dlg.dom.btn_import.attr('disabled', 'disabled');
$tp.ajax_post_json('/system/do-ldap-import', {ldap_users: items},
function (ret) {
dlg.dom.btn_save.removeAttr('disabled');
dlg.dom.btn_import.removeAttr('disabled');
if (ret.code === TPE_OK) {
$app.options.sys_cfg.ldap = dlg.ldap_config;
$tp.notify_success('保存LDAP设置成功');
CALLBACK_STACK.create()
.add($app.table_users.load_data)
.add(dlg.check_user_list_all_selected)
.add(dlg.load_users)
.exec();
$tp.notify_success('导入LDAP用户成功');
} else {
$tp.notify_error('保存LDAP设置失败' + tp_error_msg(ret.code, ret.message));
$tp.notify_error('导入LDAP用户失败' + tp_error_msg(ret.code, ret.message));
}
},
function () {
dlg.dom.btn_save.removeAttr('disabled');
$tp.notify_error('网络故障,保存LDAP设置失败!');
dlg.dom.btn_import.removeAttr('disabled');
$tp.notify_error('网络故障导入LDAP用户失败');
},
15000
);

View File

@ -44,33 +44,42 @@
<div class="table-prefix-area">
<div class="table-extend-cell">
<span class="table-name"><i class="fa fa-list fa-fw"></i> 用户列表</span>
<button id="btn-refresh-user-list" class="btn btn-sm btn-default"><i class="fa fa-redo fa-fw"></i> 刷新列表</button>
<button id="btn-refresh-user-list" class="btn btn-sm btn-default"><i class="fa fa-redo fa-fw"></i> 刷新列表
</button>
</div>
<div class="table-extend-cell table-extend-cell-right group-actions">
<button id="btn-create-user" class="btn btn-sm btn-primary"><i class="fa fa-plus-circle fa-fw"></i> 创建用户</button>
<button id="btn-import-user" class="btn btn-sm btn-default"><i class="fa fa-plus-square fa-fw"></i> 导入用户</button>
<button id="btn-create-user" class="btn btn-sm btn-primary"><i class="fa fa-plus-circle fa-fw"></i> 创建用户
</button>
<button id="btn-import-user" class="btn btn-sm btn-default"><i class="fa fa-plus-square fa-fw"></i> 导入用户
</button>
<div class="btn-group btn-group-sm dropdown" id="filter-host-group">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown"><i class="fas fa-address-book fa-fw"></i> LDAP管理(试验) <i class="fa fa-caret-right"></i></button>
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown"><i
class="fas fa-address-book fa-fw"></i> LDAP管理(试验) <i class="fa fa-caret-right"></i></button>
<ul class="dropdown-menu dropdown-menu-right dropdown-menu-sm">
<li>
<li><a href="javascript:;" data-action="ldap-import"><i class="fas fa-arrow-alt-circle-left fa-fw"></i> 导入LDAP用户</a></li>
<li><a href="javascript:;" data-action="ldap-import"><i
class="fas fa-arrow-alt-circle-left fa-fw"></i> 导入LDAP用户</a></li>
</li>
<li role="separator" class="divider"></li>
<li>
<li><a href="javascript:;" data-action="ldap-config"><i class="fas fa-cog fa-fw"></i> 设置LDAP</a></li>
<li><a href="javascript:;" data-action="ldap-config"><i class="fas fa-cog fa-fw"></i> 设置LDAP</a>
</li>
</li>
<li>
<li><a href="javascript:;" data-action="ldap-sync"><i class="fas fa-link fa-fw"></i> 同步LDAP</a></li>
<li><a href="javascript:;" data-action="ldap-sync"><i class="fas fa-link fa-fw"></i> 同步LDAP</a>
</li>
</li>
</ul>
</div>
</div>
</div>
<table id="table-user-list" class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
<table id="table-user-list"
class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
<div class="table-extend-area">
<div class="table-extend-cell checkbox-select-all"><input id="table-user-list-select-all" type="checkbox"/></div>
<div class="table-extend-cell checkbox-select-all"><input id="table-user-list-select-all" type="checkbox"/>
</div>
## _ret.push('<div id="' + _tblf.dom_id + '" class="btn-group search-select">');
## _ret.push('<button type="button" class="btn dropdown-toggle" data-toggle="dropdown">');
@ -88,14 +97,20 @@
<div class="table-extend-cell group-actions">
<div class="btn-group">
<div class="btn-group dropup" id="btn-set-role" role="group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><i class="fas fa-user fa-fw"></i> 设置角色 <i class="fa fa-caret-right"></i></button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><i
class="fas fa-user fa-fw"></i> 设置角色 <i class="fa fa-caret-right"></i></button>
<ul class="dropdown-menu dropdown-menu-sm"></ul>
</div>
## <button id="btn-set-role" type="button" class="btn btn-default"><i class="fa fa-edit fa-fw"></i> 设置角色</button>
<button id="btn-lock-user" type="button" class="btn btn-default"><i class="fa fa-lock fa-fw"></i> 禁用</button>
<button id="btn-unlock-user" type="button" class="btn btn-default"><i class="fa fa-unlock fa-fw"></i> 解禁</button>
<button id="btn-remove-user" type="button" class="btn btn-default"><i class="fa fa-times-circle fa-fw"></i> 删除</button>
<button id="btn-lock-user" type="button" class="btn btn-default"><i class="fa fa-lock fa-fw"></i> 禁用
</button>
<button id="btn-unlock-user" type="button" class="btn btn-default"><i
class="fa fa-unlock fa-fw"></i> 解禁
</button>
<button id="btn-remove-user" type="button" class="btn btn-default"><i
class="fa fa-times-circle fa-fw"></i> 删除
</button>
</div>
</div>
<div class="table-extend-cell table-item-counter">
@ -119,7 +134,9 @@
<p>说明:</p>
<ul class="help-list">
<li>可以通过表格标题栏进行搜索或过滤,以便快速定位你需要的信息。标题栏左侧的 <i class="fa fa-undo fa-fw"></i> 可以重置过滤器。</li>
<li>批量导入用户需要上传.csv格式的文件您可以 <a href="/static/download/teleport-example-user.csv"><i class="fa fa-download fa-fw"></i>下载用户信息文件模板</a> 进行编辑。</li>
<li>批量导入用户需要上传.csv格式的文件您可以 <a href="/static/download/teleport-example-user.csv"><i
class="fa fa-download fa-fw"></i>下载用户信息文件模板</a> 进行编辑。
</li>
</ul>
</div>
</div>
@ -130,7 +147,8 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 data-field="dlg-title" class="modal-title"></h3>
</div>
<div class="modal-body">
@ -141,8 +159,12 @@
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-primary" data-field="btn-edit"><i class="fa fa-edit fa-fw"></i> 编辑用户信息</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i class="fa fa-times fa-fw"></i> 关闭</button>
<button type="button" class="btn btn-sm btn-primary" data-field="btn-edit"><i
class="fa fa-edit fa-fw"></i> 编辑用户信息
</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i
class="fa fa-times fa-fw"></i> 关闭
</button>
</div>
</div>
</div>
@ -152,7 +174,8 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 data-field="dlg-title" class="modal-title"></h3>
</div>
<div class="modal-body">
@ -169,7 +192,8 @@
<div class="form-group form-group-sm">
<label for="edit-user-username" class="col-sm-2 control-label require">账号:</label>
<div class="col-sm-5">
<input id="edit-user-username" type="text" class="form-control" placeholder="用户账号,也就是用户登录名"/>
<input id="edit-user-username" type="text" class="form-control"
placeholder="用户账号,也就是用户登录名"/>
</div>
<div class="col-sm-5">
<div class="control-desc">英文字符和数字最大32字符</div>
@ -236,12 +260,16 @@
## <li><div id="sec-auth-username-password" class="tp-checkbox">用户名 + 密码</div></li>
<li>
<div id="sec-auth-username-password-captcha" class="tp-checkbox">用户名 + 密码 + 验证码</div>
<div id="sec-auth-username-password-captcha" class="tp-checkbox">用户名 + 密码 +
验证码
</div>
</li>
## <li><div id="sec-auth-username-oath" class="tp-checkbox">用户名 + 身份认证器动态密码</div></li>
<li>
<div id="sec-auth-username-password-oath" class="tp-checkbox">用户名 + 密码 + 身份认证器动态密码</div>
<div id="sec-auth-username-password-oath" class="tp-checkbox">用户名 + 密码 +
身份认证器动态密码
</div>
</li>
</ul>
</div>
@ -255,11 +283,16 @@
<div class="modal-footer">
<div class="row">
<div class="col-sm-8">
<div id="edit-user-message" class="alert alert-danger" style="text-align:left;display:none;"></div>
<div id="edit-user-message" class="alert alert-danger"
style="text-align:left;display:none;"></div>
</div>
<div class="col-sm-4">
<button type="button" class="btn btn-sm btn-primary" id="btn-edit-user-save"><i class="fa fa-check fa-fw"></i> 确定</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i class="fa fa-times fa-fw"></i> 取消</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-edit-user-save"><i
class="fa fa-check fa-fw"></i> 确定
</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i
class="fa fa-times fa-fw"></i> 取消
</button>
</div>
</div>
</div>
@ -271,7 +304,8 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 data-field="dlg-title" class="modal-title"></h3>
</div>
<div class="modal-body">
@ -287,8 +321,12 @@
<div class="form-group form-group-sm">
<div class="col-sm-10 col-sm-offset-1">
<div id="can-send-email" style="display:none">
<div> 向用户绑定的邮箱地址 <span data-field="email" class="mono"></span> 发送密码重置邮件,然后用户可通过邮件中的密码重置链接自行设置新密码。</div>
<button type="button" class="btn btn-sm btn-primary" id="btn-send-reset-email" style="margin-top:10px;"><i class="fa fa-send fa-fw"></i> 发送邮件</button>
<div> 向用户绑定的邮箱地址 <span data-field="email" class="mono"></span>
发送密码重置邮件,然后用户可通过邮件中的密码重置链接自行设置新密码。
</div>
<button type="button" class="btn btn-sm btn-primary" id="btn-send-reset-email"
style="margin-top:10px;"><i class="fa fa-send fa-fw"></i> 发送邮件
</button>
</div>
<div id="cannot-send-email" class="alert alert-warning" style="margin-top:10px;">
<i class="fas fa-exclamation-triangle fa-fw"></i> <span id="msg-cannot-send-email">用户未设置邮箱</span>,密码重置邮件功能无法使用,请使用手动重置方式。
@ -316,18 +354,25 @@
<div class="form-group form-group-sm">
<div class="col-sm-5 col-sm-offset-1">
<div class="input-group">
<input data-field="password" type="password" class="form-control mono" placeholder="设置用户的新密码">
<span class="input-group-btn"><button class="btn btn-sm btn-default" type="button" id="btn-switch-password"><i class="fa fa-eye fa-fw"></i></button></span>
<input data-field="password" type="password" class="form-control mono"
placeholder="设置用户的新密码">
<span class="input-group-btn"><button class="btn btn-sm btn-default" type="button"
id="btn-switch-password"><i
class="fa fa-eye fa-fw"></i></button></span>
</div>
</div>
<div class="col-sm-4">
<button type="button" class="btn btn-sm btn-info" id="btn-gen-random-password"><i class="far fa-snowflake fa-fw"></i> 生成随机密码</button>
<button type="button" class="btn btn-sm btn-info" id="btn-gen-random-password"><i
class="far fa-snowflake fa-fw"></i> 生成随机密码
</button>
</div>
</div>
<div class="form-group form-group-sm">
<div class="col-sm-6 col-sm-offset-1">
<button type="button" class="btn btn-sm btn-primary" id="btn-reset-password"><i class="fa fa-check fa-fw"></i> 重置密码</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-reset-password"><i
class="fa fa-check fa-fw"></i> 重置密码
</button>
</div>
</div>
@ -338,10 +383,13 @@
<div class="modal-footer">
<div class="row">
<div class="col-sm-8">
<div id="edit-user-message" class="alert alert-danger" style="text-align:left;display:none;"></div>
<div id="edit-user-message" class="alert alert-danger"
style="text-align:left;display:none;"></div>
</div>
<div class="col-sm-4">
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i class="fa fa-times fa-fw"></i> 取消</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i
class="fa fa-times fa-fw"></i> 取消
</button>
</div>
</div>
</div>
@ -353,24 +401,29 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 class="modal-title">导入用户</h3>
</div>
<div class="modal-body">
<div style="text-align:center;margin:10px 0 20px 0;">
<p>请点击图标,选择要上传的文件!</p>
<p><a href="/static/download/teleport-example-user.csv"><i class="fa fa-download fa-fw"></i>下载用户信息文件模板</a></p>
<p><a href="/static/download/teleport-example-user.csv"><i class="fa fa-download fa-fw"></i>下载用户信息文件模板</a>
</p>
</div>
<div style="text-align:center;">
<i id="btn-select-file" class="upload-button far fa-file-alt fa-fw"></i>
</div>
<div style="text-align:center;margin:10px;" id="upload-file-info">- 尚未选择文件 -</div>
<div style="text-align:center;">
<div id="upload-file-message" style="display:none;margin:10px;text-align: left;" class="alert alert-info">
<div id="upload-file-message" style="display:none;margin:10px;text-align: left;"
class="alert alert-info">
<i class="fa fa-cog fa-spin fa-fw"></i> 正在导入,请稍候...
</div>
<button type="button" class="btn btn-sm btn-primary" id="btn-do-upload-file" style="display:none;margin:10px;"><i class="fa fa-upload fa-fw"></i> 开始导入</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-do-upload-file"
style="display:none;margin:10px;"><i class="fa fa-upload fa-fw"></i> 开始导入
</button>
</div>
</div>
</div>
@ -381,7 +434,8 @@
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 class="modal-title">LDAP设置 (实验性)</h3>
</div>
<div class="modal-body">
@ -391,31 +445,34 @@
<div class="form-group form-group-sm">
<label for="edit-ldap-server" class="col-sm-2 control-label require">LDAP主机</label>
<div class="col-sm-4">
<input id="edit-ldap-server" type="text" class="form-control" placeholder="LDAP服务器IP或域名" />
<input id="edit-ldap-server" type="text" class="form-control"
placeholder="LDAP服务器IP或域名"/>
</div>
</div>
<div class="form-group form-group-sm">
<label for="edit-ldap-port" class="col-sm-2 control-label require">端口:</label>
<div class="col-sm-4">
<input id="edit-ldap-port" type="text" class="form-control" placeholder="LDAP端口默认为389" />
<input id="edit-ldap-port" type="text" class="form-control"
placeholder="LDAP端口默认为389"/>
</div>
</div>
<div class="form-group form-group-sm">
<label for="edit-ldap-domain" class="col-sm-2 control-label require">域:</label>
<div class="col-sm-4">
<input id="edit-ldap-domain" type="text" class="form-control" placeholder="" />
<input id="edit-ldap-domain" type="text" class="form-control" placeholder=""/>
</div>
<div class="col-sm-6">
<div class="control-desc-sm">LDAP的账号使用 <span class="important">用户名@域</span> 来登录teleport。</div>
<div class="control-desc-sm">LDAP的账号使用 <span class="important">用户名@域</span> 来登录teleport。
</div>
</div>
</div>
<div class="form-group form-group-sm">
<label for="edit-ldap-admin" class="col-sm-2 control-label require">管理员DN</label>
<div class="col-sm-4">
<input id="edit-ldap-admin" type="text" class="form-control" placeholder="" />
<input id="edit-ldap-admin" type="text" class="form-control" placeholder=""/>
</div>
<div class="col-sm-6">
<div class="control-desc-sm">LDAP服务的管理员账号用于列举用户、同步账号。</div>
@ -426,8 +483,11 @@
<label for="edit-ldap-password" class="col-sm-2 control-label require">密码:</label>
<div class="col-sm-4">
<div class="input-group">
<input id="edit-ldap-password" type="password" class="form-control mono" placeholder="" />
<span class="input-group-btn"><button class="btn btn-sm btn-default" type="button" id="btn-switch-ldap-password"><i class="fa fa-eye fa-fw"></i></button></span>
<input id="edit-ldap-password" type="password" class="form-control mono"
placeholder=""/>
<span class="input-group-btn"><button class="btn btn-sm btn-default" type="button"
id="btn-switch-ldap-password"><i
class="fa fa-eye fa-fw"></i></button></span>
</div>
</div>
<div class="col-sm-6">
@ -444,24 +504,30 @@
<div class="form-group form-group-sm">
<label for="edit-ldap-base-dn" class="col-sm-2 control-label require">用户基准DN</label>
<div class="col-sm-9">
<input id="edit-ldap-base-dn" type="text" class="form-control" placeholder="" />
<div class="control-desc-sm">限制用户DN的范围例如 <span class="important">ou=dev,ou=company,ou=com</span>。用户的完整DN为 <span class="important">cn=用户登录名,用户基准DN</span>。</div>
<input id="edit-ldap-base-dn" type="text" class="form-control" placeholder=""/>
<div class="control-desc-sm">限制用户DN的范围例如 <span class="important">ou=dev,ou=company,ou=com</span>。用户的完整DN为
<span class="important">cn=用户登录名,用户基准DN</span>。
</div>
</div>
</div>
<div class="form-group form-group-sm">
<label for="edit-ldap-filter" class="col-sm-2 control-label require">过滤器:</label>
<div class="col-sm-9">
<input id="edit-ldap-filter" type="text" class="form-control" placeholder="" value="(&(objectClass=person))"/>
<div class="control-desc-sm">列举用户时的过滤器,例如 <span class="important">(&(objectClass=person))</span>。</div>
<input id="edit-ldap-filter" type="text" class="form-control" placeholder=""
value="(&(objectClass=person))"/>
<div class="control-desc-sm">列举用户时的过滤器,例如 <span class="important">(&(objectClass=person))</span>。
</div>
</div>
</div>
<div class="form-group form-group-sm">
<label for="edit-ldap-attr-map" class="col-sm-2 control-label require">属性映射:</label>
<div class="col-sm-9">
<textarea id="edit-ldap-attr-map" class="form-control" style="resize:vertical;height:8em;" placeholder="">t</textarea>
<div class="control-desc-sm">将LDAP的属性映射到 teleport 的用户属性,例如 <span class="important">LDAP中的用户属性 sAMAccountName 映射为teleport的登录账号</span>。如果不清楚此LDAP服务的用户属性可使用下方的“列举属性”按钮进行查询。</div>
<textarea id="edit-ldap-attr-map" class="form-control"
style="resize:vertical;height:8em;" placeholder="">t</textarea>
<div class="control-desc-sm">将LDAP的属性映射到 teleport 的用户属性,例如 <span class="important">LDAP中的用户属性 sAMAccountName 映射为teleport的登录账号</span>。如果不清楚此LDAP服务的用户属性可使用下方的“列举属性”按钮进行查询。
</div>
</div>
</div>
@ -472,13 +538,22 @@
<div class="modal-footer">
<div class="row">
<div class="col-sm-6">
<div id="edit-user-message" class="alert alert-danger" style="text-align:left;display:none;"></div>
<div id="edit-user-message" class="alert alert-danger"
style="text-align:left;display:none;"></div>
</div>
<div class="col-sm-6" style="text-align:right;">
<button type="button" class="btn btn-sm btn-success" id="btn-ldap-config-list-attr"><i class="fa fa-list-alt fa-fw"></i> 列举属性</button>
<button type="button" class="btn btn-sm btn-success" id="btn-ldap-config-test"><i class="fa fa-bolt fa-fw"></i> 测试获取用户</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-ldap-config-save"><i class="fa fa-check fa-fw"></i> 保存设置</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i class="fa fa-times fa-fw"></i> 取消</button>
<button type="button" class="btn btn-sm btn-success" id="btn-ldap-config-list-attr"><i
class="fa fa-list-alt fa-fw"></i> 列举属性
</button>
<button type="button" class="btn btn-sm btn-success" id="btn-ldap-config-test"><i
class="fa fa-bolt fa-fw"></i> 测试获取用户
</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-ldap-config-save"><i
class="fa fa-check fa-fw"></i> 保存设置
</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i
class="fa fa-times fa-fw"></i> 取消
</button>
</div>
</div>
</div>
@ -490,7 +565,8 @@
<div class="modal-dialog" style="margin-top:50px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 class="modal-title">LDAP用户属性一览</h3>
</div>
<div class="modal-body">
@ -503,7 +579,8 @@
<div class="form-group form-group-sm">
<div class="col-sm-12">
<div id="msg-ldap-list-attr-ret" style="height:20em;overflow:scroll;padding:5px;border: 1px solid #747474;"></div>
<div id="msg-ldap-list-attr-ret"
style="height:20em;overflow:scroll;padding:5px;border: 1px solid #747474;"></div>
</div>
</div>
@ -514,7 +591,9 @@
<div class="modal-footer">
<div class="row">
<div class="col-sm-12" style="text-align:right;">
<button type="button" class="btn btn-sm btn-primary" data-dismiss="modal"><i class="fa fa-check fa-fw"></i> 确定</button>
<button type="button" class="btn btn-sm btn-primary" data-dismiss="modal"><i
class="fa fa-check fa-fw"></i> 确定
</button>
</div>
</div>
</div>
@ -526,7 +605,8 @@
<div class="modal-dialog" style="margin-top:50px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 class="modal-title">LDAP连接测试结果</h3>
</div>
<div class="modal-body">
@ -539,7 +619,8 @@
<div class="form-group form-group-sm">
<div class="col-sm-12">
<table id="table-ldap-test-ret" class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
<table id="table-ldap-test-ret"
class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
</div>
</div>
@ -550,7 +631,9 @@
<div class="modal-footer">
<div class="row">
<div class="col-sm-12" style="text-align:right;">
<button type="button" class="btn btn-sm btn-primary" data-dismiss="modal"><i class="fa fa-check fa-fw"></i> 确定</button>
<button type="button" class="btn btn-sm btn-primary" data-dismiss="modal"><i
class="fa fa-check fa-fw"></i> 确定
</button>
</div>
</div>
</div>
@ -562,33 +645,61 @@
<div class="modal-dialog modal-lg" style="margin-top:50px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i class="fa fa-times-circle fa-fw"></i></button>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><i
class="fa fa-times-circle fa-fw"></i></button>
<h3 class="modal-title">导入LDAP用户(试验)</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<div style="margin-bottom:8px;">
LDAP用户列表
<div class="table-prefix-area">
<div class="table-extend-cell">
<span class="table-name"><i class="fa fa-list fa-fw"></i> LDAP用户列表</span>
<button id="btn-ldap-import-refresh" class="btn btn-sm btn-default"><i class="fa fa-redo fa-fw"></i> 刷新列表</button>
</div>
</div>
<div class="form-group form-group-sm">
<div class="col-sm-12">
<table id="table-ldap-import" class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
<table id="table-ldap-user-list"
class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
<div class="table-extend-area">
<div class="table-extend-cell checkbox-select-all">
<input id="table-ldap-user-select-all" type="checkbox"/>
</div>
<div class="table-extend-cell group-actions">
<div class="btn-group" role="group">
<button id="btn-ldap-import-import" type="button" class="btn btn-primary">
<i class="fas fa-address-book fa-fw"></i> 导入选中的用户
</button>
</div>
</div>
</div>
## <div class="form-horizontal">
## <div style="margin-bottom:8px;">
## LDAP用户列表
## </div>
##
## <div class="form-group form-group-sm">
##
## <div class="col-sm-12">
## <table id="table-ldap-user-list"
## class="table table-striped table-bordered table-hover table-data no-footer dtr-inline"></table>
## </div>
##
## </div>
##
## </div>
</div>
<div class="modal-footer">
<div class="row">
<div class="col-sm-12" style="text-align:right;">
<button type="button" class="btn btn-sm btn-success" id="btn-ldap-import-refresh"><i class="fa fa-redo fa-fw"></i> 刷新列表</button>
<button type="button" class="btn btn-sm btn-primary" id="btn-ldap-import-import"><i class="fa fa-check fa-fw"></i> 导入选中用户</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal"><i class="fa fa-times fa-fw"></i> 取消</button>
<button type="button" class="btn btn-sm btn-default" data-dismiss="modal">
<i class="fa fa-times fa-fw"></i> 关闭
</button>
</div>
</div>
</div>

View File

@ -9,6 +9,13 @@ TP_LOGIN_AUTH_USERNAME_PASSWORD_OATH = 0x0008 # 用户名+密码+OATH
APP_MODE_NORMAL = 1
APP_MODE_MAINTENANCE = 2
# ==========================================================================
# 用户类型
# ==========================================================================
TP_USER_TYPE_NONE = 0
TP_USER_TYPE_LOCAL = 1
TP_USER_TYPE_LDAP = 2
# ==========================================================================
# 远程主机账号认证方式
# ==========================================================================

View File

@ -258,6 +258,9 @@ controllers = [
(r'/system/do-ldap-config-test', system.DoLdapConfigTestHandler),
# - [json] 获取LDAP用户列表
(r'/system/do-ldap-get-users', system.DoLdapGetUsersHandler),
# - [json] 导入LDAP用户
(r'/system/do-ldap-import', system.DoLdapImportHandler),
#
# - [json] 获取服务器时间
(r'/system/get-time', system.DoGetTimeHandler),

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import datetime
import hashlib
import json
import shutil
@ -17,6 +18,7 @@ from app.model import syslog
from app.model import record
from app.model import ops
from app.model import audit
from app.model import user
from app.base.core_server import core_service_async_post_http
from app.base.session import tp_session
from app.logic.auth.ldap import Ldap
@ -515,7 +517,7 @@ class DoLdapGetUsersHandler(TPBaseJsonHandler):
try:
if len(tp_cfg().sys_ldap_password) == 0:
return self.write_json(TPE_PARAM, message='需要设置LDAP管理员密码')
return self.write_json(TPE_PARAM, message='LDAP未能正确配置,需要管理员密码')
else:
_password = tp_cfg().sys_ldap_password
_server = tp_cfg().sys.ldap.server
@ -535,14 +537,180 @@ class DoLdapGetUsersHandler(TPBaseJsonHandler):
return self.write_json(ret, message=err_msg)
else:
# TODO: search all user in database to check if the LDAP user have already bind.
ret_data = []
for u in data:
data[u]['bind'] = False
return self.write_json(ret, data=data)
h = hashlib.sha1()
h.update(u.encode())
user = data[u]
user['bind'] = False
user['id'] = h.hexdigest()
ret_data.append(user)
return self.write_json(ret, data=ret_data)
except:
log.e('')
return self.write_json(TPE_PARAM)
class DoLdapImportHandler(TPBaseJsonHandler):
@tornado.gen.coroutine
def post(self):
ret = self.check_privilege(TP_PRIVILEGE_USER_CREATE)
if ret != TPE_OK:
return
args = self.get_argument('args', None)
if args is None:
return self.write_json(TPE_PARAM)
try:
args = json.loads(args)
except:
return self.write_json(TPE_JSON_FORMAT)
try:
dn_hash_list = args['ldap_users']
if len(tp_cfg().sys_ldap_password) == 0:
return self.write_json(TPE_PARAM, message='LDAP未能正确配置需要管理员密码')
else:
_password = tp_cfg().sys_ldap_password
_server = tp_cfg().sys.ldap.server
_port = tp_cfg().sys.ldap.port
_admin = tp_cfg().sys.ldap.admin
_base_dn = tp_cfg().sys.ldap.base_dn
_filter = tp_cfg().sys.ldap.filter
_attr_map = tp_cfg().sys.ldap.attr_map
except:
return self.write_json(TPE_PARAM)
try:
ldap = Ldap(_server, _port, _base_dn)
ret, data, err_msg = ldap.list_users(_admin, _password, _filter, _attr_map)
if ret != TPE_OK:
return self.write_json(ret, message=err_msg)
else:
# TODO: search all user in database to check if the LDAP user have already bind.
need_import = []
for u in data:
h = hashlib.sha1()
h.update(u.encode())
dn_hash = h.hexdigest()
for x in dn_hash_list:
if x == dn_hash:
_user = data[u]
_user['dn'] = u
need_import.append(_user)
break
if len(need_import) == 0:
return self.write_json(ret, message='没有可以导入的LDAP用户')
return self._do_import(need_import)
except:
log.e('')
return self.write_json(TPE_PARAM)
def _do_import(self, users):
success = list()
failed = list()
try:
user_list = []
for _u in users:
u = dict()
u['_line'] = 0
u['_id'] = 0
u['type'] = TP_USER_TYPE_LDAP
u['ldap_dn'] = _u['dn']
u['username'] = '{}@{}'.format(_u['username'], tp_cfg().sys.ldap.domain)
u['surname'] = _u['surname']
u['email'] = _u['email']
u['mobile'] = ''
u['qq'] = ''
u['wechat'] = ''
u['desc'] = ''
u['password'] = ''
# fix
if len(u['surname']) == 0:
u['surname'] = u['username']
u['username'] = u['username'].lower()
user_list.append(u)
user.create_users(self, user_list, success, failed)
# 对于创建成功的用户,发送密码邮件函
sys_smtp_password = tp_cfg().sys_smtp_password
if len(sys_smtp_password) > 0:
web_url = '{}://{}'.format(self.request.protocol, self.request.host)
for u in user_list:
if u['_id'] == 0 or len(u['email']) == 0:
continue
mmm = '{surname} 您好!\n\n已为您创建teleport系统用户账号现在可以使用以下信息登录teleport系统\n\n'
'登录用户名:{username}\n'
'密码:您正在使用的密码\n'
'地址:{web_url}\n\n\n\n'
'[本邮件由teleport系统自动发出请勿回复]'
'\n\n'
''.format(surname=u['surname'], username=u['username'], web_url=web_url)
print(mmm)
err = TPE_FAILED
msg = 'test bad.'
# err, msg = yield mail.tp_send_mail(
# u['email'],
# '{surname} 您好!\n\n已为您创建teleport系统用户账号现在可以使用以下信息登录teleport系统\n\n'
# '登录用户名:{username}\n'
# '密码:您正在使用的密码\n'
# '地址:{web_url}\n\n\n\n'
# '[本邮件由teleport系统自动发出请勿回复]'
# '\n\n'
# ''.format(surname=u['surname'], username=u['username'], web_url=web_url),
# subject='用户密码函'
# )
if err != TPE_OK:
failed.append({'line': u['_line'], 'error': '无法发送密码函到邮箱 {},错误:{}'.format(u['email'], msg)})
# 统计结果
total_success = 0
total_failed = 0
for u in user_list:
if u['_id'] == 0:
total_failed += 1
else:
total_success += 1
# 生成最终结果信息
if len(failed) == 0:
# ret['code'] = TPE_OK
# ret['message'] = '共导入 {} 个用户账号!'.format(total_success)
return self.write_json(TPE_OK, message='共导入 {} 个用户账号!'.format(total_success))
else:
# ret['code'] = TPE_FAILED
msg = ''
if total_success > 0:
msg = '{} 个用户账号导入成功,'.format(total_success)
if total_failed > 0:
msg += '{} 个用户账号未能导入!'.format(total_failed)
# ret['data'] = failed
return self.write_json(TPE_FAILED, data=failed, message=msg)
except:
log.e('got exception when import LDAP user.\n')
# ret['code'] = TPE_FAILED
msg = ''
if len(success) > 0:
msg += '{} 个用户账号导入后发生异常!'.format(len(success))
else:
msg = '发生异常!'
# ret['data'] = failed
return self.write_json(TPE_FAILED, data=failed, message=msg)
class DoCleanupStorageHandler(TPBaseJsonHandler):
@tornado.gen.coroutine
def post(self):
@ -573,5 +741,3 @@ class DoRebuildAuditAuzMapHandler(TPBaseJsonHandler):
err = ops.build_auz_map()
self.write_json(err)

View File

@ -194,6 +194,10 @@ def create_users(handler, user_list, success, failed):
for i in range(len(user_list)):
user = user_list[i]
if 'type' not in user:
user['type'] = TP_USER_TYPE_LOCAL
if 'ldap_dn' not in user:
user['ldap_dn'] = ''
err = s.reset().select_from('user', ['id']).where('user.username="{}"'.format(user['username'])).query()
if err != TPE_OK:
@ -202,11 +206,14 @@ def create_users(handler, user_list, success, failed):
failed.append({'line': user['_line'], 'error': '账号 `{}` 已经存在'.format(user['username'])})
continue
_password = tp_password_generate_secret(user['password'])
if user['type'] == TP_USER_TYPE_LOCAL:
_password = tp_password_generate_secret(user['password'])
else:
_password = ''
sql = 'INSERT INTO `{}user` (`type`, `auth_type`, `password`, `username`, `surname`, `role_id`, `state`, `email`, `creator_id`, `create_time`, `last_login`, `last_chpass`, `desc`) VALUES ' \
'(1, 0, "{password}", "{username}", "{surname}", 0, {state}, "{email}", {creator_id}, {create_time}, {last_login}, {last_chpass}, "{desc}");' \
''.format(db.table_prefix,
'({user_type}, 0, "{password}", "{username}", "{surname}", 0, {state}, "{email}", {creator_id}, {create_time}, {last_login}, {last_chpass}, "{desc}");' \
''.format(db.table_prefix, user_type=user['type'],
username=user['username'], surname=user['surname'], password=_password, state=TP_STATE_NORMAL, email=user['email'],
creator_id=operator['id'], create_time=_time_now, last_login=0, last_chpass=0, desc=user['desc'])
db_ret = db.exec(sql)