diff --git a/conf/local_settings.py b/conf/local_settings.py index 6b4ac5e..0f9445c 100644 --- a/conf/local_settings.py +++ b/conf/local_settings.py @@ -18,13 +18,16 @@ LDAP_LOGIN_USER = r'修改成自己的' LDAP_LOGIN_USER_PWD = r'修改为自己的' # BASE DN,账号的查找DN路径,例如:'DC=abc,DC=com',可以指定到OU之下,例如:'OU=RD,DC=abc,DC=com'。 +# BASE_DN限制得越细,搜索用户的目录也就越小,一般情况下可以通过SEARCH_FILTER来过滤 BASE_DN = r'修改成自己的' -# ldap的search_filter,如果需要修改,请保持用户账号部分为 点位符{} (代码中通过占位符引入账号) -# 例如,AD的用户账号属性是sAMAccountName,那么匹配的账号请配置成sAMAccountName={} -# LDAP中用户账号属性可能是uuid,那么匹配的账号请配置成uuid={} -# 默认配置是AD环境的 -SEARCH_FILTER = r'(&(objectclass=user)(sAMAccountName={}))' +# ldap的search_filter,如果需要修改,请保持用户账号部分为 点位符{0} (代码中通过占位符引入账号) +# 例如,AD的用户账号属性是sAMAccountName,那么匹配的账号请配置成sAMAccountName={0} +# LDAP中用户账号属性可能是uuid,那么匹配的账号请配置成uuid={0} +# 如果想限制用户在哪个组的才能使用,可以写成这样: +# r'(&(objectClass=user)(memberof=CN=mis,OU=Groups,OU=OnTheJob,DC=abc,DC=com)(sAMAccountName={0}))', memberof 是需要匹配的组 +# 默认配置是AD环境的,查询语句可以自行使用Apache Directory Studio测试后再配置 +SEARCH_FILTER = r'(&(objectclass=user)(sAMAccountName={0}))' # 是否启用SSL, # 注意:AD中必须使用SSL才能修改密码(这里被坑了N久...),自行部署下AD的证书服务,并颁发CA证书,重启服务器生效。具体教程百度一下,有很多。 diff --git a/pwdselfservice/settings.py b/pwdselfservice/settings.py index e97e8ea..f71aee6 100644 --- a/pwdselfservice/settings.py +++ b/pwdselfservice/settings.py @@ -30,11 +30,10 @@ LOGGING = { 'disable_existing_loggers': False, 'formatters': { 'verbose': { - 'format': '%(asctime)s %(levelname)s %(pathname)s %(module)s.%(funcName)s %(lineno)d: %(message)s' - # 日志格式 + 'format': '%(asctime)s %(levelname)s %(message)s' }, 'simple': { - 'format': '%(asctime)s %(levelname)s %(pathname)s %(module)s.%(funcName)s %(lineno)d: %(message)s' + 'format': '%(asctime)s %(levelname)s %(message)s' }, }, 'filters': { @@ -54,7 +53,7 @@ LOGGING = { 'level': 'DEBUG', 'class': 'logging.FileHandler', # 日志保存文件 - 'filename': '%s/log.log' % LOG_PATH, + 'filename': '%s/all.log' % LOG_PATH, # 日志格式,与上边的设置对应选择 'formatter': 'verbose' } diff --git a/readme.md b/readme.md index c5293ca..87bc46b 100644 --- a/readme.md +++ b/readme.md @@ -37,6 +37,13 @@ AD必须使用SSL才能修改密码(这里被坑了N久...) ### 2023/01/15 -- 更新: + 兼容PC与移动端的显示(使用Layui) + 修复一些BUG ++ 移除auto-install.sh中的redis部署步骤 ++ 抽出应用中的授权验证跳转的代码,单独做成一个auth页面,可实现选择首页是进入修改密码,还是自动跳转重置页面。 ++ 调整部分文件说明 ++ 添加日志装饰器记录请求日志 ++ 优化ad_ops中异常的处理 + +**如果想在点击应用之后自动跳转到重置页面,请将回调接口配置成YOURDOMAIN.com/auth** ## 线上环境需要的基础环境: @@ -118,7 +125,6 @@ chmod +x auto-install.sh #### 以上配置修改完成之后,则可以通过配置的域名直接访问。 - # 手动部署: #### 自行安装完python3之后,使用python3目录下的pip3进行安装依赖: #### 我自行安装的Python路径为/usr/local/python3 diff --git a/resetpwd/views.py b/resetpwd/views.py index 0435e82..6da138c 100644 --- a/resetpwd/views.py +++ b/resetpwd/views.py @@ -3,12 +3,12 @@ import logging import os from django.shortcuts import render from utils.ad_ops import AdOps -from ldap3.core.exceptions import LDAPException import urllib.parse as url_encode from utils.format_username import format2username, get_user_is_active, get_email_from_userinfo from .form import CheckForm from .utils import code_2_user_detail, ops_account from django.conf import settings +from utils.logger_filter import decorator_request_logger APP_ENV = os.getenv('APP_ENV') if APP_ENV == 'dev': @@ -43,6 +43,7 @@ scan_params = PARAMS() _ops = scan_params.ops +@decorator_request_logger(logger) def auth(request): home_url = '%s://%s' % (request.scheme, HOME_URL) corp_id = scan_params.corp_id @@ -69,6 +70,7 @@ def auth(request): logger.error('[异常] 请求方法:%s,请求路径%s' % (request.method, request.path)) +@decorator_request_logger(logger) def index(request): """ 用户自行修改密码/首页 @@ -79,10 +81,8 @@ def index(request): if request.method == 'GET': return render(request, 'index.html', locals()) - else: - logger.error('[异常] 请求方法:%s,请求路径%s' % (request.method, request.path)) - if request.method == 'POST': + elif request.method == 'POST': # 对前端提交的数据进行二次验证,防止恶意提交简单密码或篡改账号。 check_form = CheckForm(request.POST) if check_form.is_valid(): @@ -127,6 +127,7 @@ def index(request): return render(request, msg_template, context) +@decorator_request_logger(logger) def reset_password(request): """ 钉钉扫码并验证信息通过之后,在重置密码页面将用户账号进行绑定 @@ -242,6 +243,7 @@ def reset_password(request): return render(request, msg_template, context) +@decorator_request_logger(logger) def unlock_account(request): """ 解锁账号 @@ -292,6 +294,7 @@ def unlock_account(request): return render(request, msg_template, context) +@decorator_request_logger(logger) def messages(request): _msg = request.GET.get('msg') button_click = request.GET.get('button_click') diff --git a/utils/ad_ops.py b/utils/ad_ops.py index 8c8a52e..316a5eb 100644 --- a/utils/ad_ops.py +++ b/utils/ad_ops.py @@ -136,6 +136,8 @@ class AdOps(object): self.__conn() return True, self.conn.search(BASE_DN, SEARCH_FILTER.format(username), attributes=['sAMAccountName']) + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) @@ -149,6 +151,8 @@ class AdOps(object): self.__conn() self.conn.search(BASE_DN, SEARCH_FILTER.format(username), attributes=['name']) return True, self.conn.entries[0]['name'] + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) @@ -163,6 +167,8 @@ class AdOps(object): self.conn.search(BASE_DN, SEARCH_FILTER.format(username), attributes=['distinguishedName']) return True, str(self.conn.entries[0]['distinguishedName']) + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) @@ -177,6 +183,8 @@ class AdOps(object): self.conn.search(BASE_DN, SEARCH_FILTER.format(username), attributes=['userAccountControl']) return True, self.conn.entries[0]['userAccountControl'] + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) @@ -190,6 +198,8 @@ class AdOps(object): if _status: try: return True, self.conn.extend.microsoft.unlock_account(user='%s' % user_dn) + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) else: @@ -247,5 +257,7 @@ class AdOps(object): return True, 'unlocked' else: return False, locked_status + except IndexError: + return False, "AdOps Exception: Connect.search未能检索到任何信息,当前账号可能被排除在之外,请联系管理员处理。" except Exception as e: return False, "AdOps Exception: {}".format(e) diff --git a/utils/logger_filter.py b/utils/logger_filter.py new file mode 100644 index 0000000..10ae969 --- /dev/null +++ b/utils/logger_filter.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from functools import wraps +from traceback import format_exc + + +def decorator_request_logger(logger): + def decorator(func): + @wraps(func) + def wrapper(request, *args, **kwargs): + try: + rsp = func(request, *args, **kwargs) + logger.info( + f'Request Arguments: {args} {kwargs}') + # logger.info( + # f'Request: {request.META["REMOTE_ADDR"]} {request.method} "{request.META["PATH_INFO"]}' + # f'{request.META["QUERY_STRING"]} {request.META["SERVER_PROTOCOL"]}" {rsp.status_code} {rsp.content}') + logger.info(rsp) + return rsp + except Exception as e: + logger.error(format_exc()) + raise e + + return wrapper + + return decorator + + +def decorator_default_logger(logger): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + logger.info(f'{args}, {kwargs}') + try: + return func(*args, **kwargs) + except Exception as e: + logger.error(format_exc()) + raise e + + return wrapper + + return decorator