2022-01-26 21:10:09 +08:00
|
|
|
|
import hashlib
|
2022-11-03 21:59:12 +08:00
|
|
|
|
import string
|
2023-01-29 10:57:58 +08:00
|
|
|
|
from datetime import datetime, timedelta
|
2024-06-04 13:58:47 +08:00
|
|
|
|
import unicodedata
|
|
|
|
|
|
2025-05-30 10:46:21 +08:00
|
|
|
|
# 使用固定盐值增强密码安全性
|
|
|
|
|
PASSWORD_SALT = "wordfreq_salt_2023"
|
2022-01-26 21:10:09 +08:00
|
|
|
|
|
2023-04-18 21:50:54 +08:00
|
|
|
|
def md5(s):
|
|
|
|
|
'''
|
|
|
|
|
MD5摘要
|
|
|
|
|
:param str: 字符串
|
|
|
|
|
:return: 经MD5以后的字符串
|
|
|
|
|
'''
|
|
|
|
|
h = hashlib.md5(s.encode(encoding='utf-8'))
|
|
|
|
|
return h.hexdigest()
|
|
|
|
|
|
2025-05-30 10:46:21 +08:00
|
|
|
|
# 延迟导入 model.user,避免循环导入
|
2022-01-26 21:10:09 +08:00
|
|
|
|
path_prefix = '/var/www/wordfreq/wordfreq/'
|
|
|
|
|
path_prefix = './' # comment this line in deployment
|
|
|
|
|
|
|
|
|
|
def verify_user(username, password):
|
2025-05-30 10:46:21 +08:00
|
|
|
|
'''验证用户凭据'''
|
|
|
|
|
from model.user import get_user_by_username # 延迟导入
|
2023-04-18 21:50:54 +08:00
|
|
|
|
user = get_user_by_username(username)
|
2025-05-30 10:46:21 +08:00
|
|
|
|
if user is None:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 使用带盐值的加密
|
|
|
|
|
encrypted_password = md5(PASSWORD_SALT + username + password)
|
|
|
|
|
return user.password == encrypted_password
|
2022-01-26 21:10:09 +08:00
|
|
|
|
|
|
|
|
|
def add_user(username, password):
|
|
|
|
|
start_date = datetime.now().strftime('%Y%m%d')
|
2024-06-04 13:58:47 +08:00
|
|
|
|
expiry_date = (datetime.now() + timedelta(days=30)).strftime('%Y%m%d') # will expire after 30 days
|
2025-05-30 10:46:21 +08:00
|
|
|
|
|
|
|
|
|
# 使用带盐值的加密
|
|
|
|
|
encrypted_password = md5(PASSWORD_SALT + username + password)
|
|
|
|
|
|
|
|
|
|
from model.user import insert_user # 延迟导入
|
|
|
|
|
insert_user(
|
|
|
|
|
username=username,
|
|
|
|
|
password=encrypted_password,
|
|
|
|
|
start_date=start_date,
|
|
|
|
|
expiry_date=expiry_date
|
|
|
|
|
)
|
2022-01-26 21:10:09 +08:00
|
|
|
|
|
|
|
|
|
def check_username_availability(username):
|
2025-05-30 10:46:21 +08:00
|
|
|
|
# 延迟导入,避免循环导入
|
|
|
|
|
from model.user import get_user_by_username
|
|
|
|
|
return get_user_by_username(username) is None
|
2022-01-26 21:10:09 +08:00
|
|
|
|
|
2022-01-27 12:28:41 +08:00
|
|
|
|
def change_password(username, old_password, new_password):
|
2022-01-26 21:10:09 +08:00
|
|
|
|
'''
|
|
|
|
|
修改密码
|
|
|
|
|
:param username: 用户名
|
2022-01-27 12:28:41 +08:00
|
|
|
|
:param old_password: 旧的密码
|
|
|
|
|
:param new_password: 新密码
|
2022-01-26 21:10:09 +08:00
|
|
|
|
:return: 修改成功:True 否则:False
|
|
|
|
|
'''
|
2022-01-27 12:28:41 +08:00
|
|
|
|
if not verify_user(username, old_password): # 旧密码错误
|
2024-09-06 08:54:11 +08:00
|
|
|
|
return {'error':'Old password is wrong.', 'username':username}
|
2022-01-26 21:10:09 +08:00
|
|
|
|
# 将用户名和密码一起加密,以免暴露不同用户的相同密码
|
2024-09-06 08:54:11 +08:00
|
|
|
|
if new_password == old_password: #新旧密码一致
|
|
|
|
|
return {'error':'New password cannot be the same as the old password.', 'username':username}
|
2025-05-30 10:46:21 +08:00
|
|
|
|
|
|
|
|
|
# 加密新密码(修复了原代码未加密的安全漏洞)
|
|
|
|
|
encrypted_new_password = md5(PASSWORD_SALT + username + new_password)
|
|
|
|
|
# 延迟导入,避免循环导入
|
|
|
|
|
from model.user import update_password_by_username
|
|
|
|
|
update_password_by_username(username, encrypted_new_password)
|
|
|
|
|
return {'success': 'Password changed', 'username':username}
|
2022-01-26 21:10:09 +08:00
|
|
|
|
|
|
|
|
|
def get_expiry_date(username):
|
2025-05-30 10:46:21 +08:00
|
|
|
|
# 延迟导入,避免循环导入
|
|
|
|
|
from model.user import get_user_by_username
|
2023-04-18 21:50:54 +08:00
|
|
|
|
user = get_user_by_username(username)
|
2025-05-30 10:46:21 +08:00
|
|
|
|
return user.expiry_date if user else '20191024' # 默认过期日期
|
2024-06-04 13:58:47 +08:00
|
|
|
|
|
2022-11-03 21:59:12 +08:00
|
|
|
|
class UserName:
|
|
|
|
|
def __init__(self, username):
|
|
|
|
|
self.username = username
|
|
|
|
|
|
2024-06-04 13:58:47 +08:00
|
|
|
|
def contains_chinese(self):
|
|
|
|
|
for char in self.username:
|
|
|
|
|
# Check if the character is in the CJK (Chinese, Japanese, Korean) Unicode block
|
|
|
|
|
if unicodedata.name(char).startswith('CJK UNIFIED IDEOGRAPH'):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
2025-05-30 10:46:21 +08:00
|
|
|
|
|
2022-11-03 21:59:12 +08:00
|
|
|
|
def validate(self):
|
2022-11-03 22:02:32 +08:00
|
|
|
|
if len(self.username) > 20:
|
2022-11-03 21:59:12 +08:00
|
|
|
|
return f'{self.username} is too long. The user name cannot exceed 20 characters.'
|
2024-06-04 13:58:47 +08:00
|
|
|
|
if self.username.startswith('.'): # a user name must not start with a dot
|
2022-11-03 21:59:12 +08:00
|
|
|
|
return 'Period (.) is not allowed as the first letter in the user name.'
|
2024-06-04 13:58:47 +08:00
|
|
|
|
if ' ' in self.username: # a user name must not include a whitespace
|
2022-11-03 21:59:12 +08:00
|
|
|
|
return 'Whitespace is not allowed in the user name.'
|
2024-06-04 13:58:47 +08:00
|
|
|
|
for c in self.username: # a user name must not include special characters, except non-leading periods or underscores
|
2023-03-21 11:44:05 +08:00
|
|
|
|
if c in string.punctuation and c != '.' and c != '_':
|
2022-11-03 21:59:12 +08:00
|
|
|
|
return f'{c} is not allowed in the user name.'
|
2024-06-04 13:58:47 +08:00
|
|
|
|
if self.username in ['signup', 'login', 'logout', 'reset', 'mark', 'back', 'unfamiliar', 'familiar', 'del',
|
|
|
|
|
'admin']:
|
2022-11-10 19:03:59 +08:00
|
|
|
|
return 'You used a restricted word as your user name. Please come up with a better one.'
|
2024-06-04 13:58:47 +08:00
|
|
|
|
if self.contains_chinese():
|
|
|
|
|
return 'Chinese characters are not allowed in the user name.'
|
|
|
|
|
return 'OK'
|
2022-11-10 19:03:59 +08:00
|
|
|
|
|
2024-06-04 13:58:47 +08:00
|
|
|
|
class Password:
|
2025-05-30 10:46:21 +08:00
|
|
|
|
'''密码验证类'''
|
2024-06-04 13:58:47 +08:00
|
|
|
|
def __init__(self, password):
|
|
|
|
|
self.password = password
|
2025-05-30 10:46:21 +08:00
|
|
|
|
|
|
|
|
|
def contains_cjk(self):
|
2024-06-04 13:58:47 +08:00
|
|
|
|
for char in self.password:
|
|
|
|
|
# Check if the character is in the CJK (Chinese, Japanese, Korean) Unicode block
|
|
|
|
|
if unicodedata.name(char).startswith('CJK UNIFIED IDEOGRAPH'):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
2025-05-30 10:46:21 +08:00
|
|
|
|
|
2024-06-04 13:58:47 +08:00
|
|
|
|
def validate(self):
|
2025-05-30 10:46:21 +08:00
|
|
|
|
'''验证密码有效性'''
|
|
|
|
|
if len(self.password) < 6: # 提高最小长度要求
|
2024-06-04 13:58:47 +08:00
|
|
|
|
return 'Password must be at least 4 characters long.'
|
|
|
|
|
if ' ' in self.password:
|
|
|
|
|
return 'Password cannot contain spaces.'
|
2025-05-30 10:46:21 +08:00
|
|
|
|
if self.contains_cjk():
|
2024-06-04 13:58:47 +08:00
|
|
|
|
return 'Chinese characters are not allowed in the password.'
|
2025-05-30 10:46:21 +08:00
|
|
|
|
# 添加额外的安全检查
|
|
|
|
|
if not any(char.isdigit() for char in self.password):
|
|
|
|
|
return '密码应包含至少一个数字'
|
2022-11-03 21:59:12 +08:00
|
|
|
|
return 'OK'
|
|
|
|
|
|
|
|
|
|
class WarningMessage:
|
2025-05-30 10:46:21 +08:00
|
|
|
|
'''验证消息生成类'''
|
|
|
|
|
def __init__(self, input_str, input_type='username'):
|
|
|
|
|
self.input_str = input_str
|
|
|
|
|
self.input_type = input_type
|
|
|
|
|
|
2022-11-03 21:59:12 +08:00
|
|
|
|
def __str__(self):
|
2025-05-30 10:46:21 +08:00
|
|
|
|
if self.input_type == 'username':
|
|
|
|
|
return UserName(self.input_str).validate()
|
|
|
|
|
if self.input_type == 'password':
|
|
|
|
|
return Password(self.input_str).validate()
|
|
|
|
|
return '未知验证类型'
|