Skip to content

Commit

Permalink
Merge pull request jly8866#50 from woshihaoren/master
Browse files Browse the repository at this point in the history
ldap, login tips , login failed record and send mail
  • Loading branch information
Mr.July authored Jan 24, 2018
2 parents 44787fa + cb3e9c4 commit 6e2bcef
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 78 deletions.
143 changes: 79 additions & 64 deletions archer/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import ldap
# from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType
import pymysql
pymysql.install_as_MySQLdb()

Expand Down Expand Up @@ -131,72 +128,90 @@
INCEPTION_REMOTE_BACKUP_USER='inception'
INCEPTION_REMOTE_BACKUP_PASSWORD='inception'

# 账户登录失败锁定时间(秒)
LOCK_TIME_THRESHOLD = 300
# 账户登录失败 几次 锁账户
LOCK_CNT_THRESHOLD = 5

# LDAP
ENABLE_LDAP = False
# if use self signed certificate, Remove AUTH_LDAP_GLOBAL_OPTIONS annotations
#AUTH_LDAP_GLOBAL_OPTIONS={
# ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_NEVER
#}
AUTH_LDAP_BIND_DN = "cn=ro,dc=xxx,dc=cn"
AUTH_LDAP_BIND_PASSWORD = "xxxxxx"
AUTH_LDAP_SERVER_URI = "ldap://auth.xxx.com"
AUTH_LDAP_BASEDN = "ou=users,dc=xxx,dc=cn"
AUTH_LDAP_USER_DN_TEMPLATE = "cn=%(user)s,ou=users,dc=xxx,dc=cn"
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=xxx,dc=cn",
ldap.SCOPE_SUBTREE, "(objectClass=groupOfUniqueNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()
AUTH_LDAP_USER_ATTRLIST = ["cn", "sn", "mail"]
AUTH_LDAP_USER_ATTR_MAP = {
"username": "cn",
"display": "sn",
"email": "mail"
}
if ENABLE_LDAP:
import ldap
# from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
from django_auth_ldap.config import LDAPSearch, GroupOfUniqueNamesType
# if use self signed certificate, Remove AUTH_LDAP_GLOBAL_OPTIONS annotations
#AUTH_LDAP_GLOBAL_OPTIONS={
# ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_NEVER
#}
AUTH_LDAP_BIND_DN = "cn=ro,dc=xxx,dc=cn"
AUTH_LDAP_BIND_PASSWORD = "xxxxxx"
AUTH_LDAP_SERVER_URI = "ldap://auth.xxx.com"
AUTH_LDAP_BASEDN = "ou=users,dc=xxx,dc=cn"
AUTH_LDAP_USER_DN_TEMPLATE = "cn=%(user)s,ou=users,dc=xxx,dc=cn"
AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=xxx,dc=cn",
ldap.SCOPE_SUBTREE, "(objectClass=groupOfUniqueNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType()
AUTH_LDAP_USER_ATTRLIST = ["cn", "sn", "mail"]
AUTH_LDAP_USER_ATTR_MAP = {
"username": "cn",
"display": "sn",
"email": "mail"
}

# AUTH_LDAP_MIRROR_GROUPS = True # 直接把ldap的组复制到django一份,和AUTH_LDAP_FIND_GROUP_PERMS互斥.用户每次登录会根据ldap来更新数据库的组关系
# AUTH_LDAP_FIND_GROUP_PERMS = True # django从ldap的组权限中获取权限,这种方式,django自身不创建组,每次请求都调用ldap
# AUTH_LDAP_CACHE_GROUPS = True # 如打开FIND_GROUP_PERMS后,此配置生效,对组关系进行缓存,不用每次请求都调用ldap
# AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600 # 缓存时间
# AUTH_LDAP_MIRROR_GROUPS = True # 直接把ldap的组复制到django一份,和AUTH_LDAP_FIND_GROUP_PERMS互斥.用户每次登录会根据ldap来更新数据库的组关系
# AUTH_LDAP_FIND_GROUP_PERMS = True # django从ldap的组权限中获取权限,这种方式,django自身不创建组,每次请求都调用ldap
# AUTH_LDAP_CACHE_GROUPS = True # 如打开FIND_GROUP_PERMS后,此配置生效,对组关系进行缓存,不用每次请求都调用ldap
# AUTH_LDAP_GROUP_CACHE_TIMEOUT = 600 # 缓存时间

#开启以下配置注释,可以帮助调试ldap集成
#LDAP_LOGS = '/tmp/ldap.log'
#stamdard_format = '[%(asctime)s][%(threadName)s:%(thread)d]' + \
# '[task_id:%(name)s][%(filename)s:%(lineno)d] ' + \
# '[%(levelname)s]- %(message)s'
#LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'formatters': {
# 'standard': { # 详细
# 'format': stamdard_format
# },
# },
# 'handlers': {
# 'default': {
# 'level': 'DEBUG',
# 'class': 'logging.handlers.RotatingFileHandler',
# 'filename': LDAP_LOGS,
# 'maxBytes': 1024 * 1024 * 100, # 5 MB
# 'backupCount': 5,
# 'formatter': 'standard',
# },
# 'console': {
# 'level': 'DEBUG',
# 'class': 'logging.StreamHandler',
# }
# },
# 'loggers': {
# '': { # default日志,存放于log中
# 'handlers': ['default'],
# 'level': 'DEBUG',
# },
# 'django_auth_ldap': { # django_auth_ldap模块相关日志打印到console
# 'handlers': ['default'],
# 'level': 'DEBUG',
# 'propagate': True, # 选择关闭继承,不然这个logger继承自默认,日志就会被记录2次了(''一次,自己一次)
# }
# }
#}
LDAP_LOGS = '/tmp/ldap.log'
DEFAULT_LOGS = '/tmp/default.log'
stamdard_format = '[%(asctime)s][%(threadName)s:%(thread)d]' + \
'[task_id:%(name)s][%(filename)s:%(lineno)d] ' + \
'[%(levelname)s]- %(message)s'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': { # 详细
'format': stamdard_format
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': DEFAULT_LOGS,
'maxBytes': 1024 * 1024 * 100, # 5 MB
'backupCount': 5,
'formatter': 'standard',
},
'ldap': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LDAP_LOGS,
'maxBytes': 1024 * 1024 * 100, # 5 MB
'backupCount': 5,
'formatter': 'standard',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
}
},
'loggers': {
'default': { # default日志,存放于log中
'handlers': ['default'],
'level': 'DEBUG',
},
'django_auth_ldap': { # django_auth_ldap模块相关日志打印到console
'handlers': ['ldap'],
'level': 'DEBUG',
'propagate': True, # 选择关闭继承,不然这个logger继承自默认,日志就会被记录2次了(''一次,自己一次)
}
}
}

#是否开启邮件提醒功能:发起SQL上线后会发送邮件提醒审核人审核,执行完毕会发送给DBA. on是开,off是关,配置为其他值均会被archer认为不开启邮件功能
MAIL_ON_OFF='on'
Expand Down
43 changes: 29 additions & 14 deletions sql/views_ajax.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,49 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth.hashers import check_password
from django_auth_ldap.backend import LDAPBackend
if settings.ENABLE_LDAP:
from django_auth_ldap.backend import LDAPBackend

from .dao import Dao
from .const import Const
from .inception import InceptionDao
from .aes_decryptor import Prpcrypt
from .models import users, master_config, workflow
from sql.sendmail import MailSender
import logging

logger = logging.getLogger('default')
mailSender = MailSender()
dao = Dao()
inceptionDao = InceptionDao()
prpCryptor = Prpcrypt()
login_failure_counter = {} #登录失败锁定计数器,给loginAuthenticate用的
sqlSHA1_cache = {} #存储SQL文本与SHA1值的对应关系,尽量减少与数据库的交互次数,提高效率。格式: {工单ID1:{SQL内容1:sqlSHA1值1, SQL内容2:sqlSHA1值2},}

def log_mail_record(login_failed_message):
mail_title = 'login inception'
logger.warning(login_failed_message)
mailSender.sendEmail(mail_title, login_failed_message, getattr(settings, 'MAIL_REVIEW_DBA_ADDR'))

#ajax接口,登录页面调用,用来验证用户名密码
@csrf_exempt
def loginAuthenticate(username, password):
"""登录认证,包含一个登录失败计数器,5分钟内连续失败5次的账号,会被锁定5分钟"""
lockCntThreshold = 5
lockTimeThreshold = 300
lockCntThreshold = settings.LOCK_CNT_THRESHOLD
lockTimeThreshold = settings.LOCK_TIME_THRESHOLD

#服务端二次验证参数
strUsername = username
strPassword = password

if strUsername == "" or strPassword == "" or strUsername is None or strPassword is None:
result = {'status':2, 'msg':'登录用户名或密码为空,请重新输入!', 'data':''}
elif strUsername in login_failure_counter and login_failure_counter[strUsername]["cnt"] >= lockCntThreshold and (datetime.datetime.now() - login_failure_counter[strUsername]["last_failure_time"]).seconds <= lockTimeThreshold:
log_mail_record('user:{},login failed, account locking...'.format(strUsername))
result = {'status':3, 'msg':'登录失败超过5次,该账号已被锁定5分钟!', 'data':''}
else:
correct_users = users.objects.filter(username=strUsername)
if len(correct_users) == 0:
result = {'status':4, 'msg':'该用户不存在!', 'data':''}
elif not correct_users[0].is_active:
result = {'status': 5, 'msg': 'user is not active', 'data': ''}
elif len(correct_users) == 1 and check_password(strPassword, correct_users[0].password) == True:
if len(correct_users) == 1 and correct_users[0].is_active and check_password(strPassword, correct_users[0].password) == True:
#调用了django内置函数check_password函数检测输入的密码是否与django默认的PBKDF2算法相匹配
if strUsername in login_failure_counter:
#如果登录失败计数器中存在该用户名,则清除之
Expand All @@ -63,6 +71,7 @@ def loginAuthenticate(username, password):
#上一次登录失败时间早于5分钟前,则重新计数。以达到超过5分钟自动解锁的目的。
login_failure_counter[strUsername]["cnt"] = 1
login_failure_counter[strUsername]["last_failure_time"] = datetime.datetime.now()
log_mail_record('user:{},login failed, fail count:{}'.format(strUsername,login_failure_counter[strUsername]["cnt"]))
result = {'status':1, 'msg':'用户名或密码错误,请重新输入!', 'data':''}
return result

Expand All @@ -77,15 +86,21 @@ def authenticateEntry(request):
strUsername = request.POST['username']
strPassword = request.POST['password']

lockCntThreshold = settings.LOCK_CNT_THRESHOLD
lockTimeThreshold = settings.LOCK_TIME_THRESHOLD

if settings.ENABLE_LDAP:
ldap = LDAPBackend()
user = ldap.authenticate(username=strUsername, password=strPassword)
if user:
if user.is_active:
request.session['login_username'] = strUsername
result = {'status': 0, 'msg': 'ok', 'data': ''}
else:
result = {'status': 5, 'msg': 'user is not active', 'data': ''}
if strUsername in login_failure_counter and login_failure_counter[strUsername]["cnt"] >= lockCntThreshold and (
datetime.datetime.now() - login_failure_counter[strUsername][
"last_failure_time"]).seconds <= lockTimeThreshold:
log_mail_record('user:{},login failed, account locking...'.format(strUsername))
result = {'status': 3, 'msg': '登录失败超过5次,该账号已被锁定5分钟!', 'data': ''}
return HttpResponse(json.dumps(result), content_type='application/json')
if user and user.is_active:
request.session['login_username'] = strUsername
result = {'status': 0, 'msg': 'ok', 'data': ''}
return HttpResponse(json.dumps(result), content_type='application/json')

result = loginAuthenticate(strUsername, strPassword)
Expand Down

0 comments on commit 6e2bcef

Please sign in to comment.