diff --git a/spug_api/apps/account/views.py b/spug_api/apps/account/views.py index 182b9bd..d9608e4 100644 --- a/spug_api/apps/account/views.py +++ b/spug_api/apps/account/views.py @@ -201,7 +201,7 @@ def login(request): if not config: return handle_response(error='请在系统设置中配置LDAP后再尝试通过该方式登录') ldap = LDAP(**config) - is_success, message = ldap.valid_user(form.username, form.password) + is_success, message = ldap.verify_user(form.username, form.password) if is_success: if not user: user = User.objects.create(username=form.username, nickname=form.username, type=form.type) diff --git a/spug_api/apps/setting/urls.py b/spug_api/apps/setting/urls.py index 89c6de5..33ca444 100644 --- a/spug_api/apps/setting/urls.py +++ b/spug_api/apps/setting/urls.py @@ -9,7 +9,9 @@ from apps.setting.user import UserSettingView urlpatterns = [ url(r'^$', SettingView.as_view()), url(r'^user/$', UserSettingView.as_view()), + url(r'^ldap/$', LDAPUserView.as_view()), url(r'^ldap_test/$', ldap_test), + url(r'^ldap_import/$', ldap_import), url(r'^email_test/$', email_test), url(r'^mfa/$', MFAView.as_view()), url(r'^about/$', get_about) diff --git a/spug_api/apps/setting/views.py b/spug_api/apps/setting/views.py index 417cc15..ffa5cef 100644 --- a/spug_api/apps/setting/views.py +++ b/spug_api/apps/setting/views.py @@ -6,14 +6,16 @@ from django.core.cache import cache from django.conf import settings from libs import JsonParser, Argument, json_response, auth from libs.utils import generate_random_str +from libs.ldap import LDAP from libs.mail import Mail from libs.spug import send_login_wx_code from libs.mixins import AdminView from apps.setting.utils import AppSetting from apps.setting.models import Setting, KEYS_DEFAULT +from apps.account.models import User from copy import deepcopy import platform -import ldap +import json class SettingView(AdminView): @@ -68,21 +70,79 @@ class MFAView(AdminView): def ldap_test(request): form, error = JsonParser( Argument('server'), - Argument('port', type=int), Argument('admin_dn'), - Argument('password'), + Argument('admin_password'), + Argument('user_ou'), + Argument('user_filter'), + Argument('map_username'), + Argument('map_nickname'), + ).parse(request.body) + print('form', form) + if error is None: + ldap = LDAP(form.server, form.admin_dn, form.admin_password, form.user_ou, form.user_filter, form.map_username, form.map_nickname) + status, ret = ldap.all_user() + if status: + return json_response(ret) + return json_response(error=ret) + return json_response(error=error) + + +@auth('admin') +def ldap_import(request): + form, error = JsonParser( + Argument('ldap_data', type=list), + Argument('username'), + Argument('nickname'), ).parse(request.body) if error is None: - try: - con = ldap.initialize("ldap://{0}:{1}".format(form.server, form.port), bytes_mode=False) - con.simple_bind_s(form.admin_dn, form.password) - return json_response() - except Exception as e: - error = eval(str(e)) - return json_response(error=error['desc']) + for x in form.ldap_data: + User.objects.update_or_create( + username=x[form.username], + defaults={'nickname': x[form.nickname], 'type': 'ldap'} + ) + return json_response() return json_response(error=error) +class LDAPUserView(AdminView): + def get(self, request): + ldap_config = AppSetting.get('ldap_service') + if not ldap_config: + return json_response(error='LDAP服务未配置') + ldap = LDAP(**ldap_config) + status, ret = ldap.all_user() + if status: + cn_key, sn_key = ldap_config.get('map_username'), ldap_config.get('map_nickname') + system_users = [x.username for x in User.objects.filter(type='ldap', deleted_by_id__isnull=True)] + for index, u in enumerate(ret): + u['cn'] = u[cn_key] + u['sn'] = u[sn_key] + u['is_exist'] = u.get(cn_key) in system_users + u['id'] = index + return json_response(ret) + return json_response(error=ret) + + def post(self, request): + form, error = JsonParser( + Argument('server'), + Argument('admin_dn'), + Argument('admin_password'), + Argument('user_ou'), + Argument('user_filter'), + Argument('map_username'), + Argument('map_nickname'), + Argument('ldap_user', help='LDAP用户不能为空'), + Argument('ldap_password', help='LDAP密码不能为空'), + ).parse(request.body) + if error is None: + ldap = LDAP(form.server, form.admin_dn, form.admin_password, form.user_ou, form.user_filter, form.map_username, form.map_nickname) + status, msg = ldap.verify_user(form.ldap_user, form.ldap_password) + if status: + return json_response() + return json_response(error=msg) + return json_response(error=error) + + @auth('admin') def email_test(request): form, error = JsonParser( diff --git a/spug_api/libs/ldap.py b/spug_api/libs/ldap.py index fc9b6c0..d7a391c 100644 --- a/spug_api/libs/ldap.py +++ b/spug_api/libs/ldap.py @@ -5,26 +5,68 @@ import ldap class LDAP: - def __init__(self, server, port, rules, admin_dn, password, base_dn): + def __init__(self, server, admin_dn, admin_password, user_ou, user_filter, map_username, map_nickname): self.server = server - self.port = port - self.rules = rules self.admin_dn = admin_dn - self.password = password - self.base_dn = base_dn + self.admin_dn = admin_dn + self.admin_password = admin_password + self.user_ou = user_ou + self.user_filter = user_filter + self.map_username = map_username + self.map_nickname = map_nickname - def valid_user(self, username, password): + + def connect(self): try: - conn = ldap.initialize("ldap://{0}:{1}".format(self.server, self.port), bytes_mode=False) - conn.simple_bind_s(self.admin_dn, self.password) - search_filter = f'({self.rules}={username})' - ldap_result_id = conn.search(self.base_dn, ldap.SCOPE_SUBTREE, search_filter, None) - result_type, result_data = conn.result(ldap_result_id, 0) - if result_type == ldap.RES_SEARCH_ENTRY: - conn.simple_bind_s(result_data[0][0], password) - return True, None - else: - return False, None + conn = ldap.initialize(f'{self.server}', bytes_mode=False) + conn.set_option(ldap.OPT_TIMEOUT, 3) + conn.set_option(ldap.OPT_NETWORK_TIMEOUT, 3) + conn.simple_bind_s(self.admin_dn, self.admin_password) + return True, conn except Exception as error: - args = error.args - return False, args[0].get('desc', '未知错误') if args else '%s' % error + return False, error.args[0].get('desc') + + + def all_user(self): + status, conn = self.connect() + if status: + try: + # user_filter = '(cn=*)' + # map = ['cn', 'sn'] + # user_map = list(self.user_map.values()) + user_filter = "({}=*)".format(self.user_filter.split('=')[0][1:]) + user_map = [self.map_username, self.map_nickname] + ldap_result = conn.search_s(self.user_ou, ldap.SCOPE_SUBTREE, user_filter, user_map) + ldap_users = [] + for dn,entry in ldap_result: + if dn == self.user_ou: + continue + tmp_user = {} + for k,v in entry.items(): + tmp_user.update({k: v[0].decode()}) + + ldap_users.append(tmp_user) + return True, ldap_users + + except Exception as error: + return False, error.args[0].get('desc') + else: + return False, conn + + def verify_user(self, username, password): + status, conn = self.connect() + if status: + try: + user_filter = f'({self.map_username}={username})' + ldap_result_id = conn.search(self.user_ou, ldap.SCOPE_SUBTREE, user_filter, [self.map_username]) + _, result_data = conn.result(ldap_result_id, 0) + if result_data: + conn.simple_bind_s(result_data[0][0], password) + return True, True + else: + return False, '账户未找到' + except Exception as error: + return False, error.args[0].get('desc') + else: + return False, conn + diff --git a/spug_web/src/pages/system/setting/LDAPSetting.js b/spug_web/src/pages/system/setting/LDAPSetting.js index a25c46f..a17e6f3 100644 --- a/spug_web/src/pages/system/setting/LDAPSetting.js +++ b/spug_web/src/pages/system/setting/LDAPSetting.js @@ -5,8 +5,9 @@ */ import React, { useState } from 'react'; import styles from './index.module.css'; -import { Form, Button, Input, Space, message } from 'antd'; +import { Form, Button, Input, Space, message, Modal } from 'antd'; import { http } from 'libs'; +import LdapImport from './LdapImport'; import { observer } from 'mobx-react' import store from './store'; @@ -17,7 +18,7 @@ export default observer(function () { function handleSubmit() { store.loading = true; const formData = form.getFieldsValue(); - http.post('/api/setting/', {data: [{key: 'ldap_service', value: formData}]}) + http.post('/api/setting/', { data: [{ key: 'ldap_service', value: formData }] }) .then(() => { message.success('保存成功'); store.fetchSettings() @@ -28,39 +29,78 @@ export default observer(function () { function ldapTest() { setLoading(true); const formData = form.getFieldsValue(); - http.post('/api/setting/ldap_test/', formData).then(() => { - message.success('LDAP服务连接成功') + http.post('/api/setting/ldap_test/', formData).then((res) => { + message.success("成功匹配" + res.length + "个用户") }).finally(() => setLoading(false)) } + + function ldapLogin(info) { + let ldadUser; + let ldadPwd; + Modal.confirm({ + title: 'LDAP用户测试登录', + content: