mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 08:23:20 +00:00
tiny work
This commit is contained in:
parent
edb32eaf7b
commit
a324d55364
@ -24,22 +24,22 @@ class UserManager(models.Manager):
|
|||||||
|
|
||||||
|
|
||||||
class User(AbstractBaseUser):
|
class User(AbstractBaseUser):
|
||||||
username = models.CharField(max_length=30, unique=True)
|
username = models.CharField(max_length=32, unique=True)
|
||||||
email = models.EmailField(max_length=254, null=True)
|
email = models.EmailField(max_length=64, null=True)
|
||||||
create_time = models.DateTimeField(auto_now_add=True, null=True)
|
create_time = models.DateTimeField(auto_now_add=True, null=True)
|
||||||
# One of UserType
|
# One of UserType
|
||||||
admin_type = models.CharField(max_length=24, default=AdminType.REGULAR_USER)
|
admin_type = models.CharField(max_length=32, default=AdminType.REGULAR_USER)
|
||||||
problem_permission = models.CharField(max_length=24, default=ProblemPermission.NONE)
|
problem_permission = models.CharField(max_length=32, default=ProblemPermission.NONE)
|
||||||
reset_password_token = models.CharField(max_length=40, null=True)
|
reset_password_token = models.CharField(max_length=32, null=True)
|
||||||
reset_password_token_expire_time = models.DateTimeField(null=True)
|
reset_password_token_expire_time = models.DateTimeField(null=True)
|
||||||
# SSO auth token
|
# SSO auth token
|
||||||
auth_token = models.CharField(max_length=40, null=True)
|
auth_token = models.CharField(max_length=32, null=True)
|
||||||
two_factor_auth = models.BooleanField(default=False)
|
two_factor_auth = models.BooleanField(default=False)
|
||||||
tfa_token = models.CharField(max_length=40, null=True)
|
tfa_token = models.CharField(max_length=32, null=True)
|
||||||
session_keys = JSONField(default=[])
|
session_keys = JSONField(default=[])
|
||||||
# open api key
|
# open api key
|
||||||
open_api = models.BooleanField(default=False)
|
open_api = models.BooleanField(default=False)
|
||||||
open_api_appkey = models.CharField(max_length=35, null=True)
|
open_api_appkey = models.CharField(max_length=32, null=True)
|
||||||
is_disabled = models.BooleanField(default=False)
|
is_disabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
USERNAME_FIELD = "username"
|
USERNAME_FIELD = "username"
|
||||||
@ -63,10 +63,6 @@ class User(AbstractBaseUser):
|
|||||||
db_table = "user"
|
db_table = "user"
|
||||||
|
|
||||||
|
|
||||||
def _default_avatar():
|
|
||||||
return f"/{settings.IMAGE_UPLOAD_DIR}/default.png"
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
user = models.OneToOneField(User)
|
user = models.OneToOneField(User)
|
||||||
# Store user problem solution status with json string format
|
# Store user problem solution status with json string format
|
||||||
@ -75,14 +71,13 @@ class UserProfile(models.Model):
|
|||||||
# {problems: {1: 33}, contest_problems: {1: 44}, record problem_id and score
|
# {problems: {1: 33}, contest_problems: {1: 44}, record problem_id and score
|
||||||
oi_problems_status = JSONField(default={})
|
oi_problems_status = JSONField(default={})
|
||||||
|
|
||||||
real_name = models.CharField(max_length=30, blank=True, null=True)
|
real_name = models.CharField(max_length=32, blank=True, null=True)
|
||||||
avatar = models.CharField(max_length=50, default=_default_avatar())
|
avatar = models.CharField(max_length=256, default=f"{settings.IMAGE_UPLOAD_DIR}/default.png")
|
||||||
blog = models.URLField(blank=True, null=True)
|
blog = models.URLField(blank=True, null=True)
|
||||||
mood = models.CharField(max_length=200, blank=True, null=True)
|
mood = models.CharField(max_length=256, blank=True, null=True)
|
||||||
github = models.CharField(max_length=50, blank=True, null=True)
|
github = models.CharField(max_length=64, blank=True, null=True)
|
||||||
school = models.CharField(max_length=200, blank=True, null=True)
|
school = models.CharField(max_length=64, blank=True, null=True)
|
||||||
major = models.CharField(max_length=200, blank=True, null=True)
|
major = models.CharField(max_length=64, blank=True, null=True)
|
||||||
language = models.CharField(max_length=32, blank=True, null=True)
|
|
||||||
# for ACM
|
# for ACM
|
||||||
accepted_number = models.IntegerField(default=0)
|
accepted_number = models.IntegerField(default=0)
|
||||||
# for OI
|
# for OI
|
||||||
|
@ -6,27 +6,27 @@ from .models import AdminType, ProblemPermission, User, UserProfile
|
|||||||
|
|
||||||
|
|
||||||
class UserLoginSerializer(serializers.Serializer):
|
class UserLoginSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(max_length=30)
|
username = serializers.CharField()
|
||||||
password = serializers.CharField(max_length=30)
|
password = serializers.CharField()
|
||||||
tfa_code = serializers.CharField(min_length=6, max_length=6, required=False, allow_null=True)
|
tfa_code = serializers.CharField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
|
||||||
class UsernameOrEmailCheckSerializer(serializers.Serializer):
|
class UsernameOrEmailCheckSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(max_length=30, required=False)
|
username = serializers.CharField(required=False)
|
||||||
email = serializers.EmailField(max_length=30, required=False)
|
email = serializers.EmailField(required=False)
|
||||||
|
|
||||||
|
|
||||||
class UserRegisterSerializer(serializers.Serializer):
|
class UserRegisterSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField(max_length=30)
|
username = serializers.CharField(max_length=32)
|
||||||
password = serializers.CharField(max_length=30, min_length=6)
|
password = serializers.CharField(min_length=6)
|
||||||
email = serializers.EmailField(max_length=30)
|
email = serializers.EmailField(max_length=64)
|
||||||
captcha = serializers.CharField(max_length=4, min_length=1)
|
captcha = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class UserChangePasswordSerializer(serializers.Serializer):
|
class UserChangePasswordSerializer(serializers.Serializer):
|
||||||
old_password = serializers.CharField()
|
old_password = serializers.CharField()
|
||||||
new_password = serializers.CharField(max_length=30, min_length=6)
|
new_password = serializers.CharField(min_length=6)
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
@ -58,9 +58,9 @@ class UserInfoSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class EditUserSerializer(serializers.Serializer):
|
class EditUserSerializer(serializers.Serializer):
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
username = serializers.CharField(max_length=30)
|
username = serializers.CharField(max_length=32)
|
||||||
password = serializers.CharField(max_length=30, min_length=6, allow_blank=True, required=False, default=None)
|
password = serializers.CharField(min_length=6, allow_blank=True, required=False, default=None)
|
||||||
email = serializers.EmailField(max_length=254)
|
email = serializers.EmailField(max_length=64)
|
||||||
admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN))
|
admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN))
|
||||||
problem_permission = serializers.ChoiceField(choices=(ProblemPermission.NONE, ProblemPermission.OWN,
|
problem_permission = serializers.ChoiceField(choices=(ProblemPermission.NONE, ProblemPermission.OWN,
|
||||||
ProblemPermission.ALL))
|
ProblemPermission.ALL))
|
||||||
@ -70,29 +70,29 @@ class EditUserSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class EditUserProfileSerializer(serializers.Serializer):
|
class EditUserProfileSerializer(serializers.Serializer):
|
||||||
real_name = serializers.CharField(max_length=30, allow_blank=True)
|
real_name = serializers.CharField(max_length=32, allow_blank=True)
|
||||||
avatar = serializers.CharField(max_length=100, allow_blank=True, required=False)
|
avatar = serializers.CharField(max_length=256, allow_blank=True, required=False)
|
||||||
blog = serializers.URLField(allow_blank=True, required=False)
|
blog = serializers.URLField(max_length=256, allow_blank=True, required=False)
|
||||||
mood = serializers.CharField(max_length=200, allow_blank=True, required=False)
|
mood = serializers.CharField(max_length=256, allow_blank=True, required=False)
|
||||||
github = serializers.CharField(max_length=50, allow_blank=True, required=False)
|
github = serializers.CharField(max_length=64, allow_blank=True, required=False)
|
||||||
school = serializers.CharField(max_length=200, allow_blank=True, required=False)
|
school = serializers.CharField(max_length=64, allow_blank=True, required=False)
|
||||||
major = serializers.CharField(max_length=200, allow_blank=True, required=False)
|
major = serializers.CharField(max_length=64, allow_blank=True, required=False)
|
||||||
|
|
||||||
|
|
||||||
class ApplyResetPasswordSerializer(serializers.Serializer):
|
class ApplyResetPasswordSerializer(serializers.Serializer):
|
||||||
email = serializers.EmailField()
|
email = serializers.EmailField()
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class ResetPasswordSerializer(serializers.Serializer):
|
class ResetPasswordSerializer(serializers.Serializer):
|
||||||
token = serializers.CharField(min_length=1, max_length=40)
|
token = serializers.CharField()
|
||||||
password = serializers.CharField(min_length=6, max_length=30)
|
password = serializers.CharField(min_length=6)
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class SSOSerializer(serializers.Serializer):
|
class SSOSerializer(serializers.Serializer):
|
||||||
appkey = serializers.CharField(max_length=35)
|
appkey = serializers.CharField()
|
||||||
token = serializers.CharField(max_length=40)
|
token = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class TwoFactorAuthCodeSerializer(serializers.Serializer):
|
class TwoFactorAuthCodeSerializer(serializers.Serializer):
|
||||||
|
@ -19,7 +19,6 @@ urlpatterns = [
|
|||||||
url(r"^check_username_or_email", UsernameOrEmailCheck.as_view(), name="check_username_or_email"),
|
url(r"^check_username_or_email", UsernameOrEmailCheck.as_view(), name="check_username_or_email"),
|
||||||
url(r"^profile/?$", UserProfileAPI.as_view(), name="user_profile_api"),
|
url(r"^profile/?$", UserProfileAPI.as_view(), name="user_profile_api"),
|
||||||
url(r"^upload_avatar/?$", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
|
url(r"^upload_avatar/?$", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
|
||||||
url(r"^sso/?$", SSOAPI.as_view(), name="sso_api"),
|
|
||||||
url(r"^tfa_required/?$", CheckTFARequiredAPI.as_view(), name="tfa_required_check"),
|
url(r"^tfa_required/?$", CheckTFARequiredAPI.as_view(), name="tfa_required_check"),
|
||||||
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api"),
|
url(r"^two_factor_auth/?$", TwoFactorAuthAPI.as_view(), name="two_factor_auth_api"),
|
||||||
url(r"^user_rank/?$", UserRankAPI.as_view(), name="user_rank_api"),
|
url(r"^user_rank/?$", UserRankAPI.as_view(), name="user_rank_api"),
|
||||||
|
@ -12,11 +12,10 @@ from django.utils.timezone import now
|
|||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from otpauth import OtpAuth
|
from otpauth import OtpAuth
|
||||||
|
|
||||||
|
from utils.constants import ContestRuleType
|
||||||
from options.options import SysOptions
|
from options.options import SysOptions
|
||||||
from utils.api import APIView, validate_serializer
|
from utils.api import APIView, validate_serializer
|
||||||
from utils.cache import default_cache
|
|
||||||
from utils.captcha import Captcha
|
from utils.captcha import Captcha
|
||||||
from utils.constants import CacheKey
|
|
||||||
from utils.shortcuts import rand_str, img2base64, timestamp2utcstr
|
from utils.shortcuts import rand_str, img2base64, timestamp2utcstr
|
||||||
from ..decorators import login_required
|
from ..decorators import login_required
|
||||||
from ..models import User, UserProfile
|
from ..models import User, UserProfile
|
||||||
@ -38,7 +37,7 @@ class UserProfileAPI(APIView):
|
|||||||
"""
|
"""
|
||||||
user = request.user
|
user = request.user
|
||||||
if not user.is_authenticated():
|
if not user.is_authenticated():
|
||||||
return self.success({})
|
return self.success()
|
||||||
username = request.GET.get("username")
|
username = request.GET.get("username")
|
||||||
try:
|
try:
|
||||||
if username:
|
if username:
|
||||||
@ -47,8 +46,7 @@ class UserProfileAPI(APIView):
|
|||||||
user = request.user
|
user = request.user
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return self.error("User does not exist")
|
return self.error("User does not exist")
|
||||||
profile = UserProfile.objects.select_related("user").get(user=user)
|
return self.success(UserProfileSerializer(user.userprofile).data)
|
||||||
return self.success(UserProfileSerializer(profile).data)
|
|
||||||
|
|
||||||
@validate_serializer(EditUserProfileSerializer)
|
@validate_serializer(EditUserProfileSerializer)
|
||||||
@login_required
|
@login_required
|
||||||
@ -71,8 +69,7 @@ class AvatarUploadAPI(APIView):
|
|||||||
avatar = form.cleaned_data["file"]
|
avatar = form.cleaned_data["file"]
|
||||||
else:
|
else:
|
||||||
return self.error("Invalid file content")
|
return self.error("Invalid file content")
|
||||||
# 2097152 = 2 * 1024 * 1024 = 2MB
|
if avatar.size > 2 * 1024 * 1024:
|
||||||
if avatar.size > 2097152:
|
|
||||||
return self.error("Picture is too large")
|
return self.error("Picture is too large")
|
||||||
suffix = os.path.splitext(avatar.name)[-1].lower()
|
suffix = os.path.splitext(avatar.name)[-1].lower()
|
||||||
if suffix not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]:
|
if suffix not in [".gif", ".jpg", ".jpeg", ".bmp", ".png"]:
|
||||||
@ -83,46 +80,12 @@ class AvatarUploadAPI(APIView):
|
|||||||
for chunk in avatar:
|
for chunk in avatar:
|
||||||
img.write(chunk)
|
img.write(chunk)
|
||||||
user_profile = request.user.userprofile
|
user_profile = request.user.userprofile
|
||||||
_, old_avatar = os.path.split(user_profile.avatar)
|
|
||||||
if old_avatar != "default.png":
|
|
||||||
os.remove(os.path.join(settings.IMAGE_UPLOAD_DIR_ABS, old_avatar))
|
|
||||||
|
|
||||||
user_profile.avatar = f"/{settings.IMAGE_UPLOAD_DIR}/{name}"
|
user_profile.avatar = f"{settings.IMAGE_UPLOAD_DIR}/{name}"
|
||||||
user_profile.save()
|
user_profile.save()
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
|
|
||||||
|
|
||||||
class SSOAPI(APIView):
|
|
||||||
@login_required
|
|
||||||
def get(self, request):
|
|
||||||
callback = request.GET.get("callback", None)
|
|
||||||
if not callback:
|
|
||||||
return self.error("Parameter Error")
|
|
||||||
token = rand_str()
|
|
||||||
request.user.auth_token = token
|
|
||||||
request.user.save()
|
|
||||||
return self.success({"redirect_url": callback + "?token=" + token,
|
|
||||||
"callback": callback})
|
|
||||||
|
|
||||||
@validate_serializer(SSOSerializer)
|
|
||||||
def post(self, request):
|
|
||||||
data = request.data
|
|
||||||
try:
|
|
||||||
User.objects.get(open_api_appkey=data["appkey"])
|
|
||||||
except User.DoesNotExist:
|
|
||||||
return self.error("Invalid appkey")
|
|
||||||
try:
|
|
||||||
user = User.objects.get(auth_token=data["token"])
|
|
||||||
user.auth_token = None
|
|
||||||
user.save()
|
|
||||||
return self.success({"username": user.username,
|
|
||||||
"id": user.id,
|
|
||||||
"admin_type": user.admin_type,
|
|
||||||
"avatar": user.userprofile.avatar})
|
|
||||||
except User.DoesNotExist:
|
|
||||||
return self.error("User does not exist")
|
|
||||||
|
|
||||||
|
|
||||||
class TwoFactorAuthAPI(APIView):
|
class TwoFactorAuthAPI(APIView):
|
||||||
@login_required
|
@login_required
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
@ -131,7 +94,7 @@ class TwoFactorAuthAPI(APIView):
|
|||||||
"""
|
"""
|
||||||
user = request.user
|
user = request.user
|
||||||
if user.two_factor_auth:
|
if user.two_factor_auth:
|
||||||
return self.error("Already open 2FA")
|
return self.error("2FA is already turned on")
|
||||||
token = rand_str()
|
token = rand_str()
|
||||||
user.tfa_token = token
|
user.tfa_token = token
|
||||||
user.save()
|
user.save()
|
||||||
@ -161,7 +124,7 @@ class TwoFactorAuthAPI(APIView):
|
|||||||
code = request.data["code"]
|
code = request.data["code"]
|
||||||
user = request.user
|
user = request.user
|
||||||
if not user.two_factor_auth:
|
if not user.two_factor_auth:
|
||||||
return self.error("Other session have disabled TFA")
|
return self.error("2FA is already turned off")
|
||||||
if OtpAuth(user.tfa_token).valid_totp(code):
|
if OtpAuth(user.tfa_token).valid_totp(code):
|
||||||
user.two_factor_auth = False
|
user.two_factor_auth = False
|
||||||
user.save()
|
user.save()
|
||||||
@ -198,7 +161,7 @@ class UserLoginAPI(APIView):
|
|||||||
# None is returned if username or password is wrong
|
# None is returned if username or password is wrong
|
||||||
if user:
|
if user:
|
||||||
if user.is_disabled:
|
if user.is_disabled:
|
||||||
return self.error("Your account have been disabled")
|
return self.error("Your account has been disabled")
|
||||||
if not user.two_factor_auth:
|
if not user.two_factor_auth:
|
||||||
auth.login(request, user)
|
auth.login(request, user)
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
@ -218,13 +181,13 @@ class UserLoginAPI(APIView):
|
|||||||
# todo remove this, only for debug use
|
# todo remove this, only for debug use
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
auth.login(request, auth.authenticate(username=request.GET["username"], password=request.GET["password"]))
|
auth.login(request, auth.authenticate(username=request.GET["username"], password=request.GET["password"]))
|
||||||
return self.success({})
|
return self.success()
|
||||||
|
|
||||||
|
|
||||||
class UserLogoutAPI(APIView):
|
class UserLogoutAPI(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
auth.logout(request)
|
auth.logout(request)
|
||||||
return self.success({})
|
return self.success()
|
||||||
|
|
||||||
|
|
||||||
class UsernameOrEmailCheck(APIView):
|
class UsernameOrEmailCheck(APIView):
|
||||||
@ -240,11 +203,9 @@ class UsernameOrEmailCheck(APIView):
|
|||||||
"email": False
|
"email": False
|
||||||
}
|
}
|
||||||
if data.get("username"):
|
if data.get("username"):
|
||||||
if User.objects.filter(username=data["username"]).exists():
|
result["username"] = User.objects.filter(username=data["username"]).exists()
|
||||||
result["username"] = True
|
|
||||||
if data.get("email"):
|
if data.get("email"):
|
||||||
if User.objects.filter(email=data["email"]).exists():
|
result["email"] = User.objects.filter(email=data["email"]).exists()
|
||||||
result["email"] = True
|
|
||||||
return self.success(result)
|
return self.success(result)
|
||||||
|
|
||||||
|
|
||||||
@ -254,17 +215,9 @@ class UserRegisterAPI(APIView):
|
|||||||
"""
|
"""
|
||||||
User register api
|
User register api
|
||||||
"""
|
"""
|
||||||
config = default_cache.get(CacheKey.website_config)
|
|
||||||
if config:
|
|
||||||
config = pickle.loads(config)
|
|
||||||
else:
|
|
||||||
config = WebsiteConfig.objects.first()
|
|
||||||
if not config:
|
|
||||||
config = WebsiteConfig.objects.create()
|
|
||||||
default_cache.set(CacheKey.website_config, pickle.dumps(config))
|
|
||||||
|
|
||||||
if not config.allow_register:
|
if not SysOptions.allow_register:
|
||||||
return self.error("Register have been disabled by admin")
|
return self.error("Register function has been disabled by admin")
|
||||||
|
|
||||||
data = request.data
|
data = request.data
|
||||||
captcha = Captcha(request)
|
captcha = Captcha(request)
|
||||||
@ -293,6 +246,7 @@ class UserChangePasswordAPI(APIView):
|
|||||||
username = request.user.username
|
username = request.user.username
|
||||||
user = auth.authenticate(username=username, password=data["old_password"])
|
user = auth.authenticate(username=username, password=data["old_password"])
|
||||||
if user:
|
if user:
|
||||||
|
# TODO: check tfa?
|
||||||
user.set_password(data["new_password"])
|
user.set_password(data["new_password"])
|
||||||
user.save()
|
user.save()
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
@ -305,7 +259,6 @@ class ApplyResetPasswordAPI(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
data = request.data
|
data = request.data
|
||||||
captcha = Captcha(request)
|
captcha = Captcha(request)
|
||||||
config = WebsiteConfig.objects.first()
|
|
||||||
if not captcha.check(data["captcha"]):
|
if not captcha.check(data["captcha"]):
|
||||||
return self.error("Invalid captcha")
|
return self.error("Invalid captcha")
|
||||||
try:
|
try:
|
||||||
@ -320,14 +273,14 @@ class ApplyResetPasswordAPI(APIView):
|
|||||||
user.save()
|
user.save()
|
||||||
render_data = {
|
render_data = {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"website_name": config.name,
|
"website_name": SysOptions.website_name,
|
||||||
"link": f"{config.base_url}/reset-password/{user.reset_password_token}"
|
"link": f"{SysOptions.website_base_url}/reset-password/{user.reset_password_token}"
|
||||||
}
|
}
|
||||||
email_html = render_to_string("reset_password_email.html", render_data)
|
email_html = render_to_string("reset_password_email.html", render_data)
|
||||||
send_email_async.delay(config.name,
|
send_email_async.delay(SysOptions.website_name,
|
||||||
user.email,
|
user.email,
|
||||||
user.username,
|
user.username,
|
||||||
config.name + " 登录信息找回邮件",
|
f"{SysOptions.website_name} 登录信息找回邮件",
|
||||||
email_html)
|
email_html)
|
||||||
return self.success("Succeeded")
|
return self.success("Succeeded")
|
||||||
|
|
||||||
@ -342,9 +295,9 @@ class ResetPasswordAPI(APIView):
|
|||||||
try:
|
try:
|
||||||
user = User.objects.get(reset_password_token=data["token"])
|
user = User.objects.get(reset_password_token=data["token"])
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return self.error("Token dose not exist")
|
return self.error("Token does not exist")
|
||||||
if int((user.reset_password_token_expire_time - now()).total_seconds()) < 0:
|
if user.reset_password_token_expire_time < now():
|
||||||
return self.error("Token have expired")
|
return self.error("Token has expired")
|
||||||
user.reset_password_token = None
|
user.reset_password_token = None
|
||||||
user.two_factor_auth = False
|
user.two_factor_auth = False
|
||||||
user.set_password(data["password"])
|
user.set_password(data["password"])
|
||||||
@ -356,13 +309,13 @@ class SessionManagementAPI(APIView):
|
|||||||
@login_required
|
@login_required
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
engine = import_module(settings.SESSION_ENGINE)
|
engine = import_module(settings.SESSION_ENGINE)
|
||||||
SessionStore = engine.SessionStore
|
session_store = engine.SessionStore
|
||||||
current_session = request.session.session_key
|
current_session = request.session.session_key
|
||||||
session_keys = request.user.session_keys
|
session_keys = request.user.session_keys
|
||||||
result = []
|
result = []
|
||||||
modified = False
|
modified = False
|
||||||
for key in session_keys[:]:
|
for key in session_keys[:]:
|
||||||
session = SessionStore(key)
|
session = session_store(key)
|
||||||
# session does not exist or is expiry
|
# session does not exist or is expiry
|
||||||
if not session._session:
|
if not session._session:
|
||||||
session_keys.remove(key)
|
session_keys.remove(key)
|
||||||
@ -398,12 +351,12 @@ class SessionManagementAPI(APIView):
|
|||||||
class UserRankAPI(APIView):
|
class UserRankAPI(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
rule_type = request.GET.get("rule")
|
rule_type = request.GET.get("rule")
|
||||||
if rule_type not in ["acm", "oi"]:
|
if rule_type not in ContestRuleType.choices():
|
||||||
rule_type = "acm"
|
rule_type = ContestRuleType.ACM
|
||||||
profiles = UserProfile.objects.select_related("user")\
|
profiles = UserProfile.objects.select_related("user")\
|
||||||
.filter(submission_number__gt=0)\
|
.filter(submission_number__gt=0)\
|
||||||
.exclude(user__is_disabled=True)
|
.exclude(user__is_disabled=True)
|
||||||
if rule_type == "acm":
|
if rule_type == ContestRuleType.ACM:
|
||||||
profiles = profiles.order_by("-accepted_number", "submission_number")
|
profiles = profiles.order_by("-accepted_number", "submission_number")
|
||||||
else:
|
else:
|
||||||
profiles = profiles.order_by("-total_score")
|
profiles = profiles.order_by("-total_score")
|
||||||
|
@ -5,7 +5,7 @@ from utils.models import RichTextField
|
|||||||
|
|
||||||
|
|
||||||
class Announcement(models.Model):
|
class Announcement(models.Model):
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=64)
|
||||||
# HTML
|
# HTML
|
||||||
content = RichTextField()
|
content = RichTextField()
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
@ -5,8 +5,8 @@ from .models import Announcement
|
|||||||
|
|
||||||
|
|
||||||
class CreateAnnouncementSerializer(serializers.Serializer):
|
class CreateAnnouncementSerializer(serializers.Serializer):
|
||||||
title = serializers.CharField(max_length=50)
|
title = serializers.CharField(max_length=64)
|
||||||
content = serializers.CharField(max_length=10000)
|
content = serializers.CharField(max_length=1024 * 1024 * 8)
|
||||||
visible = serializers.BooleanField()
|
visible = serializers.BooleanField()
|
||||||
|
|
||||||
|
|
||||||
@ -21,6 +21,6 @@ class AnnouncementSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class EditAnnouncementSerializer(serializers.Serializer):
|
class EditAnnouncementSerializer(serializers.Serializer):
|
||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
title = serializers.CharField(max_length=50)
|
title = serializers.CharField(max_length=64)
|
||||||
content = serializers.CharField(max_length=10000)
|
content = serializers.CharField(max_length=1024 * 1024 * 8)
|
||||||
visible = serializers.BooleanField()
|
visible = serializers.BooleanField()
|
||||||
|
@ -3,16 +3,16 @@ from django.utils import timezone
|
|||||||
|
|
||||||
|
|
||||||
class JudgeServer(models.Model):
|
class JudgeServer(models.Model):
|
||||||
hostname = models.CharField(max_length=64)
|
hostname = models.CharField(max_length=128)
|
||||||
ip = models.CharField(max_length=32, blank=True, null=True)
|
ip = models.CharField(max_length=32, blank=True, null=True)
|
||||||
judger_version = models.CharField(max_length=24)
|
judger_version = models.CharField(max_length=32)
|
||||||
cpu_core = models.IntegerField()
|
cpu_core = models.IntegerField()
|
||||||
memory_usage = models.FloatField()
|
memory_usage = models.FloatField()
|
||||||
cpu_usage = models.FloatField()
|
cpu_usage = models.FloatField()
|
||||||
last_heartbeat = models.DateTimeField()
|
last_heartbeat = models.DateTimeField()
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
task_number = models.IntegerField(default=0)
|
task_number = models.IntegerField(default=0)
|
||||||
service_url = models.CharField(max_length=128, blank=True, null=True)
|
service_url = models.CharField(max_length=256, blank=True, null=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
@ -21,9 +21,9 @@ class TestSMTPConfigSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
class CreateEditWebsiteConfigSerializer(serializers.Serializer):
|
class CreateEditWebsiteConfigSerializer(serializers.Serializer):
|
||||||
website_base_url = serializers.CharField(max_length=128)
|
website_base_url = serializers.CharField(max_length=128)
|
||||||
website_name = serializers.CharField(max_length=32)
|
website_name = serializers.CharField(max_length=64)
|
||||||
website_name_shortcut = serializers.CharField(max_length=32)
|
website_name_shortcut = serializers.CharField(max_length=64)
|
||||||
website_footer = serializers.CharField(max_length=1024)
|
website_footer = serializers.CharField(max_length=1024 * 1024)
|
||||||
allow_register = serializers.BooleanField()
|
allow_register = serializers.BooleanField()
|
||||||
submission_list_show_all = serializers.BooleanField()
|
submission_list_show_all = serializers.BooleanField()
|
||||||
|
|
||||||
@ -39,10 +39,10 @@ class JudgeServerSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class JudgeServerHeartbeatSerializer(serializers.Serializer):
|
class JudgeServerHeartbeatSerializer(serializers.Serializer):
|
||||||
hostname = serializers.CharField(max_length=64)
|
hostname = serializers.CharField(max_length=128)
|
||||||
judger_version = serializers.CharField(max_length=24)
|
judger_version = serializers.CharField(max_length=32)
|
||||||
cpu_core = serializers.IntegerField(min_value=1)
|
cpu_core = serializers.IntegerField(min_value=1)
|
||||||
memory = serializers.FloatField(min_value=0, max_value=100)
|
memory = serializers.FloatField(min_value=0, max_value=100)
|
||||||
cpu = serializers.FloatField(min_value=0, max_value=100)
|
cpu = serializers.FloatField(min_value=0, max_value=100)
|
||||||
action = serializers.ChoiceField(choices=("heartbeat", ))
|
action = serializers.ChoiceField(choices=("heartbeat", ))
|
||||||
service_url = serializers.CharField(max_length=128, required=False)
|
service_url = serializers.CharField(max_length=256, required=False)
|
||||||
|
@ -2,26 +2,11 @@ from django.db import models
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
|
|
||||||
|
from utils.constants import ContestStatus, ContestRuleType, ContestType
|
||||||
from account.models import User, AdminType
|
from account.models import User, AdminType
|
||||||
from utils.models import RichTextField
|
from utils.models import RichTextField
|
||||||
|
|
||||||
|
|
||||||
class ContestType(object):
|
|
||||||
PUBLIC_CONTEST = "Public"
|
|
||||||
PASSWORD_PROTECTED_CONTEST = "Password Protected"
|
|
||||||
|
|
||||||
|
|
||||||
class ContestStatus(object):
|
|
||||||
CONTEST_NOT_START = "1"
|
|
||||||
CONTEST_ENDED = "-1"
|
|
||||||
CONTEST_UNDERWAY = "0"
|
|
||||||
|
|
||||||
|
|
||||||
class ContestRuleType(object):
|
|
||||||
ACM = "ACM"
|
|
||||||
OI = "OI"
|
|
||||||
|
|
||||||
|
|
||||||
class Contest(models.Model):
|
class Contest(models.Model):
|
||||||
title = models.CharField(max_length=40)
|
title = models.CharField(max_length=40)
|
||||||
description = RichTextField()
|
description = RichTextField()
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import pickle
|
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from utils.api import APIView, validate_serializer
|
from utils.api import APIView, validate_serializer
|
||||||
from utils.cache import default_cache
|
|
||||||
from utils.constants import CacheKey
|
from utils.constants import CacheKey
|
||||||
from account.decorators import login_required, check_contest_permission
|
from account.decorators import login_required, check_contest_permission
|
||||||
|
|
||||||
from ..models import ContestAnnouncement, Contest, ContestStatus, ContestRuleType
|
from utils.constants import ContestRuleType, ContestType, ContestStatus
|
||||||
from ..models import OIContestRank, ACMContestRank
|
from ..models import ContestAnnouncement, Contest, OIContestRank, ACMContestRank
|
||||||
from ..serializers import ContestAnnouncementSerializer
|
from ..serializers import ContestAnnouncementSerializer
|
||||||
from ..serializers import ContestSerializer, ContestPasswordVerifySerializer
|
from ..serializers import ContestSerializer, ContestPasswordVerifySerializer
|
||||||
from ..serializers import OIContestRankSerializer, ACMContestRankSerializer
|
from ..serializers import OIContestRankSerializer, ACMContestRankSerializer
|
||||||
|
@ -1,3 +1,26 @@
|
|||||||
|
class Choices:
|
||||||
|
@classmethod
|
||||||
|
def choices(cls):
|
||||||
|
d = cls.__dict__
|
||||||
|
return [d[item] for item in d.keys() if not item.startswith("__")]
|
||||||
|
|
||||||
|
|
||||||
|
class ContestType:
|
||||||
|
PUBLIC_CONTEST = "Public"
|
||||||
|
PASSWORD_PROTECTED_CONTEST = "Password Protected"
|
||||||
|
|
||||||
|
|
||||||
|
class ContestStatus:
|
||||||
|
CONTEST_NOT_START = "1"
|
||||||
|
CONTEST_ENDED = "-1"
|
||||||
|
CONTEST_UNDERWAY = "0"
|
||||||
|
|
||||||
|
|
||||||
|
class ContestRuleType(Choices):
|
||||||
|
ACM = "ACM"
|
||||||
|
OI = "OI"
|
||||||
|
|
||||||
|
|
||||||
class CacheKey:
|
class CacheKey:
|
||||||
waiting_queue = "waiting_queue"
|
waiting_queue = "waiting_queue"
|
||||||
contest_rank_cache = "contest_rank_cache_"
|
contest_rank_cache = "contest_rank_cache_"
|
||||||
|
@ -26,11 +26,8 @@ Cannot defense xss in browser which is belowed IE7
|
|||||||
浏览器版本:IE7+ 或其他浏览器,无法防御IE6及以下版本浏览器中的XSS
|
浏览器版本:IE7+ 或其他浏览器,无法防御IE6及以下版本浏览器中的XSS
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
import copy
|
||||||
try:
|
from html.parser import HTMLParser
|
||||||
from html.parser import HTMLParser
|
|
||||||
except:
|
|
||||||
from HTMLParser import HTMLParser
|
|
||||||
|
|
||||||
|
|
||||||
class XssHtml(HTMLParser):
|
class XssHtml(HTMLParser):
|
||||||
@ -163,7 +160,7 @@ class XssHtml(HTMLParser):
|
|||||||
else:
|
else:
|
||||||
other = []
|
other = []
|
||||||
if attrs:
|
if attrs:
|
||||||
for (key, value) in attrs.items():
|
for key, value in copy.deepcopy(attrs).items():
|
||||||
if key not in self.common_attrs + other:
|
if key not in self.common_attrs + other:
|
||||||
del attrs[key]
|
del attrs[key]
|
||||||
return attrs
|
return attrs
|
||||||
|
Loading…
Reference in New Issue
Block a user