mirror of https://github.com/tp4a/teleport
实现:列出LDAP服务器上的用户属性,方便管理员设置LDAP服务器配置项。
parent
de047d9a72
commit
e62d2b3546
|
@ -257,7 +257,7 @@ function tp_error_msg(error_code, message) {
|
|||
var msg = '';
|
||||
switch (error_code) {
|
||||
case TPE_NEED_LOGIN:
|
||||
msg = '需要登录';
|
||||
msg = '需要刷新页面,重新登录';
|
||||
break;
|
||||
case TPE_PRIVILEGE:
|
||||
msg = '没有此操作权限';
|
||||
|
|
|
@ -192,6 +192,9 @@ $app.create_controls = function (cb_stack) {
|
|||
$app.dlg_ldap_config.show();
|
||||
});
|
||||
|
||||
$app.dlg_ldap_list_attr_result = $app.create_dlg_ldap_list_attr_result();
|
||||
cb_stack.add($app.dlg_ldap_list_attr_result.init);
|
||||
|
||||
$app.dlg_ldap_test_result = $app.create_dlg_ldap_test_result();
|
||||
cb_stack.add($app.dlg_ldap_test_result.init);
|
||||
|
||||
|
@ -1245,11 +1248,13 @@ $app.create_dlg_ldap_config = function () {
|
|||
btn_switch_password: $('#btn-switch-ldap-password'),
|
||||
btn_switch_password_icon: $('#btn-switch-ldap-password i'),
|
||||
|
||||
btn_list_attr: $('#btn-ldap-config-list-attr'),
|
||||
btn_test: $('#btn-ldap-config-test'),
|
||||
btn_save: $('#btn-ldap-config-save')
|
||||
};
|
||||
|
||||
dlg.init = function (cb_stack) {
|
||||
dlg.dom.btn_list_attr.click(dlg.do_list_attr);
|
||||
dlg.dom.btn_test.click(dlg.do_test);
|
||||
dlg.dom.btn_save.click(dlg.do_save);
|
||||
|
||||
|
@ -1361,6 +1366,32 @@ $app.create_dlg_ldap_config = function () {
|
|||
return true;
|
||||
};
|
||||
|
||||
dlg.do_list_attr = function () {
|
||||
if (!dlg.check_fields())
|
||||
return;
|
||||
dlg.dom.btn_test.attr('disabled', 'disabled');
|
||||
$tp.ajax_post_json('/user/do-ldap-config-list-attr', {
|
||||
c: dlg.ldap_config,
|
||||
p: dlg.ldap_config_password
|
||||
},
|
||||
function (ret) {
|
||||
dlg.dom.btn_test.removeAttr('disabled');
|
||||
if (ret.code === TPE_OK) {
|
||||
$tp.notify_success('列举LDAP用户属性成功!');
|
||||
console.log(ret.data);
|
||||
$app.dlg_ldap_list_attr_result.show(ret.data.attributes);
|
||||
} else {
|
||||
$tp.notify_error('列举LDAP用户属性失败:' + tp_error_msg(ret.code, ret.message));
|
||||
}
|
||||
},
|
||||
function () {
|
||||
dlg.dom.btn_test.removeAttr('disabled');
|
||||
$tp.notify_error('网络故障,列举LDAP用户属性失败!');
|
||||
},
|
||||
15000
|
||||
);
|
||||
};
|
||||
|
||||
dlg.do_test = function () {
|
||||
if (!dlg.check_fields())
|
||||
return;
|
||||
|
@ -1372,6 +1403,7 @@ $app.create_dlg_ldap_config = function () {
|
|||
function (ret) {
|
||||
dlg.dom.btn_test.removeAttr('disabled');
|
||||
if (ret.code === TPE_OK) {
|
||||
console.log(ret.data);
|
||||
$tp.notify_success('LDAP连接测试成功!');
|
||||
$app.dlg_ldap_test_result.show(ret.data);
|
||||
} else {
|
||||
|
@ -1420,6 +1452,41 @@ $app.create_dlg_ldap_config = function () {
|
|||
return dlg;
|
||||
};
|
||||
|
||||
$app.create_dlg_ldap_list_attr_result = function () {
|
||||
var dlg = {};
|
||||
dlg.dom_id = 'dlg-ldap-list-attr-result';
|
||||
|
||||
dlg.dom = {
|
||||
dialog: $('#' + dlg.dom_id),
|
||||
msg_ret: $('#msg-ldap-list-attr-ret')
|
||||
};
|
||||
|
||||
dlg.init = function (cb_stack) {
|
||||
cb_stack.exec();
|
||||
};
|
||||
|
||||
dlg.show = function (data) {
|
||||
dlg.dom.msg_ret.html('');
|
||||
|
||||
var h = [];
|
||||
|
||||
var attr_name;
|
||||
for (attr_name in data) {
|
||||
h.push('<div style="white-space:nowrap;"><span class="mono important">' + attr_name + '</span>: ');
|
||||
// h.push('<span>'+data[attr_name]+'</span></div>');
|
||||
h.push('<span>');
|
||||
h.push(data[attr_name].join(', '));
|
||||
h.push('</span></div>');
|
||||
}
|
||||
|
||||
dlg.dom.msg_ret.html($(h.join('')));
|
||||
|
||||
dlg.dom.dialog.modal();
|
||||
};
|
||||
|
||||
return dlg;
|
||||
};
|
||||
|
||||
$app.create_dlg_ldap_test_result = function () {
|
||||
var dlg = {};
|
||||
dlg.dom_id = 'dlg-ldap-test-result';
|
||||
|
@ -1437,13 +1504,40 @@ $app.create_dlg_ldap_test_result = function () {
|
|||
dlg.show = function (data) {
|
||||
dlg.dom.table.empty();
|
||||
|
||||
// var h = [];
|
||||
// var i, x;
|
||||
// var th_created = false;
|
||||
// for (i = 0; i < data.length; ++i) {
|
||||
// if (!th_created) {
|
||||
// h.push('<thead>');
|
||||
// for (x in data[i]) {
|
||||
// h.push('<th style="text-align:left;" class="mono">');
|
||||
// h.push(x);
|
||||
// h.push('</th>');
|
||||
// }
|
||||
// h.push('</thead>');
|
||||
// th_created = true;
|
||||
// }
|
||||
//
|
||||
// h.push('<tr>');
|
||||
// for (x in data[i]) {
|
||||
// h.push('<td style="text-align:left;" class="mono">');
|
||||
// if (!_.isEmpty(data[i][x]))
|
||||
// h.push(data[i][x]);
|
||||
// else
|
||||
// h.push('');
|
||||
// h.push('</td>');
|
||||
// }
|
||||
// h.push('</tr>');
|
||||
// }
|
||||
|
||||
var h = [];
|
||||
var i, x;
|
||||
var dn, x;
|
||||
var th_created = false;
|
||||
for (i = 0; i < data.length; ++i) {
|
||||
for (dn in data) {
|
||||
if (!th_created) {
|
||||
h.push('<thead>');
|
||||
for (x in data[i]) {
|
||||
for (x in data[dn]) {
|
||||
h.push('<th style="text-align:left;" class="mono">');
|
||||
h.push(x);
|
||||
h.push('</th>');
|
||||
|
@ -1453,10 +1547,10 @@ $app.create_dlg_ldap_test_result = function () {
|
|||
}
|
||||
|
||||
h.push('<tr>');
|
||||
for (x in data[i]) {
|
||||
for (x in data[dn]) {
|
||||
h.push('<td style="text-align:left;" class="mono">');
|
||||
if (!_.isEmpty(data[i][x]))
|
||||
h.push(data[i][x]);
|
||||
if (!_.isEmpty(data[dn][x]))
|
||||
h.push(data[dn][x]);
|
||||
else
|
||||
h.push('');
|
||||
h.push('</td>');
|
||||
|
@ -1464,6 +1558,7 @@ $app.create_dlg_ldap_test_result = function () {
|
|||
h.push('</tr>');
|
||||
}
|
||||
|
||||
|
||||
dlg.dom.table.append($(h.join('')));
|
||||
dlg.dom.dialog.modal();
|
||||
};
|
||||
|
|
|
@ -389,7 +389,7 @@
|
|||
<div class="form-horizontal">
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<label for="edit-ldap-host" class="col-sm-2 control-label require">主机:</label>
|
||||
<label for="edit-ldap-host" class="col-sm-2 control-label require">LDAP主机:</label>
|
||||
<div class="col-sm-4">
|
||||
<input id="edit-ldap-host" type="text" class="form-control" placeholder="LDAP服务器IP或域名" value="192.168.0.10"/>
|
||||
</div>
|
||||
|
@ -408,17 +408,17 @@
|
|||
<input id="edit-ldap-domain" type="text" class="form-control" placeholder="" value="apexnas.com"/>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="control-desc">teleport将会用 <span class="important">用户名@域</span> 来访问此LDAP服务器。</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">管理员:</label>
|
||||
<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="" value="cn=admin,dc=apexnas,dc=com"/>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="control-desc">LDAP服务的管理员账号,用于列举用户、同步账号。</div>
|
||||
<div class="control-desc-sm">LDAP服务的管理员账号,用于列举用户、同步账号。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -430,6 +430,9 @@
|
|||
<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">
|
||||
<div class="control-desc-sm">LDAP服务的管理员密码。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -457,10 +460,10 @@
|
|||
<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="">tp.username = sAMAccountName
|
||||
<textarea id="edit-ldap-attr-map" class="form-control" style="resize:vertical;height:8em;" placeholder="">tp.username = uid
|
||||
tp.surname = cn
|
||||
tp.email = mail</textarea>
|
||||
<div class="control-desc-sm">将LDAP的属性映射到 teleport 的用户属性,例如 <span class="important">LDAP中的用户sAMAccountName 映射到teleport的登录账号</span>。</div>
|
||||
<div class="control-desc-sm">将LDAP的属性映射到 teleport 的用户属性,例如 <span class="important">LDAP中的用户属性 sAMAccountName 映射为teleport的登录账号</span>。如果不清楚此LDAP服务的用户属性,可使用下方的“列举属性”按钮进行查询。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -474,7 +477,8 @@ tp.email = mail</textarea>
|
|||
<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-test"><i class="fa fa-bolt 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>
|
||||
|
@ -484,6 +488,42 @@ tp.email = mail</textarea>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="dlg-ldap-list-attr-result" tabindex="-1" role="dialog">
|
||||
<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>
|
||||
<h3 class="modal-title">LDAP用户属性一览</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-horizontal">
|
||||
<div style="margin-bottom:8px;">
|
||||
用户属性列表
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</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-primary" data-dismiss="modal"><i class="fa fa-check fa-fw"></i> 确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="dlg-ldap-test-result" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" style="margin-top:50px;">
|
||||
<div class="modal-content">
|
||||
|
|
|
@ -82,6 +82,8 @@ controllers = [
|
|||
(r'/user/do-unbind-oath', user.DoUnBindOathHandler),
|
||||
# - [json] 测试LDAP的配置
|
||||
(r'/user/do-ldap-config-test', user.DoLdapConfigTestHandler),
|
||||
# - [json] 列出LDAP服务器的用户的属性,便于管理员做属性映射
|
||||
(r'/user/do-ldap-config-list-attr', user.DoLdapListUserAttrHandler),
|
||||
#
|
||||
# - 用户组管理页面
|
||||
(r'/user/group', user.GroupListHandler),
|
||||
|
|
|
@ -929,7 +929,8 @@ class DoLdapConfigTestHandler(TPBaseJsonHandler):
|
|||
return self.write_json(TPE_PARAM)
|
||||
|
||||
try:
|
||||
ldap = Ldap(cfg['host'], cfg['port'], cfg['base_dn'], cfg['domain'])
|
||||
# ldap = Ldap(cfg['host'], cfg['port'], cfg['base_dn'], cfg['domain'])
|
||||
ldap = Ldap(cfg['host'], cfg['port'], cfg['base_dn'])
|
||||
ret, data, err_msg = ldap.list_users(cfg['admin'], password, cfg['filter'], cfg['attr_map'], size_limit=10)
|
||||
if ret != TPE_OK:
|
||||
return self.write_json(ret, message=err_msg)
|
||||
|
@ -940,3 +941,35 @@ class DoLdapConfigTestHandler(TPBaseJsonHandler):
|
|||
return self.write_json(TPE_PARAM)
|
||||
|
||||
|
||||
class DoLdapListUserAttrHandler(TPBaseJsonHandler):
|
||||
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:
|
||||
cfg = args['c']
|
||||
cfg['port'] = int(cfg['port'])
|
||||
password = args['p']
|
||||
except:
|
||||
return self.write_json(TPE_PARAM)
|
||||
|
||||
try:
|
||||
ldap = Ldap(cfg['host'], cfg['port'], cfg['base_dn'])
|
||||
ret, data, err_msg = ldap.get_all_attr(cfg['admin'], password, cfg['filter'])
|
||||
if ret != TPE_OK:
|
||||
return self.write_json(ret, message=err_msg)
|
||||
else:
|
||||
return self.write_json(ret, data=data)
|
||||
except:
|
||||
log.e('')
|
||||
return self.write_json(TPE_PARAM)
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import ldap3
|
||||
import ldap3.core.exceptions
|
||||
from app.base.logger import *
|
||||
from app.const import *
|
||||
|
||||
|
||||
class Ldap(object):
|
||||
def __init__(self, ldap_host, ldap_port, base_dn, domain):
|
||||
def __init__(self, ldap_host, ldap_port, base_dn):
|
||||
self._server = ldap3.Server(ldap_host, ldap_port, connect_timeout=5, use_ssl=False)
|
||||
self._base_dn = base_dn
|
||||
self._domain = domain
|
||||
# self._domain = domain
|
||||
pass
|
||||
|
||||
def _parse_attr_map(self, attr_map):
|
||||
|
@ -33,6 +35,56 @@ class Ldap(object):
|
|||
|
||||
return attrs_ldap, attrs_tp, ''
|
||||
|
||||
def get_all_attr(self, admin, password, filter):
|
||||
# user = '{}@{}'.format(admin, self._domain)
|
||||
user = admin
|
||||
conn = ldap3.Connection(self._server, user=user, password=password, check_names=True, lazy=False, raise_exceptions=False)
|
||||
try:
|
||||
conn.open()
|
||||
except Exception as e:
|
||||
log.e(str(e))
|
||||
return TPE_FAILED, None, '无法连接到LDAP服务器'
|
||||
|
||||
conn.bind()
|
||||
if not ('result' in conn.result and 0 == conn.result['result'] and 'description' in conn.result and 'success' == conn.result['description']):
|
||||
return TPE_FAILED, None, 'LDAP管理员认证失败'
|
||||
|
||||
ret = conn.search(
|
||||
search_base=self._base_dn,
|
||||
size_limit=1,
|
||||
search_filter=filter, # (&(objectClass=person))
|
||||
search_scope=ldap3.SUBTREE,
|
||||
attributes=['*']
|
||||
)
|
||||
if not ret:
|
||||
return TPE_FAILED, None, '未能找到任何用户'
|
||||
|
||||
if len(conn.response) == 0:
|
||||
return TPE_FAILED, None, '未能找到任何用户'
|
||||
|
||||
# print(conn.entries[0].entry_to_json())
|
||||
result = json.loads(conn.entries[0].entry_to_json())
|
||||
|
||||
# attrs = conn.response[0]['attributes']
|
||||
#
|
||||
# result = {}
|
||||
# for a in attrs:
|
||||
# if isinstance(attrs[a], list):
|
||||
# val = []
|
||||
# for i in attrs[a]:
|
||||
# if isinstance(i, bytes):
|
||||
# val.append(i.decode())
|
||||
# else:
|
||||
# val.append(i)
|
||||
# result[a] = ', '.join(val)
|
||||
# else:
|
||||
# if isinstance(attrs[a], bytes):
|
||||
# result[a] = attrs[a].decode()
|
||||
# else:
|
||||
# result[a] = attrs[a].__str__()
|
||||
|
||||
return TPE_OK, result, ''
|
||||
|
||||
def list_users(self, admin, password, filter, attr_map, size_limit=0):
|
||||
attrs_ldap, attrs_tp, msg = self._parse_attr_map(attr_map)
|
||||
if attrs_ldap is None:
|
||||
|
@ -51,56 +103,49 @@ class Ldap(object):
|
|||
if not ('result' in conn.result and 0 == conn.result['result'] and 'description' in conn.result and 'success' == conn.result['description']):
|
||||
return TPE_FAILED, None, 'LDAP管理员认证失败'
|
||||
|
||||
# for test, list all attributes.
|
||||
ret_a = conn.search(
|
||||
search_base=self._base_dn,
|
||||
size_limit=size_limit,
|
||||
try:
|
||||
ret = conn.search(
|
||||
search_base=self._base_dn,
|
||||
size_limit=size_limit,
|
||||
search_filter=filter, # (&(objectClass=person))
|
||||
search_scope=ldap3.SUBTREE,
|
||||
|
||||
# search_filter='(&(sAMAccountName={}*)(&(objectClass=person)))'.format(username),
|
||||
# search_filter=filter, # (&(objectClass=person))
|
||||
search_filter='(cn=*)',
|
||||
search_scope=ldap3.SUBTREE,
|
||||
attributes=attrs_ldap
|
||||
)
|
||||
|
||||
# attributes=['cn', 'mail', 'sAMAccountName', 'objectGUID']
|
||||
attributes=['*']
|
||||
)
|
||||
if len(conn.response) == 0:
|
||||
return TPE_FAILED, [], ''
|
||||
u = conn.response[0]
|
||||
log.v(u['attributes'])
|
||||
if not ret:
|
||||
return TPE_FAILED, None, '未能搜索到LDAP用户,请检查用户基准DN和过滤器设置'
|
||||
|
||||
# ...
|
||||
ret = conn.search(
|
||||
search_base=self._base_dn,
|
||||
size_limit=size_limit,
|
||||
except ldap3.core.exceptions.LDAPAttributeError as e:
|
||||
log.e('')
|
||||
return TPE_FAILED, None, '请检查属性映射设置:{}'.format(e.__str__())
|
||||
|
||||
# search_filter='(&(sAMAccountName={}*)(&(objectClass=person)))'.format(username),
|
||||
search_filter=filter, # (&(objectClass=person))
|
||||
search_scope=ldap3.SUBTREE,
|
||||
result = {}
|
||||
|
||||
# attributes=['cn', 'mail', 'sAMAccountName', 'objectGUID']
|
||||
# attributes=['*']
|
||||
attributes=attrs_ldap
|
||||
)
|
||||
for i in range(0, len(conn.entries)):
|
||||
u = json.loads(conn.entries[0].entry_to_json())
|
||||
# result.append(u)
|
||||
a = {}
|
||||
for m in range(0, len(attrs_ldap)):
|
||||
a[attrs_tp[m]] = u['attributes'][attrs_ldap[m]]
|
||||
# result.append(a)
|
||||
result[u['dn']] = a
|
||||
|
||||
result = []
|
||||
|
||||
# print(self.conn.entries[0].entry_to_json)
|
||||
|
||||
if ret:
|
||||
for u in conn.response:
|
||||
# if u['attributes']['cn'].lower() in ['guest', 'krbtgt']:
|
||||
# continue
|
||||
# print(u)
|
||||
# print(u['attributes']['cn'])
|
||||
# result.append(u['attributes'])
|
||||
a = {}
|
||||
for i in range(0, len(attrs_ldap)):
|
||||
a[attrs_tp[i]] = u['attributes'][attrs_ldap[i]]
|
||||
result.append(a)
|
||||
# print(conn.entries[0].entry_to_json())
|
||||
#
|
||||
# if ret:
|
||||
# for u in conn.response:
|
||||
# # if u['attributes']['cn'].lower() in ['guest', 'krbtgt']:
|
||||
# # continue
|
||||
# # print(u)
|
||||
# # print(u['attributes']['cn'])
|
||||
# # result.append(u['attributes'])
|
||||
# a = {}
|
||||
# for i in range(0, len(attrs_ldap)):
|
||||
# a[attrs_tp[i]] = u['attributes'][attrs_ldap[i]]
|
||||
# result.append(a)
|
||||
|
||||
return TPE_OK, result, ''
|
||||
|
||||
def valid_user(self, user_dn, password):
|
||||
return False
|
||||
|
||||
|
|
Loading…
Reference in New Issue